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

Archive Download this file

Revision: 2537