ATA Work volume in now formatted again.
[AROS.git] / rom / dosboot / bootscan.c
blob68a2a7befab62db0eb2d7d5f45f806076f04cf1f
1 /*
2 Copyright © 1995-2011, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: Discover all mountable partitions
6 Lang: english
7 */
9 #include <string.h>
10 #include <stdlib.h>
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)
41 /*
42 * TODO: Check if DOSType lookup in partition.library really works
43 * and remove this table and related code.
45 static const struct _pt {
46 IPTR part,type;
47 } PartTypes[] = {
48 { 0x01, AROS_MAKE_ID('F','A','T','\0') }, /* DOS 12-bit FAT */
49 { 0x04, AROS_MAKE_ID('F','A','T','\0') }, /* DOS 16-bit FAT (up to 32M) */
50 { 0x06, AROS_MAKE_ID('F','A','T','\0') }, /* DOS 16-bit FAT (over 32M) */
51 { 0x07, AROS_MAKE_ID('N','T','F','S') }, /* Windows NT NTFS */
52 { 0x0b, AROS_MAKE_ID('F','A','T','\0') }, /* W95 FAT32 */
53 { 0x0c, AROS_MAKE_ID('F','A','T','\0') }, /* W95 LBA FAT32 */
54 { 0x0e, AROS_MAKE_ID('F','A','T','\0') }, /* 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 */
70 { 0, 0 }
73 static IPTR MatchPartType(UBYTE PartType)
75 int i;
76 IPTR type = 0;
78 for (i = 0; i < (sizeof(PartTypes) / sizeof(struct _pt)); i++)
80 if ((IPTR)PartType == PartTypes[i].part)
82 type = PartTypes[i].type;
83 break;
86 return type;
89 static ULONG GetOffset(struct Library *PartitionBase, struct PartitionHandle *ph)
91 IPTR tags[3];
92 struct DosEnvec de;
93 ULONG offset = 0;
95 tags[0] = PT_DOSENVEC;
96 tags[1] = (IPTR)&de;
97 tags[2] = TAG_DONE;
98 ph = ph->root;
99 while (ph->root)
101 GetPartitionAttrs(ph, (struct TagItem *)tags);
102 offset += de.de_LowCyl * de.de_Surfaces * de.de_BlocksPerTrack;
103 ph = ph->root;
105 return offset;
108 static VOID AddPartitionVolume(struct ExpansionBase *ExpansionBase, struct Library *PartitionBase,
109 struct FileSysStartupMsg *fssm, struct PartitionHandle *table,
110 struct PartitionHandle *pn, struct ExecBase *SysBase)
112 UBYTE name[32];
113 ULONG i, blockspercyl;
114 const struct PartitionAttribute *attrs;
115 IPTR tags[7];
116 IPTR pp[4 + DE_BOOTBLOCKS + 1] = { };
117 struct DeviceNode *devnode;
118 struct PartitionType ptyp;
119 LONG ppos;
120 TEXT *devname;
121 LONG bootable;
122 ULONG pttype = PHPTT_UNKNOWN;
123 BOOL appended, changed;
127 * TODO: Try to locate RDB filesystem for this volume and make it bootable.
128 * Use FindFileSystem() and AddBootFileSystem() for this.
131 D(bug("[Boot] AddPartitionVolume\n"));
132 GetPartitionTableAttrsTags(table, PTT_TYPE, &pttype, TAG_DONE);
134 attrs = QueryPartitionAttrs(table);
135 while ((attrs->attribute != PTA_DONE) && (attrs->attribute != PTA_NAME))
136 attrs++; /* look for name attr */
137 if (attrs->attribute != PTA_DONE)
139 D(bug("[Boot] RDB/GPT partition\n"));
141 /* partition has a name => RDB/GPT partition */
142 tags[0] = PT_NAME;
143 tags[1] = (IPTR)name;
144 tags[2] = PT_DOSENVEC;
145 tags[3] = (IPTR)&pp[4];
146 tags[4] = PT_BOOTABLE;
147 tags[5] = (IPTR)&bootable;
148 tags[6] = TAG_DONE;
149 GetPartitionAttrs(pn, (struct TagItem *)tags);
150 D(bug("[Boot] Partition name: %s bootable: %d\n", name, bootable));
153 * CHECKME: This should not be needed at all. Partition.library knows what it does,
154 * and it knows DosEnvec size. RDB partitions should have complete DosEnvec. GPT
155 * partitions (also processed here) have only fields up to de_DosType filled in,
156 * and this is correctly reflected in the DosEnvec.
157 pp[4 + DE_TABLESIZE] = DE_BOOTBLOCKS;
160 else
162 D(bug("[Boot] MBR partition\n"));
164 /* partition doesn't have a name => MBR partition */
165 tags[0] = PT_POSITION;
166 tags[1] = (IPTR)&ppos;
167 tags[2] = PT_TYPE;
168 tags[3] = (IPTR)&ptyp;
169 tags[4] = PT_DOSENVEC;
170 tags[5] = (IPTR)&pp[4];
171 tags[6] = TAG_DONE;
172 GetPartitionAttrs(pn, (struct TagItem *)tags);
173 bootable = TRUE;
175 /* make the name */
176 devname = AROS_BSTR_ADDR(fssm->fssm_Device);
177 for (i = 0; i < 26; i++)
179 if (*devname == '.' || *devname == '\0')
180 break;
181 name[i] = (UBYTE)uppercase(*devname);
182 devname++;
184 if ((fssm->fssm_Unit / 10))
185 name[i++] = '0' + (UBYTE)(fssm->fssm_Unit / 10);
186 name[i++] = '0' + (UBYTE)(fssm->fssm_Unit % 10);
187 name[i++] = 'P';
188 if (table->table->type == PHPTT_EBR)
189 ppos += 4;
190 if ((ppos / 10))
191 name[i++] = '0' + (UBYTE)(ppos / 10);
192 name[i++] = '0' + (UBYTE)(ppos % 10);
193 name[i] = '\0';
194 D(bug("[Boot] Partition name: %s type: %u\n", name, ptyp.id[0]));
197 * FIXME: These MBR-related DosEnvec patches should already be correctly done
198 * by partition.library. Test this and remove the unneeded code from here.
201 /* set DOSTYPE based on the partition type */
202 pp[4 + DE_DOSTYPE] = MatchPartType(ptyp.id[0]);
203 /* set some common DOSENV fields */
204 pp[4 + DE_TABLESIZE] = DE_BOOTBLOCKS;
205 pp[4 + DE_NUMBUFFERS] = 20;
206 pp[4 + DE_BUFMEMTYPE] = MEMF_PUBLIC;
207 /* set some fs specific fields */
208 switch(ptyp.id[0])
210 case 0x2c: /* OFS */
211 case 0x2d: /* FFS */
212 case 0x2e: /* FFS I */
213 case 0x2f: /* SFS */
214 pp[4 + DE_SECSPERBLOCK] = 1;
215 pp[4 + DE_RESERVEDBLKS] = 2;
216 pp[4 + DE_BOOTBLOCKS] = 2;
217 break;
221 if ((pp[4 + DE_TABLESIZE] < DE_DOSTYPE) || (pp[4 + DE_DOSTYPE] == 0))
224 * partition.library reports DosType == 0 for unknown filesystems.
225 * However dos.library will mount such DeviceNodes using rn_DefaultHandler
226 * (FFS). This is done for compatibility with 3rd party expansion ROMs.
227 * Here we ignore partitions with DosType == 0 and won't enter them into
228 * mountlist.
230 D(bug("[Boot] Unknown DosType for %s, skipping partition\n"));
231 return;
234 if (pttype != PHPTT_RDB)
237 * Only RDB partitions can store the complete DosEnvec.
238 * For other partition types partition.library puts some defaults
239 * into these fields, however they do not have anything to do with
240 * real values, which are device-dependent.
241 * However, the device itself knows them. Here we inherit these settings
242 * from the original DeviceNode which represents the whole drive.
243 * Note that we don't change DosEnvec size. If these fields are not included,
244 * it will stay this way.
245 * Copy members only if they are present in device's DosEnvec.
247 struct DosEnvec *devenv = BADDR(fssm->fssm_Environ);
249 if (devenv->de_TableSize >= DE_MAXTRANSFER)
251 pp[4 + DE_MAXTRANSFER] = devenv->de_MaxTransfer;
253 if (devenv->de_TableSize >= DE_MASK)
254 pp[4 + DE_MASK] = devenv->de_Mask;
259 * BHFormat complains if this bit is not set, and it's really wrong to have it unset.
260 * So we explicitly set it here. Pavel Fedin <pavel.fedin@mail.ru>
262 pp[4 + DE_BUFMEMTYPE] |= MEMF_PUBLIC;
264 pp[0] = (IPTR)name;
265 pp[1] = (IPTR)AROS_BSTR_ADDR(fssm->fssm_Device);
266 pp[2] = fssm->fssm_Unit;
267 pp[3] = fssm->fssm_Flags;
269 i = GetOffset(PartitionBase, pn);
270 blockspercyl = pp[4 + DE_BLKSPERTRACK] * pp[4 + DE_NUMHEADS];
271 if (i % blockspercyl != 0)
273 D(bug("[Boot] Start block of subtable not on cylinder boundary: "
274 "%ld (Blocks per Cylinder = %ld)\n", i, blockspercyl));
275 return;
277 i /= blockspercyl;
278 pp[4 + DE_LOWCYL] += i;
279 pp[4 + DE_HIGHCYL] += i;
281 /* Append .n if same device name already exists */
282 appended = FALSE;
283 changed = TRUE;
284 while (changed)
286 struct BootNode *bn;
287 changed = FALSE;
288 ForeachNode(&ExpansionBase->MountList, bn)
290 if (stricmp(AROS_BSTR_ADDR(((struct DeviceNode*)bn->bn_DeviceNode)->dn_Name), name) == 0)
292 if (!appended)
293 strcat(name, ".1");
294 else
295 name[strlen(name) - 1]++;
296 appended = TRUE;
297 changed = TRUE;
302 devnode = MakeDosNode(pp);
303 if (devnode != NULL) {
304 AddBootNode(bootable ? pp[4 + DE_BOOTPRI] : -128, ADNF_STARTPROC, devnode, NULL);
305 D(bug("[Boot] AddBootNode(%b, 0, 0x%p, NULL)\n", devnode->dn_Name, pp[4 + DE_DOSTYPE]));
306 return;
310 static BOOL CheckTables(struct ExpansionBase *ExpansionBase, struct Library *PartitionBase,
311 struct FileSysStartupMsg *fssm, struct PartitionHandle *table,
312 struct ExecBase *SysBase)
314 BOOL retval = FALSE;
315 struct PartitionHandle *ph;
317 /* Traverse partition tables recursively, and attempt to add a BootNode
318 for any non-subtable partitions found */
319 if (OpenPartitionTable(table) == 0)
321 ph = (struct PartitionHandle *)table->table->list.lh_Head;
322 while (ph->ln.ln_Succ)
324 /* Attempt to add partition to system if it isn't a subtable */
325 if (!CheckTables(ExpansionBase, PartitionBase, fssm, ph, SysBase))
326 AddPartitionVolume(ExpansionBase, PartitionBase, fssm, table,
327 ph, SysBase);
328 ph = (struct PartitionHandle *)ph->ln.ln_Succ;
330 retval = TRUE;
331 ClosePartitionTable(table);
333 return retval;
336 static VOID CheckPartitions(struct ExpansionBase *ExpansionBase, struct Library *PartitionBase, struct ExecBase *SysBase, struct BootNode *bn)
338 struct DeviceNode *dn = bn->bn_DeviceNode;
339 BOOL res = FALSE;
341 D(bug("CheckPartition('%b') handler = %x\n", dn->dn_Name, dn->dn_SegList));
343 /* If we already have filesystem handler, don't do anything */
344 if (dn->dn_SegList == BNULL)
346 struct FileSysStartupMsg *fssm = BADDR(dn->dn_Startup);
348 if (fssm && fssm->fssm_Device)
350 struct PartitionHandle *pt = OpenRootPartition(AROS_BSTR_ADDR(fssm->fssm_Device), fssm->fssm_Unit);
352 if (pt)
354 res = CheckTables(ExpansionBase, PartitionBase, fssm, pt, SysBase);
356 CloseRootPartition(pt);
361 if (!res)
362 /* If no partitions were found for the DeviceNode, put it back */
363 Enqueue(&ExpansionBase->MountList, &bn->bn_Node);
366 /* Scan all partitions manually for additional volumes that can be mounted. */
367 void dosboot_BootScan(LIBBASETYPEPTR LIBBASE)
369 APTR PartitionBase;
371 /* If we have partition.library, we can look for partitions */
372 PartitionBase = OpenLibrary("partition.library", 2);
373 if (PartitionBase)
376 * Remove the whole chain of BootNodes from the list and re-initialize it.
377 * We will insert new nodes into it, based on old ones.
378 * What is done here is safe as long as we don't move the list itself.
379 * ln_Succ of the last node in chain points to the lh_Tail of our list
380 * which always contains NULL.
382 struct BootNode *bootNode = (struct BootNode *)LIBBASE->bm_ExpansionBase->MountList.lh_Head;
384 NEWLIST(&LIBBASE->bm_ExpansionBase->MountList);
386 while (bootNode->bn_Node.ln_Succ)
388 /* Keep ln_Succ because it can be clobbered by reinsertion */
389 struct BootNode *nextNode = (struct BootNode *)bootNode->bn_Node.ln_Succ;
391 CheckPartitions(LIBBASE->bm_ExpansionBase, PartitionBase, SysBase, bootNode);
392 bootNode = nextNode;
395 CloseLibrary(PartitionBase);