revert between 56095 -> 55830 in arch
[AROS.git] / rom / dosboot / bootscan.c
blob0c1866b2f191b03f1f1740fec2db2568de12681d
1 /*
2 Copyright © 1995-2014, 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>
34 #include <clib/alib_protos.h>
36 #include LC_LIBDEFS_FILE
38 #include "dosboot_intern.h"
39 #include "../expansion/expansion_intern.h"
41 #define uppercase(x) ((x >= 'a' && x <= 'z') ? (x & 0xdf) : x)
43 static ULONG GetOffset(struct Library *PartitionBase, struct PartitionHandle *ph)
45 IPTR tags[3];
46 struct DosEnvec de;
47 ULONG offset = 0;
49 tags[0] = PT_DOSENVEC;
50 tags[1] = (IPTR)&de;
51 tags[2] = TAG_DONE;
52 ph = ph->root;
53 while (ph->root)
55 GetPartitionAttrs(ph, (struct TagItem *)tags);
56 offset += de.de_LowCyl * de.de_Surfaces * de.de_BlocksPerTrack;
57 ph = ph->root;
59 return offset;
62 static VOID AddPartitionVolume(struct ExpansionBase *ExpansionBase, struct Library *PartitionBase,
63 struct FileSysStartupMsg *fssm, struct PartitionHandle *table,
64 struct PartitionHandle *pn, struct ExecBase *SysBase)
66 UBYTE name[32];
67 ULONG i, blockspercyl;
68 const struct PartitionAttribute *attrs;
69 IPTR tags[7];
70 IPTR pp[4 + DE_BOOTBLOCKS + 1] = { };
71 struct DeviceNode *devnode;
72 LONG ppos;
73 TEXT *devname;
74 LONG bootable;
75 ULONG pttype = PHPTT_UNKNOWN;
76 BOOL appended, changed;
77 struct Node *fsnode;
79 D(bug("[Boot] AddPartitionVolume\n"));
80 GetPartitionTableAttrsTags(table, PTT_TYPE, &pttype, TAG_DONE);
82 attrs = QueryPartitionAttrs(table);
83 while ((attrs->attribute != TAG_DONE) && (attrs->attribute != PT_NAME))
84 attrs++; /* look for name attr */
86 if (attrs->attribute != TAG_DONE)
88 D(bug("[Boot] RDB/GPT partition\n"));
90 /* partition has a name => RDB/GPT partition */
91 tags[0] = PT_NAME;
92 tags[1] = (IPTR)name;
93 tags[2] = PT_DOSENVEC;
94 tags[3] = (IPTR)&pp[4];
95 tags[4] = PT_BOOTABLE;
96 tags[5] = (IPTR)&bootable;
97 tags[6] = TAG_DONE;
98 GetPartitionAttrs(pn, (struct TagItem *)tags);
100 D(bug("[Boot] Partition name: %s bootable: %d\n", name, bootable));
102 else
104 D(bug("[Boot] MBR/EBR partition\n"));
106 /* partition doesn't have a name => MBR/EBR partition */
107 tags[0] = PT_POSITION;
108 tags[1] = (IPTR)&ppos;
109 tags[2] = PT_DOSENVEC;
110 tags[3] = (IPTR)&pp[4];
111 tags[4] = TAG_DONE;
112 GetPartitionAttrs(pn, (struct TagItem *)tags);
115 * 'Active' is not the same as 'Bootable'. Theoretically we can set Active flag for multiple
116 * partitions, but this may screw up Microsoft system software which expects to see only one
117 * active partition.
119 bootable = TRUE;
121 /* make the name */
122 devname = AROS_BSTR_ADDR(fssm->fssm_Device);
123 for (i = 0; i < 26; i++)
125 if (*devname == '.' || *devname == '\0')
126 break;
127 name[i] = (UBYTE)uppercase(*devname);
128 devname++;
130 if ((fssm->fssm_Unit / 10))
131 name[i++] = '0' + (UBYTE)(fssm->fssm_Unit / 10);
132 name[i++] = '0' + (UBYTE)(fssm->fssm_Unit % 10);
133 name[i++] = 'P';
134 if (table->table->type == PHPTT_EBR)
135 ppos += 4;
136 if ((ppos / 10))
137 name[i++] = '0' + (UBYTE)(ppos / 10);
138 name[i++] = '0' + (UBYTE)(ppos % 10);
139 name[i] = '\0';
141 D(bug("[Boot] Partition name: %s\n", name));
144 if ((pp[4 + DE_TABLESIZE] < DE_DOSTYPE) || (pp[4 + DE_DOSTYPE] == 0))
147 * partition.library reports DosType == 0 for unknown filesystems.
148 * However dos.library will mount such DeviceNodes using rn_DefaultHandler
149 * (FFS). This is done for compatibility with 3rd party expansion ROMs.
150 * Here we ignore partitions with DosType == 0 and won't enter them into
151 * mountlist.
153 D(bug("[Boot] Unknown DosType for %s, skipping partition\n"));
154 return;
157 if (pttype != PHPTT_RDB)
160 * Only RDB partitions can store the complete DosEnvec.
161 * For other partition types partition.library puts some defaults
162 * into these fields, however they do not have anything to do with
163 * real values, which are device-dependent.
164 * However, the device itself knows them. Here we inherit these settings
165 * from the original DeviceNode which represents the whole drive.
166 * Note that we don't change DosEnvec size. If these fields are not included,
167 * it will stay this way.
168 * Copy members only if they are present in device's DosEnvec.
170 struct DosEnvec *devenv = BADDR(fssm->fssm_Environ);
172 if (devenv->de_TableSize >= DE_MAXTRANSFER)
174 pp[4 + DE_MAXTRANSFER] = devenv->de_MaxTransfer;
176 if (devenv->de_TableSize >= DE_MASK)
177 pp[4 + DE_MASK] = devenv->de_Mask;
182 * BHFormat complains if this bit is not set, and it's really wrong to have it unset.
183 * So we explicitly set it here. Pavel Fedin <pavel.fedin@mail.ru>
185 pp[4 + DE_BUFMEMTYPE] |= MEMF_PUBLIC;
187 pp[0] = (IPTR)name;
188 pp[1] = (IPTR)AROS_BSTR_ADDR(fssm->fssm_Device);
189 pp[2] = fssm->fssm_Unit;
190 pp[3] = fssm->fssm_Flags;
192 i = GetOffset(PartitionBase, pn);
193 blockspercyl = pp[4 + DE_BLKSPERTRACK] * pp[4 + DE_NUMHEADS];
194 if (i % blockspercyl != 0)
196 D(bug("[Boot] Start block of subtable not on cylinder boundary: "
197 "%ld (Blocks per Cylinder = %ld)\n", i, blockspercyl));
198 return;
200 i /= blockspercyl;
201 pp[4 + DE_LOWCYL] += i;
202 pp[4 + DE_HIGHCYL] += i;
204 /* Append .n if same device name already exists */
205 appended = FALSE;
206 changed = TRUE;
207 while (changed)
209 struct BootNode *bn;
210 changed = FALSE;
212 /* Note that we already have the mount list semaphore */
213 ForeachNode(&ExpansionBase->MountList, bn)
215 if (stricmp(AROS_BSTR_ADDR(((struct DeviceNode*)bn->bn_DeviceNode)->dn_Name), name) == 0)
217 if (!appended)
218 strcat(name, ".1");
219 else
220 name[strlen(name) - 1]++;
221 appended = TRUE;
222 changed = TRUE;
227 fsnode = FindFileSystem(table, FST_ID, pp[4 + DE_DOSTYPE], TAG_DONE);
228 if (fsnode) {
229 D(bug("[Boot] Found on-disk filesystem 0x%08x\n", pp[4 + DE_DOSTYPE]));
230 AddBootFileSystem(fsnode);
233 devnode = MakeDosNode(pp);
234 if (devnode != NULL) {
235 AddBootNode(bootable ? pp[4 + DE_BOOTPRI] : -128, ADNF_STARTPROC, devnode, NULL);
236 D(bug("[Boot] AddBootNode(%b, 0, 0x%p, NULL)\n", devnode->dn_Name, pp[4 + DE_DOSTYPE]));
237 return;
241 static BOOL CheckTables(struct ExpansionBase *ExpansionBase, struct Library *PartitionBase,
242 struct FileSysStartupMsg *fssm, struct PartitionHandle *table,
243 struct ExecBase *SysBase)
245 BOOL retval = FALSE;
246 struct PartitionHandle *ph;
248 /* Traverse partition tables recursively, and attempt to add a BootNode
249 for any non-subtable partitions found */
250 if (OpenPartitionTable(table) == 0)
252 ph = (struct PartitionHandle *)table->table->list.lh_Head;
253 while (ph->ln.ln_Succ)
255 /* Attempt to add partition to system if it isn't a subtable */
256 if (!CheckTables(ExpansionBase, PartitionBase, fssm, ph, SysBase))
257 AddPartitionVolume(ExpansionBase, PartitionBase, fssm, table,
258 ph, SysBase);
259 ph = (struct PartitionHandle *)ph->ln.ln_Succ;
261 retval = TRUE;
262 ClosePartitionTable(table);
264 return retval;
267 static VOID CheckPartitions(struct ExpansionBase *ExpansionBase, struct Library *PartitionBase, struct ExecBase *SysBase, struct BootNode *bn)
269 struct DeviceNode *dn = bn->bn_DeviceNode;
270 BOOL res = FALSE;
272 D(bug("CheckPartitions('%b') handler seglist = %x, handler = %s\n", dn->dn_Name,
273 dn->dn_SegList, AROS_BSTR_ADDR(dn->dn_Handler)));
275 /* Examples:
276 * ata.device registers a HDx device describing whole disk with no handler name and no seglist
277 * massstorage.class registers each partition giving it a handler name but not seglist
280 /* If we already have filesystem handler, don't do anything */
281 if (dn->dn_SegList == BNULL && dn->dn_Handler == BNULL)
283 struct FileSysStartupMsg *fssm = BADDR(dn->dn_Startup);
285 if (fssm && fssm->fssm_Device)
287 struct PartitionHandle *pt = OpenRootPartition(AROS_BSTR_ADDR(fssm->fssm_Device), fssm->fssm_Unit);
289 if (pt)
291 res = CheckTables(ExpansionBase, PartitionBase, fssm, pt, SysBase);
293 CloseRootPartition(pt);
298 if (!res)
300 /* If no partitions were found for the DeviceNode, put it back */
301 Remove(&bn->bn_Node);
302 Enqueue(&ExpansionBase->MountList, &bn->bn_Node);
306 /* Scan all partitions manually for additional volumes that can be mounted. */
307 void dosboot_BootScan(LIBBASETYPEPTR LIBBASE)
309 struct ExpansionBase *ExpansionBase = LIBBASE->bm_ExpansionBase;
310 APTR PartitionBase;
311 struct BootNode *bootNode, *temp;
312 struct List rootList;
314 /* If we have partition.library, we can look for partitions */
315 PartitionBase = OpenLibrary("partition.library", 2);
316 if (PartitionBase)
318 ObtainSemaphore(&IntExpBase(ExpansionBase)->BootSemaphore);
320 /* Transfer all bootnodes in the mountlist into a temporary list.
321 The assumption is that all bootnodes created before now represent
322 entire disks */
323 NewList(&rootList);
324 while ((temp = (struct BootNode *)RemHead(&ExpansionBase->MountList))
325 != NULL)
326 AddTail(&rootList, (struct Node *) temp);
328 ForeachNodeSafe (&rootList, bootNode, temp)
329 CheckPartitions(ExpansionBase, PartitionBase, SysBase,
330 bootNode);
332 ReleaseSemaphore(&IntExpBase(ExpansionBase)->BootSemaphore);
334 CloseLibrary(PartitionBase);