Chameleon

Chameleon Svn Source Tree

Root/branches/ErmaC/Enoch/i386/boot2/options.c

1/*
2 * Copyright (c) 1999-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Portions Copyright (c) 1999-2004 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 2.0 (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#include "ramdisk.h"
29#include "gui.h"
30#include "term.h"
31#include "embedded.h"
32#include "pci.h"
33#include "modules.h"
34
35#if DEBUG
36#define DBG(x...)printf(x)
37#else
38#define DBG(x...)msglog(x)
39#endif
40
41bool showBootBanner = true; //Azi:showinfo
42static bool shouldboot = false;
43
44extern int multiboot_timeout;
45extern int multiboot_timeout_set;
46
47extern BVRef bvChain;
48//extern intmenucount;
49
50extern intgDeviceCount;
51
52intselectIndex = 0;
53MenuItem * menuItems = NULL;
54
55enum {
56 kMenuTopRow = 5,
57 kMenuMaxItems = 10,
58 kScreenLastRow = 24
59};
60
61//==========================================================================
62
63typedef struct {
64 int x;
65 int y;
66 int type;
67} CursorState;
68
69static void changeCursor( int col, int row, int type, CursorState * cs )
70{
71 if (cs) getCursorPositionAndType( &cs->x, &cs->y, &cs->type );
72 setCursorType( type );
73 setCursorPosition( col, row, 0 );
74}
75
76static void moveCursor( int col, int row )
77{
78 setCursorPosition( col, row, 0 );
79}
80
81static void restoreCursor( const CursorState * cs )
82{
83 setCursorPosition( cs->x, cs->y, 0 );
84 setCursorType( cs->type );
85}
86
87//==========================================================================
88
89/* Flush keyboard buffer; returns TRUE if any of the flushed
90 * characters was F8.
91 */
92
93static bool flushKeyboardBuffer(void)
94{
95 bool status = false;
96
97 while ( readKeyboardStatus() ) {
98 if (bgetc() == 0x4200) status = true;
99 }
100 return status;
101}
102
103//==========================================================================
104
105static int countdown( const char * msg, int row, int timeout )
106{
107 unsigned long time;
108 int ch = 0;
109 int col = strlen(msg) + 1;
110
111 flushKeyboardBuffer();
112
113if( bootArgs->Video.v_display == VGA_TEXT_MODE )
114{
115moveCursor( 0, row );
116printf(msg);
117
118} else {
119
120position_t p = pos( gui.screen.width / 2 + 1 , ( gui.devicelist.pos.y + 3 ) + ( ( gui.devicelist.height - gui.devicelist.iconspacing ) / 2 ) );
121
122char dummy[80];
123getBootVolumeDescription( gBootVolume, dummy, sizeof(dummy) - 1, true );
124drawDeviceIcon( gBootVolume, gui.screen.pixmap, p, true );
125drawStrCenteredAt( (char *) msg, &font_small, gui.screen.pixmap, gui.countdown.pos );
126
127// make this screen the new background
128memcpy( gui.backbuffer->pixels, gui.screen.pixmap->pixels, gui.backbuffer->width * gui.backbuffer->height * 4 );
129
130}
131
132int multi_buff = 18 * (timeout);
133 int multi = ++multi_buff;
134
135 int lasttime=0;
136
137 for ( time = time18(), timeout++; timeout > 0; )
138 {
139if( time18() > lasttime)
140{
141multi--;
142lasttime=time18();
143}
144
145 if ( (ch = readKeyboardStatus()) )
146 break;
147
148 // Count can be interrupted by holding down shift,
149 // control or alt key
150 if ( ( readKeyboardShiftFlags() & 0x0F ) != 0 )
151{
152 ch = 1;
153 break;
154 }
155
156 if ( time18() >= time )
157 {
158 time += 18;
159 timeout--;
160
161if( bootArgs->Video.v_display == VGA_TEXT_MODE )
162{
163moveCursor( col, row );
164printf("(%d) ", timeout);
165}
166 }
167
168if( bootArgs->Video.v_display != VGA_TEXT_MODE )
169{
170drawProgressBar( gui.screen.pixmap, 100, gui.progressbar.pos , ( multi * 100 / multi_buff ) );
171gui.redraw = true;
172updateVRAM();
173}
174
175 }
176
177 flushKeyboardBuffer();
178
179 return ch;
180}
181
182//==========================================================================
183
184char gBootArgs[BOOT_STRING_LEN];
185static char * gBootArgsPtr = gBootArgs;
186static char * gBootArgsEnd = gBootArgs + BOOT_STRING_LEN - 1;
187static char booterCommand[BOOT_STRING_LEN];
188static char booterParam[BOOT_STRING_LEN];
189
190static void clearBootArgs(void)
191{
192gBootArgsPtr = gBootArgs;
193memset(gBootArgs, '\0', BOOT_STRING_LEN);
194
195if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
196clearGraphicBootPrompt();
197}
198execute_hook("ClearArgs", NULL, NULL, NULL, NULL);
199}
200
201void addBootArg(const char * argStr)
202{
203if ( (gBootArgsPtr + strlen(argStr) + 1) < gBootArgsEnd)
204{
205if(gBootArgsPtr != gBootArgs) *gBootArgsPtr++ = ' ';
206strcat(gBootArgs, argStr);
207gBootArgsPtr += strlen(argStr);
208}
209}
210
211//==========================================================================
212
213static void showBootPrompt(int row, bool visible)
214{
215extern char bootPrompt[];
216extern char bootRescanPrompt[];
217
218if( bootArgs->Video.v_display == VGA_TEXT_MODE ) {
219changeCursor( 0, row, kCursorTypeUnderline, 0 );
220clearScreenRows( row, kScreenLastRow );
221}
222
223clearBootArgs();
224
225if (visible) {
226if (bootArgs->Video.v_display == VGA_TEXT_MODE) {
227if (gEnableCDROMRescan) {
228printf( bootRescanPrompt );
229} else {
230printf( bootPrompt );
231printf( gBootArgs );
232}
233}
234} else {
235if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
236clearGraphicBootPrompt();
237} else {
238printf("Press Enter to start up the foreign OS. ");
239}
240}
241}
242
243//==========================================================================
244
245static void updateBootArgs( int key )
246{
247 key = ASCII_KEY(key);
248
249 switch ( key )
250 {
251 case KEY_BKSP:
252 if ( gBootArgsPtr > gBootArgs )
253 {
254 *--gBootArgsPtr = '\0';
255
256 int x, y, t;
257 getCursorPositionAndType( &x, &y, &t );
258 if ( x == 0 && y )
259 {
260 x = 80; y--;
261 }
262 if (x) {
263x--;
264}
265
266if( bootArgs->Video.v_display == VGA_TEXT_MODE )
267{
268setCursorPosition( x, y, 0 );
269putca(' ', 0x07, 1);
270}
271 else
272 {
273 updateGraphicBootPrompt();
274 }
275 }
276break;
277
278 default:
279 if ( key >= ' ' && gBootArgsPtr < gBootArgsEnd)
280 {
281 *gBootArgsPtr++ = key;
282
283 if( bootArgs->Video.v_display != VGA_TEXT_MODE ) updateGraphicBootPrompt();
284 else if ( key >= ' ' && key < 0x7f) putchar(key);
285}
286
287break;
288 }
289}
290
291//==========================================================================
292
293static const MenuItem * gMenuItems = NULL;
294
295static int gMenuItemCount;
296static int gMenuRow;
297static int gMenuHeight;
298static int gMenuTop;
299static int gMenuBottom;
300static int gMenuSelection;
301
302static int gMenuStart;
303static int gMenuEnd;
304
305static void printMenuItem( const MenuItem * item, int highlight )
306{
307 printf(" ");
308
309 if ( highlight )
310 putca(' ', 0x70, strlen(item->name) + 4);
311 else
312 putca(' ', 0x07, 40);
313
314 printf(" %40s\n", item->name);
315}
316
317//==========================================================================
318
319static void showMenu( const MenuItem * items, int count,
320 int selection, int row, int height )
321{
322 int i;
323 CursorState cursorState;
324
325 if ( items == NULL || count == 0 )
326return;
327
328 // head and tail points to the start and the end of the list.
329 // top and bottom points to the first and last visible items
330 // in the menu window.
331
332 gMenuItems= items;
333 gMenuRow= row;
334 gMenuHeight= height;
335 gMenuItemCount= count;
336 gMenuTop= 0;
337 gMenuBottom= MIN( count, height ) - 1;
338 gMenuSelection= selection;
339
340 gMenuStart= 0;
341 gMenuEnd = MIN( count, gui.maxdevices ) - 1;
342
343// If the selected item is not visible, shift the list down.
344
345 if ( gMenuSelection > gMenuBottom )
346 {
347 gMenuTop += ( gMenuSelection - gMenuBottom );
348 gMenuBottom = gMenuSelection;
349 }
350
351if ( gMenuSelection > gMenuEnd )
352 {
353gMenuStart += ( gMenuSelection - gMenuEnd );
354 gMenuEnd = gMenuSelection;
355 }
356
357// Draw the visible items.
358
359if( bootArgs->Video.v_display != VGA_TEXT_MODE )
360
361drawDeviceList(gMenuStart, gMenuEnd, gMenuSelection);
362
363else {
364
365changeCursor( 0, row, kCursorTypeHidden, &cursorState );
366
367for ( i = gMenuTop; i <= gMenuBottom; i++ )
368{
369printMenuItem( &items[i], (i == gMenuSelection) );
370}
371
372restoreCursor( &cursorState );
373 }
374}
375
376//==========================================================================
377
378static int updateMenu( int key, void ** paramPtr )
379{
380 int moved = 0;
381
382 union {
383 struct {
384 unsigned int
385 selectionUp : 1,
386 selectionDown : 1,
387 scrollUp : 1,
388 scrollDown : 1;
389 } f;
390 unsigned int w;
391 } draw = {{0}};
392
393 if ( gMenuItems == NULL )
394return 0;
395
396if( bootArgs->Video.v_display != VGA_TEXT_MODE )
397{
398int res;
399
400// set navigation keys for horizontal layout as defaults
401int previous= 0x4B00;// left arrow
402int subsequent= 0x4D00;// right arrow
403int menu= 0x5000;// down arrow
404
405if ( gui.layout == VerticalLayout )
406{
407// set navigation keys for vertical layout
408previous= 0x4800;// up arrow
409subsequent= 0x5000;// down arrow
410menu= 0x4B00;// right arrow
411}
412
413if ( key == previous )
414{
415if ( gMenuSelection > gMenuTop )
416draw.f.selectionUp = 1;
417else if ( gMenuTop > 0 )
418draw.f.scrollDown = 1;
419
420}
421
422else if ( key == subsequent )
423{
424if ( gMenuSelection != gMenuBottom)
425draw.f.selectionDown = 1;
426else if ( gMenuBottom < ( gMenuItemCount - 1 ) )
427draw.f.scrollUp = 1;
428}
429
430else if ( key == menu )
431{
432if ( gui.menu.draw )
433updateInfoMenu(key);
434else
435drawInfoMenu();
436}
437
438else if ( gui.menu.draw )
439{
440res = updateInfoMenu(key);
441
442if ( res == CLOSE_INFO_MENU )
443gui.menu.draw = false;
444else
445{
446shouldboot = ( res != DO_NOT_BOOT );
447
448if ( shouldboot )
449gui.menu.draw = false;
450
451switch (res)
452{
453case BOOT_NORMAL:
454gVerboseMode = false;
455gBootMode = kBootModeNormal;
456break;
457
458case BOOT_VERBOSE:
459gVerboseMode = true;
460gBootMode = kBootModeNormal;
461addBootArg(kVerboseModeFlag);
462break;
463
464case BOOT_IGNORECACHE:
465gVerboseMode = false;
466gBootMode = kBootModeNormal;
467addBootArg(kIgnoreCachesFlag);
468break;
469
470case BOOT_SINGLEUSER:
471gVerboseMode = true;
472gBootMode = kBootModeNormal;
473addBootArg(kSingleUserModeFlag);
474break;
475}
476
477}
478
479}
480
481} else {
482switch ( key )
483{
484 case 0x4800: // Up Arrow
485if ( gMenuSelection != gMenuTop )
486draw.f.selectionUp = 1;
487else if ( gMenuTop > 0 )
488draw.f.scrollDown = 1;
489break;
490
491case 0x5000: // Down Arrow
492if ( gMenuSelection != gMenuBottom )
493draw.f.selectionDown = 1;
494else if ( gMenuBottom < (gMenuItemCount - 1) )
495draw.f.scrollUp = 1;
496break;
497}
498}
499
500 if ( draw.w )
501 {
502 if ( draw.f.scrollUp )
503 {
504 scollPage(0, gMenuRow, 40, gMenuRow + gMenuHeight - 1, 0x07, 1, 1);
505 gMenuTop++; gMenuBottom++;
506gMenuStart++; gMenuEnd++;
507 draw.f.selectionDown = 1;
508 }
509
510 if ( draw.f.scrollDown )
511 {
512 scollPage(0, gMenuRow, 40, gMenuRow + gMenuHeight - 1, 0x07, 1, -1);
513 gMenuTop--; gMenuBottom--;
514 gMenuStart--; gMenuEnd--;
515 draw.f.selectionUp = 1;
516 }
517
518 if ( draw.f.selectionUp || draw.f.selectionDown )
519 {
520
521CursorState cursorState;
522
523// Set cursor at current position, and clear inverse video.
524
525if( bootArgs->Video.v_display == VGA_TEXT_MODE )
526{
527changeCursor( 0, gMenuRow + gMenuSelection - gMenuTop, kCursorTypeHidden, &cursorState );
528printMenuItem( &gMenuItems[gMenuSelection], 0 );
529}
530
531if ( draw.f.selectionUp )
532{
533gMenuSelection--;
534if(( gMenuSelection - gMenuStart) == -1 )
535{
536gMenuStart--;
537gMenuEnd--;
538}
539
540} else {
541gMenuSelection++;
542if(( gMenuSelection - ( gui.maxdevices - 1) - gMenuStart) > 0 )
543{
544gMenuStart++;
545gMenuEnd++;
546}
547 }
548
549if( bootArgs->Video.v_display == VGA_TEXT_MODE )
550 {
551moveCursor( 0, gMenuRow + gMenuSelection - gMenuTop );
552printMenuItem( &gMenuItems[gMenuSelection], 1 );
553restoreCursor( &cursorState );
554
555 } else
556
557drawDeviceList (gMenuStart, gMenuEnd, gMenuSelection);
558
559}
560
561 *paramPtr = gMenuItems[gMenuSelection].param;
562 moved = 1;
563 }
564
565return moved;
566}
567
568//==========================================================================
569
570static void skipblanks( const char ** cpp )
571{
572 while ( **(cpp) == ' ' || **(cpp) == '\t' ) ++(*cpp);
573}
574
575//==========================================================================
576
577static const char * extractKernelName( char ** cpp )
578{
579 char * kn = *cpp;
580 char * cp = *cpp;
581 char c;
582
583 // Convert char to lower case.
584
585 c = *cp | 0x20;
586
587 // Must start with a letter or a '/'.
588
589 if ( (c < 'a' || c > 'z') && ( c != '/' ) )
590 return 0;
591
592 // Keep consuming characters until we hit a separator.
593
594 while ( *cp && (*cp != '=') && (*cp != ' ') && (*cp != '\t') )
595 cp++;
596
597 // Only SPACE or TAB separator is accepted.
598 // Reject everything else.
599
600 if (*cp == '=')
601 return 0;
602
603 // Overwrite the separator, and move the pointer past
604 // the kernel name.
605
606 if (*cp != '\0') *cp++ = '\0';
607 *cpp = cp;
608
609 return kn;
610}
611
612//==========================================================================
613
614static void printMemoryInfo(void)
615{
616 int line;
617 int i;
618 MemoryRange *mp = bootInfo->memoryMap;
619
620 // Activate and clear page 1
621 setActiveDisplayPage(1);
622 clearScreenRows(0, 24);
623 setCursorPosition( 0, 0, 1 );
624
625 printf("BIOS reported memory ranges:\n");
626 line = 1;
627 for (i=0; i<bootInfo->memoryMapCount; i++) {
628 printf("Base 0x%08x%08x, ",
629 (unsigned long)(mp->base >> 32),
630 (unsigned long)(mp->base));
631 printf("length 0x%08x%08x, type %d\n",
632 (unsigned long)(mp->length >> 32),
633 (unsigned long)(mp->length),
634 mp->type);
635 if (line++ > 20) {
636 pause();
637 line = 0;
638 }
639 mp++;
640 }
641 if (line > 0) {
642 pause();
643 }
644
645 setActiveDisplayPage(0);
646}
647
648char *getMemoryInfoString()
649{
650int i, bufflen;
651MemoryRange *mp = bootInfo->memoryMap;
652char *buff = malloc(sizeof(char)*1024);
653if(!buff) {
654return 0;
655}
656
657static const char info[] = "BIOS reported memory ranges:\n";
658bufflen = sprintf(buff, "%s", info);
659
660for (i = 0;
661(i < bootInfo->memoryMapCount) && (bufflen < 1024); /* prevent buffer overflow */
662i++) {
663bufflen += snprintf(buff+bufflen, 1024-bufflen, "Base 0x%08x%08x, ",
664 (unsigned long)(mp->base >> 32),
665 (unsigned long)(mp->base));
666bufflen += snprintf(buff+bufflen, 1024-bufflen, "length 0x%08x%08x, type %d\n",
667 (unsigned long)(mp->length >> 32),
668 (unsigned long)(mp->length),
669 mp->type);
670mp++;
671}
672return buff;
673}
674
675//==========================================================================
676
677void lspci(void)
678{
679if (bootArgs->Video.v_display == VGA_TEXT_MODE) {
680setActiveDisplayPage(1);
681clearScreenRows(0, 24);
682setCursorPosition(0, 0, 1);
683}
684
685dump_pci_dt(root_pci_dev->children);
686
687pause();
688
689if (bootArgs->Video.v_display == VGA_TEXT_MODE) {
690setActiveDisplayPage(0);
691}
692}
693
694//==========================================================================
695
696int getBootOptions(bool firstRun)
697{
698int i;
699int key;
700int nextRow;
701int timeout;
702int bvCount;
703BVRef bvr;
704BVRef menuBVR;
705bool showPrompt, newShowPrompt, isCDROM;
706
707// Initialize default menu selection entry.
708gBootVolume = menuBVR = selectBootVolume(bvChain);
709
710if (biosDevIsCDROM(gBIOSDev)) {
711isCDROM = true;
712} else {
713isCDROM = false;
714}
715
716// ensure we're in graphics mode if gui is setup
717if (firstRun && gui.initialised && bootArgs->Video.v_display == VGA_TEXT_MODE)
718{
719setVideoMode(GRAPHICS_MODE, 0);
720}
721
722// Clear command line boot arguments
723clearBootArgs();
724
725// Allow user to override default timeout.
726if (multiboot_timeout_set) {
727timeout = multiboot_timeout;
728} else if (!getIntForKey(kTimeoutKey, &timeout, &bootInfo->chameleonConfig)) {
729/* If there is no timeout key in the file use the default timeout
730 which is different for CDs vs. hard disks. However, if not booting
731 a CD and no config file could be loaded set the timeout
732 to zero which causes the menu to display immediately.
733 This way, if no partitions can be found, that is the disk is unpartitioned
734 or simply cannot be read) then an empty menu is displayed.
735 If some partitions are found, for example a Windows partition, then
736 these will be displayed in the menu as foreign partitions.
737 */
738if (isCDROM) {
739timeout = kCDBootTimeout;
740} else {
741timeout = sysConfigValid ? kBootTimeout : 0;
742}
743}
744
745if (timeout < 0) {
746gBootMode |= kBootModeQuiet;
747}
748
749// If the user is holding down a modifier key, enter safe mode.
750if ((readKeyboardShiftFlags() & 0x0F) != 0) {
751gBootMode |= kBootModeSafe;
752}
753
754// Checking user pressed keys
755bool f8press = false, spress = false, vpress = false;
756while (readKeyboardStatus()) {
757key = bgetc ();
758if (key == 0x4200) f8press = true;
759if ((key & 0xff) == 's' || (key & 0xff) == 'S') spress = true;
760if ((key & 0xff) == 'v' || (key & 0xff) == 'V') vpress = true;
761}
762// If user typed F8, abort quiet mode, and display the menu.
763if (f8press) {
764gBootMode &= ~kBootModeQuiet;
765timeout = 0;
766}
767// If user typed 'v' or 'V', boot in verbose mode.
768if ((gBootMode & kBootModeQuiet) && firstRun && vpress) {
769addBootArg(kVerboseModeFlag);
770}
771// If user typed 's' or 'S', boot in single user mode.
772if ((gBootMode & kBootModeQuiet) && firstRun && spress) {
773addBootArg(kSingleUserModeFlag);
774}
775
776if (bootArgs->Video.v_display == VGA_TEXT_MODE) {
777setCursorPosition(0, 0, 0);
778clearScreenRows(0, kScreenLastRow);
779if (!(gBootMode & kBootModeQuiet)) {
780// Display banner and show hardware info.
781printf(bootBanner, (bootInfo->convmem + bootInfo->extmem) / 1024);
782printf(getVBEInfoString());
783}
784changeCursor(0, kMenuTopRow, kCursorTypeUnderline, 0);
785verbose("Scanning device %x...", gBIOSDev);
786}
787
788// When booting from CD, default to hard drive boot when possible.
789if (isCDROM && firstRun) {
790const char *val;
791char *prompt = NULL;
792char *name = NULL;
793int cnt;
794int optionKey;
795
796if (getValueForKey(kCDROMPromptKey, &val, &cnt, &bootInfo->chameleonConfig)) {
797prompt = malloc(cnt + 1);
798strncat(prompt, val, cnt);
799} else {
800name = malloc(80);
801getBootVolumeDescription(gBootVolume, name, 79, false);
802prompt = malloc(256);
803sprintf(prompt, "Press any key to start up from %s, or press F8 to enter startup options.", name);
804free(name);
805}
806
807if (getIntForKey( kCDROMOptionKey, &optionKey, &bootInfo->chameleonConfig )) {
808// The key specified is a special key.
809} else {
810// Default to F8.
811optionKey = 0x4200;
812}
813
814// If the timeout is zero then it must have been set above due to the
815// early catch of F8 which means the user wants to set boot options
816// which we ought to interpret as meaning he wants to boot the CD.
817if (timeout != 0) {
818key = countdown(prompt, kMenuTopRow, timeout);
819} else {
820key = optionKey;
821}
822
823if (prompt != NULL) {
824free(prompt);
825}
826
827clearScreenRows( kMenuTopRow, kMenuTopRow + 2 );
828
829// Hit the option key ?
830if (key == optionKey) {
831gBootMode &= ~kBootModeQuiet;
832timeout = 0;
833} else {
834key = key & 0xFF;
835
836// Try booting hard disk if user pressed 'h'
837if (biosDevIsCDROM(gBIOSDev) && key == 'h') {
838BVRef bvr;
839
840// Look at partitions hosting OS X other than the CD-ROM
841for (bvr = bvChain; bvr; bvr=bvr->next) {
842if ((bvr->flags & kBVFlagSystemVolume) && bvr->biosdev != gBIOSDev) {
843gBootVolume = bvr;
844}
845}
846}
847goto done;
848}
849}
850
851if (gBootMode & kBootModeQuiet) {
852// No input allowed from user.
853goto done;
854}
855
856if (firstRun && timeout > 0 && countdown("Press any key to enter startup options.", kMenuTopRow, timeout) == 0) {
857// If the user is holding down a modifier key,
858// enter safe mode.
859if ((readKeyboardShiftFlags() & 0x0F) != 0) {
860gBootMode |= kBootModeSafe;
861}
862goto done;
863}
864
865if (gDeviceCount > 0) {
866// Allocate memory for an array of menu items.
867menuItems = malloc(sizeof(MenuItem) * gDeviceCount);
868if (menuItems == NULL) {
869goto done;
870}
871
872// Associate a menu item for each BVRef.
873for (bvr=bvChain, i=gDeviceCount-1, selectIndex=-1; bvr; bvr=bvr->next) {
874if (bvr->visible) {
875getBootVolumeDescription(bvr, menuItems[i].name, sizeof(menuItems[i].name) - 1, true);
876menuItems[i].param = (void *) bvr;
877if (bvr == menuBVR) {
878selectIndex = i;
879}
880i--;
881}
882}
883// Jief : In case the default partition (returned by selectBootVolume) is not in the menu
884if ( selectIndex == -1 )
885{
886selectIndex = 0;
887
888// gDeviceCount is actually > 0, so menuItems[selectIndex] exists
889menuBVR = (BVRef)(menuItems[selectIndex].param);
890// what happen is bvChain is empty ?
891}
892}
893
894if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
895// redraw the background buffer
896gui.logo.draw = true;
897drawBackground();
898gui.devicelist.draw = true;
899gui.redraw = true;
900if (!(gBootMode & kBootModeQuiet)) {
901
902// Check if "Boot Banner"=N switch is present in config file.
903getBoolForKey(kBootBannerKey, &showBootBanner, &bootInfo->chameleonConfig);
904if (showBootBanner) {
905// Display banner and show hardware info.
906gprintf(&gui.screen, bootBanner + 1, (bootInfo->convmem + bootInfo->extmem) / 1024);
907}
908
909// redraw background
910memcpy(gui.backbuffer->pixels, gui.screen.pixmap->pixels, gui.backbuffer->width * gui.backbuffer->height * 4);
911}
912} else {
913// Clear screen and hide the blinking cursor.
914clearScreenRows(kMenuTopRow, kMenuTopRow + 2);
915changeCursor(0, kMenuTopRow, kCursorTypeHidden, 0);
916}
917
918nextRow = kMenuTopRow;
919showPrompt = true;
920
921if (gDeviceCount) {
922if( bootArgs->Video.v_display == VGA_TEXT_MODE ) {
923printf("Use \30\31 keys to select the startup volume.");
924}
925showMenu( menuItems, gDeviceCount, selectIndex, kMenuTopRow + 2, kMenuMaxItems );
926nextRow += MIN( gDeviceCount, kMenuMaxItems ) + 3;
927}
928
929// Show the boot prompt.
930showPrompt = (gDeviceCount == 0) || (menuBVR->flags & kBVFlagNativeBoot);
931showBootPrompt( nextRow, showPrompt );
932
933do {
934if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
935// redraw background
936memcpy( gui.backbuffer->pixels, gui.screen.pixmap->pixels, gui.backbuffer->width * gui.backbuffer->height * 4 );
937// reset cursor co-ords
938gui.debug.cursor = pos( gui.screen.width - 160 , 10 );
939}
940key = getchar();
941updateMenu( key, (void **) &menuBVR );
942newShowPrompt = (gDeviceCount == 0) || (menuBVR->flags & kBVFlagNativeBoot);
943
944if (newShowPrompt != showPrompt) {
945showPrompt = newShowPrompt;
946showBootPrompt( nextRow, showPrompt );
947}
948
949if (showPrompt) {
950updateBootArgs(key);
951}
952
953switch (key) {
954case KEY_ENTER:
955if (gui.menu.draw) {
956key=0;
957break;
958}
959if (*gBootArgs == '?') {
960char * argPtr = gBootArgs;
961
962// Skip the leading "?" character.
963argPtr++;
964getNextArg(&argPtr, booterCommand);
965getNextArg(&argPtr, booterParam);
966
967/*
968* TODO: this needs to be refactored.
969*/
970if (strcmp( booterCommand, "video" ) == 0) {
971if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
972showInfoBox(getVBEInfoString(), getVBEModeInfoString());
973} else {
974printVBEModeInfo();
975}
976} else if ( strcmp( booterCommand, "memory" ) == 0) {
977if (bootArgs->Video.v_display != VGA_TEXT_MODE ) {
978showInfoBox("Memory Map", getMemoryInfoString());
979} else {
980printMemoryInfo();
981}
982} else if (strcmp(booterCommand, "lspci") == 0) {
983lspci();
984} else if (strcmp(booterCommand, "more") == 0) {
985showTextFile(booterParam);
986} else if (strcmp(booterCommand, "rd") == 0) {
987processRAMDiskCommand(&argPtr, booterParam);
988} else if (strcmp(booterCommand, "norescan") == 0) {
989if (gEnableCDROMRescan) {
990gEnableCDROMRescan = false;
991break;
992}
993} else {
994showHelp();
995}
996key = 0;
997showBootPrompt(nextRow, showPrompt);
998break;
999}
1000gBootVolume = menuBVR;
1001setRootVolume(menuBVR);
1002gBIOSDev = menuBVR->biosdev;
1003break;
1004
1005case KEY_ESC:
1006clearBootArgs();
1007break;
1008
1009case KEY_F5:
1010// New behavior:
1011// Clear gBootVolume to restart the loop
1012// if the user enabled rescanning the optical drive.
1013// Otherwise boot the default boot volume.
1014if (gEnableCDROMRescan) {
1015gBootVolume = NULL;
1016clearBootArgs();
1017}
1018break;
1019
1020case KEY_F10:
1021gScanSingleDrive = false;
1022scanDisks(gBIOSDev, &bvCount);
1023gBootVolume = NULL;
1024clearBootArgs();
1025break;
1026
1027case KEY_TAB:
1028// New behavior:
1029// Switch between text & graphic interfaces
1030// Only Permitted if started in graphics interface
1031if (useGUI) {
1032if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
1033setVideoMode(VGA_TEXT_MODE, 0);
1034
1035setCursorPosition(0, 0, 0);
1036clearScreenRows(0, kScreenLastRow);
1037
1038// Display banner and show hardware info.
1039printf(bootBanner, (bootInfo->convmem + bootInfo->extmem) / 1024);
1040printf(getVBEInfoString());
1041
1042clearScreenRows(kMenuTopRow, kMenuTopRow + 2);
1043changeCursor(0, kMenuTopRow, kCursorTypeHidden, 0);
1044
1045nextRow = kMenuTopRow;
1046showPrompt = true;
1047
1048if (gDeviceCount) {
1049printf("Use \30\31 keys to select the startup volume.");
1050showMenu(menuItems, gDeviceCount, selectIndex, kMenuTopRow + 2, kMenuMaxItems);
1051nextRow += MIN(gDeviceCount, kMenuMaxItems) + 3;
1052}
1053
1054showPrompt = (gDeviceCount == 0) || (menuBVR->flags & kBVFlagNativeBoot);
1055showBootPrompt(nextRow, showPrompt);
1056//changeCursor( 0, kMenuTopRow, kCursorTypeUnderline, 0 );
1057} else {
1058gui.redraw = true;
1059setVideoMode(GRAPHICS_MODE, 0);
1060updateVRAM();
1061 updateGraphicBootPrompt();
1062}
1063}
1064key = 0;
1065break;
1066
1067default:
1068key = 0;
1069break;
1070}
1071} while (0 == key);
1072
1073done:
1074if (bootArgs->Video.v_display == VGA_TEXT_MODE) {
1075clearScreenRows(kMenuTopRow, kScreenLastRow);
1076changeCursor(0, kMenuTopRow, kCursorTypeUnderline, 0);
1077}
1078shouldboot = false;
1079gui.menu.draw = false;
1080if (menuItems) {
1081free(menuItems);
1082menuItems = NULL;
1083}
1084execute_hook("BootOptions", gBootArgs, gBootArgsPtr, NULL, NULL);
1085return 0;
1086}
1087
1088//==========================================================================
1089
1090char gBootUUIDString[32+4+1] = ""; // UUID of the boot volume e.g. 5EB1869F-C4FA-3502-BDEB-3B8ED5D87292
1091extern unsigned char chainbootdev;
1092extern unsigned char chainbootflag;
1093
1094bool copyArgument(const char *argName, const char *val, int cnt, char **argP, int *cntRemainingP)
1095{
1096int argLen = argName ? strlen(argName) : 0;
1097int len = argLen + cnt + 1; // + 1 to account for space.
1098
1099if (argName)
1100{
1101len++; // +1 to account for '='
1102}
1103
1104if (len > *cntRemainingP) {
1105error("Warning: boot arguments too long, truncating\n");
1106return false;
1107}
1108
1109if (argName)
1110{
1111strncpy(*argP, argName, argLen);
1112*argP += argLen;
1113*argP[0] = '=';
1114(*argP)++;
1115}
1116
1117strncpy(*argP, val, cnt);
1118*argP += cnt;
1119*argP[0] = ' ';
1120(*argP)++;
1121*cntRemainingP -= len;
1122
1123return true;
1124}
1125
1126//
1127// Returns TRUE if an argument was copied, FALSE otherwise
1128bool
1129processBootArgument(
1130 const char *argName, // The argument to search for
1131 const char *userString, // Typed-in boot arguments
1132 const char *kernelFlags, // Kernel flags from config table
1133 const char *configTable,
1134 char **argP, // Output value
1135 int *cntRemainingP, // Output count
1136 char *foundVal, // found value
1137 int foundValSize // max found value size
1138 ) {
1139const char *val;
1140int cnt;
1141bool found = false;
1142
1143if (getValueForBootKey(userString, argName, &val, &cnt)) {
1144// Don't copy; these values will be copied at the end of argument processing.
1145found = true;
1146} else if (getValueForBootKey(kernelFlags, argName, &val, &cnt)) {
1147// Don't copy; these values will be copied at the end of argument processing.
1148found = true;
1149} else if (getValueForKey(argName, &val, &cnt, &bootInfo->chameleonConfig)) {
1150copyArgument(argName, val, cnt, argP, cntRemainingP);
1151found = true;
1152}
1153if (found && foundVal) {
1154strlcpy(foundVal, val, foundValSize);
1155}
1156return found;
1157}
1158
1159// Maximum config table value size
1160#define VALUE_SIZE 2048
1161
1162int
1163processBootOptions()
1164{
1165const char *cp = gBootArgs;
1166const char *val = 0;
1167const char *kernel;
1168int cnt;
1169int userCnt;
1170int cntRemaining;
1171char *argP;
1172char *configKernelFlags;
1173char *valueBuffer;
1174
1175valueBuffer = malloc(VALUE_SIZE);
1176
1177skipblanks( &cp );
1178
1179// Update the unit and partition number.
1180
1181if ( gBootVolume ) {
1182if (!( gBootVolume->flags & kBVFlagNativeBoot )) {
1183readBootSector( gBootVolume->biosdev, gBootVolume->part_boff, (void *) 0x7c00 );
1184//
1185// Setup edx, and signal intention to chain load the
1186// foreign booter.
1187//
1188
1189chainbootdev = gBootVolume->biosdev;
1190chainbootflag = 1;
1191
1192return 1;
1193}
1194
1195setRootVolume(gBootVolume);
1196
1197}
1198// If no boot volume fail immediately because we're just going to fail
1199// trying to load the config file anyway.
1200else {
1201return -1;
1202}
1203
1204// Find out which version mac os we're booting.
1205strncpy(gMacOSVersion, gBootVolume->OSVersion, sizeof(gMacOSVersion));
1206
1207// Load config table specified by the user, or use the default.
1208
1209if (!getValueForBootKey(cp, "config", &val, &cnt)) {
1210val = 0;
1211cnt = 0;
1212}
1213
1214// Load com.apple.Boot.plist from the selected volume
1215// and use its contents to override default bootConfig.
1216
1217loadSystemConfig(&bootInfo->bootConfig);
1218loadChameleonConfig(&bootInfo->chameleonConfig, NULL);
1219
1220// Use the kernel name specified by the user, or fetch the name
1221// in the config table, or use the default if not specified.
1222// Specifying a kernel name on the command line, or specifying
1223// a non-default kernel name in the config file counts as
1224// overriding the kernel, which causes the kernelcache not
1225// to be used.
1226
1227gOverrideKernel = false;
1228if (( kernel = extractKernelName((char **)&cp) ))
1229{
1230strlcpy( bootInfo->bootFile, kernel, sizeof(bootInfo->bootFile) );
1231}
1232else
1233{
1234if ( getValueForKey( kKernelNameKey, &val, &cnt, &bootInfo->bootConfig ) )
1235{
1236strlcpy( bootInfo->bootFile, val, cnt+1 );
1237}
1238else
1239{
1240if( YOSEMITE ) // is 10.10
1241{
1242
1243strlcpy( bootInfo->bootFile, kOSXKernel, sizeof(bootInfo->bootFile) );
1244//printf(HEADER "/System/Library/Kernels/%s\n", bootInfo->bootFile);
1245}
1246else
1247{ // OSX is not 10.10
1248
1249strlcpy( bootInfo->bootFile, kDefaultKernel, sizeof(bootInfo->bootFile) );
1250//printf(HEADER "/%s\n", bootInfo->bootFile);
1251}
1252}
1253}
1254
1255if (!YOSEMITE) // not 10.10 so 10.9 and previus
1256{
1257if (strcmp( bootInfo->bootFile, kDefaultKernel ) != 0)
1258{
1259 //printf(HEADER "org.chameleon.Boot.plist found path for custom '%s' found!\n", bootInfo->bootFile);
1260gOverrideKernel = true;
1261}
1262}
1263else
1264{ // OSX is 10.10
1265if (strcmp( bootInfo->bootFile, kOSXKernel ) != 0)
1266{
1267 //printf(HEADER "org.chameleon.Boot.plist found path for custom '%s' found!\n", bootInfo->bootFile);
1268gOverrideKernel = true;
1269}
1270}
1271
1272cntRemaining = BOOT_STRING_LEN - 2; // save 1 for NULL, 1 for space
1273argP = bootArgs->CommandLine;
1274
1275// Get config kernel flags, if not ignored.
1276if (getValueForBootKey(cp, kIgnoreBootFileFlag, &val, &cnt) ||
1277 !getValueForKey( kKernelFlagsKey, &val, &cnt, &bootInfo->bootConfig ))
1278{
1279val = "";
1280cnt = 0;
1281}
1282configKernelFlags = malloc(cnt + 1);
1283strlcpy(configKernelFlags, val, cnt + 1);
1284
1285// boot-uuid can be set either on the command-line or in the config file
1286if (!processBootArgument(kBootUUIDKey, cp, configKernelFlags, bootInfo->config,
1287 &argP, &cntRemaining, gBootUUIDString, sizeof(gBootUUIDString)))
1288{
1289//
1290// Try an alternate method for getting the root UUID on boot helper partitions.
1291//
1292if (gBootVolume->flags & kBVFlagBooter)
1293{
1294// Load the configuration store in the boot helper partition
1295if (loadHelperConfig(&bootInfo->helperConfig) == 0)
1296{
1297val = getStringForKey(kHelperRootUUIDKey, &bootInfo->helperConfig);
1298if (val != NULL)
1299{
1300strlcpy(gBootUUIDString, val, sizeof(gBootUUIDString));
1301}
1302}
1303}
1304/*
1305// Try to get the volume uuid string
1306if (!strlen(gBootUUIDString) && gBootVolume->fs_getuuid)
1307{
1308gBootVolume->fs_getuuid(gBootVolume, gBootUUIDString);
1309}
1310*/
1311// If we have the volume uuid add it to the commandline arguments
1312if (strlen(gBootUUIDString))
1313{
1314copyArgument(kBootUUIDKey, gBootUUIDString, strlen(gBootUUIDString), &argP, &cntRemaining);
1315}
1316// Try to get the volume uuid string
1317if (!strlen(gBootUUIDString) && gBootVolume->fs_getuuid)
1318{
1319gBootVolume->fs_getuuid(gBootVolume, gBootUUIDString);
1320DBG("boot-uuid: %s\n", gBootUUIDString);
1321}
1322}
1323
1324if (!processBootArgument(kRootDeviceKey, cp, configKernelFlags, bootInfo->config,
1325 &argP, &cntRemaining, gRootDevice, ROOT_DEVICE_SIZE))
1326{
1327cnt = 0;
1328if ( getValueForKey( kBootDeviceKey, &val, &cnt, &bootInfo->chameleonConfig))
1329{
1330valueBuffer[0] = '*';
1331cnt++;
1332strlcpy(valueBuffer + 1, val, cnt);
1333val = valueBuffer;
1334}
1335else
1336{ /*
1337if (strlen(gBootUUIDString))
1338{
1339val = "*uuid";
1340cnt = 5;
1341}
1342else
1343{ */
1344// Don't set "rd=.." if there is no boot device key
1345// and no UUID.
1346val = "";
1347cnt = 0;
1348/* } */
1349}
1350
1351if (cnt > 0)
1352{
1353copyArgument( kRootDeviceKey, val, cnt, &argP, &cntRemaining);
1354}
1355strlcpy( gRootDevice, val, (cnt + 1));
1356}
1357
1358/*
1359 * Removed. We don't need this anymore.
1360 *
1361if (!processBootArgument(kPlatformKey, cp, configKernelFlags, bootInfo->config,
1362 &argP, &cntRemaining, gPlatformName, sizeof(gCacheNameAdler)))
1363{
1364getPlatformName(gPlatformName);
1365copyArgument(kPlatformKey, gPlatformName, strlen(gPlatformName), &argP, &cntRemaining);
1366}
1367*/
1368
1369if (!getValueForBootKey(cp, kSafeModeFlag, &val, &cnt) &&
1370 !getValueForBootKey(configKernelFlags, kSafeModeFlag, &val, &cnt))
1371{
1372if (gBootMode & kBootModeSafe)
1373{
1374copyArgument(0, kSafeModeFlag, strlen(kSafeModeFlag), &argP, &cntRemaining);
1375}
1376}
1377
1378// Store the merged kernel flags and boot args.
1379
1380cnt = strlen(configKernelFlags);
1381if (cnt)
1382{
1383if (cnt > cntRemaining)
1384{
1385error("Warning: boot arguments too long, truncating\n");
1386cnt = cntRemaining;
1387}
1388strncpy(argP, configKernelFlags, cnt);
1389argP[cnt++] = ' ';
1390cntRemaining -= cnt;
1391}
1392userCnt = strlen(cp);
1393if (userCnt > cntRemaining)
1394{
1395error("Warning: boot arguments too long, truncating\n");
1396userCnt = cntRemaining;
1397}
1398strncpy(&argP[cnt], cp, userCnt);
1399argP[cnt+userCnt] = '\0';
1400
1401if(!shouldboot)
1402{
1403gVerboseMode = getValueForKey( kVerboseModeFlag, &val, &cnt, &bootInfo->chameleonConfig ) ||
1404getValueForKey( kSingleUserModeFlag, &val, &cnt, &bootInfo->chameleonConfig );
1405
1406gBootMode = ( getValueForKey( kSafeModeFlag, &val, &cnt, &bootInfo->chameleonConfig ) ) ?
1407kBootModeSafe : kBootModeNormal;
1408
1409if ( getValueForKey( kIgnoreCachesFlag, &val, &cnt, &bootInfo->chameleonConfig ) )
1410{
1411gBootMode = kBootModeSafe;
1412}
1413}
1414
1415if ( getValueForKey( kMKextCacheKey, &val, &cnt, &bootInfo->bootConfig ) )
1416{
1417strlcpy(gMKextName, val, cnt + 1);
1418}
1419else
1420{
1421gMKextName[0]=0;
1422}
1423
1424free(configKernelFlags);
1425free(valueBuffer);
1426
1427return 0;
1428}
1429
1430
1431//==========================================================================
1432// Load the help file and display the file contents on the screen.
1433
1434void showTextBuffer(char *buf_orig, int size)
1435{
1436char*bp;
1437char* buf;
1438intline;
1439intline_offset;
1440intc;
1441
1442if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
1443showInfoBox( "Press q to continue, space for next page.\n",buf_orig );
1444return;
1445}
1446
1447// Create a copy so that we don't mangle the original
1448buf = malloc(size + 1);
1449memcpy(buf, buf_orig, size);
1450
1451
1452 bp = buf;
1453 while (size-- > 0) {
1454if (*bp == '\n') {
1455*bp = '\0';
1456}
1457bp++;
1458 }
1459 *bp = '\1';
1460 line_offset = 0;
1461
1462 setActiveDisplayPage(1);
1463
1464 while (1) {
1465clearScreenRows(0, 24);
1466setCursorPosition(0, 0, 1);
1467bp = buf;
1468for (line = 0; *bp != '\1' && line < line_offset; line++) {
1469while (*bp != '\0') {
1470bp++;
1471}
1472bp++;
1473}
1474for (line = 0; *bp != '\1' && line < 23; line++) {
1475setCursorPosition(0, line, 1);
1476printf("%s\n", bp);
1477while (*bp != '\0') {
1478bp++;
1479}
1480bp++;
1481}
1482
1483setCursorPosition(0, 23, 1);
1484if (*bp == '\1') {
1485printf("[Type %sq or space to quit viewer]", (line_offset > 0) ? "p for previous page, " : "");
1486} else {
1487printf("[Type %s%sq to quit viewer]", (line_offset > 0) ? "p for previous page, " : "", (*bp != '\1') ? "space for next page, " : "");
1488}
1489
1490c = getchar();
1491if (c == 'q' || c == 'Q') {
1492break;
1493}
1494if ((c == 'p' || c == 'P') && line_offset > 0) {
1495line_offset -= 23;
1496}
1497if (c == ' ') {
1498if (*bp == '\1') {
1499break;
1500} else {
1501line_offset += 23;
1502}
1503}
1504 }
1505 setActiveDisplayPage(0);
1506}
1507
1508void showHelp(void)
1509{
1510if (bootArgs->Video.v_display != VGA_TEXT_MODE)
1511{
1512showInfoBox("Help. Press q to quit.\n", (char *)BootHelp_txt);
1513}
1514else
1515{
1516showTextBuffer((char *)BootHelp_txt, BootHelp_txt_len);
1517}
1518}
1519
1520void showTextFile(const char * filename)
1521{
1522#define MAX_TEXT_FILE_SIZE 65536
1523char*buf;
1524intfd;
1525intsize;
1526
1527if ((fd = open_bvdev("bt(0,0)", filename, 0)) < 0)
1528{
1529printf("\nFile not found: %s\n", filename);
1530sleep(2);
1531return;
1532}
1533
1534 size = file_size(fd);
1535 if (size > MAX_TEXT_FILE_SIZE)
1536{
1537size = MAX_TEXT_FILE_SIZE;
1538}
1539 buf = malloc(size);
1540 read(fd, buf, size);
1541 close(fd);
1542showTextBuffer(buf, size);
1543free(buf);
1544}
1545
1546// This is a very simplistic prompting scheme that just grabs two hex characters
1547// Eventually we need to do something more user-friendly like display a menu
1548// based off of the Multiboot device list
1549
1550int selectAlternateBootDevice(int bootdevice)
1551{
1552int key;
1553int newbootdevice;
1554int digitsI = 0;
1555char *end;
1556char digits[3] = {0,0,0};
1557
1558// We've already printed the current boot device so user knows what it is
1559printf("Typical boot devices are 80 (First HD), 81 (Second HD)\n");
1560printf("Enter two-digit hexadecimal boot device [%02x]: ", bootdevice);
1561do {
1562key = getchar();
1563switch (ASCII_KEY(key))
1564{
1565case KEY_BKSP:
1566if (digitsI > 0)
1567{
1568int x, y, t;
1569getCursorPositionAndType(&x, &y, &t);
1570// Assume x is not 0;
1571x--;
1572setCursorPosition(x,y,0); // back up one char
1573// Overwrite with space without moving cursor position
1574putca(' ', 0x07, 1);
1575digitsI--;
1576}
1577else
1578{
1579// TODO: Beep or something
1580}
1581break;
1582
1583case KEY_ENTER:
1584digits[digitsI] = '\0';
1585newbootdevice = strtol(digits, &end, 16);
1586if (end == digits && *end == '\0')
1587{
1588// User entered empty string
1589printf("\nUsing default boot device %x\n", bootdevice);
1590key = 0;
1591} else if(end != digits && *end == '\0') {
1592bootdevice = newbootdevice;
1593printf("\n");
1594key = 0; // We gots da boot device
1595} else {
1596printf("\nCouldn't parse. try again: ");
1597digitsI = 0;
1598}
1599break;
1600
1601default:
1602if (isxdigit(ASCII_KEY(key)) && digitsI < 2)
1603{
1604putchar(ASCII_KEY(key));
1605digits[digitsI++] = ASCII_KEY(key);
1606}
1607else
1608{
1609// TODO: Beep or something
1610}
1611break;
1612};
1613} while (key != 0);
1614
1615return bootdevice;
1616}
1617
1618bool promptForRescanOption(void)
1619{
1620printf("\nWould you like to enable media rescan option?\nPress ENTER to enable or any key to skip.\n");
1621if (getchar() == KEY_ENTER)
1622{
1623return true;
1624}
1625else
1626{
1627return false;
1628}
1629}
1630

Archive Download this file

Revision: 2471