Chameleon

Chameleon Svn Source Tree

Root/tags/2.3/i386/boot2/mboot.c

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

Archive Download this file

Revision: HEAD