2 Copyright © 1995-2011, The AROS Development Team. All rights reserved.
5 Desc: Discover all mountable partitions
12 #include <aros/debug.h>
13 #include <exec/alerts.h>
14 #include <aros/asmcall.h>
15 #include <aros/bootloader.h>
16 #include <exec/lists.h>
17 #include <exec/memory.h>
18 #include <exec/resident.h>
19 #include <exec/types.h>
20 #include <libraries/configvars.h>
21 #include <libraries/expansion.h>
22 #include <libraries/expansionbase.h>
23 #include <libraries/partition.h>
24 #include <utility/tagitem.h>
25 #include <devices/bootblock.h>
26 #include <devices/timer.h>
27 #include <dos/dosextens.h>
28 #include <resources/filesysres.h>
30 #include <proto/exec.h>
31 #include <proto/expansion.h>
32 #include <proto/partition.h>
33 #include <proto/bootloader.h>
35 #include LC_LIBDEFS_FILE
37 #include "dosboot_intern.h"
39 #define uppercase(x) ((x >= 'a' && x <= 'z') ? (x & 0xdf) : x)
42 * TODO: Check if DOSType lookup in partition.library really works
43 * and remove this table and related code.
45 static const struct _pt
{
48 { 0x01, AROS_MAKE_ID('F','A','T',' ') }, /* DOS 12-bit FAT */
49 { 0x04, AROS_MAKE_ID('F','A','T',' ') }, /* DOS 16-bit FAT (up to 32M) */
50 { 0x06, AROS_MAKE_ID('F','A','T',' ') }, /* DOS 16-bit FAT (over 32M) */
51 { 0x07, AROS_MAKE_ID('N','T','F','S') }, /* Windows NT NTFS */
52 { 0x0b, AROS_MAKE_ID('V','F','A','T') }, /* W95 FAT32 */
53 { 0x0c, AROS_MAKE_ID('V','F','A','T') }, /* W95 LBA FAT32 */
54 { 0x0e, AROS_MAKE_ID('F','A','T',' ') }, /* W95 16-bit LBA FAT */
55 { 0x2c, AROS_MAKE_ID('D','O','S','\0') }, /* AOS OFS */
56 { 0x2d, AROS_MAKE_ID('D','O','S','\1') }, /* AOS FFS */
57 { 0x2e, AROS_MAKE_ID('D','O','S','\3') }, /* AOS FFS-I */
58 { 0x2f, AROS_MAKE_ID('S','F','S','\0') }, /* AOS SFS */
59 { 0x80, AROS_MAKE_ID('M','N','X','\0') }, /* MINIX until 1.4a */
60 { 0x81, AROS_MAKE_ID('M','N','X','\1') }, /* MINIX since 1.4b */
61 { 0x83, AROS_MAKE_ID('E','X','T','\2') }, /* linux native partition */
62 { 0x8e, AROS_MAKE_ID('L','V','M','\0') }, /* linux LVM partition */
63 { 0x9f, AROS_MAKE_ID('B','S','D','\0') }, /* BSD/OS */
64 { 0xa5, AROS_MAKE_ID('B','S','D','\1') }, /* NetBSD, FreeBSD */
65 { 0xa6, AROS_MAKE_ID('B','S','D','\2') }, /* OpenBSD */
66 { 0xdb, AROS_MAKE_ID('C','P','M','\2') }, /* CPM/M */
67 { 0xeb, AROS_MAKE_ID('B','E','F','S') }, /* BeOS FS */
68 { 0xec, AROS_MAKE_ID('S','K','Y','\0') }, /* SkyOS FS */
69 { 0xfd, AROS_MAKE_ID('R','A','I','D') }, /* linux RAID with autodetect */
73 static IPTR
MatchPartType(UBYTE PartType
)
78 for (i
= 0; i
< (sizeof(PartTypes
) / sizeof(struct _pt
)); i
++)
80 if ((IPTR
)PartType
== PartTypes
[i
].part
)
82 type
= PartTypes
[i
].type
;
89 static ULONG
GetOffset(struct Library
*PartitionBase
, struct PartitionHandle
*ph
)
95 tags
[0] = PT_DOSENVEC
;
101 GetPartitionAttrs(ph
, (struct TagItem
*)tags
);
102 offset
+= de
.de_LowCyl
* de
.de_Surfaces
* de
.de_BlocksPerTrack
;
108 static VOID
AddPartitionVolume(struct ExpansionBase
*ExpansionBase
, struct Library
*PartitionBase
,
109 struct FileSysStartupMsg
*fssm
, struct PartitionHandle
*table
,
110 struct PartitionHandle
*pn
, struct ExecBase
*SysBase
)
113 ULONG i
, blockspercyl
;
114 const struct PartitionAttribute
*attrs
;
116 IPTR pp
[4 + DE_BOOTBLOCKS
+ 1] = { };
117 struct DeviceNode
*devnode
;
118 struct PartitionType ptyp
;
122 ULONG pttype
= PHPTT_UNKNOWN
;
126 * TODO: Try to locate RDB filesystem for this volume and make it bootable.
127 * Use FindFileSystem() and AddBootFileSystem() for this.
130 D(bug("[Boot] AddPartitionVolume\n"));
131 GetPartitionTableAttrsTags(table
, PTT_TYPE
, &pttype
, TAG_DONE
);
133 attrs
= QueryPartitionAttrs(table
);
134 while ((attrs
->attribute
!= PTA_DONE
) && (attrs
->attribute
!= PTA_NAME
))
135 attrs
++; /* look for name attr */
136 if (attrs
->attribute
!= PTA_DONE
)
138 D(bug("[Boot] RDB/GPT partition\n"));
140 /* partition has a name => RDB/GPT partition */
142 tags
[1] = (IPTR
)name
;
143 tags
[2] = PT_DOSENVEC
;
144 tags
[3] = (IPTR
)&pp
[4];
145 tags
[4] = PT_BOOTABLE
;
146 tags
[5] = (IPTR
)&bootable
;
148 GetPartitionAttrs(pn
, (struct TagItem
*)tags
);
149 D(bug("[Boot] Partition name: %s bootable: %d\n", name
, bootable
));
152 * CHECKME: This should not be needed at all. Partition.library knows what it does,
153 * and it knows DosEnvec size. RDB partitions should have complete DosEnvec. GPT
154 * partitions (also processed here) have only fields up to de_DosType filled in,
155 * and this is correctly reflected in the DosEnvec.
156 pp[4 + DE_TABLESIZE] = DE_BOOTBLOCKS;
161 D(bug("[Boot] MBR partition\n"));
163 /* partition doesn't have a name => MBR partition */
164 tags
[0] = PT_POSITION
;
165 tags
[1] = (IPTR
)&ppos
;
167 tags
[3] = (IPTR
)&ptyp
;
168 tags
[4] = PT_DOSENVEC
;
169 tags
[5] = (IPTR
)&pp
[4];
171 GetPartitionAttrs(pn
, (struct TagItem
*)tags
);
175 devname
= AROS_BSTR_ADDR(fssm
->fssm_Device
);
176 for (i
= 0; i
< 26; i
++)
178 if (*devname
== '.' || *devname
== '\0')
180 name
[i
] = (UBYTE
)uppercase(*devname
);
183 if ((fssm
->fssm_Unit
/ 10))
184 name
[i
++] = '0' + (UBYTE
)(fssm
->fssm_Unit
/ 10);
185 name
[i
++] = '0' + (UBYTE
)(fssm
->fssm_Unit
% 10);
187 if (table
->table
->type
== PHPTT_EBR
)
190 name
[i
++] = '0' + (UBYTE
)(ppos
/ 10);
191 name
[i
++] = '0' + (UBYTE
)(ppos
% 10);
193 D(bug("[Boot] Partition name: %s type: %u\n", name
, ptyp
.id
[0]));
196 * FIXME: These MBR-related DosEnvec patches should already be correctly done
197 * by partition.library. Test this and remove the unneeded code from here.
200 /* set DOSTYPE based on the partition type */
201 pp
[4 + DE_DOSTYPE
] = MatchPartType(ptyp
.id
[0]);
202 /* set some common DOSENV fields */
203 pp
[4 + DE_TABLESIZE
] = DE_BOOTBLOCKS
;
204 pp
[4 + DE_NUMBUFFERS
] = 20;
205 pp
[4 + DE_BUFMEMTYPE
] = MEMF_PUBLIC
;
206 /* set some fs specific fields */
211 case 0x2e: /* FFS I */
213 pp
[4 + DE_SECSPERBLOCK
] = 1;
214 pp
[4 + DE_RESERVEDBLKS
] = 2;
215 pp
[4 + DE_BOOTBLOCKS
] = 2;
220 if ((pp
[4 + DE_TABLESIZE
] < DE_DOSTYPE
) || (pp
[4 + DE_DOSTYPE
] == 0))
223 * partition.library reports DosType == 0 for unknown filesystems.
224 * However dos.library will mount such DeviceNodes using rn_DefaultHandler
225 * (FFS). This is done for compatibility with 3rd party expansion ROMs.
226 * Here we ignore partitions with DosType == 0 and won't enter them into
229 D(bug("[Boot] Unknown DosType for %s, skipping partition\n"));
233 if (pttype
!= PHPTT_RDB
)
236 * Only RDB partitions can store the complete DosEnvec.
237 * For other partition types partition.library puts some defaults
238 * into these fields, however they do not have anything to do with
239 * real values, which are device-dependent.
240 * However, the device itself knows them. Here we inherit these settings
241 * from the original DeviceNode which represents the whole drive.
242 * Note that we don't change DosEnvec size. If these fields are not included,
243 * it will stay this way.
244 * Copy members only if they are present in device's DosEnvec.
246 struct DosEnvec
*devenv
= BADDR(fssm
->fssm_Environ
);
248 if (devenv
->de_TableSize
>= DE_MAXTRANSFER
)
250 pp
[4 + DE_MAXTRANSFER
] = devenv
->de_MaxTransfer
;
252 if (devenv
->de_TableSize
>= DE_MASK
)
253 pp
[4 + DE_MASK
] = devenv
->de_Mask
;
258 * BHFormat complains if this bit is not set, and it's really wrong to have it unset.
259 * So we explicitly set it here. Pavel Fedin <pavel.fedin@mail.ru>
261 pp
[4 + DE_BUFMEMTYPE
] |= MEMF_PUBLIC
;
264 pp
[1] = (IPTR
)AROS_BSTR_ADDR(fssm
->fssm_Device
);
265 pp
[2] = fssm
->fssm_Unit
;
266 pp
[3] = fssm
->fssm_Flags
;
268 i
= GetOffset(PartitionBase
, pn
);
269 blockspercyl
= pp
[4 + DE_BLKSPERTRACK
] * pp
[4 + DE_NUMHEADS
];
270 if (i
% blockspercyl
!= 0)
272 D(bug("[Boot] Start block of subtable not on cylinder boundary: "
273 "%ld (Blocks per Cylinder = %ld)\n", i
, blockspercyl
));
277 pp
[4 + DE_LOWCYL
] += i
;
278 pp
[4 + DE_HIGHCYL
] += i
;
280 devnode
= MakeDosNode(pp
);
281 if (devnode
!= NULL
) {
282 AddBootNode(bootable
? pp
[4 + DE_BOOTPRI
] : -128, ADNF_STARTPROC
, devnode
, NULL
);
283 D(bug("[Boot] AddBootNode(%b, 0, 0x%p, NULL)\n", devnode
->dn_Name
, pp
[4 + DE_DOSTYPE
]));
288 static BOOL
CheckTables(struct ExpansionBase
*ExpansionBase
, struct Library
*PartitionBase
,
289 struct FileSysStartupMsg
*fssm
, struct PartitionHandle
*table
,
290 struct ExecBase
*SysBase
)
293 struct PartitionHandle
*ph
;
295 /* Traverse partition tables recursively, and attempt to add a BootNode
296 for any non-subtable partitions found */
297 if (OpenPartitionTable(table
) == 0)
299 ph
= (struct PartitionHandle
*)table
->table
->list
.lh_Head
;
300 while (ph
->ln
.ln_Succ
)
302 /* Attempt to add partition to system if it isn't a subtable */
303 if (!CheckTables(ExpansionBase
, PartitionBase
, fssm
, ph
, SysBase
))
304 AddPartitionVolume(ExpansionBase
, PartitionBase
, fssm
, table
,
306 ph
= (struct PartitionHandle
*)ph
->ln
.ln_Succ
;
309 ClosePartitionTable(table
);
314 static BOOL
IsRemovable(struct ExecBase
*SysBase
, struct IOExtTD
*ioreq
)
316 struct DriveGeometry dg
;
319 * On AROS m68k, CF cards are removable, but are
320 * bootable. Also, we should support CDROMs that
321 * have RDB blocks. So, in all cases, allow the
323 * UPD: this is not so easy. If you remove one disk with RDB on it
324 * and insert another one, number of partitions and their parameters
325 * will change. This means that we actually have to dismount all DeviceNodes
326 * for old disk and mount new ones.
327 * This is technically possible, however rather complex (we need to track down
328 * which DeviceNodes are currently in use, and be able to reuse them when the
329 * disk is inserted again in a response to "Insert disk XXX in any drive".
330 * MorphOS has this mechanism implemented in mount.library.
331 * An alternative is to bind mounted DeviceNodes to a particular disk and mount
332 * a new set for every new one. Perhaps it's simpler, but anyway, needs to be
333 * handled in some special way.
335 if (!strcmp(ioreq
->iotd_Req
.io_Device
->dd_Library
.lib_Node
.ln_Name
, "carddisk.device"))
338 ioreq
->iotd_Req
.io_Command
= TD_GETGEOMETRY
;
339 ioreq
->iotd_Req
.io_Data
= &dg
;
340 ioreq
->iotd_Req
.io_Length
= sizeof(struct DriveGeometry
);
341 DoIO((struct IORequest
*)ioreq
);
343 return (dg
.dg_Flags
& DGF_REMOVABLE
) ? TRUE
: FALSE
;
346 static VOID
CheckPartitions(struct ExpansionBase
*ExpansionBase
, struct Library
*PartitionBase
, struct ExecBase
*SysBase
, struct BootNode
*bn
)
348 struct DeviceNode
*dn
= bn
->bn_DeviceNode
;
351 D(bug("CheckPartition('%b') handler = %x\n", dn
->dn_Name
, dn
->dn_SegList
));
353 /* If we already have filesystem handler, don't do anything */
354 if (dn
->dn_SegList
== BNULL
)
356 struct FileSysStartupMsg
*fssm
= BADDR(dn
->dn_Startup
);
358 if (fssm
&& fssm
->fssm_Device
)
360 struct PartitionHandle
*pt
= OpenRootPartition(AROS_BSTR_ADDR(fssm
->fssm_Device
), fssm
->fssm_Unit
);
364 /* don't check removable devices for partition tables */
365 if (!IsRemovable(SysBase
, pt
->bd
->ioreq
))
366 res
= CheckTables(ExpansionBase
, PartitionBase
, fssm
, pt
, SysBase
);
368 CloseRootPartition(pt
);
374 /* If no partitions were found for the DeviceNode, put it back */
375 Enqueue(&ExpansionBase
->MountList
, &bn
->bn_Node
);
378 /* Scan all partitions manually for additional volumes that can be mounted. */
379 void dosboot_BootScan(LIBBASETYPEPTR LIBBASE
)
383 /* If we have partition.library, we can look for partitions */
384 PartitionBase
= OpenLibrary("partition.library", 2);
388 * Remove the whole chain of BootNodes from the list and re-initialize it.
389 * We will insert new nodes into it, based on old ones.
390 * What is done here is safe as long is we don't move the list itself.
391 * ln_Succ of the last node in chain points to the lh_Tail of our list
392 * which always contains NULL.
394 struct BootNode
*bootNode
= (struct BootNode
*)LIBBASE
->bm_ExpansionBase
->MountList
.lh_Head
;
396 NEWLIST(&LIBBASE
->bm_ExpansionBase
->MountList
);
398 while (bootNode
->bn_Node
.ln_Succ
)
400 /* Keep ln_Succ because it can be clobbered by reinsertion */
401 struct BootNode
*nextNode
= (struct BootNode
*)bootNode
->bn_Node
.ln_Succ
;
403 CheckPartitions(LIBBASE
->bm_ExpansionBase
, PartitionBase
, SysBase
, bootNode
);
407 CloseLibrary(PartitionBase
);