]>
Commit | Line | Data |
---|---|---|
75b89a82 A |
1 | /* |
2 | * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights | |
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 | |
9 | * Source License Version 1.1 (the "License"). You may not use this file | |
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. | |
13 | * | |
14 | * The Original Code and all software distributed under the License are | |
15 | * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
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. | |
21 | * | |
22 | * @APPLE_LICENSE_HEADER_END@ | |
23 | */ | |
24 | ||
25 | #include "boot.h" | |
26 | #include "fdisk.h" | |
27 | ||
28 | enum { | |
29 | kReturnKey = 0x0d, | |
30 | kEscapeKey = 0x1b, | |
31 | kBackspaceKey = 0x08, | |
32 | kASCIIKeyMask = 0x7f | |
33 | }; | |
34 | ||
35 | enum { | |
36 | kMenuTopRow = 5, | |
37 | kMenuMaxItems = 6, | |
38 | kScreenLastRow = 24 | |
39 | }; | |
40 | ||
41 | static BVRef gBootVolume = 0; | |
42 | ||
43 | static void showHelp(); | |
44 | ||
45 | //========================================================================== | |
46 | ||
47 | enum { | |
48 | kCursorTypeHidden = 0x0100, | |
49 | kCursorTypeUnderline = 0x0607 | |
50 | }; | |
51 | ||
52 | typedef struct { | |
53 | int x; | |
54 | int y; | |
55 | int type; | |
56 | } CursorState; | |
57 | ||
58 | static void changeCursor( int col, int row, int type, CursorState * cs ) | |
59 | { | |
60 | if (cs) getCursorPositionAndType( &cs->x, &cs->y, &cs->type ); | |
61 | setCursorType( type ); | |
62 | setCursorPosition( col, row, 0 ); | |
63 | } | |
64 | ||
65 | static void moveCursor( int col, int row ) | |
66 | { | |
67 | setCursorPosition( col, row, 0 ); | |
68 | } | |
69 | ||
70 | static void restoreCursor( const CursorState * cs ) | |
71 | { | |
72 | setCursorPosition( cs->x, cs->y, 0 ); | |
73 | setCursorType( cs->type ); | |
74 | } | |
75 | ||
76 | //========================================================================== | |
77 | ||
78 | static void flushKeyboardBuffer() | |
79 | { | |
80 | while ( readKeyboardStatus() ) getc(); | |
81 | } | |
82 | ||
83 | //========================================================================== | |
84 | ||
85 | static int countdown( const char * msg, int row, int timeout ) | |
86 | { | |
87 | int time; | |
88 | int ch = 0; | |
89 | int col = strlen(msg) + 1; | |
90 | ||
91 | flushKeyboardBuffer(); | |
92 | ||
93 | moveCursor( 0, row ); | |
94 | printf(msg); | |
95 | ||
96 | for ( time = time18(), timeout++; timeout; ) | |
97 | { | |
98 | if (ch = readKeyboardStatus()) | |
99 | break; | |
100 | ||
101 | if ( time18() >= time ) | |
102 | { | |
103 | time += 18; | |
104 | timeout--; | |
105 | moveCursor( col, row ); | |
106 | printf("(%d) ", timeout); | |
107 | } | |
108 | } | |
109 | ||
110 | flushKeyboardBuffer(); | |
111 | ||
112 | return ch; | |
113 | } | |
114 | ||
115 | //========================================================================== | |
116 | ||
117 | static char gBootArgs[BOOT_STRING_LEN]; | |
118 | static char * gBootArgsPtr = gBootArgs; | |
119 | static char * gBootArgsEnd = gBootArgs + BOOT_STRING_LEN - 1; | |
120 | ||
121 | static void clearBootArgs() | |
122 | { | |
123 | gBootArgsPtr = gBootArgs; | |
124 | memset( gBootArgs, '\0', BOOT_STRING_LEN ); | |
125 | } | |
126 | ||
127 | //========================================================================== | |
128 | ||
129 | static void showBootPrompt( int row, BOOL visible ) | |
130 | { | |
131 | extern char bootPrompt[]; | |
132 | ||
133 | changeCursor( 0, row, kCursorTypeUnderline, 0 ); | |
134 | clearScreenRows( row, kScreenLastRow ); | |
135 | clearBootArgs(); | |
136 | ||
137 | if ( visible ) | |
138 | { | |
139 | printf( bootPrompt ); | |
140 | } | |
141 | else | |
142 | { | |
143 | printf("Press Return to start up the foreign OS. "); | |
144 | } | |
145 | } | |
146 | ||
147 | //========================================================================== | |
148 | ||
149 | static void updateBootArgs( int key ) | |
150 | { | |
151 | key &= kASCIIKeyMask; | |
152 | ||
153 | switch ( key ) | |
154 | { | |
155 | case kBackspaceKey: | |
156 | if ( gBootArgsPtr > gBootArgs ) | |
157 | { | |
158 | int x, y, t; | |
159 | getCursorPositionAndType( &x, &y, &t ); | |
160 | if ( x == 0 && y ) | |
161 | { | |
162 | x = 80; y--; | |
163 | } | |
164 | if (x) x--; | |
165 | setCursorPosition( x, y, 0 ); | |
166 | putca(' ', 0x07, 1); | |
167 | *gBootArgsPtr-- = '\0'; | |
168 | } | |
169 | break; | |
170 | ||
171 | default: | |
172 | if ( key >= ' ' && gBootArgsPtr < gBootArgsEnd) | |
173 | { | |
174 | putchar(key); // echo to screen | |
175 | *gBootArgsPtr++ = key; | |
176 | } | |
177 | break; | |
178 | } | |
179 | } | |
180 | ||
181 | //========================================================================== | |
182 | ||
183 | typedef struct { | |
184 | char name[80]; | |
185 | void * param; | |
186 | } MenuItem; | |
187 | ||
188 | static const MenuItem * gMenuItems = NULL; | |
189 | static int gMenuItemCount; | |
190 | static int gMenuRow; | |
191 | static int gMenuHeight; | |
192 | static int gMenuTop; | |
193 | static int gMenuBottom; | |
194 | static int gMenuSelection; | |
195 | ||
196 | static void printMenuItem( const MenuItem * item, int highlight ) | |
197 | { | |
198 | printf(" "); | |
199 | ||
200 | if ( highlight ) | |
201 | putca(' ', 0x70, strlen(item->name) + 4); | |
202 | else | |
203 | putca(' ', 0x07, 40); | |
204 | ||
205 | printf(" %40s\n", item->name); | |
206 | } | |
207 | ||
208 | //========================================================================== | |
209 | ||
210 | static void showMenu( const MenuItem * items, int count, | |
211 | int selection, int row, int height ) | |
212 | { | |
213 | int i; | |
214 | CursorState cursorState; | |
215 | ||
216 | if ( items == NULL || count == 0 ) return; | |
217 | ||
218 | // head and tail points to the start and the end of the list. | |
219 | // top and bottom points to the first and last visible items | |
220 | // in the menu window. | |
221 | ||
222 | gMenuItems = items; | |
223 | gMenuRow = row; | |
224 | gMenuHeight = height; | |
225 | gMenuItemCount = count; | |
226 | gMenuTop = 0; | |
227 | gMenuBottom = min( count, height ) - 1; | |
228 | gMenuSelection = selection; | |
229 | ||
230 | // If the selected item is not visible, shift the list down. | |
231 | ||
232 | if ( gMenuSelection > gMenuBottom ) | |
233 | { | |
234 | gMenuTop += ( gMenuSelection - gMenuBottom ); | |
235 | gMenuBottom = gMenuSelection; | |
236 | } | |
237 | ||
238 | // Draw the visible items. | |
239 | ||
240 | changeCursor( 0, row, kCursorTypeHidden, &cursorState ); | |
241 | ||
242 | for ( i = gMenuTop; i <= gMenuBottom; i++ ) | |
243 | { | |
244 | printMenuItem( &items[i], (i == gMenuSelection) ); | |
245 | } | |
246 | ||
247 | restoreCursor( &cursorState ); | |
248 | } | |
249 | ||
250 | //========================================================================== | |
251 | ||
252 | static int updateMenu( int key, void ** paramPtr ) | |
253 | { | |
254 | int moved = 0; | |
255 | ||
256 | union { | |
257 | struct { | |
258 | unsigned int | |
259 | selectionUp : 1, | |
260 | selectionDown : 1, | |
261 | scrollUp : 1, | |
262 | scrollDown : 1; | |
263 | } f; | |
264 | unsigned int w; | |
265 | } draw = {{0}}; | |
266 | ||
267 | if ( NULL == gMenuItems ) return 0; | |
268 | ||
269 | // Look at the scan code. | |
270 | ||
271 | switch ( key ) | |
272 | { | |
273 | case 0x4800: // Up Arrow | |
274 | if ( gMenuSelection != gMenuTop ) | |
275 | draw.f.selectionUp = 1; | |
276 | else if ( gMenuTop > 0 ) | |
277 | draw.f.scrollDown = 1; | |
278 | break; | |
279 | ||
280 | case 0x5000: // Down Arrow | |
281 | if ( gMenuSelection != gMenuBottom ) | |
282 | draw.f.selectionDown = 1; | |
283 | else if ( gMenuBottom < (gMenuItemCount - 1) ) | |
284 | draw.f.scrollUp = 1; | |
285 | break; | |
286 | } | |
287 | ||
288 | if ( draw.w ) | |
289 | { | |
290 | if ( draw.f.scrollUp ) | |
291 | { | |
292 | scollPage(0, gMenuRow, 40, gMenuRow + gMenuHeight - 1, 0x07, 1, 1); | |
293 | gMenuTop++; gMenuBottom++; | |
294 | draw.f.selectionDown = 1; | |
295 | } | |
296 | ||
297 | if ( draw.f.scrollDown ) | |
298 | { | |
299 | scollPage(0, gMenuRow, 40, gMenuRow + gMenuHeight - 1, 0x07, 1, -1); | |
300 | gMenuTop--; gMenuBottom--; | |
301 | draw.f.selectionUp = 1; | |
302 | } | |
303 | ||
304 | if ( draw.f.selectionUp || draw.f.selectionDown ) | |
305 | { | |
306 | CursorState cursorState; | |
307 | ||
308 | // Set cursor at current position, and clear inverse video. | |
309 | ||
310 | changeCursor( 0, gMenuRow + gMenuSelection - gMenuTop, | |
311 | kCursorTypeHidden, &cursorState ); | |
312 | ||
313 | printMenuItem( &gMenuItems[gMenuSelection], 0 ); | |
314 | ||
315 | if ( draw.f.selectionUp ) gMenuSelection--; | |
316 | else gMenuSelection++; | |
317 | ||
318 | moveCursor( 0, gMenuRow + gMenuSelection - gMenuTop ); | |
319 | ||
320 | printMenuItem( &gMenuItems[gMenuSelection], 1 ); | |
321 | ||
322 | restoreCursor( &cursorState ); | |
323 | } | |
324 | ||
325 | *paramPtr = gMenuItems[gMenuSelection].param; | |
326 | moved = 1; | |
327 | } | |
328 | ||
329 | return moved; | |
330 | } | |
331 | ||
332 | //========================================================================== | |
333 | ||
334 | static void skipblanks( const char ** cpp ) | |
335 | { | |
336 | while ( **(cpp) == ' ' || **(cpp) == '\t' ) ++(*cpp); | |
337 | } | |
338 | ||
339 | //========================================================================== | |
340 | ||
341 | static const char * extractKernelName( char ** cpp ) | |
342 | { | |
343 | char * kn = *cpp; | |
344 | char * cp = *cpp; | |
345 | char c; | |
346 | ||
347 | // Convert char to lower case. | |
348 | ||
349 | c = *cp | 0x20; | |
350 | ||
351 | // Must start with a letter or a '/'. | |
352 | ||
353 | if ( (c < 'a' || c > 'z') && ( c != '/' ) ) | |
354 | return 0; | |
355 | ||
356 | // Keep consuming characters until we hit a separator. | |
357 | ||
358 | while ( *cp && (*cp != '=') && (*cp != ' ') && (*cp != '\t') ) | |
359 | cp++; | |
360 | ||
361 | // Only SPACE or TAB separator is accepted. | |
362 | // Reject everything else. | |
363 | ||
364 | if (*cp == '=') | |
365 | return 0; | |
366 | ||
367 | // Overwrite the separator, and move the pointer past | |
368 | // the kernel name. | |
369 | ||
370 | if (*cp != '\0') *cp++ = '\0'; | |
371 | *cpp = cp; | |
372 | ||
373 | return kn; | |
374 | } | |
375 | ||
376 | //========================================================================== | |
377 | ||
378 | void getBootOptions() | |
379 | { | |
380 | int i; | |
381 | int key; | |
382 | int selectIndex = 0; | |
383 | int bvCount; | |
384 | int nextRow; | |
385 | BVRef bvr; | |
386 | BVRef bvChain; | |
387 | BVRef menuBVR; | |
388 | BOOL showPrompt, newShowPrompt; | |
389 | MenuItem * menuItems = NULL; | |
390 | static BOOL firstRun = YES; | |
391 | ||
392 | clearBootArgs(); | |
393 | clearScreenRows( kMenuTopRow, kScreenLastRow ); | |
394 | changeCursor( 0, kMenuTopRow, kCursorTypeUnderline, 0 ); | |
395 | printf("Scanning device %x...", gBIOSDev); | |
396 | ||
397 | // Get a list of bootable volumes on the device. | |
398 | ||
399 | bvChain = scanBootVolumes( gBIOSDev, &bvCount ); | |
400 | gBootVolume = menuBVR = selectBootVolume( bvChain ); | |
401 | ||
402 | #if 0 | |
403 | // When booting from CD (via HD emulation), default to hard | |
404 | // drive boot when possible. | |
405 | ||
406 | if ( gBootVolume->part_type == FDISK_BOOTER && | |
407 | gBootVolume->biosdev == 0x80 ) | |
408 | { | |
409 | // Scan the original device 0x80 that has been displaced | |
410 | // by the CD-ROM. | |
411 | ||
412 | BVRef hd_bvr = selectBootVolume(scanBootVolumes(0x81, 0)); | |
413 | if ( hd_bvr->flags & kBVFlagNativeBoot ) | |
414 | { | |
415 | int key = countdown("Press C to start up from CD-ROM.", | |
416 | kMenuTopRow, 5); | |
417 | ||
418 | if ( (key & 0x5f) != 'c' ) | |
419 | { | |
420 | gBootVolume = hd_bvr; | |
421 | gBIOSDev = hd_bvr->biosdev; | |
422 | initKernBootStruct( gBIOSDev ); | |
423 | goto done; | |
424 | } | |
425 | } | |
426 | } | |
427 | #endif | |
428 | ||
429 | // Allow user to override default setting. | |
430 | ||
431 | if ( firstRun && | |
432 | countdown("Press any key to enter startup options.", | |
433 | kMenuTopRow, 3) == 0 ) | |
434 | { | |
435 | goto done; | |
436 | } | |
437 | ||
438 | if ( bvCount ) | |
439 | { | |
440 | // Allocate memory for an array of menu items. | |
441 | ||
442 | menuItems = (MenuItem *) malloc( sizeof(MenuItem) * bvCount ); | |
443 | if ( menuItems == NULL ) goto done; | |
444 | ||
445 | // Associate a menu item for each BVRef. | |
446 | ||
447 | for ( bvr = bvChain, i = bvCount - 1, selectIndex = 0; | |
448 | bvr; bvr = bvr->next, i-- ) | |
449 | { | |
450 | getBootVolumeDescription( bvr, menuItems[i].name, 80 ); | |
451 | menuItems[i].param = (void *) bvr; | |
452 | if ( bvr == menuBVR ) selectIndex = i; | |
453 | } | |
454 | } | |
455 | ||
456 | // Clear screen and hide the blinking cursor. | |
457 | ||
458 | clearScreenRows( kMenuTopRow, kMenuTopRow + 2 ); | |
459 | changeCursor( 0, kMenuTopRow, kCursorTypeHidden, 0 ); | |
460 | nextRow = kMenuTopRow; | |
461 | showPrompt = YES; | |
462 | ||
463 | // Show the menu. | |
464 | ||
465 | if ( bvCount ) | |
466 | { | |
467 | printf("Use \30\31 keys to select the startup volume."); | |
468 | showMenu( menuItems, bvCount, selectIndex, kMenuTopRow + 2, kMenuMaxItems ); | |
469 | nextRow += min( bvCount, kMenuMaxItems ) + 3; | |
470 | } | |
471 | ||
472 | // Show the boot prompt. | |
473 | ||
474 | showPrompt = (bvCount == 0) || (menuBVR->flags & kBVFlagNativeBoot); | |
475 | showBootPrompt( nextRow, showPrompt ); | |
476 | ||
477 | do { | |
478 | key = getc(); | |
479 | ||
480 | updateMenu( key, (void **) &menuBVR ); | |
481 | ||
482 | newShowPrompt = (bvCount == 0) || | |
483 | (menuBVR->flags & kBVFlagNativeBoot); | |
484 | ||
485 | if ( newShowPrompt != showPrompt ) | |
486 | { | |
487 | showPrompt = newShowPrompt; | |
488 | showBootPrompt( nextRow, showPrompt ); | |
489 | } | |
490 | if ( showPrompt ) updateBootArgs( key ); | |
491 | ||
492 | switch ( key & kASCIIKeyMask ) | |
493 | { | |
494 | case kReturnKey: | |
495 | if ( *gBootArgs == '?' ) | |
496 | { | |
497 | showHelp(); key = 0; | |
498 | showBootPrompt( nextRow, showPrompt ); | |
499 | break; | |
500 | } | |
501 | gBootVolume = menuBVR; | |
502 | break; | |
503 | ||
504 | case kEscapeKey: | |
505 | clearBootArgs(); | |
506 | break; | |
507 | ||
508 | default: | |
509 | key = 0; | |
510 | } | |
511 | } | |
512 | while ( 0 == key ); | |
513 | ||
514 | done: | |
515 | firstRun = NO; | |
516 | ||
517 | clearScreenRows( kMenuTopRow, kScreenLastRow ); | |
518 | changeCursor( 0, kMenuTopRow, kCursorTypeUnderline, 0 ); | |
519 | ||
520 | if ( menuItems ) free(menuItems); | |
521 | } | |
522 | ||
523 | //========================================================================== | |
524 | ||
525 | extern unsigned char chainbootdev; | |
526 | extern unsigned char chainbootflag; | |
527 | ||
528 | int processBootOptions() | |
529 | { | |
530 | KERNBOOTSTRUCT * kbp = kernBootStruct; | |
531 | const char * cp = gBootArgs; | |
532 | const char * val = 0; | |
533 | const char * kernel; | |
534 | int cnt; | |
535 | ||
536 | skipblanks( &cp ); | |
537 | ||
538 | // Update the unit and partition number. | |
539 | ||
540 | if ( gBootVolume ) | |
541 | { | |
542 | if ( gBootVolume->flags & kBVFlagForeignBoot ) | |
543 | { | |
544 | readBootSector( gBootVolume->biosdev, gBootVolume->part_boff, | |
545 | (void *) 0x7c00 ); | |
546 | ||
547 | // | |
548 | // Setup edx, and signal intention to chain load the | |
549 | // foreign booter. | |
550 | // | |
551 | ||
552 | chainbootdev = gBootVolume->biosdev; | |
553 | chainbootflag = 1; | |
554 | ||
555 | return 1; | |
556 | } | |
557 | ||
558 | kbp->kernDev &= ~((B_UNITMASK << B_UNITSHIFT ) | | |
559 | (B_PARTITIONMASK << B_PARTITIONSHIFT)); | |
560 | ||
561 | kbp->kernDev |= MAKEKERNDEV( 0, | |
562 | /* unit */ BIOS_DEV_UNIT(gBootVolume->biosdev), | |
563 | /* partition */ gBootVolume->part_no ); | |
564 | } | |
565 | ||
566 | // Load config table specified by the user, or use the default. | |
567 | ||
568 | getValueForBootKey( cp, "config", &val, &cnt ); | |
569 | loadSystemConfig(val, cnt); | |
570 | if ( !sysConfigValid ) return -1; | |
571 | ||
572 | // Use the kernel name specified by the user, or fetch the name | |
573 | // in the config table. | |
574 | ||
575 | if (( kernel = extractKernelName((char **)&cp) )) | |
576 | { | |
577 | strcpy( kbp->bootFile, kernel ); | |
578 | } | |
579 | else | |
580 | { | |
581 | if ( getValueForKey( kKernelNameKey, &val, &cnt ) ) | |
582 | strncpy( kbp->bootFile, val, cnt ); | |
583 | } | |
584 | ||
585 | // Store the merged kernel flags and boot args. | |
586 | ||
587 | getValueForKey( kKernelFlagsKey, &val, &cnt ); | |
588 | if ( (strlen(cp) + 1 + cnt) < BOOT_STRING_LEN ) | |
589 | { | |
590 | if (cnt) strncpy(kbp->bootString, val, cnt); | |
591 | sprintf(&kbp->bootString[cnt], "%s%s", cnt ? " " : "", cp); | |
592 | } | |
593 | ||
594 | gVerboseMode = getValueForKey( "-v", &val, &cnt ) || | |
595 | getValueForKey( "-s", &val, &cnt ); | |
596 | ||
597 | gBootGraphics = getBoolForKey( kBootGraphicsKey ); | |
598 | ||
599 | gBootMode = ( getValueForKey( "-f", &val, &cnt ) ) ? | |
600 | kBootModeSafe : kBootModeNormal; | |
601 | ||
602 | return 0; | |
603 | } | |
604 | ||
605 | //========================================================================== | |
606 | // Load the help file and display the file contents on the screen. | |
607 | ||
608 | static void showHelp() | |
609 | { | |
610 | #define BOOT_HELP_PATH "/usr/standalone/i386/BootHelp.txt" | |
611 | ||
612 | int fd; | |
613 | ||
614 | if ( (fd = open(BOOT_HELP_PATH, 0)) >= 0 ) | |
615 | { | |
616 | char * buffer; | |
617 | ||
618 | // Activate and clear page 1 | |
619 | // Perhaps this should be loaded only once? | |
620 | ||
621 | setActiveDisplayPage(1); | |
622 | clearScreenRows(0, 24); | |
623 | setCursorPosition( 0, 0, 1 ); | |
624 | ||
625 | buffer = malloc( file_size(fd) ); | |
626 | read(fd, buffer, file_size(fd) - 1); | |
627 | close(fd); | |
628 | printf("%s", buffer); | |
629 | free(buffer); | |
630 | ||
631 | // Wait for a keystroke and return to page 0. | |
632 | ||
633 | getc(); | |
634 | setActiveDisplayPage(0); | |
635 | } | |
636 | } |