#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);
}
syntax highlighted by Code2HTML, v. 0.9.1