Adjust memory layout for 2.6.22+ kernels with 32KB setup code
[mknbi.git] / nfl.c
1 #include        "stddef.h"
2 #include        "string.h"
3 #include        "linux-asm-io.h"
4 #include        "string.h"
5 #include        "etherboot.h"
6 #include        "startmenu.h"
7 #include        "elf_boot.h"
8 #include        "nfl.h"
9
10 /*
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License as
13  * published by the Free Software Foundation; either version 2, or (at
14  * your option) any later version.
15  */
16
17 /*
18
19 This is an example program which shows how the extension routine
20 feature in Etherboot 5.0 works.
21
22 This program presents a list of valid boot images from a data segment
23 that is loaded into another area of memory, and prompts the user for a
24 number, and modifies the bootp record to the filename to be loaded.  You
25 can make the menu program as elaborate as you like, the sky is the
26 limit.
27
28 Ideally, there should be a menu generation program that takes a
29 high-level description of menus and valid inputs and creates a data
30 file to be loaded to the data area. The menu program should agree with
31 the menu generator on the layout of the data area.
32
33 This program is linked to run at 0x60000, and expects to find config
34 data at 0x70000. This means the code can be up to 64kB long.
35
36 When the program starts it receives 3 parameters from Etherboot:
37
38 Pointer to ebinfo structure
39 Pointer to image header structure (either a tagged or ELF image header)
40 Pointer to bootp/DHCP reply obtained by Etherboot from bootpd or DHCPD
41
42 Etherboot expects this program to return an int. The values have these
43 meanings:
44
45 <0      Do not use
46 0       Same as 1, for implementation reasons
47 1       Redo tftp with possibly modified bootp record
48 2       Redo bootp and tftp
49 255     Exit Etherboot
50
51 Observe that this program causes Etherboot to load a different program
52 next by modifying the contents of the filename field in the bootp record
53 and then returning 1. It can also send parameters to the next program by
54 modifying tag 129 in the bootp record. This is how the menu system
55 works.
56
57 The data segment that this particular program expects is of the form
58
59         choice 1\nchoice 2\nchoice 3\n\0
60
61 where the \n are newlines and the \0 the teminating zero byte. Therefore
62 you can create this file with a Unix text editor (but see next
63 paragraph). choice 1, etc are the pathnames or filenames of the next
64 file to load by TFTP. If the string starts with / then it's assumed to
65 be a pathname and the whole of the bootp filename area is replaced by
66 it, otherwise just the filename portion of the pathname, i.e. the same
67 directory as the menu file is assumed.
68
69 This program also illustrates the use of a timeout to select a default
70 item by using currticks() to obtain the value of the BIOS clock and
71 console_ischar to determine if a character has been typed at the
72 keyboard.
73
74 Commentary: This program is just to illustrate a very simple menu
75 system.  There are known bugs:
76
77 mknbi-menu/mkelf-menu does not add the ending NUL byte, but this is
78 present due to the NUL fill to the next block boundary. If the size of
79 the data goes exactly up to a block boundary, then it is possible there
80 will be a spurious final item that goes up the next NUL byte in memory.
81
82 Another bug is that there is no overflow checking when writing into
83 bootp->bp_file.
84
85 Yet another bug is that there is no facility to correct input entry on
86 lines. getline() should be smarter.
87
88 */
89
90 /*
91
92 Memory layout assumed by mknbi and this program
93
94 0x60000-0x6FFFF    64 kB        Menu program
95 0x70000-0x7FFFF    64 kB        Menu data (initial)
96
97 */
98
99 #define NFL_VERSION     "0.56"
100
101 #define TIMEOUT         10                      /* seconds */
102 #define MENU_DATA       ((char *)0x70000)
103 #define MENU_COLS       75
104 #define MENU_ROWS       14
105 #define MAX_ENTRIES     100
106
107 static  int dbg_count1=0;
108
109 static char *menu_entries[MAX_ENTRIES];
110 static unsigned short num_entries;
111 static unsigned short cur_entry;
112 static unsigned short top_entry;
113
114 static int timeout = 0;
115 /* Color settings.  */
116 static int color_fg = 7;
117 static int color_bg = 4;
118 static int number_entries = 0;
119
120 /* Default Menu Size */
121 static unsigned short menu_ulx  = 1;
122 static unsigned short menu_uly  = 1;
123 static unsigned short menu_rows = 0x0800;
124 static unsigned short menu_cols = 0x0800;
125
126 /* Terminal types.  */
127 int terminal = TERMINAL_CONSOLE;
128
129 /* Defined in ANSI.C */
130 extern unsigned short rows, columns, attr;
131 extern void ansi_putc(unsigned int);
132 extern void ansi_reset(void);
133 extern void enable_cursor(int);
134
135 extern void printf(const char *, ...);
136 extern void sprintf(char *, const char *, ...);
137 extern char *strcat(char *, const char *);
138 extern char *strncat(char *, const char *, unsigned int);
139
140 /*
141 --------------------------------------------------------------------------*/
142 void putchar(int c)
143 {
144         if (c == '\n')
145                 ansi_putc('\r');
146         ansi_putc(c);
147 }
148
149 /*
150 --------------------------------------------------------------------------*/
151 int getchar(void)
152 {
153         int     c;
154
155         c = console_getc();
156         if (c == '\r')
157                 c = '\n';
158         return (c);
159 }
160
161 /* Wait for a keypress and return its code.
162 --------------------------------------------------------------------------*/
163 int getkey (void)
164 {
165   int c = -1;
166   
167   if ((terminal & TERMINAL_CONSOLE)
168 #ifdef SUPPORT_HERCULES
169       || (terminal & TERMINAL_HERCULES)
170 #endif /* SUPPORT_HERCULES */
171       )
172     c = console_getkey ();
173 #ifdef SUPPORT_SERIAL
174   else if (terminal & TERMINAL_SERIAL)
175     c = serial_getkey ();
176 #endif /* SUPPORT_SERIAL */
177
178   return c;
179 }
180
181 /* Check if a key code is available.
182 --------------------------------------------------------------------------*/
183 int checkkey (void)
184 {
185   int c = -1;
186
187   if ((terminal & TERMINAL_CONSOLE)
188 #ifdef SUPPORT_HERCULES
189       || (terminal & TERMINAL_HERCULES)
190 #endif /* SUPPORT_HERCULES */
191       )
192     c = console_checkkey ();
193   
194 #ifdef SUPPORT_SERIAL
195   if (terminal & TERMINAL_SERIAL)
196     c = serial_checkkey ();
197 #endif /* SUPPORT_SERIAL */
198
199   return c;
200 }
201
202 /* Translate a special key to a common ascii code.
203 --------------------------------------------------------------------------*/
204 int translate_keycode (int c)
205 {
206 #if 0 //def SUPPORT_SERIAL
207         if (terminal & TERMINAL_SERIAL)
208         {
209                 /*
210                 In a serial terminal, things are complicated, because several
211                 key codes start from the character ESC, while we want to accept
212                 ESC itself.
213                 */
214                 if (c == '\e')
215                 {
216                         int start;
217
218                         /* Get current time.  */
219                         start = currticks ();
220
221                         while (checkkey () == -1)
222                         {
223                                 /*
224                                 Wait for a next character, at least for 0.1 sec
225                                 (18.2 ticks/sec).
226                                 */
227                                 int now;
228               
229                                 now = currticks ();
230                                 if (now - start >= 2)
231                                         return c;
232                         }
233
234                         c = getkey ();
235                         if (c == '[')
236                         {
237                                 int c1, c2;
238
239                                 /* To filter illegal states.  */
240                                 c = 0;
241                                 c1 = getkey ();
242                                 switch (c1)
243                                 {
244                                         case 'A':       /* KEY_UP */
245                                                 c = 16;
246                                                 break;
247                                         case 'B':       /* KEY_DOWN */
248                                                 c = 14;
249                                                 break;
250                                         case 'C':       /* KEY_RIGHT */
251                                                 c = 6;
252                                                 break;
253                                         case 'D':       /* KEY_LEFT */
254                                                 c = 2;
255                                                 break;
256                                         case 'F':       /* End */
257                                                 c = 5;
258                                                 break;
259                                         case 'H':       /* Home */
260                                                 c = 1;
261                                                 break;
262                                         case '1':
263                                                 c2 = getkey ();
264                                                 if (c2 == '~')
265                                                 {
266                                                         /* One of control keys (pos1,....).  */
267                                                         c = 1;
268                                                 }
269                                                 break;
270                                         case '3':
271                                                 c2 = getkey ();
272                                                 if (c2 == '~')
273                                                 {
274                                                         /* One of control keys (del,....).  */
275                                                         c = 4;
276                                                 }
277                                                 break;
278                                         case '4':       /* Del */
279                                                 c = 4;
280                                                 break;
281                                 }
282                         }
283                 }
284         }
285         else
286 #endif /* SUPPORT_SERIAL */
287         {
288                 switch (c)
289                 {
290                         case KEY_LEFT:
291                                 c = 2;
292                                 break;
293                         case KEY_RIGHT:
294                                 c = 6;
295                                 break;
296                         case KEY_UP:
297                                 c = 16;
298                                 break;
299                         case KEY_DOWN:
300                                 c = 14;
301                                 break;
302                         case KEY_HOME:
303                                 c = 1;
304                                 break;
305                         case KEY_END:
306                                 c = 5;
307                                 break;
308                         case KEY_DC:
309                                 c = 4;
310                                 break;
311                         case KEY_BACKSPACE:
312                                 c = 8;
313                                 break;
314                 }
315         }
316   
317         return ASCII_CHAR (c);
318 }
319
320
321 /* 
322 Get a line, ignore characters after array limit reached. Echo the character 
323 if the flag says so.
324 --------------------------------------------------------------------------*/
325 int getline(char *line, int length, int echo)
326 {
327         int     c;
328         char    *p = line;
329
330         while ((c = getchar()) != '\n') {
331                 if (p < &line[length-1]) {      /* within array? */
332                         if (echo)
333                                 ansi_putc(c);
334                         *p++ = c;
335                 }
336         }
337         *p++ = '\0';
338         if (echo)
339                 ansi_putc('\n');
340         return (line - p);
341 }
342
343 /* Build menu entries array starting at index one for clarity...
344 --------------------------------------------------------------------------*/
345 int scan_entries(void)
346 {
347         int     i = 0, first_entry_char = 1;
348         char    *p = MENU_DATA;
349
350         if (p == '\0')
351                 return( 0 );
352
353         do {
354                 /* Skip leading white-space */
355                 while ((*p == '\b') || (*p == '\t'))
356                         p++;
357                 menu_entries[i+1] = p;
358                 while ((*p != '\0') && (*p != '\n')) 
359                 {
360                         /* Point to next character */
361                         p++;
362                         /* At least one non-blank character, index to next entry */
363                         if (first_entry_char)
364                         {
365                                 first_entry_char = 0;
366                                 i++;
367                         }
368                 }
369                 if (*p == '\0')
370                         break;
371                 if (*p == '\n') {
372                         /* Null-terminate this entry string */
373                         *p++ = '\0';
374                         /* Set 'waiting for first entry character' flag */
375                         first_entry_char = 1;
376                 }
377         } while (i < MAX_ENTRIES);
378
379         return ( i );
380 }
381
382 /* NOTE: ANSI defines (1,1) as upper left corner, IBM BIOS uses (0,0)
383 --------------------------------------------------------------------------*/
384 void gotoxy (int x, int y)
385 {
386         printf ("\e[%d;%dH", y+1, x+1);
387 }
388
389 /*
390 Display screen border.
391 --------------------------------------------------------------------------*/
392 static void show_border ( void )
393 {
394         unsigned short i;
395         int disp_ul = DISP_UL;
396         int disp_ur = DISP_UR;
397         int disp_ll = DISP_LL;
398         int disp_lr = DISP_LR;
399         int disp_horiz = DISP_HORIZ;
400         int disp_vert = DISP_VERT;
401
402         /* Clear the screen, and set foreground and background attributes */
403         for (i = 0; i<rows; i++)
404         {
405                 gotoxy (0, i);
406                 printf ( "\e[K" );
407         }
408
409         /* Turn off the text cursor */
410         console_nocursor();
411
412         /* Show the top line of the border */   
413         gotoxy (menu_ulx, menu_uly);
414         ansi_putc (disp_ul);
415         for (i = 0; i < (menu_cols); i++)
416                 ansi_putc (disp_horiz);
417         gotoxy (menu_ulx+menu_cols+1, menu_uly);
418         ansi_putc (disp_ur);
419
420         /* Show the sides of the border */      
421         for (i = 0; i<menu_rows; i++)
422         {
423                 gotoxy (menu_ulx, menu_uly+1+i);
424                 ansi_putc (disp_vert);
425                 gotoxy (menu_ulx+menu_cols+1, menu_uly+1+i);
426                 ansi_putc (disp_vert);
427         }
428
429         /* Show the bottom line of the border */        
430         gotoxy (menu_ulx, menu_uly+menu_rows+1);
431         ansi_putc (disp_ll);
432         for (i = 0; i < (menu_cols); i++)
433                 ansi_putc (disp_horiz);
434         gotoxy (menu_ulx+menu_cols+1, menu_uly+menu_rows+1);
435         ansi_putc (disp_lr);
436
437         gotoxy ( 1, 0 );
438         printf ( "Network Free-Loader v" NFL_VERSION );
439 }
440
441 /*
442 --------------------------------------------------------------------------*/
443 static void pad_printxy ( char *szBuff, int x, unsigned short y, int len_entry, int reverse )
444 {
445         int i;
446 #if 0
447         gotoxy( 30, 20);
448         printf( "strlen:%d", strlen(szBuff));
449 #endif
450         for ( i=strlen(szBuff); (i < len_entry ); i++ )
451                 strcat( szBuff, " " );
452
453         gotoxy( x, y );
454         if ( reverse )
455                 printf ( "\e[7m%s\e[27m", szBuff );
456         else
457                 printf ( "%s", szBuff );
458 }
459
460 /*
461 --------------------------------------------------------------------------*/
462 static void show_entries ( unsigned short first_row, unsigned short num_rows )
463 {
464         unsigned short i, bot_entry;
465         int len_entry;
466         int disp_up = DISP_UP;
467         int disp_down = DISP_DOWN;
468         char szTmp[10], szBuff[80];
469
470 #ifdef SUPPORT_SERIAL
471         if (terminal & TERMINAL_SERIAL)
472         {
473                 disp_up = ACS_UARROW;
474                 disp_down = ACS_DARROW;
475         }
476 #endif /* SUPPORT_SERIAL */
477
478 /*      menu_cols = 25; */
479         len_entry = menu_cols;
480
481 /*  
482         bot_entry = top_entry+menu_rows-1;
483         if (bot_entry > num_entries)
484                 bot_entry = num_entries;
485 */      
486 #if 0
487         gotoxy( 30, 15 );
488         printf( "[%d]SE(1R:%d, #:%d, cur:%d, top:%d", dbg_count1++, first_row, num_rows, cur_entry, top_entry );
489 #endif
490 /*      for (i = 0; i <= bot_entry-top_entry; i++) { */
491         for (i = first_row; ((i < first_row+num_rows) && ((top_entry+i-1) <= num_entries)); i++) {
492
493                 *szTmp = 0;
494                 *szBuff = 0;
495
496                 if (number_entries) {
497                         if ((top_entry+i-1) <= 9)
498                                 strncat( szBuff, " ", len_entry );
499                         sprintf ( szTmp, "%d. ", (top_entry+i-1) );
500                         strncat( szBuff, szTmp, len_entry );
501 //                      len_entry -= strlen( szBuff );
502                 }
503         
504                 /* add the entry to the current string */
505                 strncat ( szBuff, menu_entries[top_entry+i-1], len_entry );
506                 pad_printxy( szBuff, menu_ulx+1, menu_uly+i, len_entry, ((top_entry+i-1) == cur_entry) );
507 #if 0
508                 if (i == first_row) {
509                         gotoxy( 30, 16);
510                         printf( "%d. %d [%s]", i, cur_entry, szBuff );
511                         gotoxy( 30, 17);
512                         printf( "                      " );
513                         gotoxy( 30, 18);
514                         printf( "                      " );
515                         gotoxy( 30, 19);
516                         printf( "                      " );
517                 }
518                 else
519                 if (i == first_row+1)
520                 {
521                         gotoxy( 30,17);
522                         printf( "%d. %d [%s]", i, cur_entry, szBuff );
523                 }
524 #endif
525         }
526         if (top_entry != 1) {
527                 *szBuff = 0;
528                 sprintf ( szBuff, " %c  ", disp_up );
529                 pad_printxy( szBuff, menu_ulx+1, menu_uly+1, len_entry, 0 );
530 #if 0
531                 gotoxy( 30, 18);
532                 printf( "%d. %d [%s]", i, cur_entry, szBuff );
533 #endif
534         }
535         if ((top_entry+menu_rows-1) < num_entries) {
536                 *szBuff = 0;
537                 sprintf ( szBuff, " %c  ", disp_down );
538                 pad_printxy( szBuff, menu_ulx+1, menu_uly+menu_rows, len_entry, 0 );
539 #if 0
540                 gotoxy( 30, 19);
541                 printf( "%d. %d [%s]", i, cur_entry, szBuff );
542 #endif
543         }
544 }
545
546
547 /*
548 --------------------------------------------------------------------------*/
549 static int select_entry ( void )
550 {
551         int c, c1, count=0;
552         unsigned short bot_entry;
553         unsigned short tmp;
554
555         ansi_reset();
556         enable_cursor(0);
557         printf ( "\e[3%dm\e[4%dm",  color_fg, color_bg );
558         gotoxy( 20, 5 );
559         
560         tmp = columns-5;
561         if (menu_cols > tmp)
562                 menu_cols = tmp;
563         tmp = rows-3;
564         if (menu_rows > tmp)
565                 menu_rows = tmp;
566
567         show_border ();
568
569         top_entry = 1;
570         cur_entry = 1;
571         bot_entry = top_entry+menu_rows-1;
572
573         show_entries ( 1, menu_rows );
574
575         while (1)
576         {
577                 if ((checkkey () != -1) || (timeout == -1)) 
578                 {
579                         /*
580                         Key was pressed, show which entry is selected before GETKEY,
581                         since we're comming in here also on TIMEOUT == -1 and
582                         hang in GETKEY 
583                         */
584                         c1 = getkey();
585
586                         c = translate_keycode( c1 );
587 #if 0
588                         gotoxy( 30, 10 );
589                         printf( "\e[K%d [%X]->[%X]", count++, c1, c );
590
591                         gotoxy( 30, 11 );
592                         printf( "cur:%d top:%d bot:%d rows:%d num_entries:%d", cur_entry, top_entry, bot_entry, menu_rows, num_entries );
593 #endif
594                         /* We told them above (at least in SUPPORT_SERIAL) to use
595                         '^' or 'v' so accept these keys.  */
596                         if (c == 16 || c == '^')
597                         {
598                                 if (terminal & TERMINAL_DUMB)
599                                 {
600                                         if (cur_entry > 1)
601                                                 cur_entry--;
602                                 } else {
603                                         if (cur_entry > 1)
604                                         {
605                                                 cur_entry--;
606                                                 if ((cur_entry == top_entry) && (top_entry > 1)) {
607
608                                                         top_entry--;
609                                                         bot_entry--;
610                                                         show_entries ( 1, (bot_entry-top_entry+1) );
611                                                 } else
612                                                         show_entries ( (cur_entry-top_entry+1), 2 );
613                                         }
614                                 }
615                         }
616                         if ((c == 14 || c == 'v') && ((cur_entry + 1) <= num_entries))
617                         {
618                                 if (terminal & TERMINAL_DUMB)
619                                         cur_entry++;
620                                 else {
621                                         if (cur_entry < num_entries)
622                                         {
623                                                 cur_entry++;
624                                                 if ((cur_entry >= bot_entry) && (bot_entry < num_entries)) {
625
626                                                         top_entry++;
627                                                         bot_entry++;
628                                                         show_entries ( 1, (bot_entry-top_entry+1) );
629                                                 }
630                                                 else
631                                                         show_entries( (cur_entry-top_entry), 2 );
632                                         }
633                                 }
634                         }
635                         if (c == '\r') {
636                                 return( cur_entry );
637                         }
638                 }
639         }
640 }
641
642 /*
643 Set new filename in bootp path
644 --------------------------------------------------------------------------*/
645 void set_file_to_load( struct bootp_t *bootp, char *file2load )
646 {
647         char    *p, *szPath = bootp->bp_file;
648
649         if (*file2load != '/')
650         {
651                 /* if reletive path, append to current path */
652                 if ((p = strrchr( szPath, '/')) != 0)
653                         szPath = p+1;
654         }
655         strcpy( szPath, file2load );
656 }
657
658 static void parse_elf_boot_notes(
659         void *notes, union infoblock **rheader, struct bootp_t **rbootp)
660 {
661         unsigned char *note, *end;
662         Elf_Bhdr *bhdr;
663         Elf_Nhdr *hdr;
664
665         bhdr = notes;
666         if (bhdr->b_signature != ELF_BHDR_MAGIC) {
667                 return;
668         }
669
670         note = ((char *)bhdr) + sizeof(*bhdr);
671         end  = ((char *)bhdr) + bhdr->b_size;
672         while (note < end) {
673                 unsigned char *n_name, *n_desc, *next;
674                 hdr = (Elf_Nhdr *)note;
675                 n_name = note + sizeof(*hdr);
676                 n_desc = n_name + ((hdr->n_namesz + 3) & ~3);
677                 next = n_desc + ((hdr->n_descsz + 3) & ~3);
678                 if (next > end) 
679                         break;
680 #if 0
681                 printf("n_type: %x n_name(%d): n_desc(%d): \n", 
682                         hdr->n_type, hdr->n_namesz, hdr->n_descsz);
683 #endif
684
685                 if ((hdr->n_namesz == 10) &&
686                         (memcmp(n_name, "Etherboot", 10) == 0)) {
687                         switch(hdr->n_type) {
688                         case EB_BOOTP_DATA:
689                                 *rbootp = *((void **)n_desc);
690                                 break;
691                         case EB_HEADER:
692                                 *rheader = *((void **)n_desc);
693                                 break;
694                         default:
695                                 break;
696                         }
697                 }
698                 note = next;
699         }
700 }
701
702 /*
703 --------------------------------------------------------------------------*/
704 int menu(struct ebinfo *eb, union infoblock *header, struct bootp_t *bootp)
705 {
706         int     i;
707
708         parse_elf_boot_notes(eb, &header, &bootp);
709         num_entries = scan_entries();
710         if (!num_entries) {
711                 printf( 
712                         "ERROR: No menu entries found\n"
713 #if 1
714                         "Rebuild menu program with a menu configuration file.\n"
715 #endif
716                         "Press any key:"
717                 );
718                 getchar();
719
720                 /* 2 = Tell Etherboot to retry */               
721                 return( 2 );
722         }
723         i = select_entry( );
724 #if 0
725         gotoxy( 30, 13 );
726         printf( "Selected entry:%d [%s]", i, menu_entries[i] );
727         getchar();
728 #else
729         console_cls();
730 #endif
731         if (memcmp(menu_entries[i], "Quit Etherboot", sizeof("Quit Etherboot")) == 0)
732                 return (255);
733         set_file_to_load ( bootp,  menu_entries[ i ] );
734         return ( 1 );
735 }