- Don't access list nodes after their memory has been freed.
[AROS.git] / rom / dosboot / bootstrap.c
blobc7377804ad4ecad2f4bf9689722d7227d2c30110
1 /*
2 Copyright © 1995-2011, 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"
40 #ifdef __mc68000
42 /* These two functions are implemented in arch/m68k/all/dosboot/bootcode.c */
44 extern VOID_FUNC CallBootBlockCode(APTR bootcode, struct IOStdReq *io, struct ExpansionBase *ExpansionBase);
45 extern void dosboot_BootPoint(struct BootNode *bn);
47 #else
49 #define CallBootBlockCode(bootcode, io, ExpansionBase) NULL
50 #define dosboot_BootPoint(bn)
52 #endif
54 static BOOL GetBootNodeDeviceUnit(struct BootNode *bn, BPTR *device, IPTR *unit, ULONG *bootblocks)
56 struct DeviceNode *dn;
57 struct FileSysStartupMsg *fssm;
58 struct DosEnvec *de;
60 if (bn == NULL)
61 return FALSE;
63 dn = bn->bn_DeviceNode;
64 if (dn == NULL)
65 return FALSE;
67 fssm = BADDR(dn->dn_Startup);
68 if (fssm == NULL)
69 return FALSE;
71 *unit = fssm->fssm_Unit;
72 *device = fssm->fssm_Device;
74 de = BADDR(fssm->fssm_Environ);
75 /* Following check from Guru Book */
76 if (de == NULL || (de->de_TableSize & 0xffffff00) != 0 || de->de_TableSize < DE_BOOTBLOCKS)
77 return FALSE;
78 *bootblocks = de->de_BootBlocks * de->de_SizeBlock * sizeof(ULONG);
79 if (*bootblocks == 0)
80 return FALSE;
81 return TRUE;
84 static BOOL BootBlockCheckSum(UBYTE *bootblock, ULONG bootblock_size)
86 ULONG crc = 0, crc2 = 0;
87 UWORD i;
89 for (i = 0; i < bootblock_size; i += 4) {
90 ULONG v = AROS_LONG2BE(*(ULONG*)(bootblock + i));
91 if (i == 4) {
92 crc2 = v;
93 v = 0;
95 if (crc + v < crc)
96 crc++;
97 crc += v;
99 crc ^= 0xffffffff;
100 D(bug("[Strap] bootblock %08x checksum %s (%08x %08x)\n",
101 AROS_LONG2BE(*(ULONG*)bootblock), crc == crc2 ? "ok" : "error", crc, crc2));
102 return crc == crc2;
105 static BOOL BootBlockCheck(UBYTE *bootblock, ULONG bootblock_size)
107 struct FileSysResource *fsr;
108 struct FileSysEntry *fse;
109 ULONG dostype;
111 if (!BootBlockCheckSum(bootblock, bootblock_size))
112 return FALSE;
113 if (!(fsr = OpenResource("FileSystem.resource")))
114 return FALSE;
115 dostype = AROS_LONG2BE(*(ULONG*)bootblock);
116 ForeachNode(&fsr->fsr_FileSysEntries, fse) {
117 if (fse->fse_DosType == dostype)
118 return TRUE;
120 D(bug("[Strap] unknown bootblock dostype %08x\n", dostype));
121 return FALSE;
124 static inline void SetBootNodeDosType(struct BootNode *bn, ULONG dostype)
126 struct DeviceNode *dn;
127 struct FileSysStartupMsg *fssm;
128 struct DosEnvec *de;
130 dn = bn->bn_DeviceNode;
131 if (dn == NULL)
132 return;
134 fssm = BADDR(dn->dn_Startup);
135 if (fssm == NULL)
136 return;
138 de = BADDR(fssm->fssm_Environ);
139 if (de == NULL || de->de_TableSize < DE_DOSTYPE)
140 return;
142 de->de_DosType = dostype;
145 /* Returns TRUE if it was a BootBlock style, but couldn't
146 * be booted, FALSE if not a BootBlock style, and doesn't
147 * return at all on a successful boot.
149 static BOOL dosboot_BootBlock(struct BootNode *bn, struct ExpansionBase *ExpansionBase)
151 ULONG bootblock_size;
152 struct MsgPort *msgport;
153 struct IOStdReq *io;
154 BPTR device;
155 IPTR unit;
156 VOID_FUNC init = NULL;
157 UBYTE *buffer;
159 if (!GetBootNodeDeviceUnit(bn, &device, &unit, &bootblock_size))
160 return FALSE;
162 D(bug("%s: Probing for boot block on %b.%d\n", __func__, device, unit));
163 /* memf_chip not required but more compatible with old bootblocks */
164 buffer = AllocMem(bootblock_size, MEMF_CHIP);
165 if (buffer != NULL)
167 D(bug("[Strap] bootblock address %p\n", buffer));
168 if ((msgport = CreateMsgPort()))
170 if ((io = CreateIORequest(msgport, sizeof(struct IOStdReq))))
172 if (!OpenDevice(AROS_BSTR_ADDR(device), unit, (struct IORequest*)io, 0))
174 /* Read the device's boot block */
175 io->io_Length = bootblock_size;
176 io->io_Data = buffer;
177 io->io_Offset = 0;
178 io->io_Command = CMD_READ;
179 D(bug("[Strap] %b.%d bootblock read (%d bytes)\n", device, unit, bootblock_size));
180 DoIO((struct IORequest*)io);
182 if (io->io_Error == 0)
184 D(bug("[Strap] %b.%d bootblock read to %p ok\n", device, unit, buffer));
185 if (BootBlockCheck(buffer, bootblock_size))
187 SetBootNodeDosType(bn, AROS_LONG2BE(*(LONG *)buffer));
188 CacheClearE(buffer, bootblock_size, CACRF_ClearI|CACRF_ClearD);
189 init = CallBootBlockCode(buffer + 12, io, ExpansionBase);
191 else
193 D(bug("[Strap] Not a valid bootblock\n"));
195 } else {
196 D(bug("[Strap] io_Error %d\n", io->io_Error));
198 io->io_Command = TD_MOTOR;
199 io->io_Length = 0;
200 DoIO((struct IORequest*)io);
201 CloseDevice((struct IORequest *)io);
203 DeleteIORequest((struct IORequest*)io);
205 DeleteMsgPort(msgport);
207 FreeMem(buffer, bootblock_size);
210 if (init != NULL)
212 D(bug("calling bootblock loaded code at %p\n", init));
215 * This is actually rt_Init calling convention for non-autoinit residents.
216 * Workbench floppy bootblocks return a pointer to dos.library init routine,
217 * and it needs SysBase in A6.
218 * We don't close boot screen and libraries here. We will close them after
219 * dos.library is succesfully initialized, using a second RTF_AFTERDOS ROMTag.
220 * This is needed because dos.library contains the second part of "bootable"
221 * test, trying to mount a filesystem and read the volume.
222 * We hope it won't do any harm for NDOS game disks.
224 AROS_UFC3NR(void, init,
225 AROS_UFCA(APTR, NULL, D0),
226 AROS_UFCA(BPTR, BNULL, A0),
227 AROS_UFCA(struct ExecBase *, SysBase, A6));
230 #ifdef __mc68000
231 /* Device *was* BootBlock style, but couldn't boot. */
232 return TRUE;
233 #else
234 /* Device *was* BootBlock style, but couldn't boot.
235 * Non-m68k will try as DOS Boot anyway!
237 return FALSE;
238 #endif
241 /* Attempt to boot via dos.library directly
243 static inline void dosboot_BootDos(void)
245 struct Resident *DOSResident;
247 /* Initialize dos.library manually. This is what Workbench floppy bootblock do. */
248 DOSResident = FindResident( "dos.library" );
250 if( DOSResident == NULL )
252 Alert( AT_DeadEnd | AG_OpenLib | AN_BootStrap | AO_DOSLib );
255 /* InitResident() of dos.library will not return on success. */
256 InitResident( DOSResident, BNULL );
260 /* Attempt to boot, first from the BootNode boot blocks,
261 * then via the DOS handlers
263 LONG dosboot_BootStrap(LIBBASETYPEPTR LIBBASE)
265 struct BootNode *bn;
266 int i, nodes;
269 * Try to boot from any device in the boot list,
270 * highest priority first.
272 ListLength(&LIBBASE->bm_ExpansionBase->MountList, nodes);
273 for (i = 0; i < nodes; i++)
275 bn = (struct BootNode *)GetHead(&LIBBASE->bm_ExpansionBase->MountList);
277 if (bn->bn_Node.ln_Type != NT_BOOTNODE ||
278 bn->bn_Node.ln_Pri <= -128 ||
279 bn->bn_DeviceNode == NULL)
281 D(bug("%s: Ignoring %p, not a bootable node\n", __func__, bn));
282 REMOVE(bn);
283 ADDTAIL(&LIBBASE->bm_ExpansionBase->MountList, bn);
284 continue;
287 /* For each attempt, this node is at the head
288 * of the MountList, so that DOS will try to
289 * use it as SYS: if the strap works
292 /* First try as a BootBlock.
293 * dosboot_BootBlock returns TRUE if it *was*
294 * a BootBlock device, but couldn't be booted.
295 * Returns FALSE if not a bootblock device,
296 * and doesn't return at all if the bootblock
297 * was successful.
299 D(bug("%s: Attempting %b as BootBlock\n",__func__, ((struct DeviceNode *)bn->bn_DeviceNode)->dn_Name));
300 if (!dosboot_BootBlock(bn, LIBBASE->bm_ExpansionBase)) {
301 /* Then as a BootPoint node */
302 D(bug("%s: Attempting %b as BootPoint\n", __func__, ((struct DeviceNode *)bn->bn_DeviceNode)->dn_Name));
303 dosboot_BootPoint(bn);
305 /* And finally with DOS */
306 D(bug("%s: Attempting %b with DOS\n", __func__, ((struct DeviceNode *)bn->bn_DeviceNode)->dn_Name));
307 dosboot_BootDos();
310 /* Didn't work. Next! */
311 D(bug("%s: DeviceNode %b (%d) was not bootable\n", __func__,
312 ((struct DeviceNode *)bn->bn_DeviceNode)->dn_Name,
313 bn->bn_Node.ln_Pri));
315 REMOVE(bn);
316 ADDTAIL(&LIBBASE->bm_ExpansionBase->MountList, bn);
319 D(bug("%s: No BootBlock, BootPoint, or BootDos nodes found\n",__func__));
321 /* At this point we now know that we were unable to
322 * strap any bootable devices.
325 return ERROR_NO_DISK;