#include "stddef.h" #include "string.h" #include "printf.h" #include "ansiesc.h" #include "misc.h" #include "linux-asm-io.h" #include "etherboot.h" #include "startmenu.h" #include "elf_boot.h" #include "bootmenu.h" /* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2, or (at * your option) any later version. */ /* This is an example program which shows how the extension routine feature in Etherboot 5.0 works. This program is linked to run at 0x60000, and expects to find config data if any at 0x70000. This means the code can be up to 64kB long. When the program starts it receives 3 parameters from Etherboot: Pointer to ebinfo structure Pointer to image header structure (either a tagged or ELF image header) Pointer to bootp/DHCP reply obtained by Etherboot from bootpd or DHCPD Etherboot expects this program to return an int. The values have these meanings: <0 Do not use 0 Same as 1, for implementation reasons 1 Redo tftp with possibly modified bootp record 2 Redo bootp and tftp 255 Exit Etherboot Observe that this program causes Etherboot to load a different program next by modifying the contents of the filename field in the bootp record and then returning 1. It can also send parameters to the next program by modifying tag 129 in the bootp record. */ /* Memory layout assumed by mknbi and this program 0x60000-0x6FFFF 64 kB Menu program 0x70000-0x7FFFF 64 kB Menu data (initial) */ static unsigned char *vendortags; static unsigned char *end_of_rfc1533; #ifdef IMAGE_FREEBSD /* yes this is a pain FreeBSD uses this for swap, however, there are cases when you don't want swap and then you want this set to get the extra features so lets just set if dealing with FreeBSD. I haven't run into any troubles with this but I have without it */ static int vendorext_is_valid = 1; #else static int vendorext_is_valid = 0; #endif static unsigned char *motd[RFC1533_VENDOR_NUMOFMOTD] = { 0 }; static unsigned char *imagelist[RFC1533_VENDOR_NUMOFIMG] = { 0 }; static inline void checkvendor(void) { union { unsigned long l; unsigned char c[4]; } u; memcpy(u.c, vendortags, sizeof(u)); if (u.l == RFC_1048 || u.l == VEND_CMU || u.l == VEND_STAN) vendortags += 4; else vendortags = 0; } static void parsebootp() { unsigned char *p; unsigned int c; static unsigned char vendorext_magic[] = {0xE4,0x45,0x74,0x68}; /* äEth */ memset(motd, 0, sizeof(motd)); memset(imagelist, 0, sizeof(imagelist)); if (vendortags == 0) return; for (p = vendortags; (c = *p) != RFC1533_END; ) { if (c == RFC1533_PAD) { p++; continue; } #if DEBUG > 1 printf("Tag %d\n", c); #endif switch (c) { case RFC1533_VENDOR_MAGIC: if (TAG_LEN(p) >= 6 && !memcmp(p+2, vendorext_magic, 4) && p[6] == RFC1533_VENDOR_MAJOR) vendorext_is_valid = 1; break; case RFC1533_VENDOR_MENUOPTS: parse_menuopts(p+2, TAG_LEN(p)); break; default: if (c >= RFC1533_VENDOR_MOTD && c < RFC1533_VENDOR_MOTD + RFC1533_VENDOR_NUMOFMOTD) motd[c-RFC1533_VENDOR_MOTD] = p; else if (c >= RFC1533_VENDOR_IMG && c < RFC1533_VENDOR_IMG + RFC1533_VENDOR_NUMOFIMG) imagelist[c-RFC1533_VENDOR_IMG] = p; break; } p += p[1] + 2; } end_of_rfc1533 = p; } static void parse_elf_boot_notes( void *notes, union infoblock **rheader, struct bootpd_t **rbootp) { unsigned char *note, *end; Elf_Bhdr *bhdr; Elf_Nhdr *hdr; bhdr = notes; if (bhdr->b_signature != ELF_BHDR_MAGIC) { return; } note = ((char *)bhdr) + sizeof(*bhdr); end = ((char *)bhdr) + bhdr->b_size; while (note < end) { unsigned char *n_name, *n_desc, *next; hdr = (Elf_Nhdr *)note; n_name = note + sizeof(*hdr); n_desc = n_name + ((hdr->n_namesz + 3) & ~3); next = n_desc + ((hdr->n_descsz + 3) & ~3); if (next > end) break; #if 0 printf("n_type: %x n_name(%d): n_desc(%d): \n", hdr->n_type, hdr->n_namesz, hdr->n_descsz); #endif if ((hdr->n_namesz == 10) && (memcmp(n_name, "Etherboot", 10) == 0)) { switch(hdr->n_type) { case EB_BOOTP_DATA: *rbootp = *((void **)n_desc); break; case EB_HEADER: *rheader = *((void **)n_desc); break; default: break; } } note = next; } } int menu(struct ebinfo *eb, union infoblock *header, struct bootpd_t *bootp) { int i; extern int serial_init(void); extern void serial_fini(void); #ifdef DEBUG printf(MKNBI_VERSION "\n"); #endif parse_elf_boot_notes(eb, &header, &bootp); /* Sanity check */ if (header->img.magic != ELF_MAGIC && header->img.magic != TAG_MAGIC) { printf("Bad argument passed from Etherboot\n"); return (255); } vendortags = (unsigned char *)bootp->bootp_reply.bp_vend; checkvendor(); parsebootp(); if (!vendorext_is_valid) { printf("No menu vendor tags found, returning to Etherboot\n"); sleep(10); return(2); } #ifdef CONSOLE_SERIAL serial_init(); #endif #ifdef ANSIESC ansi_reset(); #endif show_motd(motd); i = selectImage(bootp, imagelist, end_of_rfc1533); if (i == 2) { printf("No selection, returning to Etherboot\n"); sleep(10); } #ifdef CONSOLE_SERIAL serial_fini(); #endif return (i); }