Chameleon

Chameleon Svn Source Tree

Root/branches/valv/AnVAL/i386/boot2/mboot.c

1/*
2 File added by David F. Elliott <dfe@cox.net> on 2007/06/26
3*/
4
5#include "libsaio.h"
6#include "boot.h"
7#include "bootstruct.h"
8
9#include "mboot.h"
10
11int multiboot_timeout=0;
12int multiboot_timeout_set=0;
13int multiboot_partition=0;
14int multiboot_partition_set=0;
15
16// Global multiboot info, if using multiboot.
17struct multiboot_info *gMI;
18
19extern void continue_at_low_address(void);
20
21// prototype hi_multiboot and keep its implementation below multiboot_to_boot
22// to ensure that it doesn't get inlined by the compiler
23// We don't want it inlined because we specifically want the stack frame
24// pointer to be as high as possible and the hi_multiboot function
25// copies multiboot_info onto its stack.
26uint32_t hi_multiboot(int multiboot_magic, struct multiboot_info *mi_orig);
27// prototype dochainload for the same reason.
28void dochainload();
29
30#define OFFSET_1MEG 0x100000
31#define BAD_BOOT_DEVICE 0xffffffff
32
33// This assumes that the address of the first argument to the function will
34// be exactly 4 bytes above the address of the return address.
35// It is intended to be used as an lvalue with a statement like this -= OFFSET_1MEG;
36#define RETURN_ADDRESS_USING_FIRST_ARG(arg) \
37 (*(uint32_t*)((char*)&(arg) - 4))
38
39#define FIX_RETURN_ADDRESS_USING_FIRST_ARG(arg) \
40 RETURN_ADDRESS_USING_FIRST_ARG(arg) -= OFFSET_1MEG
41
42extern void jump_to_chainbooter();
43extern unsigned char chainbootdev;
44extern unsigned char chainbootflag;
45
46void chainLoad();
47void waitThenReload();
48
49int multibootRamdiskReadBytes( int biosdev, unsigned int blkno,
50 unsigned int byteoff,
51 unsigned int byteCount, void * buffer );
52int multiboot_get_ramdisk_info(int biosdev, struct driveInfo *dip);
53static long multiboot_LoadExtraDrivers(FileLoadDrivers_t FileLoadDrivers_p);
54
55// Starts off in the multiboot context 1 MB high but eventually gets into low memory
56// and winds up with a bootdevice in eax which is all that boot() wants
57// This lets the stack pointer remain very high.
58// If we were to call boot directly from multiboot then the whole multiboot_info
59// would be on the stack which would possibly be using way too much stack.
60void multiboot_to_boot(int multiboot_magic, struct multiboot_info *mi_orig)
61{
62 uint32_t bootdevice = hi_multiboot(multiboot_magic, mi_orig);
63 if(bootdevice != BAD_BOOT_DEVICE)
64 {
65 // boot only returns to do a chain load.
66 for(;;)
67 { // NOTE: boot only uses the last byte (the drive number)
68 common_boot(bootdevice);
69 if(chainbootflag)
70 chainLoad();
71 else
72 waitThenReload();
73 }
74 }
75 // Avoid returning to high-memory address which isn't valid in the segment
76 // we are now in.
77 // Calling sleep() ensures the user ought to be able to use Ctrl+Alt+Del
78 // because the BIOS will have interrupts on.
79 for(;;)
80 sleep(10);
81 // NOTE: *IF* we needed to return we'd have to fix up our return address to
82 // be in low memory using the same trick as below.
83 // However, there doesn't seem to be any point in returning to assembly
84 // particularly when the remaining code merely halts the processor.
85}
86
87void chainLoad()
88{
89 /* TODO: We ought to load the appropriate partition table, for example
90 the MBR if booting a primary partition or the particular extended
91 partition table if booting a logical drive. For example, the
92 regular MS MBR booter will relocate itself (e.g. the MBR) from
93 0:7C00 to 0:0600 and will use SI as the offset when reading
94 the partition data from itself. Thus when it jumps to the partition
95 boot sector, SI will be 0x600 + 446 + i<<4 where i is the partition
96 table index.
97
98 On the other hand, our code for the non-Multiboot case doesn't do
99 this either, although GRUB does.
100 */
101
102 const unsigned char *bootcode = (const unsigned char*)0x7c00;
103 if(bootcode[0x1fe] == 0x55 && bootcode[0x1ff] == 0xaa)
104 {
105 printf("Calling chainbooter\n");
106 jump_to_chainbooter();
107 /* NORETURN */
108 }
109 else
110 {
111 printf("Bad chain boot sector magic: %02x%02x\n", bootcode[0x1fe], bootcode[0x1ff]);
112 }
113}
114
115void waitThenReload()
116{
117 /* FIXME: Ctrl+Alt+Del does not work under Boot Camp */
118 printf("Darwin booter exited for some reason.\n");
119 printf("Please reboot (Ctrl+Alt+Del) your machine.\n");
120 printf("Restarting Darwin booter in 5 seconds...");
121 sleep(1);
122 printf("4...");
123 sleep(1);
124 printf("3...");
125 sleep(1);
126 printf("2...");
127 sleep(1);
128 printf("1...");
129 sleep(1);
130 printf("0\n");
131}
132
133// Declare boot2_sym as an opaque struct so it can't be converted to a pointer
134// i.e. ensure the idiot programmer (me) makes sure to use address-of
135// Technically it's a function but it's real mode code and we sure don't
136// want to call it under any circumstances.
137extern struct {} boot2_sym asm("boot2");
138
139// prototype multiboot and keep its implementation below hi_multiboot to
140// ensure that it doesn't get inlined by the compiler
141static inline uint32_t multiboot(int multiboot_magic, struct multiboot_info *mi);
142
143
144/*!
145 Returns a pointer to the first safe address we can use for stowing the multiboot info.
146 This might actually be a bit pedantic because mboot.c32 and GRUB both stow the multiboot
147 info in low memory meaning that the >= 128 MB location we choose is plenty high enough.
148 */
149void *determine_safe_hi_addr(int multiboot_magic, struct multiboot_info *mi_orig)
150{
151 // hi_addr must be at least up in 128MB+ space so it doesn't get clobbered
152 void *hi_addr = (void*)PREBOOT_DATA;
153
154 // Fail if the magic isn't correct. We'll complain later.
155 if(multiboot_magic != MULTIBOOT_INFO_MAGIC)
156 return NULL;
157 // Make sure the command-line isn't in high memory.
158 if(mi_orig->mi_flags & MULTIBOOT_INFO_HAS_CMDLINE)
159 {
160 char *end = mi_orig->mi_cmdline;
161 if(end != NULL)
162 {
163 for(; *end != '\0'; ++end)
164 ;
165 ++end;
166 if( (void*)end > hi_addr)
167 hi_addr = end;
168 }
169 }
170 // Make sure the module information isn't in high memory
171 if(mi_orig->mi_flags & MULTIBOOT_INFO_HAS_MODS)
172 {
173 struct multiboot_module *modules = (void*)mi_orig->mi_mods_addr;
174 int i;
175 for(i=0; i < mi_orig->mi_mods_count; ++i)
176 {
177 // make sure the multiboot_module struct itself won't get clobbered
178 void *modinfo_end = modules+i+1;
179 if(modinfo_end > hi_addr)
180 hi_addr = modinfo_end;
181 // make sure the module itself won't get clobbered
182 modinfo_end = (void*)modules[i].mm_mod_end;
183 if(modinfo_end > hi_addr)
184 hi_addr = modinfo_end;
185 // make sure the module string doesn't get clobbered
186 char *end = modules[i].mm_string;
187 for(; *end != '\0'; ++end)
188 ;
189 ++end;
190 modinfo_end = end;
191 if(modinfo_end > hi_addr)
192 hi_addr = modinfo_end;
193 }
194 }
195 // TODO: Copy syms (never needed), mmap, drives, config table, loader name, apm table, VBE info
196
197 // Round up to page size
198 hi_addr = (void*)(((uint32_t)hi_addr + 0xfff) & ~(uint32_t)0xfff);
199 return hi_addr;
200}
201
202/*!
203 Like malloc but with a preceding input/output parameter which points to the next available
204 location for data. The original value of *hi_addr is returned and *hi_addr is incremented
205 by size bytes.
206 */
207void * _hi_malloc(void **hi_addr, size_t size)
208{
209 void *ret = *hi_addr;
210 *hi_addr += size;
211 return ret;
212}
213
214/*!
215 Like strdup but with a preceding input/output parameter. The original value of *hi_addr is
216 returned and *hi_addr is incremented by the number of bytes necessary to complete the string
217 copy including its NUL terminator.
218 */
219char * _hi_strdup(void **hi_addr, char *src)
220{
221 char *dstStart;
222 char *dst = dstStart = *hi_addr;
223 for(; *src != '\0'; ++src, ++dst, ++(*hi_addr))
224 *dst = *src;
225 *dst = '\0';
226 ++(*hi_addr);
227 return dstStart;
228}
229
230// Convenience macros
231#define hi_malloc(size) _hi_malloc(&hi_addr, (size))
232#define hi_strdup(src) _hi_strdup(&hi_addr, (src))
233
234/*!
235 Copies the Multiboot info and any associated data (e.g. various strings and any multiboot modules)
236 up to very high RAM (above 128 MB) to ensure it doesn't get clobbered by the booter.
237 */
238struct multiboot_info * copyMultibootInfo(int multiboot_magic, struct multiboot_info *mi_orig)
239{
240 void *hi_addr = determine_safe_hi_addr(multiboot_magic, mi_orig);
241 if(hi_addr == NULL)
242 return NULL;
243
244 struct multiboot_info *mi_copy = hi_malloc(sizeof(*mi_copy));
245 memcpy(mi_copy, mi_orig, sizeof(*mi_copy));
246
247 // Copy the command line
248 if(mi_orig->mi_flags & MULTIBOOT_INFO_HAS_CMDLINE)
249 {
250 mi_copy->mi_cmdline = hi_strdup(mi_orig->mi_cmdline);
251 }
252 // Copy the loader name
253 if(mi_orig->mi_flags & MULTIBOOT_INFO_HAS_LOADER_NAME)
254 {
255 mi_copy->mi_loader_name = hi_strdup(mi_orig->mi_loader_name);
256 }
257 // Copy the module info
258 if(mi_orig->mi_flags & MULTIBOOT_INFO_HAS_MODS)
259 {
260 struct multiboot_module *dst_modules = hi_malloc(sizeof(*dst_modules)*mi_orig->mi_mods_count);
261 struct multiboot_module *src_modules = (void*)mi_orig->mi_mods_addr;
262 mi_copy->mi_mods_addr = (uint32_t)dst_modules;
263
264 // Copy all of the module info plus the actual module into high memory
265 int i;
266 for(i=0; i < mi_orig->mi_mods_count; ++i)
267 {
268 // Assume mod_end is 1 past the actual end (i.e. it is start + size, not really end (i.e. start + size - 1))
269 // This is what GRUB and mboot.c32 do although the spec is unclear on this.
270 uint32_t mod_length = src_modules[i].mm_mod_end - src_modules[i].mm_mod_start;
271
272 dst_modules[i].mm_mod_start = (uint32_t)hi_malloc(mod_length);
273 dst_modules[i].mm_mod_end = (uint32_t)dst_modules[i].mm_mod_start + mod_length;
274 memcpy((char*)dst_modules[i].mm_mod_start, (char*)src_modules[i].mm_mod_start, mod_length);
275
276 dst_modules[i].mm_string = hi_strdup(src_modules[i].mm_string);
277 dst_modules[i].mm_reserved = src_modules[i].mm_reserved;
278 }
279 }
280 // Make sure that only stuff that didn't need to be copied or that we did deep copy is indicated in the copied struct.
281 mi_copy->mi_flags &= MULTIBOOT_INFO_HAS_MEMORY | MULTIBOOT_INFO_HAS_BOOT_DEVICE | MULTIBOOT_INFO_HAS_CMDLINE | MULTIBOOT_INFO_HAS_LOADER_NAME | MULTIBOOT_INFO_HAS_MODS;
282
283 return mi_copy;
284}
285
286// When we enter, we're actually 1 MB high.
287// Fortunately, memcpy is position independent, and it's all we need
288uint32_t hi_multiboot(int multiboot_magic, struct multiboot_info *mi_orig)
289{
290 // Copy the multiboot info out of the way.
291 // We can't bitch about the magic yet because printf won't work
292 // because it contains an absolute location of putchar which
293 // contains absolute locations to other things which eventually
294 // makes a BIOS call from real mode which of course won't work
295 // because we're stuck in extended memory at this point.
296 struct multiboot_info *mi_p = copyMultibootInfo(multiboot_magic, mi_orig);
297
298 // Get us in to low memory so we can run everything
299
300 // We cannot possibly be more than 383.5k and copying extra won't really hurt anything
301 // We use the address of the assembly entrypoint to get our starting location.
302 memcpy(&boot2_sym, (char*)&boot2_sym + OFFSET_1MEG, 0x5fe00 /* 383.5k */);
303
304 // This is a little assembler routine that returns to us in the correct selector
305 // instead of the kernel selector we're running in now and at the correct
306 // instruction pointer ( current minus 1 MB ). It does not fix our return
307 // address nor does it fix the return address of our caller.
308 continue_at_low_address();
309
310 // Now fix our return address.
311 FIX_RETURN_ADDRESS_USING_FIRST_ARG(multiboot_magic);
312
313 // We can now do just about anything, including return to our caller correctly.
314 // However, our caller must fix his return address if he wishes to return to
315 // his caller and so on and so forth.
316
317 /* Zero the BSS and initialize malloc */
318 initialize_runtime();
319
320 gMI = mi_p;
321
322 /* Set up a temporary bootArgs so we can call console output routines
323 like printf that check the v_display. Note that we purposefully
324 do not initialize anything else at this early stage.
325
326 We are reasonably sure we're already in text mode if GRUB booted us.
327 This is the same assumption that initKernBootStruct makes.
328 We could check the multiboot info I guess, but why bother?
329 */
330 boot_args temporaryBootArgsData;
331 bzero(&temporaryBootArgsData, sizeof(boot_args));
332 bootArgs = &temporaryBootArgsData;
333 bootArgs->Video.v_display = VGA_TEXT_MODE;
334
335 // Install ramdisk and extra driver hooks
336 p_get_ramdisk_info = &multiboot_get_ramdisk_info;
337 p_ramdiskReadBytes = &multibootRamdiskReadBytes;
338 LoadExtraDrivers_p = &multiboot_LoadExtraDrivers;
339
340 // Since we call multiboot ourselves, its return address will be correct.
341 // That is unless it's inlined in which case it does not matter.
342 uint32_t bootdevice = multiboot(multiboot_magic, mi_p);
343 // We're about to exit and temporaryBootArgs will no longer be valid
344 bootArgs = NULL;
345 return bootdevice;
346}
347
348enum {
349 kReturnKey = 0x0d,
350 kEscapeKey = 0x1b,
351 kBackspaceKey = 0x08,
352 kASCIIKeyMask = 0x7f
353};
354
355// This is the meat of our implementation. It grabs the boot device from
356// the multiboot_info and returns it as is. If it fails it returns
357// BAD_BOOT_DEVICE. We can call an awful lot of libsa and libsaio but
358// we need to take care not to call anything that requires malloc because
359// it won't be initialized until boot() does it.
360static inline uint32_t multiboot(int multiboot_magic, struct multiboot_info *mi)
361{
362 if(multiboot_magic != MULTIBOOT_INFO_MAGIC)
363 {
364 printf("Wrong Multiboot magic\n");
365 sleep(2);
366 return BAD_BOOT_DEVICE;
367 }
368 printf("Multiboot info @0x%x\n", (uint32_t)mi);
369 if(mi->mi_flags & MULTIBOOT_INFO_HAS_LOADER_NAME)
370 printf("Loaded by %s\n", mi->mi_loader_name);
371
372 // Multiboot puts boot device in high byte
373 // Normal booter wants it in low byte
374 int bootdevice = mi->mi_boot_device_drive;
375
376 bool doSelectDevice = false;
377 if(mi->mi_flags & MULTIBOOT_INFO_HAS_BOOT_DEVICE)
378 {
379 printf("Boot device 0x%x\n", bootdevice);
380 }
381 else
382 {
383 printf("Multiboot info does not include chosen boot device\n");
384 doSelectDevice = true;
385 bootdevice = BAD_BOOT_DEVICE;
386 }
387 if(mi->mi_flags & MULTIBOOT_INFO_HAS_CMDLINE)
388 {
389 const char *val;
390 int size;
391
392 if(getValueForBootKey(mi->mi_cmdline, "biosdev", &val, &size))
393 {
394 char *endptr;
395 int intVal = strtol(val, &endptr, 16 /* always hex */);
396 if(*val != '\0' && (*endptr == '\0' || *endptr == ' ' || *endptr == '\t'))
397 {
398 printf("Boot device overridden to %02x with biosdev=%s\n", intVal, val);
399 bootdevice = intVal;
400 doSelectDevice = false;
401 }
402 else
403 doSelectDevice = true;
404 }
405
406 if(getValueForBootKey(mi->mi_cmdline, "timeout", &val, &size))
407 {
408 char *endptr;
409 int intVal = strtol(val, &endptr, 0);
410 if(*val != '\0' && (*endptr == '\0' || *endptr == ' ' || *endptr == '\t'))
411 {
412 printf("Timeout overridden to %d with timeout=%s\n", intVal, val);
413 multiboot_timeout = intVal;
414 multiboot_timeout_set = 1;
415 }
416 }
417
418 if(getValueForBootKey(mi->mi_cmdline, "partno", &val, &size))
419 {
420 char *endptr;
421 int intVal = strtol(val, &endptr, 0);
422 if(*val != '\0' && (*endptr == '\0' || *endptr == ' ' || *endptr == '\t'))
423 {
424 printf("Default partition overridden to %d with timeout=%s\n", intVal, val);
425 multiboot_partition = intVal;
426 multiboot_partition_set = 1;
427 }
428 }
429 }
430 if(doSelectDevice)
431 {
432 bootdevice = selectAlternateBootDevice(bootdevice);
433 }
434 if(bootdevice == BAD_BOOT_DEVICE)
435 sleep(2); // pause for a second before halting
436 return bootdevice;
437}
438
439///////////////////////////////////////////////////////////////////////////
440// Ramdisk and extra drivers code
441
442int multibootRamdiskReadBytes( int biosdev, unsigned int blkno,
443 unsigned int byteoff,
444 unsigned int byteCount, void * buffer )
445{
446 int module_count = gMI->mi_mods_count;
447 struct multiboot_module *modules = (void*)gMI->mi_mods_addr;
448 if(biosdev < 0x100)
449 return -1;
450 if(biosdev >= (0x100 + module_count))
451 return -1;
452 struct multiboot_module *module = modules + (biosdev - 0x100);
453
454 void *p_initrd = (void*)module->mm_mod_start;
455 bcopy(p_initrd + blkno*512 + byteoff, buffer, byteCount);
456 return 0;
457}
458
459int multiboot_get_ramdisk_info(int biosdev, struct driveInfo *dip)
460{
461 int module_count = gMI->mi_mods_count;
462 struct multiboot_module *modules = (void*)gMI->mi_mods_addr;
463 if(biosdev < 0x100)
464 return -1;
465 if(biosdev >= (0x100 + module_count))
466 return -1;
467 struct multiboot_module *module = modules + (biosdev - 0x100);
468 dip->biosdev = biosdev;
469 dip->uses_ebios = true;// XXX aserebln uses_ebios isn't a boolean at all
470 dip->di.params.phys_sectors = (module->mm_mod_end - module->mm_mod_start + 511) / 512;
471 dip->valid = true;
472 return 0;
473}
474
475static long multiboot_LoadExtraDrivers(FileLoadDrivers_t FileLoadDrivers_p)
476{
477 char extensionsSpec[1024];
478 int ramdiskUnit;
479 for(ramdiskUnit = 0; ramdiskUnit < gMI->mi_mods_count; ++ramdiskUnit)
480 {
481 int partCount; // unused
482 BVRef ramdiskChain = diskScanBootVolumes(0x100 + ramdiskUnit, &partCount);
483 if(ramdiskChain == NULL)
484 {
485 verbose("Ramdisk contains no partitions\n");
486 continue;
487 }
488 for(; ramdiskChain != NULL; ramdiskChain = ramdiskChain->next)
489 {
490 sprintf(extensionsSpec, "rd(%d,%d)/Extra/", ramdiskUnit, ramdiskChain->part_no);
491 struct dirstuff *extradir = opendir(extensionsSpec);
492 closedir(extradir);
493 if(extradir != NULL)
494 {
495 int ret = FileLoadDrivers_p(extensionsSpec, 0 /* this is a kext root dir, not a kext with plugins */);
496 if(ret != 0)
497 {
498 verbose("FileLoadDrivers failed on a ramdisk\n");
499 return ret;
500 }
501 }
502 }
503 }
504 return 0;
505}
506

Archive Download this file

Revision: 160