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