Chameleon

Chameleon Svn Source Tree

Root/branches/cparm/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#include "mboot.h"
9
10#define OFFSET_1MEG 0x100000
11#define BAD_BOOT_DEVICE 0xffffffff
12
13//struct multiboot_info *gMI;
14
15#if UNUSED
16int multiboot_timeout=0;
17int multiboot_timeout_set=0;
18int multiboot_partition=0;
19int multiboot_partition_set=0;
20#endif
21// Global multiboot info, if using multiboot.
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
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) \
42RETURN_ADDRESS_USING_FIRST_ARG(arg) -= OFFSET_1MEG
43
44extern void jump_to_chainbooter(void);
45extern unsigned char chainbootdev;
46extern unsigned char chainbootflag;
47
48void chainLoad(void);
49void waitThenReload(void);
50void multiboot_to_boot(int multiboot_magic, struct multiboot_info *mi_orig);
51void *determine_safe_hi_addr(int multiboot_magic, struct multiboot_info *mi_orig);
52void * _hi_malloc(void **hi_addr, size_t size);
53char * _hi_strdup(void **hi_addr, char *src);
54struct multiboot_info * copyMultibootInfo(int multiboot_magic, struct multiboot_info *mi_orig);
55static inline uint32_t multiboot(int multiboot_magic, struct multiboot_info *mi);
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 uint32_t bootdevice = hi_multiboot(multiboot_magic, mi_orig);
65 if(bootdevice != BAD_BOOT_DEVICE)
66 {
67 // boot only returns to do a chain load.
68 for(;;)
69 { // NOTE: boot only uses the last byte (the drive number)
70 common_boot(bootdevice);
71 if(chainbootflag)
72 chainLoad();
73 else
74 waitThenReload();
75 }
76 }
77 // Avoid returning to high-memory address which isn't valid in the segment
78 // we are now in.
79 // Calling sleep() ensures the user ought to be able to use Ctrl+Alt+Del
80 // because the BIOS will have interrupts on.
81 for(;;)
82 sleep(10);
83 // NOTE: *IF* we needed to return we'd have to fix up our return address to
84 // be in low memory using the same trick as below.
85 // However, there doesn't seem to be any point in returning to assembly
86 // particularly when the remaining code merely halts the processor.
87}
88
89void chainLoad(void)
90{
91 /* TODO: We ought to load the appropriate partition table, for example
92 the MBR if booting a primary partition or the particular extended
93 partition table if booting a logical drive. For example, the
94 regular MS MBR booter will relocate itself (e.g. the MBR) from
95 0:7C00 to 0:0600 and will use SI as the offset when reading
96 the partition data from itself. Thus when it jumps to the partition
97 boot sector, SI will be 0x600 + 446 + i<<4 where i is the partition
98 table index.
99
100 On the other hand, our code for the non-Multiboot case doesn't do
101 this either, although GRUB does.
102 */
103
104 const unsigned char *bootcode = (const unsigned char*)0x7c00;
105 if(bootcode[0x1fe] == 0x55 && bootcode[0x1ff] == 0xaa)
106 {
107 printf("Calling chainbooter\n");
108 jump_to_chainbooter();
109 /* NORETURN */
110 }
111 else
112 {
113 printf("Bad chain boot sector magic: %02x%02x\n", bootcode[0x1fe], bootcode[0x1ff]);
114 }
115}
116
117void waitThenReload(void)
118{
119 /* FIXME: Ctrl+Alt+Del does not work under Boot Camp */
120uint8_t i = 5;
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 %d seconds...",i);
124 sleep(1);
125while (1 < i--) {
126printf("%d...",i);
127sleep(1);
128}
129printf("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 uint32_t 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(struct multiboot_info));
244if (!mi_copy) {
245iloop();
246}
247 memcpy(mi_copy, mi_orig, sizeof(struct multiboot_info));
248
249 // Copy the command line
250 if(mi_orig->mi_flags & MULTIBOOT_INFO_HAS_CMDLINE)
251 {
252 mi_copy->mi_cmdline = hi_strdup(mi_orig->mi_cmdline);
253 }
254 // Copy the loader name
255 if(mi_orig->mi_flags & MULTIBOOT_INFO_HAS_LOADER_NAME)
256 {
257 mi_copy->mi_loader_name = hi_strdup(mi_orig->mi_loader_name);
258 }
259 // Copy the module info
260 if(mi_orig->mi_flags & MULTIBOOT_INFO_HAS_MODS)
261 {
262 struct multiboot_module *dst_modules = hi_malloc(sizeof(struct multiboot_module) * mi_orig->mi_mods_count);
263if (!dst_modules) {
264iloop();
265}
266 struct multiboot_module *src_modules = (void*)mi_orig->mi_mods_addr;
267 mi_copy->mi_mods_addr = (uint32_t)dst_modules;
268
269 // Copy all of the module info plus the actual module into high memory
270 uint32_t i;
271 for(i=0; i < mi_orig->mi_mods_count; ++i)
272 {
273 // Assume mod_end is 1 past the actual end (i.e. it is start + size, not really end (i.e. start + size - 1))
274 // This is what GRUB and mboot.c32 do although the spec is unclear on this.
275 uint32_t mod_length = src_modules[i].mm_mod_end - src_modules[i].mm_mod_start;
276
277 dst_modules[i].mm_mod_start = (uint32_t)hi_malloc(mod_length);
278if (!dst_modules[i].mm_mod_start) {
279iloop();
280}
281 dst_modules[i].mm_mod_end = (uint32_t)dst_modules[i].mm_mod_start + mod_length;
282 memcpy((char*)dst_modules[i].mm_mod_start, (char*)src_modules[i].mm_mod_start, mod_length);
283
284 dst_modules[i].mm_string = hi_strdup(src_modules[i].mm_string);
285 dst_modules[i].mm_reserved = src_modules[i].mm_reserved;
286 }
287 }
288 // Make sure that only stuff that didn't need to be copied or that we did deep copy is indicated in the copied struct.
289 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;
290
291 return mi_copy;
292}
293
294
295// When we enter, we're actually 1 MB high.
296// Fortunately, memcpy is position independent, and it's all we need
297uint32_t hi_multiboot(int multiboot_magic, struct multiboot_info *mi_orig)
298{
299 // Copy the multiboot info out of the way.
300 // We can't bitch about the magic yet because printf won't work
301 // because it contains an absolute location of putchar which
302 // contains absolute locations to other things which eventually
303 // makes a BIOS call from real mode which of course won't work
304 // because we're stuck in extended memory at this point.
305 struct multiboot_info *mi_p = copyMultibootInfo(multiboot_magic, mi_orig);
306
307if (mi_p == NULL)
308 return BAD_BOOT_DEVICE;
309#if 0
310 /*
311 * cparm: I'm not an asm guru but i don't see what's the use of this part of code, moreover some
312 * compilers seems to don't like this at all and crash during compilation, if it's really
313 * necesary maybe we should consider to do it in some other way
314 */
315
316 // Get us in to low memory so we can run everything
317
318 // We cannot possibly be more than 383.5k and copying extra won't really hurt anything
319 // We use the address of the assembly entrypoint to get our starting location.
320 memcpy(&boot2_sym, (char*)&boot2_sym + OFFSET_1MEG, 0x5fe00 /* 383.5k */);
321#endif
322
323 // This is a little assembler routine that returns to us in the correct selector
324 // instead of the kernel selector we're running in now and at the correct
325 // instruction pointer ( current minus 1 MB ). It does not fix our return
326 // address nor does it fix the return address of our caller.
327 continue_at_low_address();
328
329// Now fix our return address.
330 FIX_RETURN_ADDRESS_USING_FIRST_ARG(multiboot_magic);
331
332 // We can now do just about anything, including return to our caller correctly.
333 // However, our caller must fix his return address if he wishes to return to
334 // his caller and so on and so forth.
335
336
337
338 /* Zero the BSS and initialize malloc */
339 initialize_runtime();
340
341 //gMI = mi_p;
342
343 /* Set up a temporary bootArgs so we can call console output routines
344 like printf that check the v_display. Note that we purposefully
345 do not initialize anything else at this early stage.
346
347 We are reasonably sure we're already in text mode if GRUB booted us.
348 This is the same assumption that initKernBootStruct makes.
349 We could check the multiboot info I guess, but why bother?
350 */
351 boot_args temporaryBootArgsData;
352 bzero(&temporaryBootArgsData, sizeof(boot_args));
353 bootArgs = &temporaryBootArgsData;
354 bootArgs->Video.v_display = VGA_TEXT_MODE;
355
356 // Since we call multiboot ourselves, its return address will be correct.
357 // That is unless it's inlined in which case it does not matter.
358 uint32_t bootdevice = multiboot(multiboot_magic, mi_p);
359 // We're about to exit and temporaryBootArgs will no longer be valid
360 bootArgs = NULL;
361
362 return bootdevice;
363}
364
365// This is the meat of our implementation. It grabs the boot device from
366// the multiboot_info and returns it as is. If it fails it returns
367// BAD_BOOT_DEVICE. We can call an awful lot of libsa and libsaio but
368// we need to take care not to call anything that requires malloc because
369// it won't be initialized until boot() does it.
370static inline uint32_t multiboot(int multiboot_magic, struct multiboot_info *mi)
371{
372 if(multiboot_magic != MULTIBOOT_INFO_MAGIC)
373 {
374 printf("Wrong Multiboot magic\n");
375 sleep(2);
376 return BAD_BOOT_DEVICE;
377 }
378 printf("Multiboot info @0x%x\n", (uint32_t)mi);
379 if(mi->mi_flags & MULTIBOOT_INFO_HAS_LOADER_NAME)
380 printf("Loaded by %s\n", mi->mi_loader_name);
381
382 // Multiboot puts boot device in high byte
383 // Normal booter wants it in low byte
384 uint32_t bootdevice = mi->mi_boot_device_drive;
385
386 //bool doSelectDevice = false;
387 if(mi->mi_flags & MULTIBOOT_INFO_HAS_BOOT_DEVICE)
388 {
389 printf("Boot device 0x%x\n", bootdevice);
390 }
391 else
392 {
393 printf("Multiboot info does not include chosen boot device\n");
394 //doSelectDevice = true;
395 bootdevice = BAD_BOOT_DEVICE;
396 }
397 if(mi->mi_flags & MULTIBOOT_INFO_HAS_CMDLINE)
398 {
399 const char *val;
400 int size;
401
402 if(getValueForBootKey(mi->mi_cmdline, "biosdev", &val, &size))
403 {
404 char *endptr;
405 int intVal = strtol(val, &endptr, 16 /* always hex */);
406 if(*val != '\0' && (*endptr == '\0' || *endptr == ' ' || *endptr == '\t'))
407 {
408 printf("Boot device overridden to %02x with biosdev=%s\n", intVal, val);
409 bootdevice = intVal;
410 //doSelectDevice = false;
411 }
412 //else
413 // doSelectDevice = true;
414 }
415#if UNUSED
416 if(getValueForBootKey(mi->mi_cmdline, "timeout", &val, &size))
417 {
418 char *endptr;
419 int intVal = strtol(val, &endptr, 0);
420 if(*val != '\0' && (*endptr == '\0' || *endptr == ' ' || *endptr == '\t'))
421 {
422 printf("Timeout overridden to %d with timeout=%s\n", intVal, val);
423 multiboot_timeout = intVal;
424 multiboot_timeout_set = 1;
425 }
426 }
427
428 if(getValueForBootKey(mi->mi_cmdline, "partno", &val, &size))
429 {
430 char *endptr;
431 int intVal = strtol(val, &endptr, 0);
432 if(*val != '\0' && (*endptr == '\0' || *endptr == ' ' || *endptr == '\t'))
433 {
434 printf("Default partition overridden to %d with timeout=%s\n", intVal, val);
435 multiboot_partition = intVal;
436 multiboot_partition_set = 1;
437 }
438 }
439#endif
440 }
441 if(bootdevice == BAD_BOOT_DEVICE)
442 sleep(2); // pause for a second before halting
443 return bootdevice;
444}
445

Archive Download this file

Revision: HEAD