Chameleon

Chameleon Svn Source Tree

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

Archive Download this file

Revision: 2582