Chameleon

Chameleon Svn Source Tree

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

Archive Download this file

Revision: 141