]> git.saurik.com Git - apple/boot.git/blame - i386/boot2/options.c
boot-93.tar.gz
[apple/boot.git] / i386 / boot2 / options.c
CommitLineData
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
28enum {
29 kReturnKey = 0x0d,
30 kEscapeKey = 0x1b,
31 kBackspaceKey = 0x08,
32 kASCIIKeyMask = 0x7f
33};
34
35enum {
36 kMenuTopRow = 5,
37 kMenuMaxItems = 6,
38 kScreenLastRow = 24
39};
40
41static BVRef gBootVolume = 0;
42
43static void showHelp();
44
45//==========================================================================
46
47enum {
48 kCursorTypeHidden = 0x0100,
49 kCursorTypeUnderline = 0x0607
50};
51
52typedef struct {
53 int x;
54 int y;
55 int type;
56} CursorState;
57
58static 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
65static void moveCursor( int col, int row )
66{
67 setCursorPosition( col, row, 0 );
68}
69
70static void restoreCursor( const CursorState * cs )
71{
72 setCursorPosition( cs->x, cs->y, 0 );
73 setCursorType( cs->type );
74}
75
76//==========================================================================
77
78static void flushKeyboardBuffer()
79{
80 while ( readKeyboardStatus() ) getc();
81}
82
83//==========================================================================
84
85static 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
117static char gBootArgs[BOOT_STRING_LEN];
118static char * gBootArgsPtr = gBootArgs;
119static char * gBootArgsEnd = gBootArgs + BOOT_STRING_LEN - 1;
120
121static void clearBootArgs()
122{
123 gBootArgsPtr = gBootArgs;
124 memset( gBootArgs, '\0', BOOT_STRING_LEN );
125}
126
127//==========================================================================
128
129static 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
149static 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
183typedef struct {
184 char name[80];
185 void * param;
186} MenuItem;
187
188static const MenuItem * gMenuItems = NULL;
189static int gMenuItemCount;
190static int gMenuRow;
191static int gMenuHeight;
192static int gMenuTop;
193static int gMenuBottom;
194static int gMenuSelection;
195
196static 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
210static 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
252static 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
334static void skipblanks( const char ** cpp )
335{
336 while ( **(cpp) == ' ' || **(cpp) == '\t' ) ++(*cpp);
337}
338
339//==========================================================================
340
341static 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
378void 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
514done:
515 firstRun = NO;
516
517 clearScreenRows( kMenuTopRow, kScreenLastRow );
518 changeCursor( 0, kMenuTopRow, kCursorTypeUnderline, 0 );
519
520 if ( menuItems ) free(menuItems);
521}
522
523//==========================================================================
524
525extern unsigned char chainbootdev;
526extern unsigned char chainbootflag;
527
528int 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
608static 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}