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{
454 int moved = 0;
455
456 union {
457 struct {
458 unsigned int
459 selectionUp : 1,
460 selectionDown : 1,
461 scrollUp : 1,
462 scrollDown : 1;
463 } f;
464 unsigned int w;
465 } draw = {{0}};
466
467 if ( 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} else {
556switch ( key )
557{
558 case 0x4800: // Up Arrow
559if ( gMenuSelection != gMenuTop )
560draw.f.selectionUp = 1;
561else if ( gMenuTop > 0 )
562draw.f.scrollDown = 1;
563break;
564
565case 0x5000: // Down Arrow
566if ( gMenuSelection != gMenuBottom )
567draw.f.selectionDown = 1;
568else if ( gMenuBottom < (gMenuItemCount - 1) )
569draw.f.scrollUp = 1;
570break;
571}
572}
573
574 if ( draw.w )
575 {
576 if ( draw.f.scrollUp )
577 {
578 scollPage(0, gMenuRow, 40, gMenuRow + gMenuHeight - 1, 0x07, 1, 1);
579 gMenuTop++; gMenuBottom++;
580gMenuStart++; gMenuEnd++;
581 draw.f.selectionDown = 1;
582 }
583
584 if ( draw.f.scrollDown )
585 {
586 scollPage(0, gMenuRow, 40, gMenuRow + gMenuHeight - 1, 0x07, 1, -1);
587 gMenuTop--; gMenuBottom--;
588 gMenuStart--; gMenuEnd--;
589 draw.f.selectionUp = 1;
590 }
591
592 if ( draw.f.selectionUp || draw.f.selectionDown )
593 {
594
595CursorState cursorState;
596
597// Set cursor at current position, and clear inverse video.
598
599if( bootArgs->Video.v_display == VGA_TEXT_MODE )
600{
601changeCursor( 0, gMenuRow + gMenuSelection - gMenuTop, kCursorTypeHidden, &cursorState );
602printMenuItem( &gMenuItems[gMenuSelection], 0 );
603}
604
605if ( draw.f.selectionUp )
606{
607gMenuSelection--;
608if(( gMenuSelection - gMenuStart) == -1 )
609{
610gMenuStart--;
611gMenuEnd--;
612}
613
614} else {
615gMenuSelection++;
616if(( gMenuSelection - ( gui.maxdevices - 1) - gMenuStart) > 0 )
617{
618gMenuStart++;
619gMenuEnd++;
620}
621 }
622
623if( bootArgs->Video.v_display == VGA_TEXT_MODE )
624 {
625moveCursor( 0, gMenuRow + gMenuSelection - gMenuTop );
626printMenuItem( &gMenuItems[gMenuSelection], 1 );
627restoreCursor( &cursorState );
628
629 } else
630
631drawDeviceList (gMenuStart, gMenuEnd, gMenuSelection);
632
633}
634
635 *paramPtr = gMenuItems[gMenuSelection].param;
636 moved = 1;
637 }
638
639return moved;
640}
641
642//==========================================================================
643
644static void skipblanks( const char ** cpp )
645{
646 while ( **(cpp) == ' ' || **(cpp) == '\t' ) ++(*cpp);
647}
648
649//==========================================================================
650
651static const char * extractKernelName( char ** cpp )
652{
653 char * kn = *cpp;
654 char * cp = *cpp;
655 char c;
656
657 // Convert char to lower case.
658
659 c = *cp | 0x20;
660
661 // Must start with a letter or a '/'.
662
663 if ( (c < 'a' || c > 'z') && ( c != '/' ) )
664 return 0;
665
666 // Keep consuming characters until we hit a separator.
667
668 while ( *cp && (*cp != '=') && (*cp != ' ') && (*cp != '\t') )
669 cp++;
670
671 // Only SPACE or TAB separator is accepted.
672 // Reject everything else.
673
674 if (*cp == '=')
675 return 0;
676
677 // Overwrite the separator, and move the pointer past
678 // the kernel name.
679
680 if (*cp != '\0') *cp++ = '\0';
681 *cpp = cp;
682
683 return kn;
684}
685
686//==========================================================================
687
688static void printMemoryInfo(void)
689{
690 int line;
691 int i;
692 MemoryRange *mp = bootInfo->memoryMap;
693
694 // Activate and clear page 1
695 setActiveDisplayPage(1);
696 clearScreenRows(0, 24);
697 setCursorPosition( 0, 0, 1 );
698
699 printf("BIOS reported memory ranges:\n");
700 line = 1;
701 for (i=0; i<bootInfo->memoryMapCount; i++) {
702 printf("Base 0x%08x%08x, ",
703 (unsigned long)(mp->base >> 32),
704 (unsigned long)(mp->base));
705 printf("length 0x%08x%08x, type %d\n",
706 (unsigned long)(mp->length >> 32),
707 (unsigned long)(mp->length),
708 mp->type);
709 if (line++ > 20) {
710 pause();
711 line = 0;
712 }
713 mp++;
714 }
715 if (line > 0) {
716 pause();
717 }
718
719 setActiveDisplayPage(0);
720}
721
722char *getMemoryInfoString()
723{
724int i, bufflen;
725MemoryRange *mp = bootInfo->memoryMap;
726char *buff = malloc(sizeof(char)*1024);
727if(!buff) {
728return 0;
729}
730
731static const char info[] = "BIOS reported memory ranges:\n";
732bufflen = sprintf(buff, "%s", info);
733
734for (i = 0;
735(i < bootInfo->memoryMapCount) && (bufflen < 1024); /* prevent buffer overflow */
736i++) {
737bufflen += snprintf(buff+bufflen, 1024-bufflen, "Base 0x%08x%08x, ",
738 (unsigned long)(mp->base >> 32),
739 (unsigned long)(mp->base));
740bufflen += snprintf(buff+bufflen, 1024-bufflen, "length 0x%08x%08x, type %d\n",
741 (unsigned long)(mp->length >> 32),
742 (unsigned long)(mp->length),
743 mp->type);
744mp++;
745}
746return buff;
747}
748
749//==========================================================================
750
751void lspci(void)
752{
753if (bootArgs->Video.v_display == VGA_TEXT_MODE) {
754setActiveDisplayPage(1);
755clearScreenRows(0, 24);
756setCursorPosition(0, 0, 1);
757}
758
759dump_pci_dt(root_pci_dev->children);
760
761pause();
762
763if (bootArgs->Video.v_display == VGA_TEXT_MODE) {
764setActiveDisplayPage(0);
765}
766}
767
768//==========================================================================
769
770int getBootOptions(bool firstRun)
771{
772int i;
773int key;
774int nextRow;
775int timeout;
776int bvCount;
777BVRef bvr;
778BVRef menuBVR;
779bool showPrompt, newShowPrompt, isCDROM;
780
781// Initialize default menu selection entry.
782gBootVolume = menuBVR = selectBootVolume(bvChain);
783
784if (biosDevIsCDROM(gBIOSDev)) {
785isCDROM = true;
786} else {
787isCDROM = false;
788}
789
790// ensure we're in graphics mode if gui is setup
791if (firstRun && gui.initialised && bootArgs->Video.v_display == VGA_TEXT_MODE)
792{
793setVideoMode(GRAPHICS_MODE, 0);
794}
795
796// Clear command line boot arguments
797clearBootArgs();
798
799// Allow user to override default timeout.
800if (multiboot_timeout_set) {
801timeout = multiboot_timeout;
802} else if (!getIntForKey(kTimeoutKey, &timeout, &bootInfo->chameleonConfig)) {
803/* If there is no timeout key in the file use the default timeout
804 which is different for CDs vs. hard disks. However, if not booting
805 a CD and no config file could be loaded set the timeout
806 to zero which causes the menu to display immediately.
807 This way, if no partitions can be found, that is the disk is unpartitioned
808 or simply cannot be read) then an empty menu is displayed.
809 If some partitions are found, for example a Windows partition, then
810 these will be displayed in the menu as foreign partitions.
811 */
812if (isCDROM) {
813timeout = kCDBootTimeout;
814} else {
815timeout = sysConfigValid ? kBootTimeout : 0;
816}
817}
818
819if (timeout < 0) {
820gBootMode |= kBootModeQuiet;
821}
822
823// If the user is holding down a modifier key, enter safe mode.
824if ((readKeyboardShiftFlags() & 0x0F) != 0) {
825gBootMode |= kBootModeSafe;
826}
827
828// Checking user pressed keys
829bool f8press = false, spress = false, vpress = false;
830while (readKeyboardStatus()) {
831key = bgetc ();
832if (key == 0x4200) f8press = true;
833if ((key & 0xff) == 's' || (key & 0xff) == 'S') spress = true;
834if ((key & 0xff) == 'v' || (key & 0xff) == 'V') vpress = true;
835}
836// If user typed F8, abort quiet mode, and display the menu.
837if (f8press) {
838gBootMode &= ~kBootModeQuiet;
839timeout = 0;
840}
841// If user typed 'v' or 'V', boot in verbose mode.
842if ((gBootMode & kBootModeQuiet) && firstRun && vpress) {
843addBootArg(kVerboseModeFlag);
844}
845// If user typed 's' or 'S', boot in single user mode.
846if ((gBootMode & kBootModeQuiet) && firstRun && spress) {
847addBootArg(kSingleUserModeFlag);
848}
849
850if (bootArgs->Video.v_display == VGA_TEXT_MODE) {
851setCursorPosition(0, 0, 0);
852clearScreenRows(0, kScreenLastRow);
853if (!(gBootMode & kBootModeQuiet)) {
854// Display banner and show hardware info.
855printf(bootBanner, (bootInfo->convmem + bootInfo->extmem) / 1024);
856printf(getVBEInfoString());
857}
858changeCursor(0, kMenuTopRow, kCursorTypeUnderline, 0);
859verbose("Scanning device %x...", gBIOSDev);
860}
861
862// When booting from CD, default to hard drive boot when possible.
863if (isCDROM && firstRun) {
864const char *val;
865char *prompt = NULL;
866char *name = NULL;
867int cnt;
868int optionKey;
869
870if (getValueForKey(kCDROMPromptKey, &val, &cnt, &bootInfo->chameleonConfig)) {
871prompt = malloc(cnt + 1);
872strncat(prompt, val, cnt);
873} else {
874name = malloc(80);
875getBootVolumeDescription(gBootVolume, name, 79, false);
876prompt = malloc(256);
877sprintf(prompt, "Press any key to start up from %s, or press F8 to enter startup options.", name);
878free(name);
879}
880
881if (getIntForKey( kCDROMOptionKey, &optionKey, &bootInfo->chameleonConfig )) {
882// The key specified is a special key.
883} else {
884// Default to F8.
885optionKey = 0x4200;
886}
887
888// If the timeout is zero then it must have been set above due to the
889// early catch of F8 which means the user wants to set boot options
890// which we ought to interpret as meaning he wants to boot the CD.
891if (timeout != 0) {
892key = countdown(prompt, kMenuTopRow, timeout);
893} else {
894key = optionKey;
895}
896
897if (prompt != NULL) {
898free(prompt);
899}
900
901clearScreenRows( kMenuTopRow, kMenuTopRow + 2 );
902
903// Hit the option key ?
904if (key == optionKey) {
905gBootMode &= ~kBootModeQuiet;
906timeout = 0;
907} else {
908key = key & 0xFF;
909
910// Try booting hard disk if user pressed 'h'
911if (biosDevIsCDROM(gBIOSDev) && key == 'h') {
912BVRef bvr;
913
914// Look at partitions hosting OS X other than the CD-ROM
915for (bvr = bvChain; bvr; bvr=bvr->next) {
916if ((bvr->flags & kBVFlagSystemVolume) && bvr->biosdev != gBIOSDev) {
917gBootVolume = bvr;
918}
919}
920}
921goto done;
922}
923}
924
925if (gBootMode & kBootModeQuiet) {
926// No input allowed from user.
927goto done;
928}
929
930if (firstRun && timeout > 0 && countdown("Press any key to enter startup options.", kMenuTopRow, timeout) == 0) {
931// If the user is holding down a modifier key,
932// enter safe mode.
933if ((readKeyboardShiftFlags() & 0x0F) != 0) {
934gBootMode |= kBootModeSafe;
935}
936goto done;
937}
938
939if (gDeviceCount > 0) {
940// Allocate memory for an array of menu items.
941menuItems = malloc(sizeof(MenuItem) * gDeviceCount);
942if (menuItems == NULL) {
943goto done;
944}
945
946// Associate a menu item for each BVRef.
947for (bvr=bvChain, i=gDeviceCount-1, selectIndex=-1; bvr; bvr=bvr->next) {
948if (bvr->visible) {
949getBootVolumeDescription(bvr, menuItems[i].name, sizeof(menuItems[i].name) - 1, true);
950menuItems[i].param = (void *) bvr;
951if (bvr == menuBVR) {
952selectIndex = i;
953}
954i--;
955}
956}
957// Jief : In case the default partition (returned by selectBootVolume) is not in the menu
958if ( selectIndex == -1 )
959{
960selectIndex = 0;
961
962// gDeviceCount is actually > 0, so menuItems[selectIndex] exists
963menuBVR = (BVRef)(menuItems[selectIndex].param);
964// what happen if bvChain is empty ?
965}
966}
967
968if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
969// redraw the background buffer
970gui.logo.draw = true;
971drawBackground();
972gui.devicelist.draw = true;
973gui.redraw = true;
974if (!(gBootMode & kBootModeQuiet)) {
975
976// Check if "Boot Banner"=N switch is present in config file.
977getBoolForKey(kBootBannerKey, &showBootBanner, &bootInfo->chameleonConfig);
978if (showBootBanner) {
979// Display banner and show hardware info.
980gprintf(&gui.screen, bootBanner + 1, (bootInfo->convmem + bootInfo->extmem) / 1024);
981}
982
983// redraw background
984memcpy(gui.backbuffer->pixels, gui.screen.pixmap->pixels, gui.backbuffer->width * gui.backbuffer->height * 4);
985}
986} else {
987// Clear screen and hide the blinking cursor.
988clearScreenRows(kMenuTopRow, kMenuTopRow + 2);
989changeCursor(0, kMenuTopRow, kCursorTypeHidden, 0);
990}
991
992nextRow = kMenuTopRow;
993showPrompt = true;
994
995if (gDeviceCount) {
996if( bootArgs->Video.v_display == VGA_TEXT_MODE ) {
997printf("Use \30\31 keys to select the startup volume.");
998}
999showMenu( menuItems, gDeviceCount, selectIndex, kMenuTopRow + 2, kMenuMaxItems );
1000nextRow += MIN( gDeviceCount, kMenuMaxItems ) + 3;
1001}
1002
1003// Show the boot prompt.
1004showPrompt = (gDeviceCount == 0) || (menuBVR->flags & kBVFlagNativeBoot);
1005showBootPrompt( nextRow, showPrompt );
1006
1007do {
1008if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
1009// redraw background
1010memcpy( gui.backbuffer->pixels, gui.screen.pixmap->pixels, gui.backbuffer->width * gui.backbuffer->height * 4 );
1011// reset cursor co-ords
1012gui.debug.cursor = pos( gui.screen.width - 160 , 10 );
1013}
1014key = getchar();
1015updateMenu( key, (void **) &menuBVR );
1016newShowPrompt = (gDeviceCount == 0) || (menuBVR->flags & kBVFlagNativeBoot);
1017
1018if (newShowPrompt != showPrompt) {
1019showPrompt = newShowPrompt;
1020showBootPrompt( nextRow, showPrompt );
1021}
1022
1023if (showPrompt) {
1024updateBootArgs(key);
1025}
1026
1027switch (key) {
1028case KEY_ENTER:
1029if (gui.menu.draw) {
1030key=0;
1031break;
1032}
1033if (*gBootArgs == '?') {
1034char * argPtr = gBootArgs;
1035
1036// Skip the leading "?" character.
1037argPtr++;
1038getNextArg(&argPtr, booterCommand);
1039getNextArg(&argPtr, booterParam);
1040
1041/*
1042* TODO: this needs to be refactored.
1043*/
1044if (strcmp( booterCommand, "video" ) == 0) {
1045if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
1046showInfoBox(getVBEInfoString(), getVBEModeInfoString());
1047} else {
1048printVBEModeInfo();
1049}
1050} else if ( strcmp( booterCommand, "memory" ) == 0) {
1051if (bootArgs->Video.v_display != VGA_TEXT_MODE ) {
1052showInfoBox("Memory Map", getMemoryInfoString());
1053} else {
1054printMemoryInfo();
1055}
1056} else if (strcmp(booterCommand, "lspci") == 0) {
1057lspci();
1058} else if (strcmp(booterCommand, "log") == 0) {
1059 showTextBuffer(msgbuf, strlen(msgbuf));
1060} else if (strcmp(booterCommand, "more") == 0) {
1061showTextFile(booterParam);
1062} else if (strcmp(booterCommand, "rd") == 0) {
1063processRAMDiskCommand(&argPtr, booterParam);
1064} else if (strcmp(booterCommand, "norescan") == 0) {
1065if (gEnableCDROMRescan) {
1066gEnableCDROMRescan = false;
1067break;
1068}
1069} else {
1070showHelp();
1071}
1072key = 0;
1073showBootPrompt(nextRow, showPrompt);
1074break;
1075}
1076gBootVolume = menuBVR;
1077setRootVolume(menuBVR);
1078gBIOSDev = menuBVR->biosdev;
1079break;
1080
1081case KEY_ESC:
1082clearBootArgs();
1083break;
1084
1085case KEY_F5:
1086// New behavior:
1087// Clear gBootVolume to restart the loop
1088// if the user enabled rescanning the optical drive.
1089// Otherwise boot the default boot volume.
1090if (gEnableCDROMRescan) {
1091gBootVolume = NULL;
1092clearBootArgs();
1093}
1094break;
1095
1096case KEY_F10:
1097gScanSingleDrive = false;
1098scanDisks(gBIOSDev, &bvCount);
1099gBootVolume = NULL;
1100clearBootArgs();
1101break;
1102
1103case KEY_TAB:
1104// New behavior:
1105// Switch between text & graphic interfaces
1106// Only Permitted if started in graphics interface
1107if (useGUI)
1108{
1109if (bootArgs->Video.v_display != VGA_TEXT_MODE)
1110{
1111setVideoMode(VGA_TEXT_MODE, 0);
1112
1113setCursorPosition(0, 0, 0);
1114clearScreenRows(0, kScreenLastRow);
1115
1116// Display banner and show hardware info.
1117printf(bootBanner, (bootInfo->convmem + bootInfo->extmem) / 1024);
1118printf(getVBEInfoString());
1119
1120clearScreenRows(kMenuTopRow, kMenuTopRow + 2);
1121changeCursor(0, kMenuTopRow, kCursorTypeHidden, 0);
1122
1123nextRow = kMenuTopRow;
1124showPrompt = true;
1125
1126if (gDeviceCount)
1127{
1128printf("Use \30\31 keys to select the startup volume.");
1129showMenu(menuItems, gDeviceCount, selectIndex, kMenuTopRow + 2, kMenuMaxItems);
1130nextRow += MIN(gDeviceCount, kMenuMaxItems) + 3;
1131}
1132
1133showPrompt = (gDeviceCount == 0) || (menuBVR->flags & kBVFlagNativeBoot);
1134showBootPrompt(nextRow, showPrompt);
1135//changeCursor( 0, kMenuTopRow, kCursorTypeUnderline, 0 );
1136}
1137else
1138{
1139gui.redraw = true;
1140setVideoMode(GRAPHICS_MODE, 0);
1141updateVRAM();
1142 updateGraphicBootPrompt();
1143}
1144}
1145key = 0;
1146break;
1147
1148default:
1149key = 0;
1150break;
1151}
1152} while (0 == key);
1153
1154done:
1155if (bootArgs->Video.v_display == VGA_TEXT_MODE) {
1156clearScreenRows(kMenuTopRow, kScreenLastRow);
1157changeCursor(0, kMenuTopRow, kCursorTypeUnderline, 0);
1158}
1159shouldboot = false;
1160gui.menu.draw = false;
1161if (menuItems) {
1162free(menuItems);
1163menuItems = NULL;
1164}
1165execute_hook("BootOptions", gBootArgs, gBootArgsPtr, NULL, NULL);
1166return 0;
1167}
1168
1169//==========================================================================
1170
1171char gBootUUIDString[32+4+1] = ""; // UUID of the boot volume e.g. 5EB1869F-C4FA-3502-BDEB-3B8ED5D87292
1172extern unsigned char chainbootdev;
1173extern unsigned char chainbootflag;
1174
1175bool copyArgument(const char *argName, const char *val, int cnt, char **argP, int *cntRemainingP)
1176{
1177int argLen = argName ? strlen(argName) : 0;
1178int len = argLen + cnt + 1; // +1 to account for space
1179
1180if (argName)
1181{
1182len++; // +1 to account for '='
1183}
1184
1185if (len > *cntRemainingP)
1186{
1187error("Warning: boot arguments too long, truncating\n");
1188return false;
1189}
1190
1191if (argName)
1192{
1193strncpy(*argP, argName, argLen);
1194*argP += argLen;
1195*argP[0] = '=';
1196(*argP)++;
1197}
1198
1199strncpy(*argP, val, cnt);
1200*argP += cnt;
1201*argP[0] = ' ';
1202(*argP)++;
1203*cntRemainingP -= len;
1204
1205return true;
1206}
1207
1208//
1209// Returns TRUE if an argument was copied, FALSE otherwise
1210bool
1211processBootArgument(
1212 const char *argName, // The argument to search for
1213 const char *userString, // Typed-in boot arguments
1214 const char *kernelFlags, // Kernel flags from config table
1215 const char *configTable,
1216 char **argP, // Output value
1217 int *cntRemainingP, // Output count
1218 char *foundVal, // found value
1219 int foundValSize // max found value size
1220 ) {
1221const char *val;
1222int cnt;
1223bool found = false;
1224
1225if (getValueForBootKey(userString, argName, &val, &cnt)) {
1226// Don't copy; these values will be copied at the end of argument processing.
1227found = true;
1228} else if (getValueForBootKey(kernelFlags, argName, &val, &cnt)) {
1229// Don't copy; these values will be copied at the end of argument processing.
1230found = true;
1231} else if (getValueForKey(argName, &val, &cnt, &bootInfo->chameleonConfig)) {
1232copyArgument(argName, val, cnt, argP, cntRemainingP);
1233found = true;
1234}
1235if (found && foundVal) {
1236strlcpy(foundVal, val, foundValSize);
1237}
1238return found;
1239}
1240
1241// Maximum config table value size
1242#define VALUE_SIZE 2048
1243
1244int processBootOptions()
1245{
1246const char *cp = gBootArgs;
1247const char *val = 0;
1248const char *kernel;
1249int cnt;
1250int userCnt;
1251int cntRemaining;
1252char *argP;
1253char *configKernelFlags;
1254char *valueBuffer;
1255
1256valueBuffer = malloc(VALUE_SIZE);
1257
1258skipblanks( &cp );
1259
1260// Update the unit and partition number.
1261if (gBootVolume)
1262{
1263if (!(gBootVolume->flags & kBVFlagNativeBoot))
1264{
1265readBootSector(gBootVolume->biosdev, gBootVolume->part_boff, (void *)0x7c00);
1266//
1267// Setup edx, and signal intention to chain load the
1268// foreign booter.
1269//
1270
1271chainbootdev = gBootVolume->biosdev;
1272chainbootflag = 1;
1273
1274return 1;
1275}
1276
1277setRootVolume(gBootVolume);
1278
1279}
1280// If no boot volume fail immediately because we're just going to fail
1281// trying to load the config file anyway.
1282else
1283{
1284return -1;
1285}
1286
1287// Save a version of mac os we're booting.
1288MacOSVerCurrent = MacOSVer2Int(gBootVolume->OSVersion);
1289// so copy it and trim
1290gMacOSVersion[0] = 0;
1291if (MacOSVerCurrent >= MacOSVer2Int("10.10"))
1292{
1293strncat(gMacOSVersion, gBootVolume->OSVersion, 5);
1294}
1295else
1296{
1297strncat(gMacOSVersion, gBootVolume->OSVersion, 4);
1298}
1299
1300// Load config table specified by the user, or use the default.
1301
1302if (!getValueForBootKey(cp, "config", &val, &cnt))
1303{
1304val = 0;
1305cnt = 0;
1306}
1307
1308// Load com.apple.Boot.plist from the selected volume
1309// and use its contents to override default bootConfig.
1310
1311loadSystemConfig(&bootInfo->bootConfig);
1312loadChameleonConfig(&bootInfo->chameleonConfig, NULL);
1313
1314// Use the kernel name specified by the user, or fetch the name
1315// in the config table, or use the default if not specified.
1316// Specifying a kernel name on the command line, or specifying
1317// a non-default kernel name in the config file counts as
1318// overriding the kernel, which causes the kernelcache not
1319// to be used.
1320
1321gOverrideKernel = false;
1322if (( kernel = extractKernelName((char **)&cp) ))
1323{
1324strlcpy( bootInfo->bootFile, kernel, sizeof(bootInfo->bootFile) );
1325}
1326else
1327{
1328if ( getValueForKey( kKernelNameKey, &val, &cnt, &bootInfo->bootConfig ) )
1329{
1330strlcpy( bootInfo->bootFile, val, cnt+1 );
1331}
1332else
1333{
1334if (MacOSVerCurrent >= MacOSVer2Int("10.10")) // OS X is 10.10 or newer
1335{
1336strlcpy( bootInfo->bootFile, kOSXKernel, sizeof(bootInfo->bootFile) );
1337}
1338else
1339{
1340// or 10.9 and previous
1341strlcpy( bootInfo->bootFile, kDefaultKernel, sizeof(bootInfo->bootFile) );
1342}
1343}
1344}
1345
1346if ((strcmp( bootInfo->bootFile, kDefaultKernel ) != 0) && (strcmp( bootInfo->bootFile, kOSXKernel ) != 0))
1347{
1348gOverrideKernel = true;
1349}
1350
1351// Ermac : Inject "kext-dev-mode=1" if OS X 10.10 is detected
1352if (MacOSVerCurrent >= MacOSVer2Int("10.10")) // OS X is 10.10 or newer
1353{
1354addBootArg("kext-dev-mode=1");
1355}
1356
1357cntRemaining = BOOT_STRING_LEN - 2; // save 1 for NULL, 1 for space
1358argP = bootArgs->CommandLine;
1359
1360// Get config kernel flags, if not ignored.
1361if (getValueForBootKey(cp, kIgnoreBootFileFlag, &val, &cnt) || !getValueForKey(kKernelFlagsKey, &val, &cnt, &bootInfo->bootConfig))
1362{
1363val = "";
1364cnt = 0;
1365}
1366configKernelFlags = malloc(cnt + 1);
1367strlcpy(configKernelFlags, val, cnt + 1);
1368
1369// boot-uuid can be set either on the command-line or in the config file
1370if (!processBootArgument(kBootUUIDKey, cp, configKernelFlags, bootInfo->config,
1371 &argP, &cntRemaining, gBootUUIDString, sizeof(gBootUUIDString)))
1372{
1373//
1374// Try an alternate method for getting the root UUID on boot helper partitions.
1375//
1376if (gBootVolume->flags & kBVFlagBooter)
1377{
1378// Load the configuration store in the boot helper partition
1379if (loadHelperConfig(&bootInfo->helperConfig) == 0)
1380{
1381val = getStringForKey(kHelperRootUUIDKey, &bootInfo->helperConfig);
1382if (val != NULL)
1383{
1384strlcpy(gBootUUIDString, val, sizeof(gBootUUIDString));
1385}
1386}
1387}
1388/*
1389// Try to get the volume uuid string
1390if (!strlen(gBootUUIDString) && gBootVolume->fs_getuuid)
1391{
1392gBootVolume->fs_getuuid(gBootVolume, gBootUUIDString);
1393}
1394*/
1395// If we have the volume uuid add it to the commandline arguments
1396if (strlen(gBootUUIDString))
1397{
1398copyArgument(kBootUUIDKey, gBootUUIDString, strlen(gBootUUIDString), &argP, &cntRemaining);
1399}
1400// Try to get the volume uuid string
1401if (!strlen(gBootUUIDString) && gBootVolume->fs_getuuid)
1402{
1403gBootVolume->fs_getuuid(gBootVolume, gBootUUIDString);
1404}
1405}
1406DBG("Boot UUID [%s (%s), %s]: %s\n", gBootVolume->label, gBootVolume->altlabel, gBootVolume->type_name, gBootUUIDString);
1407
1408if (!processBootArgument(kRootDeviceKey, cp, configKernelFlags, bootInfo->config,
1409 &argP, &cntRemaining, gRootDevice, ROOT_DEVICE_SIZE))
1410{
1411cnt = 0;
1412if ( getValueForKey( kBootDeviceKey, &val, &cnt, &bootInfo->chameleonConfig))
1413{
1414valueBuffer[0] = '*';
1415cnt++;
1416strlcpy(valueBuffer + 1, val, cnt);
1417val = valueBuffer;
1418if (cnt > 0)
1419{
1420copyArgument( kRootDeviceKey, val, cnt, &argP, &cntRemaining);
1421}
1422}
1423else
1424{
1425if (strlen(gBootUUIDString))
1426{
1427val = "*uuid";
1428cnt = 5;
1429}
1430else
1431{
1432// Don't set "rd=.." if there is no boot device key
1433// and no UUID.
1434val = "";
1435cnt = 0;
1436}
1437}
1438/* Bungo
1439if (cnt > 0)
1440{
1441copyArgument( kRootDeviceKey, val, cnt, &argP, &cntRemaining);
1442}
1443*/
1444strlcpy(gRootDevice, val, (cnt + 1));
1445}
1446
1447/*
1448 * Removed. We don't need this anymore.
1449 *
1450if (!processBootArgument(kPlatformKey, cp, configKernelFlags, bootInfo->config,
1451 &argP, &cntRemaining, gPlatformName, sizeof(gCacheNameAdler)))
1452{
1453getPlatformName(gPlatformName);
1454copyArgument(kPlatformKey, gPlatformName, strlen(gPlatformName), &argP, &cntRemaining);
1455}
1456*/
1457
1458if (!getValueForBootKey(cp, kSafeModeFlag, &val, &cnt) &&
1459 !getValueForBootKey(configKernelFlags, kSafeModeFlag, &val, &cnt))
1460{
1461if (gBootMode & kBootModeSafe)
1462{
1463copyArgument(0, kSafeModeFlag, strlen(kSafeModeFlag), &argP, &cntRemaining);
1464}
1465}
1466
1467// Store the merged kernel flags and boot args.
1468
1469cnt = strlen(configKernelFlags);
1470if (cnt)
1471{
1472if (cnt > cntRemaining)
1473{
1474error("Warning: boot arguments too long, truncating\n");
1475cnt = cntRemaining;
1476}
1477strncpy(argP, configKernelFlags, cnt);
1478argP[cnt++] = ' ';
1479cntRemaining -= cnt;
1480}
1481userCnt = strlen(cp);
1482if (userCnt > cntRemaining)
1483{
1484error("Warning: boot arguments too long, truncating\n");
1485userCnt = cntRemaining;
1486}
1487strncpy(&argP[cnt], cp, userCnt);
1488argP[cnt+userCnt] = '\0';
1489
1490if(!shouldboot)
1491{
1492gVerboseMode = getValueForKey( kVerboseModeFlag, &val, &cnt, &bootInfo->chameleonConfig ) ||
1493getValueForKey( kSingleUserModeFlag, &val, &cnt, &bootInfo->chameleonConfig );
1494
1495gBootMode = ( getValueForKey( kSafeModeFlag, &val, &cnt, &bootInfo->chameleonConfig ) ) ?
1496kBootModeSafe : kBootModeNormal;
1497
1498if ( getValueForKey( kIgnoreCachesFlag, &val, &cnt, &bootInfo->chameleonConfig ) )
1499{
1500gBootMode = kBootModeSafe;
1501}
1502}
1503
1504if ( getValueForKey( kMKextCacheKey, &val, &cnt, &bootInfo->bootConfig ) )
1505{
1506strlcpy(gMKextName, val, cnt + 1);
1507}
1508else
1509{
1510gMKextName[0]=0;
1511}
1512
1513free(configKernelFlags);
1514free(valueBuffer);
1515
1516return 0;
1517}
1518
1519
1520//==========================================================================
1521// Load the help file and display the file contents on the screen.
1522
1523void showTextBuffer(char *buf_orig, int size)
1524{
1525char*bp;
1526char*buf;
1527intline;
1528intline_offset;
1529intc;
1530
1531if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
1532showInfoBox( "Press q to continue, space for next page.\n",buf_orig );
1533return;
1534}
1535
1536// Create a copy so that we don't mangle the original
1537buf = malloc(size + 1);
1538memcpy(buf, buf_orig, size);
1539
1540
1541 bp = buf;
1542 while (size-- > 0) {
1543if (*bp == '\n') {
1544*bp = '\0';
1545}
1546bp++;
1547 }
1548 *bp = '\1';
1549 line_offset = 0;
1550
1551 setActiveDisplayPage(1);
1552
1553 while (1) {
1554clearScreenRows(0, 24);
1555setCursorPosition(0, 0, 1);
1556bp = buf;
1557for (line = 0; *bp != '\1' && line < line_offset; line++) {
1558while (*bp != '\0') {
1559bp++;
1560}
1561bp++;
1562}
1563for (line = 0; *bp != '\1' && line < 23; line++) {
1564setCursorPosition(0, line, 1);
1565printf("%s\n", bp);
1566while (*bp != '\0') {
1567bp++;
1568}
1569bp++;
1570}
1571
1572setCursorPosition(0, 23, 1);
1573if (*bp == '\1') {
1574printf("[Type %sq or space to quit viewer]", (line_offset > 0) ? "p for previous page, " : "");
1575} else {
1576printf("[Type %s%sq to quit viewer]", (line_offset > 0) ? "p for previous page, " : "", (*bp != '\1') ? "space for next page, " : "");
1577}
1578
1579c = getchar();
1580if (c == 'q' || c == 'Q') {
1581break;
1582}
1583if ((c == 'p' || c == 'P') && line_offset > 0) {
1584line_offset -= 23;
1585}
1586if (c == ' ') {
1587if (*bp == '\1') {
1588break;
1589} else {
1590line_offset += 23;
1591}
1592}
1593 }
1594 setActiveDisplayPage(0);
1595}
1596
1597void showHelp(void)
1598{
1599if (bootArgs->Video.v_display != VGA_TEXT_MODE)
1600{
1601showInfoBox("Help. Press q to quit.\n", (char *)BootHelp_txt);
1602}
1603else
1604{
1605showTextBuffer((char *)BootHelp_txt, BootHelp_txt_len);
1606}
1607}
1608
1609void showTextFile(const char * filename)
1610{
1611#define MAX_TEXT_FILE_SIZE 65536
1612char*buf;
1613intfd;
1614intsize;
1615
1616if ((fd = open_bvdev("bt(0,0)", filename, 0)) < 0)
1617{
1618printf("\nFile not found: %s\n", filename);
1619sleep(2);
1620return;
1621}
1622
1623 size = file_size(fd);
1624 if (size > MAX_TEXT_FILE_SIZE)
1625{
1626size = MAX_TEXT_FILE_SIZE;
1627}
1628 buf = malloc(size);
1629 read(fd, buf, size);
1630 close(fd);
1631showTextBuffer(buf, size);
1632free(buf);
1633}
1634
1635// This is a very simplistic prompting scheme that just grabs two hex characters
1636// Eventually we need to do something more user-friendly like display a menu
1637// based off of the Multiboot device list
1638
1639int selectAlternateBootDevice(int bootdevice)
1640{
1641int key;
1642int newbootdevice;
1643int digitsI = 0;
1644char *end;
1645char digits[3] = {0,0,0};
1646
1647// We've already printed the current boot device so user knows what it is
1648printf("Typical boot devices are 80 (First HD), 81 (Second HD)\n");
1649printf("Enter two-digit hexadecimal boot device [%02x]: ", bootdevice);
1650do {
1651key = getchar();
1652switch (ASCII_KEY(key))
1653{
1654case KEY_BKSP:
1655if (digitsI > 0)
1656{
1657int x, y, t;
1658getCursorPositionAndType(&x, &y, &t);
1659// Assume x is not 0;
1660x--;
1661setCursorPosition(x,y,0); // back up one char
1662// Overwrite with space without moving cursor position
1663putca(' ', 0x07, 1);
1664digitsI--;
1665}
1666else
1667{
1668// TODO: Beep or something
1669}
1670break;
1671
1672case KEY_ENTER:
1673digits[digitsI] = '\0';
1674newbootdevice = strtol(digits, &end, 16);
1675if (end == digits && *end == '\0')
1676{
1677// User entered empty string
1678printf("\nUsing default boot device %x\n", bootdevice);
1679key = 0;
1680} else if(end != digits && *end == '\0') {
1681bootdevice = newbootdevice;
1682printf("\n");
1683key = 0; // We gots da boot device
1684} else {
1685printf("\nCouldn't parse. try again: ");
1686digitsI = 0;
1687}
1688break;
1689
1690default:
1691if (isxdigit(ASCII_KEY(key)) && digitsI < 2)
1692{
1693putchar(ASCII_KEY(key));
1694digits[digitsI++] = ASCII_KEY(key);
1695}
1696else
1697{
1698// TODO: Beep or something
1699}
1700break;
1701};
1702} while (key != 0);
1703
1704return bootdevice;
1705}
1706
1707bool promptForRescanOption(void)
1708{
1709printf("\nWould you like to enable media rescan option?\nPress ENTER to enable or any key to skip.\n");
1710if (getchar() == KEY_ENTER)
1711{
1712return true;
1713}
1714else
1715{
1716return false;
1717}
1718}
1719

Archive Download this file

Revision: 2573