Chameleon

Chameleon Svn Source Tree

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

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

Archive Download this file

Revision: 1066