Exec locks provides means to protect exec's lists from beeing accessed. The lock...
[AROS.git] / rom / dosboot / bootstrap.c
blob48334f7b46bb10a1d115629ea91d9401f21dd314
1 /*
2 Copyright © 1995-2016, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: Boot AROS
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 <proto/dos.h>
36 #include LC_LIBDEFS_FILE
38 #include "dosboot_intern.h"
39 #include "../expansion/expansion_intern.h"
41 #ifdef __mc68000
43 /* These two functions are implemented in arch/m68k/all/dosboot/bootcode.c */
45 extern VOID_FUNC CallBootBlockCode(APTR bootcode, struct IOStdReq *io, struct ExpansionBase *ExpansionBase);
46 extern void dosboot_BootPoint(struct BootNode *bn);
48 #else
50 #define CallBootBlockCode(bootcode, io, ExpansionBase) NULL
51 #define dosboot_BootPoint(bn)
53 #endif
55 static BOOL GetBootNodeDeviceUnit(struct BootNode *bn, BPTR *device, IPTR *unit, ULONG *bootblocks)
57 struct DeviceNode *dn;
58 struct FileSysStartupMsg *fssm;
59 struct DosEnvec *de;
61 if (bn == NULL)
62 return FALSE;
64 dn = bn->bn_DeviceNode;
65 if (dn == NULL)
66 return FALSE;
68 fssm = BADDR(dn->dn_Startup);
69 if (fssm == NULL)
70 return FALSE;
72 *unit = fssm->fssm_Unit;
73 *device = fssm->fssm_Device;
75 de = BADDR(fssm->fssm_Environ);
76 /* Following check from Guru Book */
77 if (de == NULL || (de->de_TableSize & 0xffffff00) != 0 || de->de_TableSize < DE_BOOTBLOCKS)
78 return FALSE;
79 *bootblocks = de->de_BootBlocks * de->de_SizeBlock * sizeof(ULONG);
80 if (*bootblocks == 0)
81 return FALSE;
82 return TRUE;
85 static BOOL BootBlockCheckSum(UBYTE *bootblock, ULONG bootblock_size)
87 ULONG crc = 0, crc2 = 0;
88 UWORD i;
90 for (i = 0; i < bootblock_size; i += 4) {
91 ULONG v = AROS_LONG2BE(*(ULONG*)(bootblock + i));
92 if (i == 4) {
93 crc2 = v;
94 v = 0;
96 if (crc + v < crc)
97 crc++;
98 crc += v;
100 crc ^= 0xffffffff;
101 D(bug("[Strap] bootblock %08x checksum %s (%08x %08x)\n",
102 AROS_LONG2BE(*(ULONG*)bootblock), crc == crc2 ? "ok" : "error", crc, crc2));
103 return crc == crc2;
106 static BOOL BootBlockCheck(UBYTE *bootblock, ULONG bootblock_size)
108 struct FileSysResource *fsr;
109 struct FileSysEntry *fse;
110 ULONG dostype;
112 if (!BootBlockCheckSum(bootblock, bootblock_size))
113 return FALSE;
114 if (!(fsr = OpenResource("FileSystem.resource")))
115 return FALSE;
116 dostype = AROS_LONG2BE(*(ULONG*)bootblock);
117 ForeachNode(&fsr->fsr_FileSysEntries, fse) {
118 if (fse->fse_DosType == dostype)
119 return TRUE;
121 D(bug("[Strap] unknown bootblock dostype %08x\n", dostype));
122 return FALSE;
125 static inline void SetBootNodeDosType(struct BootNode *bn, ULONG dostype)
127 struct DeviceNode *dn;
128 struct FileSysStartupMsg *fssm;
129 struct DosEnvec *de;
131 dn = bn->bn_DeviceNode;
132 if (dn == NULL)
133 return;
135 fssm = BADDR(dn->dn_Startup);
136 if (fssm == NULL)
137 return;
139 de = BADDR(fssm->fssm_Environ);
140 if (de == NULL || de->de_TableSize < DE_DOSTYPE)
141 return;
143 de->de_DosType = dostype;
146 /* Returns TRUE if it was a BootBlock style, but couldn't
147 * be booted, FALSE if not a BootBlock style, and doesn't
148 * return at all on a successful boot.
150 static BOOL dosboot_BootBlock(struct BootNode *bn, struct ExpansionBase *ExpansionBase)
152 ULONG bootblock_size;
153 struct MsgPort *msgport;
154 struct IOStdReq *io;
155 BPTR device;
156 IPTR unit;
157 VOID_FUNC init = NULL;
158 UBYTE *buffer;
160 if (!GetBootNodeDeviceUnit(bn, &device, &unit, &bootblock_size))
161 return FALSE;
163 D(bug("%s: Probing for boot block on %b.%d\n", __func__, device, unit));
164 /* memf_chip not required but more compatible with old bootblocks */
165 buffer = AllocMem(bootblock_size, MEMF_CHIP);
166 if (buffer != NULL)
168 D(bug("[Strap] bootblock address %p\n", buffer));
169 if ((msgport = CreateMsgPort()))
171 if ((io = CreateIORequest(msgport, sizeof(struct IOStdReq))))
173 if (!OpenDevice(AROS_BSTR_ADDR(device), unit, (struct IORequest*)io, 0))
175 /* Read the device's boot block */
176 io->io_Length = bootblock_size;
177 io->io_Data = buffer;
178 io->io_Offset = 0;
179 io->io_Command = CMD_READ;
180 D(bug("[Strap] %b.%d bootblock read (%d bytes)\n", device, unit, bootblock_size));
181 DoIO((struct IORequest*)io);
183 if (io->io_Error == 0)
185 D(bug("[Strap] %b.%d bootblock read to %p ok\n", device, unit, buffer));
186 if (BootBlockCheck(buffer, bootblock_size))
188 SetBootNodeDosType(bn, AROS_LONG2BE(*(LONG *)buffer));
189 CacheClearE(buffer, bootblock_size, CACRF_ClearI|CACRF_ClearD);
190 init = CallBootBlockCode(buffer + 12, io, ExpansionBase);
192 else
194 D(bug("[Strap] Not a valid bootblock\n"));
196 } else {
197 D(bug("[Strap] io_Error %d\n", io->io_Error));
199 io->io_Command = TD_MOTOR;
200 io->io_Length = 0;
201 DoIO((struct IORequest*)io);
202 CloseDevice((struct IORequest *)io);
204 DeleteIORequest((struct IORequest*)io);
206 DeleteMsgPort(msgport);
208 FreeMem(buffer, bootblock_size);
211 if (init != NULL)
213 D(bug("calling bootblock loaded code at %p\n", init));
216 * This is actually rt_Init calling convention for non-autoinit residents.
217 * Workbench floppy bootblocks return a pointer to dos.library init routine,
218 * and it needs SysBase in A6.
219 * We don't close boot screen and libraries here. We will close them after
220 * dos.library is successfully initialized, using a second RTF_AFTERDOS ROMTag.
221 * This is needed because dos.library contains the second part of "bootable"
222 * test, trying to mount a filesystem and read the volume.
223 * We hope it won't do any harm for NDOS game disks.
225 AROS_UFC3NR(void, init,
226 AROS_UFCA(APTR, NULL, D0),
227 AROS_UFCA(BPTR, BNULL, A0),
228 AROS_UFCA(struct ExecBase *, SysBase, A6));
231 #ifdef __mc68000
232 /* Device *was* BootBlock style, but couldn't boot. */
233 return TRUE;
234 #else
235 /* Device *was* BootBlock style, but couldn't boot.
236 * Non-m68k will try as DOS Boot anyway!
238 return FALSE;
239 #endif
242 /* Attempt to boot via dos.library directly
244 static inline void dosboot_BootDos(void)
246 struct Resident *DOSResident;
248 /* Initialize dos.library manually. This is what Workbench floppy bootblocks do. */
249 DOSResident = FindResident( "dos.library" );
251 if( DOSResident == NULL )
253 Alert( AT_DeadEnd | AG_OpenLib | AN_BootStrap | AO_DOSLib );
256 /* InitResident() of dos.library will not return on success. */
257 InitResident( DOSResident, BNULL );
261 /* Attempt to boot, first from the BootNode boot blocks,
262 * then via the DOS handlers
264 LONG dosboot_BootStrap(LIBBASETYPEPTR LIBBASE)
266 struct ExpansionBase *ExpansionBase = LIBBASE->bm_ExpansionBase;
267 struct BootNode *bn;
268 int i, nodes;
271 * Try to boot from any device in the boot list,
272 * highest priority first.
274 ListLength(&ExpansionBase->MountList, nodes);
275 for (i = 0; i < nodes; i++)
277 bn = (struct BootNode *)GetHead(&ExpansionBase->MountList);
279 if (bn->bn_Node.ln_Type != NT_BOOTNODE ||
280 bn->bn_Node.ln_Pri <= -128 ||
281 bn->bn_DeviceNode == NULL)
283 D(bug("%s: Ignoring %p, not a bootable node\n", __func__, bn));
284 ObtainSemaphore(&IntExpBase(ExpansionBase)->BootSemaphore);
285 REMOVE(bn);
286 ADDTAIL(&ExpansionBase->MountList, bn);
287 ReleaseSemaphore(&IntExpBase(ExpansionBase)->BootSemaphore);
288 continue;
291 /* For each attempt, this node is at the head
292 * of the MountList, so that DOS will try to
293 * use it as SYS: if the strap works
296 /* First try as a BootBlock.
297 * dosboot_BootBlock returns TRUE if it *was*
298 * a BootBlock device, but couldn't be booted.
299 * Returns FALSE if not a bootblock device,
300 * and doesn't return at all if the bootblock
301 * was successful.
303 D(bug("%s: Attempting %b as BootBlock\n",__func__, ((struct DeviceNode *)bn->bn_DeviceNode)->dn_Name));
304 if (!dosboot_BootBlock(bn, ExpansionBase)) {
305 /* Then as a BootPoint node */
306 D(bug("%s: Attempting %b as BootPoint\n", __func__, ((struct DeviceNode *)bn->bn_DeviceNode)->dn_Name));
307 dosboot_BootPoint(bn);
309 /* And finally with DOS */
310 D(bug("%s: Attempting %b with DOS\n", __func__, ((struct DeviceNode *)bn->bn_DeviceNode)->dn_Name));
311 dosboot_BootDos();
314 /* Didn't work. Next! */
315 D(bug("%s: DeviceNode %b (%d) was not bootable\n", __func__,
316 ((struct DeviceNode *)bn->bn_DeviceNode)->dn_Name,
317 bn->bn_Node.ln_Pri));
319 ObtainSemaphore(&IntExpBase(ExpansionBase)->BootSemaphore);
320 REMOVE(bn);
321 ADDTAIL(&ExpansionBase->MountList, bn);
322 ReleaseSemaphore(&IntExpBase(ExpansionBase)->BootSemaphore);
325 D(bug("%s: No BootBlock, BootPoint, or BootDos nodes found\n",__func__));
327 /* At this point we now know that we were unable to
328 * strap any bootable devices.
331 return ERROR_NO_DISK;