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

Archive Download this file

Revision: 1525