]> git.saurik.com Git - apple/boot.git/blob - i386/boot2/options.c
boot-93.tar.gz
[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 "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 }