move delay code into separate function.
[AROS.git] / rom / partition / partitionmbr.c
blobd07d0015054b75533241a0842bdb6647199cfe6b
1 /*
2 Copyright © 1995-2012, The AROS Development Team. All rights reserved.
3 $Id$
4 */
6 #include <exec/memory.h>
7 #include <exec/types.h>
8 #include <libraries/partition.h>
9 #include <proto/exec.h>
10 #include <proto/partition.h>
11 #include <proto/utility.h>
13 #include "partition_types.h"
14 #include "partition_support.h"
15 #include "partitionmbr.h"
16 #include "platform.h"
17 #include "debug.h"
19 struct MBRData {
20 struct PCPartitionTable *entry;
21 UBYTE position;
24 struct FATBootSector {
25 UBYTE bs_jmp_boot[3];
26 UBYTE bs_oem_name[8];
27 UWORD bpb_bytes_per_sect;
28 UBYTE bpb_sect_per_clust;
29 UWORD bpb_rsvd_sect_count;
30 UBYTE bpb_num_fats;
31 UWORD bpb_root_entries_count;
32 UWORD bpb_total_sectors_16;
33 UBYTE bpb_media;
34 UWORD bpb_fat_size_16;
35 UWORD bpb_sect_per_track;
36 UWORD bpb_num_heads;
37 ULONG bpb_hidden_sect;
38 ULONG bpb_total_sectors_32;
40 union {
41 struct {
42 UBYTE bs_drvnum;
43 UBYTE bs_reserved1;
44 UBYTE bs_bootsig;
45 ULONG bs_volid;
46 UBYTE bs_vollab[11];
47 UBYTE bs_filsystype[8];
48 } __attribute__ ((__packed__)) fat16;
50 struct {
51 ULONG bpb_fat_size_32;
52 UWORD bpb_extflags;
53 UWORD bpb_fs_verion;
54 ULONG bpb_root_cluster;
55 UWORD bpb_fs_info;
56 UWORD bpb_back_bootsec;
57 UBYTE bpb_reserved[12];
58 UBYTE bs_drvnum;
59 UBYTE bs_reserved1;
60 UBYTE bs_bootsig;
61 ULONG bs_volid;
62 UBYTE bs_vollab[11];
63 UBYTE bs_filsystype[8];
64 } __attribute__ ((__packed__)) fat32;
65 } type;
66 UBYTE pad[420];
67 UBYTE bpb_signature[2];
68 } __attribute__ ((__packed__));
70 struct rootblock
72 union
74 struct MBR mbr;
75 struct FATBootSector bs;
80 LONG MBRCheckPartitionTable(struct Library *PartitionBase, struct PartitionHandle *root, void *buffer)
82 struct rootblock *blk = buffer;
83 LONG res = 0;
85 if (readBlock(PartitionBase, root, 0, blk) == 0)
87 /* Check it doesn't look like a FAT boot sector */
88 ULONG sectorsize, clustersectors;
90 /* Valid sector size: 512, 1024, 2048, 4096 */
91 sectorsize = AROS_LE2WORD(blk->u.bs.bpb_bytes_per_sect);
92 if (sectorsize != 512 && sectorsize != 1024 && sectorsize != 2048 && sectorsize != 4096)
93 res = 1;
95 /* Valid bpb_sect_per_clust: 1, 2, 4, 8, 16, 32, 64, 128 */
96 clustersectors = blk->u.bs.bpb_sect_per_clust;
97 if ((clustersectors & (clustersectors - 1)) != 0 || clustersectors == 0 || clustersectors > 128)
98 res = 1;
100 /* Valid cluster size: 512, 1024, 2048, 4096, 8192, 16k, 32k, 64k */
101 if (clustersectors * sectorsize > 64 * 1024)
102 res = 1;
104 if (blk->u.bs.bpb_media < 0xF0)
105 res = 1;
107 if (res)
109 struct PCPartitionTable *pcpt = blk->u.mbr.pcpt;
111 /* Check status bytes of all partition slots and block signature */
112 if ((AROS_LE2WORD(blk->u.mbr.magic) != MBR_MAGIC) ||
113 (!MBR_STATUS_VALID(pcpt[0].status)) || (!MBR_STATUS_VALID(pcpt[1].status)) ||
114 (!MBR_STATUS_VALID(pcpt[2].status)) || (!MBR_STATUS_VALID(pcpt[3].status)))
116 res = 0;
121 return res;
124 static LONG PartitionMBRCheckPartitionTable(struct Library *PartitionBase, struct PartitionHandle *root)
126 LONG res;
127 void *blk;
129 /* MBR can be placed only in the root of the disk */
130 if (root->root)
131 return 0;
133 blk = AllocMem(root->de.de_SizeBlock << 2, MEMF_ANY);
134 if (!blk)
135 return 0;
137 res = MBRCheckPartitionTable(PartitionBase, root, blk);
139 FreeMem(blk, root->de.de_SizeBlock << 2);
140 return res;
143 static struct PartitionHandle *PartitionMBRNewHandle(struct Library *PartitionBase, struct PartitionHandle *root, UBYTE position, struct PCPartitionTable *entry)
145 struct PartitionHandle *ph;
147 if (entry->first_sector != 0)
149 ph = AllocMem(sizeof(struct PartitionHandle), MEMF_PUBLIC | MEMF_CLEAR);
150 if (ph)
152 struct MBRData *data;
154 data = AllocMem(sizeof(struct MBRData), MEMF_PUBLIC);
155 if (data)
157 data->entry = entry;
158 data->position = position;
159 ph->data = data;
161 /* initialize DosEnvec and DriveGeometry */
162 initPartitionHandle(root, ph, AROS_LE2LONG(data->entry->first_sector), AROS_LE2LONG(data->entry->count_sector));
164 /* Map type ID to a DOSType */
165 setDosType(&ph->de, MBR_FindDosType(data->entry->type));
167 /* Set position as priority */
168 ph->ln.ln_Pri = MBR_MAX_PARTITIONS - 1 - position;
169 return ph;
171 FreeMem(ph, sizeof(struct PartitionHandle));
174 return NULL;
177 static LONG PartitionMBROpenPartitionTable(struct Library *PartitionBase, struct PartitionHandle *root)
179 struct PartitionHandle *ph;
180 struct MBR *mbr;
181 UBYTE i;
183 mbr = AllocMem(root->de.de_SizeBlock<<2, MEMF_PUBLIC);
184 if (mbr)
186 if (readBlock(PartitionBase, root, 0, mbr) == 0)
188 root->table->data = mbr;
189 for (i=0;i<4;i++)
191 ph = PartitionMBRNewHandle(PartitionBase, root, i, &mbr->pcpt[i]);
192 if (ph != NULL)
193 Enqueue(&root->table->list, &ph->ln);
195 return 0;
197 FreeMem(mbr, root->de.de_SizeBlock<<2);
199 return 1;
202 static void PartitionMBRFreeHandle
204 struct Library *PartitionBase,
205 struct PartitionHandle *ph
208 ClosePartitionTable(ph);
209 FreeMem(ph->data, sizeof(struct MBRData));
210 FreeMem(ph, sizeof(struct PartitionHandle));
213 static void PartitionMBRClosePartitionTable
215 struct Library *PartitionBase,
216 struct PartitionHandle *root
219 struct PartitionHandle *ph;
221 while ((ph = (struct PartitionHandle *)RemTail(&root->table->list)))
222 PartitionMBRFreeHandle(PartitionBase, ph);
223 FreeMem(root->table->data, root->de.de_SizeBlock<<2);
226 static LONG PartitionMBRWritePartitionTable
228 struct Library *PartitionBase,
229 struct PartitionHandle *root
232 /* FIXME: readBlock(0) and synchronize data */
234 /* root->data = mbr is up to date */
235 if (writeBlock(PartitionBase, root, 0, root->table->data))
236 return 1;
237 return 0;
240 static LONG PartitionMBRCreatePartitionTable
242 struct Library *PartitionBase,
243 struct PartitionHandle *ph
246 struct MBR *mbr;
248 mbr = AllocMem(ph->de.de_SizeBlock<<2, MEMF_PUBLIC);
249 if (mbr)
251 if (readBlock(PartitionBase, ph, 0, mbr) == 0)
253 ph->table->data = mbr;
255 memset(mbr->pcpt, 0, sizeof(mbr->pcpt));
256 mbr->magic = AROS_WORD2LE(0xAA55);
258 NEWLIST(&ph->table->list);
259 return 0;
261 FreeMem(mbr, ph->de.de_SizeBlock<<2);
263 return 1;
266 void PartitionMBRSetGeometry
268 struct PartitionHandle *root,
269 struct PCPartitionTable *entry,
270 ULONG sector,
271 ULONG count,
272 ULONG relative_sector
275 ULONG track;
276 ULONG cyl;
278 /* Store LBA start block and block count */
280 entry->first_sector = AROS_LONG2LE(sector - relative_sector);
281 entry->count_sector = AROS_LONG2LE(count);
283 /* Store CHS-address of start block. The upper two bits of the cylinder
284 number are stored in the upper two bits of the sector field */
286 track = sector/root->de.de_BlocksPerTrack;
287 cyl = track/root->de.de_Surfaces;
288 if (cyl<1024)
290 entry->start_head = track % root->de.de_Surfaces;
291 entry->start_sector =
292 ((sector % root->de.de_BlocksPerTrack) + 1)
293 | ((cyl & 0x300) >> 2);
294 entry->start_cylinder = (cyl & 0xFF);
296 else
298 entry->start_head = 0xFE;
299 entry->start_sector = 0xFF;
300 entry->start_cylinder = 0xFF;
303 /* Store CHS-address of last block */
305 sector += count - 1;
306 track = sector/root->de.de_BlocksPerTrack;
307 cyl = track/root->de.de_Surfaces;
308 if (cyl<1024)
310 entry->end_head = track % root->de.de_Surfaces;
311 entry->end_sector = ((sector % root->de.de_BlocksPerTrack) + 1)
312 | ((cyl & 0x300)>>2);
313 entry->end_cylinder = (cyl & 0xFF);
315 else
317 entry->end_head = 0xFE;
318 entry->end_sector = 0xFF;
319 entry->end_cylinder = 0xFF;
323 static void PartitionMBRSetDosEnvec
325 struct PartitionHandle *root,
326 struct PCPartitionTable *entry,
327 struct DosEnvec *de
330 ULONG sector, count;
332 sector = de->de_LowCyl * de->de_Surfaces * de->de_BlocksPerTrack;
333 count = (de->de_HighCyl - de->de_LowCyl + 1) *
334 de->de_Surfaces *
335 de->de_BlocksPerTrack;
336 PartitionMBRSetGeometry(root, entry, sector, count, 0);
339 static struct PartitionHandle *PartitionMBRAddPartition(struct Library *PartitionBase, struct PartitionHandle *root, struct TagItem *taglist)
341 struct TagItem *tag;
343 tag = FindTagItem(PT_DOSENVEC, taglist);
345 if (tag)
347 struct PCPartitionTable *entry;
348 struct PartitionHandle *ph;
349 struct DosEnvec *de = (struct DosEnvec *)tag->ti_Data;
350 WORD pos = -1, i;
352 tag = FindTagItem(PT_POSITION, taglist);
353 if (tag != NULL)
354 pos = tag->ti_Data;
355 else
357 // Find an unused slot
358 for (i = 0; i < MBR_MAX_PARTITIONS && pos == -1; i++)
360 entry = &((struct MBR *)root->table->data)->pcpt[i];
361 if (entry->type == 0)
362 pos = i;
366 if (pos != -1)
368 entry = &((struct MBR *)root->table->data)->pcpt[pos];
369 tag = FindTagItem(PT_ACTIVE, taglist);
370 if (tag)
371 entry->status = tag->ti_Data ? 0x80 : 0;
372 else
373 entry->status = 0;
374 tag = FindTagItem(PT_TYPE, taglist);
375 if (tag)
377 struct PartitionType *ptype = (struct PartitionType *)tag->ti_Data;
379 entry->type = ptype->id[0];
381 else
382 entry->type = 0;
383 PartitionMBRSetDosEnvec(root, entry, de);
384 ph = PartitionMBRNewHandle(PartitionBase, root, pos, entry);
385 if (ph != NULL)
386 Enqueue(&root->table->list, &ph->ln);
387 else
388 memset(entry, 0, sizeof(struct PCPartitionTable));
390 return ph;
393 return NULL;
396 static void PartitionMBRDeletePartition(struct Library *PartitionBase, struct PartitionHandle *ph)
398 struct MBRData *data = (struct MBRData *)ph->data;
400 memset(data->entry, 0, sizeof(struct PCPartitionTable));
402 Remove(&ph->ln);
403 PartitionMBRFreeHandle(PartitionBase, ph);
406 static LONG PartitionMBRGetPartitionTableAttr(struct Library *PartitionBase, struct PartitionHandle *root, struct TagItem *tag)
408 switch (tag->ti_Tag)
410 case PTT_RESERVED:
411 *((LONG *)tag->ti_Data) = root->de.de_BlocksPerTrack; /* One track */
412 return TRUE;
414 case PTT_MAX_PARTITIONS:
415 *((LONG *)tag->ti_Data) = MBR_MAX_PARTITIONS;
416 return TRUE;
419 return 0;
422 static LONG PartitionMBRGetPartitionAttr(struct Library *PartitionBase, struct PartitionHandle *ph, struct TagItem *tag)
424 struct MBRData *data = (struct MBRData *)ph->data;
426 switch (tag->ti_Tag)
428 case PT_TYPE:
429 PTYPE(tag->ti_Data)->id[0] = (LONG)data->entry->type;
430 PTYPE(tag->ti_Data)->id_len = 1;
431 return TRUE;
433 case PT_POSITION:
434 *((LONG *)tag->ti_Data) = (LONG)data->position;
435 return TRUE;
437 case PT_ACTIVE:
438 *((LONG *)tag->ti_Data) = data->entry->status & 0x80 ? 1 : 0;
439 return TRUE;
441 case PT_STARTBLOCK:
442 *((UQUAD *)tag->ti_Data) = AROS_LE2LONG(data->entry->first_sector);
443 return TRUE;
445 case PT_ENDBLOCK:
446 *((UQUAD *)tag->ti_Data) = AROS_LE2LONG(data->entry->first_sector) + AROS_LE2LONG(data->entry->count_sector) - 1;
447 return TRUE;
450 /* Everything else gets default values */
451 return 0;
454 static LONG PartitionMBRSetPartitionAttrs(struct Library *PartitionBase, struct PartitionHandle *ph, const struct TagItem *taglist)
456 struct MBRData *data = (struct MBRData *)ph->data;
457 struct TagItem *tag;
459 while ((tag = NextTagItem((struct TagItem **)&taglist)))
461 switch (tag->ti_Tag)
463 case PT_DOSENVEC:
464 CopyMem((struct DosEnvec *)tag->ti_Data, &ph->de, sizeof(struct DosEnvec));
465 PartitionMBRSetDosEnvec(ph->root, data->entry, (struct DosEnvec *)tag->ti_Data);
466 break;
468 case PT_TYPE:
469 data->entry->type = PTYPE(tag->ti_Data)->id[0];
470 /* Update DOSType according to a new type ID */
471 setDosType(&ph->de, MBR_FindDosType(data->entry->type));
472 break;
474 case PT_POSITION:
475 if (tag->ti_Data != data->position)
477 struct PartitionHandle *node;
478 struct PCPartitionTable *entry;
480 node = (struct PartitionHandle *)ph->root->table->list.lh_Head;
481 while (node->ln.ln_Succ)
483 if (tag->ti_Data == ((struct MBRData *)node->data)->position)
484 goto posbreak;
485 node = (struct PartitionHandle *)node->ln.ln_Succ;
487 data->position = tag->ti_Data;
488 entry = &((struct MBR *)ph->root->table->data)->pcpt[data->position];
489 CopyMem(data->entry, entry, sizeof(struct PCPartitionTable));
490 memset(data->entry, 0, sizeof(struct PCPartitionTable));
491 data->entry = entry;
492 ph->ln.ln_Pri = MBR_MAX_PARTITIONS-1-data->position;
493 Remove(&ph->ln);
494 Enqueue(&ph->root->table->list, &ph->ln);
495 posbreak:
498 break;
500 case PT_ACTIVE:
501 if (tag->ti_Data)
502 data->entry->status |= 0x80;
503 else
504 data->entry->status &= ~0x80;
505 break;
508 return 0;
511 static const struct PartitionAttribute PartitionMBRPartitionTableAttrs[]=
513 {PTT_TYPE, PLAM_READ},
514 {PTT_RESERVED, PLAM_READ},
515 {PTT_MAX_PARTITIONS, PLAM_READ},
516 {TAG_DONE, 0}
519 static const struct PartitionAttribute PartitionMBRPartitionAttrs[]=
521 {PT_GEOMETRY, PLAM_READ},
522 {PT_TYPE, PLAM_READ | PLAM_WRITE},
523 {PT_POSITION, PLAM_READ | PLAM_WRITE},
524 {PT_ACTIVE, PLAM_READ | PLAM_WRITE},
525 {PT_STARTBLOCK, PLAM_READ},
526 {PT_ENDBLOCK, PLAM_READ},
527 {TAG_DONE, 0}
530 ULONG PartitionMBRDestroyPartitionTable(struct Library *PartitionBase,
531 struct PartitionHandle *root)
533 struct MBR *mbr = root->table->data;
535 memset(mbr->pcpt, 0, sizeof(mbr->pcpt));
536 /* deleting the magic value will invalidate the
537 * partition table so it cannot be opened again
539 mbr->magic = 0;
540 if (writeBlock(PartitionBase, root, 0, root->table->data))
541 return 1;
542 return 0;
545 const struct PTFunctionTable PartitionMBR =
547 PHPTT_MBR,
548 "PC-MBR",
549 PartitionMBRCheckPartitionTable,
550 PartitionMBROpenPartitionTable,
551 PartitionMBRClosePartitionTable,
552 PartitionMBRWritePartitionTable,
553 PartitionMBRCreatePartitionTable,
554 PartitionMBRAddPartition,
555 PartitionMBRDeletePartition,
556 PartitionMBRGetPartitionTableAttr,
557 NULL,
558 PartitionMBRGetPartitionAttr,
559 PartitionMBRSetPartitionAttrs,
560 PartitionMBRPartitionTableAttrs,
561 PartitionMBRPartitionAttrs,
562 PartitionMBRDestroyPartitionTable,
563 NULL