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

Archive Download this file

Revision: 2411