Chameleon

Chameleon Svn Source Tree

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

Archive Download this file

Revision: 1232