Chameleon

Chameleon Svn Source Tree

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

Archive Download this file

Revision: 1444