Chameleon

Chameleon Svn Source Tree

Root/branches/meklort/i386/boot2/mboot.c

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

Archive Download this file

Revision: 575