Adjust memory layout for 2.6.22+ kernels with 32KB setup code
[mknbi.git] / menu.c
1 #include        "stddef.h"
2 #include        "string.h"
3 #include        "printf.h"
4 #include        "ansiesc.h"
5 #include        "misc.h"
6 #include        "linux-asm-io.h"
7 #include        "etherboot.h"
8 #include        "startmenu.h"
9 #include        "elf_boot.h"
10 #include        "bootmenu.h"
11 #ifdef CONSOLE_SERIAL
12 #include        "serial.h"
13 #endif /* CONSOLE_SERIAL */
14
15 /*
16  * This program is free software; you can redistribute it and/or
17  * modify it under the terms of the GNU General Public License as
18  * published by the Free Software Foundation; either version 2, or (at
19  * your option) any later version.
20  */
21
22 /*
23
24 This is an example program which shows how the extension routine
25 feature in Etherboot 5.0 works.
26
27 This program is linked to run at 0x60000, and expects to find config
28 data if any at 0x70000. This means the code can be up to 64kB long.
29
30 When the program starts it receives 3 parameters from Etherboot:
31
32 Pointer to ebinfo structure
33 Pointer to image header structure (either a tagged or ELF image header)
34 Pointer to bootp/DHCP reply obtained by Etherboot from bootpd or DHCPD
35
36 Etherboot expects this program to return an int. The values have these
37 meanings:
38
39 <0      Do not use
40 0       Same as 1, for implementation reasons
41 1       Redo tftp with possibly modified bootp record
42 2       Redo bootp and tftp
43 255     Exit Etherboot
44
45 Observe that this program causes Etherboot to load a different program
46 next by modifying the contents of the filename field in the bootp record
47 and then returning 1. It can also send parameters to the next program by
48 modifying tag 129 in the bootp record.
49
50 */
51
52 /*
53
54 Memory layout assumed by mknbi and this program
55
56 0x60000-0x6FFFF    64 kB        Menu program
57 0x70000-0x7FFFF    64 kB        Menu data (initial)
58
59 */
60
61 static unsigned char    *vendortags;
62 static unsigned char    *end_of_rfc1533;
63 #ifdef IMAGE_FREEBSD
64         /* yes this is a pain FreeBSD uses this for swap, however,
65            there are cases when you don't want swap and then
66            you want this set to get the extra features so lets
67            just set if dealing with FreeBSD.  I haven't run into
68            any troubles with this but I have without it
69         */
70 static int vendorext_is_valid = 1;
71 #else
72 static int vendorext_is_valid = 0;
73 #endif
74 static unsigned char    *motd[RFC1533_VENDOR_NUMOFMOTD] = { 0 };
75 static unsigned char    *imagelist[RFC1533_VENDOR_NUMOFIMG] = { 0 };
76
77 static inline void checkvendor(void)
78 {
79         union {
80                 unsigned long   l;
81                 unsigned char   c[4];
82         } u;
83
84         memcpy(u.c, vendortags, sizeof(u));
85         if (u.l == RFC_1048 || u.l == VEND_CMU || u.l == VEND_STAN)
86                 vendortags += 4;
87         else
88                 vendortags = 0;
89 }
90
91 static void parsebootp(unsigned char *vendor_tags)
92 {
93         unsigned char           *p;
94         unsigned int            c;
95         static unsigned char    vendorext_magic[] = {0xE4,0x45,0x74,0x68}; /* √§Eth */
96
97         memset(motd, 0, sizeof(motd));
98         memset(imagelist, 0, sizeof(imagelist));
99         if (vendortags == 0)
100                 return;
101         for (p = vendor_tags; (c = *p) != RFC1533_END; ) {
102                 if (c == RFC1533_PAD) {
103                         p++;
104                         continue;
105                 }
106 #if     DEBUG > 1
107                 printf("Tag %d\n", c);
108 #endif
109                 switch (c) {
110                 case RFC1533_VENDOR_MAGIC:
111                         if (TAG_LEN(p) >= 6
112                                 && !memcmp(p+2, vendorext_magic, 4)
113                                 && p[6] == RFC1533_VENDOR_MAJOR)
114                                 vendorext_is_valid = 1;
115                         break;
116                 case RFC1533_VENDOR_ETHERBOOT_ENCAP:
117                         parsebootp(p+2);
118                         break;
119                 case RFC1533_VENDOR_MENUOPTS:
120                         parse_menuopts(p+2, TAG_LEN(p));
121                         break;
122                 default:
123                         if (c >= RFC1533_VENDOR_MOTD && c < RFC1533_VENDOR_MOTD + RFC1533_VENDOR_NUMOFMOTD)
124                                 motd[c-RFC1533_VENDOR_MOTD] = p;
125                         else if (c >= RFC1533_VENDOR_IMG && c < RFC1533_VENDOR_IMG + RFC1533_VENDOR_NUMOFIMG) 
126                                 imagelist[c-RFC1533_VENDOR_IMG] = p;
127                         break;
128                 }
129                 p += p[1] + 2;
130         }
131         end_of_rfc1533 = p;
132 }
133
134 static void parse_elf_boot_notes(
135         void *notes, union infoblock **rheader, struct bootpd_t **rbootp)
136 {
137         unsigned char *note, *end;
138         Elf_Bhdr *bhdr;
139         Elf_Nhdr *hdr;
140
141         bhdr = notes;
142         if (bhdr->b_signature != ELF_BHDR_MAGIC) {
143                 return;
144         }
145
146         note = ((char *)bhdr) + sizeof(*bhdr);
147         end  = ((char *)bhdr) + bhdr->b_size;
148         while (note < end) {
149                 unsigned char *n_name, *n_desc, *next;
150                 hdr = (Elf_Nhdr *)note;
151                 n_name = note + sizeof(*hdr);
152                 n_desc = n_name + ((hdr->n_namesz + 3) & ~3);
153                 next = n_desc + ((hdr->n_descsz + 3) & ~3);
154                 if (next > end) 
155                         break;
156 #if 0
157                 printf("n_type: %x n_name(%d): n_desc(%d): \n", 
158                         hdr->n_type, hdr->n_namesz, hdr->n_descsz);
159 #endif
160
161                 if ((hdr->n_namesz == 10) &&
162                         (memcmp(n_name, "Etherboot", 10) == 0)) {
163                         switch(hdr->n_type) {
164                         case EB_BOOTP_DATA:
165                                 *rbootp = *((void **)n_desc);
166                                 break;
167                         case EB_HEADER:
168                                 *rheader = *((void **)n_desc);
169                                 break;
170                         default:
171                                 break;
172                         }
173                 }
174                 note = next;
175         }
176 }
177
178 int menu(struct ebinfo *eb, union infoblock *header, struct bootpd_t *bootp)
179 {
180         int             i;
181
182 #ifdef  DEBUG
183         printf(MKNBI_VERSION "\n");
184 #endif
185         parse_elf_boot_notes(eb, &header, &bootp);
186         /* Sanity check */
187         if (header->img.magic != ELF_MAGIC && header->img.magic != TAG_MAGIC) {
188                 printf("Bad argument passed from Etherboot\n");
189                 return (255);
190         }
191         vendortags = (unsigned char *)bootp->bootp_reply.bp_vend;
192         checkvendor();
193         parsebootp(vendortags);
194         if (!vendorext_is_valid) {
195                 printf("No menu vendor tags found, returning to Etherboot\n");
196                 sleep(10);
197                 return(2);
198         }
199 #ifdef CONSOLE_SERIAL
200         serial_init();
201 #endif
202 #ifdef  ANSIESC
203         ansi_reset();
204 #endif
205         show_motd(motd);
206         i = selectImage(bootp, imagelist, end_of_rfc1533);
207         if (i == 2) {
208                 printf("No selection, returning to Etherboot\n");
209                 sleep(10);
210         }
211 #ifdef CONSOLE_SERIAL
212         serial_fini();
213 #endif
214         return (i);
215 }