Chameleon

Chameleon Svn Source Tree

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

Archive Download this file

Revision: 2469