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) {
1108if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
1109setVideoMode(VGA_TEXT_MODE, 0);
1110
1111setCursorPosition(0, 0, 0);
1112clearScreenRows(0, kScreenLastRow);
1113
1114// Display banner and show hardware info.
1115printf(bootBanner, (bootInfo->convmem + bootInfo->extmem) / 1024);
1116printf(getVBEInfoString());
1117
1118clearScreenRows(kMenuTopRow, kMenuTopRow + 2);
1119changeCursor(0, kMenuTopRow, kCursorTypeHidden, 0);
1120
1121nextRow = kMenuTopRow;
1122showPrompt = true;
1123
1124if (gDeviceCount) {
1125printf("Use \30\31 keys to select the startup volume.");
1126showMenu(menuItems, gDeviceCount, selectIndex, kMenuTopRow + 2, kMenuMaxItems);
1127nextRow += MIN(gDeviceCount, kMenuMaxItems) + 3;
1128}
1129
1130showPrompt = (gDeviceCount == 0) || (menuBVR->flags & kBVFlagNativeBoot);
1131showBootPrompt(nextRow, showPrompt);
1132//changeCursor( 0, kMenuTopRow, kCursorTypeUnderline, 0 );
1133} else {
1134gui.redraw = true;
1135setVideoMode(GRAPHICS_MODE, 0);
1136updateVRAM();
1137 updateGraphicBootPrompt();
1138}
1139}
1140key = 0;
1141break;
1142
1143default:
1144key = 0;
1145break;
1146}
1147} while (0 == key);
1148
1149done:
1150if (bootArgs->Video.v_display == VGA_TEXT_MODE) {
1151clearScreenRows(kMenuTopRow, kScreenLastRow);
1152changeCursor(0, kMenuTopRow, kCursorTypeUnderline, 0);
1153}
1154shouldboot = false;
1155gui.menu.draw = false;
1156if (menuItems) {
1157free(menuItems);
1158menuItems = NULL;
1159}
1160execute_hook("BootOptions", gBootArgs, gBootArgsPtr, NULL, NULL);
1161return 0;
1162}
1163
1164//==========================================================================
1165
1166char gBootUUIDString[32+4+1] = ""; // UUID of the boot volume e.g. 5EB1869F-C4FA-3502-BDEB-3B8ED5D87292
1167extern unsigned char chainbootdev;
1168extern unsigned char chainbootflag;
1169
1170bool copyArgument(const char *argName, const char *val, int cnt, char **argP, int *cntRemainingP)
1171{
1172int argLen = argName ? strlen(argName) : 0;
1173int len = argLen + cnt + 1; // +1 to account for space
1174
1175if (argName)
1176{
1177len++; // +1 to account for '='
1178}
1179
1180if (len > *cntRemainingP)
1181{
1182error("Warning: boot arguments too long, truncating\n");
1183return false;
1184}
1185
1186if (argName)
1187{
1188strncpy(*argP, argName, argLen);
1189*argP += argLen;
1190*argP[0] = '=';
1191(*argP)++;
1192}
1193
1194strncpy(*argP, val, cnt);
1195*argP += cnt;
1196*argP[0] = ' ';
1197(*argP)++;
1198*cntRemainingP -= len;
1199
1200return true;
1201}
1202
1203//
1204// Returns TRUE if an argument was copied, FALSE otherwise
1205bool
1206processBootArgument(
1207 const char *argName, // The argument to search for
1208 const char *userString, // Typed-in boot arguments
1209 const char *kernelFlags, // Kernel flags from config table
1210 const char *configTable,
1211 char **argP, // Output value
1212 int *cntRemainingP, // Output count
1213 char *foundVal, // found value
1214 int foundValSize // max found value size
1215 ) {
1216const char *val;
1217int cnt;
1218bool found = false;
1219
1220if (getValueForBootKey(userString, argName, &val, &cnt)) {
1221// Don't copy; these values will be copied at the end of argument processing.
1222found = true;
1223} else if (getValueForBootKey(kernelFlags, argName, &val, &cnt)) {
1224// Don't copy; these values will be copied at the end of argument processing.
1225found = true;
1226} else if (getValueForKey(argName, &val, &cnt, &bootInfo->chameleonConfig)) {
1227copyArgument(argName, val, cnt, argP, cntRemainingP);
1228found = true;
1229}
1230if (found && foundVal) {
1231strlcpy(foundVal, val, foundValSize);
1232}
1233return found;
1234}
1235
1236// Maximum config table value size
1237#define VALUE_SIZE 2048
1238
1239int processBootOptions()
1240{
1241const char *cp = gBootArgs;
1242const char *val = 0;
1243const char *kernel;
1244int cnt;
1245int userCnt;
1246int cntRemaining;
1247char *argP;
1248char *configKernelFlags;
1249char *valueBuffer;
1250
1251valueBuffer = malloc(VALUE_SIZE);
1252
1253skipblanks( &cp );
1254
1255// Update the unit and partition number.
1256if (gBootVolume)
1257{
1258if (!(gBootVolume->flags & kBVFlagNativeBoot))
1259{
1260readBootSector(gBootVolume->biosdev, gBootVolume->part_boff, (void *)0x7c00);
1261//
1262// Setup edx, and signal intention to chain load the
1263// foreign booter.
1264//
1265
1266chainbootdev = gBootVolume->biosdev;
1267chainbootflag = 1;
1268
1269return 1;
1270}
1271
1272setRootVolume(gBootVolume);
1273
1274}
1275// If no boot volume fail immediately because we're just going to fail
1276// trying to load the config file anyway.
1277else
1278{
1279return -1;
1280}
1281
1282// Save a version of mac os we're booting.
1283MacOSVerCurrent = MacOSVer2Int(gBootVolume->OSVersion);
1284// so copy it and trim
1285gMacOSVersion[0] = 0;
1286if (MacOSVerCurrent >= MacOSVer2Int("10.10"))
1287{
1288strncat(gMacOSVersion, gBootVolume->OSVersion, 5);
1289}
1290else
1291{
1292strncat(gMacOSVersion, gBootVolume->OSVersion, 4);
1293}
1294
1295// Load config table specified by the user, or use the default.
1296
1297if (!getValueForBootKey(cp, "config", &val, &cnt))
1298{
1299val = 0;
1300cnt = 0;
1301}
1302
1303// Load com.apple.Boot.plist from the selected volume
1304// and use its contents to override default bootConfig.
1305
1306loadSystemConfig(&bootInfo->bootConfig);
1307loadChameleonConfig(&bootInfo->chameleonConfig, NULL);
1308
1309// Use the kernel name specified by the user, or fetch the name
1310// in the config table, or use the default if not specified.
1311// Specifying a kernel name on the command line, or specifying
1312// a non-default kernel name in the config file counts as
1313// overriding the kernel, which causes the kernelcache not
1314// to be used.
1315
1316gOverrideKernel = false;
1317if (( kernel = extractKernelName((char **)&cp) ))
1318{
1319strlcpy( bootInfo->bootFile, kernel, sizeof(bootInfo->bootFile) );
1320}
1321else
1322{
1323if ( getValueForKey( kKernelNameKey, &val, &cnt, &bootInfo->bootConfig ) )
1324{
1325strlcpy( bootInfo->bootFile, val, cnt+1 );
1326}
1327else
1328{
1329if (MacOSVerCurrent >= MacOSVer2Int("10.10")) // OS X is 10.10 or newer
1330{
1331strlcpy( bootInfo->bootFile, kOSXKernel, sizeof(bootInfo->bootFile) );
1332}
1333else
1334{
1335// or 10.9 and previous
1336strlcpy( bootInfo->bootFile, kDefaultKernel, sizeof(bootInfo->bootFile) );
1337}
1338}
1339}
1340
1341if ((strcmp( bootInfo->bootFile, kDefaultKernel ) != 0) && (strcmp( bootInfo->bootFile, kOSXKernel ) != 0))
1342{
1343gOverrideKernel = true;
1344}
1345
1346// Ermac : Inject "kext-dev-mode=1" if OS X 10.10 is detected
1347if (MacOSVerCurrent >= MacOSVer2Int("10.10")) // OS X is 10.10 or newer
1348{
1349addBootArg("kext-dev-mode=1");
1350}
1351
1352cntRemaining = BOOT_STRING_LEN - 2; // save 1 for NULL, 1 for space
1353argP = bootArgs->CommandLine;
1354
1355// Get config kernel flags, if not ignored.
1356if (getValueForBootKey(cp, kIgnoreBootFileFlag, &val, &cnt) || !getValueForKey(kKernelFlagsKey, &val, &cnt, &bootInfo->bootConfig))
1357{
1358val = "";
1359cnt = 0;
1360}
1361configKernelFlags = malloc(cnt + 1);
1362strlcpy(configKernelFlags, val, cnt + 1);
1363
1364// boot-uuid can be set either on the command-line or in the config file
1365if (!processBootArgument(kBootUUIDKey, cp, configKernelFlags, bootInfo->config,
1366 &argP, &cntRemaining, gBootUUIDString, sizeof(gBootUUIDString)))
1367{
1368//
1369// Try an alternate method for getting the root UUID on boot helper partitions.
1370//
1371if (gBootVolume->flags & kBVFlagBooter)
1372{
1373// Load the configuration store in the boot helper partition
1374if (loadHelperConfig(&bootInfo->helperConfig) == 0)
1375{
1376val = getStringForKey(kHelperRootUUIDKey, &bootInfo->helperConfig);
1377if (val != NULL)
1378{
1379strlcpy(gBootUUIDString, val, sizeof(gBootUUIDString));
1380}
1381}
1382}
1383/*
1384// Try to get the volume uuid string
1385if (!strlen(gBootUUIDString) && gBootVolume->fs_getuuid)
1386{
1387gBootVolume->fs_getuuid(gBootVolume, gBootUUIDString);
1388}
1389*/
1390// If we have the volume uuid add it to the commandline arguments
1391if (strlen(gBootUUIDString))
1392{
1393copyArgument(kBootUUIDKey, gBootUUIDString, strlen(gBootUUIDString), &argP, &cntRemaining);
1394}
1395// Try to get the volume uuid string
1396if (!strlen(gBootUUIDString) && gBootVolume->fs_getuuid)
1397{
1398gBootVolume->fs_getuuid(gBootVolume, gBootUUIDString);
1399}
1400}
1401DBG("Boot UUID [%s (%s), %s]: %s\n", gBootVolume->label, gBootVolume->altlabel, gBootVolume->type_name, gBootUUIDString);
1402
1403if (!processBootArgument(kRootDeviceKey, cp, configKernelFlags, bootInfo->config,
1404 &argP, &cntRemaining, gRootDevice, ROOT_DEVICE_SIZE))
1405{
1406cnt = 0;
1407if ( getValueForKey( kBootDeviceKey, &val, &cnt, &bootInfo->chameleonConfig))
1408{
1409valueBuffer[0] = '*';
1410cnt++;
1411strlcpy(valueBuffer + 1, val, cnt);
1412val = valueBuffer;
1413if (cnt > 0)
1414{
1415copyArgument( kRootDeviceKey, val, cnt, &argP, &cntRemaining);
1416}
1417}
1418else
1419{
1420if (strlen(gBootUUIDString))
1421{
1422val = "*uuid";
1423cnt = 5;
1424}
1425else
1426{
1427// Don't set "rd=.." if there is no boot device key
1428// and no UUID.
1429val = "";
1430cnt = 0;
1431}
1432}
1433/* Bungo
1434if (cnt > 0)
1435{
1436copyArgument( kRootDeviceKey, val, cnt, &argP, &cntRemaining);
1437}
1438*/
1439strlcpy(gRootDevice, val, (cnt + 1));
1440}
1441
1442/*
1443 * Removed. We don't need this anymore.
1444 *
1445if (!processBootArgument(kPlatformKey, cp, configKernelFlags, bootInfo->config,
1446 &argP, &cntRemaining, gPlatformName, sizeof(gCacheNameAdler)))
1447{
1448getPlatformName(gPlatformName);
1449copyArgument(kPlatformKey, gPlatformName, strlen(gPlatformName), &argP, &cntRemaining);
1450}
1451*/
1452
1453if (!getValueForBootKey(cp, kSafeModeFlag, &val, &cnt) &&
1454 !getValueForBootKey(configKernelFlags, kSafeModeFlag, &val, &cnt))
1455{
1456if (gBootMode & kBootModeSafe)
1457{
1458copyArgument(0, kSafeModeFlag, strlen(kSafeModeFlag), &argP, &cntRemaining);
1459}
1460}
1461
1462// Store the merged kernel flags and boot args.
1463
1464cnt = strlen(configKernelFlags);
1465if (cnt)
1466{
1467if (cnt > cntRemaining)
1468{
1469error("Warning: boot arguments too long, truncating\n");
1470cnt = cntRemaining;
1471}
1472strncpy(argP, configKernelFlags, cnt);
1473argP[cnt++] = ' ';
1474cntRemaining -= cnt;
1475}
1476userCnt = strlen(cp);
1477if (userCnt > cntRemaining)
1478{
1479error("Warning: boot arguments too long, truncating\n");
1480userCnt = cntRemaining;
1481}
1482strncpy(&argP[cnt], cp, userCnt);
1483argP[cnt+userCnt] = '\0';
1484
1485if(!shouldboot)
1486{
1487gVerboseMode = getValueForKey( kVerboseModeFlag, &val, &cnt, &bootInfo->chameleonConfig ) ||
1488getValueForKey( kSingleUserModeFlag, &val, &cnt, &bootInfo->chameleonConfig );
1489
1490gBootMode = ( getValueForKey( kSafeModeFlag, &val, &cnt, &bootInfo->chameleonConfig ) ) ?
1491kBootModeSafe : kBootModeNormal;
1492
1493if ( getValueForKey( kIgnoreCachesFlag, &val, &cnt, &bootInfo->chameleonConfig ) )
1494{
1495gBootMode = kBootModeSafe;
1496}
1497}
1498
1499if ( getValueForKey( kMKextCacheKey, &val, &cnt, &bootInfo->bootConfig ) )
1500{
1501strlcpy(gMKextName, val, cnt + 1);
1502}
1503else
1504{
1505gMKextName[0]=0;
1506}
1507
1508free(configKernelFlags);
1509free(valueBuffer);
1510
1511return 0;
1512}
1513
1514
1515//==========================================================================
1516// Load the help file and display the file contents on the screen.
1517
1518void showTextBuffer(char *buf_orig, int size)
1519{
1520char*bp;
1521char* buf;
1522intline;
1523intline_offset;
1524intc;
1525
1526if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
1527showInfoBox( "Press q to continue, space for next page.\n",buf_orig );
1528return;
1529}
1530
1531// Create a copy so that we don't mangle the original
1532buf = malloc(size + 1);
1533memcpy(buf, buf_orig, size);
1534
1535
1536 bp = buf;
1537 while (size-- > 0) {
1538if (*bp == '\n') {
1539*bp = '\0';
1540}
1541bp++;
1542 }
1543 *bp = '\1';
1544 line_offset = 0;
1545
1546 setActiveDisplayPage(1);
1547
1548 while (1) {
1549clearScreenRows(0, 24);
1550setCursorPosition(0, 0, 1);
1551bp = buf;
1552for (line = 0; *bp != '\1' && line < line_offset; line++) {
1553while (*bp != '\0') {
1554bp++;
1555}
1556bp++;
1557}
1558for (line = 0; *bp != '\1' && line < 23; line++) {
1559setCursorPosition(0, line, 1);
1560printf("%s\n", bp);
1561while (*bp != '\0') {
1562bp++;
1563}
1564bp++;
1565}
1566
1567setCursorPosition(0, 23, 1);
1568if (*bp == '\1') {
1569printf("[Type %sq or space to quit viewer]", (line_offset > 0) ? "p for previous page, " : "");
1570} else {
1571printf("[Type %s%sq to quit viewer]", (line_offset > 0) ? "p for previous page, " : "", (*bp != '\1') ? "space for next page, " : "");
1572}
1573
1574c = getchar();
1575if (c == 'q' || c == 'Q') {
1576break;
1577}
1578if ((c == 'p' || c == 'P') && line_offset > 0) {
1579line_offset -= 23;
1580}
1581if (c == ' ') {
1582if (*bp == '\1') {
1583break;
1584} else {
1585line_offset += 23;
1586}
1587}
1588 }
1589 setActiveDisplayPage(0);
1590}
1591
1592void showHelp(void)
1593{
1594if (bootArgs->Video.v_display != VGA_TEXT_MODE)
1595{
1596showInfoBox("Help. Press q to quit.\n", (char *)BootHelp_txt);
1597}
1598else
1599{
1600showTextBuffer((char *)BootHelp_txt, BootHelp_txt_len);
1601}
1602}
1603
1604void showTextFile(const char * filename)
1605{
1606#define MAX_TEXT_FILE_SIZE 65536
1607char*buf;
1608intfd;
1609intsize;
1610
1611if ((fd = open_bvdev("bt(0,0)", filename, 0)) < 0)
1612{
1613printf("\nFile not found: %s\n", filename);
1614sleep(2);
1615return;
1616}
1617
1618 size = file_size(fd);
1619 if (size > MAX_TEXT_FILE_SIZE)
1620{
1621size = MAX_TEXT_FILE_SIZE;
1622}
1623 buf = malloc(size);
1624 read(fd, buf, size);
1625 close(fd);
1626showTextBuffer(buf, size);
1627free(buf);
1628}
1629
1630// This is a very simplistic prompting scheme that just grabs two hex characters
1631// Eventually we need to do something more user-friendly like display a menu
1632// based off of the Multiboot device list
1633
1634int selectAlternateBootDevice(int bootdevice)
1635{
1636int key;
1637int newbootdevice;
1638int digitsI = 0;
1639char *end;
1640char digits[3] = {0,0,0};
1641
1642// We've already printed the current boot device so user knows what it is
1643printf("Typical boot devices are 80 (First HD), 81 (Second HD)\n");
1644printf("Enter two-digit hexadecimal boot device [%02x]: ", bootdevice);
1645do {
1646key = getchar();
1647switch (ASCII_KEY(key))
1648{
1649case KEY_BKSP:
1650if (digitsI > 0)
1651{
1652int x, y, t;
1653getCursorPositionAndType(&x, &y, &t);
1654// Assume x is not 0;
1655x--;
1656setCursorPosition(x,y,0); // back up one char
1657// Overwrite with space without moving cursor position
1658putca(' ', 0x07, 1);
1659digitsI--;
1660}
1661else
1662{
1663// TODO: Beep or something
1664}
1665break;
1666
1667case KEY_ENTER:
1668digits[digitsI] = '\0';
1669newbootdevice = strtol(digits, &end, 16);
1670if (end == digits && *end == '\0')
1671{
1672// User entered empty string
1673printf("\nUsing default boot device %x\n", bootdevice);
1674key = 0;
1675} else if(end != digits && *end == '\0') {
1676bootdevice = newbootdevice;
1677printf("\n");
1678key = 0; // We gots da boot device
1679} else {
1680printf("\nCouldn't parse. try again: ");
1681digitsI = 0;
1682}
1683break;
1684
1685default:
1686if (isxdigit(ASCII_KEY(key)) && digitsI < 2)
1687{
1688putchar(ASCII_KEY(key));
1689digits[digitsI++] = ASCII_KEY(key);
1690}
1691else
1692{
1693// TODO: Beep or something
1694}
1695break;
1696};
1697} while (key != 0);
1698
1699return bootdevice;
1700}
1701
1702bool promptForRescanOption(void)
1703{
1704printf("\nWould you like to enable media rescan option?\nPress ENTER to enable or any key to skip.\n");
1705if (getchar() == KEY_ENTER)
1706{
1707return true;
1708}
1709else
1710{
1711return false;
1712}
1713}
1714

Archive Download this file

Revision: 2562