]>
Commit | Line | Data |
---|---|---|
75b89a82 | 1 | /* |
57c72a9a | 2 | * Copyright (c) 1999-2004 Apple Computer, Inc. All rights reserved. |
75b89a82 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
57c72a9a | 6 | * Portions Copyright (c) 1999-2004 Apple Computer, Inc. All Rights |
4f6e3300 A |
7 | * Reserved. This file contains Original Code and/or Modifications of |
8 | * Original Code as defined in and that are subject to the Apple Public | |
57c72a9a | 9 | * Source License Version 2.0 (the "License"). You may not use this file |
4f6e3300 A |
10 | * except in compliance with the License. Please obtain a copy of the |
11 | * License at http://www.apple.com/publicsource and read it before using | |
12 | * this file. | |
75b89a82 A |
13 | * |
14 | * The Original Code and all software distributed under the License are | |
4f6e3300 | 15 | * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER |
75b89a82 A |
16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
4f6e3300 A |
18 | * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the |
19 | * License for the specific language governing rights and limitations | |
20 | * under the License. | |
75b89a82 A |
21 | * |
22 | * @APPLE_LICENSE_HEADER_END@ | |
23 | */ | |
24 | ||
25 | #include "boot.h" | |
f083c6c3 | 26 | #include "bootstruct.h" |
75b89a82 A |
27 | #include "fdisk.h" |
28 | ||
29 | enum { | |
30 | kReturnKey = 0x0d, | |
31 | kEscapeKey = 0x1b, | |
32 | kBackspaceKey = 0x08, | |
33 | kASCIIKeyMask = 0x7f | |
34 | }; | |
35 | ||
36 | enum { | |
37 | kMenuTopRow = 5, | |
38 | kMenuMaxItems = 6, | |
39 | kScreenLastRow = 24 | |
40 | }; | |
41 | ||
75b89a82 A |
42 | static void showHelp(); |
43 | ||
44 | //========================================================================== | |
45 | ||
75b89a82 A |
46 | typedef struct { |
47 | int x; | |
48 | int y; | |
49 | int type; | |
50 | } CursorState; | |
51 | ||
52 | static void changeCursor( int col, int row, int type, CursorState * cs ) | |
53 | { | |
54 | if (cs) getCursorPositionAndType( &cs->x, &cs->y, &cs->type ); | |
55 | setCursorType( type ); | |
56 | setCursorPosition( col, row, 0 ); | |
57 | } | |
58 | ||
59 | static void moveCursor( int col, int row ) | |
60 | { | |
61 | setCursorPosition( col, row, 0 ); | |
62 | } | |
63 | ||
64 | static void restoreCursor( const CursorState * cs ) | |
65 | { | |
66 | setCursorPosition( cs->x, cs->y, 0 ); | |
67 | setCursorType( cs->type ); | |
68 | } | |
69 | ||
70 | //========================================================================== | |
71 | ||
57c72a9a A |
72 | /* Flush keyboard buffer; returns TRUE if any of the flushed |
73 | * characters was F8. | |
74 | */ | |
75 | ||
76 | static BOOL flushKeyboardBuffer() | |
75b89a82 | 77 | { |
57c72a9a A |
78 | BOOL status = FALSE; |
79 | ||
80 | while ( readKeyboardStatus() ) { | |
81 | if (bgetc() == 0x4200) status = TRUE; | |
82 | } | |
83 | return status; | |
75b89a82 A |
84 | } |
85 | ||
86 | //========================================================================== | |
87 | ||
88 | static int countdown( const char * msg, int row, int timeout ) | |
89 | { | |
f083c6c3 | 90 | unsigned long time; |
75b89a82 A |
91 | int ch = 0; |
92 | int col = strlen(msg) + 1; | |
93 | ||
94 | flushKeyboardBuffer(); | |
95 | ||
96 | moveCursor( 0, row ); | |
97 | printf(msg); | |
98 | ||
bba600dd | 99 | for ( time = time18(), timeout++; timeout > 0; ) |
75b89a82 A |
100 | { |
101 | if (ch = readKeyboardStatus()) | |
102 | break; | |
103 | ||
57c72a9a A |
104 | // Count can be interrupted by holding down shift, |
105 | // control or alt key | |
106 | if ( ( readKeyboardShiftFlags() & 0x0F ) != 0 ) { | |
107 | ch = 1; | |
108 | break; | |
109 | } | |
110 | ||
75b89a82 A |
111 | if ( time18() >= time ) |
112 | { | |
113 | time += 18; | |
114 | timeout--; | |
115 | moveCursor( col, row ); | |
116 | printf("(%d) ", timeout); | |
117 | } | |
118 | } | |
119 | ||
120 | flushKeyboardBuffer(); | |
121 | ||
122 | return ch; | |
123 | } | |
124 | ||
125 | //========================================================================== | |
126 | ||
127 | static char gBootArgs[BOOT_STRING_LEN]; | |
128 | static char * gBootArgsPtr = gBootArgs; | |
129 | static char * gBootArgsEnd = gBootArgs + BOOT_STRING_LEN - 1; | |
130 | ||
131 | static void clearBootArgs() | |
132 | { | |
133 | gBootArgsPtr = gBootArgs; | |
134 | memset( gBootArgs, '\0', BOOT_STRING_LEN ); | |
135 | } | |
136 | ||
137 | //========================================================================== | |
138 | ||
139 | static void showBootPrompt( int row, BOOL visible ) | |
140 | { | |
141 | extern char bootPrompt[]; | |
142 | ||
143 | changeCursor( 0, row, kCursorTypeUnderline, 0 ); | |
144 | clearScreenRows( row, kScreenLastRow ); | |
145 | clearBootArgs(); | |
146 | ||
147 | if ( visible ) | |
148 | { | |
149 | printf( bootPrompt ); | |
150 | } | |
151 | else | |
152 | { | |
57c72a9a | 153 | printf("Press Enter to start up the foreign OS. "); |
75b89a82 A |
154 | } |
155 | } | |
156 | ||
157 | //========================================================================== | |
158 | ||
159 | static void updateBootArgs( int key ) | |
160 | { | |
161 | key &= kASCIIKeyMask; | |
162 | ||
163 | switch ( key ) | |
164 | { | |
165 | case kBackspaceKey: | |
166 | if ( gBootArgsPtr > gBootArgs ) | |
167 | { | |
168 | int x, y, t; | |
169 | getCursorPositionAndType( &x, &y, &t ); | |
170 | if ( x == 0 && y ) | |
171 | { | |
172 | x = 80; y--; | |
173 | } | |
174 | if (x) x--; | |
175 | setCursorPosition( x, y, 0 ); | |
176 | putca(' ', 0x07, 1); | |
177 | *gBootArgsPtr-- = '\0'; | |
178 | } | |
179 | break; | |
180 | ||
181 | default: | |
182 | if ( key >= ' ' && gBootArgsPtr < gBootArgsEnd) | |
183 | { | |
184 | putchar(key); // echo to screen | |
185 | *gBootArgsPtr++ = key; | |
186 | } | |
187 | break; | |
188 | } | |
189 | } | |
190 | ||
191 | //========================================================================== | |
192 | ||
193 | typedef struct { | |
194 | char name[80]; | |
195 | void * param; | |
196 | } MenuItem; | |
197 | ||
198 | static const MenuItem * gMenuItems = NULL; | |
199 | static int gMenuItemCount; | |
200 | static int gMenuRow; | |
201 | static int gMenuHeight; | |
202 | static int gMenuTop; | |
203 | static int gMenuBottom; | |
204 | static int gMenuSelection; | |
205 | ||
206 | static void printMenuItem( const MenuItem * item, int highlight ) | |
207 | { | |
208 | printf(" "); | |
209 | ||
210 | if ( highlight ) | |
211 | putca(' ', 0x70, strlen(item->name) + 4); | |
212 | else | |
213 | putca(' ', 0x07, 40); | |
214 | ||
215 | printf(" %40s\n", item->name); | |
216 | } | |
217 | ||
218 | //========================================================================== | |
219 | ||
220 | static void showMenu( const MenuItem * items, int count, | |
221 | int selection, int row, int height ) | |
222 | { | |
223 | int i; | |
224 | CursorState cursorState; | |
225 | ||
226 | if ( items == NULL || count == 0 ) return; | |
227 | ||
228 | // head and tail points to the start and the end of the list. | |
229 | // top and bottom points to the first and last visible items | |
230 | // in the menu window. | |
231 | ||
232 | gMenuItems = items; | |
233 | gMenuRow = row; | |
234 | gMenuHeight = height; | |
235 | gMenuItemCount = count; | |
236 | gMenuTop = 0; | |
237 | gMenuBottom = min( count, height ) - 1; | |
238 | gMenuSelection = selection; | |
239 | ||
240 | // If the selected item is not visible, shift the list down. | |
241 | ||
242 | if ( gMenuSelection > gMenuBottom ) | |
243 | { | |
244 | gMenuTop += ( gMenuSelection - gMenuBottom ); | |
245 | gMenuBottom = gMenuSelection; | |
246 | } | |
247 | ||
248 | // Draw the visible items. | |
249 | ||
250 | changeCursor( 0, row, kCursorTypeHidden, &cursorState ); | |
251 | ||
252 | for ( i = gMenuTop; i <= gMenuBottom; i++ ) | |
253 | { | |
254 | printMenuItem( &items[i], (i == gMenuSelection) ); | |
255 | } | |
256 | ||
257 | restoreCursor( &cursorState ); | |
258 | } | |
259 | ||
260 | //========================================================================== | |
261 | ||
262 | static int updateMenu( int key, void ** paramPtr ) | |
263 | { | |
264 | int moved = 0; | |
265 | ||
266 | union { | |
267 | struct { | |
268 | unsigned int | |
269 | selectionUp : 1, | |
270 | selectionDown : 1, | |
271 | scrollUp : 1, | |
272 | scrollDown : 1; | |
273 | } f; | |
274 | unsigned int w; | |
275 | } draw = {{0}}; | |
276 | ||
277 | if ( NULL == gMenuItems ) return 0; | |
278 | ||
279 | // Look at the scan code. | |
280 | ||
281 | switch ( key ) | |
282 | { | |
283 | case 0x4800: // Up Arrow | |
284 | if ( gMenuSelection != gMenuTop ) | |
285 | draw.f.selectionUp = 1; | |
286 | else if ( gMenuTop > 0 ) | |
287 | draw.f.scrollDown = 1; | |
288 | break; | |
289 | ||
290 | case 0x5000: // Down Arrow | |
291 | if ( gMenuSelection != gMenuBottom ) | |
292 | draw.f.selectionDown = 1; | |
293 | else if ( gMenuBottom < (gMenuItemCount - 1) ) | |
294 | draw.f.scrollUp = 1; | |
295 | break; | |
296 | } | |
297 | ||
298 | if ( draw.w ) | |
299 | { | |
300 | if ( draw.f.scrollUp ) | |
301 | { | |
302 | scollPage(0, gMenuRow, 40, gMenuRow + gMenuHeight - 1, 0x07, 1, 1); | |
303 | gMenuTop++; gMenuBottom++; | |
304 | draw.f.selectionDown = 1; | |
305 | } | |
306 | ||
307 | if ( draw.f.scrollDown ) | |
308 | { | |
309 | scollPage(0, gMenuRow, 40, gMenuRow + gMenuHeight - 1, 0x07, 1, -1); | |
310 | gMenuTop--; gMenuBottom--; | |
311 | draw.f.selectionUp = 1; | |
312 | } | |
313 | ||
314 | if ( draw.f.selectionUp || draw.f.selectionDown ) | |
315 | { | |
316 | CursorState cursorState; | |
317 | ||
318 | // Set cursor at current position, and clear inverse video. | |
319 | ||
320 | changeCursor( 0, gMenuRow + gMenuSelection - gMenuTop, | |
321 | kCursorTypeHidden, &cursorState ); | |
322 | ||
323 | printMenuItem( &gMenuItems[gMenuSelection], 0 ); | |
324 | ||
325 | if ( draw.f.selectionUp ) gMenuSelection--; | |
326 | else gMenuSelection++; | |
327 | ||
328 | moveCursor( 0, gMenuRow + gMenuSelection - gMenuTop ); | |
329 | ||
330 | printMenuItem( &gMenuItems[gMenuSelection], 1 ); | |
331 | ||
332 | restoreCursor( &cursorState ); | |
333 | } | |
334 | ||
335 | *paramPtr = gMenuItems[gMenuSelection].param; | |
336 | moved = 1; | |
337 | } | |
338 | ||
339 | return moved; | |
340 | } | |
341 | ||
342 | //========================================================================== | |
343 | ||
344 | static void skipblanks( const char ** cpp ) | |
345 | { | |
346 | while ( **(cpp) == ' ' || **(cpp) == '\t' ) ++(*cpp); | |
347 | } | |
348 | ||
349 | //========================================================================== | |
350 | ||
351 | static const char * extractKernelName( char ** cpp ) | |
352 | { | |
353 | char * kn = *cpp; | |
354 | char * cp = *cpp; | |
355 | char c; | |
356 | ||
357 | // Convert char to lower case. | |
358 | ||
359 | c = *cp | 0x20; | |
360 | ||
361 | // Must start with a letter or a '/'. | |
362 | ||
363 | if ( (c < 'a' || c > 'z') && ( c != '/' ) ) | |
364 | return 0; | |
365 | ||
366 | // Keep consuming characters until we hit a separator. | |
367 | ||
368 | while ( *cp && (*cp != '=') && (*cp != ' ') && (*cp != '\t') ) | |
369 | cp++; | |
370 | ||
371 | // Only SPACE or TAB separator is accepted. | |
372 | // Reject everything else. | |
373 | ||
374 | if (*cp == '=') | |
375 | return 0; | |
376 | ||
377 | // Overwrite the separator, and move the pointer past | |
378 | // the kernel name. | |
379 | ||
380 | if (*cp != '\0') *cp++ = '\0'; | |
381 | *cpp = cp; | |
382 | ||
383 | return kn; | |
384 | } | |
385 | ||
386 | //========================================================================== | |
387 | ||
57c72a9a A |
388 | static void |
389 | printMemoryInfo(void) | |
390 | { | |
391 | int line; | |
392 | int i; | |
bba600dd | 393 | MemoryRange *mp = bootInfo->memoryMap; |
57c72a9a A |
394 | |
395 | // Activate and clear page 1 | |
396 | setActiveDisplayPage(1); | |
397 | clearScreenRows(0, 24); | |
398 | setCursorPosition( 0, 0, 1 ); | |
399 | ||
400 | printf("BIOS reported memory ranges:\n"); | |
401 | line = 1; | |
bba600dd | 402 | for (i=0; i<bootInfo->memoryMapCount; i++) { |
57c72a9a A |
403 | printf("Base 0x%08x%08x, ", |
404 | (unsigned long)(mp->base >> 32), | |
405 | (unsigned long)(mp->base)); | |
406 | printf("length 0x%08x%08x, type %d\n", | |
407 | (unsigned long)(mp->length >> 32), | |
408 | (unsigned long)(mp->length), | |
409 | mp->type); | |
410 | if (line++ > 20) { | |
411 | printf("(Press a key to continue...)"); | |
412 | getc(); | |
413 | line = 0; | |
414 | } | |
415 | mp++; | |
416 | } | |
417 | if (line > 0) { | |
418 | printf("(Press a key to continue...)"); | |
419 | getc(); | |
420 | } | |
421 | ||
422 | setActiveDisplayPage(0); | |
423 | } | |
424 | ||
425 | //========================================================================== | |
426 | ||
427 | int | |
428 | getBootOptions(BOOL firstRun) | |
75b89a82 A |
429 | { |
430 | int i; | |
431 | int key; | |
432 | int selectIndex = 0; | |
433 | int bvCount; | |
434 | int nextRow; | |
57c72a9a | 435 | int timeout; |
75b89a82 A |
436 | BVRef bvr; |
437 | BVRef bvChain; | |
438 | BVRef menuBVR; | |
bba600dd | 439 | BOOL showPrompt, newShowPrompt, isCDROM; |
75b89a82 | 440 | MenuItem * menuItems = NULL; |
57c72a9a | 441 | |
bba600dd A |
442 | if ( diskIsCDROM(gBootVolume) ) |
443 | isCDROM = TRUE; | |
444 | else | |
445 | isCDROM = FALSE; | |
446 | ||
57c72a9a A |
447 | // Allow user to override default timeout. |
448 | ||
449 | if ( getIntForKey(kTimeoutKey, &timeout) == NO ) | |
450 | { | |
bba600dd A |
451 | if ( isCDROM ) |
452 | timeout = kCDBootTimeout; | |
453 | else | |
454 | timeout = kBootTimeout; | |
57c72a9a | 455 | } |
bba600dd | 456 | if (timeout < 0) gBootMode |= kBootModeQuiet; |
57c72a9a | 457 | |
bba600dd A |
458 | // If the user is holding down a modifier key, |
459 | // enter safe mode. | |
57c72a9a | 460 | if ( ( readKeyboardShiftFlags() & 0x0F ) != 0 ) { |
bba600dd | 461 | gBootMode |= kBootModeSafe; |
57c72a9a A |
462 | } |
463 | ||
464 | // If user typed F8, abort quiet mode, | |
465 | // and display the menu. | |
466 | if (flushKeyboardBuffer()) { | |
467 | gBootMode &= ~kBootModeQuiet; | |
468 | timeout = 0; | |
469 | } | |
75b89a82 A |
470 | |
471 | clearBootArgs(); | |
57c72a9a A |
472 | |
473 | setCursorPosition( 0, 0, 0 ); | |
474 | clearScreenRows( 0, kScreenLastRow ); | |
475 | if ( ! ( gBootMode & kBootModeQuiet ) ) { | |
476 | // Display banner and show hardware info. | |
bba600dd | 477 | printf( bootBanner, (bootInfo->convmem + bootInfo->extmem) / 1024 ); |
57c72a9a A |
478 | printVBEInfo(); |
479 | } | |
480 | ||
75b89a82 | 481 | changeCursor( 0, kMenuTopRow, kCursorTypeUnderline, 0 ); |
57c72a9a | 482 | |
f083c6c3 | 483 | verbose("Scanning device %x...", gBIOSDev); |
75b89a82 A |
484 | |
485 | // Get a list of bootable volumes on the device. | |
486 | ||
487 | bvChain = scanBootVolumes( gBIOSDev, &bvCount ); | |
488 | gBootVolume = menuBVR = selectBootVolume( bvChain ); | |
489 | ||
bba600dd | 490 | // When booting from CD, default to hard |
75b89a82 A |
491 | // drive boot when possible. |
492 | ||
bba600dd | 493 | if ( isCDROM ) |
75b89a82 | 494 | { |
bba600dd A |
495 | const char *val; |
496 | char *prompt; | |
497 | int cnt; | |
498 | int optionKey; | |
499 | ||
500 | if (getValueForKey( kCDROMPromptKey, &val, &cnt )) { | |
501 | cnt += 1; | |
502 | prompt = malloc(cnt); | |
503 | strlcpy(prompt, val, cnt); | |
504 | } else { | |
505 | prompt = "Press any key to start up from CD-ROM, " | |
506 | "or press F8 to enter startup options."; | |
507 | cnt = 0; | |
508 | } | |
75b89a82 | 509 | |
bba600dd A |
510 | if (getIntForKey( kCDROMOptionKey, &optionKey )) { |
511 | // The key specified is a special key. | |
512 | } else if (getValueForKey( kCDROMOptionKey, &val, &cnt) && cnt >= 1) { | |
513 | optionKey = val[0]; | |
514 | } else { | |
515 | // Default to F8. | |
516 | optionKey = 0x4200; | |
517 | } | |
518 | ||
519 | key = countdown(prompt, kMenuTopRow, timeout); | |
520 | if (cnt) | |
521 | free(prompt); | |
522 | ||
523 | clearScreenRows( kMenuTopRow, kMenuTopRow + 2 ); | |
524 | ||
525 | if (key == 0) { | |
526 | // Boot from hard disk. | |
527 | // Scan the original device 0x80. | |
528 | ||
529 | BVRef hd_bvr = selectBootVolume(scanBootVolumes(0x80, 0)); | |
530 | if ( hd_bvr->flags & kBVFlagNativeBoot ) { | |
75b89a82 A |
531 | gBootVolume = hd_bvr; |
532 | gBIOSDev = hd_bvr->biosdev; | |
533 | initKernBootStruct( gBIOSDev ); | |
534 | goto done; | |
535 | } | |
bba600dd A |
536 | } else { |
537 | if (optionKey < 0x100) | |
538 | key = key & 0x5F; | |
539 | if (key != optionKey) | |
540 | goto done; | |
75b89a82 | 541 | } |
bba600dd A |
542 | gBootMode &= ~kBootModeQuiet; |
543 | timeout = 0; | |
75b89a82 | 544 | } |
75b89a82 | 545 | |
57c72a9a A |
546 | if ( gBootMode & kBootModeQuiet ) |
547 | { | |
548 | // No input allowed from user. | |
549 | goto done; | |
550 | } | |
75b89a82 | 551 | |
57c72a9a A |
552 | if ( firstRun && ( timeout > 0 ) && |
553 | ( countdown("Press any key to enter startup options.", | |
554 | kMenuTopRow, timeout) == 0 ) ) | |
75b89a82 | 555 | { |
bba600dd A |
556 | // If the user is holding down a modifier key, |
557 | // enter safe mode. | |
558 | if ( ( readKeyboardShiftFlags() & 0x0F ) != 0 ) { | |
559 | gBootMode |= kBootModeSafe; | |
560 | } | |
75b89a82 A |
561 | goto done; |
562 | } | |
563 | ||
564 | if ( bvCount ) | |
565 | { | |
566 | // Allocate memory for an array of menu items. | |
567 | ||
568 | menuItems = (MenuItem *) malloc( sizeof(MenuItem) * bvCount ); | |
569 | if ( menuItems == NULL ) goto done; | |
570 | ||
571 | // Associate a menu item for each BVRef. | |
572 | ||
573 | for ( bvr = bvChain, i = bvCount - 1, selectIndex = 0; | |
574 | bvr; bvr = bvr->next, i-- ) | |
575 | { | |
57c72a9a | 576 | getBootVolumeDescription( bvr, menuItems[i].name, 80, YES ); |
75b89a82 A |
577 | menuItems[i].param = (void *) bvr; |
578 | if ( bvr == menuBVR ) selectIndex = i; | |
579 | } | |
580 | } | |
581 | ||
582 | // Clear screen and hide the blinking cursor. | |
583 | ||
584 | clearScreenRows( kMenuTopRow, kMenuTopRow + 2 ); | |
585 | changeCursor( 0, kMenuTopRow, kCursorTypeHidden, 0 ); | |
586 | nextRow = kMenuTopRow; | |
587 | showPrompt = YES; | |
588 | ||
589 | // Show the menu. | |
590 | ||
591 | if ( bvCount ) | |
592 | { | |
593 | printf("Use \30\31 keys to select the startup volume."); | |
594 | showMenu( menuItems, bvCount, selectIndex, kMenuTopRow + 2, kMenuMaxItems ); | |
595 | nextRow += min( bvCount, kMenuMaxItems ) + 3; | |
596 | } | |
597 | ||
598 | // Show the boot prompt. | |
599 | ||
600 | showPrompt = (bvCount == 0) || (menuBVR->flags & kBVFlagNativeBoot); | |
601 | showBootPrompt( nextRow, showPrompt ); | |
602 | ||
603 | do { | |
604 | key = getc(); | |
605 | ||
606 | updateMenu( key, (void **) &menuBVR ); | |
607 | ||
608 | newShowPrompt = (bvCount == 0) || | |
609 | (menuBVR->flags & kBVFlagNativeBoot); | |
610 | ||
611 | if ( newShowPrompt != showPrompt ) | |
612 | { | |
613 | showPrompt = newShowPrompt; | |
614 | showBootPrompt( nextRow, showPrompt ); | |
615 | } | |
616 | if ( showPrompt ) updateBootArgs( key ); | |
617 | ||
618 | switch ( key & kASCIIKeyMask ) | |
619 | { | |
620 | case kReturnKey: | |
621 | if ( *gBootArgs == '?' ) | |
622 | { | |
57c72a9a A |
623 | if ( strcmp( gBootArgs, "?video" ) == 0 ) { |
624 | printVBEModeInfo(); | |
625 | } else if ( strcmp( gBootArgs, "?memory" ) == 0 ) { | |
626 | printMemoryInfo(); | |
627 | } else { | |
628 | showHelp(); | |
629 | } | |
630 | key = 0; | |
75b89a82 A |
631 | showBootPrompt( nextRow, showPrompt ); |
632 | break; | |
633 | } | |
634 | gBootVolume = menuBVR; | |
635 | break; | |
636 | ||
637 | case kEscapeKey: | |
638 | clearBootArgs(); | |
639 | break; | |
640 | ||
641 | default: | |
642 | key = 0; | |
643 | } | |
644 | } | |
645 | while ( 0 == key ); | |
646 | ||
647 | done: | |
648 | firstRun = NO; | |
649 | ||
650 | clearScreenRows( kMenuTopRow, kScreenLastRow ); | |
651 | changeCursor( 0, kMenuTopRow, kCursorTypeUnderline, 0 ); | |
652 | ||
653 | if ( menuItems ) free(menuItems); | |
57c72a9a A |
654 | |
655 | return 0; | |
75b89a82 A |
656 | } |
657 | ||
658 | //========================================================================== | |
659 | ||
660 | extern unsigned char chainbootdev; | |
661 | extern unsigned char chainbootflag; | |
662 | ||
bba600dd A |
663 | BOOL |
664 | copyArgument(const char *argName, const char *val, int cnt, char **argP, int *cntRemainingP) | |
665 | { | |
666 | int argLen = argName ? strlen(argName) : 0; | |
667 | int len = argLen + cnt + 1; // +1 to account for space | |
668 | ||
669 | if (len > *cntRemainingP) { | |
670 | error("Warning: boot arguments too long, truncating\n"); | |
671 | return NO; | |
672 | } | |
673 | ||
674 | if (argName) { | |
675 | strncpy( *argP, argName, argLen ); | |
676 | *argP += argLen; | |
677 | *argP[0] = '='; | |
678 | (*argP)++; | |
679 | len++; // +1 to account for '=' | |
680 | } | |
681 | strncpy( *argP, val, cnt ); | |
682 | *argP += cnt; | |
683 | *argP[0] = ' '; | |
684 | (*argP)++; | |
685 | ||
686 | *cntRemainingP -= len; | |
687 | return YES; | |
688 | } | |
689 | // | |
690 | // Returns TRUE if an argument was copied, FALSE otherwise | |
691 | ||
692 | BOOL | |
693 | processBootArgument( | |
694 | const char *argName, // The argument to search for | |
695 | const char *userString, // Typed-in boot arguments | |
696 | const char *kernelFlags, // Kernel flags from config table | |
697 | const char *configTable, | |
698 | char **argP, // Output value | |
699 | int *cntRemainingP, // Output count | |
700 | char *foundVal // found value | |
701 | ) | |
702 | { | |
703 | const char *val; | |
704 | int cnt; | |
705 | BOOL found = NO; | |
706 | ||
707 | if (getValueForBootKey(userString, argName, &val, &cnt)) { | |
708 | // Don't copy; these values will be copied at the end of argument processing. | |
709 | found = YES; | |
710 | } else if (getValueForBootKey(kernelFlags, argName, &val, &cnt)) { | |
711 | // Don't copy; these values will be copied at the end of argument processing. | |
712 | found = YES; | |
713 | } else if (getValueForConfigTableKey(configTable, argName, &val, &cnt)) { | |
714 | copyArgument(argName, val, cnt, argP, cntRemainingP); | |
715 | found = YES; | |
716 | } | |
717 | if (found && foundVal) { | |
718 | strlcpy(foundVal, val, cnt+1); | |
719 | } | |
720 | return found; | |
721 | } | |
722 | ||
723 | // Maximum config table value size | |
724 | #define VALUE_SIZE 1024 | |
725 | ||
726 | int | |
727 | processBootOptions() | |
75b89a82 | 728 | { |
75b89a82 A |
729 | const char * cp = gBootArgs; |
730 | const char * val = 0; | |
731 | const char * kernel; | |
732 | int cnt; | |
f083c6c3 A |
733 | int userCnt; |
734 | int cntRemaining; | |
57c72a9a | 735 | char * argP; |
bba600dd A |
736 | char uuidStr[64]; |
737 | BOOL uuidSet = NO; | |
738 | char * configKernelFlags; | |
739 | char * valueBuffer; | |
740 | ||
741 | valueBuffer = (char *)malloc(VALUE_SIZE); | |
75b89a82 A |
742 | |
743 | skipblanks( &cp ); | |
744 | ||
745 | // Update the unit and partition number. | |
746 | ||
747 | if ( gBootVolume ) | |
748 | { | |
749 | if ( gBootVolume->flags & kBVFlagForeignBoot ) | |
750 | { | |
751 | readBootSector( gBootVolume->biosdev, gBootVolume->part_boff, | |
752 | (void *) 0x7c00 ); | |
753 | ||
754 | // | |
755 | // Setup edx, and signal intention to chain load the | |
756 | // foreign booter. | |
757 | // | |
758 | ||
759 | chainbootdev = gBootVolume->biosdev; | |
760 | chainbootflag = 1; | |
761 | ||
762 | return 1; | |
763 | } | |
764 | ||
bba600dd | 765 | bootInfo->kernDev &= ~((B_UNITMASK << B_UNITSHIFT ) | |
75b89a82 A |
766 | (B_PARTITIONMASK << B_PARTITIONSHIFT)); |
767 | ||
bba600dd | 768 | bootInfo->kernDev |= MAKEKERNDEV( 0, |
f083c6c3 | 769 | /* unit */ BIOS_DEV_UNIT(gBootVolume), |
75b89a82 A |
770 | /* partition */ gBootVolume->part_no ); |
771 | } | |
772 | ||
773 | // Load config table specified by the user, or use the default. | |
774 | ||
f083c6c3 A |
775 | if (getValueForBootKey( cp, "config", &val, &cnt ) == FALSE) { |
776 | val = 0; | |
777 | cnt = 0; | |
778 | } | |
75b89a82 A |
779 | loadSystemConfig(val, cnt); |
780 | if ( !sysConfigValid ) return -1; | |
781 | ||
782 | // Use the kernel name specified by the user, or fetch the name | |
57c72a9a A |
783 | // in the config table, or use the default if not specified. |
784 | // Specifying a kernel name on the command line, or specifying | |
785 | // a non-default kernel name in the config file counts as | |
786 | // overriding the kernel, which causes the kernelcache not | |
787 | // to be used. | |
788 | ||
789 | gOverrideKernel = NO; | |
790 | if (( kernel = extractKernelName((char **)&cp) )) { | |
bba600dd | 791 | strcpy( bootInfo->bootFile, kernel ); |
57c72a9a A |
792 | gOverrideKernel = YES; |
793 | } else { | |
794 | if ( getValueForKey( kKernelNameKey, &val, &cnt ) ) { | |
bba600dd A |
795 | strlcpy( bootInfo->bootFile, val, cnt+1 ); |
796 | if (strcmp( bootInfo->bootFile, kDefaultKernel ) != 0) { | |
57c72a9a A |
797 | gOverrideKernel = YES; |
798 | } | |
799 | } else { | |
bba600dd | 800 | strcpy( bootInfo->bootFile, kDefaultKernel ); |
57c72a9a A |
801 | } |
802 | } | |
803 | ||
804 | cntRemaining = BOOT_STRING_LEN - 2; // save 1 for NULL, 1 for space | |
bba600dd | 805 | argP = bootArgs->CommandLine; |
57c72a9a | 806 | |
bba600dd A |
807 | // Get config table kernel flags, if not ignored. |
808 | if (getValueForBootKey(cp, kIgnoreBootFileFlag, &val, &cnt) == TRUE || | |
809 | getValueForKey( kKernelFlagsKey, &val, &cnt ) == FALSE) { | |
810 | val = ""; | |
811 | cnt = 0; | |
812 | } | |
813 | configKernelFlags = (char *)malloc(cnt + 1); | |
814 | strlcpy(configKernelFlags, val, cnt + 1); | |
815 | ||
816 | if (processBootArgument(kBootUUIDKey, cp, configKernelFlags, bootInfo->config, &argP, &cntRemaining, 0)) { | |
817 | // boot-uuid was set either on the command-line | |
818 | // or in the config file. | |
819 | uuidSet = YES; | |
820 | } else { | |
821 | if (GetFSUUID(bootInfo->bootFile, uuidStr) == 0) { | |
822 | verbose("Setting boot-uuid to: %s\n", uuidStr); | |
823 | copyArgument(kBootUUIDKey, uuidStr, strlen(uuidStr), &argP, &cntRemaining); | |
824 | uuidSet = YES; | |
57c72a9a | 825 | } |
f083c6c3 A |
826 | } |
827 | ||
bba600dd A |
828 | if (!processBootArgument(kRootDeviceKey, cp, configKernelFlags, bootInfo->config, &argP, &cntRemaining, gRootDevice)) { |
829 | cnt = 0; | |
830 | if ( getValueForKey( kBootDeviceKey, &val, &cnt)) { | |
831 | valueBuffer[0] = '*'; | |
832 | cnt++; | |
833 | strlcpy(valueBuffer + 1, val, cnt); | |
834 | val = valueBuffer; | |
835 | } else { | |
836 | if (uuidSet) { | |
837 | val = "*uuid"; | |
838 | cnt = 5; | |
839 | } else { | |
840 | // Don't set "rd=.." if there is no boot device key | |
841 | // and no UUID. | |
842 | val = ""; | |
843 | cnt = 0; | |
844 | } | |
845 | } | |
846 | if (cnt > 0) { | |
847 | copyArgument( kRootDeviceKey, val, cnt, &argP, &cntRemaining); | |
f083c6c3 | 848 | } |
bba600dd | 849 | strlcpy( gRootDevice, val, (cnt + 1)); |
75b89a82 A |
850 | } |
851 | ||
bba600dd A |
852 | if (!processBootArgument(kPlatformKey, cp, configKernelFlags, bootInfo->config, &argP, &cntRemaining, gPlatformName)) { |
853 | getPlatformName(gPlatformName); | |
854 | copyArgument(kPlatformKey, gPlatformName, strlen(gPlatformName), &argP, &cntRemaining); | |
855 | } | |
75b89a82 | 856 | |
bba600dd A |
857 | if (!getValueForBootKey(cp, kSafeModeFlag, &val, &cnt) && |
858 | !getValueForBootKey(configKernelFlags, kSafeModeFlag, &val, &cnt)) { | |
859 | if (gBootMode & kBootModeSafe) { | |
860 | copyArgument(0, kSafeModeFlag, strlen(kSafeModeFlag), &argP, &cntRemaining); | |
861 | } | |
f083c6c3 | 862 | } |
bba600dd A |
863 | |
864 | // Store the merged kernel flags and boot args. | |
865 | ||
866 | cnt = strlen(configKernelFlags); | |
f083c6c3 | 867 | if (cnt) { |
bba600dd A |
868 | if (cnt > cntRemaining) { |
869 | error("Warning: boot arguments too long, truncating\n"); | |
870 | cnt = cntRemaining; | |
871 | } | |
872 | strncpy(argP, configKernelFlags, cnt); | |
873 | argP[cnt++] = ' '; | |
874 | cntRemaining -= cnt; | |
75b89a82 | 875 | } |
f083c6c3 A |
876 | userCnt = strlen(cp); |
877 | if (userCnt > cntRemaining) { | |
bba600dd | 878 | error("Warning: boot arguments too long, truncating\n"); |
f083c6c3 A |
879 | userCnt = cntRemaining; |
880 | } | |
57c72a9a A |
881 | strncpy(&argP[cnt], cp, userCnt); |
882 | argP[cnt+userCnt] = '\0'; | |
75b89a82 | 883 | |
57c72a9a A |
884 | gVerboseMode = getValueForKey( kVerboseModeFlag, &val, &cnt ) || |
885 | getValueForKey( kSingleUserModeFlag, &val, &cnt ); | |
75b89a82 | 886 | |
57c72a9a A |
887 | gBootMode = ( getValueForKey( kSafeModeFlag, &val, &cnt ) ) ? |
888 | kBootModeSafe : kBootModeNormal; | |
75b89a82 | 889 | |
bba600dd A |
890 | if ( getValueForKey( kOldSafeModeFlag, &val, &cnt ) ) { |
891 | gBootMode = kBootModeSafe; | |
57c72a9a | 892 | } |
f083c6c3 | 893 | |
57c72a9a A |
894 | if ( getValueForKey( kMKextCacheKey, &val, &cnt ) ) { |
895 | strlcpy(gMKextName, val, cnt + 1); | |
896 | } | |
75b89a82 | 897 | |
bba600dd A |
898 | free(configKernelFlags); |
899 | free(valueBuffer); | |
900 | ||
75b89a82 A |
901 | return 0; |
902 | } | |
903 | ||
bba600dd | 904 | |
75b89a82 A |
905 | //========================================================================== |
906 | // Load the help file and display the file contents on the screen. | |
907 | ||
908 | static void showHelp() | |
909 | { | |
910 | #define BOOT_HELP_PATH "/usr/standalone/i386/BootHelp.txt" | |
911 | ||
912 | int fd; | |
57c72a9a A |
913 | int size; |
914 | int line; | |
915 | int line_offset; | |
916 | int c; | |
75b89a82 A |
917 | |
918 | if ( (fd = open(BOOT_HELP_PATH, 0)) >= 0 ) | |
919 | { | |
920 | char * buffer; | |
57c72a9a A |
921 | char * bp; |
922 | ||
923 | size = file_size(fd); | |
924 | buffer = malloc( size + 1 ); | |
925 | read(fd, buffer, size); | |
926 | close(fd); | |
75b89a82 | 927 | |
57c72a9a A |
928 | bp = buffer; |
929 | while (size > 0) { | |
930 | while (*bp != '\n') { | |
931 | bp++; | |
932 | size--; | |
933 | } | |
934 | *bp++ = '\0'; | |
935 | size--; | |
936 | } | |
937 | *bp = '\1'; | |
938 | line_offset = 0; | |
75b89a82 A |
939 | |
940 | setActiveDisplayPage(1); | |
75b89a82 | 941 | |
57c72a9a A |
942 | while (1) { |
943 | clearScreenRows(0, 24); | |
944 | setCursorPosition(0, 0, 1); | |
945 | bp = buffer; | |
946 | for (line = 0; *bp != '\1' && line < line_offset; line++) { | |
947 | while (*bp != '\0') bp++; | |
948 | bp++; | |
949 | } | |
950 | for (line = 0; *bp != '\1' && line < 23; line++) { | |
951 | setCursorPosition(0, line, 1); | |
952 | printf("%s\n", bp); | |
953 | while (*bp != '\0') bp++; | |
954 | bp++; | |
955 | } | |
956 | ||
957 | setCursorPosition(0, 23, 1); | |
958 | if (*bp == '\1') { | |
959 | printf("[Type %sq or space to quit help]", | |
960 | (line_offset > 0) ? "p for previous page, " : ""); | |
961 | } else { | |
962 | printf("[Type %s%sq to quit help]", | |
963 | (line_offset > 0) ? "p for previous page, " : "", | |
964 | (*bp != '\1') ? "space for next page, " : ""); | |
965 | } | |
966 | ||
967 | c = getc(); | |
968 | if (c == 'q' || c == 'Q') { | |
969 | break; | |
970 | } | |
971 | if ((c == 'p' || c == 'P') && line_offset > 0) { | |
972 | line_offset -= 23; | |
973 | } | |
974 | if (c == ' ') { | |
975 | if (*bp == '\1') { | |
976 | break; | |
977 | } else { | |
978 | line_offset += 23; | |
979 | } | |
980 | } | |
981 | } | |
982 | ||
983 | free(buffer); | |
75b89a82 A |
984 | setActiveDisplayPage(0); |
985 | } | |
986 | } |