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

Archive Download this file

Revision: 1468