summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
authorMichael Brown2005-03-08 19:53:11 +0100
committerMichael Brown2005-03-08 19:53:11 +0100
commit3d6123e69ab879c72ff489afc5bf93ef0b7a94ce (patch)
tree9f3277569153a550fa8d81ebd61bd88f266eb8da /src/core
downloadipxe-3d6123e69ab879c72ff489afc5bf93ef0b7a94ce.tar.gz
ipxe-3d6123e69ab879c72ff489afc5bf93ef0b7a94ce.tar.xz
ipxe-3d6123e69ab879c72ff489afc5bf93ef0b7a94ce.zip
Initial revision
Diffstat (limited to 'src/core')
-rw-r--r--src/core/btext.c5196
-rw-r--r--src/core/config.c161
-rw-r--r--src/core/disk.c283
-rw-r--r--src/core/dns_resolver.c419
-rw-r--r--src/core/elf_loader.c658
-rw-r--r--src/core/heap.c168
-rw-r--r--src/core/i82365.c656
-rw-r--r--src/core/isa_probe.c66
-rw-r--r--src/core/isapnp.c382
-rw-r--r--src/core/main.c529
-rw-r--r--src/core/misc.c415
-rw-r--r--src/core/nfs.c610
-rw-r--r--src/core/nic.c1778
-rw-r--r--src/core/osloader.c365
-rw-r--r--src/core/pc_kbd.c108
-rw-r--r--src/core/pci.c337
-rw-r--r--src/core/pci_probe.c70
-rw-r--r--src/core/pcmcia.c269
-rw-r--r--src/core/proto_eth_slow.c407
-rw-r--r--src/core/proto_http.c206
-rw-r--r--src/core/proto_slam.c541
-rw-r--r--src/core/proto_tftm.c491
-rw-r--r--src/core/pxe_export.c1424
-rw-r--r--src/core/relocate.c103
-rw-r--r--src/core/serial.c236
-rw-r--r--src/core/string.c540
-rw-r--r--src/core/timer.c30
-rw-r--r--src/core/vsprintf.c166
28 files changed, 16614 insertions, 0 deletions
diff --git a/src/core/btext.c b/src/core/btext.c
new file mode 100644
index 00000000..12afb8d5
--- /dev/null
+++ b/src/core/btext.c
@@ -0,0 +1,5196 @@
+#ifdef CONSOLE_BTEXT
+#ifdef CONFIG_PCI
+/*
+ * Procedures for drawing on the screen early on in the boot process.
+ *
+ * Benjamin Herrenschmidt <benh@kernel.crashing.org>
+ *
+ * move to LinuxBIOS by LYH yhlu@tyan.com
+ * move to Etherboot by LYH
+ */
+
+#include "etherboot.h"
+#include "pci.h"
+
+#ifdef CONFIG_FILO
+#include <lib.h>
+#endif
+
+#undef __BIG_ENDIAN
+#if 0
+#define __LITTLE_ENDIAN
+#endif
+
+#include "btext.h"
+
+//#define NO_SCROLL
+
+#ifndef NO_SCROLL
+static void scrollscreen(void);
+#endif
+
+static void draw_byte(unsigned char c, u32 locX, u32 locY);
+#if 0
+static void draw_byte_32(unsigned char *bits, u32 *base, u32 rb);
+static void draw_byte_16(unsigned char *bits, u32 *base, u32 rb);
+#endif
+static void draw_byte_8(unsigned char *bits, u32 *base, u32 rb);
+
+static u32 g_loc_X;
+static u32 g_loc_Y;
+static u32 g_max_loc_X;
+static u32 g_max_loc_Y;
+
+#define CHAR_256 0
+
+#if CHAR_256==1
+#define cmapsz (16*256)
+#else
+#define cmapsz (16*96)
+#endif
+
+static unsigned char vga_font[cmapsz];
+
+u32 boot_text_mapped;
+
+boot_infos_t disp_bi;
+
+#define BTEXT
+#define BTDATA
+
+
+/* This function will enable the early boot text when doing OF booting. This
+ * way, xmon output should work too
+ */
+void
+btext_setup_display(u32 width, u32 height, u32 depth, u32 pitch,
+ unsigned long address)
+{
+ boot_infos_t* bi = &disp_bi;
+
+ g_loc_X = 0;
+ g_loc_Y = 0;
+ g_max_loc_X = width / 8;
+ g_max_loc_Y = height / 16;
+// bi->logicalDisplayBase = (unsigned char *)address;
+ bi->dispDeviceBase = (unsigned char *)address;
+ bi->dispDeviceRowBytes = pitch;
+ bi->dispDeviceDepth = depth;
+ bi->dispDeviceRect[0] = bi->dispDeviceRect[1] = 0;
+ bi->dispDeviceRect[2] = width;
+ bi->dispDeviceRect[3] = height;
+ boot_text_mapped = 0;
+}
+
+/* Here's a small text engine to use during early boot
+ * or for debugging purposes
+ *
+ * todo:
+ *
+ * - build some kind of vgacon with it to enable early printk
+ * - move to a separate file
+ * - add a few video driver hooks to keep in sync with display
+ * changes.
+ */
+
+void
+map_boot_text(void)
+{
+ boot_infos_t *bi = &disp_bi;
+
+ if (bi->dispDeviceBase == 0)
+ return;
+
+ boot_text_mapped = 0;
+
+ bi->logicalDisplayBase = phys_to_virt(bi->dispDeviceBase);
+
+ boot_text_mapped = 1;
+}
+
+/* Calc the base address of a given point (x,y) */
+static unsigned char * BTEXT
+calc_base(boot_infos_t *bi, u32 x, u32 y)
+{
+ unsigned char *base;
+#if 1
+ base = bi->logicalDisplayBase;
+ if (base == 0)
+#endif
+ base = bi->dispDeviceBase;
+ base += (x + bi->dispDeviceRect[0]) * (bi->dispDeviceDepth >> 3);
+ base += (y + bi->dispDeviceRect[1]) * bi->dispDeviceRowBytes;
+ return base;
+}
+
+
+void BTEXT btext_clearscreen(void)
+{
+ boot_infos_t* bi = &disp_bi;
+ u32 *base = (u32 *)calc_base(bi, 0, 0);
+ u32 width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) *
+ (bi->dispDeviceDepth >> 3)) >> 2;
+ u32 i,j;
+
+ for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1]); i++)
+ {
+ u32 *ptr = base;
+ for(j=width; j; --j)
+ *(ptr++) = 0;
+ base += (bi->dispDeviceRowBytes >> 2);
+ }
+}
+
+#if 0
+__inline__ void dcbst(const void* addr)
+{
+ __asm__ __volatile__ ("dcbst 0,%0" :: "r" (addr));
+}
+
+void BTEXT btext_flushscreen(void)
+{
+ boot_infos_t* bi = &disp_bi;
+ u32 *base = (unsigned long *)calc_base(bi, 0, 0);
+ u32 width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) *
+ (bi->dispDeviceDepth >> 3)) >> 2;
+ u32 i,j;
+
+ for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1]); i++)
+ {
+ u32 *ptr = base;
+ for(j=width; j>0; j-=8) {
+ dcbst(ptr);
+ ptr += 8;
+ }
+ base += (bi->dispDeviceRowBytes >> 2);
+ }
+}
+#endif
+
+
+#ifndef NO_SCROLL
+static BTEXT void
+scrollscreen(void)
+{
+ boot_infos_t* bi = &disp_bi;
+ u32 *src = (u32 *)calc_base(bi,0,16);
+ u32 *dst = (u32 *)calc_base(bi,0,0);
+ u32 width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) *
+ (bi->dispDeviceDepth >> 3)) >> 2;
+ u32 i,j;
+
+ for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1] - 16); i++)
+ {
+ u32 *src_ptr = src;
+ u32 *dst_ptr = dst;
+ for(j=width; j; --j)
+ *(dst_ptr++) = *(src_ptr++);
+ src += (bi->dispDeviceRowBytes >> 2);
+ dst += (bi->dispDeviceRowBytes >> 2);
+ }
+ for (i=0; i<16; i++)
+ {
+ u32 *dst_ptr = dst;
+ for(j=width; j; --j)
+ *(dst_ptr++) = 0;
+ dst += (bi->dispDeviceRowBytes >> 2);
+ }
+}
+#endif /* ndef NO_SCROLL */
+
+void BTEXT btext_drawchar(char c)
+{
+ u32 cline = 0;
+
+ if (!boot_text_mapped)
+ return;
+
+ switch (c) {
+ case '\b':
+ if (g_loc_X > 0)
+ --g_loc_X;
+ break;
+ case '\t':
+ g_loc_X = (g_loc_X & -8) + 8;
+ break;
+ case '\r':
+ g_loc_X = 0;
+ break;
+ case '\n':
+ g_loc_X = 0;
+ g_loc_Y++;
+ cline = 1;
+ break;
+ default:
+ draw_byte(c, g_loc_X++, g_loc_Y);
+ }
+ if (g_loc_X >= g_max_loc_X) {
+ g_loc_X = 0;
+ g_loc_Y++;
+ cline = 1;
+ }
+#ifndef NO_SCROLL
+ while (g_loc_Y >= g_max_loc_Y) {
+ scrollscreen();
+ g_loc_Y--;
+ }
+#else
+ /* wrap around from bottom to top of screen so we don't
+ waste time scrolling each line. -- paulus. */
+ if (g_loc_Y >= g_max_loc_Y)
+ g_loc_Y = 0;
+ if (cline) {
+ for (x = 0; x < g_max_loc_X; ++x)
+ draw_byte(' ', x, g_loc_Y);
+ }
+#endif
+}
+#if 0
+void BTEXT
+btext_drawstring(const char *c)
+{
+ if (!boot_text_mapped)
+ return;
+ while (*c)
+ btext_drawchar(*c++);
+}
+void BTEXT
+btext_drawhex(u32 v)
+{
+ static char hex_table[] = "0123456789abcdef";
+
+ if (!boot_text_mapped)
+ return;
+ btext_drawchar(hex_table[(v >> 28) & 0x0000000FUL]);
+ btext_drawchar(hex_table[(v >> 24) & 0x0000000FUL]);
+ btext_drawchar(hex_table[(v >> 20) & 0x0000000FUL]);
+ btext_drawchar(hex_table[(v >> 16) & 0x0000000FUL]);
+ btext_drawchar(hex_table[(v >> 12) & 0x0000000FUL]);
+ btext_drawchar(hex_table[(v >> 8) & 0x0000000FUL]);
+ btext_drawchar(hex_table[(v >> 4) & 0x0000000FUL]);
+ btext_drawchar(hex_table[(v >> 0) & 0x0000000FUL]);
+ btext_drawchar(' ');
+}
+#endif
+
+static void BTEXT
+draw_byte(unsigned char c, u32 locX, u32 locY)
+{
+ boot_infos_t* bi = &disp_bi;
+ unsigned char *base = calc_base(bi, locX << 3, locY << 4);
+#if CHAR_256==1
+ unsigned char *font = &vga_font[(((u32)c)) * 16];
+#else
+ unsigned char *font = &vga_font[(((u32)c-0x20)) * 16];
+#endif
+
+ u32 rb = bi->dispDeviceRowBytes;
+
+ switch(bi->dispDeviceDepth) {
+#if 0
+ case 24:
+ case 32:
+ draw_byte_32(font, (u32 *)base, rb);
+ break;
+ case 15:
+ case 16:
+ draw_byte_16(font, (u32 *)base, rb);
+ break;
+#endif
+ case 8:
+ draw_byte_8(font, (u32 *)base, rb);
+ break;
+ }
+}
+static u32 expand_bits_8[16] BTDATA = {
+#if defined(__BIG_ENDIAN)
+ 0x00000000,0x000000ff,0x0000ff00,0x0000ffff,
+ 0x00ff0000,0x00ff00ff,0x00ffff00,0x00ffffff,
+ 0xff000000,0xff0000ff,0xff00ff00,0xff00ffff,
+ 0xffff0000,0xffff00ff,0xffffff00,0xffffffff
+#elif defined(__LITTLE_ENDIAN)
+ 0x00000000,0xff000000,0x00ff0000,0xffff0000,
+ 0x0000ff00,0xff00ff00,0x00ffff00,0xffffff00,
+ 0x000000ff,0xff0000ff,0x00ff00ff,0xffff00ff,
+ 0x0000ffff,0xff00ffff,0x00ffffff,0xffffffff
+#else
+#error FIXME: No endianness??
+#endif
+};
+#if 0
+static const u32 expand_bits_16[4] BTDATA = {
+#if defined(__BIG_ENDIAN)
+ 0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff
+#elif defined(__LITTLE_ENDIAN)
+ 0x00000000, 0xffff0000, 0x0000ffff, 0xffffffff
+#else
+#error FIXME: No endianness??
+#endif
+};
+#endif
+#if 0
+static void BTEXT
+draw_byte_32(unsigned char *font, u32 *base, u32 rb)
+{
+ u32 l, bits;
+ u32 fg = 0xFFFFFFFF;
+ u32 bg = 0x00000000;
+
+ for (l = 0; l < 16; ++l)
+ {
+ bits = *font++;
+ base[0] = (-(bits >> 7) & fg) ^ bg;
+ base[1] = (-((bits >> 6) & 1) & fg) ^ bg;
+ base[2] = (-((bits >> 5) & 1) & fg) ^ bg;
+ base[3] = (-((bits >> 4) & 1) & fg) ^ bg;
+ base[4] = (-((bits >> 3) & 1) & fg) ^ bg;
+ base[5] = (-((bits >> 2) & 1) & fg) ^ bg;
+ base[6] = (-((bits >> 1) & 1) & fg) ^ bg;
+ base[7] = (-(bits & 1) & fg) ^ bg;
+ base = (u32 *) ((char *)base + rb);
+ }
+}
+
+static void BTEXT
+draw_byte_16(unsigned char *font, u32 *base, u32 rb)
+{
+ u32 l, bits;
+ u32 fg = 0xFFFFFFFF;
+ u32 bg = 0x00000000;
+ u32 *eb = expand_bits_16;
+
+ for (l = 0; l < 16; ++l)
+ {
+ bits = *font++;
+ base[0] = (eb[bits >> 6] & fg) ^ bg;
+ base[1] = (eb[(bits >> 4) & 3] & fg) ^ bg;
+ base[2] = (eb[(bits >> 2) & 3] & fg) ^ bg;
+ base[3] = (eb[bits & 3] & fg) ^ bg;
+ base = (u32 *) ((char *)base + rb);
+ }
+}
+#endif
+static void BTEXT
+draw_byte_8(unsigned char *font, u32 *base, u32 rb)
+{
+ u32 l, bits;
+ u32 fg = 0x0F0F0F0F;
+ u32 bg = 0x00000000;
+ u32 *eb = expand_bits_8;
+
+ for (l = 0; l < 16; ++l)
+ {
+ bits = *font++;
+ base[0] = (eb[bits >> 4] & fg) ^ bg;
+ base[1] = (eb[bits & 0xf] & fg) ^ bg;
+ base = (u32 *) ((char *)base + rb);
+ }
+}
+
+#ifdef CONFIG_FILO
+#define USE_FILO_PCI_FIND 1
+#else
+#define USE_FILO_PCI_FIND 0
+#endif
+
+
+void btext_init(void)
+{
+#if 0
+// for debug
+#define frame_buffer 0xfc000000
+#else
+ uint32_t frame_buffer;// 0xfc000000
+
+ struct pci_device *dev = 0;
+
+#if USE_FILO_PCI_FIND==0
+ pci_find_device_x(0x1002, 0x4752, 0, &dev);
+ if(dev.vendor==0) return; // no fb
+
+ frame_buffer = (uint32_t)dev.membase;
+#else
+ pci_init();
+ dev = pci_find_device(0x1002, 0x4752, -1, -1, 0);
+ if(!dev) {
+ return; // no fb
+ }
+
+ pci_read_config_dword(dev, 0x10, &frame_buffer);
+#endif
+
+#endif
+
+ btext_setup_display(640, 480, 8, 640,frame_buffer);
+// btext_clearscreen(); //move to main
+// map_boot_text(); //move console_init
+}
+void btext_putc(int c)
+{
+ btext_drawchar((unsigned char)c);
+}
+#if 0
+static struct console_driver btext_console __console = {
+ .init = btext_init,
+ .tx_byte = btext_tx_byte,
+ .rx_byte = 0,
+ .tst_byte = 0,
+};
+#endif
+#if USE_FILO_PCI_FIND==0
+int pci_find_device_x(int vendorx, int devicex, int index, struct pci_device *dev)
+{
+ unsigned int first_bus, first_devfn;
+ unsigned int devfn, bus, buses;
+#if 1
+ unsigned char hdr_type = 0;
+#endif
+ uint32_t class;
+ uint16_t vendor, device;
+ uint32_t l, membase;
+#if 0
+ uint32_t ioaddr, romaddr;
+ int reg;
+#endif
+
+ first_bus = 0;
+ first_devfn = 0;
+#if 0
+ if(dev->vendor!=0){
+ first_bus = dev->bus;
+ first_devfn = dev->devfn;
+ /* Re read the header type on a restart */
+ pcibios_read_config_byte(first_bus, first_devfn & ~0x7,
+ PCI_HEADER_TYPE, &hdr_type);
+ dev->bus = 0;
+ dev->devfn = 0;
+ }
+#endif
+
+ /* Scan all PCI buses, until we find our card.
+ * We could be smart only scan the required buses but that
+ * is error prone, and tricky.
+ * By scanning all possible pci buses in order we should find
+ * our card eventually.
+ */
+ buses=256;
+ for (bus = first_bus; bus < buses; ++bus) {
+ for (devfn = first_devfn; devfn < 0xff; ++devfn) {
+#if 1
+ if (PCI_FUNC (devfn) == 0)
+ pcibios_read_config_byte(bus, devfn, PCI_HEADER_TYPE, &hdr_type);
+ else if (!(hdr_type & 0x80)) /* not a multi-function device */
+ continue;
+#endif
+ pcibios_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l);
+ /* some broken boards return 0 if a slot is empty: */
+ if (l == 0xffffffff || l == 0x00000000) {
+ continue;
+ }
+ vendor = l & 0xffff;
+ device = (l >> 16) & 0xffff;
+#if 0
+ pcibios_read_config_dword(bus, devfn, PCI_REVISION, &l);
+ class = (l >> 8) & 0xffffff;
+#endif
+
+#if 0
+ {
+ int i;
+ printf("%hhx:%hhx.%hhx [%hX/%hX]\n",
+ bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
+ vendor, device);
+#if 0
+ for(i = 0; i < 256; i++) {
+ unsigned char byte;
+ if ((i & 0xf) == 0) {
+ printf("%hhx: ", i);
+ }
+ pcibios_read_config_byte(bus, devfn, i, &byte);
+ printf("%hhx ", byte);
+ if ((i & 0xf) == 0xf) {
+ printf("\n");
+ }
+ }
+#endif
+
+ }
+#endif
+ if(vendor != vendorx) continue;
+
+ if(device != devicex) continue;
+
+ if(index !=0 ) {
+ index--;
+ continue;
+ }
+
+
+ dev->devfn = devfn;
+ dev->bus = bus;
+#if 0
+ dev->class = class;
+#endif
+ dev->vendor = vendor;
+ dev->dev_id = device;
+
+#if 0
+ /* Get the ROM base address */
+ pcibios_read_config_dword(bus, devfn,
+ PCI_ROM_ADDRESS, &romaddr);
+ romaddr >>= 10;
+ dev->romaddr = romaddr;
+#endif
+
+ /* Get the ``membase'' */
+ pcibios_read_config_dword(bus, devfn,
+ PCI_BASE_ADDRESS_0, &membase);
+ dev->membase = membase;
+#if 0
+ /* Get the ``ioaddr'' */
+ for (reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4) {
+ pcibios_read_config_dword(bus, devfn, reg, &ioaddr);
+ if ((ioaddr & PCI_BASE_ADDRESS_IO_MASK) == 0 || (ioaddr & PCI_BASE_ADDRESS_SPACE_IO) == 0)
+ continue;
+
+
+ /* Strip the I/O address out of the returned value */
+ ioaddr &= PCI_BASE_ADDRESS_IO_MASK;
+
+ /* Take the first one or the one that matches in boot ROM address */
+ dev->ioaddr = ioaddr;
+ }
+#endif
+#if 0
+ printf("Found %s ROM address %#hx\n",
+ dev->name, romaddr);
+#endif
+ return 1;
+ }
+ first_devfn = 0;
+ }
+ first_bus = 0;
+ return 0;
+}
+#endif
+//come from linux/drivers/video/font-8x16.c
+/**********************************************/
+/* */
+/* Font file generated by cpi2fnt */
+/* */
+/**********************************************/
+
+
+static unsigned char vga_font[cmapsz] BTDATA = {
+#if CHAR_256==1
+ /* 0 0x00 '^@' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 1 0x01 '^A' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x81, /* 10000001 */
+ 0xa5, /* 10100101 */
+ 0x81, /* 10000001 */
+ 0x81, /* 10000001 */
+ 0xbd, /* 10111101 */
+ 0x99, /* 10011001 */
+ 0x81, /* 10000001 */
+ 0x81, /* 10000001 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 2 0x02 '^B' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0xff, /* 11111111 */
+ 0xdb, /* 11011011 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xc3, /* 11000011 */
+ 0xe7, /* 11100111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 3 0x03 '^C' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x6c, /* 01101100 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0x7c, /* 01111100 */
+ 0x38, /* 00111000 */
+ 0x10, /* 00010000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 4 0x04 '^D' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x7c, /* 01111100 */
+ 0xfe, /* 11111110 */
+ 0x7c, /* 01111100 */
+ 0x38, /* 00111000 */
+ 0x10, /* 00010000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 5 0x05 '^E' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0xe7, /* 11100111 */
+ 0xe7, /* 11100111 */
+ 0xe7, /* 11100111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 6 0x06 '^F' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x7e, /* 01111110 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 7 0x07 '^G' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 8 0x08 '^H' */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xe7, /* 11100111 */
+ 0xc3, /* 11000011 */
+ 0xc3, /* 11000011 */
+ 0xe7, /* 11100111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+
+ /* 9 0x09 '^I' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x42, /* 01000010 */
+ 0x42, /* 01000010 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 10 0x0a '^J' */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xc3, /* 11000011 */
+ 0x99, /* 10011001 */
+ 0xbd, /* 10111101 */
+ 0xbd, /* 10111101 */
+ 0x99, /* 10011001 */
+ 0xc3, /* 11000011 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+
+ /* 11 0x0b '^K' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1e, /* 00011110 */
+ 0x0e, /* 00001110 */
+ 0x1a, /* 00011010 */
+ 0x32, /* 00110010 */
+ 0x78, /* 01111000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x78, /* 01111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 12 0x0c '^L' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 13 0x0d '^M' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3f, /* 00111111 */
+ 0x33, /* 00110011 */
+ 0x3f, /* 00111111 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x70, /* 01110000 */
+ 0xf0, /* 11110000 */
+ 0xe0, /* 11100000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 14 0x0e '^N' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7f, /* 01111111 */
+ 0x63, /* 01100011 */
+ 0x7f, /* 01111111 */
+ 0x63, /* 01100011 */
+ 0x63, /* 01100011 */
+ 0x63, /* 01100011 */
+ 0x63, /* 01100011 */
+ 0x67, /* 01100111 */
+ 0xe7, /* 11100111 */
+ 0xe6, /* 11100110 */
+ 0xc0, /* 11000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 15 0x0f '^O' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xdb, /* 11011011 */
+ 0x3c, /* 00111100 */
+ 0xe7, /* 11100111 */
+ 0x3c, /* 00111100 */
+ 0xdb, /* 11011011 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 16 0x10 '^P' */
+ 0x00, /* 00000000 */
+ 0x80, /* 10000000 */
+ 0xc0, /* 11000000 */
+ 0xe0, /* 11100000 */
+ 0xf0, /* 11110000 */
+ 0xf8, /* 11111000 */
+ 0xfe, /* 11111110 */
+ 0xf8, /* 11111000 */
+ 0xf0, /* 11110000 */
+ 0xe0, /* 11100000 */
+ 0xc0, /* 11000000 */
+ 0x80, /* 10000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 17 0x11 '^Q' */
+ 0x00, /* 00000000 */
+ 0x02, /* 00000010 */
+ 0x06, /* 00000110 */
+ 0x0e, /* 00001110 */
+ 0x1e, /* 00011110 */
+ 0x3e, /* 00111110 */
+ 0xfe, /* 11111110 */
+ 0x3e, /* 00111110 */
+ 0x1e, /* 00011110 */
+ 0x0e, /* 00001110 */
+ 0x06, /* 00000110 */
+ 0x02, /* 00000010 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 18 0x12 '^R' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 19 0x13 '^S' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 20 0x14 '^T' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7f, /* 01111111 */
+ 0xdb, /* 11011011 */
+ 0xdb, /* 11011011 */
+ 0xdb, /* 11011011 */
+ 0x7b, /* 01111011 */
+ 0x1b, /* 00011011 */
+ 0x1b, /* 00011011 */
+ 0x1b, /* 00011011 */
+ 0x1b, /* 00011011 */
+ 0x1b, /* 00011011 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 21 0x15 '^U' */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0x60, /* 01100000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x0c, /* 00001100 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 22 0x16 '^V' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 23 0x17 '^W' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 24 0x18 '^X' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 25 0x19 '^Y' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 26 0x1a '^Z' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0xfe, /* 11111110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 27 0x1b '^[' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xfe, /* 11111110 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 28 0x1c '^\' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 29 0x1d '^]' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x28, /* 00101000 */
+ 0x6c, /* 01101100 */
+ 0xfe, /* 11111110 */
+ 0x6c, /* 01101100 */
+ 0x28, /* 00101000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 30 0x1e '^^' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x38, /* 00111000 */
+ 0x7c, /* 01111100 */
+ 0x7c, /* 01111100 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 31 0x1f '^_' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0x7c, /* 01111100 */
+ 0x7c, /* 01111100 */
+ 0x38, /* 00111000 */
+ 0x38, /* 00111000 */
+ 0x10, /* 00010000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+#endif
+ /* 32 0x20 ' ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 33 0x21 '!' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 34 0x22 '"' */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x24, /* 00100100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 35 0x23 '#' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0xfe, /* 11111110 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0xfe, /* 11111110 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 36 0x24 '$' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc2, /* 11000010 */
+ 0xc0, /* 11000000 */
+ 0x7c, /* 01111100 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x86, /* 10000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 37 0x25 '%' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc2, /* 11000010 */
+ 0xc6, /* 11000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc6, /* 11000110 */
+ 0x86, /* 10000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 38 0x26 '&' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 39 0x27 ''' */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 40 0x28 '(' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 41 0x29 ')' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 42 0x2a '*' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0xff, /* 11111111 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 43 0x2b '+' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 44 0x2c ',' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 45 0x2d '-' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 46 0x2e '.' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 47 0x2f '/' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x02, /* 00000010 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+ 0x80, /* 10000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 48 0x30 '0' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 49 0x31 '1' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x38, /* 00111000 */
+ 0x78, /* 01111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 50 0x32 '2' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 51 0x33 '3' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x3c, /* 00111100 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 52 0x34 '4' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x0c, /* 00001100 */
+ 0x1c, /* 00011100 */
+ 0x3c, /* 00111100 */
+ 0x6c, /* 01101100 */
+ 0xcc, /* 11001100 */
+ 0xfe, /* 11111110 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x1e, /* 00011110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 53 0x35 '5' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xfc, /* 11111100 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 54 0x36 '6' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xfc, /* 11111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 55 0x37 '7' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 56 0x38 '8' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 57 0x39 '9' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7e, /* 01111110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x78, /* 01111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 58 0x3a ':' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 59 0x3b ';' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 60 0x3c '<' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x06, /* 00000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 61 0x3d '=' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 62 0x3e '>' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 63 0x3f '?' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 64 0x40 '@' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xde, /* 11011110 */
+ 0xde, /* 11011110 */
+ 0xde, /* 11011110 */
+ 0xdc, /* 11011100 */
+ 0xc0, /* 11000000 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 65 0x41 'A' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 66 0x42 'B' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfc, /* 11111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0xfc, /* 11111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 67 0x43 'C' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0xc2, /* 11000010 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc2, /* 11000010 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 68 0x44 'D' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xf8, /* 11111000 */
+ 0x6c, /* 01101100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x6c, /* 01101100 */
+ 0xf8, /* 11111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 69 0x45 'E' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x66, /* 01100110 */
+ 0x62, /* 01100010 */
+ 0x68, /* 01101000 */
+ 0x78, /* 01111000 */
+ 0x68, /* 01101000 */
+ 0x60, /* 01100000 */
+ 0x62, /* 01100010 */
+ 0x66, /* 01100110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 70 0x46 'F' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x66, /* 01100110 */
+ 0x62, /* 01100010 */
+ 0x68, /* 01101000 */
+ 0x78, /* 01111000 */
+ 0x68, /* 01101000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0xf0, /* 11110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 71 0x47 'G' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0xc2, /* 11000010 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xde, /* 11011110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x66, /* 01100110 */
+ 0x3a, /* 00111010 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 72 0x48 'H' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 73 0x49 'I' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 74 0x4a 'J' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1e, /* 00011110 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x78, /* 01111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 75 0x4b 'K' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xe6, /* 11100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x6c, /* 01101100 */
+ 0x78, /* 01111000 */
+ 0x78, /* 01111000 */
+ 0x6c, /* 01101100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0xe6, /* 11100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 76 0x4c 'L' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xf0, /* 11110000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x62, /* 01100010 */
+ 0x66, /* 01100110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 77 0x4d 'M' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xee, /* 11101110 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0xd6, /* 11010110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 78 0x4e 'N' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xe6, /* 11100110 */
+ 0xf6, /* 11110110 */
+ 0xfe, /* 11111110 */
+ 0xde, /* 11011110 */
+ 0xce, /* 11001110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 79 0x4f 'O' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 80 0x50 'P' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfc, /* 11111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0xf0, /* 11110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 81 0x51 'Q' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xd6, /* 11010110 */
+ 0xde, /* 11011110 */
+ 0x7c, /* 01111100 */
+ 0x0c, /* 00001100 */
+ 0x0e, /* 00001110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 82 0x52 'R' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfc, /* 11111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0x6c, /* 01101100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0xe6, /* 11100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 83 0x53 'S' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x60, /* 01100000 */
+ 0x38, /* 00111000 */
+ 0x0c, /* 00001100 */
+ 0x06, /* 00000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 84 0x54 'T' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x5a, /* 01011010 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 85 0x55 'U' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 86 0x56 'V' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x10, /* 00010000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 87 0x57 'W' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xfe, /* 11111110 */
+ 0xee, /* 11101110 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 88 0x58 'X' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x7c, /* 01111100 */
+ 0x38, /* 00111000 */
+ 0x38, /* 00111000 */
+ 0x7c, /* 01111100 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 89 0x59 'Y' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 90 0x5a 'Z' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0x86, /* 10000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc2, /* 11000010 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 91 0x5b '[' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 92 0x5c '\' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x80, /* 10000000 */
+ 0xc0, /* 11000000 */
+ 0xe0, /* 11100000 */
+ 0x70, /* 01110000 */
+ 0x38, /* 00111000 */
+ 0x1c, /* 00011100 */
+ 0x0e, /* 00001110 */
+ 0x06, /* 00000110 */
+ 0x02, /* 00000010 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 93 0x5d ']' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 94 0x5e '^' */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 95 0x5f '_' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 96 0x60 '`' */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 97 0x61 'a' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 98 0x62 'b' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xe0, /* 11100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x78, /* 01111000 */
+ 0x6c, /* 01101100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 99 0x63 'c' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 100 0x64 'd' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1c, /* 00011100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x3c, /* 00111100 */
+ 0x6c, /* 01101100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 101 0x65 'e' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 102 0x66 'f' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1c, /* 00011100 */
+ 0x36, /* 00110110 */
+ 0x32, /* 00110010 */
+ 0x30, /* 00110000 */
+ 0x78, /* 01111000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x78, /* 01111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 103 0x67 'g' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x7c, /* 01111100 */
+ 0x0c, /* 00001100 */
+ 0xcc, /* 11001100 */
+ 0x78, /* 01111000 */
+ 0x00, /* 00000000 */
+
+ /* 104 0x68 'h' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xe0, /* 11100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x6c, /* 01101100 */
+ 0x76, /* 01110110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0xe6, /* 11100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 105 0x69 'i' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 106 0x6a 'j' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x00, /* 00000000 */
+ 0x0e, /* 00001110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+
+ /* 107 0x6b 'k' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xe0, /* 11100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x66, /* 01100110 */
+ 0x6c, /* 01101100 */
+ 0x78, /* 01111000 */
+ 0x78, /* 01111000 */
+ 0x6c, /* 01101100 */
+ 0x66, /* 01100110 */
+ 0xe6, /* 11100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 108 0x6c 'l' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 109 0x6d 'm' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xec, /* 11101100 */
+ 0xfe, /* 11111110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 110 0x6e 'n' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xdc, /* 11011100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 111 0x6f 'o' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 112 0x70 'p' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xdc, /* 11011100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0xf0, /* 11110000 */
+ 0x00, /* 00000000 */
+
+ /* 113 0x71 'q' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x7c, /* 01111100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x1e, /* 00011110 */
+ 0x00, /* 00000000 */
+
+ /* 114 0x72 'r' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xdc, /* 11011100 */
+ 0x76, /* 01110110 */
+ 0x66, /* 01100110 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0xf0, /* 11110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 115 0x73 's' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0x60, /* 01100000 */
+ 0x38, /* 00111000 */
+ 0x0c, /* 00001100 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 116 0x74 't' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0xfc, /* 11111100 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x36, /* 00110110 */
+ 0x1c, /* 00011100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 117 0x75 'u' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 118 0x76 'v' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 119 0x77 'w' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xfe, /* 11111110 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 120 0x78 'x' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x38, /* 00111000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 121 0x79 'y' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7e, /* 01111110 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0xf8, /* 11111000 */
+ 0x00, /* 00000000 */
+
+ /* 122 0x7a 'z' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xcc, /* 11001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 123 0x7b '{' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x0e, /* 00001110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x70, /* 01110000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x0e, /* 00001110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 124 0x7c '|' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 125 0x7d '}' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x70, /* 01110000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x0e, /* 00001110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 126 0x7e '~' */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 127 0x7f '' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+#if CHAR_256256==1
+ /* 128 0x80 '€' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0xc2, /* 11000010 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc2, /* 11000010 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 129 0x81 '' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 130 0x82 '‚' */
+ 0x00, /* 00000000 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 131 0x83 'ƒ' */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 132 0x84 '„' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 133 0x85 '…' */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 134 0x86 '†' */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 135 0x87 '‡' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x18, /* 00011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 136 0x88 'ˆ' */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 137 0x89 '‰' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 138 0x8a 'Š' */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 139 0x8b '‹' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 140 0x8c 'Œ' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 141 0x8d '' */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 142 0x8e 'Ž' */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 143 0x8f '' */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 144 0x90 '' */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x66, /* 01100110 */
+ 0x62, /* 01100010 */
+ 0x68, /* 01101000 */
+ 0x78, /* 01111000 */
+ 0x68, /* 01101000 */
+ 0x62, /* 01100010 */
+ 0x66, /* 01100110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 145 0x91 '‘' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xec, /* 11101100 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x7e, /* 01111110 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0x6e, /* 01101110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 146 0x92 '’' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3e, /* 00111110 */
+ 0x6c, /* 01101100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xfe, /* 11111110 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xce, /* 11001110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 147 0x93 '“' */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 148 0x94 '”' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 149 0x95 '•' */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 150 0x96 '–' */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x78, /* 01111000 */
+ 0xcc, /* 11001100 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 151 0x97 '—' */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 152 0x98 '˜' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7e, /* 01111110 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x78, /* 01111000 */
+ 0x00, /* 00000000 */
+
+ /* 153 0x99 '™' */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 154 0x9a 'š' */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 155 0x9b '›' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 156 0x9c 'œ' */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x64, /* 01100100 */
+ 0x60, /* 01100000 */
+ 0xf0, /* 11110000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0xe6, /* 11100110 */
+ 0xfc, /* 11111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 157 0x9d '' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 158 0x9e 'ž' */
+ 0x00, /* 00000000 */
+ 0xf8, /* 11111000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xf8, /* 11111000 */
+ 0xc4, /* 11000100 */
+ 0xcc, /* 11001100 */
+ 0xde, /* 11011110 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 159 0x9f 'Ÿ' */
+ 0x00, /* 00000000 */
+ 0x0e, /* 00001110 */
+ 0x1b, /* 00011011 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xd8, /* 11011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 160 0xa0 ' ' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 161 0xa1 '¡' */
+ 0x00, /* 00000000 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 162 0xa2 '¢' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 163 0xa3 '£' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 164 0xa4 '¤' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x00, /* 00000000 */
+ 0xdc, /* 11011100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 165 0xa5 '¥' */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xe6, /* 11100110 */
+ 0xf6, /* 11110110 */
+ 0xfe, /* 11111110 */
+ 0xde, /* 11011110 */
+ 0xce, /* 11001110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 166 0xa6 '¦' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x3e, /* 00111110 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 167 0xa7 '§' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 168 0xa8 '¨' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 169 0xa9 '©' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 170 0xaa 'ª' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 171 0xab '«' */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0xe0, /* 11100000 */
+ 0x62, /* 01100010 */
+ 0x66, /* 01100110 */
+ 0x6c, /* 01101100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xdc, /* 11011100 */
+ 0x86, /* 10000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x3e, /* 00111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 172 0xac '¬' */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0xe0, /* 11100000 */
+ 0x62, /* 01100010 */
+ 0x66, /* 01100110 */
+ 0x6c, /* 01101100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x66, /* 01100110 */
+ 0xce, /* 11001110 */
+ 0x9a, /* 10011010 */
+ 0x3f, /* 00111111 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 173 0xad '­' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 174 0xae '®' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x36, /* 00110110 */
+ 0x6c, /* 01101100 */
+ 0xd8, /* 11011000 */
+ 0x6c, /* 01101100 */
+ 0x36, /* 00110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 175 0xaf '¯' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xd8, /* 11011000 */
+ 0x6c, /* 01101100 */
+ 0x36, /* 00110110 */
+ 0x6c, /* 01101100 */
+ 0xd8, /* 11011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 176 0xb0 '°' */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+
+ /* 177 0xb1 '±' */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+
+ /* 178 0xb2 '²' */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+
+ /* 179 0xb3 '³' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 180 0xb4 '´' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 181 0xb5 'µ' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 182 0xb6 '¶' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xf6, /* 11110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 183 0xb7 '·' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 184 0xb8 '¸' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 185 0xb9 '¹' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xf6, /* 11110110 */
+ 0x06, /* 00000110 */
+ 0xf6, /* 11110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 186 0xba 'º' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 187 0xbb '»' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x06, /* 00000110 */
+ 0xf6, /* 11110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 188 0xbc '¼' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xf6, /* 11110110 */
+ 0x06, /* 00000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 189 0xbd '½' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 190 0xbe '¾' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 191 0xbf '¿' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 192 0xc0 'À' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 193 0xc1 'Á' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 194 0xc2 'Â' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 195 0xc3 'Ã' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 196 0xc4 'Ä' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 197 0xc5 'Å' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 198 0xc6 'Æ' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 199 0xc7 'Ç' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x37, /* 00110111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 200 0xc8 'È' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x37, /* 00110111 */
+ 0x30, /* 00110000 */
+ 0x3f, /* 00111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 201 0xc9 'É' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3f, /* 00111111 */
+ 0x30, /* 00110000 */
+ 0x37, /* 00110111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 202 0xca 'Ê' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xf7, /* 11110111 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 203 0xcb 'Ë' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0xf7, /* 11110111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 204 0xcc 'Ì' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x37, /* 00110111 */
+ 0x30, /* 00110000 */
+ 0x37, /* 00110111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 205 0xcd 'Í' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 206 0xce 'Î' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xf7, /* 11110111 */
+ 0x00, /* 00000000 */
+ 0xf7, /* 11110111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 207 0xcf 'Ï' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 208 0xd0 'Ð' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 209 0xd1 'Ñ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 210 0xd2 'Ò' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 211 0xd3 'Ó' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x3f, /* 00111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 212 0xd4 'Ô' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 213 0xd5 'Õ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 214 0xd6 'Ö' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3f, /* 00111111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 215 0xd7 '×' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xff, /* 11111111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 216 0xd8 'Ø' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 217 0xd9 'Ù' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 218 0xda 'Ú' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 219 0xdb 'Û' */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+
+ /* 220 0xdc 'Ü' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+
+ /* 221 0xdd 'Ý' */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+
+ /* 222 0xde 'Þ' */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+
+ /* 223 0xdf 'ß' */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 224 0xe0 'à' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0xdc, /* 11011100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 225 0xe1 'á' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xd8, /* 11011000 */
+ 0xcc, /* 11001100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xcc, /* 11001100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 226 0xe2 'â' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 227 0xe3 'ã' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 228 0xe4 'ä' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 229 0xe5 'å' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 230 0xe6 'æ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+ 0x00, /* 00000000 */
+
+ /* 231 0xe7 'ç' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 232 0xe8 'è' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 233 0xe9 'é' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 234 0xea 'ê' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0xee, /* 11101110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 235 0xeb 'ë' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1e, /* 00011110 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x3e, /* 00111110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 236 0xec 'ì' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0xdb, /* 11011011 */
+ 0xdb, /* 11011011 */
+ 0xdb, /* 11011011 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 237 0xed 'í' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x03, /* 00000011 */
+ 0x06, /* 00000110 */
+ 0x7e, /* 01111110 */
+ 0xdb, /* 11011011 */
+ 0xdb, /* 11011011 */
+ 0xf3, /* 11110011 */
+ 0x7e, /* 01111110 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 238 0xee 'î' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1c, /* 00011100 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x7c, /* 01111100 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x1c, /* 00011100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 239 0xef 'ï' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 240 0xf0 'ð' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 241 0xf1 'ñ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 242 0xf2 'ò' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 243 0xf3 'ó' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 244 0xf4 'ô' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x0e, /* 00001110 */
+ 0x1b, /* 00011011 */
+ 0x1b, /* 00011011 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 245 0xf5 'õ' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 246 0xf6 'ö' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 247 0xf7 '÷' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 248 0xf8 'ø' */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 249 0xf9 'ù' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 250 0xfa 'ú' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 251 0xfb 'û' */
+ 0x00, /* 00000000 */
+ 0x0f, /* 00001111 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0xec, /* 11101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x3c, /* 00111100 */
+ 0x1c, /* 00011100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 252 0xfc 'ü' */
+ 0x00, /* 00000000 */
+ 0x6c, /* 01101100 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 253 0xfd 'ý' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x32, /* 00110010 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 254 0xfe 'þ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 255 0xff 'ÿ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+#endif
+};
+#endif
+#endif
diff --git a/src/core/config.c b/src/core/config.c
new file mode 100644
index 00000000..180b0669
--- /dev/null
+++ b/src/core/config.c
@@ -0,0 +1,161 @@
+/*
+ * 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.
+ */
+
+#include "etherboot.h"
+#include "nic.h"
+#ifdef BUILD_SERIAL
+#include ".buildserial.h"
+#define xstr(s) str(s)
+#define str(s) #s
+#endif
+
+void print_config(void)
+{
+ printf( "Etherboot " VERSION
+#ifdef BUILD_SERIAL
+ " [build "
+#ifdef BUILD_ID
+ BUILD_ID " "
+#endif
+ "#" xstr(BUILD_SERIAL_NUM) "]"
+#endif /* BUILD_SERIAL */
+ " (GPL) http://etherboot.org\n"
+ "Drivers: " );
+#ifdef CONFIG_PCI
+ pci_enumerate();
+#endif
+#ifdef CONFIG_ISA
+ isa_enumerate();
+#endif
+ printf( " Images: "
+#ifdef TAGGED_IMAGE
+ "NBI "
+#endif
+#ifdef ELF64_IMAGE
+ "ELF64 "
+#endif
+#ifdef ELF_IMAGE
+ "ELF "
+#endif
+#ifdef COFF_IMAGE
+ "COFF "
+#endif
+#ifdef IMAGE_FREEBSD
+ "FreeBSD "
+#endif
+#ifdef IMAGE_MULTIBOOT
+ "Multiboot "
+#endif
+#ifdef AOUT_IMAGE
+ "a.out "
+#endif
+#ifdef WINCE_IMAGE
+ "WINCE "
+#endif
+#ifdef PXE_IMAGE
+ "PXE "
+#endif
+#ifdef PXE_EXPORT /* All possible exports */
+ " Exports: "
+#ifdef PXE_EXPORT
+ "PXE "
+#endif
+#endif /* All possible exports */
+ " "
+ );
+#if (BOOTP_SERVER != 67) || (BOOTP_CLIENT != 68)
+ printf( "[DHCP ports %d and %d] ",
+ BOOTP_SERVER, BOOTP_CLIENT);
+#endif
+ putchar('\n');
+ printf( "Protocols: "
+#ifdef RARP_NOT_BOOTP
+ "RARP "
+#else
+# ifndef NO_DHCP_SUPPORT
+ "DHCP "
+# else
+ "BOOTP "
+# endif
+#endif
+#ifdef DOWNLOAD_PROTO_TFTP
+ "TFTP "
+#endif
+#ifdef DOWNLOAD_PROTO_NFS
+ "NFS "
+#endif
+#ifdef DOWNLOAD_PROTO_SLAM
+ "SLAM "
+#endif
+#ifdef DOWNLOAD_PROTO_TFTM
+ "TFTM "
+#endif
+#ifdef DOWNLOAD_PROTO_HTTP
+ "HTTP "
+#endif
+#ifdef PROTO_LACP
+ "LACP "
+#endif
+#ifdef DNS_RESOLVER
+ "DNS "
+#endif
+ "\n");
+}
+
+static const char *driver_name[] = {
+ "nic",
+ "disk",
+ "floppy",
+};
+
+int probe(struct dev *dev)
+{
+ const char *type_name;
+ type_name = "";
+ if ((dev->type >= 0) &&
+ ((unsigned)dev->type < sizeof(driver_name)/sizeof(driver_name[0]))) {
+ type_name = driver_name[dev->type];
+ }
+ if (dev->how_probe == PROBE_FIRST) {
+ dev->to_probe = PROBE_PCI;
+ memset(&dev->state, 0, sizeof(dev->state));
+ }
+ if (dev->to_probe == PROBE_PCI) {
+#ifdef CONFIG_PCI
+ dev->how_probe = pci_probe(dev, type_name);
+#else
+ dev->how_probe = PROBE_FAILED;
+#endif
+ if (dev->how_probe == PROBE_FAILED) {
+ dev->to_probe = PROBE_ISA;
+ }
+ }
+ if (dev->to_probe == PROBE_ISA) {
+#ifdef CONFIG_ISA
+ dev->how_probe = isa_probe(dev, type_name);
+#else
+ dev->how_probe = PROBE_FAILED;
+#endif
+ if (dev->how_probe == PROBE_FAILED) {
+ dev->to_probe = PROBE_NONE;
+ }
+ }
+ if ((dev->to_probe != PROBE_PCI) &&
+ (dev->to_probe != PROBE_ISA)) {
+ dev->how_probe = PROBE_FAILED;
+
+ }
+ return dev->how_probe;
+}
+
+void disable(struct dev *dev)
+{
+ if (dev->disable) {
+ dev->disable(dev);
+ dev->disable = 0;
+ }
+}
diff --git a/src/core/disk.c b/src/core/disk.c
new file mode 100644
index 00000000..2d31b627
--- /dev/null
+++ b/src/core/disk.c
@@ -0,0 +1,283 @@
+#include "etherboot.h"
+#include "disk.h"
+
+#undef disk_disable
+
+static int dummy(void *unused __unused)
+{
+ return (0);
+}
+
+static unsigned char disk_buffer[DISK_BUFFER_SIZE];
+struct disk disk =
+{
+ {
+ 0, /* dev.disable */
+ {
+ 0,
+ 0,
+ PCI_BUS_TYPE,
+ }, /* dev.devid */
+ 0, /* index */
+ 0, /* type */
+ PROBE_FIRST, /* how_probe */
+ PROBE_NONE, /* to_probe */
+ 0, /* failsafe */
+ 0, /* type_index */
+ {}, /* state */
+ },
+ (int (*)(struct disk *, sector_t ))dummy, /* read */
+ 0 - 1, /* drive */
+ 0, /* hw_sector_size */
+ 0, /* sectors_per_read */
+ 0, /* bytes */
+ 0, /* sectors */
+ 0, /* sector */
+ disk_buffer, /* buffer */
+ 0, /* priv */
+
+ 0, /* disk_offset */
+ 0, /* direction */
+};
+
+
+static int disk_read(
+ struct disk *disk, unsigned char *buffer, sector_t sector)
+{
+ int result;
+ sector_t base_sector;
+
+ /* Note: I do not handle disk wrap around here! */
+
+ /* Compute the start of the track cache */
+ base_sector = sector;
+ /* Support sectors_per_read > 1 only on small disks */
+ if ((sizeof(sector_t) > sizeof(unsigned long)) &&
+ (disk->sectors_per_read > 1)) {
+ unsigned long offset;
+ offset = ((unsigned long)sector) % disk->sectors_per_read;
+ base_sector -= offset;
+ }
+
+ /* See if I need to update the track cache */
+ if ((sector < disk->sector) ||
+ sector >= disk->sector + (disk->bytes >> 9)) {
+ twiddle();
+ result = disk->read(disk, base_sector);
+ if (result < 0)
+ return result;
+ }
+ /* Service the request from the track cache */
+ memcpy(buffer, disk->buffer + ((sector - base_sector)<<9), SECTOR_SIZE);
+ return 0;
+}
+
+static int disk_read_sectors(
+ struct disk *disk,
+ unsigned char *buffer,
+ sector_t base_sector, unsigned int sectors)
+{
+ sector_t sector = 0;
+ unsigned long offset;
+ int result = 0;
+
+ for(offset = 0; offset < sectors; offset++) {
+ sector = base_sector + offset;
+ if (sector >= disk->sectors) {
+ sector -= disk->sectors;
+ }
+ result = disk_read(disk, buffer + (offset << 9), sector);
+ if (result < 0)
+ break;
+ }
+ if (result < 0) {
+ printf("disk read error at 0x%lx\n", sector);
+ }
+ return result;
+}
+
+static os_download_t probe_buffer(unsigned char *buffer, unsigned int len,
+ int increment, unsigned int offset, unsigned int *roffset)
+{
+ os_download_t os_download;
+ unsigned int end;
+ end = 0;
+ os_download = 0;
+ if (increment > 0) {
+ end = len - SECTOR_SIZE;
+ }
+ do {
+ offset += increment;
+ os_download = probe_image(buffer + offset, len - offset);
+ } while(!os_download && (offset != end));
+ *roffset = offset;
+ return os_download;
+}
+
+static int load_image(
+ struct disk *disk,
+ unsigned char *buffer, unsigned int buf_sectors,
+ sector_t block, unsigned int offset,
+ os_download_t os_download)
+{
+ sector_t skip_sectors;
+
+ skip_sectors = 0;
+ while(1) {
+ skip_sectors = os_download(buffer + offset,
+ (buf_sectors << 9) - offset, 0);
+
+ block += skip_sectors + buf_sectors;
+ if (block >= disk->sectors) {
+ block -= disk->sectors;
+ }
+
+ offset = 0;
+ buf_sectors = 1;
+ if (disk_read_sectors(disk, buffer, block, 1) < 0) {
+ return 0;
+ }
+ }
+ return -1;
+}
+
+int disk_probe(struct dev *dev)
+{
+ struct disk *disk = (struct disk *)dev;
+ if (dev->how_probe == PROBE_NEXT) {
+ disk->drive += 1;
+ }
+ return probe(dev);
+}
+
+
+int disk_load_configuration(struct dev *dev)
+{
+ /* Start with the very simplest possible disk configuration */
+ struct disk *disk = (struct disk *)dev;
+ disk->direction = (dev->failsafe)?-1:1;
+ disk->disk_offset = 0;
+ return 0;
+}
+
+int disk_load(struct dev *dev)
+{
+ struct disk *disk = (struct disk *)dev;
+ /* 16K == 8K in either direction from the start of the disk */
+ static unsigned char buffer[32*SECTOR_SIZE];
+ os_download_t os_download;
+ unsigned int offset;
+ unsigned int len;
+ unsigned int buf_sectors;
+ volatile sector_t block;
+ volatile int inc, increment;
+ int i;
+ int result;
+ jmp_buf real_restart;
+
+
+ printf("Searching for image...\n");
+ result = 0;
+ /* Only check for 16byte aligned images */
+ increment = (disk->direction < 0)?-16:16;
+ /* Load a buffer, and see if it contains the start of an image
+ * we can boot from disk.
+ */
+ len = sizeof(buffer);
+ buf_sectors = sizeof(buffer) / SECTOR_SIZE;
+ inc = increment;
+ block = (disk->disk_offset) >> 9;
+ if (buf_sectors/2 > block) {
+ block = (disk->sectors - (buf_sectors/2)) + block;
+ }
+ /* let probe buffer assume offset always needs to be incremented */
+ offset = (len/2 + ((disk->disk_offset) & 0x1ff)) - inc;
+
+ /* Catch longjmp so if this image fails to load, I start looking
+ * for the next image where I left off looking for this image.
+ */
+ memcpy(&real_restart, &restart_etherboot, sizeof(jmp_buf));
+ i = setjmp(restart_etherboot);
+ if ((i != 0) && (i != -2)) {
+ memcpy(&restart_etherboot, &real_restart, sizeof(jmp_buf));
+ longjmp(restart_etherboot, i);
+ }
+ /* Read the canidate sectors into the buffer */
+ if (disk_read_sectors(disk, buffer, block, buf_sectors) < 0) {
+ result = -1;
+ goto out;
+ }
+ if (inc == increment) {
+ os_download = probe_buffer(buffer, len, inc, offset, &offset);
+ if (os_download)
+ goto load_image;
+ inc = -inc;
+ }
+ os_download = probe_buffer(buffer, len, inc, offset, &offset);
+ if (!os_download) {
+ result = -1;
+ goto out;
+ }
+ load_image:
+ printf("Loading image...\n");
+ result = load_image(disk, buffer, buf_sectors, block, offset, os_download);
+ out:
+ memcpy(&restart_etherboot, &real_restart, sizeof(jmp_buf));
+ return result;
+}
+
+int url_file(const char *name,
+ int (*fnc)(unsigned char *, unsigned int, unsigned int, int) __unused)
+{
+ unsigned int drive;
+ unsigned long disk_offset;
+ int direction;
+ int type;
+
+ disk_offset = 0;
+ direction = 1;
+ if (memcmp(name, "disk", 4) == 0) {
+ type = DISK_DRIVER;
+ name += 4;
+ }
+ else if (memcmp(name, "floppy", 6) == 0) {
+ type = FLOPPY_DRIVER;
+ name += 6;
+ }
+ else {
+ printf("Unknown device type\n");
+ return 0;
+ }
+ drive = strtoul(name, &name, 10);
+ if ((name[0] == '+') || (name[0] == '-')) {
+ direction = (name[0] == '-')? -1 : 1;
+ name++;
+ disk_offset = strtoul(name, &name, 10);
+ }
+ if (name[0]) {
+ printf("Junk '%s' at end of disk url\n", name);
+ return 0;
+ }
+ memset(&disk, 0, sizeof(disk));
+ disk.buffer = disk_buffer;
+ disk.drive = 0;
+ disk.dev.how_probe = PROBE_FIRST;
+ disk.dev.type = type;
+ do {
+ disk_disable();
+ disk.dev.how_probe = disk_probe(&disk.dev);
+ if (disk.dev.how_probe == PROBE_FAILED) {
+ printf("Not that many drives\n");
+ return 0;
+ }
+ } while(disk.drive < drive);
+ disk.direction = direction;
+ disk.disk_offset = disk_offset;
+
+ return disk_load(&disk.dev);
+}
+
+void disk_disable(void)
+{
+ disable(&disk.dev);
+}
diff --git a/src/core/dns_resolver.c b/src/core/dns_resolver.c
new file mode 100644
index 00000000..52f24a4d
--- /dev/null
+++ b/src/core/dns_resolver.c
@@ -0,0 +1,419 @@
+/**************************************************************************
+*
+* dns_resolver.c: Etherboot support for resolution of host/domain
+* names in filename parameters
+* Written 2004 by Anselm M. Hoffmeister
+* <stockholm@users.sourceforge.net>
+*
+* 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 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* This code is using nuts and bolts from throughout etherboot.
+* It is a fresh implementation according to the DNS RFC, #1035
+*
+* $Revision$
+* $Author$
+* $Date$
+*
+* REVISION HISTORY:
+* ================
+* 2004-05-10 File created
+* 2004-05-19 First release to CVS
+* 2004-05-22 CNAME support first stage finished
+* 2004-05-24 First "stable" release to CVS
+* 2004-08-28 Improve readability, set recursion flag
+***************************************************************************/
+
+#ifdef DNS_RESOLVER
+#include "etherboot.h"
+#include "nic.h"
+#include "dns_resolver.h"
+
+#define MAX_DNS_RETRIES 3
+#define MAX_CNAME_RECURSION 0x30
+#undef DNSDEBUG
+
+int donameresolution ( char * hostname, int hnlength, char * deststring );
+
+/*
+ * dns_resolver
+ * Function: Main function for name resolution - will be called by other
+ * parts of etherboot
+ * Param: string filename (not containing proto prefix like "tftp://")
+ * Return: string filename, with hostname replaced by IP in dotted quad
+ * or NULL for resolver error
+ * In case no substitution is necessary, return will be = param
+ * The returned string, if not == input, will be temporary and
+ * probably be overwritten during the next call to dns_resolver
+ * The returned string may (or may not) only contain an IP
+ * address, or an IP followed by a ":" or "/", or be NULL(=error)
+ */
+char * dns_resolver ( char * filename ) {
+ int i = 0, j, k;
+ static char ipaddr[16] = { 0 };
+ // Search for "end of hostname" (which might be either ":" or "/")
+ for ( j = i; (filename[j] != ':') && (filename[j] != '/'); ++j ) {
+ // If no hostname delimiter was found, assume no name present
+ if ( filename[j] == 0 ) return filename;
+ }
+ // Check if the filename is an IP, in which case, leave unchanged
+ k = j - i - 1;
+ while ( ( '.' == filename[i+k] ) ||
+ ( ( '0' <= filename[i+k] ) && ( '9' >= filename[i+k] ) ) ) {
+ --k;
+ if ( k < 0 ) return filename; // Only had nums and dots->IP
+ }
+ // Now that we know it's a full hostname, attempt to resolve
+ if ( donameresolution ( filename + i, j - i, ipaddr ) ) {
+ return NULL; // Error in resolving - Fatal.
+ }
+ // Return the dotted-quad IP which resulted
+ return ipaddr;
+}
+
+/*
+ * await_dns
+ * Shall be called on any incoming packet during the resolution process
+ * (as is the case with all the other await_ functions in etherboot)
+ * Param: as any await functions
+ * Return: see dns_resolver.h for constant return values + descriptions
+ */
+static int await_dns (int ival, void *ptr,
+ unsigned short ptype __unused, struct iphdr *ip __unused,
+ struct udphdr *udp, struct tcphdr *tcp __unused) {
+ int i, j, k;
+ unsigned char *p = (unsigned char *)udp + sizeof(struct udphdr);
+ // p is set to the beginning of the payload
+ unsigned char *q;
+ unsigned int querytype = QUERYTYPE_A;
+ if ( 0 == udp ) // Parser couldn't find UDP header
+ return RET_PACK_GARBAG; // Not a UDP packet
+ if (( UDP_PORT_DNS != ntohs (udp->src )) ||
+ ( UDP_PORT_DNS != ntohs (udp->dest)) )
+ // Neither source nor destination port is "53"
+ return RET_PACK_GARBAG; // UDP port wrong
+ if (( p[QINDEX_ID ] != 0) ||
+ ( p[QINDEX_ID+1] != (ival & 0xff)))
+ // Checking if this packet has set (inside payload)
+ // the sequence identifier that we expect
+ return RET_PACK_GARBAG; // Not matching our request ID
+ if (( p[QINDEX_FLAGS ] & QUERYFLAGS_MASK ) != QUERYFLAGS_WANT )
+ // We only accept responses to the query(ies) we sent
+ return RET_PACK_GARBAG; // Is not response=opcode <0>
+ querytype = (ival & 0xff00) >> 8;
+ if (((p[QINDEX_NUMANSW+1] + (p[QINDEX_NUMANSW+1]<<8)) == 0 ) ||
+ ( ERR_NOSUCHNAME == (p[QINDEX_FLAGS+1] & 0x0f) ) ) {
+ // Answer section has 0 entries, or "no such name" returned
+ if ( QUERYTYPE_A == querytype) {
+ // It was an A type query, so we should try if there's
+ // an alternative "CNAME" record available
+ return RET_RUN_CNAME_Q; // So try CNAME query next
+ } else if ( QUERYTYPE_CNAME == querytype) {
+ // There's no CNAME either, so give up
+ return RET_NOSUCHNAME;
+ } else {
+ // Anything else? No idea what. Should not happen.
+ return RET_NOSUCHNAME; // Bail out with error
+ }
+ }
+ if ( 0 != ( p[QINDEX_FLAGS+1] & 0x0f ) )
+ // The response packet's flag section tells us:
+ return RET_NOSUCHNAME; // Another (unspecific) error occured
+ // Now we have an answer packet in response to our query. Next thing
+ // to do is to search the payload for the "answer section", as there is
+ // a question section first usually that needs to be skipped
+ // If question section was not repeated, that saves a lot of work :
+ if ( 0 >= (i = ((p[QINDEX_NUMQUEST] << 8) + p[QINDEX_NUMQUEST+1]))) {
+ q = p+ QINDEX_QUESTION; // No question section, just continue;
+ } else if ( i >= 2 ) { // More than one query section? Error!
+ return RET_NOSUCHNAME; // That's invalid for us anyway -
+ // We only place one query at a time
+ } else {
+ // We have to skip through the question section first to
+ // find the beginning of the answer section
+ q = p + QINDEX_QUESTION;
+ while ( 0 != q[0] )
+ q += q[0] + 1; // Skip through
+ q += 5; // Skip over end-\0 and query type section
+ }
+ // Now our pointer shows the beginning of the answer section
+ // So now move it past the (repeated) query string, we only
+ // want the answer
+ while ( 0 != q[0] ) {
+ if ( 0xc0 == ( q[0] & 0xc0 ) ) { // Pointer
+ ++q;
+ break;
+ }
+ q += q[0] + 1;
+ }
+ ++q;
+ // Now check wether it's an INET host address (resp. CNAME)?
+ // There seem to be nameservers out there (Bind 9, for example),
+ // that return CNAMEs when no As are there, e.g. in
+ // testname.test. IN CNAME othername.test.
+ // case, bind 9 would return the CNAME record on an A query.
+ // Accept this case as it saves a lot of work (an extra query)
+ if (( QUERYTYPE_CNAME == q[1] ) &&
+ ( QUERYTYPE_A == querytype )) {
+ // A query, but CNAME response.
+ // Do simulate having done a CNAME query now and just assume
+ // the answer we are working on here is that of a CNAME query
+ // This works for single-depth CNAMEs fine.
+ // For deeper recursion, we won't parse the answer
+ // packet deeper but rely on a separate CNAME query
+ querytype = QUERYTYPE_CNAME;
+ }
+ // Now check wether the answer packet is of the expected type
+ // (remember, we just tweaked CNAME answers to A queries already)
+ if ( (q[0] != 0) ||
+ (q[1] != querytype) || // query type matches?
+ (q[2] != ((QUERYCLASS_INET & 0xff00) >> 8)) ||
+ (q[3] != (QUERYCLASS_INET & 0x00ff))) { // class IN response?
+ return RET_DNSERROR; // Should not happen. DNS server bad?
+ }
+ q += 8; // skip querytype/-class/ttl which are uninteresting now
+ if ( querytype == QUERYTYPE_A ) {
+ // So what we sent was an A query - expect an IPv4 address
+ // Check if datalength looks satisfactory
+ if ( ( 0 != q[0] ) || ( 4 != q[1] ) ) {
+ // Data length is not 4 bytes
+ return RET_DNSERROR;
+ }
+ // Go to the IP address and copy it to the response buffer
+ p = ptr + QINDEX_STORE_A;
+ for ( i = 0; i < 4; ++i ) {
+ p[i] = q[i+2];
+ }
+ return RET_GOT_ADDR; // OK! Address RETURNED! VERY FINE!
+ } else if ( querytype == QUERYTYPE_CNAME ) { // CNAME QUERY
+ // The pointer "q" now stands on the "payload length" of the
+ // CNAME data [2 byte field]. We will have to check wether this
+ // name is referenced in a following response, which would save
+ // us further queries, or if it is standalone, which calls for
+ // making a separate query
+ // This statement above probably needs clarification. To help
+ // the reader understand what's going on, imagine the DNS
+ // answer to be 4-section: query(repeating what we sent to the
+ // DNS server), answer(like the CNAME record we wanted),
+ // additional (which might hold the A for the CNAME - esp.
+ // Bind9 seems to provide us with this info when we didn't
+ // query for it yet) and authoritative (the DNS server's info
+ // on who is responsible for that data). For compression's
+ // sake, instead of specifying the full hostname string all
+ // the time, one can instead specify something like "same as on
+ // payload offset 0x123" - if this happens here, we can check
+ // if that payload offset given here by coincidence is that of
+ // an additional "A" record which saves us sending a separate
+ // query.
+ // We will look if there's a/ two or more answer sections
+ // AND b/ the next is a reference to us.
+ p = (unsigned char *)udp + sizeof(struct udphdr);
+ i = q + 2 - p;
+ if ( p[7] > 1 ) { // More than one answer section
+ // Check the next if it's a ref
+ // For that, we have to locate the next. Do this with "q".
+ p = q + (q[0] * 0x100) + q[1] + 2;
+ if ( (p[0] == (0xc0 + ((i & 0xf00)/256))) &&
+ (p[1] == (i & 0xff) ) &&
+ (p[2] == ((QUERYTYPE_A & 0xff00) >> 8)) &&
+ (p[3] == (QUERYTYPE_A & 0x00ff)) &&
+ (p[4] == ((QUERYCLASS_INET & 0xff00) >> 8 )) &&
+ (p[5] == (QUERYCLASS_INET & 0x00ff)) &&
+ (p[10]== 0) && // Data length expected
+ (p[11]== 4)) { // to be <4> (IP-addr)
+ // Behind that sections: TTL, data length(4)
+ for ( i = 0; i < 4; ++i ) {
+ ((unsigned char*)ptr)[QINDEX_STORE_A+i] =
+ p[12+i];
+ }
+ return RET_GOT_ADDR;
+ }
+ }
+ // Reference not found, next query (A) needed (because CNAME
+ // queries usually return another hostname, not an IP address
+ // as we need it - that's the point in CNAME, anyway :-)
+ p = (unsigned char *)udp + sizeof(struct udphdr);
+#ifdef DNSDEBUG
+ printf ( " ->[");
+#endif
+ k = QINDEX_QUESTION;
+ i = (q-p) + 2;
+ // Compose the hostname that needs to be queried for
+ // This looks complicated (and is a little bit) because
+ // we might not be able to copy verbatim, but need to
+ // check wether the CNAME looks like
+ // "servername" "plus what offset 0x123 of the payload says"
+ // (this saves transfer bandwidth, which is why DNS allows
+ // this, but it makes programmers' lives more difficult)
+ while (p[i] != 0) {
+ if ( (((unsigned char *)p)[i] & 0xc0) != 0 ) {
+ i = ((p[i] & 0x3f) * 0x100) + p[i+1];
+ continue;
+ }
+ ((unsigned char *)ptr)[k] = p[i];
+ for ( j = i + 1; j <= i + p[i]; ++j ) {
+ ((unsigned char *)ptr)[k+j-i] = p[j];
+#ifdef DNSDEBUG
+ printf ( "%c", p[j] );
+#endif
+ }
+ k += ((unsigned char *)ptr)[k] + 1;
+ i += p[i] + 1;
+#ifdef DNSDEBUG
+ printf ( (p[i] ? ".": "") );
+#endif
+ }
+ ((unsigned char *)ptr)[k] = 0;
+#ifdef DNSDEBUG
+ printf ( "].." );
+#endif
+ // So we need to run another query, this time try to
+ // get an "A" record for the hostname that the last CNAME
+ // query pointed to
+ return RET_RUN_NEXT_A;
+ }
+ // We only accept CNAME or A replies from the nameserver, every-
+ // thing else is of no use for us.
+ // Must be an invalid packet that the nameserver sent.
+ return RET_DNSERROR;
+}
+
+int chars_to_next_dot ( char * countfrom, int maxnum ) {
+ // Count the number of characters of this part of a hostname
+ int i;
+ for ( i = 1; i < maxnum; ++i ) {
+ if ( countfrom[i] == '.' ) return i;
+ }
+ return maxnum;
+}
+
+/*
+ * donameresolution
+ * Function: Compose the initial query packet, handle answers until
+ * a/ an IP address is retrieved
+ * b/ too many CNAME references occured (max. MAX_DNS_RETRIES)
+ * c/ No matching record for A or CNAME can be found
+ * Param: string hostname, length (hostname needs no \0-end-marker),
+ * string to which dotted-quad-IP shall be written
+ * Return: 0 for success, >0 for failure
+ */
+int donameresolution ( char * hostname, int hnlength, char * deststring ) {
+ unsigned char querybuf[260+sizeof(struct iphdr)+sizeof(struct udphdr)];
+ // 256 for the DNS query payload, +4 for the result temporary
+ unsigned char *query = &querybuf[sizeof(struct iphdr)+sizeof(struct udphdr)];
+ // Pointer to the payload
+ int i, h = hnlength;
+ long timeout;
+ int retry, recursion;
+ // Setup the query data
+ query[QINDEX_ID ] = (QUERYIDENTIFIER & 0xff00) >> 8;
+ query[QINDEX_ID+1] = QUERYIDENTIFIER & 0xff;
+ query[QINDEX_FLAGS ] = (QUERYFLAGS & 0xff00) >> 8;
+ query[QINDEX_FLAGS+1] = QUERYFLAGS & 0xff;
+ query[QINDEX_NUMQUEST ]= 0;
+ query[QINDEX_NUMQUEST+1]= 1; // 1 standard query to be sent
+ query[QINDEX_NUMANSW ] = 0;
+ query[QINDEX_NUMANSW+1] = 0;
+ query[QINDEX_NUMAUTH ] = 0;
+ query[QINDEX_NUMAUTH+1] = 0;
+ query[QINDEX_NUMADDIT ]= 0;
+ query[QINDEX_NUMADDIT+1]= 0;
+ query[QINDEX_QUESTION] = chars_to_next_dot(hostname,h);
+ if ( h > 236 ) return 1; // Hostnames longer than 236 chars are refused.
+ // This is an arbitrary decision, SHOULD check
+ // what the RFCs say about that
+ for ( i = 0; i < h; ++i ) {
+ // Compose the query section's hostname - replacing dots (and
+ // preceding the string) with one-byte substring-length values
+ // (for the immediately following substring) - \0 terminated
+ query[QINDEX_QUESTION+i+1] = hostname[i];
+ if ( hostname[i] == '.' )
+ query[QINDEX_QUESTION+i+1] = chars_to_next_dot(hostname + i + 1, h - i - 1);
+ }
+ query[QINDEX_QTYPE+h ] = (QUERYTYPE_A & 0xff00) >> 8;
+ query[QINDEX_QTYPE+h+1] = QUERYTYPE_A & 0xff;
+ // First try an A query, if that
+ // won't work, try CNAME
+ printf ( "Resolving hostname [" );
+ for ( i = 0; i < hnlength; ++i ) { printf ( "%c", hostname[i] ); }
+ printf ("]" );
+ for ( recursion = MAX_CNAME_RECURSION; recursion > 0; --recursion ) {
+ printf ( ".." );
+ // Try MAX_CNAME_RECURSION CNAME queries maximally, if then no
+ // A record is found, assume the hostname to not be resolvable
+ query[QINDEX_QUESTION+h+1] = 0; // Marks the end of
+ // the query string
+ query[QINDEX_QCLASS+h ]= (QUERYCLASS_INET & 0xff00) >> 8;
+ query[QINDEX_QCLASS+h+1]= QUERYCLASS_INET & 0xff;
+ rx_qdrain(); // Clear NIC packet buffer -
+ // there won't be anything of interest *now*.
+ udp_transmit ( arptable[ARP_NAMESERVER].ipaddr.s_addr,
+ UDP_PORT_DNS, UDP_PORT_DNS,
+ hnlength + 18 + sizeof(struct iphdr) +
+ sizeof(struct udphdr), querybuf );
+ // If no answer comes in in a certain period of time, retry
+ for (retry = 1; retry <= MAX_DNS_RETRIES; retry++) {
+ timeout = rfc2131_sleep_interval(TIMEOUT, retry);
+ i = await_reply ( await_dns,
+ (query[QINDEX_QTYPE+h+1] << 8) +
+ ((unsigned char *)query)[QINDEX_ID+1],
+ query, timeout);
+ // The parameters given are
+ // bits 15...8 of integer = Query type (low bits)
+ // bits 7...0 of integer = Query index (low bits)
+ // query + QINDEX_STORE_A points to the place
+ // where A records shall be stored (4 bytes);
+ // query + 12(QINDEX_QUESTION) points to where
+ // a CNAME should go in case one is received
+ if (i) break;
+ }
+ switch ( i ) {
+ case RET_GOT_ADDR: // Address successfully retrieved
+ sprintf( deststring, "%@",
+ (long)query[QINDEX_STORE_A ] +
+ ( (long)query[QINDEX_STORE_A+1] * 256 ) +
+ ( (long)query[QINDEX_STORE_A+2] * 65536 )+
+ ( (long)query[QINDEX_STORE_A+3] * 16777216 ));
+ printf ( " -> IP [%s]\n", deststring );
+ return RET_DNS_OK;
+ case RET_RUN_CNAME_Q: // No A record found, try CNAME
+ query[QINDEX_QTYPE+h ]=(QUERYTYPE_CNAME & 0xff00)>> 8;
+ query[QINDEX_QTYPE+h+1]= QUERYTYPE_CNAME & 0xff;
+ break;
+ case RET_RUN_NEXT_A:
+ // Found a CNAME, now try A for the name it pointed to
+ for ( i = 0; query[QINDEX_QUESTION+i] != 0;
+ i += query[QINDEX_QUESTION+i] + 1 ) {;}
+ h = i - 1;
+ query[QINDEX_QTYPE+h ]=(QUERYTYPE_A & 0xff00)>> 8;
+ query[QINDEX_QTYPE+h+1]= QUERYTYPE_A & 0xff;
+ break;
+ case RET_CNAME_FAIL:
+ // Neither A nor CNAME gave a usable result
+ printf ("Host name cannot be resolved\n");
+ return RET_DNS_FAIL;
+ case RET_NOSUCHNAME:
+ default:
+ printf ( "Name resolution failed\n" );
+ return RET_DNS_FAIL;
+ }
+ query[QINDEX_ID ] = 0; // We will probably never have more
+ // than 256 queries in one run
+ query[QINDEX_ID+1]++;
+ }
+ // To deep recursion
+ printf ( "CNAME recursion to deep - abort name resolver\n" );
+ return RET_DNS_FAIL;
+}
+#endif /* DNS_RESOLVER */
diff --git a/src/core/elf_loader.c b/src/core/elf_loader.c
new file mode 100644
index 00000000..c1906d8a
--- /dev/null
+++ b/src/core/elf_loader.c
@@ -0,0 +1,658 @@
+#include "elf.h"
+
+#ifndef ELF_CHECK_ARCH
+#error ELF_CHECK_ARCH not defined
+#endif
+
+#define ELF_NOTES 1
+#define ELF_DEBUG 0
+
+struct elf_state
+{
+ union {
+ Elf32_Ehdr elf32;
+ Elf64_Ehdr elf64;
+ } e;
+ union {
+ Elf32_Phdr phdr32[1];
+ Elf64_Phdr phdr64[1];
+ unsigned char dummy[1024];
+ } p;
+ unsigned long curaddr;
+ int segment; /* current segment number, -1 for none */
+ uint64_t loc; /* start offset of current block */
+ uint64_t skip; /* padding to be skipped to current segment */
+ unsigned long toread; /* remaining data to be read in the segment */
+#if ELF_NOTES
+ int check_ip_checksum;
+ uint16_t ip_checksum;
+ unsigned long ip_checksum_offset;
+#endif
+};
+
+static struct elf_state estate;
+
+static void elf_boot(unsigned long machine, unsigned long entry)
+{
+ int result;
+ struct Elf_Bhdr *hdr;
+ multiboot_boot(entry);
+ /* We cleanup unconditionally, and then reawaken the network
+ * adapter after the longjmp.
+ */
+ hdr = prepare_boot_params(&estate.e);
+ result = elf_start(machine, entry, virt_to_phys(hdr));
+ if (result == 0) {
+ result = -1;
+ }
+ printf("Secondary program returned %d\n", result);
+ longjmp(restart_etherboot, result);
+}
+
+#if ELF_NOTES
+static int elf_prep_segment(
+ unsigned long start __unused, unsigned long mid __unused, unsigned long end __unused,
+ unsigned long istart, unsigned long iend)
+
+{
+ if (estate.check_ip_checksum) {
+ if ((istart <= estate.ip_checksum_offset) &&
+ (iend > estate.ip_checksum_offset)) {
+ /* The checksum note is also loaded in a
+ * PT_LOAD segment, so the computed checksum
+ * should be 0.
+ */
+ estate.ip_checksum = 0;
+ }
+ }
+ return 1;
+}
+#else
+#define elf_prep_segment(start, mid, end, istart, iend) (1)
+#endif
+
+
+#if ELF_NOTES
+static void process_elf_notes(unsigned char *header,
+ unsigned long offset, unsigned long length)
+{
+ unsigned char *note, *end;
+ char *program, *version;
+
+ estate.check_ip_checksum = 0;
+ note = header + offset;
+ end = note + length;
+ program = version = 0;
+ while(note < end) {
+ Elf_Nhdr *hdr;
+ 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 ((hdr->n_namesz == sizeof(ELF_NOTE_BOOT)) &&
+ (memcmp(n_name, ELF_NOTE_BOOT, sizeof(ELF_NOTE_BOOT)) == 0)) {
+ switch(hdr->n_type) {
+ case EIN_PROGRAM_NAME:
+ if (n_desc[hdr->n_descsz -1] == 0) {
+ program = n_desc;
+ }
+ break;
+ case EIN_PROGRAM_VERSION:
+ if (n_desc[hdr->n_descsz -1] == 0) {
+ version = n_desc;
+ }
+ break;
+ case EIN_PROGRAM_CHECKSUM:
+ estate.check_ip_checksum = 1;
+ estate.ip_checksum = *((uint16_t *)n_desc);
+ /* Remember where the segment is so
+ * I can detect segment overlaps.
+ */
+ estate.ip_checksum_offset = n_desc - header;
+#if ELF_DEBUG
+ printf("Checksum: %hx\n", estate.ip_checksum);
+#endif
+
+ break;
+ }
+ }
+#if ELF_DEBUG
+ printf("n_type: %x n_name(%d): %s n_desc(%d): %s\n",
+ hdr->n_type,
+ hdr->n_namesz, n_name,
+ hdr->n_descsz, n_desc);
+#endif
+ note = next;
+ }
+ if (program && version) {
+ printf("\nLoading %s version: %s\n", program, version);
+ }
+}
+#endif
+
+#ifdef ELF_IMAGE
+static sector_t elf32_download(unsigned char *data, unsigned int len, int eof);
+static inline os_download_t elf32_probe(unsigned char *data, unsigned int len)
+{
+ unsigned long phdr_size;
+ if (len < sizeof(estate.e.elf32)) {
+ return 0;
+ }
+ memcpy(&estate.e.elf32, data, sizeof(estate.e.elf32));
+ if ((estate.e.elf32.e_ident[EI_MAG0] != ELFMAG0) ||
+ (estate.e.elf32.e_ident[EI_MAG1] != ELFMAG1) ||
+ (estate.e.elf32.e_ident[EI_MAG2] != ELFMAG2) ||
+ (estate.e.elf32.e_ident[EI_MAG3] != ELFMAG3) ||
+ (estate.e.elf32.e_ident[EI_CLASS] != ELFCLASS32) ||
+ (estate.e.elf32.e_ident[EI_DATA] != ELFDATA_CURRENT) ||
+ (estate.e.elf32.e_ident[EI_VERSION] != EV_CURRENT) ||
+ ( (estate.e.elf32.e_type != ET_EXEC) &&
+ (estate.e.elf32.e_type != ET_DYN)) ||
+ (estate.e.elf32.e_version != EV_CURRENT) ||
+ (estate.e.elf32.e_ehsize != sizeof(Elf32_Ehdr)) ||
+ (estate.e.elf32.e_phentsize != sizeof(Elf32_Phdr)) ||
+ !ELF_CHECK_ARCH(estate.e.elf32)) {
+ return 0;
+ }
+ printf("(ELF");
+ elf_freebsd_probe();
+ multiboot_probe(data, len);
+ printf(")... ");
+ phdr_size = estate.e.elf32.e_phnum * estate.e.elf32.e_phentsize;
+ if (estate.e.elf32.e_phoff + phdr_size > len) {
+ printf("ELF header outside first block\n");
+ return dead_download;
+ }
+ if (phdr_size > sizeof(estate.p.dummy)) {
+ printf("Program header to big\n");
+ return dead_download;
+ }
+ memcpy(&estate.p.phdr32, data + estate.e.elf32.e_phoff, phdr_size);
+ if (estate.e.elf32.e_type == ET_DYN) {
+ Elf32_Addr min, max, base_addr, delta, align;
+ min = -1;
+ max = 0;
+ align = 1;
+ for(estate.segment = 0; estate.segment < estate.e.elf32.e_phnum; estate.segment++) {
+ Elf32_Addr val;
+ if (estate.p.phdr32[estate.segment].p_type != PT_LOAD)
+ continue;
+ val = estate.p.phdr32[estate.segment].p_paddr;
+ if (val < min) {
+ min = val;
+ }
+ val += estate.p.phdr32[estate.segment].p_memsz;
+ if (val > max) {
+ max = val;
+ }
+ if (estate.p.phdr32[estate.segment].p_align > align) {
+ align = estate.p.phdr32[estate.segment].p_align;
+ }
+ }
+ if (align & (align -1)) {
+ printf("ELF base address alignment is not a power of 2\n");
+ return dead_download;
+ }
+ base_addr = find_segment(max - min, align);
+ if (base_addr == ULONG_MAX) {
+ printf("ELF base address not available for size %ld\n", max - min);
+ return dead_download;
+ }
+ /* Compute the change in base address and fix up the addresses */
+ delta = base_addr - min;
+ for(estate.segment = 0; estate.segment < estate.e.elf32.e_phnum; estate.segment++) {
+ /* Change the base address of the object to load */
+ estate.p.phdr32[estate.segment].p_paddr += delta;
+ }
+ estate.e.elf32.e_entry += delta;
+ }
+#if ELF_NOTES
+ /* Load ELF notes from the image */
+ for(estate.segment = 0; estate.segment < estate.e.elf32.e_phnum; estate.segment++) {
+ if (estate.p.phdr32[estate.segment].p_type != PT_NOTE)
+ continue;
+ if (estate.p.phdr32[estate.segment].p_offset + estate.p.phdr32[estate.segment].p_filesz > len) {
+ /* Ignore ELF notes outside of the first block */
+ continue;
+ }
+ process_elf_notes(data,
+ estate.p.phdr32[estate.segment].p_offset, estate.p.phdr32[estate.segment].p_filesz);
+ }
+#endif
+ /* Check for Etherboot related limitations. Memory
+ * between _text and _end is not allowed.
+ * Reasons: the Etherboot code/data area.
+ */
+ for (estate.segment = 0; estate.segment < estate.e.elf32.e_phnum; estate.segment++) {
+ unsigned long start, mid, end, istart, iend;
+ if (estate.p.phdr32[estate.segment].p_type != PT_LOAD)
+ continue;
+
+ elf_freebsd_fixup_segment();
+
+ start = estate.p.phdr32[estate.segment].p_paddr;
+ mid = start + estate.p.phdr32[estate.segment].p_filesz;
+ end = start + estate.p.phdr32[estate.segment].p_memsz;
+ istart = estate.p.phdr32[estate.segment].p_offset;
+ iend = istart + estate.p.phdr32[estate.segment].p_filesz;
+ if (!prep_segment(start, mid, end, istart, iend)) {
+ return dead_download;
+ }
+ if (!elf_prep_segment(start, mid, end, istart, iend)) {
+ return dead_download;
+ }
+ }
+ estate.segment = -1;
+ estate.loc = 0;
+ estate.skip = 0;
+ estate.toread = 0;
+ return elf32_download;
+}
+
+static sector_t elf32_download(unsigned char *data, unsigned int len, int eof)
+{
+ unsigned long skip_sectors = 0;
+ unsigned int offset; /* working offset in the current data block */
+ int i;
+
+ offset = 0;
+ do {
+ if (estate.segment != -1) {
+ if (estate.skip) {
+ if (estate.skip >= len - offset) {
+ estate.skip -= len - offset;
+ break;
+ }
+ offset += estate.skip;
+ estate.skip = 0;
+ }
+
+ if (estate.toread) {
+ unsigned int cplen;
+ cplen = len - offset;
+ if (cplen >= estate.toread) {
+ cplen = estate.toread;
+ }
+ memcpy(phys_to_virt(estate.curaddr), data+offset, cplen);
+ estate.curaddr += cplen;
+ estate.toread -= cplen;
+ offset += cplen;
+ if (estate.toread)
+ break;
+ elf_freebsd_find_segment_end();
+ }
+ }
+
+ /* Data left, but current segment finished - look for the next
+ * segment (in file offset order) that needs to be loaded.
+ * We can only seek forward, so select the program headers,
+ * in the correct order.
+ */
+ estate.segment = -1;
+ for (i = 0; i < estate.e.elf32.e_phnum; i++) {
+ if (estate.p.phdr32[i].p_type != PT_LOAD)
+ continue;
+ if (estate.p.phdr32[i].p_filesz == 0)
+ continue;
+ if (estate.p.phdr32[i].p_offset < estate.loc + offset)
+ continue; /* can't go backwards */
+ if ((estate.segment != -1) &&
+ (estate.p.phdr32[i].p_offset >= estate.p.phdr32[estate.segment].p_offset))
+ continue; /* search minimum file offset */
+ estate.segment = i;
+ }
+ if (estate.segment == -1) {
+ if (elf_freebsd_debug_loader(offset)) {
+ estate.segment = 0; /* -1 makes it not read anymore */
+ continue;
+ }
+ /* No more segments to be loaded, so just start the
+ * kernel. This saves a lot of network bandwidth if
+ * debug info is in the kernel but not loaded. */
+ goto elf_startkernel;
+ break;
+ }
+ estate.curaddr = estate.p.phdr32[estate.segment].p_paddr;
+ estate.skip = estate.p.phdr32[estate.segment].p_offset - (estate.loc + offset);
+ estate.toread = estate.p.phdr32[estate.segment].p_filesz;
+#if ELF_DEBUG
+ printf("PHDR %d, size %#lX, curaddr %#lX\n",
+ estate.segment, estate.toread, estate.curaddr);
+#endif
+ } while (offset < len);
+
+ estate.loc += len + (estate.skip & ~0x1ff);
+ skip_sectors = estate.skip >> 9;
+ estate.skip &= 0x1ff;
+
+ if (eof) {
+ unsigned long entry;
+ unsigned long machine;
+elf_startkernel:
+ entry = estate.e.elf32.e_entry;
+ machine = estate.e.elf32.e_machine;
+
+#if ELF_NOTES
+ if (estate.check_ip_checksum) {
+ unsigned long bytes = 0;
+ uint16_t sum, new_sum;
+
+ sum = ipchksum(&estate.e.elf32, sizeof(estate.e.elf32));
+ bytes = sizeof(estate.e.elf32);
+#if ELF_DEBUG
+ printf("Ehdr: %hx %hx sz: %lx bytes: %lx\n",
+ sum, sum, bytes, bytes);
+#endif
+
+ new_sum = ipchksum(estate.p.phdr32, sizeof(estate.p.phdr32[0]) * estate.e.elf32.e_phnum);
+ sum = add_ipchksums(bytes, sum, new_sum);
+ bytes += sizeof(estate.p.phdr32[0]) * estate.e.elf32.e_phnum;
+#if ELF_DEBUG
+ printf("Phdr: %hx %hx sz: %lx bytes: %lx\n",
+ new_sum, sum,
+ sizeof(estate.p.phdr32[0]) * estate.e.elf32.e_phnum, bytes);
+#endif
+
+ for(i = 0; i < estate.e.elf32.e_phnum; i++) {
+ if (estate.p.phdr32[i].p_type != PT_LOAD)
+ continue;
+ new_sum = ipchksum(phys_to_virt(estate.p.phdr32[i].p_paddr),
+ estate.p.phdr32[i].p_memsz);
+ sum = add_ipchksums(bytes, sum, new_sum);
+ bytes += estate.p.phdr32[i].p_memsz;
+#if ELF_DEBUG
+ printf("seg%d: %hx %hx sz: %x bytes: %lx\n",
+ i, new_sum, sum,
+ estate.p.phdr32[i].p_memsz, bytes);
+#endif
+
+ }
+ if (estate.ip_checksum != sum) {
+ printf("\nImage checksum: %hx != computed checksum: %hx\n",
+ estate.ip_checksum, sum);
+ longjmp(restart_etherboot, -2);
+ }
+ }
+#endif
+ done(1);
+ /* Fixup the offset to the program header so you can find the program headers from
+ * the ELF header mknbi needs this.
+ */
+ estate.e.elf32.e_phoff = (char *)&estate.p - (char *)&estate.e;
+ elf_freebsd_boot(entry);
+ elf_boot(machine,entry);
+ }
+ return skip_sectors;
+}
+#endif /* ELF_IMAGE */
+
+#ifdef ELF64_IMAGE
+static sector_t elf64_download(unsigned char *data, unsigned int len, int eof);
+static inline os_download_t elf64_probe(unsigned char *data, unsigned int len)
+{
+ unsigned long phdr_size;
+ if (len < sizeof(estate.e.elf64)) {
+ return 0;
+ }
+ memcpy(&estate.e.elf64, data, sizeof(estate.e.elf64));
+ if ((estate.e.elf64.e_ident[EI_MAG0] != ELFMAG0) ||
+ (estate.e.elf64.e_ident[EI_MAG1] != ELFMAG1) ||
+ (estate.e.elf64.e_ident[EI_MAG2] != ELFMAG2) ||
+ (estate.e.elf64.e_ident[EI_MAG3] != ELFMAG3) ||
+ (estate.e.elf64.e_ident[EI_CLASS] != ELFCLASS64) ||
+ (estate.e.elf64.e_ident[EI_DATA] != ELFDATA_CURRENT) ||
+ (estate.e.elf64.e_ident[EI_VERSION] != EV_CURRENT) ||
+ ( (estate.e.elf64.e_type != ET_EXEC) &&
+ (estate.e.elf64.e_type != ET_DYN)) ||
+ (estate.e.elf64.e_version != EV_CURRENT) ||
+ (estate.e.elf64.e_ehsize != sizeof(Elf64_Ehdr)) ||
+ (estate.e.elf64.e_phentsize != sizeof(Elf64_Phdr)) ||
+ !ELF_CHECK_ARCH(estate.e.elf64)) {
+ return 0;
+ }
+ printf("(ELF64)... ");
+ phdr_size = estate.e.elf64.e_phnum * estate.e.elf64.e_phentsize;
+ if (estate.e.elf64.e_phoff + phdr_size > len) {
+ printf("ELF header outside first block\n");
+ return dead_download;
+ }
+ if (phdr_size > sizeof(estate.p.dummy)) {
+ printf("Program header to big\n");
+ return dead_download;
+ }
+ if (estate.e.elf64.e_entry > ULONG_MAX) {
+ printf("ELF entry point exceeds address space\n");
+ return dead_download;
+ }
+ memcpy(&estate.p.phdr64, data + estate.e.elf64.e_phoff, phdr_size);
+ if (estate.e.elf64.e_type == ET_DYN) {
+ Elf64_Addr min, max, base_addr, delta, align;
+ min = -1;
+ max = 0;
+ align = 1;
+ for(estate.segment = 0; estate.segment < estate.e.elf64.e_phnum; estate.segment++) {
+ Elf64_Addr val;
+ if (estate.p.phdr64[estate.segment].p_type != PT_LOAD)
+ continue;
+ val = estate.p.phdr64[estate.segment].p_paddr;
+ if (val < min) {
+ min = val;
+ }
+ val += estate.p.phdr64[estate.segment].p_memsz;
+ if (val > max) {
+ max = val;
+ }
+ if (estate.p.phdr64[estate.segment].p_align > align) {
+ align = estate.p.phdr64[estate.segment].p_align;
+ }
+ }
+ if (align > ULONG_MAX) {
+ printf("ELF base address alignment exceeds address space\n");
+ return dead_download;
+ }
+ if (align & (align -1)) {
+ printf("ELF base address alignment is not a power of 2\n");
+ return dead_download;
+ }
+ if ((max - min) > ULONG_MAX) {
+ printf("ELF size exceeds address space\n");
+ return dead_download;
+ }
+ base_addr = find_segment(max - min, align);
+ if (base_addr == ULONG_MAX) {
+ printf("ELF base address not available for size %ld\n", max - min);
+ return dead_download;
+ }
+ /* Compute the change in base address and fix up the addresses */
+ delta = base_addr - min;
+ for(estate.segment = 0; estate.segment < estate.e.elf64.e_phnum; estate.segment++) {
+ /* Change the base address of the object to load */
+ estate.p.phdr64[estate.segment].p_paddr += delta;
+ }
+ estate.e.elf64.e_entry += delta;
+ }
+#if ELF_NOTES
+ /* Load ELF notes from the image */
+ for(estate.segment = 0; estate.segment < estate.e.elf64.e_phnum; estate.segment++) {
+ if (estate.p.phdr64[estate.segment].p_type != PT_NOTE)
+ continue;
+ if (estate.p.phdr64[estate.segment].p_offset + estate.p.phdr64[estate.segment].p_filesz > len) {
+ /* Ignore ELF notes outside of the first block */
+ continue;
+ }
+ process_elf_notes(data,
+ estate.p.phdr64[estate.segment].p_offset, estate.p.phdr64[estate.segment].p_filesz);
+ }
+#endif
+ /* Check for Etherboot related limitations. Memory
+ * between _text and _end is not allowed.
+ * Reasons: the Etherboot code/data area.
+ */
+ for (estate.segment = 0; estate.segment < estate.e.elf64.e_phnum; estate.segment++) {
+ unsigned long start, mid, end, istart, iend;
+ if (estate.p.phdr64[estate.segment].p_type != PT_LOAD)
+ continue;
+ if ((estate.p.phdr64[estate.segment].p_paddr > ULONG_MAX) ||
+ ((estate.p.phdr64[estate.segment].p_paddr + estate.p.phdr64[estate.segment].p_filesz) > ULONG_MAX) ||
+ ((estate.p.phdr64[estate.segment].p_paddr + estate.p.phdr64[estate.segment].p_memsz) > ULONG_MAX)) {
+ printf("ELF segment exceeds address space\n");
+ return dead_download;
+ }
+ start = estate.p.phdr64[estate.segment].p_paddr;
+ mid = start + estate.p.phdr64[estate.segment].p_filesz;
+ end = start + estate.p.phdr64[estate.segment].p_memsz;
+ istart = iend = ULONG_MAX;
+ if ((estate.p.phdr64[estate.segment].p_offset < ULONG_MAX) &&
+ ((estate.p.phdr64[estate.segment].p_offset + estate.p.phdr64[estate.segment].p_filesz) < ULONG_MAX))
+ {
+ istart = estate.p.phdr64[estate.segment].p_offset;
+ iend = istart + estate.p.phdr64[estate.segment].p_filesz;
+ }
+ if (!prep_segment(start, mid, end, istart, iend)) {
+ return dead_download;
+ }
+ if (!elf_prep_segment(start, mid, end, istart, iend)) {
+ return dead_download;
+ }
+ }
+ estate.segment = -1;
+ estate.loc = 0;
+ estate.skip = 0;
+ estate.toread = 0;
+ return elf64_download;
+}
+
+static sector_t elf64_download(unsigned char *data, unsigned int len, int eof)
+{
+ unsigned long skip_sectors = 0;
+ unsigned int offset; /* working offset in the current data block */
+ int i;
+
+ offset = 0;
+ do {
+ if (estate.segment != -1) {
+ if (estate.skip) {
+ if (estate.skip >= len - offset) {
+ estate.skip -= len - offset;
+ break;
+ }
+ offset += estate.skip;
+ estate.skip = 0;
+ }
+
+ if (estate.toread) {
+ unsigned int cplen;
+ cplen = len - offset;
+ if (cplen >= estate.toread) {
+ cplen = estate.toread;
+ }
+ memcpy(phys_to_virt(estate.curaddr), data+offset, cplen);
+ estate.curaddr += cplen;
+ estate.toread -= cplen;
+ offset += cplen;
+ if (estate.toread)
+ break;
+ }
+ }
+
+ /* Data left, but current segment finished - look for the next
+ * segment (in file offset order) that needs to be loaded.
+ * We can only seek forward, so select the program headers,
+ * in the correct order.
+ */
+ estate.segment = -1;
+ for (i = 0; i < estate.e.elf64.e_phnum; i++) {
+ if (estate.p.phdr64[i].p_type != PT_LOAD)
+ continue;
+ if (estate.p.phdr64[i].p_filesz == 0)
+ continue;
+ if (estate.p.phdr64[i].p_offset < estate.loc + offset)
+ continue; /* can't go backwards */
+ if ((estate.segment != -1) &&
+ (estate.p.phdr64[i].p_offset >= estate.p.phdr64[estate.segment].p_offset))
+ continue; /* search minimum file offset */
+ estate.segment = i;
+ }
+ if (estate.segment == -1) {
+ /* No more segments to be loaded, so just start the
+ * kernel. This saves a lot of network bandwidth if
+ * debug info is in the kernel but not loaded. */
+ goto elf_startkernel;
+ break;
+ }
+ estate.curaddr = estate.p.phdr64[estate.segment].p_paddr;
+ estate.skip = estate.p.phdr64[estate.segment].p_offset - (estate.loc + offset);
+ estate.toread = estate.p.phdr64[estate.segment].p_filesz;
+#if ELF_DEBUG
+ printf("PHDR %d, size %#lX, curaddr %#lX\n",
+ estate.segment, estate.toread, estate.curaddr);
+#endif
+ } while (offset < len);
+
+ estate.loc += len + (estate.skip & ~0x1ff);
+ skip_sectors = estate.skip >> 9;
+ estate.skip &= 0x1ff;
+
+ if (eof) {
+ unsigned long entry;
+ unsigned long machine;
+elf_startkernel:
+ entry = estate.e.elf64.e_entry;
+ machine = estate.e.elf64.e_machine;
+#if ELF_NOTES
+ if (estate.check_ip_checksum) {
+ unsigned long bytes = 0;
+ uint16_t sum, new_sum;
+
+ sum = ipchksum(&estate.e.elf64, sizeof(estate.e.elf64));
+ bytes = sizeof(estate.e.elf64);
+#if ELF_DEBUG
+ printf("Ehdr: %hx %hx sz: %lx bytes: %lx\n",
+ sum, sum, bytes, bytes);
+#endif
+
+ new_sum = ipchksum(estate.p.phdr64, sizeof(estate.p.phdr64[0]) * estate.e.elf64.e_phnum);
+ sum = add_ipchksums(bytes, sum, new_sum);
+ bytes += sizeof(estate.p.phdr64[0]) * estate.e.elf64.e_phnum;
+#if ELF_DEBUG
+ printf("Phdr: %hx %hx sz: %lx bytes: %lx\n",
+ new_sum, sum,
+ sizeof(estate.p.phdr64[0]) * estate.e.elf64.e_phnum, bytes);
+#endif
+
+ for(i = 0; i < estate.e.elf64.e_phnum; i++) {
+ if (estate.p.phdr64[i].p_type != PT_LOAD)
+ continue;
+ new_sum = ipchksum(phys_to_virt(estate.p.phdr64[i].p_paddr),
+ estate.p.phdr64[i].p_memsz);
+ sum = add_ipchksums(bytes, sum, new_sum);
+ bytes += estate.p.phdr64[i].p_memsz;
+#if ELF_DEBUG
+ printf("seg%d: %hx %hx sz: %x bytes: %lx\n",
+ i, new_sum, sum,
+ estate.p.phdr64[i].p_memsz, bytes);
+#endif
+
+ }
+ if (estate.ip_checksum != sum) {
+ printf("\nImage checksum: %hx != computed checksum: %hx\n",
+ estate.ip_checksum, sum);
+ longjmp(restart_etherboot, -2);
+ }
+ }
+#endif
+ done(1);
+ /* Fixup the offset to the program header so you can find the program headers from
+ * the ELF header mknbi needs this.
+ */
+ estate.e.elf64.e_phoff = (char *)&estate.p - (char *)&estate.e;
+ elf_boot(machine,entry);
+ }
+ return skip_sectors;
+}
+
+#endif /* ELF64_IMAGE */
diff --git a/src/core/heap.c b/src/core/heap.c
new file mode 100644
index 00000000..51ce47d7
--- /dev/null
+++ b/src/core/heap.c
@@ -0,0 +1,168 @@
+#include "etherboot.h"
+
+size_t heap_ptr, heap_top, heap_bot;
+
+void init_heap(void)
+{
+ size_t size;
+ size_t start, end;
+ unsigned i;
+ /* Find the largest contiguous area of memory that
+ * I can use for the heap, which is organized as
+ * a stack that grows backwards through memory.
+ */
+
+ /* If I have virtual address that do not equal physical addresses
+ * there is a change I will try to use memory from both sides of
+ * the virtual address space simultaneously, which can cause all kinds
+ * of interesting problems.
+ * Avoid it by logically extending etherboot. Once I know that relocation
+ * works I can just start the virtual address space at 0, and this problem goes
+ * away so that is probably a better solution.
+ */
+#if 0
+ start = virt_to_phys(_text);
+#else
+ /* segment wrap around is nasty don't chance it. */
+ start = virt_to_phys(_virt_start);
+#endif
+ end = virt_to_phys(_end);
+ size = 0;
+ for(i = 0; i < meminfo.map_count; i++) {
+ unsigned long r_start, r_end;
+ if (meminfo.map[i].type != E820_RAM)
+ continue;
+ if (meminfo.map[i].addr > ULONG_MAX)
+ continue;
+ if (meminfo.map[i].size > ULONG_MAX)
+ continue;
+
+ r_start = meminfo.map[i].addr;
+ r_end = r_start + meminfo.map[i].size;
+ if (r_end < r_start) {
+ r_end = ULONG_MAX;
+ }
+ /* Handle areas that overlap etherboot */
+ if ((end > r_start) && (start < r_end)) {
+ /* Etherboot completely covers the region */
+ if ((start <= r_start) && (end >= r_end))
+ continue;
+ /* Etherboot is completely contained in the region */
+ if ((start > r_start) && (end < r_end)) {
+ /* keep the larger piece */
+ if ((r_end - end) >= (r_start - start)) {
+ r_start = end;
+ }
+ else {
+ r_end = start;
+ }
+ }
+ /* Etherboot covers one end of the region.
+ * Shrink the region.
+ */
+ else if (end >= r_end) {
+ r_end = start;
+ }
+ else if (start <= r_start) {
+ r_start = end;
+ }
+ }
+ /* If two areas are the size prefer the greater address */
+ if (((r_end - r_start) > size) ||
+ (((r_end - r_start) == size) && (r_start > heap_top))) {
+ size = r_end - r_start;
+ heap_top = r_start;
+ heap_bot = r_end;
+ }
+ }
+ if (size == 0) {
+ printf("init_heap: No heap found.\n");
+ exit(1);
+ }
+ heap_ptr = heap_bot;
+}
+
+void *allot(size_t size)
+{
+ void *ptr;
+ size_t *mark, addr;
+ /* Get an 16 byte aligned chunk of memory off of the heap
+ * An extra sizeof(size_t) bytes is allocated to track
+ * the size of the object allocated on the heap.
+ */
+ addr = (heap_ptr - (size + sizeof(size_t))) & ~15;
+ if (addr < heap_top) {
+ ptr = 0;
+ } else {
+ mark = phys_to_virt(addr);
+ *mark = size;
+ heap_ptr = addr;
+ ptr = phys_to_virt(addr + sizeof(size_t));
+ }
+ return ptr;
+}
+
+//if mask = 0xf, it will be 16 byte aligned
+//if mask = 0xff, it will be 256 byte aligned
+//For DMA memory allocation, because it has more reqiurement on alignment
+void *allot2(size_t size, uint32_t mask)
+{
+ void *ptr;
+ size_t *mark, addr;
+ uint32_t *mark1;
+
+ addr = ((heap_ptr - size ) & ~mask) - sizeof(size_t) - sizeof(uint32_t);
+ if (addr < heap_top) {
+ ptr = 0;
+ } else {
+ mark = phys_to_virt(addr);
+ *mark = size;
+ mark1 = phys_to_virt(addr+sizeof(size_t));
+ *mark1 = mask;
+ heap_ptr = addr;
+ ptr = phys_to_virt(addr + sizeof(size_t) + sizeof(uint32_t));
+ }
+ return ptr;
+}
+
+void forget(void *ptr)
+{
+ size_t *mark, addr;
+ size_t size;
+
+ if (!ptr) {
+ return;
+ }
+ addr = virt_to_phys(ptr);
+ mark = phys_to_virt(addr - sizeof(size_t));
+ size = *mark;
+ addr += (size + 15) & ~15;
+
+ if (addr > heap_bot) {
+ addr = heap_bot;
+ }
+ heap_ptr = addr;
+}
+
+void forget2(void *ptr)
+{
+ size_t *mark, addr;
+ size_t size;
+ uint32_t mask;
+ uint32_t *mark1;
+
+ if (!ptr) {
+ return;
+ }
+ addr = virt_to_phys(ptr);
+ mark = phys_to_virt(addr - sizeof(size_t) - sizeof(uint32_t));
+ size = *mark;
+ mark1 = phys_to_virt(addr - sizeof(uint32_t));
+ mask = *mark1;
+ addr += (size + mask) & ~mask;
+
+ if (addr > heap_bot) {
+ addr = heap_bot;
+ }
+ heap_ptr = addr;
+}
diff --git a/src/core/i82365.c b/src/core/i82365.c
new file mode 100644
index 00000000..c26639e0
--- /dev/null
+++ b/src/core/i82365.c
@@ -0,0 +1,656 @@
+#ifdef CONFIG_PCMCIA
+
+/*
+ * i82365.c
+ * Support for i82365 and similar ISA-to-PCMCIA bridges
+ *
+ * Taken from Linux kernel sources, distributed under GPL2
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The initial developer of the original code is David A. Hinds
+ * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+ *
+ * Ported by: Anselm Martin Hoffmeister, Stockholm Projekt Computer-Service, Sankt Augustin/Bonn, GERMANY
+ */
+
+/*
+ *
+ *
+ * ******************************
+ * PLEASE DO NOT YET WORK ON THIS
+ * ******************************
+ *
+ * I'm still fixing it up on every end, so we most probably would interfere
+ * at some point. If there's anything obvious or better, not-so-obvious,
+ * please contact me by e-mail: anselm (AT) hoffmeister (DOT) be *THANKS*
+ */
+#include "../include/pcmcia.h"
+#include "../include/pcmcia-opts.h"
+#include "../include/i82365.h"
+
+#ifndef CONFIG_ISA
+#error PCMCIA_I82365 only works with ISA defined - set CONFIG_ISA
+#endif
+
+typedef enum pcic_id {
+ IS_I82365A, IS_I82365B, IS_I82365DF,
+ IS_IBM, IS_RF5Cx96, IS_VLSI, IS_VG468, IS_VG469,
+ IS_PD6710, IS_PD672X, IS_VT83C469,
+} pcic_id;
+
+/* Flags for classifying groups of controllers */
+#define IS_VADEM 0x0001
+#define IS_CIRRUS 0x0002
+#define IS_TI 0x0004
+#define IS_O2MICRO 0x0008
+#define IS_VIA 0x0010
+#define IS_TOPIC 0x0020
+#define IS_RICOH 0x0040
+#define IS_UNKNOWN 0x0400
+#define IS_VG_PWR 0x0800
+#define IS_DF_PWR 0x1000
+#define IS_PCI 0x2000
+#define IS_ALIVE 0x8000
+
+typedef struct pcic_t {
+ char *name;
+ u_short flags;
+} pcic_t;
+
+static pcic_t pcic[] = {
+ { "Intel i82365sl A step", 0 },
+ { "Intel i82365sl B step", 0 },
+ { "Intel i82365sl DF", IS_DF_PWR },
+ { "IBM Clone", 0 },
+ { "Ricoh RF5C296/396", 0 },
+ { "VLSI 82C146", 0 },
+ { "Vadem VG-468", IS_VADEM },
+ { "Vadem VG-469", IS_VADEM|IS_VG_PWR },
+ { "Cirrus PD6710", IS_CIRRUS },
+ { "Cirrus PD672x", IS_CIRRUS },
+ { "VIA VT83C469", IS_CIRRUS|IS_VIA },
+};
+
+typedef struct cirrus_state_t {
+ u_char misc1, misc2;
+ u_char timer[6];
+} cirrus_state_t;
+
+typedef struct vg46x_state_t {
+ u_char ctl, ema;
+} vg46x_state_t;
+
+typedef struct socket_info_t {
+ u_short type, flags;
+ socket_cap_t cap;
+ ioaddr_t ioaddr;
+ u_short psock;
+ u_char cs_irq, intr;
+ void (*handler)(void *info, u_int events);
+ void *info;
+ union {
+ cirrus_state_t cirrus;
+ vg46x_state_t vg46x;
+ } state;
+} socket_info_t;
+
+//static socket_info_t socket[8];
+
+int i365_base = 0x3e0; // Default in Linux kernel
+int cycle_time = 120; // External clock time in ns, 120ns =~ 8.33 MHz
+int mydriverid = 0;
+
+void phex ( unsigned char c );
+/*static int to_cycles(int ns)
+{
+ return ns/cycle_time;
+}
+*/
+/*static int to_ns(int cycles)
+{
+ return cycle_time*cycles;
+}
+*/
+
+static u_char i365_get(u_short sock, u_short reg)
+{
+ //unsigned long flags;
+ //spin_lock_irqsave(&bus_lock,flags);
+ {
+ ioaddr_t port = pccsock[sock].ioaddr;
+ u_char val;
+ reg = I365_REG(pccsock[sock].internalid, reg);
+ outb(reg, port); val = inb(port+1);
+ //spin_unlock_irqrestore(&bus_lock,flags);
+ return val;
+ }
+}
+
+static void i365_set(u_short sock, u_short reg, u_char data)
+{
+ //unsigned long flags;
+ //spin_lock_irqsave(&bus_lock,flags);
+ {
+ ioaddr_t port = pccsock[sock].ioaddr;
+ u_char val = I365_REG(pccsock[sock].internalid, reg);
+ outb(val, port); outb(data, port+1);
+ //spin_unlock_irqrestore(&bus_lock,flags);
+ }
+}
+
+void add_socket_i365(u_short port, int psock, int type) {
+ pccsock[pccsocks].ioaddr = port;
+ pccsock[pccsocks].internalid = psock;
+ pccsock[pccsocks].type = type;
+ pccsock[pccsocks].flags = pcic[type].flags;
+ pccsock[pccsocks].drivernum = mydriverid;
+ pccsock[pccsocks].configoffset = -1;
+ // Find out if a card in inside that socket
+ pccsock[pccsocks].status = (( 12 == (i365_get(pccsocks,I365_STATUS)&12) ) ? HASCARD : EMPTY );
+ // *TODO* check if that's all
+ if ( 0 == (psock & 1) ) {
+ printf ( "Found a PCMCIA controller (i82365) at io %x, type '%s'\n", port, pcic[type].name );
+ // pccsock[pccsocks].status == HASCARD? "holds card":"empty" );
+ }
+ pccsocks++;
+ return;
+}
+
+void i365_bset(u_short sock, u_short reg, u_char mask) {
+ u_char d = i365_get(sock, reg);
+ d |= mask;
+ i365_set(sock, reg, d);
+}
+
+void i365_bclr(u_short sock, u_short reg, u_char mask) {
+ u_char d = i365_get(sock, reg);
+ d &= ~mask;
+ i365_set(sock, reg, d);
+}
+
+
+/*static void i365_bflip(u_short sock, u_short reg, u_char mask, int b)
+{
+ u_char d = i365_get(sock, reg);
+ if (b)
+ d |= mask;
+ else
+ d &= ~mask;
+ i365_set(sock, reg, d);
+}
+*/
+
+/*
+static u_short i365_get_pair(u_short sock, u_short reg)
+{
+ u_short a, b;
+ a = i365_get(sock, reg);
+ b = i365_get(sock, reg+1);
+ return (a + (b<<8));
+}
+*/
+
+/*
+static void i365_set_pair(u_short sock, u_short reg, u_short data)
+{
+ i365_set(sock, reg, data & 0xff);
+ i365_set(sock, reg+1, data >> 8);
+}
+*/
+int identify_i365 ( u_short port, u_short sock ) {
+ u_char val;
+ int type = -1;
+ /* Use the next free entry in the socket table */
+ pccsock[pccsocks].ioaddr = port;
+ pccsock[pccsocks].internalid = sock;
+ // *TODO* wakeup a sleepy cirrus controller?
+
+ if ((val = i365_get(pccsocks, I365_IDENT)) & 0x70)
+ return -1;
+ switch (val) {
+ case 0x82:
+ type = IS_I82365A; break;
+ case 0x83:
+ type = IS_I82365B; break;
+ case 0x84:
+ type = IS_I82365DF; break;
+ case 0x88: case 0x89: case 0x8a:
+ type = IS_IBM; break;
+ }
+ /* Check for Vadem VG-468 chips */
+ outb(0x0e, port);
+ outb(0x37, port);
+ i365_bset(pccsocks, VG468_MISC, VG468_MISC_VADEMREV);
+ val = i365_get(pccsocks, I365_IDENT);
+ if (val & I365_IDENT_VADEM) {
+ i365_bclr(pccsocks, VG468_MISC, VG468_MISC_VADEMREV);
+ type = ((val & 7) >= 4) ? IS_VG469 : IS_VG468;
+ }
+
+ /* Check for Ricoh chips */
+ val = i365_get(pccsocks, RF5C_CHIP_ID);
+ if ((val == RF5C_CHIP_RF5C296) || (val == RF5C_CHIP_RF5C396)) type = IS_RF5Cx96;
+
+ /* Check for Cirrus CL-PD67xx chips */
+ i365_set(pccsocks, PD67_CHIP_INFO, 0);
+ val = i365_get(pccsocks, PD67_CHIP_INFO);
+ if ((val & PD67_INFO_CHIP_ID) == PD67_INFO_CHIP_ID) {
+ val = i365_get(pccsocks, PD67_CHIP_INFO);
+ if ((val & PD67_INFO_CHIP_ID) == 0) {
+ type = (val & PD67_INFO_SLOTS) ? IS_PD672X : IS_PD6710;
+ i365_set(pccsocks, PD67_EXT_INDEX, 0xe5);
+ if (i365_get(pccsocks, PD67_EXT_INDEX) != 0xe5) type = IS_VT83C469;
+ }
+ }
+ return type;
+}
+
+int init_i82365(void) {
+ int i, j, sock, k, ns, id;
+ //unsigned int ui,uj;
+ //unsigned char * upc;
+ ioaddr_t port;
+ int i82365s = 0;
+ // Change from kernel: No irq init, no check_region, no isapnp support
+ // No ignore socket, no extra sockets to check (so it's easier here :-/)
+ // Probably we don't need any of them; in case YOU do, SHOUT AT ME!
+ id = identify_i365(i365_base, 0);
+ if ((id == IS_I82365DF) && (identify_i365(i365_base, 1) != id)) {
+ for (i = 0; i < 4; i++) {
+ port = i365_base + ((i & 1) << 2) + ((i & 2) << 1);
+ sock = (i & 1) << 1;
+ if (identify_i365(port, sock) == IS_I82365DF) {
+ add_socket_i365(port, sock, IS_VLSI);
+ }
+ }
+ } else {
+ for (i = 0; i < 4; i += 2) {
+ port = i365_base + 2*(i>>2);
+ sock = (i & 3);
+ id = identify_i365(port, sock);
+ if (id < 0) continue;
+
+ for (j = ns = 0; j < 2; j++) {
+ /* Does the socket exist? */
+ if (identify_i365(port, sock+j) < 0) continue;
+ /* Check for bad socket decode */
+ for (k = 0; k <= i82365s; k++)
+ i365_set(k, I365_MEM(0)+I365_W_OFF, k);
+ for (k = 0; k <= i82365s; k++)
+ if (i365_get(k, I365_MEM(0)+I365_W_OFF) != k)
+ break;
+ if (k <= i82365s) break;
+ add_socket_i365(port, sock+j, id); ns++;
+ }
+ }
+ }
+ return 0;
+
+
+
+
+
+
+
+/* printf ( "Selecting config 1: io 0x300 @byte 87*2.." );
+ upc[(2*87)] = 2;
+ i365_bclr(1, I365_ADDRWIN, 1 );
+ i365_set(1,I365_INTCTL, 0x65 ); //no-reset, memory-card
+ i365_set(1, I365_IO(0)+0, 0x20 );
+ i365_set(1, I365_IO(0)+1, 0x03 );
+ i365_set(1, I365_IO(0)+2, 0x3f );
+ i365_set(1, I365_IO(0)+3, 0x03 );
+ i365_set(1, 0x3a, 0x05 );
+ i365_set(1, 0x3b, 0x05 );
+ i365_set(1, 0x3c, 0x05 );
+ i365_set(1, 0x3d, 0x05 );
+ i365_set(1, 0x3e, 0x05 );
+ i365_set(1, 0x3f, 0x05 );
+ i365_set(1, 0x07, 0x0a );
+ i365_set(1, I365_ADDRWIN, 0x40 ); // 0x40
+ printf ( "!\n" ); getchar();
+ printf ( "\n" );
+ return 0; */
+}
+
+void phex ( unsigned char c ) {
+ unsigned char a = 0, b = 0;
+ b = ( c & 0xf );
+ if ( b > 9 ) b += ('a'-'9'-1);
+ b += '0';
+ a = ( c & 0xf0 ) >> 4;
+ if ( a > 9 ) a += ('a'-'9'-1);
+ a += '0';
+ printf ( "%c%c ", a, b );
+ return;
+}
+
+int deinit_i82365(void) {
+ printf("Deinitializing i82365\n" );
+ return 0;
+}
+
+/*static int i365_get_status(u_short sock, u_int *value)
+{
+ u_int status;
+
+ status = i365_get(sock, I365_STATUS);
+ *value = ((status & I365_CS_DETECT) == I365_CS_DETECT)
+ ? SS_DETECT : 0;
+
+ if (i365_get(sock, I365_INTCTL) & I365_PC_IOCARD)
+ *value |= (status & I365_CS_STSCHG) ? 0 : SS_STSCHG;
+ else {
+ *value |= (status & I365_CS_BVD1) ? 0 : SS_BATDEAD;
+ *value |= (status & I365_CS_BVD2) ? 0 : SS_BATWARN;
+ }
+ *value |= (status & I365_CS_WRPROT) ? SS_WRPROT : 0;
+ *value |= (status & I365_CS_READY) ? SS_READY : 0;
+ *value |= (status & I365_CS_POWERON) ? SS_POWERON : 0;
+
+#ifdef CONFIG_ISA
+ if (pccsock[sock].type == IS_VG469) {
+ status = i365_get(sock, VG469_VSENSE);
+ if (pccsock[sock].internalid & 1) {
+ *value |= (status & VG469_VSENSE_B_VS1) ? 0 : SS_3VCARD;
+ *value |= (status & VG469_VSENSE_B_VS2) ? 0 : SS_XVCARD;
+ } else {
+ *value |= (status & VG469_VSENSE_A_VS1) ? 0 : SS_3VCARD;
+ *value |= (status & VG469_VSENSE_A_VS2) ? 0 : SS_XVCARD;
+ }
+ }
+#endif
+
+ printf("i82365: GetStatus(%d) = %#4.4x\n", sock, *value);
+ return 0;
+} //i365_get_status
+*/
+
+/*static int i365_set_socket(u_short sock, socket_state_t *state)
+{
+ socket_info_t *t = &socket[sock];
+ u_char reg;
+
+ printf("i82365: SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
+ "io_irq %d, csc_mask %#2.2x)\n", sock, state->flags,
+ state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
+printf ("\nERROR:UNIMPLEMENTED\n" );
+return 0;
+ // First set global controller options
+ // set_bridge_state(sock); *TODO* check: need this here?
+
+ // IO card, RESET flag, IO interrupt
+ reg = t->intr;
+ if (state->io_irq != t->cap.pci_irq) reg |= state->io_irq;
+ reg |= (state->flags & SS_RESET) ? 0 : I365_PC_RESET;
+ reg |= (state->flags & SS_IOCARD) ? I365_PC_IOCARD : 0;
+ i365_set(sock, I365_INTCTL, reg);
+
+ reg = I365_PWR_NORESET;
+ if (state->flags & SS_PWR_AUTO) reg |= I365_PWR_AUTO;
+ if (state->flags & SS_OUTPUT_ENA) reg |= I365_PWR_OUT;
+
+ if (t->flags & IS_CIRRUS) {
+ if (state->Vpp != 0) {
+ if (state->Vpp == 120)
+ reg |= I365_VPP1_12V;
+ else if (state->Vpp == state->Vcc)
+ reg |= I365_VPP1_5V;
+ else return -EINVAL;
+ }
+ if (state->Vcc != 0) {
+ reg |= I365_VCC_5V;
+ if (state->Vcc == 33)
+ i365_bset(sock, PD67_MISC_CTL_1, PD67_MC1_VCC_3V);
+ else if (state->Vcc == 50)
+ i365_bclr(sock, PD67_MISC_CTL_1, PD67_MC1_VCC_3V);
+ else return -EINVAL;
+ }
+ } else if (t->flags & IS_VG_PWR) {
+ if (state->Vpp != 0) {
+ if (state->Vpp == 120)
+ reg |= I365_VPP1_12V;
+ else if (state->Vpp == state->Vcc)
+ reg |= I365_VPP1_5V;
+ else return -EINVAL;
+ }
+ if (state->Vcc != 0) {
+ reg |= I365_VCC_5V;
+ if (state->Vcc == 33)
+ i365_bset(sock, VG469_VSELECT, VG469_VSEL_VCC);
+ else if (state->Vcc == 50)
+ i365_bclr(sock, VG469_VSELECT, VG469_VSEL_VCC);
+ else return -EINVAL;
+ }
+ } else if (t->flags & IS_DF_PWR) {
+ switch (state->Vcc) {
+ case 0: break;
+ case 33: reg |= I365_VCC_3V; break;
+ case 50: reg |= I365_VCC_5V; break;
+ default: return -EINVAL;
+ }
+ switch (state->Vpp) {
+ case 0: break;
+ case 50: reg |= I365_VPP1_5V; break;
+ case 120: reg |= I365_VPP1_12V; break;
+ default: return -EINVAL;
+ }
+ } else {
+ switch (state->Vcc) {
+ case 0: break;
+ case 50: reg |= I365_VCC_5V; break;
+ default: return -EINVAL;
+ }
+ switch (state->Vpp) {
+ case 0: break;
+ case 50: reg |= I365_VPP1_5V | I365_VPP2_5V; break;
+ case 120: reg |= I365_VPP1_12V | I365_VPP2_12V; break;
+ default: return -EINVAL;
+ }
+ }
+
+ if (reg != i365_get(sock, I365_POWER))
+ i365_set(sock, I365_POWER, reg);
+
+ // Chipset-specific functions
+ if (t->flags & IS_CIRRUS) {
+ // Speaker control
+ i365_bflip(sock, PD67_MISC_CTL_1, PD67_MC1_SPKR_ENA,
+ state->flags & SS_SPKR_ENA);
+ }
+
+ // Card status change interrupt mask
+ reg = t->cs_irq << 4;
+ if (state->csc_mask & SS_DETECT) reg |= I365_CSC_DETECT;
+ if (state->flags & SS_IOCARD) {
+ if (state->csc_mask & SS_STSCHG) reg |= I365_CSC_STSCHG;
+ } else {
+ if (state->csc_mask & SS_BATDEAD) reg |= I365_CSC_BVD1;
+ if (state->csc_mask & SS_BATWARN) reg |= I365_CSC_BVD2;
+ if (state->csc_mask & SS_READY) reg |= I365_CSC_READY;
+ }
+ i365_set(sock, I365_CSCINT, reg);
+ i365_get(sock, I365_CSC);
+
+ return 0;
+} // i365_set_socket
+*/
+
+/*static int i365_get_io_map(u_short sock, struct pccard_io_map *io)
+{
+ u_char map, ioctl, addr;
+ printf ( "GETIOMAP unimplemented\n" ); return 0;
+ map = io->map;
+ if (map > 1) return -EINVAL;
+ io->start = i365_get_pair(sock, I365_IO(map)+I365_W_START);
+ io->stop = i365_get_pair(sock, I365_IO(map)+I365_W_STOP);
+ ioctl = i365_get(sock, I365_IOCTL);
+ addr = i365_get(sock, I365_ADDRWIN);
+ io->speed = to_ns(ioctl & I365_IOCTL_WAIT(map)) ? 1 : 0;
+ io->flags = (addr & I365_ENA_IO(map)) ? MAP_ACTIVE : 0;
+ io->flags |= (ioctl & I365_IOCTL_0WS(map)) ? MAP_0WS : 0;
+ io->flags |= (ioctl & I365_IOCTL_16BIT(map)) ? MAP_16BIT : 0;
+ io->flags |= (ioctl & I365_IOCTL_IOCS16(map)) ? MAP_AUTOSZ : 0;
+ printf("i82365: GetIOMap(%d, %d) = %#2.2x, %d ns, "
+ "%#4.4x-%#4.4x\n", sock, map, io->flags, io->speed,
+ io->start, io->stop);
+ return 0;
+} // i365_get_io_map
+*/
+
+/*====================================================================*/
+
+/*static int i365_set_io_map(u_short sock, struct pccard_io_map *io)
+{
+ u_char map, ioctl;
+
+ printf("i82365: SetIOMap(%d, %d, %#2.2x, %d ns, "
+ "%#4.4x-%#4.4x)\n", sock, io->map, io->flags,
+ io->speed, io->start, io->stop);
+printf ( "UNIMPLEMENTED\n" );
+ return 0;
+ map = io->map;
+ //if ((map > 1) || (io->start > 0xffff) || (io->stop > 0xffff) ||
+ if ((map > 1) ||
+ (io->stop < io->start)) return -EINVAL;
+ // Turn off the window before changing anything
+ if (i365_get(sock, I365_ADDRWIN) & I365_ENA_IO(map))
+ i365_bclr(sock, I365_ADDRWIN, I365_ENA_IO(map));
+ i365_set_pair(sock, I365_IO(map)+I365_W_START, io->start);
+ i365_set_pair(sock, I365_IO(map)+I365_W_STOP, io->stop);
+ ioctl = i365_get(sock, I365_IOCTL) & ~I365_IOCTL_MASK(map);
+ if (io->speed) ioctl |= I365_IOCTL_WAIT(map);
+ if (io->flags & MAP_0WS) ioctl |= I365_IOCTL_0WS(map);
+ if (io->flags & MAP_16BIT) ioctl |= I365_IOCTL_16BIT(map);
+ if (io->flags & MAP_AUTOSZ) ioctl |= I365_IOCTL_IOCS16(map);
+ i365_set(sock, I365_IOCTL, ioctl);
+ // Turn on the window if necessary
+ if (io->flags & MAP_ACTIVE)
+ i365_bset(sock, I365_ADDRWIN, I365_ENA_IO(map));
+ return 0;
+} // i365_set_io_map
+*/
+
+/*
+static int i365_set_mem_map(u_short sock, struct pccard_mem_map *mem)
+{
+ u_short base, i;
+ u_char map;
+
+ printf("i82365: SetMemMap(%d, %d, %#2.2x, %d ns, %#5.5lx-%#5.5"
+ "lx, %#5.5x)\n", sock, mem->map, mem->flags, mem->speed,
+ mem->sys_start, mem->sys_stop, mem->card_start);
+
+printf ( "UNIMPLEMENTED\n" );
+ return 0;
+ map = mem->map;
+ if ((map > 4) || (mem->card_start > 0x3ffffff) ||
+ (mem->sys_start > mem->sys_stop) || (mem->speed > 1000))
+ return -EINVAL;
+ if (!(socket[sock].flags & IS_PCI) &&
+ ((mem->sys_start > 0xffffff) || (mem->sys_stop > 0xffffff)))
+ return -EINVAL;
+
+ // Turn off the window before changing anything
+ if (i365_get(sock, I365_ADDRWIN) & I365_ENA_MEM(map))
+ i365_bclr(sock, I365_ADDRWIN, I365_ENA_MEM(map));
+
+ base = I365_MEM(map);
+ i = (mem->sys_start >> 12) & 0x0fff;
+ if (mem->flags & MAP_16BIT) i |= I365_MEM_16BIT;
+ if (mem->flags & MAP_0WS) i |= I365_MEM_0WS;
+ i365_set_pair(sock, base+I365_W_START, i);
+
+ i = (mem->sys_stop >> 12) & 0x0fff;
+ switch (to_cycles(mem->speed)) {
+ case 0: break;
+ case 1: i |= I365_MEM_WS0; break;
+ case 2: i |= I365_MEM_WS1; break;
+ default: i |= I365_MEM_WS1 | I365_MEM_WS0; break;
+ }
+ i365_set_pair(sock, base+I365_W_STOP, i);
+
+ i = ((mem->card_start - mem->sys_start) >> 12) & 0x3fff;
+ if (mem->flags & MAP_WRPROT) i |= I365_MEM_WRPROT;
+ if (mem->flags & MAP_ATTRIB) i |= I365_MEM_REG;
+ i365_set_pair(sock, base+I365_W_OFF, i);
+
+ // Turn on the window if necessary
+ if (mem->flags & MAP_ACTIVE)
+ i365_bset(sock, I365_ADDRWIN, I365_ENA_MEM(map));
+ return 0;
+} // i365_set_mem_map
+*/
+
+
+int i82365_interfacer ( interface_func_t func, int sockno, int par1, int par2, void* par3 ) {
+ //int i, j, k;
+ //u_int ui;
+ u_char *upc;
+ struct pcc_config_t * pccc;
+ switch ( func ) {
+ case INIT:
+ mydriverid = par1;
+ return init_i82365();
+ case SHUTDOWN:
+ i365_set(sockno, I365_ADDRWIN, i365_get(sockno, I365_ADDRWIN) & 0x20 );
+ i365_set(sockno, I365_INTCTL, 0x05 );
+ sleepticks(2);
+ i365_set(sockno,I365_INTCTL, 0x45 ); //no-reset, memory-card
+ break;
+ case MAPATTRMEM:
+ i365_set(sockno,I365_POWER, 0xb1 );
+ i365_set(sockno, I365_INTCTL, 0x05 );
+ sleepticks(2);
+ i365_set(sockno,I365_INTCTL, 0x45 ); //no-reset, memory-card
+ i365_set(sockno, I365_ADDRWIN, i365_get(sockno, I365_ADDRWIN) & 0x20 );
+ //i365_bclr(sockno, I365_ADDRWIN, 1 );
+ i365_set(sockno, I365_MEM(0)+0, ( par1 >> 12 )& 0xff ); //start
+ i365_set(sockno, I365_MEM(0)+1, ( par1 >> 20 ) & 0x0f );
+ i365_set(sockno, I365_MEM(0)+2, ((par1 + par2 - 1 ) >> 12 ) & 0xff ); //end
+ i365_set(sockno, I365_MEM(0)+3, (( par1 + par2 - 1 ) >> 20 ) & 0x0f );
+ i365_set(sockno, I365_MEM(0)+4, ((0x4000000 - par1) >> 12) & 0xff ); //offset low
+ i365_set(sockno, I365_MEM(0)+5, 0x40 | (((0x40000000 - par1) >> 12) & 0x3f));
+ i365_bset(sockno, I365_ADDRWIN, 1 );
+ if ( ! ( 1 & i365_get ( sockno, I365_ADDRWIN ) ) ) return 1;
+ break;
+ case UNMAPATTRMEM:
+ i365_set(sockno, I365_ADDRWIN, i365_get(sockno, I365_ADDRWIN) & 0x20 );
+ i365_set(sockno,I365_INTCTL, 0x45 ); //no-reset, memory-card
+ break;
+ case SELECTCONFIG: // Params: par1: config number; par3 config pointer pointer
+ if ( 0 > pccsock[sockno].configoffset ) return 1;
+ if ( NULL == (pccc = par3 ) ) return 2;
+ // write config number to
+ upc = ioremap ( MAP_ATTRMEM_TO, MAP_ATTRMEM_LEN );
+ if ( pccsock[sockno].configoffset > MAP_ATTRMEM_LEN ) return 3;
+ if ( ( par1 & 0x7fffffc0 ) ) return 4;
+ if ( pccc->index != par1 ) return 5;
+ upc[pccsock[sockno].configoffset] = ( upc[pccsock[sockno].configoffset] & 0xc0 ) | ( par1 & 0x3f );
+ i365_set(sockno, I365_IOCTL, (i365_get(sockno, I365_IOCTL) & 0xfe) | 0x20 ); // 16bit autosize
+ i365_set(sockno, I365_IO(0)+0, pccc->iowin & 0xff);
+ i365_set(sockno, I365_IO(0)+1, (pccc->iowin >> 8) & 0xff);
+ i365_set(sockno, I365_IO(0)+2, (pccc->iowin+pccc->iolen - 1) & 0xff);
+ i365_set(sockno, I365_IO(0)+3, ((pccc->iowin+pccc->iolen- 1) >> 8) & 0xff);
+ // Disable mem mapping
+ i365_bclr(sockno, I365_ADDRWIN, 1);
+ i365_set(sockno, I365_INTCTL, 0x65);
+ i365_bset(sockno, I365_ADDRWIN,0x40);
+ break;
+ default:
+ return -1; // ERROR: Unknown function called
+ }
+ return 0;
+}
+
+// get_mem_map[1320]
+// cirrus_get_state/set/opts...
+// vg46x_get_state/...
+// get_bridge_state/...
+
+#endif /* CONFIG_PCMCIA */
diff --git a/src/core/isa_probe.c b/src/core/isa_probe.c
new file mode 100644
index 00000000..2bc523e7
--- /dev/null
+++ b/src/core/isa_probe.c
@@ -0,0 +1,66 @@
+#ifdef CONFIG_ISA
+/*
+ * 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.
+ */
+
+#include "etherboot.h"
+#include "nic.h"
+#include "isa.h"
+
+void isa_enumerate(void)
+{
+ const struct isa_driver *driver;
+ for(driver = isa_drivers; driver < isa_drivers_end; driver++) {
+ printf("%s ", driver->name);
+ }
+
+}
+
+int isa_probe(struct dev *dev, const char *type_name)
+{
+/*
+ * NIC probing is in the order the drivers were linked togeter.
+ * If for some reason you want to change the order,
+ * just change the order you list the drivers in.
+ */
+ struct isa_probe_state *state = &dev->state.isa;
+ printf("Probing isa %s...\n", type_name);
+ if (dev->how_probe == PROBE_FIRST) {
+ state->advance = 0;
+ state->driver = isa_drivers;
+ dev->index = -1;
+ }
+ for(;;)
+ {
+ if ((dev->how_probe != PROBE_AWAKE) && state->advance) {
+ state->driver++;
+ dev->index = -1;
+ }
+ state->advance = 1;
+
+ if (state->driver >= isa_drivers_end)
+ break;
+
+ if (state->driver->type != dev->type)
+ continue;
+
+ if (dev->how_probe != PROBE_AWAKE) {
+ dev->type_index++;
+ }
+ printf("[%s]", state->driver->name);
+ dev->devid.bus_type = ISA_BUS_TYPE;
+ /* FIXME how do I handle dev->index + PROBE_AGAIN?? */
+ /* driver will fill in vendor and device IDs */
+ if (state->driver->probe(dev, state->driver->ioaddrs)) {
+ state->advance = (dev->index == -1);
+ return PROBE_WORKED;
+ }
+ putchar('\n');
+ }
+ return PROBE_FAILED;
+}
+
+#endif
diff --git a/src/core/isapnp.c b/src/core/isapnp.c
new file mode 100644
index 00000000..b03da92c
--- /dev/null
+++ b/src/core/isapnp.c
@@ -0,0 +1,382 @@
+#ifdef CONFIG_ISA
+/**************************************************************************
+*
+* isapnp.c -- Etherboot isapnp support for the 3Com 3c515
+* Written 2002-2003 by Timothy Legge <tlegge@rogers.com>
+*
+* 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 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* Portions of this code:
+* Copyright (C) 2001 P.J.H.Fox (fox@roestock.demon.co.uk)
+*
+*
+* REVISION HISTORY:
+* ================
+* Version 0.1 April 26, 2002 TJL
+* Version 0.2 01/08/2003 TJL Moved outside the 3c515.c driver file
+* Version 0.3 Sept 23, 2003 timlegge Change delay to currticks
+*
+*
+* Indent Options: indent -kr -i8
+***************************************************************************/
+
+/* to get some global routines like printf */
+#include "etherboot.h"
+#include "timer.h"
+#include "isapnp.h"
+
+static int pnp_card_csn = 0;
+
+void isapnp_wait(unsigned int nticks)
+{
+ unsigned int to = currticks() + nticks;
+ while (currticks() < to)
+ /* Wait */ ;
+}
+
+/* The following code is the ISA PNP logic required to activate the 3c515 */
+/* PNP Defines */
+#define IDENT_LEN 9
+#define NUM_CARDS 128
+
+/* PNP declares */
+static unsigned char serial_identifier[NUM_CARDS + 1][IDENT_LEN];
+static unsigned char isapnp_checksum_value;
+static char initdata[INIT_LENGTH] = INITDATA;
+int read_port = 0;
+
+
+/* PNP Prototypes */
+static int Isolate(void);
+static int do_isapnp_isolate(void);
+static int isapnp_build_device_list(void);
+static int isapnp_isolate_rdp_select(void);
+static int isapnp_next_rdp(void);
+static void isapnp_peek(unsigned char *data, int bytes);
+static void send_key(void);
+static unsigned char isapnp_checksum(unsigned char *data);
+int Config(int csn);
+
+void config_pnp_device(void)
+{
+ /* PNP Configuration */
+ printf("Probing/Configuring ISAPNP devices\n");
+ if (!read_port) {
+ Isolate();
+ if (pnp_card_csn)
+ Config(pnp_card_csn);
+ }
+}
+
+/* Isolate all the PNP Boards on the ISA BUS */
+static int Isolate(void)
+{
+ int cards = 0;
+ if (read_port < 0x203 || read_port > 0x3ff) {
+ cards = do_isapnp_isolate();
+ if (cards < 0 || (read_port < 0x203 || read_port > 0x3ff)) {
+ printf("No Plug & Play device found\n");
+ return 0;
+ }
+ }
+ isapnp_build_device_list();
+#ifdef EDEBUG
+ printf("%d Plug & Play device found\n", cards);
+#endif
+ return 0;
+}
+
+static int do_isapnp_isolate(void)
+{
+ unsigned char checksum = 0x6a;
+ unsigned char chksum = 0x00;
+ unsigned char bit = 0x00;
+ unsigned char c1, c2;
+ int csn = 0;
+ int i;
+ int iteration = 1;
+
+ read_port = 0x213;
+ if (isapnp_isolate_rdp_select() < 0)
+ return -1;
+
+ while (1) {
+ for (i = 1; i <= 64; i++) {
+ c1 = READ_DATA;
+ isapnp_wait(1);
+ c2 = READ_DATA;
+ isapnp_wait(1);
+ if (c1 == 0x55) {
+ if (c2 == 0xAA) {
+ bit = 0x01;
+ }
+ }
+ checksum =
+ ((((checksum ^ (checksum >> 1)) & 0x01) ^ bit)
+ << 7) | (checksum >> 1);
+ bit = 0x00;
+ }
+#ifdef EDEBUG
+ printf("Calc checksum %d", checksum);
+#endif
+ for (i = 65; i <= 72; i++) {
+ c1 = READ_DATA;
+ udelay(250);
+ c2 = READ_DATA;
+ udelay(250);
+ if (c1 == 0x55) {
+ if (c2 == 0xAA)
+ chksum |= (1 << (i - 65));
+ }
+ }
+#ifdef EDEBUG
+ printf("Actual checksum %d", chksum);
+#endif
+ if (checksum != 0x00 && checksum == chksum) {
+ csn++;
+ serial_identifier[csn][iteration] >>= 1;
+ serial_identifier[csn][iteration] |= bit;
+ CARDSELECTNUMBER;
+#ifdef EDEBUG
+ printf("Writing csn: %d", csn);
+#endif
+ WRITE_DATA(csn);
+ udelay(250);
+ iteration++;
+ /* Force all cards without a CSN into Isolation state */
+ Wake(0);
+ SetRdPort(read_port);
+ udelay(1000);
+ SERIALISOLATION;
+ udelay(1000);
+ goto __next;
+ }
+ if (iteration == 1) {
+ read_port += READ_ADDR_STEP;
+ if (isapnp_isolate_rdp_select() < 0)
+ return -1;
+ } else if (iteration > 1) {
+ break;
+ }
+ __next:
+ checksum = 0x6a;
+ chksum = 0x00;
+ bit = 0x00;
+ }
+ return csn;
+}
+
+/*
+ * Build device list for all present ISA PnP devices.
+ */
+static int isapnp_build_device_list(void)
+{
+ int csn, device, vendor, serial;
+ unsigned char header[9], checksum;
+ for (csn = 1; csn <= 10; csn++) {
+ Wake(csn);
+ isapnp_peek(header, 9);
+ checksum = isapnp_checksum(header);
+#ifdef EDEBUG
+ printf
+ ("vendor: 0x%hX:0x%hX:0x%hX:0x%hX:0x%hX:0x%hX:0x%hX:0x%hX:0x%hX\n",
+ header[0], header[1], header[2], header[3], header[4],
+ header[5], header[6], header[7], header[8]);
+ printf("checksum = 0xhX\n", checksum);
+#endif
+ /* Don't be strict on the checksum, here !
+ e.g. 'SCM SwapBox Plug and Play' has header[8]==0 (should be: b7) */
+ if (header[8] == 0);
+ else if (checksum == 0x00 || checksum != header[8]) /* not valid CSN */
+ continue;
+
+ vendor = (header[1] << 8) | header[0];
+ device = (header[3] << 8) | header[2];
+ serial =
+ (header[7] << 24) | (header[6] << 16) | (header[5] <<
+ 8) |
+ header[4];
+ if (vendor == 0x6D50)
+ if (device == 0x5150) {
+ printf
+ ("\nFound 3Com 3c515 PNP Card!\n Vendor ID: 0x%hX, Device ID: 0x%hX, Serial Num: 0x%hX\n",
+ vendor, device, serial);
+ pnp_card_csn = csn;
+ }
+ isapnp_checksum_value = 0x00;
+ }
+ return 0;
+}
+
+int Config(int csn)
+{
+#define TIMEOUT_PNP 100
+ unsigned char id[IDENT_LEN];
+ int i, x;
+ Wake(csn);
+ udelay(1000);
+ for (i = 0; i < IDENT_LEN; i++) {
+ for (x = 1; x < TIMEOUT_PNP; x++) {
+ if (STATUS & 1)
+ break;
+ udelay(1000);
+ }
+ id[i] = RESOURCEDATA;
+#ifdef EDEBUG
+ printf(" 0x%hX ", id[i]);
+#endif
+ }
+#ifdef EDEBUG
+ printf("Got The status bit\n");
+#endif
+ /*Set Logical Device Register active */
+ LOGICALDEVICENUMBER;
+ /* Specify the first logical device */
+ WRITE_DATA(0);
+
+
+ /* Apparently just activating the card is enough
+ for Etherboot to detect it. Why bother with the
+ following code. Left in place in case it is
+ later required */
+/*==========================================*/
+ /* set DMA */
+/* ADDRESS(0x74 + 0);
+ WRITE_DATA(7); */
+
+ /*Set IRQ */
+/* udelay(1000);
+ ADDRESS(0x70 + (0 << 1));
+ WRITE_DATA(9);
+ udelay(1000); */
+/*=============================================*/
+ /*Activate */
+ ACTIVATE;
+ WRITE_DATA(1);
+ udelay(250);
+ /* Ask for access to the Wait for Key command - ConfigControl register */
+ CONFIGCONTROL;
+ /* Write the Wait for Key Command to the ConfigControl Register */
+ WRITE_DATA(CONFIG_WAIT_FOR_KEY);
+ /* As per doc. Two Write cycles of 0x00 required befor the Initialization key is sent */
+ ADDRESS(0);
+ ADDRESS(0);
+
+ return 1;
+}
+
+static void send_key(void)
+{
+ int i;
+ /* Ask for access to the Wait for Key command - ConfigControl register */
+ CONFIGCONTROL;
+ /* Write the Wait for Key Command to the ConfigControl Register */
+ WRITE_DATA(CONFIG_WAIT_FOR_KEY);
+ /* As per doc. Two Write cycles of 0x00 required befor the Initialization key is sent */
+ ADDRESS(0);
+ ADDRESS(0);
+ /* 32 writes of the initiation key to the card */
+ for (i = 0; i < INIT_LENGTH; i++)
+ ADDRESS(initdata[i]);
+}
+
+static void isapnp_peek(unsigned char *data, int bytes)
+{
+ int i, j;
+ unsigned char d = 0;
+
+ for (i = 1; i <= bytes; i++) {
+ for (j = 0; j < 20; j++) {
+ d = STATUS;
+ if (d & 1)
+ break;
+ udelay(100);
+ }
+ if (!(d & 1)) {
+ if (data != NULL)
+ *data++ = 0xff;
+ continue;
+ }
+ d = RESOURCEDATA; /* PRESDI */
+ isapnp_checksum_value += d;
+ if (data != NULL)
+ *data++ = d;
+ }
+}
+
+/*
+ * Compute ISA PnP checksum for first eight bytes.
+ */
+static unsigned char isapnp_checksum(unsigned char *data)
+{
+ int i, j;
+ unsigned char checksum = 0x6a, bit, b;
+
+ for (i = 0; i < 8; i++) {
+ b = data[i];
+ for (j = 0; j < 8; j++) {
+ bit = 0;
+ if (b & (1 << j))
+ bit = 1;
+ checksum =
+ ((((checksum ^ (checksum >> 1)) & 0x01) ^ bit)
+ << 7) | (checksum >> 1);
+ }
+ }
+ return checksum;
+}
+static int isapnp_next_rdp(void)
+{
+ int rdp = read_port;
+ while (rdp <= 0x3ff) {
+ /*
+ * We cannot use NE2000 probe spaces for ISAPnP or we
+ * will lock up machines.
+ */
+ if ((rdp < 0x280 || rdp > 0x380)) {
+ read_port = rdp;
+ return 0;
+ }
+ rdp += READ_ADDR_STEP;
+ }
+ return -1;
+}
+
+static int isapnp_isolate_rdp_select(void)
+{
+ send_key();
+ /* Control: reset CSN and conditionally everything else too */
+ CONFIGCONTROL;
+ WRITE_DATA((CONFIG_RESET_CSN | CONFIG_WAIT_FOR_KEY));
+ mdelay(2);
+
+ send_key();
+ Wake(0);
+
+ if (isapnp_next_rdp() < 0) {
+ /* Ask for access to the Wait for Key command - ConfigControl register */
+ CONFIGCONTROL;
+ /* Write the Wait for Key Command to the ConfigControl Register */
+ WRITE_DATA(CONFIG_WAIT_FOR_KEY);
+ return -1;
+ }
+
+ SetRdPort(read_port);
+ udelay(1000);
+ SERIALISOLATION;
+ udelay(1000);
+ return 0;
+}
+#endif /* CONFIG_ISA */
diff --git a/src/core/main.c b/src/core/main.c
new file mode 100644
index 00000000..e92d2929
--- /dev/null
+++ b/src/core/main.c
@@ -0,0 +1,529 @@
+/**************************************************************************
+Etherboot - Network Bootstrap Program
+
+Literature dealing with the network protocols:
+ ARP - RFC826
+ RARP - RFC903
+ UDP - RFC768
+ BOOTP - RFC951, RFC2132 (vendor extensions)
+ DHCP - RFC2131, RFC2132 (options)
+ TFTP - RFC1350, RFC2347 (options), RFC2348 (blocksize), RFC2349 (tsize)
+ RPC - RFC1831, RFC1832 (XDR), RFC1833 (rpcbind/portmapper)
+ NFS - RFC1094, RFC1813 (v3, useful for clarifications, not implemented)
+ IGMP - RFC1112
+
+**************************************************************************/
+
+/* #define MDEBUG */
+
+#include "etherboot.h"
+#include "dev.h"
+#include "nic.h"
+#include "disk.h"
+#include "http.h"
+#include "timer.h"
+#include "cpu.h"
+#include <stdarg.h>
+
+#ifdef CONSOLE_BTEXT
+#include "btext.h"
+#endif
+
+#ifdef CONFIG_FILO
+#include <lib.h>
+#endif
+
+jmp_buf restart_etherboot;
+int url_port;
+
+char as_main_program = 1;
+
+#ifdef IMAGE_FREEBSD
+int freebsd_howto = 0;
+char freebsd_kernel_env[FREEBSD_KERNEL_ENV_SIZE];
+#endif
+
+/* in_call(): the entry point to Etherboot. Generally called from
+ * arch_in_call(), which in turn will have been invoked from
+ * platform-specific assembly code.
+ */
+int in_call ( in_call_data_t *data, uint32_t opcode, va_list params ) {
+ int old_as_main_program = as_main_program;
+ int ret = 0;
+
+ /* Set flat to indicate that we are not running as the main
+ * program (i.e. we are something like a PXE stack).
+ */
+ as_main_program = 0;
+
+ /* NOTE: params will cease to be valid if we relocate, since
+ * it represents a virtual address
+ */
+ switch ( EB_OPCODE(opcode) ) {
+
+ case EB_OPCODE_CHECK:
+ /* Installation check
+ */
+ ret = EB_CHECK_RESULT;
+ break;
+ case EB_OPCODE_MAIN:
+ /* Start up Etherboot as a standalone program. */
+ as_main_program = 1;
+ ret = main ( data, params );
+ break;
+#ifdef PXE_EXPORT
+ case EB_OPCODE_PXE:
+ /* !PXE API call */
+ ret = pxe_in_call ( data, params );
+ break;
+#endif
+ default:
+ printf ( "Unsupported API \"%c%c\"\n",
+ EB_OPCODE(opcode) >> 8, EB_OPCODE(opcode) & 0xff );
+ ret = -1;
+ break;
+ }
+
+ as_main_program = old_as_main_program;
+ return ret;
+}
+
+static inline unsigned long ask_boot(unsigned *index)
+{
+ unsigned long order = DEFAULT_BOOT_ORDER;
+ *index = DEFAULT_BOOT_INDEX;
+#ifdef LINUXBIOS
+ order = get_boot_order(order, index);
+#endif
+#if defined(ASK_BOOT)
+#if ASK_BOOT >= 0
+ while(1) {
+ int c = 0;
+ printf(ASK_PROMPT);
+#if ASK_BOOT > 0
+ {
+ unsigned long time;
+ for ( time = currticks() + ASK_BOOT*TICKS_PER_SEC;
+ !c && !iskey(); ) {
+ if (currticks() > time) c = ANS_DEFAULT;
+ }
+ }
+#endif /* ASK_BOOT > 0 */
+ if ( !c ) c = getchar();
+ if ((c >= 'a') && (c <= 'z')) c &= 0x5F;
+ if ((c >= ' ') && (c <= '~')) putchar(c);
+ putchar('\n');
+
+ switch(c) {
+ default:
+ /* Nothing useful try again */
+ continue;
+ case ANS_QUIT:
+ order = BOOT_NOTHING;
+ *index = 0;
+ break;
+ case ANS_DEFAULT:
+ /* Preserve the default boot order */
+ break;
+ case ANS_NETWORK:
+ order = (BOOT_NIC << (0*BOOT_BITS)) |
+ (BOOT_NOTHING << (1*BOOT_BITS));
+ *index = 0;
+ break;
+ case ANS_DISK:
+ order = (BOOT_DISK << (0*BOOT_BITS)) |
+ (BOOT_NOTHING << (1*BOOT_BITS));
+ *index = 0;
+ break;
+ case ANS_FLOPPY:
+ order = (BOOT_FLOPPY << (0*BOOT_BITS)) |
+ (BOOT_NOTHING << (1*BOOT_BITS));
+ *index = 0;
+ break;
+ }
+ break;
+ }
+ putchar('\n');
+#endif /* ASK_BOOT >= 0 */
+#endif /* defined(ASK_BOOT) */
+ return order;
+}
+
+static inline void try_floppy_first(void)
+{
+#if (TRY_FLOPPY_FIRST > 0)
+ int i;
+ printf("Trying floppy");
+ disk_init();
+ for (i = TRY_FLOPPY_FIRST; i-- > 0; ) {
+ putchar('.');
+ if (pcbios_disk_read(0, 0, 0, 0, ((char *)FLOPPY_BOOT_LOCATION)) != 0x8000) {
+ printf("using floppy\n");
+ exit(0);
+ }
+ }
+ printf("no floppy\n");
+#endif /* TRY_FLOPPY_FIRST */
+}
+
+void console_init(void)
+{
+#ifdef CONSOLE_SERIAL
+ (void)serial_init();
+#endif
+#ifdef CONSOLE_DIRECT_VGA
+ video_init();
+#endif
+#ifdef CONSOLE_BTEXT
+ map_boot_text();
+#endif
+}
+
+static void console_fini(void)
+{
+#ifdef CONSOLE_SERIAL
+ (void)serial_fini();
+#endif
+}
+
+static struct class_operations {
+ struct dev *dev;
+ int (*probe)(struct dev *dev);
+ int (*load_configuration)(struct dev *dev);
+ int (*load)(struct dev *dev);
+}
+operations[] = {
+ { &nic.dev, eth_probe, eth_load_configuration, eth_load },
+ { &disk.dev, disk_probe, disk_load_configuration, disk_load },
+ { &disk.dev, disk_probe, disk_load_configuration, disk_load },
+};
+
+
+static int main_loop(int state);
+static int exit_ok;
+static int exit_status;
+static int initialized;
+
+/**************************************************************************
+MAIN - Kick off routine
+**************************************************************************/
+int main(in_call_data_t *data, va_list params)
+{
+ char *p;
+ int state;
+
+ for (p = _bss; p < _ebss; p++)
+ *p = 0; /* Zero BSS */
+
+ console_init();
+ arch_main(data,params);
+
+#if 0
+#ifdef CONSOLE_BTEXT
+ btext_init();
+ map_boot_text();
+ btext_clearscreen();
+#endif
+#endif
+
+ if ( rom.rom_segment ) {
+ printf ( "ROM segment %#hx length %#hx reloc %#x\n",
+ rom.rom_segment, rom.rom_length, _text );
+ }
+
+ cpu_setup();
+ setup_timers();
+ gateA20_set();
+ print_config();
+ get_memsizes();
+ cleanup();
+
+#ifdef CONFIG_PCMCIA
+ pcmcia_init_all();
+#endif
+
+ /* -1: timeout or ESC
+ -2: error return from loader
+ -3: finish the current run.
+ 0: retry booting bootp and tftp
+ 1: retry tftp with possibly modified bootp reply
+ 2: retry bootp and tftp
+ 3: retry probe bootp and tftp
+ 4: start with the next device and retry from there...
+ 255: exit Etherboot
+ 256: retry after relocation
+ */
+ state = setjmp(restart_etherboot);
+ exit_ok = 1;
+ for(;state != 255;) {
+ state = main_loop(state);
+ }
+ arch_on_exit(exit_status);
+#ifdef CONFIG_PCMCIA
+ pcmcia_shutdown_all();
+#endif
+ return exit_status;
+}
+
+void exit(int status)
+{
+ while(!exit_ok)
+ ;
+ exit_status = status;
+ longjmp(restart_etherboot, 255);
+}
+
+static int main_loop(int state)
+{
+ /* Splitting main into 2 pieces makes the semantics of
+ * which variables are preserved across a longjmp clean
+ * and predictable.
+ */
+ static unsigned long order;
+ static unsigned boot_index;
+ static struct dev * dev = 0;
+ static struct class_operations *ops;
+ static void *heap_base;
+ static int type;
+ static int i;
+
+ if (!initialized) {
+ initialized = 1;
+ console_init();
+ if (dev && (state >= 1) && (state <= 2)) {
+ dev->how_probe = PROBE_AWAKE;
+ dev->how_probe = ops->probe(dev);
+ if (dev->how_probe == PROBE_FAILED) {
+ state = -1;
+ }
+ }
+ }
+ switch(state) {
+ case 0:
+ {
+ static int firsttime = 1;
+ /* First time through */
+ if (firsttime) {
+ relocate();
+ cleanup();
+ console_init();
+ init_heap();
+#ifdef CONSOLE_BTEXT
+ //I need to all allot
+ btext_init();
+ map_boot_text();
+ btext_clearscreen();
+#else
+ #ifdef CONFIG_FILO
+ pci_init();
+ #endif
+#endif
+
+ firsttime = 0;
+ }
+#ifdef EXIT_IF_NO_OFFER
+ else {
+ cleanup();
+ exit(0);
+ }
+#endif
+ heap_base = allot(0);
+ i = -1;
+ state = 4;
+ dev = 0;
+
+ /* We just called setjmp ... */
+ order = ask_boot(&boot_index);
+ try_floppy_first();
+ break;
+ }
+ case 4:
+ cleanup();
+ console_init();
+ forget(heap_base);
+ /* Find a dev entry to probe with */
+ if (!dev) {
+ int boot;
+ int failsafe;
+
+ /* Advance to the next device type */
+ i++;
+ boot = (order >> (i * BOOT_BITS)) & BOOT_MASK;
+ type = boot & BOOT_TYPE_MASK;
+ failsafe = (boot & BOOT_FAILSAFE) != 0;
+ if (i >= MAX_BOOT_ENTRIES) {
+ type = BOOT_NOTHING;
+ }
+ if ((i == 0) && (type == BOOT_NOTHING)) {
+ /* Return to caller */
+ exit(0);
+ }
+ if (type >= BOOT_NOTHING) {
+ interruptible_sleep(2);
+ state = 0;
+ break;
+ }
+ ops = &operations[type];
+ dev = ops->dev;
+ dev->how_probe = PROBE_FIRST;
+ dev->type = type;
+ dev->failsafe = failsafe;
+ dev->type_index = 0;
+ } else {
+ /* Advance to the next device of the same type */
+ dev->how_probe = PROBE_NEXT;
+ }
+ state = 3;
+ break;
+ case 3:
+ state = -1;
+ heap_base = allot(0);
+ dev->how_probe = ops->probe(dev);
+ if (dev->how_probe == PROBE_FAILED) {
+ dev = 0;
+ state = 4;
+ } else if (boot_index && (i == 0) && (boot_index != (unsigned)dev->type_index)) {
+ printf("Wrong index\n");
+ state = 4;
+ }
+ else {
+ state = 2;
+ }
+ break;
+ case 2:
+ state = -1;
+ if (ops->load_configuration(dev) >= 0) {
+ state = 1;
+ }
+ break;
+ case 1:
+ /* Any return from load is a failure */
+ ops->load(dev);
+ state = -1;
+ break;
+ case 256:
+ state = 0;
+ break;
+ case -3:
+ i = MAX_BOOT_ENTRIES;
+ type = BOOT_NOTHING;
+ /* fall through */
+ default:
+ printf("<abort>\n");
+ state = 4;
+ /* At the end goto state 0 */
+ if ((type >= BOOT_NOTHING) || (i >= MAX_BOOT_ENTRIES)) {
+ state = 0;
+ }
+ break;
+ }
+ return state;
+}
+
+
+/**************************************************************************
+LOADKERNEL - Try to load kernel image
+**************************************************************************/
+struct proto {
+ char *name;
+ int (*load)(const char *name,
+ int (*fnc)(unsigned char *, unsigned int, unsigned int, int));
+};
+static const struct proto protos[] = {
+#ifdef DOWNLOAD_PROTO_TFTM
+ { "x-tftm", url_tftm },
+#endif
+#ifdef DOWNLOAD_PROTO_SLAM
+ { "x-slam", url_slam },
+#endif
+#ifdef DOWNLOAD_PROTO_NFS
+ { "nfs", nfs },
+#endif
+#ifdef DOWNLOAD_PROTO_DISK
+ { "file", url_file },
+#endif
+#ifdef DOWNLOAD_PROTO_TFTP
+ { "tftp", tftp },
+#endif
+#ifdef DOWNLOAD_PROTO_HTTP
+ { "http", http },
+#endif
+};
+
+int loadkernel(const char *fname)
+{
+ static const struct proto * const last_proto =
+ &protos[sizeof(protos)/sizeof(protos[0])];
+ const struct proto *proto;
+ in_addr ip;
+ int len;
+ const char *name;
+#ifdef DNS_RESOLVER
+ const char *resolvt;
+#endif
+ ip.s_addr = arptable[ARP_SERVER].ipaddr.s_addr;
+ name = fname;
+ url_port = -1;
+ len = 0;
+ while(fname[len] && fname[len] != ':') {
+ len++;
+ }
+ for(proto = &protos[0]; proto < last_proto; proto++) {
+ if (memcmp(name, proto->name, len) == 0) {
+ break;
+ }
+ }
+ if ((proto < last_proto) && (memcmp(fname + len, "://", 3) == 0)) {
+ name += len + 3;
+ if (name[0] != '/') {
+#ifdef DNS_RESOLVER
+ resolvt = dns_resolver ( name );
+ if ( NULL != resolvt ) {
+ //printf ("Resolved host name [%s] to [%s]\n",
+ // name, resolvt );
+ inet_aton(resolvt, &ip);
+ while ( ( '/' != name[0] ) && ( 0 != name[0]))
+ ++name;
+ } else
+#endif /* DNS_RESOLVER */
+ name += inet_aton(name, &ip);
+ if (name[0] == ':') {
+ name++;
+ url_port = strtoul(name, &name, 10);
+ }
+ }
+ if (name[0] == '/') {
+ arptable[ARP_SERVER].ipaddr.s_addr = ip.s_addr;
+ printf( "Loading %s ", fname );
+ return proto->load(name + 1, load_block);
+ }
+ }
+ printf("Loading %@:%s ", arptable[ARP_SERVER].ipaddr, fname);
+#ifdef DEFAULT_PROTO_NFS
+ return nfs(fname, load_block);
+#else
+ return tftp(fname, load_block);
+#endif
+}
+
+
+/**************************************************************************
+CLEANUP - shut down networking and console so that the OS may be called
+**************************************************************************/
+void cleanup(void)
+{
+#ifdef DOWNLOAD_PROTO_NFS
+ nfs_umountall(ARP_SERVER);
+#endif
+ /* Stop receiving packets */
+ eth_disable();
+ disk_disable();
+ console_fini();
+ initialized = 0;
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/src/core/misc.c b/src/core/misc.c
new file mode 100644
index 00000000..f5ce9d8f
--- /dev/null
+++ b/src/core/misc.c
@@ -0,0 +1,415 @@
+/**************************************************************************
+MISC Support Routines
+**************************************************************************/
+
+#include "etherboot.h"
+#ifdef CONSOLE_BTEXT
+#include <btext.h>
+#endif
+#ifdef CONSOLE_PC_KBD
+#include <pc_kbd.h>
+#endif
+
+
+/**************************************************************************
+IPCHKSUM - Checksum IP Header
+**************************************************************************/
+uint16_t ipchksum(const void *data, unsigned long length)
+{
+ unsigned long sum;
+ unsigned long i;
+ const uint8_t *ptr;
+
+ /* In the most straight forward way possible,
+ * compute an ip style checksum.
+ */
+ sum = 0;
+ ptr = data;
+ for(i = 0; i < length; i++) {
+ unsigned long value;
+ value = ptr[i];
+ if (i & 1) {
+ value <<= 8;
+ }
+ /* Add the new value */
+ sum += value;
+ /* Wrap around the carry */
+ if (sum > 0xFFFF) {
+ sum = (sum + (sum >> 16)) & 0xFFFF;
+ }
+ }
+ return (~cpu_to_le16(sum)) & 0xFFFF;
+}
+
+uint16_t add_ipchksums(unsigned long offset, uint16_t sum, uint16_t new)
+{
+ unsigned long checksum;
+ sum = ~sum & 0xFFFF;
+ new = ~new & 0xFFFF;
+ if (offset & 1) {
+ /* byte swap the sum if it came from an odd offset
+ * since the computation is endian independant this
+ * works.
+ */
+ new = bswap_16(new);
+ }
+ checksum = sum + new;
+ if (checksum > 0xFFFF) {
+ checksum -= 0xFFFF;
+ }
+ return (~checksum) & 0xFFFF;
+}
+
+
+
+/**************************************************************************
+RANDOM - compute a random number between 0 and 2147483647L or 2147483562?
+**************************************************************************/
+int32_t random(void)
+{
+ static int32_t seed = 0;
+ int32_t q;
+ if (!seed) /* Initialize linear congruential generator */
+ seed = currticks() + *(int32_t *)&arptable[ARP_CLIENT].node
+ + ((int16_t *)arptable[ARP_CLIENT].node)[2];
+ /* simplified version of the LCG given in Bruce Schneier's
+ "Applied Cryptography" */
+ q = seed/53668;
+ if ((seed = 40014*(seed-53668*q) - 12211*q) < 0) seed += 2147483563L;
+ return seed;
+}
+
+/**************************************************************************
+POLL INTERRUPTIONS
+**************************************************************************/
+void poll_interruptions(void)
+{
+ int ch;
+ if ( ! as_main_program ) return;
+ /* If an interruption has occured restart etherboot */
+ if (iskey() && (ch = getchar(), (ch == K_ESC) || (ch == K_EOF) || (ch == K_INTR))) {
+ int state = (ch != K_INTR)? -1 : -3;
+ longjmp(restart_etherboot, state);
+ }
+}
+
+/**************************************************************************
+SLEEP
+**************************************************************************/
+void sleep(int secs)
+{
+ unsigned long tmo;
+
+ for (tmo = currticks()+secs*TICKS_PER_SEC; currticks() < tmo; ) {
+ poll_interruptions();
+ }
+}
+
+/**************************************************************************
+INTERRUPTIBLE SLEEP
+**************************************************************************/
+void interruptible_sleep(int secs)
+{
+ printf("<sleep>\n");
+ return sleep(secs);
+}
+
+/**************************************************************************
+TWIDDLE
+**************************************************************************/
+void twiddle(void)
+{
+#ifdef BAR_PROGRESS
+ static int count=0;
+ static const char tiddles[]="-\\|/";
+ static unsigned long lastticks = 0;
+ unsigned long ticks;
+#endif
+ if ( ! as_main_program ) return;
+#ifdef BAR_PROGRESS
+ /* Limit the maximum rate at which characters are printed */
+ ticks = currticks();
+ if ((lastticks + (TICKS_PER_SEC/18)) > ticks)
+ return;
+ lastticks = ticks;
+
+ putchar(tiddles[(count++)&3]);
+ putchar('\b');
+#else
+ putchar('.');
+#endif /* BAR_PROGRESS */
+}
+
+/**************************************************************************
+STRCASECMP (not entirely correct, but this will do for our purposes)
+**************************************************************************/
+int strcasecmp(const char *a, const char *b)
+{
+ while (*a && *b && (*a & ~0x20) == (*b & ~0x20)) {a++; b++; }
+ return((*a & ~0x20) - (*b & ~0x20));
+}
+
+/**************************************************************************
+INET_ATON - Convert an ascii x.x.x.x to binary form
+**************************************************************************/
+int inet_aton(const char *start, in_addr *i)
+{
+ const char *p = start;
+ const char *digits_start;
+ unsigned long ip = 0;
+ unsigned long val;
+ int j;
+ for(j = 0; j <= 3; j++) {
+ digits_start = p;
+ val = strtoul(p, &p, 10);
+ if ((p == digits_start) || (val > 255)) return 0;
+ if ( ( j < 3 ) && ( *(p++) != '.' ) ) return 0;
+ ip = (ip << 8) | val;
+ }
+ i->s_addr = htonl(ip);
+ return p - start;
+}
+
+
+unsigned long strtoul(const char *p, const char **endp, int base)
+{
+ unsigned long ret = 0;
+ if (base != 10) return 0;
+ while((*p >= '0') && (*p <= '9')) {
+ ret = ret*10 + (*p - '0');
+ p++;
+ }
+ if (endp)
+ *endp = p;
+ return(ret);
+
+}
+
+#define K_RDWR 0x60 /* keyboard data & cmds (read/write) */
+#define K_STATUS 0x64 /* keyboard status */
+#define K_CMD 0x64 /* keybd ctlr command (write-only) */
+
+#define K_OBUF_FUL 0x01 /* output buffer full */
+#define K_IBUF_FUL 0x02 /* input buffer full */
+
+#define KC_CMD_WIN 0xd0 /* read output port */
+#define KC_CMD_WOUT 0xd1 /* write output port */
+#define KB_SET_A20 0xdf /* enable A20,
+ enable output buffer full interrupt
+ enable data line
+ disable clock line */
+#define KB_UNSET_A20 0xdd /* enable A20,
+ enable output buffer full interrupt
+ enable data line
+ disable clock line */
+
+enum { Disable_A20 = 0x2400, Enable_A20 = 0x2401, Query_A20_Status = 0x2402,
+ Query_A20_Support = 0x2403 };
+
+#if defined(PCBIOS) && !defined(IBM_L40)
+static void empty_8042(void)
+{
+ unsigned long time;
+ char st;
+
+ time = currticks() + TICKS_PER_SEC; /* max wait of 1 second */
+ while ((((st = inb(K_CMD)) & K_OBUF_FUL) ||
+ (st & K_IBUF_FUL)) &&
+ currticks() < time)
+ inb(K_RDWR);
+}
+#endif /* IBM_L40 */
+
+#if defined(PCBIOS)
+/*
+ * Gate A20 for high memory
+ */
+void gateA20_set(void)
+{
+#warning "gateA20_set should test to see if it is already set"
+ if (int15(Enable_A20) == 0) {
+ return;
+ }
+#ifdef IBM_L40
+ outb(0x2, 0x92);
+#else /* IBM_L40 */
+ empty_8042();
+ outb(KC_CMD_WOUT, K_CMD);
+ empty_8042();
+ outb(KB_SET_A20, K_RDWR);
+ empty_8042();
+#endif /* IBM_L40 */
+}
+#endif
+
+
+int last_putchar; // From filo
+
+void
+putchar(int c)
+{
+ c &= 0xff;
+ last_putchar = c;
+
+ if (c == '\n')
+ putchar('\r');
+#ifdef CONSOLE_FIRMWARE
+ console_putc(c);
+#endif
+#ifdef CONSOLE_DIRECT_VGA
+ vga_putc(c);
+#endif
+#ifdef CONSOLE_BTEXT
+ btext_putc(c);
+#endif
+#ifdef CONSOLE_SERIAL
+ serial_putc(c);
+#endif
+}
+
+/**************************************************************************
+GETCHAR - Read the next character from input device WITHOUT ECHO
+**************************************************************************/
+int getchar(void)
+{
+ int c = 256;
+
+ do {
+#if defined(PCBIOS) && defined(POWERSAVE)
+ /* Doze for a while (until the next interrupt). This works
+ * fine, because the keyboard is interrupt-driven, and the
+ * timer interrupt (approx. every 50msec) takes care of the
+ * serial port, which is read by polling. This reduces the
+ * power dissipation of a modern CPU considerably, and also
+ * makes Etherboot waiting for user interaction waste a lot
+ * less CPU time in a VMware session. */
+ cpu_nap();
+#endif /* POWERSAVE */
+#ifdef CONSOLE_FIRMWARE
+ if (console_ischar())
+ c = console_getc();
+#endif
+#ifdef CONSOLE_SERIAL
+ if (serial_ischar())
+ c = serial_getc();
+#endif
+#ifdef CONSOLE_PC_KBD
+ if (kbd_ischar())
+ c = kbd_getc();
+#endif
+ } while (c==256);
+ if (c == '\r')
+ c = '\n';
+ return c;
+}
+
+int iskey(void)
+{
+#ifdef CONSOLE_FIRMWARE
+ if (console_ischar())
+ return 1;
+#endif
+#ifdef CONSOLE_SERIAL
+ if (serial_ischar())
+ return 1;
+#endif
+#ifdef CONSOLE_PC_KBD
+ if (kbd_ischar())
+ return 1;
+#endif
+ return 0;
+}
+
+#if DEBUG_UTILS
+
+void pause ( void ) {
+ printf ( "\nPress a key" );
+ getchar();
+ printf ( "\r \r" );
+}
+
+void more ( void ) {
+ printf ( "---more---" );
+ getchar();
+ printf ( "\r \r" );
+}
+
+/* Produce a paged hex dump of the specified data and length */
+void hex_dump ( const char *data, const unsigned int len ) {
+ unsigned int index;
+ for ( index = 0; index < len; index++ ) {
+ if ( ( index % 16 ) == 0 ) {
+ printf ( "\n" );
+ }
+ if ( ( index % 368 ) == 352 ) {
+ more();
+ }
+ if ( ( index % 16 ) == 0 ) {
+ printf ( "%X [%X] : %hX :", data + index,
+ virt_to_phys ( data + index ), index );
+ }
+ printf ( " %hhX", data[index] );
+ }
+ printf ( "\n" );
+}
+
+#define GUARD_SYMBOL ( ( 'M' << 24 ) | ( 'I' << 16 ) | ( 'N' << 8 ) | 'E' )
+/* Fill a region with guard markers. We use a 4-byte pattern to make
+ * it less likely that check_region will find spurious 1-byte regions
+ * of non-corruption.
+ */
+void guard_region ( void *region, size_t len ) {
+ uint32_t offset = 0;
+
+ len &= ~0x03;
+ for ( offset = 0; offset < len ; offset += 4 ) {
+ *((uint32_t *)(region + offset)) = GUARD_SYMBOL;
+ }
+}
+
+/* Check a region that has been guarded with guard_region() for
+ * corruption.
+ */
+int check_region ( void *region, size_t len ) {
+ uint8_t corrupted = 0;
+ uint8_t in_corruption = 0;
+ uint32_t offset = 0;
+ uint32_t test = 0;
+
+ len &= ~0x03;
+ for ( offset = 0; offset < len ; offset += 4 ) {
+ test = *((uint32_t *)(region + offset)) = GUARD_SYMBOL;
+ if ( ( in_corruption == 0 ) &&
+ ( test != GUARD_SYMBOL ) ) {
+ /* Start of corruption */
+ if ( corrupted == 0 ) {
+ corrupted = 1;
+ printf ( "Region %#x-%#x (physical %#x-%#x) "
+ "corrupted\n",
+ region, region + len,
+ virt_to_phys ( region ),
+ virt_to_phys ( region + len ) );
+ }
+ in_corruption = 1;
+ printf ( "--- offset %#x ", offset );
+ } else if ( ( in_corruption != 0 ) &&
+ ( test == GUARD_SYMBOL ) ) {
+ /* End of corruption */
+ in_corruption = 0;
+ printf ( "to offset %#x", offset );
+ }
+
+ }
+ if ( in_corruption != 0 ) {
+ printf ( "to offset %#x (end of region)\n", len-1 );
+ }
+ return corrupted;
+}
+
+#endif /* DEBUG_UTILS */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/src/core/nfs.c b/src/core/nfs.c
new file mode 100644
index 00000000..cbda6ab7
--- /dev/null
+++ b/src/core/nfs.c
@@ -0,0 +1,610 @@
+#ifdef DOWNLOAD_PROTO_NFS
+
+#include "etherboot.h"
+#include "nic.h"
+
+/* NOTE: the NFS code is heavily inspired by the NetBSD netboot code (read:
+ * large portions are copied verbatim) as distributed in OSKit 0.97. A few
+ * changes were necessary to adapt the code to Etherboot and to fix several
+ * inconsistencies. Also the RPC message preparation is done "by hand" to
+ * avoid adding netsprintf() which I find hard to understand and use. */
+
+/* NOTE 2: Etherboot does not care about things beyond the kernel image, so
+ * it loads the kernel image off the boot server (ARP_SERVER) and does not
+ * access the client root disk (root-path in dhcpd.conf), which would use
+ * ARP_ROOTSERVER. The root disk is something the operating system we are
+ * about to load needs to use. This is different from the OSKit 0.97 logic. */
+
+/* NOTE 3: Symlink handling introduced by Anselm M Hoffmeister, 2003-July-14
+ * If a symlink is encountered, it is followed as far as possible (recursion
+ * possible, maximum 16 steps). There is no clearing of ".."'s inside the
+ * path, so please DON'T DO THAT. thx. */
+
+#define START_OPORT 700 /* mountd usually insists on secure ports */
+#define OPORT_SWEEP 200 /* make sure we don't leave secure range */
+
+static int oport = START_OPORT;
+static int mount_port = -1;
+static int nfs_port = -1;
+static int fs_mounted = 0;
+static unsigned long rpc_id;
+
+/**************************************************************************
+RPC_INIT - set up the ID counter to something fairly random
+**************************************************************************/
+void rpc_init(void)
+{
+ unsigned long t;
+
+ t = currticks();
+ rpc_id = t ^ (t << 8) ^ (t << 16);
+}
+
+
+/**************************************************************************
+RPC_PRINTERROR - Print a low level RPC error message
+**************************************************************************/
+static void rpc_printerror(struct rpc_t *rpc)
+{
+ if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
+ rpc->u.reply.astatus) {
+ /* rpc_printerror() is called for any RPC related error,
+ * suppress output if no low level RPC error happened. */
+ printf("RPC error: (%d,%d,%d)\n", ntohl(rpc->u.reply.rstatus),
+ ntohl(rpc->u.reply.verifier),
+ ntohl(rpc->u.reply.astatus));
+ }
+}
+
+/**************************************************************************
+AWAIT_RPC - Wait for an rpc packet
+**************************************************************************/
+static int await_rpc(int ival, void *ptr,
+ unsigned short ptype, struct iphdr *ip, struct udphdr *udp)
+{
+ struct rpc_t *rpc;
+ if (!udp)
+ return 0;
+ if (arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr)
+ return 0;
+ if (ntohs(udp->dest) != ival)
+ return 0;
+ if (nic.packetlen < ETH_HLEN + sizeof(struct iphdr) + sizeof(struct udphdr) + 8)
+ return 0;
+ rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
+ if (*(unsigned long *)ptr != ntohl(rpc->u.reply.id))
+ return 0;
+ if (MSG_REPLY != ntohl(rpc->u.reply.type))
+ return 0;
+ return 1;
+}
+
+/**************************************************************************
+RPC_LOOKUP - Lookup RPC Port numbers
+**************************************************************************/
+static int rpc_lookup(int addr, int prog, int ver, int sport)
+{
+ struct rpc_t buf, *rpc;
+ unsigned long id;
+ int retries;
+ long *p;
+
+ id = rpc_id++;
+ buf.u.call.id = htonl(id);
+ buf.u.call.type = htonl(MSG_CALL);
+ buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
+ buf.u.call.prog = htonl(PROG_PORTMAP);
+ buf.u.call.vers = htonl(2); /* portmapper is version 2 */
+ buf.u.call.proc = htonl(PORTMAP_GETPORT);
+ p = (long *)buf.u.call.data;
+ *p++ = 0; *p++ = 0; /* auth credential */
+ *p++ = 0; *p++ = 0; /* auth verifier */
+ *p++ = htonl(prog);
+ *p++ = htonl(ver);
+ *p++ = htonl(IP_UDP);
+ *p++ = 0;
+ for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
+ long timeout;
+ udp_transmit(arptable[addr].ipaddr.s_addr, sport, SUNRPC_PORT,
+ (char *)p - (char *)&buf, &buf);
+ timeout = rfc2131_sleep_interval(TIMEOUT, retries);
+ if (await_reply(await_rpc, sport, &id, timeout)) {
+ rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
+ if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
+ rpc->u.reply.astatus) {
+ rpc_printerror(rpc);
+ return -1;
+ } else {
+ return ntohl(rpc->u.reply.data[0]);
+ }
+ }
+ }
+ return -1;
+}
+
+/**************************************************************************
+RPC_ADD_CREDENTIALS - Add RPC authentication/verifier entries
+**************************************************************************/
+static long *rpc_add_credentials(long *p)
+{
+ int hl;
+
+ /* Here's the executive summary on authentication requirements of the
+ * various NFS server implementations: Linux accepts both AUTH_NONE
+ * and AUTH_UNIX authentication (also accepts an empty hostname field
+ * in the AUTH_UNIX scheme). *BSD refuses AUTH_NONE, but accepts
+ * AUTH_UNIX (also accepts an empty hostname field in the AUTH_UNIX
+ * scheme). To be safe, use AUTH_UNIX and pass the hostname if we have
+ * it (if the BOOTP/DHCP reply didn't give one, just use an empty
+ * hostname). */
+
+ hl = (hostnamelen + 3) & ~3;
+
+ /* Provide an AUTH_UNIX credential. */
+ *p++ = htonl(1); /* AUTH_UNIX */
+ *p++ = htonl(hl+20); /* auth length */
+ *p++ = htonl(0); /* stamp */
+ *p++ = htonl(hostnamelen); /* hostname string */
+ if (hostnamelen & 3) {
+ *(p + hostnamelen / 4) = 0; /* add zero padding */
+ }
+ memcpy(p, hostname, hostnamelen);
+ p += hl / 4;
+ *p++ = 0; /* uid */
+ *p++ = 0; /* gid */
+ *p++ = 0; /* auxiliary gid list */
+
+ /* Provide an AUTH_NONE verifier. */
+ *p++ = 0; /* AUTH_NONE */
+ *p++ = 0; /* auth length */
+
+ return p;
+}
+
+/**************************************************************************
+NFS_PRINTERROR - Print a NFS error message
+**************************************************************************/
+static void nfs_printerror(int err)
+{
+ switch (-err) {
+ case NFSERR_PERM:
+ printf("Not owner\n");
+ break;
+ case NFSERR_NOENT:
+ printf("No such file or directory\n");
+ break;
+ case NFSERR_ACCES:
+ printf("Permission denied\n");
+ break;
+ case NFSERR_ISDIR:
+ printf("Directory given where filename expected\n");
+ break;
+ case NFSERR_INVAL:
+ printf("Invalid filehandle\n");
+ break; // INVAL is not defined in NFSv2, some NFS-servers
+ // seem to use it in answers to v2 nevertheless.
+ case 9998:
+ printf("low-level RPC failure (parameter decoding problem?)\n");
+ break;
+ case 9999:
+ printf("low-level RPC failure (authentication problem?)\n");
+ break;
+ default:
+ printf("Unknown NFS error %d\n", -err);
+ }
+}
+
+/**************************************************************************
+NFS_MOUNT - Mount an NFS Filesystem
+**************************************************************************/
+static int nfs_mount(int server, int port, char *path, char *fh, int sport)
+{
+ struct rpc_t buf, *rpc;
+ unsigned long id;
+ int retries;
+ long *p;
+ int pathlen = strlen(path);
+
+ id = rpc_id++;
+ buf.u.call.id = htonl(id);
+ buf.u.call.type = htonl(MSG_CALL);
+ buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
+ buf.u.call.prog = htonl(PROG_MOUNT);
+ buf.u.call.vers = htonl(1); /* mountd is version 1 */
+ buf.u.call.proc = htonl(MOUNT_ADDENTRY);
+ p = rpc_add_credentials((long *)buf.u.call.data);
+ *p++ = htonl(pathlen);
+ if (pathlen & 3) {
+ *(p + pathlen / 4) = 0; /* add zero padding */
+ }
+ memcpy(p, path, pathlen);
+ p += (pathlen + 3) / 4;
+ for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
+ long timeout;
+ udp_transmit(arptable[server].ipaddr.s_addr, sport, port,
+ (char *)p - (char *)&buf, &buf);
+ timeout = rfc2131_sleep_interval(TIMEOUT, retries);
+ if (await_reply(await_rpc, sport, &id, timeout)) {
+ rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
+ if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
+ rpc->u.reply.astatus || rpc->u.reply.data[0]) {
+ rpc_printerror(rpc);
+ if (rpc->u.reply.rstatus) {
+ /* RPC failed, no verifier, data[0] */
+ return -9999;
+ }
+ if (rpc->u.reply.astatus) {
+ /* RPC couldn't decode parameters */
+ return -9998;
+ }
+ return -ntohl(rpc->u.reply.data[0]);
+ } else {
+ fs_mounted = 1;
+ memcpy(fh, rpc->u.reply.data + 1, NFS_FHSIZE);
+ return 0;
+ }
+ }
+ }
+ return -1;
+}
+
+/**************************************************************************
+NFS_UMOUNTALL - Unmount all our NFS Filesystems on the Server
+**************************************************************************/
+void nfs_umountall(int server)
+{
+ struct rpc_t buf, *rpc;
+ unsigned long id;
+ int retries;
+ long *p;
+
+ if (!arptable[server].ipaddr.s_addr) {
+ /* Haven't sent a single UDP packet to this server */
+ return;
+ }
+ if ((mount_port == -1) || (!fs_mounted)) {
+ /* Nothing mounted, nothing to umount */
+ return;
+ }
+ id = rpc_id++;
+ buf.u.call.id = htonl(id);
+ buf.u.call.type = htonl(MSG_CALL);
+ buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
+ buf.u.call.prog = htonl(PROG_MOUNT);
+ buf.u.call.vers = htonl(1); /* mountd is version 1 */
+ buf.u.call.proc = htonl(MOUNT_UMOUNTALL);
+ p = rpc_add_credentials((long *)buf.u.call.data);
+ for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
+ long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
+ udp_transmit(arptable[server].ipaddr.s_addr, oport, mount_port,
+ (char *)p - (char *)&buf, &buf);
+ if (await_reply(await_rpc, oport, &id, timeout)) {
+ rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
+ if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
+ rpc->u.reply.astatus) {
+ rpc_printerror(rpc);
+ }
+ fs_mounted = 0;
+ return;
+ }
+ }
+}
+/***************************************************************************
+ * NFS_READLINK (AH 2003-07-14)
+ * This procedure is called when read of the first block fails -
+ * this probably happens when it's a directory or a symlink
+ * In case of successful readlink(), the dirname is manipulated,
+ * so that inside the nfs() function a recursion can be done.
+ **************************************************************************/
+static int nfs_readlink(int server, int port, char *fh, char *path, char *nfh,
+ int sport)
+{
+ struct rpc_t buf, *rpc;
+ unsigned long id;
+ long *p;
+ int retries;
+ int pathlen = strlen(path);
+
+ id = rpc_id++;
+ buf.u.call.id = htonl(id);
+ buf.u.call.type = htonl(MSG_CALL);
+ buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
+ buf.u.call.prog = htonl(PROG_NFS);
+ buf.u.call.vers = htonl(2); /* nfsd is version 2 */
+ buf.u.call.proc = htonl(NFS_READLINK);
+ p = rpc_add_credentials((long *)buf.u.call.data);
+ memcpy(p, nfh, NFS_FHSIZE);
+ p += (NFS_FHSIZE / 4);
+ for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
+ long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
+ udp_transmit(arptable[server].ipaddr.s_addr, sport, port,
+ (char *)p - (char *)&buf, &buf);
+ if (await_reply(await_rpc, sport, &id, timeout)) {
+ rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
+ if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
+ rpc->u.reply.astatus || rpc->u.reply.data[0]) {
+ rpc_printerror(rpc);
+ if (rpc->u.reply.rstatus) {
+ /* RPC failed, no verifier, data[0] */
+ return -9999;
+ }
+ if (rpc->u.reply.astatus) {
+ /* RPC couldn't decode parameters */
+ return -9998;
+ }
+ return -ntohl(rpc->u.reply.data[0]);
+ } else {
+ // It *is* a link.
+ // If it's a relative link, append everything to dirname, filename TOO!
+ retries = strlen ( (char *)(&(rpc->u.reply.data[2]) ));
+ if ( *((char *)(&(rpc->u.reply.data[2]))) != '/' ) {
+ path[pathlen++] = '/';
+ while ( ( retries + pathlen ) > 298 ) {
+ retries--;
+ }
+ if ( retries > 0 ) {
+ memcpy(path + pathlen, &(rpc->u.reply.data[2]), retries + 1);
+ } else { retries = 0; }
+ path[pathlen + retries] = 0;
+ } else {
+ // Else make it the only path.
+ if ( retries > 298 ) { retries = 298; }
+ memcpy ( path, &(rpc->u.reply.data[2]), retries + 1 );
+ path[retries] = 0;
+ }
+ return 0;
+ }
+ }
+ }
+ return -1;
+}
+/**************************************************************************
+NFS_LOOKUP - Lookup Pathname
+**************************************************************************/
+static int nfs_lookup(int server, int port, char *fh, char *path, char *nfh,
+ int sport)
+{
+ struct rpc_t buf, *rpc;
+ unsigned long id;
+ long *p;
+ int retries;
+ int pathlen = strlen(path);
+
+ id = rpc_id++;
+ buf.u.call.id = htonl(id);
+ buf.u.call.type = htonl(MSG_CALL);
+ buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
+ buf.u.call.prog = htonl(PROG_NFS);
+ buf.u.call.vers = htonl(2); /* nfsd is version 2 */
+ buf.u.call.proc = htonl(NFS_LOOKUP);
+ p = rpc_add_credentials((long *)buf.u.call.data);
+ memcpy(p, fh, NFS_FHSIZE);
+ p += (NFS_FHSIZE / 4);
+ *p++ = htonl(pathlen);
+ if (pathlen & 3) {
+ *(p + pathlen / 4) = 0; /* add zero padding */
+ }
+ memcpy(p, path, pathlen);
+ p += (pathlen + 3) / 4;
+ for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
+ long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
+ udp_transmit(arptable[server].ipaddr.s_addr, sport, port,
+ (char *)p - (char *)&buf, &buf);
+ if (await_reply(await_rpc, sport, &id, timeout)) {
+ rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
+ if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
+ rpc->u.reply.astatus || rpc->u.reply.data[0]) {
+ rpc_printerror(rpc);
+ if (rpc->u.reply.rstatus) {
+ /* RPC failed, no verifier, data[0] */
+ return -9999;
+ }
+ if (rpc->u.reply.astatus) {
+ /* RPC couldn't decode parameters */
+ return -9998;
+ }
+ return -ntohl(rpc->u.reply.data[0]);
+ } else {
+ memcpy(nfh, rpc->u.reply.data + 1, NFS_FHSIZE);
+ return 0;
+ }
+ }
+ }
+ return -1;
+}
+
+/**************************************************************************
+NFS_READ - Read File on NFS Server
+**************************************************************************/
+static int nfs_read(int server, int port, char *fh, int offset, int len,
+ int sport)
+{
+ struct rpc_t buf, *rpc;
+ unsigned long id;
+ int retries;
+ long *p;
+
+ static int tokens=0;
+ /*
+ * Try to implement something similar to a window protocol in
+ * terms of response to losses. On successful receive, increment
+ * the number of tokens by 1 (cap at 256). On failure, halve it.
+ * When the number of tokens is >= 2, use a very short timeout.
+ */
+
+ id = rpc_id++;
+ buf.u.call.id = htonl(id);
+ buf.u.call.type = htonl(MSG_CALL);
+ buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
+ buf.u.call.prog = htonl(PROG_NFS);
+ buf.u.call.vers = htonl(2); /* nfsd is version 2 */
+ buf.u.call.proc = htonl(NFS_READ);
+ p = rpc_add_credentials((long *)buf.u.call.data);
+ memcpy(p, fh, NFS_FHSIZE);
+ p += NFS_FHSIZE / 4;
+ *p++ = htonl(offset);
+ *p++ = htonl(len);
+ *p++ = 0; /* unused parameter */
+ for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
+ long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
+ if (tokens >= 2)
+ timeout = TICKS_PER_SEC/2;
+
+ udp_transmit(arptable[server].ipaddr.s_addr, sport, port,
+ (char *)p - (char *)&buf, &buf);
+ if (await_reply(await_rpc, sport, &id, timeout)) {
+ if (tokens < 256)
+ tokens++;
+ rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
+ if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
+ rpc->u.reply.astatus || rpc->u.reply.data[0]) {
+ rpc_printerror(rpc);
+ if (rpc->u.reply.rstatus) {
+ /* RPC failed, no verifier, data[0] */
+ return -9999;
+ }
+ if (rpc->u.reply.astatus) {
+ /* RPC couldn't decode parameters */
+ return -9998;
+ }
+ return -ntohl(rpc->u.reply.data[0]);
+ } else {
+ return 0;
+ }
+ } else
+ tokens >>= 1;
+ }
+ return -1;
+}
+
+/**************************************************************************
+NFS - Download extended BOOTP data, or kernel image from NFS server
+**************************************************************************/
+int nfs(const char *name, int (*fnc)(unsigned char *, unsigned int, unsigned int, int))
+{
+ static int recursion = 0;
+ int sport;
+ int err, namelen = strlen(name);
+ char dirname[300], *fname;
+ char dirfh[NFS_FHSIZE]; /* file handle of directory */
+ char filefh[NFS_FHSIZE]; /* file handle of kernel image */
+ unsigned int block;
+ int rlen, size, offs, len;
+ struct rpc_t *rpc;
+
+ rx_qdrain();
+
+ sport = oport++;
+ if (oport > START_OPORT+OPORT_SWEEP) {
+ oport = START_OPORT;
+ }
+ if ( name != dirname ) {
+ memcpy(dirname, name, namelen + 1);
+ }
+ recursion = 0;
+nfssymlink:
+ if ( recursion > NFS_MAXLINKDEPTH ) {
+ printf ( "\nRecursion: More than %d symlinks followed. Abort.\n", NFS_MAXLINKDEPTH );
+ return 0;
+ }
+ recursion++;
+ fname = dirname + (namelen - 1);
+ while (fname >= dirname) {
+ if (*fname == '/') {
+ *fname = '\0';
+ fname++;
+ break;
+ }
+ fname--;
+ }
+ if (fname < dirname) {
+ printf("can't parse file name %s\n", name);
+ return 0;
+ }
+
+ if (mount_port == -1) {
+ mount_port = rpc_lookup(ARP_SERVER, PROG_MOUNT, 1, sport);
+ }
+ if (nfs_port == -1) {
+ nfs_port = rpc_lookup(ARP_SERVER, PROG_NFS, 2, sport);
+ }
+ if (nfs_port == -1 || mount_port == -1) {
+ printf("can't get nfs/mount ports from portmapper\n");
+ return 0;
+ }
+
+
+ err = nfs_mount(ARP_SERVER, mount_port, dirname, dirfh, sport);
+ if (err) {
+ printf("mounting %s: ", dirname);
+ nfs_printerror(err);
+ /* just to be sure... */
+ nfs_umountall(ARP_SERVER);
+ return 0;
+ }
+
+ err = nfs_lookup(ARP_SERVER, nfs_port, dirfh, fname, filefh, sport);
+ if (err) {
+ printf("looking up %s: ", fname);
+ nfs_printerror(err);
+ nfs_umountall(ARP_SERVER);
+ return 0;
+ }
+
+ offs = 0;
+ block = 1; /* blocks are numbered starting from 1 */
+ size = -1; /* will be set properly with the first reply */
+ len = NFS_READ_SIZE; /* first request is always full size */
+ do {
+ err = nfs_read(ARP_SERVER, nfs_port, filefh, offs, len, sport);
+ if ((err <= -NFSERR_ISDIR)&&(err >= -NFSERR_INVAL) && (offs == 0)) {
+ // An error occured. NFS servers tend to sending
+ // errors 21 / 22 when symlink instead of real file
+ // is requested. So check if it's a symlink!
+ block = nfs_readlink(ARP_SERVER, nfs_port, dirfh, dirname,
+ filefh, sport);
+ if ( 0 == block ) {
+ printf("\nLoading symlink:%s ..",dirname);
+ goto nfssymlink;
+ }
+ nfs_printerror(err);
+ nfs_umountall(ARP_SERVER);
+ return 0;
+ }
+ if (err) {
+ printf("reading at offset %d: ", offs);
+ nfs_printerror(err);
+ nfs_umountall(ARP_SERVER);
+ return 0;
+ }
+
+ rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
+
+ /* size must be found out early to allow EOF detection */
+ if (size == -1) {
+ size = ntohl(rpc->u.reply.data[6]);
+ }
+ rlen = ntohl(rpc->u.reply.data[18]);
+ if (rlen > len) {
+ rlen = len; /* shouldn't happen... */
+ }
+
+ err = fnc((char *)&rpc->u.reply.data[19], block, rlen,
+ (offs+rlen == size));
+ if (err <= 0) {
+ nfs_umountall(ARP_SERVER);
+ return err;
+ }
+
+ block++;
+ offs += rlen;
+ /* last request is done with matching requested read size */
+ if (size-offs < NFS_READ_SIZE) {
+ len = size-offs;
+ }
+ } while (len != 0);
+ /* len == 0 means that all the file has been read */
+ return 1;
+}
+
+#endif /* DOWNLOAD_PROTO_NFS */
diff --git a/src/core/nic.c b/src/core/nic.c
new file mode 100644
index 00000000..44f80c35
--- /dev/null
+++ b/src/core/nic.c
@@ -0,0 +1,1778 @@
+/**************************************************************************
+Etherboot - Network Bootstrap Program
+
+Literature dealing with the network protocols:
+ ARP - RFC826
+ RARP - RFC903
+ IP - RFC791
+ UDP - RFC768
+ BOOTP - RFC951, RFC2132 (vendor extensions)
+ DHCP - RFC2131, RFC2132, RFC3004 (options)
+ TFTP - RFC1350, RFC2347 (options), RFC2348 (blocksize), RFC2349 (tsize)
+ RPC - RFC1831, RFC1832 (XDR), RFC1833 (rpcbind/portmapper)
+ NFS - RFC1094, RFC1813 (v3, useful for clarifications, not implemented)
+ IGMP - RFC1112, RFC2113, RFC2365, RFC2236, RFC3171
+
+**************************************************************************/
+#include "etherboot.h"
+#include "nic.h"
+#include "elf.h" /* FOR EM_CURRENT */
+
+struct arptable_t arptable[MAX_ARP];
+#if MULTICAST_LEVEL2
+unsigned long last_igmpv1 = 0;
+struct igmptable_t igmptable[MAX_IGMP];
+#endif
+/* Put rom_info in .nocompress section so romprefix.S can write to it */
+struct rom_info rom __attribute__ ((section (".text16.nocompress"))) = {0,0};
+static unsigned long netmask;
+/* Used by nfs.c */
+char *hostname = "";
+int hostnamelen = 0;
+static uint32_t xid;
+unsigned char *end_of_rfc1533 = NULL;
+static int vendorext_isvalid;
+static const unsigned char vendorext_magic[] = {0xE4,0x45,0x74,0x68}; /* äEth */
+static const unsigned char broadcast[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+static const in_addr zeroIP = { 0L };
+
+struct bootpd_t bootp_data;
+
+#ifdef NO_DHCP_SUPPORT
+static unsigned char rfc1533_cookie[5] = { RFC1533_COOKIE, RFC1533_END };
+#else /* !NO_DHCP_SUPPORT */
+static int dhcp_reply;
+static in_addr dhcp_server = { 0L };
+static in_addr dhcp_addr = { 0L };
+static unsigned char rfc1533_cookie[] = { RFC1533_COOKIE };
+#define DHCP_MACHINE_INFO_SIZE (sizeof dhcp_machine_info)
+static unsigned char dhcp_machine_info[] = {
+ /* Our enclosing DHCP tag */
+ RFC1533_VENDOR_ETHERBOOT_ENCAP, 11,
+ /* Our boot device */
+ RFC1533_VENDOR_NIC_DEV_ID, 5, PCI_BUS_TYPE, 0, 0, 0, 0,
+ /* Our current architecture */
+ RFC1533_VENDOR_ARCH, 2, EM_CURRENT & 0xff, (EM_CURRENT >> 8) & 0xff,
+#ifdef EM_CURRENT_64
+ /* The 64bit version of our current architecture */
+ RFC1533_VENDOR_ARCH, 2, EM_CURRENT_64 & 0xff, (EM_CURRENT_64 >> 8) & 0xff,
+#undef DHCP_MACHINE_INFO_SIZE
+#define DHCP_MACHINE_INFO_SIZE (sizeof(dhcp_machine_info) - (EM_CURRENT_64_PRESENT? 0: 4))
+#endif /* EM_CURRENT_64 */
+};
+static const unsigned char dhcpdiscover[] = {
+ RFC2132_MSG_TYPE,1,DHCPDISCOVER,
+ RFC2132_MAX_SIZE,2, /* request as much as we can */
+ ETH_MAX_MTU / 256, ETH_MAX_MTU % 256,
+#ifdef PXE_DHCP_STRICT
+ RFC3679_PXE_CLIENT_UUID,RFC3679_PXE_CLIENT_UUID_LENGTH,RFC3679_PXE_CLIENT_UUID_DEFAULT,
+ RFC3679_PXE_CLIENT_ARCH,RFC3679_PXE_CLIENT_ARCH_LENGTH,RFC3679_PXE_CLIENT_ARCH_IAX86PC,
+ RFC3679_PXE_CLIENT_NDI, RFC3679_PXE_CLIENT_NDI_LENGTH, RFC3679_PXE_CLIENT_NDI_21,
+ RFC2132_VENDOR_CLASS_ID,RFC2132_VENDOR_CLASS_ID_PXE_LENGTH,RFC2132_VENDOR_CLASS_ID_PXE,
+#else
+ RFC2132_VENDOR_CLASS_ID,13,'E','t','h','e','r','b','o','o','t',
+ '-',VERSION_MAJOR+'0','.',VERSION_MINOR+'0',
+#endif /* PXE_DHCP_STRICT */
+#ifdef DHCP_CLIENT_ID
+ /* Client ID Option */
+ RFC2132_CLIENT_ID, ( DHCP_CLIENT_ID_LEN + 1 ),
+ DHCP_CLIENT_ID_TYPE, DHCP_CLIENT_ID,
+#endif /* DHCP_CLIENT_ID */
+#ifdef DHCP_USER_CLASS
+ /* User Class Option */
+ RFC3004_USER_CLASS, DHCP_USER_CLASS_LEN, DHCP_USER_CLASS,
+#endif /* DHCP_USER_CLASS */
+ RFC2132_PARAM_LIST,
+#define DHCPDISCOVER_PARAMS_BASE 4
+#ifdef PXE_DHCP_STRICT
+#define DHCPDISCOVER_PARAMS_PXE ( 1 + 8 )
+#else
+#define DHCPDISCOVER_PARAMS_PXE 0
+#endif /* PXE_DHCP_STRICT */
+#ifdef DNS_RESOLVER
+#define DHCPDISCOVER_PARAMS_DNS 1
+#else
+#define DHCPDISCOVER_PARAMS_DNS 0
+#endif /* DNS_RESOLVER */
+ ( DHCPDISCOVER_PARAMS_BASE +
+ DHCPDISCOVER_PARAMS_PXE+
+ DHCPDISCOVER_PARAMS_DNS ),
+ RFC1533_NETMASK,
+ RFC1533_GATEWAY,
+ RFC1533_HOSTNAME,
+ RFC1533_VENDOR
+#ifdef PXE_DHCP_STRICT
+ ,RFC2132_VENDOR_CLASS_ID,
+ RFC1533_VENDOR_PXE_OPT128,
+ RFC1533_VENDOR_PXE_OPT129,
+ RFC1533_VENDOR_PXE_OPT130,
+ RFC1533_VENDOR_PXE_OPT131,
+ RFC1533_VENDOR_PXE_OPT132,
+ RFC1533_VENDOR_PXE_OPT133,
+ RFC1533_VENDOR_PXE_OPT134,
+ RFC1533_VENDOR_PXE_OPT135
+#endif /* PXE_DHCP_STRICT */
+#ifdef DNS_RESOLVER
+ ,RFC1533_DNS
+#endif
+};
+static const unsigned char dhcprequest [] = {
+ RFC2132_MSG_TYPE,1,DHCPREQUEST,
+ RFC2132_SRV_ID,4,0,0,0,0,
+ RFC2132_REQ_ADDR,4,0,0,0,0,
+ RFC2132_MAX_SIZE,2, /* request as much as we can */
+ ETH_MAX_MTU / 256, ETH_MAX_MTU % 256,
+#ifdef PXE_DHCP_STRICT
+ RFC3679_PXE_CLIENT_UUID,RFC3679_PXE_CLIENT_UUID_LENGTH,RFC3679_PXE_CLIENT_UUID_DEFAULT,
+ RFC3679_PXE_CLIENT_ARCH,RFC3679_PXE_CLIENT_ARCH_LENGTH,RFC3679_PXE_CLIENT_ARCH_IAX86PC,
+ RFC3679_PXE_CLIENT_NDI, RFC3679_PXE_CLIENT_NDI_LENGTH, RFC3679_PXE_CLIENT_NDI_21,
+ RFC2132_VENDOR_CLASS_ID,RFC2132_VENDOR_CLASS_ID_PXE_LENGTH,RFC2132_VENDOR_CLASS_ID_PXE,
+#else
+ RFC2132_VENDOR_CLASS_ID,13,'E','t','h','e','r','b','o','o','t',
+ '-',VERSION_MAJOR+'0','.',VERSION_MINOR+'0',
+#endif /* PXE_DHCP_STRICT */
+#ifdef DHCP_CLIENT_ID
+ /* Client ID Option */
+ RFC2132_CLIENT_ID, ( DHCP_CLIENT_ID_LEN + 1 ),
+ DHCP_CLIENT_ID_TYPE, DHCP_CLIENT_ID,
+#endif /* DHCP_CLIENT_ID */
+#ifdef DHCP_USER_CLASS
+ /* User Class Option */
+ RFC3004_USER_CLASS, DHCP_USER_CLASS_LEN, DHCP_USER_CLASS,
+#endif /* DHCP_USER_CLASS */
+ /* request parameters */
+ RFC2132_PARAM_LIST,
+#define DHCPREQUEST_PARAMS_BASE 5
+#ifdef PXE_DHCP_STRICT
+#define DHCPREQUEST_PARAMS_PXE 1
+#define DHCPREQUEST_PARAMS_VENDOR_PXE 8
+#define DHCPREQUEST_PARAMS_VENDOR_EB 0
+#else
+#define DHCPREQUEST_PARAMS_PXE 0
+#define DHCPREQUEST_PARAMS_VENDOR_PXE 0
+#define DHCPREQUEST_PARAMS_VENDOR_EB 4
+#endif /* PXE_DHCP_STRICT */
+#ifdef IMAGE_FREEBSD
+#define DHCPREQUEST_PARAMS_FREEBSD 2
+#else
+#define DHCPREQUEST_PARAMS_FREEBSD 0
+#endif /* IMAGE_FREEBSD */
+#ifdef DNS_RESOLVER
+#define DHCPREQUEST_PARAMS_DNS 1
+#else
+#define DHCPREQUEST_PARAMS_DNS 0
+#endif /* DNS_RESOLVER */
+ ( DHCPREQUEST_PARAMS_BASE +
+ DHCPREQUEST_PARAMS_PXE +
+ DHCPREQUEST_PARAMS_VENDOR_PXE +
+ DHCPREQUEST_PARAMS_VENDOR_EB +
+ DHCPREQUEST_PARAMS_DNS +
+ DHCPREQUEST_PARAMS_FREEBSD ),
+ /* 5 Standard parameters */
+ RFC1533_NETMASK,
+ RFC1533_GATEWAY,
+ RFC1533_HOSTNAME,
+ RFC1533_VENDOR,
+ RFC1533_ROOTPATH, /* only passed to the booted image */
+#ifndef PXE_DHCP_STRICT
+ /* 4 Etherboot vendortags */
+ RFC1533_VENDOR_MAGIC,
+ RFC1533_VENDOR_ADDPARM,
+ RFC1533_VENDOR_ETHDEV,
+ RFC1533_VENDOR_ETHERBOOT_ENCAP,
+#endif /* ! PXE_DHCP_STRICT */
+#ifdef IMAGE_FREEBSD
+ /* 2 FreeBSD options */
+ RFC1533_VENDOR_HOWTO,
+ RFC1533_VENDOR_KERNEL_ENV,
+#endif
+#ifdef DNS_RESOLVER
+ /* 1 DNS option */
+ RFC1533_DNS,
+#endif
+#ifdef PXE_DHCP_STRICT
+ RFC2132_VENDOR_CLASS_ID,
+ RFC1533_VENDOR_PXE_OPT128,
+ RFC1533_VENDOR_PXE_OPT129,
+ RFC1533_VENDOR_PXE_OPT130,
+ RFC1533_VENDOR_PXE_OPT131,
+ RFC1533_VENDOR_PXE_OPT132,
+ RFC1533_VENDOR_PXE_OPT133,
+ RFC1533_VENDOR_PXE_OPT134,
+ RFC1533_VENDOR_PXE_OPT135,
+#endif /* PXE_DHCP_STRICT */
+};
+#ifdef PXE_EXPORT
+static const unsigned char proxydhcprequest [] = {
+ RFC2132_MSG_TYPE,1,DHCPREQUEST,
+ RFC2132_MAX_SIZE,2, /* request as much as we can */
+ ETH_MAX_MTU / 256, ETH_MAX_MTU % 256,
+#ifdef PXE_DHCP_STRICT
+ RFC3679_PXE_CLIENT_UUID,RFC3679_PXE_CLIENT_UUID_LENGTH,RFC3679_PXE_CLIENT_UUID_DEFAULT,
+ RFC3679_PXE_CLIENT_ARCH,RFC3679_PXE_CLIENT_ARCH_LENGTH,RFC3679_PXE_CLIENT_ARCH_IAX86PC,
+ RFC3679_PXE_CLIENT_NDI, RFC3679_PXE_CLIENT_NDI_LENGTH, RFC3679_PXE_CLIENT_NDI_21,
+ RFC2132_VENDOR_CLASS_ID,RFC2132_VENDOR_CLASS_ID_PXE_LENGTH,RFC2132_VENDOR_CLASS_ID_PXE,
+#endif /* PXE_DHCP_STRICT */
+};
+#endif
+
+#ifdef REQUIRE_VCI_ETHERBOOT
+int vci_etherboot;
+#endif
+#endif /* NO_DHCP_SUPPORT */
+
+static int dummy(void *unused __unused)
+{
+ return (0);
+}
+
+/* Careful. We need an aligned buffer to avoid problems on machines
+ * that care about alignment. To trivally align the ethernet data
+ * (the ip hdr and arp requests) we offset the packet by 2 bytes.
+ * leaving the ethernet data 16 byte aligned. Beyond this
+ * we use memmove but this makes the common cast simple and fast.
+ */
+static char packet[ETH_FRAME_LEN + ETH_DATA_ALIGN] __aligned;
+
+struct nic nic =
+{
+ {
+ 0, /* dev.disable */
+ {
+ 0,
+ 0,
+ PCI_BUS_TYPE,
+ }, /* dev.devid */
+ 0, /* index */
+ 0, /* type */
+ PROBE_FIRST, /* how_pobe */
+ PROBE_NONE, /* to_probe */
+ 0, /* failsafe */
+ 0, /* type_index */
+ {}, /* state */
+ },
+ (int (*)(struct nic *, int))dummy, /* poll */
+ (void (*)(struct nic *, const char *,
+ unsigned int, unsigned int,
+ const char *))dummy, /* transmit */
+ (void (*)(struct nic *,
+ irq_action_t))dummy, /* irq */
+ 0, /* flags */
+ &rom, /* rom_info */
+ arptable[ARP_CLIENT].node, /* node_addr */
+ packet + ETH_DATA_ALIGN, /* packet */
+ 0, /* packetlen */
+ 0, /* ioaddr */
+ 0, /* irqno */
+ 0, /* priv_data */
+};
+
+#ifdef RARP_NOT_BOOTP
+static int rarp(void);
+#else
+static int bootp(void);
+#endif
+static unsigned short tcpudpchksum(struct iphdr *ip);
+
+
+int eth_probe(struct dev *dev)
+{
+ return probe(dev);
+}
+
+int eth_poll(int retrieve)
+{
+ return ((*nic.poll)(&nic, retrieve));
+}
+
+void eth_transmit(const char *d, unsigned int t, unsigned int s, const void *p)
+{
+ (*nic.transmit)(&nic, d, t, s, p);
+ if (t == ETH_P_IP) twiddle();
+}
+
+void eth_disable(void)
+{
+#ifdef MULTICAST_LEVEL2
+ int i;
+ for(i = 0; i < MAX_IGMP; i++) {
+ leave_group(i);
+ }
+#endif
+ disable(&nic.dev);
+}
+
+void eth_irq (irq_action_t action)
+{
+ (*nic.irq)(&nic,action);
+}
+
+/*
+ * Find out what our boot parameters are
+ */
+int eth_load_configuration(struct dev *dev __unused)
+{
+ int server_found;
+ /* Find a server to get BOOTP reply from */
+#ifdef RARP_NOT_BOOTP
+ printf("Searching for server (RARP)...");
+#else
+#ifndef NO_DHCP_SUPPORT
+ printf("Searching for server (DHCP)...");
+#else
+ printf("Searching for server (BOOTP)...");
+#endif
+#endif
+
+#ifdef RARP_NOT_BOOTP
+ server_found = rarp();
+#else
+ server_found = bootp();
+#endif
+ if (!server_found) {
+ printf("No Server found\n");
+ longjmp(restart_etherboot, -1);
+ }
+ return 0;
+}
+
+
+/**************************************************************************
+LOAD - Try to get booted
+**************************************************************************/
+int eth_load(struct dev *dev __unused)
+{
+ const char *kernel;
+ printf("\nMe: %@", arptable[ARP_CLIENT].ipaddr.s_addr );
+#ifndef NO_DHCP_SUPPORT
+ printf(", DHCP: %@", dhcp_server );
+#ifdef PXE_EXPORT
+ if (arptable[ARP_PROXYDHCP].ipaddr.s_addr)
+ printf(" (& %@)",
+ arptable[ARP_PROXYDHCP].ipaddr.s_addr);
+#endif /* PXE_EXPORT */
+#endif /* ! NO_DHCP_SUPPORT */
+ printf(", TFTP: %@", arptable[ARP_SERVER].ipaddr.s_addr);
+ if (BOOTP_DATA_ADDR->bootp_reply.bp_giaddr.s_addr)
+ printf(", Relay: %@",
+ BOOTP_DATA_ADDR->bootp_reply.bp_giaddr.s_addr);
+ if (arptable[ARP_GATEWAY].ipaddr.s_addr)
+ printf(", Gateway %@", arptable[ARP_GATEWAY].ipaddr.s_addr);
+#ifdef DNS_RESOLVER
+ if (arptable[ARP_NAMESERVER].ipaddr.s_addr)
+ printf(", Nameserver %@", arptable[ARP_NAMESERVER].ipaddr.s_addr);
+#endif
+ putchar('\n');
+
+#ifdef MDEBUG
+ printf("\n=>>"); getchar();
+#endif
+
+ /* Now use TFTP to load file */
+#ifdef DOWNLOAD_PROTO_NFS
+ rpc_init();
+#endif
+ kernel = KERNEL_BUF[0] == '\0' ?
+#ifdef DEFAULT_BOOTFILE
+ DEFAULT_BOOTFILE
+#else
+ NULL
+#endif
+ : KERNEL_BUF;
+ if ( kernel ) {
+ loadkernel(kernel); /* We don't return except on error */
+ printf("Unable to load file.\n");
+ } else {
+ printf("No filename\n");
+ }
+ interruptible_sleep(2); /* lay off the server for a while */
+ longjmp(restart_etherboot, -1);
+}
+
+
+/**************************************************************************
+DEFAULT_NETMASK - Return default netmask for IP address
+**************************************************************************/
+static inline unsigned long default_netmask(void)
+{
+ int net = ntohl(arptable[ARP_CLIENT].ipaddr.s_addr) >> 24;
+ if (net <= 127)
+ return(htonl(0xff000000));
+ else if (net < 192)
+ return(htonl(0xffff0000));
+ else
+ return(htonl(0xffffff00));
+}
+
+/**************************************************************************
+IP_TRANSMIT - Send an IP datagram
+**************************************************************************/
+static int await_arp(int ival, void *ptr,
+ unsigned short ptype, struct iphdr *ip __unused, struct udphdr *udp __unused,
+ struct tcphdr *tcp __unused)
+{
+ struct arprequest *arpreply;
+ if (ptype != ETH_P_ARP)
+ return 0;
+ if (nic.packetlen < ETH_HLEN + sizeof(struct arprequest))
+ return 0;
+ arpreply = (struct arprequest *)&nic.packet[ETH_HLEN];
+
+ if (arpreply->opcode != htons(ARP_REPLY))
+ return 0;
+ if (memcmp(arpreply->sipaddr, ptr, sizeof(in_addr)) != 0)
+ return 0;
+ memcpy(arptable[ival].node, arpreply->shwaddr, ETH_ALEN);
+ return 1;
+}
+
+int ip_transmit(int len, const void *buf)
+{
+ unsigned long destip;
+ struct iphdr *ip;
+ struct arprequest arpreq;
+ int arpentry, i;
+ int retry;
+
+ ip = (struct iphdr *)buf;
+ destip = ip->dest.s_addr;
+ if (destip == IP_BROADCAST) {
+ eth_transmit(broadcast, ETH_P_IP, len, buf);
+#ifdef MULTICAST_LEVEL1
+ } else if ((destip & htonl(MULTICAST_MASK)) == htonl(MULTICAST_NETWORK)) {
+ unsigned char multicast[6];
+ unsigned long hdestip;
+ hdestip = ntohl(destip);
+ multicast[0] = 0x01;
+ multicast[1] = 0x00;
+ multicast[2] = 0x5e;
+ multicast[3] = (hdestip >> 16) & 0x7;
+ multicast[4] = (hdestip >> 8) & 0xff;
+ multicast[5] = hdestip & 0xff;
+ eth_transmit(multicast, ETH_P_IP, len, buf);
+#endif
+ } else {
+ if (((destip & netmask) !=
+ (arptable[ARP_CLIENT].ipaddr.s_addr & netmask)) &&
+ arptable[ARP_GATEWAY].ipaddr.s_addr)
+ destip = arptable[ARP_GATEWAY].ipaddr.s_addr;
+ for(arpentry = 0; arpentry<MAX_ARP; arpentry++)
+ if (arptable[arpentry].ipaddr.s_addr == destip) break;
+ if (arpentry == MAX_ARP) {
+ printf("%@ is not in my arp table!\n", destip);
+ return(0);
+ }
+ for (i = 0; i < ETH_ALEN; i++)
+ if (arptable[arpentry].node[i])
+ break;
+ if (i == ETH_ALEN) { /* Need to do arp request */
+ arpreq.hwtype = htons(1);
+ arpreq.protocol = htons(IP);
+ arpreq.hwlen = ETH_ALEN;
+ arpreq.protolen = 4;
+ arpreq.opcode = htons(ARP_REQUEST);
+ memcpy(arpreq.shwaddr, arptable[ARP_CLIENT].node, ETH_ALEN);
+ memcpy(arpreq.sipaddr, &arptable[ARP_CLIENT].ipaddr, sizeof(in_addr));
+ memset(arpreq.thwaddr, 0, ETH_ALEN);
+ memcpy(arpreq.tipaddr, &destip, sizeof(in_addr));
+ for (retry = 1; retry <= MAX_ARP_RETRIES; retry++) {
+ long timeout;
+ eth_transmit(broadcast, ETH_P_ARP, sizeof(arpreq),
+ &arpreq);
+ timeout = rfc2131_sleep_interval(TIMEOUT, retry);
+ if (await_reply(await_arp, arpentry,
+ arpreq.tipaddr, timeout)) goto xmit;
+ }
+ return(0);
+ }
+xmit:
+ eth_transmit(arptable[arpentry].node, ETH_P_IP, len, buf);
+ }
+ return 1;
+}
+
+void build_ip_hdr(unsigned long destip, int ttl, int protocol, int option_len,
+ int len, const void *buf)
+{
+ struct iphdr *ip;
+ ip = (struct iphdr *)buf;
+ ip->verhdrlen = 0x45;
+ ip->verhdrlen += (option_len/4);
+ ip->service = 0;
+ ip->len = htons(len);
+ ip->ident = 0;
+ ip->frags = 0; /* Should we set don't fragment? */
+ ip->ttl = ttl;
+ ip->protocol = protocol;
+ ip->chksum = 0;
+ ip->src.s_addr = arptable[ARP_CLIENT].ipaddr.s_addr;
+ ip->dest.s_addr = destip;
+ ip->chksum = ipchksum(buf, sizeof(struct iphdr) + option_len);
+}
+
+void build_udp_hdr(unsigned long destip,
+ unsigned int srcsock, unsigned int destsock, int ttl,
+ int len, const void *buf)
+{
+ struct iphdr *ip;
+ struct udphdr *udp;
+ ip = (struct iphdr *)buf;
+ build_ip_hdr(destip, ttl, IP_UDP, 0, len, buf);
+ udp = (struct udphdr *)((char *)buf + sizeof(struct iphdr));
+ udp->src = htons(srcsock);
+ udp->dest = htons(destsock);
+ udp->len = htons(len - sizeof(struct iphdr));
+ udp->chksum = 0;
+ if ((udp->chksum = tcpudpchksum(ip)) == 0)
+ udp->chksum = 0xffff;
+}
+
+#ifdef DOWNLOAD_PROTO_HTTP
+void build_tcp_hdr(unsigned long destip, unsigned int srcsock,
+ unsigned int destsock, long send_seq, long recv_seq,
+ int window, int flags, int ttl, int len, const void *buf)
+{
+ struct iphdr *ip;
+ struct tcphdr *tcp;
+ ip = (struct iphdr *)buf;
+ build_ip_hdr(destip, ttl, IP_TCP, 0, len, buf);
+ tcp = (struct tcphdr *)(ip + 1);
+ tcp->src = htons(srcsock);
+ tcp->dst = htons(destsock);
+ tcp->seq = htonl(send_seq);
+ tcp->ack = htonl(recv_seq);
+ tcp->ctrl = htons(flags + (5 << 12)); /* No TCP options */
+ tcp->window = htons(window);
+ tcp->chksum = 0;
+ if ((tcp->chksum = tcpudpchksum(ip)) == 0)
+ tcp->chksum = 0xffff;
+}
+#endif
+
+
+/**************************************************************************
+UDP_TRANSMIT - Send an UDP datagram
+**************************************************************************/
+int udp_transmit(unsigned long destip, unsigned int srcsock,
+ unsigned int destsock, int len, const void *buf)
+{
+ build_udp_hdr(destip, srcsock, destsock, 60, len, buf);
+ return ip_transmit(len, buf);
+}
+
+/**************************************************************************
+TCP_TRANSMIT - Send a TCP packet
+**************************************************************************/
+#ifdef DOWNLOAD_PROTO_HTTP
+int tcp_transmit(unsigned long destip, unsigned int srcsock,
+ unsigned int destsock, long send_seq, long recv_seq,
+ int window, int flags, int len, const void *buf)
+{
+ build_tcp_hdr(destip, srcsock, destsock, send_seq, recv_seq,
+ window, flags, 60, len, buf);
+ return ip_transmit(len, buf);
+}
+
+int tcp_reset(struct iphdr *ip) {
+ struct tcphdr *tcp = (struct tcphdr *)(ip + 1);
+ char buf[sizeof(struct iphdr) + sizeof(struct tcphdr)];
+
+ if (!(tcp->ctrl & htons(RST))) {
+ long seq = ntohl(tcp->seq) + ntohs(ip->len) -
+ sizeof(struct iphdr) -
+ ((ntohs(tcp->ctrl) >> 10) & 0x3C);
+ if (tcp->ctrl & htons(SYN|FIN))
+ seq++;
+ return tcp_transmit(ntohl(ip->src.s_addr),
+ ntohs(tcp->dst), ntohs(tcp->src),
+ tcp->ctrl&htons(ACK) ? ntohl(tcp->ack) : 0,
+ seq, TCP_MAX_WINDOW, RST, sizeof(buf), buf);
+ }
+ return (1);
+}
+#endif
+
+/**************************************************************************
+QDRAIN - clear the nic's receive queue
+**************************************************************************/
+static int await_qdrain(int ival __unused, void *ptr __unused,
+ unsigned short ptype __unused,
+ struct iphdr *ip __unused, struct udphdr *udp __unused,
+ struct tcphdr *tcp __unused)
+{
+ return 0;
+}
+
+void rx_qdrain(void)
+{
+ /* Clear out the Rx queue first. It contains nothing of interest,
+ * except possibly ARP requests from the DHCP/TFTP server. We use
+ * polling throughout Etherboot, so some time may have passed since we
+ * last polled the receive queue, which may now be filled with
+ * broadcast packets. This will cause the reply to the packets we are
+ * about to send to be lost immediately. Not very clever. */
+ await_reply(await_qdrain, 0, NULL, 0);
+}
+
+#ifdef DOWNLOAD_PROTO_TFTP
+/**************************************************************************
+TFTP - Download extended BOOTP data, or kernel image
+**************************************************************************/
+static int await_tftp(int ival, void *ptr __unused,
+ unsigned short ptype __unused, struct iphdr *ip, struct udphdr *udp,
+ struct tcphdr *tcp __unused)
+{
+ if (!udp) {
+ return 0;
+ }
+ if (arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr)
+ return 0;
+ if (ntohs(udp->dest) != ival)
+ return 0;
+ return 1;
+}
+
+int tftp ( const char *name,
+ int (*fnc)(unsigned char *, unsigned int, unsigned int, int) )
+{
+ struct tftpreq_info_t request_data =
+ { name, TFTP_PORT, TFTP_MAX_PACKET };
+ struct tftpreq_info_t *request = &request_data;
+ struct tftpblk_info_t block;
+ int rc;
+
+ while ( tftp_block ( request, &block ) ) {
+ request = NULL; /* Send request only once */
+ rc = fnc ( block.data, block.block, block.len, block.eof );
+ if ( rc <= 0 ) return (rc);
+ if ( block.eof ) {
+ /* fnc should not have returned */
+ printf ( "TFTP download complete, but\n" );
+ return (0);
+ }
+ }
+ return (0);
+}
+
+int tftp_block ( struct tftpreq_info_t *request, struct tftpblk_info_t *block )
+{
+ static unsigned short lport = 2000; /* local port */
+ static unsigned short rport = TFTP_PORT; /* remote port */
+ struct tftp_t *rcvd = NULL;
+ static struct tftpreq_t xmit;
+ static unsigned short xmitlen = 0;
+ static unsigned short blockidx = 0; /* Last block received */
+ static unsigned short retry = 0; /* Retry attempts on last block */
+ static int blksize = 0;
+ unsigned short recvlen = 0;
+
+ /* If this is a new request (i.e. if name is set), fill in
+ * transmit block with RRQ and send it.
+ */
+ if ( request ) {
+ rx_qdrain(); /* Flush receive queue */
+ xmit.opcode = htons(TFTP_RRQ);
+ xmitlen = (void*)&xmit.u.rrq - (void*)&xmit +
+ sprintf((char*)xmit.u.rrq, "%s%coctet%cblksize%c%d",
+ request->name, 0, 0, 0, request->blksize)
+ + 1; /* null terminator */
+ blockidx = 0; /* Reset counters */
+ retry = 0;
+ blksize = TFTP_DEFAULTSIZE_PACKET;
+ lport++; /* Use new local port */
+ rport = request->port;
+ if ( !udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, lport,
+ rport, xmitlen, &xmit) )
+ return (0);
+ }
+ /* Exit if no transfer in progress */
+ if ( !blksize ) return (0);
+ /* Loop to wait until we get a packet we're interested in */
+ block->data = NULL; /* Used as flag */
+ while ( block->data == NULL ) {
+ long timeout = rfc2131_sleep_interval ( blockidx ? TFTP_REXMT :
+ TIMEOUT, retry );
+ if ( !await_reply(await_tftp, lport, NULL, timeout) ) {
+ /* No packet received */
+ if ( retry++ > MAX_TFTP_RETRIES ) break;
+ /* Retransmit last packet */
+ if ( !blockidx ) lport++; /* New lport if new RRQ */
+ if ( !udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
+ lport, rport, xmitlen, &xmit) )
+ return (0);
+ continue; /* Back to waiting for packet */
+ }
+ /* Packet has been received */
+ rcvd = (struct tftp_t *)&nic.packet[ETH_HLEN];
+ recvlen = ntohs(rcvd->udp.len) - sizeof(struct udphdr)
+ - sizeof(rcvd->opcode);
+ rport = ntohs(rcvd->udp.src);
+ retry = 0; /* Reset retry counter */
+ switch ( htons(rcvd->opcode) ) {
+ case TFTP_ERROR : {
+ printf ( "TFTP error %d (%s)\n",
+ ntohs(rcvd->u.err.errcode),
+ rcvd->u.err.errmsg );
+ return (0); /* abort */
+ }
+ case TFTP_OACK : {
+ const char *p = rcvd->u.oack.data;
+ const char *e = p + recvlen - 10; /* "blksize\0\d\0" */
+
+ *((char*)(p+recvlen-1)) = '\0'; /* Force final 0 */
+ if ( blockidx || !request ) break; /* Too late */
+ if ( recvlen <= TFTP_MAX_PACKET ) /* sanity */ {
+ /* Check for blksize option honoured */
+ while ( p < e ) {
+ if ( strcasecmp("blksize",p) == 0 &&
+ p[7] == '\0' ) {
+ blksize = strtoul(p+8,&p,10);
+ p++; /* skip null */
+ }
+ while ( *(p++) ) {};
+ }
+ }
+ if ( blksize < TFTP_DEFAULTSIZE_PACKET || blksize > request->blksize ) {
+ /* Incorrect blksize - error and abort */
+ xmit.opcode = htons(TFTP_ERROR);
+ xmit.u.err.errcode = 8;
+ xmitlen = (void*)&xmit.u.err.errmsg
+ - (void*)&xmit
+ + sprintf((char*)xmit.u.err.errmsg,
+ "RFC1782 error")
+ + 1;
+ udp_transmit(
+ arptable[ARP_SERVER].ipaddr.s_addr,
+ lport, rport, xmitlen, &xmit);
+ return (0);
+ }
+ } break;
+ case TFTP_DATA :
+ if ( ntohs(rcvd->u.data.block) != ( blockidx + 1 ) )
+ break; /* Re-ACK last block sent */
+ if ( recvlen > ( blksize+sizeof(rcvd->u.data.block) ) )
+ break; /* Too large; ignore */
+ block->data = rcvd->u.data.download;
+ block->block = ++blockidx;
+ block->len = recvlen - sizeof(rcvd->u.data.block);
+ block->eof = ( (unsigned short)block->len < blksize );
+ /* If EOF, zero blksize to indicate transfer done */
+ if ( block->eof ) blksize = 0;
+ break;
+ default: break; /* Do nothing */
+ }
+ /* Send ACK */
+ xmit.opcode = htons(TFTP_ACK);
+ xmit.u.ack.block = htons(blockidx);
+ xmitlen = TFTP_MIN_PACKET;
+ udp_transmit ( arptable[ARP_SERVER].ipaddr.s_addr,
+ lport, rport, xmitlen, &xmit );
+ }
+ return ( block->data ? 1 : 0 );
+}
+#endif /* DOWNLOAD_PROTO_TFTP */
+
+#ifdef RARP_NOT_BOOTP
+/**************************************************************************
+RARP - Get my IP address and load information
+**************************************************************************/
+static int await_rarp(int ival, void *ptr,
+ unsigned short ptype, struct iphdr *ip, struct udphdr *udp,
+ struct tcphdr *tcp __unused)
+{
+ struct arprequest *arpreply;
+ if (ptype != ETH_P_RARP)
+ return 0;
+ if (nic.packetlen < ETH_HLEN + sizeof(struct arprequest))
+ return 0;
+ arpreply = (struct arprequest *)&nic.packet[ETH_HLEN];
+ if (arpreply->opcode != htons(RARP_REPLY))
+ return 0;
+ if ((arpreply->opcode == htons(RARP_REPLY)) &&
+ (memcmp(arpreply->thwaddr, ptr, ETH_ALEN) == 0)) {
+ memcpy(arptable[ARP_SERVER].node, arpreply->shwaddr, ETH_ALEN);
+ memcpy(&arptable[ARP_SERVER].ipaddr, arpreply->sipaddr, sizeof(in_addr));
+ memcpy(&arptable[ARP_CLIENT].ipaddr, arpreply->tipaddr, sizeof(in_addr));
+ return 1;
+ }
+ return 0;
+}
+
+static int rarp(void)
+{
+ int retry;
+
+ /* arp and rarp requests share the same packet structure. */
+ struct arprequest rarpreq;
+
+ memset(&rarpreq, 0, sizeof(rarpreq));
+
+ rarpreq.hwtype = htons(1);
+ rarpreq.protocol = htons(IP);
+ rarpreq.hwlen = ETH_ALEN;
+ rarpreq.protolen = 4;
+ rarpreq.opcode = htons(RARP_REQUEST);
+ memcpy(&rarpreq.shwaddr, arptable[ARP_CLIENT].node, ETH_ALEN);
+ /* sipaddr is already zeroed out */
+ memcpy(&rarpreq.thwaddr, arptable[ARP_CLIENT].node, ETH_ALEN);
+ /* tipaddr is already zeroed out */
+
+ for (retry = 0; retry < MAX_ARP_RETRIES; ++retry) {
+ long timeout;
+ eth_transmit(broadcast, ETH_P_RARP, sizeof(rarpreq), &rarpreq);
+
+ timeout = rfc2131_sleep_interval(TIMEOUT, retry);
+ if (await_reply(await_rarp, 0, rarpreq.shwaddr, timeout))
+ break;
+ }
+
+ if (retry < MAX_ARP_RETRIES) {
+ (void)sprintf(KERNEL_BUF, DEFAULT_KERNELPATH, arptable[ARP_CLIENT].ipaddr);
+
+ return (1);
+ }
+ return (0);
+}
+
+#else
+
+/**************************************************************************
+BOOTP - Get my IP address and load information
+**************************************************************************/
+static int await_bootp(int ival __unused, void *ptr __unused,
+ unsigned short ptype __unused, struct iphdr *ip __unused,
+ struct udphdr *udp, struct tcphdr *tcp __unused)
+{
+ struct bootp_t *bootpreply;
+ if (!udp) {
+ return 0;
+ }
+ bootpreply = (struct bootp_t *)&nic.packet[ETH_HLEN +
+ sizeof(struct iphdr) + sizeof(struct udphdr)];
+ if (nic.packetlen < ETH_HLEN + sizeof(struct iphdr) +
+ sizeof(struct udphdr) +
+#ifdef NO_DHCP_SUPPORT
+ sizeof(struct bootp_t)
+#else
+ sizeof(struct bootp_t) - DHCP_OPT_LEN
+#endif /* NO_DHCP_SUPPORT */
+ ) {
+ return 0;
+ }
+ if (udp->dest != htons(BOOTP_CLIENT))
+ return 0;
+ if (bootpreply->bp_op != BOOTP_REPLY)
+ return 0;
+ if (bootpreply->bp_xid != xid)
+ return 0;
+ if (memcmp(&bootpreply->bp_siaddr, &zeroIP, sizeof(in_addr)) == 0)
+ return 0;
+ if ((memcmp(broadcast, bootpreply->bp_hwaddr, ETH_ALEN) != 0) &&
+ (memcmp(arptable[ARP_CLIENT].node, bootpreply->bp_hwaddr, ETH_ALEN) != 0)) {
+ return 0;
+ }
+ if ( bootpreply->bp_siaddr.s_addr ) {
+ arptable[ARP_SERVER].ipaddr.s_addr = bootpreply->bp_siaddr.s_addr;
+ memset(arptable[ARP_SERVER].node, 0, ETH_ALEN); /* Kill arp */
+ }
+ if ( bootpreply->bp_giaddr.s_addr ) {
+ arptable[ARP_GATEWAY].ipaddr.s_addr = bootpreply->bp_giaddr.s_addr;
+ memset(arptable[ARP_GATEWAY].node, 0, ETH_ALEN); /* Kill arp */
+ }
+ if (bootpreply->bp_yiaddr.s_addr) {
+ /* Offer with an IP address */
+ arptable[ARP_CLIENT].ipaddr.s_addr = bootpreply->bp_yiaddr.s_addr;
+#ifndef NO_DHCP_SUPPORT
+ dhcp_addr.s_addr = bootpreply->bp_yiaddr.s_addr;
+#endif /* NO_DHCP_SUPPORT */
+ netmask = default_netmask();
+ /* bootpreply->bp_file will be copied to KERNEL_BUF in the memcpy */
+ memcpy((char *)BOOTP_DATA_ADDR, (char *)bootpreply, sizeof(struct bootpd_t));
+ decode_rfc1533(BOOTP_DATA_ADDR->bootp_reply.bp_vend, 0,
+#ifdef NO_DHCP_SUPPORT
+ BOOTP_VENDOR_LEN + MAX_BOOTP_EXTLEN,
+#else
+ DHCP_OPT_LEN + MAX_BOOTP_EXTLEN,
+#endif /* NO_DHCP_SUPPORT */
+ 1);
+#ifdef PXE_EXPORT
+ } else {
+ /* Offer without an IP address - use as ProxyDHCP server */
+ arptable[ARP_PROXYDHCP].ipaddr.s_addr = bootpreply->bp_siaddr.s_addr;
+ memset(arptable[ARP_PROXYDHCP].node, 0, ETH_ALEN); /* Kill arp */
+ /* Grab only the bootfile name from a ProxyDHCP packet */
+ memcpy(KERNEL_BUF, bootpreply->bp_file, sizeof(KERNEL_BUF));
+#endif /* PXE_EXPORT */
+ }
+#ifdef REQUIRE_VCI_ETHERBOOT
+ if (!vci_etherboot)
+ return (0);
+#endif
+ return(1);
+}
+
+static int bootp(void)
+{
+ int retry;
+#ifndef NO_DHCP_SUPPORT
+ int reqretry;
+#endif /* NO_DHCP_SUPPORT */
+ struct bootpip_t ip;
+ unsigned long starttime;
+ unsigned char *bp_vend;
+
+#ifndef NO_DHCP_SUPPORT
+ dhcp_machine_info[4] = nic.dev.devid.bus_type;
+ dhcp_machine_info[5] = nic.dev.devid.vendor_id & 0xff;
+ dhcp_machine_info[6] = ((nic.dev.devid.vendor_id) >> 8) & 0xff;
+ dhcp_machine_info[7] = nic.dev.devid.device_id & 0xff;
+ dhcp_machine_info[8] = ((nic.dev.devid.device_id) >> 8) & 0xff;
+#endif /* NO_DHCP_SUPPORT */
+ memset(&ip, 0, sizeof(struct bootpip_t));
+ ip.bp.bp_op = BOOTP_REQUEST;
+ ip.bp.bp_htype = 1;
+ ip.bp.bp_hlen = ETH_ALEN;
+ starttime = currticks();
+ /* Use lower 32 bits of node address, more likely to be
+ distinct than the time since booting */
+ memcpy(&xid, &arptable[ARP_CLIENT].node[2], sizeof(xid));
+ ip.bp.bp_xid = xid += htonl(starttime);
+ memcpy(ip.bp.bp_hwaddr, arptable[ARP_CLIENT].node, ETH_ALEN);
+#ifdef NO_DHCP_SUPPORT
+ memcpy(ip.bp.bp_vend, rfc1533_cookie, 5); /* request RFC-style options */
+#else
+ memcpy(ip.bp.bp_vend, rfc1533_cookie, sizeof rfc1533_cookie); /* request RFC-style options */
+ memcpy(ip.bp.bp_vend + sizeof rfc1533_cookie, dhcpdiscover, sizeof dhcpdiscover);
+ /* Append machine_info to end, in encapsulated option */
+ bp_vend = ip.bp.bp_vend + sizeof rfc1533_cookie + sizeof dhcpdiscover;
+ memcpy(bp_vend, dhcp_machine_info, DHCP_MACHINE_INFO_SIZE);
+ bp_vend += DHCP_MACHINE_INFO_SIZE;
+ *bp_vend++ = RFC1533_END;
+#endif /* NO_DHCP_SUPPORT */
+
+ for (retry = 0; retry < MAX_BOOTP_RETRIES; ) {
+ uint8_t my_hwaddr[ETH_ALEN];
+ unsigned long stop_time;
+ long remaining_time;
+
+ rx_qdrain();
+
+ /* Kill arptable to avoid keeping stale entries */
+ memcpy ( my_hwaddr, arptable[ARP_CLIENT].node, ETH_ALEN );
+ memset ( arptable, 0, sizeof(arptable) );
+ memcpy ( arptable[ARP_CLIENT].node, my_hwaddr, ETH_ALEN );
+
+ udp_transmit(IP_BROADCAST, BOOTP_CLIENT, BOOTP_SERVER,
+ sizeof(struct bootpip_t), &ip);
+ remaining_time = rfc2131_sleep_interval(BOOTP_TIMEOUT, retry++);
+ stop_time = currticks() + remaining_time;
+#ifdef NO_DHCP_SUPPORT
+ if (await_reply(await_bootp, 0, NULL, timeout))
+ return(1);
+#else
+ while ( remaining_time > 0 ) {
+ if (await_reply(await_bootp, 0, NULL, remaining_time)){
+ }
+ remaining_time = stop_time - currticks();
+ }
+ if ( ! arptable[ARP_CLIENT].ipaddr.s_addr ) {
+ printf("No IP address\n");
+ continue;
+ }
+ /* If not a DHCPOFFER then must be just a BOOTP reply,
+ * be backward compatible with BOOTP then */
+ if (dhcp_reply != DHCPOFFER)
+ return(1);
+ dhcp_reply = 0;
+ /* Construct the DHCPREQUEST packet */
+ memcpy(ip.bp.bp_vend, rfc1533_cookie, sizeof rfc1533_cookie);
+ memcpy(ip.bp.bp_vend + sizeof rfc1533_cookie, dhcprequest, sizeof dhcprequest);
+ /* Beware: the magic numbers 9 and 15 depend on
+ the layout of dhcprequest */
+ memcpy(&ip.bp.bp_vend[9], &dhcp_server, sizeof(in_addr));
+ memcpy(&ip.bp.bp_vend[15], &dhcp_addr, sizeof(in_addr));
+ bp_vend = ip.bp.bp_vend + sizeof rfc1533_cookie + sizeof dhcprequest;
+ /* Append machine_info to end, in encapsulated option */
+ memcpy(bp_vend, dhcp_machine_info, DHCP_MACHINE_INFO_SIZE);
+ bp_vend += DHCP_MACHINE_INFO_SIZE;
+ *bp_vend++ = RFC1533_END;
+ for (reqretry = 0; reqretry < MAX_BOOTP_RETRIES; ) {
+ unsigned long timeout;
+
+ udp_transmit(IP_BROADCAST, BOOTP_CLIENT, BOOTP_SERVER,
+ sizeof(struct bootpip_t), &ip);
+ dhcp_reply=0;
+ timeout = rfc2131_sleep_interval(TIMEOUT, reqretry++);
+ if (!await_reply(await_bootp, 0, NULL, timeout))
+ continue;
+ if (dhcp_reply != DHCPACK)
+ continue;
+ dhcp_reply = 0;
+#ifdef PXE_EXPORT
+ if ( arptable[ARP_PROXYDHCP].ipaddr.s_addr ) {
+ /* Construct the ProxyDHCPREQUEST packet */
+ memcpy(ip.bp.bp_vend, rfc1533_cookie, sizeof rfc1533_cookie);
+ memcpy(ip.bp.bp_vend + sizeof rfc1533_cookie, proxydhcprequest, sizeof proxydhcprequest);
+ for (reqretry = 0; reqretry < MAX_BOOTP_RETRIES; ) {
+ printf ( "\nSending ProxyDHCP request to %@...", arptable[ARP_PROXYDHCP].ipaddr.s_addr);
+ udp_transmit(arptable[ARP_PROXYDHCP].ipaddr.s_addr, BOOTP_CLIENT, PROXYDHCP_SERVER,
+ sizeof(struct bootpip_t), &ip);
+ timeout = rfc2131_sleep_interval(TIMEOUT, reqretry++);
+ if (await_reply(await_bootp, 0, NULL, timeout)) {
+ break;
+ }
+ }
+ }
+#endif /* PXE_EXPORT */
+ return(1);
+ }
+#endif /* NO_DHCP_SUPPORT */
+ ip.bp.bp_secs = htons((currticks()-starttime)/TICKS_PER_SEC);
+ }
+ return(0);
+}
+#endif /* RARP_NOT_BOOTP */
+
+static uint16_t tcpudpchksum(struct iphdr *ip)
+{
+ struct udp_pseudo_hdr pseudo;
+ uint16_t checksum;
+
+ /* Compute the pseudo header */
+ pseudo.src.s_addr = ip->src.s_addr;
+ pseudo.dest.s_addr = ip->dest.s_addr;
+ pseudo.unused = 0;
+ pseudo.protocol = ip->protocol;
+ pseudo.len = htons(ntohs(ip->len) - sizeof(struct iphdr));
+
+ /* Sum the pseudo header */
+ checksum = ipchksum(&pseudo, 12);
+
+ /* Sum the rest of the tcp/udp packet */
+ checksum = add_ipchksums(12, checksum, ipchksum(ip + 1,
+ ntohs(ip->len) - sizeof(struct iphdr)));
+ return checksum;
+}
+
+#ifdef MULTICAST_LEVEL2
+static void send_igmp_reports(unsigned long now)
+{
+ int i;
+ for(i = 0; i < MAX_IGMP; i++) {
+ if (igmptable[i].time && (now >= igmptable[i].time)) {
+ struct igmp_ip_t igmp;
+ igmp.router_alert[0] = 0x94;
+ igmp.router_alert[1] = 0x04;
+ igmp.router_alert[2] = 0;
+ igmp.router_alert[3] = 0;
+ build_ip_hdr(igmptable[i].group.s_addr,
+ 1, IP_IGMP, sizeof(igmp.router_alert), sizeof(igmp), &igmp);
+ igmp.igmp.type = IGMPv2_REPORT;
+ if (last_igmpv1 &&
+ (now < last_igmpv1 + IGMPv1_ROUTER_PRESENT_TIMEOUT)) {
+ igmp.igmp.type = IGMPv1_REPORT;
+ }
+ igmp.igmp.response_time = 0;
+ igmp.igmp.chksum = 0;
+ igmp.igmp.group.s_addr = igmptable[i].group.s_addr;
+ igmp.igmp.chksum = ipchksum(&igmp.igmp, sizeof(igmp.igmp));
+ ip_transmit(sizeof(igmp), &igmp);
+#ifdef MDEBUG
+ printf("Sent IGMP report to: %@\n", igmp.igmp.group.s_addr);
+#endif
+ /* Don't send another igmp report until asked */
+ igmptable[i].time = 0;
+ }
+ }
+}
+
+static void process_igmp(struct iphdr *ip, unsigned long now)
+{
+ struct igmp *igmp;
+ int i;
+ unsigned iplen;
+ if (!ip || (ip->protocol == IP_IGMP) ||
+ (nic.packetlen < sizeof(struct iphdr) + sizeof(struct igmp))) {
+ return;
+ }
+ iplen = (ip->verhdrlen & 0xf)*4;
+ igmp = (struct igmp *)&nic.packet[sizeof(struct iphdr)];
+ if (ipchksum(igmp, ntohs(ip->len) - iplen) != 0)
+ return;
+ if ((igmp->type == IGMP_QUERY) &&
+ (ip->dest.s_addr == htonl(GROUP_ALL_HOSTS))) {
+ unsigned long interval = IGMP_INTERVAL;
+ if (igmp->response_time == 0) {
+ last_igmpv1 = now;
+ } else {
+ interval = (igmp->response_time * TICKS_PER_SEC)/10;
+ }
+
+#ifdef MDEBUG
+ printf("Received IGMP query for: %@\n", igmp->group.s_addr);
+#endif
+ for(i = 0; i < MAX_IGMP; i++) {
+ uint32_t group = igmptable[i].group.s_addr;
+ if ((group == 0) || (group == igmp->group.s_addr)) {
+ unsigned long time;
+ time = currticks() + rfc1112_sleep_interval(interval, 0);
+ if (time < igmptable[i].time) {
+ igmptable[i].time = time;
+ }
+ }
+ }
+ }
+ if (((igmp->type == IGMPv1_REPORT) || (igmp->type == IGMPv2_REPORT)) &&
+ (ip->dest.s_addr == igmp->group.s_addr)) {
+#ifdef MDEBUG
+ printf("Received IGMP report for: %@\n", igmp->group.s_addr);
+#endif
+ for(i = 0; i < MAX_IGMP; i++) {
+ if ((igmptable[i].group.s_addr == igmp->group.s_addr) &&
+ igmptable[i].time != 0) {
+ igmptable[i].time = 0;
+ }
+ }
+ }
+}
+
+void leave_group(int slot)
+{
+ /* Be very stupid and always send a leave group message if
+ * I have subscribed. Imperfect but it is standards
+ * compliant, easy and reliable to implement.
+ *
+ * The optimal group leave method is to only send leave when,
+ * we were the last host to respond to a query on this group,
+ * and igmpv1 compatibility is not enabled.
+ */
+ if (igmptable[slot].group.s_addr) {
+ struct igmp_ip_t igmp;
+ igmp.router_alert[0] = 0x94;
+ igmp.router_alert[1] = 0x04;
+ igmp.router_alert[2] = 0;
+ igmp.router_alert[3] = 0;
+ build_ip_hdr(htonl(GROUP_ALL_HOSTS),
+ 1, IP_IGMP, sizeof(igmp.router_alert), sizeof(igmp), &igmp);
+ igmp.igmp.type = IGMP_LEAVE;
+ igmp.igmp.response_time = 0;
+ igmp.igmp.chksum = 0;
+ igmp.igmp.group.s_addr = igmptable[slot].group.s_addr;
+ igmp.igmp.chksum = ipchksum(&igmp.igmp, sizeof(igmp));
+ ip_transmit(sizeof(igmp), &igmp);
+#ifdef MDEBUG
+ printf("Sent IGMP leave for: %@\n", igmp.igmp.group.s_addr);
+#endif
+ }
+ memset(&igmptable[slot], 0, sizeof(igmptable[0]));
+}
+
+void join_group(int slot, unsigned long group)
+{
+ /* I have already joined */
+ if (igmptable[slot].group.s_addr == group)
+ return;
+ if (igmptable[slot].group.s_addr) {
+ leave_group(slot);
+ }
+ /* Only join a group if we are given a multicast ip, this way
+ * code can be given a non-multicast (broadcast or unicast ip)
+ * and still work...
+ */
+ if ((group & htonl(MULTICAST_MASK)) == htonl(MULTICAST_NETWORK)) {
+ igmptable[slot].group.s_addr = group;
+ igmptable[slot].time = currticks();
+ }
+}
+#else
+#define send_igmp_reports(now) do {} while(0)
+#define process_igmp(ip, now) do {} while(0)
+#endif
+
+#include "proto_eth_slow.c"
+
+/**************************************************************************
+TCP - Simple-minded TCP stack. Can only send data once and then
+ receive the response. The algorithm for computing window
+ sizes and delaying ack's is currently broken, and thus
+ disabled. Performance would probably improve a little, if
+ this gets fixed. FIXME
+**************************************************************************/
+#ifdef DOWNLOAD_PROTO_HTTP
+static int await_tcp(int ival, void *ptr, unsigned short ptype __unused,
+ struct iphdr *ip, struct udphdr *udp __unused,
+ struct tcphdr *tcp)
+{
+ if (!tcp) {
+ return 0;
+ }
+ if (arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr)
+ return 0;
+ if (ntohs(tcp->dst) != ival) {
+ tcp_reset(ip);
+ return 0;
+ }
+ *(void **)ptr = tcp;
+ return 1;
+}
+
+int tcp_transaction(unsigned long destip, unsigned int destsock, void *ptr,
+ int (*send)(int len, void *buf, void *ptr),
+ int (*recv)(int len, const void *buf, void *ptr)) {
+ static uint16_t srcsock = 0;
+ int rc = 1;
+ long send_seq = currticks();
+ long recv_seq = 0;
+ int can_send = 0;
+ int sent_all = 0;
+ struct iphdr *ip;
+ struct tcphdr *tcp;
+ int ctrl = SYN;
+ char buf[128]; /* Small outgoing buffer */
+ long payload;
+ int header_size;
+ int window = 3*TCP_MIN_WINDOW;
+ long last_ack = 0;
+ long last_sent = 0;
+ long rtt = 0;
+ long srtt = 0;
+ long rto = TCP_INITIAL_TIMEOUT;
+ int retry = TCP_MAX_TIMEOUT/TCP_INITIAL_TIMEOUT;
+ enum { CLOSED, SYN_RCVD, ESTABLISHED,
+ FIN_WAIT_1, FIN_WAIT_2 } state = CLOSED;
+
+ if (!srcsock) {
+ srcsock = currticks();
+ }
+ if (++srcsock < 1024)
+ srcsock += 1024;
+
+ await_reply(await_qdrain, 0, NULL, 0);
+
+ send_data:
+ if (ctrl & ACK)
+ last_ack = recv_seq;
+ if (!tcp_transmit(destip, srcsock, destsock, send_seq,
+ recv_seq, window, ctrl,
+ sizeof(struct iphdr) + sizeof(struct tcphdr)+
+ can_send, buf)) {
+ return (0);
+ }
+ last_sent = currticks();
+
+ recv_data:
+ if (!await_reply(await_tcp, srcsock, &tcp,
+ (state == ESTABLISHED && !can_send)
+ ? TCP_MAX_TIMEOUT : rto)) {
+ if (state == ESTABLISHED) {
+ close:
+ ctrl = FIN|ACK;
+ state = FIN_WAIT_1;
+ rc = 0;
+ goto send_data;
+ }
+
+ if (state == FIN_WAIT_1 || state == FIN_WAIT_2)
+ return (rc);
+
+ if (--retry <= 0) {
+ /* time out */
+ if (state == SYN_RCVD) {
+ tcp_transmit(destip, srcsock, destsock,
+ send_seq, 0, window, RST,
+ sizeof(struct iphdr) +
+ sizeof(struct tcphdr), buf);
+ }
+ return (0);
+ }
+ /* retransmit */
+ goto send_data;
+ }
+ got_data:
+ retry = TCP_MAX_RETRY;
+
+ if (tcp->ctrl & htons(ACK) ) {
+ char *data;
+ int syn_ack, consumed;
+
+ if (state == FIN_WAIT_1 || state == FIN_WAIT_2) {
+ state = FIN_WAIT_2;
+ ctrl = ACK;
+ goto consume_data;
+ }
+ syn_ack = state == CLOSED || state == SYN_RCVD;
+ consumed = ntohl(tcp->ack) - send_seq - syn_ack;
+ if (consumed < 0 || consumed > can_send) {
+ tcp_reset((struct iphdr *)&nic.packet[ETH_HLEN]);
+ goto recv_data;
+ }
+
+ rtt = currticks() - last_sent;
+ srtt = !srtt ? rtt : (srtt*4 + rtt)/5;
+ rto = srtt + srtt/2;
+ if (rto < TCP_MIN_TIMEOUT)
+ rto = TCP_MIN_TIMEOUT;
+ else if (rto > TCP_MAX_TIMEOUT)
+ rto = TCP_MAX_TIMEOUT;
+
+ can_send -= consumed;
+ send_seq += consumed + syn_ack;
+ data = buf + sizeof(struct iphdr) + sizeof(struct tcphdr);
+ if (can_send) {
+ memmove(data, data + consumed, can_send);
+ }
+ if (!sent_all) {
+ int more_data;
+ data += can_send;
+ more_data = buf + sizeof(buf) - data;
+ if (more_data > 0) {
+ more_data = send(more_data, data, ptr);
+ can_send += more_data;
+ }
+ sent_all = !more_data;
+ }
+ if (state == SYN_RCVD) {
+ state = ESTABLISHED;
+ ctrl = PSH|ACK;
+ goto consume_data;
+ }
+ if (tcp->ctrl & htons(RST))
+ return (0);
+ } else if (tcp->ctrl & htons(RST)) {
+ if (state == CLOSED)
+ goto recv_data;
+ return (0);
+ }
+
+ consume_data:
+ ip = (struct iphdr *)&nic.packet[ETH_HLEN];
+ header_size = sizeof(struct iphdr) + ((ntohs(tcp->ctrl)>>10)&0x3C);
+ payload = ntohs(ip->len) - header_size;
+ if (payload > 0 && state == ESTABLISHED) {
+ int old_bytes = recv_seq - (long)ntohl(tcp->seq);
+ if (old_bytes >= 0 && payload - old_bytes > 0) {
+ recv_seq += payload - old_bytes;
+ if (state != FIN_WAIT_1 && state != FIN_WAIT_2 &&
+ !recv(payload - old_bytes,
+ &nic.packet[ETH_HLEN+header_size+old_bytes],
+ ptr)) {
+ goto close;
+ }
+ if ((state == ESTABLISHED || state == SYN_RCVD) &&
+ !(tcp->ctrl & htons(FIN))) {
+ int in_window = window - 2*TCP_MIN_WINDOW >
+ recv_seq - last_ack;
+ ctrl = can_send ? PSH|ACK : ACK;
+ if (!can_send && in_window) {
+/* Window scaling is broken right now, just fall back to acknowledging every */
+/* packet immediately and unconditionally. FIXME */ /***/
+/* if (await_reply(await_tcp, srcsock,
+ &tcp, rto))
+ goto got_data;
+ else */
+ goto send_data;
+ }
+ if (!in_window) {
+ window += TCP_MIN_WINDOW;
+ if (window > TCP_MAX_WINDOW)
+ window = TCP_MAX_WINDOW;
+ }
+ goto send_data;
+ }
+ } else {
+ /* saw old data again, must have lost packets */
+ window /= 2;
+ if (window < 2*TCP_MIN_WINDOW)
+ window = 2*TCP_MIN_WINDOW;
+ }
+ }
+
+ if (tcp->ctrl & htons(FIN)) {
+ if (state == ESTABLISHED) {
+ ctrl = FIN|ACK;
+ } else if (state == FIN_WAIT_1 || state == FIN_WAIT_2) {
+ ctrl = ACK;
+ } else {
+ ctrl = RST;
+ }
+ return (tcp_transmit(destip, srcsock, destsock,
+ send_seq, recv_seq + 1, window, ctrl,
+ sizeof(struct iphdr) +
+ sizeof(struct tcphdr), buf) &&
+ (state == ESTABLISHED ||
+ state == FIN_WAIT_1 || state == FIN_WAIT_2) &&
+ !can_send);
+ }
+
+ if (state == CLOSED) {
+ if (tcp->ctrl & htons(SYN)) {
+ recv_seq = ntohl(tcp->seq) + 1;
+ if (!(tcp->ctrl & htons(ACK))) {
+ state = SYN_RCVD;
+ ctrl = SYN|ACK|PSH;
+ goto send_data;
+ } else {
+ state = ESTABLISHED;
+ ctrl = PSH|ACK;
+ }
+ }
+ }
+
+ if (can_send || payload) {
+ goto send_data;
+ }
+ goto recv_data;
+}
+#endif
+
+/**************************************************************************
+AWAIT_REPLY - Wait until we get a response for our request
+************f**************************************************************/
+int await_reply(reply_t reply, int ival, void *ptr, long timeout)
+{
+ unsigned long time, now;
+ struct iphdr *ip;
+ unsigned iplen = 0;
+ struct udphdr *udp;
+ struct tcphdr *tcp;
+ unsigned short ptype;
+ int result;
+
+ time = timeout + currticks();
+ /* The timeout check is done below. The timeout is only checked if
+ * there is no packet in the Rx queue. This assumes that eth_poll()
+ * needs a negligible amount of time.
+ */
+ for (;;) {
+ now = currticks();
+ send_eth_slow_reports(now);
+ send_igmp_reports(now);
+ result = eth_poll(1);
+ if (result == 0) {
+ /* We don't have anything */
+
+ /* Check for abort key only if the Rx queue is empty -
+ * as long as we have something to process, don't
+ * assume that something failed. It is unlikely that
+ * we have no processing time left between packets. */
+ poll_interruptions();
+ /* Do the timeout after at least a full queue walk. */
+ if ((timeout == 0) || (currticks() > time)) {
+ break;
+ }
+ continue;
+ }
+
+ /* We have something! */
+
+ /* Find the Ethernet packet type */
+ if (nic.packetlen >= ETH_HLEN) {
+ ptype = ((unsigned short) nic.packet[12]) << 8
+ | ((unsigned short) nic.packet[13]);
+ } else continue; /* what else could we do with it? */
+ /* Verify an IP header */
+ ip = 0;
+ if ((ptype == ETH_P_IP) && (nic.packetlen >= ETH_HLEN + sizeof(struct iphdr))) {
+ unsigned ipoptlen;
+ ip = (struct iphdr *)&nic.packet[ETH_HLEN];
+ if ((ip->verhdrlen < 0x45) || (ip->verhdrlen > 0x4F))
+ continue;
+ iplen = (ip->verhdrlen & 0xf) * 4;
+ if (ipchksum(ip, iplen) != 0)
+ continue;
+ if (ip->frags & htons(0x3FFF)) {
+ static int warned_fragmentation = 0;
+ if (!warned_fragmentation) {
+ printf("ALERT: got a fragmented packet - reconfigure your server\n");
+ warned_fragmentation = 1;
+ }
+ continue;
+ }
+ if (ntohs(ip->len) > ETH_MAX_MTU)
+ continue;
+
+ ipoptlen = iplen - sizeof(struct iphdr);
+ if (ipoptlen) {
+ /* Delete the ip options, to guarantee
+ * good alignment, and make etherboot simpler.
+ */
+ memmove(&nic.packet[ETH_HLEN + sizeof(struct iphdr)],
+ &nic.packet[ETH_HLEN + iplen],
+ nic.packetlen - ipoptlen);
+ nic.packetlen -= ipoptlen;
+ }
+ }
+ udp = 0;
+ if (ip && (ip->protocol == IP_UDP) &&
+ (nic.packetlen >=
+ ETH_HLEN + sizeof(struct iphdr) + sizeof(struct udphdr))) {
+ udp = (struct udphdr *)&nic.packet[ETH_HLEN + sizeof(struct iphdr)];
+
+ /* Make certain we have a reasonable packet length */
+ if (ntohs(udp->len) > (ntohs(ip->len) - iplen))
+ continue;
+
+ if (udp->chksum && tcpudpchksum(ip)) {
+ printf("UDP checksum error\n");
+ continue;
+ }
+ }
+ tcp = 0;
+#ifdef DOWNLOAD_PROTO_HTTP
+ if (ip && (ip->protocol == IP_TCP) &&
+ (nic.packetlen >=
+ ETH_HLEN + sizeof(struct iphdr) + sizeof(struct tcphdr))){
+ tcp = (struct tcphdr *)&nic.packet[ETH_HLEN +
+ sizeof(struct iphdr)];
+ /* Make certain we have a reasonable packet length */
+ if (((ntohs(tcp->ctrl) >> 10) & 0x3C) >
+ ntohs(ip->len) - (int)iplen)
+ continue;
+ if (tcpudpchksum(ip)) {
+ printf("TCP checksum error\n");
+ continue;
+ }
+
+ }
+#endif
+ result = reply(ival, ptr, ptype, ip, udp, tcp);
+ if (result > 0) {
+ return result;
+ }
+
+ /* If it isn't a packet the upper layer wants see if there is a default
+ * action. This allows us reply to arp, igmp, and lacp queries.
+ */
+ if ((ptype == ETH_P_ARP) &&
+ (nic.packetlen >= ETH_HLEN + sizeof(struct arprequest))) {
+ struct arprequest *arpreply;
+ unsigned long tmp;
+
+ arpreply = (struct arprequest *)&nic.packet[ETH_HLEN];
+ memcpy(&tmp, arpreply->tipaddr, sizeof(in_addr));
+ if ((arpreply->opcode == htons(ARP_REQUEST)) &&
+ (tmp == arptable[ARP_CLIENT].ipaddr.s_addr)) {
+ arpreply->opcode = htons(ARP_REPLY);
+ memcpy(arpreply->tipaddr, arpreply->sipaddr, sizeof(in_addr));
+ memcpy(arpreply->thwaddr, arpreply->shwaddr, ETH_ALEN);
+ memcpy(arpreply->sipaddr, &arptable[ARP_CLIENT].ipaddr, sizeof(in_addr));
+ memcpy(arpreply->shwaddr, arptable[ARP_CLIENT].node, ETH_ALEN);
+ eth_transmit(arpreply->thwaddr, ETH_P_ARP,
+ sizeof(struct arprequest),
+ arpreply);
+#ifdef MDEBUG
+ memcpy(&tmp, arpreply->tipaddr, sizeof(in_addr));
+ printf("Sent ARP reply to: %@\n",tmp);
+#endif /* MDEBUG */
+ }
+ }
+ process_eth_slow(ptype, now);
+ process_igmp(ip, now);
+ }
+ return(0);
+}
+
+#ifdef REQUIRE_VCI_ETHERBOOT
+/**************************************************************************
+FIND_VCI_ETHERBOOT - Looks for "Etherboot" in Vendor Encapsulated Identifiers
+On entry p points to byte count of VCI options
+**************************************************************************/
+static int find_vci_etherboot(unsigned char *p)
+{
+ unsigned char *end = p + 1 + *p;
+
+ for (p++; p < end; ) {
+ if (*p == RFC2132_VENDOR_CLASS_ID) {
+ if (strncmp("Etherboot", p + 2, sizeof("Etherboot") - 1) == 0)
+ return (1);
+ } else if (*p == RFC1533_END)
+ return (0);
+ p += TAG_LEN(p) + 2;
+ }
+ return (0);
+}
+#endif /* REQUIRE_VCI_ETHERBOOT */
+
+/**************************************************************************
+DECODE_RFC1533 - Decodes RFC1533 header
+**************************************************************************/
+int decode_rfc1533(unsigned char *p, unsigned int block, unsigned int len, int eof)
+{
+ static unsigned char *extdata = NULL, *extend = NULL;
+ unsigned char *extpath = NULL;
+ unsigned char *endp;
+ static unsigned char in_encapsulated_options = 0;
+
+ if (eof == -1) {
+ /* Encapsulated option block */
+ endp = p + len;
+ }
+ else if (block == 0) {
+#ifdef REQUIRE_VCI_ETHERBOOT
+ vci_etherboot = 0;
+#endif
+ end_of_rfc1533 = NULL;
+#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
+ */
+ vendorext_isvalid = 1;
+#ifdef FREEBSD_KERNEL_ENV
+ memcpy(freebsd_kernel_env, FREEBSD_KERNEL_ENV,
+ sizeof(FREEBSD_KERNEL_ENV));
+ /* FREEBSD_KERNEL_ENV had better be a string constant */
+#else
+ freebsd_kernel_env[0]='\0';
+#endif
+#else
+ vendorext_isvalid = 0;
+#endif
+ if (memcmp(p, rfc1533_cookie, 4))
+ return(0); /* no RFC 1533 header found */
+ p += 4;
+ endp = p + len;
+ } else {
+ if (block == 1) {
+ if (memcmp(p, rfc1533_cookie, 4))
+ return(0); /* no RFC 1533 header found */
+ p += 4;
+ len -= 4; }
+ if (extend + len <= (unsigned char *)&(BOOTP_DATA_ADDR->bootp_extension[MAX_BOOTP_EXTLEN])) {
+ memcpy(extend, p, len);
+ extend += len;
+ } else {
+ printf("Overflow in vendor data buffer! Aborting...\n");
+ *extdata = RFC1533_END;
+ return(0);
+ }
+ p = extdata; endp = extend;
+ }
+ if (!eof)
+ return 1;
+ while (p < endp) {
+ unsigned char c = *p;
+ if (c == RFC1533_PAD) {
+ p++;
+ continue;
+ }
+ else if (c == RFC1533_END) {
+ end_of_rfc1533 = endp = p;
+ continue;
+ }
+ else if (NON_ENCAP_OPT c == RFC1533_NETMASK)
+ memcpy(&netmask, p+2, sizeof(in_addr));
+ else if (NON_ENCAP_OPT c == RFC1533_GATEWAY) {
+ /* This is a little simplistic, but it will
+ usually be sufficient.
+ Take only the first entry */
+ if (TAG_LEN(p) >= sizeof(in_addr))
+ memcpy(&arptable[ARP_GATEWAY].ipaddr, p+2, sizeof(in_addr));
+ }
+ else if (c == RFC1533_EXTENSIONPATH)
+ extpath = p;
+#ifndef NO_DHCP_SUPPORT
+#ifdef REQUIRE_VCI_ETHERBOOT
+ else if (NON_ENCAP_OPT c == RFC1533_VENDOR) {
+ vci_etherboot = find_vci_etherboot(p+1);
+#ifdef MDEBUG
+ printf("vci_etherboot %d\n", vci_etherboot);
+#endif
+ }
+#endif /* REQUIRE_VCI_ETHERBOOT */
+ else if (NON_ENCAP_OPT c == RFC2132_MSG_TYPE)
+ dhcp_reply=*(p+2);
+ else if (NON_ENCAP_OPT c == RFC2132_SRV_ID)
+ memcpy(&dhcp_server, p+2, sizeof(in_addr));
+#endif /* NO_DHCP_SUPPORT */
+ else if (NON_ENCAP_OPT c == RFC1533_HOSTNAME) {
+ hostname = p + 2;
+ hostnamelen = *(p + 1);
+ }
+ else if (ENCAP_OPT c == RFC1533_VENDOR_MAGIC
+ && TAG_LEN(p) >= 6 &&
+ !memcmp(p+2,vendorext_magic,4) &&
+ p[6] == RFC1533_VENDOR_MAJOR
+ )
+ vendorext_isvalid++;
+ else if (NON_ENCAP_OPT c == RFC1533_VENDOR_ETHERBOOT_ENCAP) {
+ in_encapsulated_options = 1;
+ decode_rfc1533(p+2, 0, TAG_LEN(p), -1);
+ in_encapsulated_options = 0;
+ }
+#ifdef IMAGE_FREEBSD
+ else if (NON_ENCAP_OPT c == RFC1533_VENDOR_HOWTO)
+ freebsd_howto = ((p[2]*256+p[3])*256+p[4])*256+p[5];
+ else if (NON_ENCAP_OPT c == RFC1533_VENDOR_KERNEL_ENV){
+ if(*(p + 1) < sizeof(freebsd_kernel_env)){
+ memcpy(freebsd_kernel_env,p+2,*(p+1));
+ }else{
+ printf("Only support %ld bytes in Kernel Env\n",
+ sizeof(freebsd_kernel_env));
+ }
+ }
+#endif
+#ifdef DNS_RESOLVER
+ else if (NON_ENCAP_OPT c == RFC1533_DNS) {
+ // TODO: Copy the DNS IP somewhere reasonable
+ if (TAG_LEN(p) >= sizeof(in_addr))
+ memcpy(&arptable[ARP_NAMESERVER].ipaddr, p+2, sizeof(in_addr));
+ }
+#endif
+ else {
+#if 0
+ unsigned char *q;
+ printf("Unknown RFC1533-tag ");
+ for(q=p;q<p+2+TAG_LEN(p);q++)
+ printf("%hhX ",*q);
+ putchar('\n');
+#endif
+ }
+ p += TAG_LEN(p) + 2;
+ }
+ extdata = extend = endp;
+ if (block <= 0 && extpath != NULL) {
+ char fname[64];
+ memcpy(fname, extpath+2, TAG_LEN(extpath));
+ fname[(int)TAG_LEN(extpath)] = '\0';
+ printf("Loading BOOTP-extension file: %s\n",fname);
+ tftp(fname, decode_rfc1533);
+ }
+ return 1; /* proceed with next block */
+}
+
+
+/* FIXME double check TWO_SECOND_DIVISOR */
+#define TWO_SECOND_DIVISOR (RAND_MAX/TICKS_PER_SEC)
+/**************************************************************************
+RFC2131_SLEEP_INTERVAL - sleep for expotentially longer times (base << exp) +- 1 sec)
+**************************************************************************/
+long rfc2131_sleep_interval(long base, int exp)
+{
+ unsigned long tmo;
+#ifdef BACKOFF_LIMIT
+ if (exp > BACKOFF_LIMIT)
+ exp = BACKOFF_LIMIT;
+#endif
+ tmo = (base << exp) + (TICKS_PER_SEC - (random()/TWO_SECOND_DIVISOR));
+ return tmo;
+}
+
+#ifdef MULTICAST_LEVEL2
+/**************************************************************************
+RFC1112_SLEEP_INTERVAL - sleep for expotentially longer times, up to (base << exp)
+**************************************************************************/
+long rfc1112_sleep_interval(long base, int exp)
+{
+ unsigned long divisor, tmo;
+#ifdef BACKOFF_LIMIT
+ if (exp > BACKOFF_LIMIT)
+ exp = BACKOFF_LIMIT;
+#endif
+ divisor = RAND_MAX/(base << exp);
+ tmo = random()/divisor;
+ return tmo;
+}
+#endif /* MULTICAST_LEVEL_2 */
diff --git a/src/core/osloader.c b/src/core/osloader.c
new file mode 100644
index 00000000..ae67b34d
--- /dev/null
+++ b/src/core/osloader.c
@@ -0,0 +1,365 @@
+/**************************************************************************
+OS loader
+
+Author: Markus Gutschke (gutschk@math.uni-muenster.de)
+ Date: Sep/95
+Modifications: Ken Yap (for Etherboot/16)
+ Doug Ambrisko (ELF and a.out support)
+ Klaus Espenlaub (rewrote ELF and a.out (did it really work before?) support,
+ added ELF Multiboot images). Someone should merge the ELF and a.out
+ loaders, as most of the code is now identical. Maybe even NBI could be
+ rewritten and merged into the generic loading framework. This should
+ save quite a few bytes of code if you have selected more than one format.
+ Ken Yap (Jan 2001)
+ Added support for linear entry addresses in tagged images,
+ which allows a more efficient protected mode call instead of
+ going to real mode and back. Also means entry addresses > 1 MB can
+ be called. Conditional on the LINEAR_EXEC_ADDR bit.
+ Added support for Etherboot extension calls. Conditional on the
+ TAGGED_PROGRAM_RETURNS bit. Implies LINEAR_EXEC_ADDR.
+ Added support for non-MULTIBOOT ELF which also supports Etherboot
+ extension calls. Conditional on the ELF_PROGRAM_RETURNS bit.
+
+**************************************************************************/
+
+/*
+ * 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.
+ */
+
+#include "etherboot.h"
+
+struct os_entry_regs os_regs;
+
+static struct ebinfo loaderinfo = {
+ VERSION_MAJOR, VERSION_MINOR,
+ 0
+};
+
+#define LOAD_DEBUG 0
+
+static int prep_segment(unsigned long start, unsigned long mid, unsigned long end,
+ unsigned long istart, unsigned long iend);
+static unsigned long find_segment(unsigned long size, unsigned long align);
+static sector_t dead_download ( unsigned char *data, unsigned int len, int eof);
+static void done(int do_cleanup);
+
+#if defined(IMAGE_FREEBSD) && defined(ELF_IMAGE)
+static void elf_freebsd_probe(void);
+static void elf_freebsd_fixup_segment(void);
+static void elf_freebsd_find_segment_end(void);
+static int elf_freebsd_debug_loader(unsigned int offset);
+static void elf_freebsd_boot(unsigned long entry);
+#else
+#define elf_freebsd_probe() do {} while(0)
+#define elf_freebsd_fixup_segment() do {} while(0)
+#define elf_freebsd_find_segment_end() do {} while(0)
+#define elf_freebsd_debug_loader(off) (0)
+#define elf_freebsd_boot(entry) do {} while(0)
+#endif
+#if defined(IMAGE_FREEBSD) && defined(AOUT_IMAGE)
+static void aout_freebsd_probe(void);
+static void aout_freebsd_boot(void);
+#else
+#define aout_freebsd_probe() do {} while(0)
+#define aout_freebsd_boot() do {} while(0)
+#endif
+
+/**************************************************************************
+dead_download - Restart etherboot if probe image fails
+**************************************************************************/
+static sector_t dead_download ( unsigned char *data __unused, unsigned int len __unused, int eof __unused) {
+ longjmp(restart_etherboot, -2);
+}
+
+#ifdef IMAGE_MULTIBOOT
+#include "../arch/i386/core/multiboot_loader.c"
+#else
+#define multiboot_probe(data, len) do {} while(0)
+#define multiboot_boot(entry) do {} while(0)
+#endif
+
+
+#ifdef WINCE_IMAGE
+#include "../arch/i386/core/wince_loader.c"
+#endif
+
+#ifdef AOUT_IMAGE
+#include "../arch/i386/core/aout_loader.c"
+#endif
+
+#ifdef TAGGED_IMAGE
+#include "../arch/i386/core/tagged_loader.c"
+#endif
+
+#if defined(ELF_IMAGE) || defined(ELF64_IMAGE)
+#include "elf_loader.c"
+#endif
+
+#if defined(COFF_IMAGE)
+#include "../arch/e1/core/coff_loader.c"
+#endif
+
+#ifdef IMAGE_FREEBSD
+#include "../arch/i386/core/freebsd_loader.c"
+#endif
+
+#ifdef PXE_IMAGE
+#include "../arch/i386/core/pxe_loader.c"
+#endif
+
+#ifdef RAW_IMAGE
+#include "../arch/armnommu/core/raw_loader.c"
+#endif
+
+static void done(int do_cleanup)
+{
+#ifdef SIZEINDICATOR
+ printf("K ");
+#endif
+ printf("done\n");
+ /* We may not want to do the cleanup: when booting a PXE
+ * image, for example, we need to leave the network card
+ * enabled, and it helps debugging if the serial console
+ * remains enabled. The call the cleanup() will be triggered
+ * when the PXE stack is shut down.
+ */
+ if ( do_cleanup ) {
+ cleanup();
+ arch_on_exit(0);
+ }
+}
+
+static int prep_segment(unsigned long start, unsigned long mid, unsigned long end,
+ unsigned long istart __unused, unsigned long iend __unused)
+{
+ unsigned fit, i;
+
+#if LOAD_DEBUG
+ printf ( "\nAbout to prepare segment [%lX,%lX)\n", start, end );
+ sleep ( 3 );
+#endif
+
+ if (mid > end) {
+ printf("filesz > memsz\n");
+ return 0;
+ }
+ if ((end > virt_to_phys(_text)) &&
+ (start < virt_to_phys(_end))) {
+ printf("segment [%lX, %lX) overlaps etherboot [%lX, %lX)\n",
+ start, end,
+ virt_to_phys(_text), virt_to_phys(_end)
+ );
+ return 0;
+ }
+ if ((end > heap_ptr) && (start < heap_bot)) {
+ printf("segment [%lX, %lX) overlaps heap [%lX, %lX)\n",
+ start, end,
+ heap_ptr, heap_bot
+ );
+ return 0;
+ }
+ fit = 0;
+ for(i = 0; i < meminfo.map_count; i++) {
+ unsigned long long r_start, r_end;
+ if (meminfo.map[i].type != E820_RAM)
+ continue;
+ r_start = meminfo.map[i].addr;
+ r_end = r_start + meminfo.map[i].size;
+ if ((start >= r_start) && (end <= r_end)) {
+ fit = 1;
+ break;
+ }
+ }
+ if (!fit) {
+ printf("\nsegment [%lX,%lX) does not fit in any memory region\n",
+ start, end);
+#if LOAD_DEBUG
+ printf("Memory regions(%d):\n", meminfo.map_count);
+ for(i = 0; i < meminfo.map_count; i++) {
+ unsigned long long r_start, r_end;
+ if (meminfo.map[i].type != E820_RAM)
+ continue;
+ r_start = meminfo.map[i].addr;
+ r_end = r_start + meminfo.map[i].size;
+ printf("[%X%X, %X%X) type %d\n",
+ (unsigned long)(r_start >> 32),
+ (unsigned long)r_start,
+ (unsigned long)(r_end >> 32),
+ (unsigned long)r_end,
+ meminfo.map[i].type);
+ }
+#endif
+ return 0;
+ }
+#if LOAD_DEBUG
+ /* Zap the whole lot. Do this so that if we're treading on
+ * anything, it shows up now, when the debug message is
+ * visible, rather than when we're partway through downloading
+ * the file.
+ *
+ * If you see an entire screen full of exclamation marks, then
+ * you've almost certainly written all over the display RAM.
+ * This is likely to happen if the status of the A20 line gets
+ * screwed up. Of course, if this happens, it's a good bet
+ * that you've also trashed the whole of low memory, so expect
+ * interesting things to happen...
+ */
+ memset(phys_to_virt(start), '!', mid - start);
+#endif
+ /* Zero the bss */
+ if (end > mid) {
+ memset(phys_to_virt(mid), 0, end - mid);
+ }
+ return 1;
+}
+
+static unsigned long find_segment(unsigned long size, unsigned long align)
+{
+ unsigned i;
+ /* Verify I have a power of 2 alignment */
+ if (align & (align - 1)) {
+ return ULONG_MAX;
+ }
+ for(i = 0; i < meminfo.map_count; i++) {
+ unsigned long r_start, r_end;
+ if (meminfo.map[i].type != E820_RAM)
+ continue;
+ if ((meminfo.map[i].addr + meminfo.map[i].size) > ULONG_MAX) {
+ continue;
+ }
+ r_start = meminfo.map[i].addr;
+ r_end = r_start + meminfo.map[i].size;
+ /* Don't allow the segment to overlap etherboot */
+ if ((r_end > virt_to_phys(_text)) && (r_start < virt_to_phys(_text))) {
+ r_end = virt_to_phys(_text);
+ }
+ if ((r_start > virt_to_phys(_text)) && (r_start < virt_to_phys(_end))) {
+ r_start = virt_to_phys(_end);
+ }
+ /* Don't allow the segment to overlap the heap */
+ if ((r_end > heap_ptr) && (r_start < heap_ptr)) {
+ r_end = heap_ptr;
+ }
+ if ((r_start > heap_ptr) && (r_start < heap_bot)) {
+ r_start = heap_ptr;
+ }
+ r_start = (r_start + align - 1) & ~(align - 1);
+ if ((r_end >= r_start) && ((r_end - r_start) >= size)) {
+ return r_start;
+ }
+ }
+ /* I did not find anything :( */
+ return ULONG_MAX;
+}
+
+/**************************************************************************
+PROBE_IMAGE - Detect image file type
+**************************************************************************/
+os_download_t probe_image(unsigned char *data, unsigned int len)
+{
+ os_download_t os_download = 0;
+#ifdef AOUT_IMAGE
+ if (!os_download) os_download = aout_probe(data, len);
+#endif
+#ifdef ELF_IMAGE
+ if (!os_download) os_download = elf32_probe(data, len);
+#endif
+#ifdef ELF64_IMAGE
+ if (!os_download) os_download = elf64_probe(data, len);
+#endif
+#ifdef COFF_IMAGE
+ if (!os_download) os_download = coff_probe(data, len);
+#endif
+#ifdef WINCE_IMAGE
+ if (!os_download) os_download = wince_probe(data, len);
+#endif
+#ifdef TAGGED_IMAGE
+ if (!os_download) os_download = tagged_probe(data, len);
+#endif
+/* PXE_IMAGE must always be last */
+#ifdef PXE_IMAGE
+ if (!os_download) os_download = pxe_probe(data, len);
+#endif
+#ifdef RAW_IMAGE
+ if (!os_download) os_download = raw_probe(data, len);
+#endif
+ return os_download;
+}
+
+/**************************************************************************
+LOAD_BLOCK - Try to load file
+**************************************************************************/
+int load_block(unsigned char *data, unsigned int block, unsigned int len, int eof)
+{
+ static os_download_t os_download;
+ static sector_t skip_sectors;
+ static unsigned int skip_bytes;
+#ifdef SIZEINDICATOR
+ static int rlen = 0;
+
+ if (block == 1)
+ {
+ rlen=len;
+ printf("XXXX");
+ }
+ if (!(block % 4) || eof) {
+ int size;
+ size = ((block-1) * rlen + len) / 1024;
+
+ putchar('\b');
+ putchar('\b');
+ putchar('\b');
+ putchar('\b');
+
+ putchar('0' + (size/1000)%10);
+ putchar('0' + (size/100)%10);
+ putchar('0' + (size/10)%10);
+ putchar('0' + (size/1)%10);
+ }
+#endif
+ if (block == 1)
+ {
+ skip_sectors = 0;
+ skip_bytes = 0;
+ os_download = probe_image(data, len);
+ if (!os_download) {
+ printf("error: not a valid image\n");
+#if 0
+ printf("block: %d len: %d\n", block, len);
+ printf("%hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx\n",
+ data[0], data[1], data[2], data[3],
+ data[4], data[5], data[6], data[7]);
+#endif
+ return 0;
+ }
+ } /* end of block zero processing */
+
+ /* Either len is greater or the skip is greater */
+ if ((skip_sectors > (len >> 9)) ||
+ ((skip_sectors == (len >> 9)) && (skip_bytes >= (len & 0x1ff)))) {
+ /* If I don't have enough bytes borrow them from skip_sectors */
+ if (skip_bytes < len) {
+ skip_sectors -= (len - skip_bytes + 511) >> 9;
+ skip_bytes += (len - skip_bytes + 511) & ~0x1ff;
+ }
+ skip_bytes -= len;
+ }
+ else {
+ len -= (skip_sectors << 9) + skip_bytes;
+ data += (skip_sectors << 9) + skip_bytes;
+ }
+ skip_sectors = os_download(data, len, eof);
+ skip_bytes = 0;
+
+ return 1;
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
+
diff --git a/src/core/pc_kbd.c b/src/core/pc_kbd.c
new file mode 100644
index 00000000..9fe82924
--- /dev/null
+++ b/src/core/pc_kbd.c
@@ -0,0 +1,108 @@
+/* Minimal polling PC keyboard driver
+ * - No interrupt
+ * - No LED
+ * - No special keys
+ *
+ * still Enough For Me to type a filename.
+ *
+ * 2003-07 by SONE Takesh
+ * 2004-04 moved by LYH From filo to Etherboot
+ * yhlu@tyan.com
+ */
+#ifdef CONSOLE_PC_KBD
+#include "etherboot.h"
+
+static char key_map[][128] = {
+ {
+ "\0\x1b""1234567890-=\b\t"
+ "qwertyuiop[]\r\0as"
+ "dfghjkl;'`\0\\zxcv"
+ "bnm,./\0*\0 \0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0""789-456+1"
+ "230."
+ },{
+ "\0\x1b""!@#$%^&*()_+\b\t"
+ "QWERTYUIOP{}\r\0AS"
+ "DFGHJKL:\"~\0|ZXCV"
+ "BNM<>?\0\0\0 \0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0""789-456+1"
+ "230."
+ }
+};
+
+static int cur_scan;
+static unsigned int shift_state;
+#define SHIFT 1
+#define CONTROL 2
+#define CAPS 4
+
+static int get_scancode(void)
+{
+ int scan;
+
+ if ((inb(0x64) & 1) == 0)
+ return 0;
+ scan = inb(0x60);
+
+ switch (scan) {
+ case 0x2a:
+ case 0x36:
+ shift_state |= SHIFT;
+ break;
+ case 0xaa:
+ case 0xb6:
+ shift_state &= ~SHIFT;
+ break;
+ case 0x1d:
+ shift_state |= CONTROL;
+ break;
+ case 0x9d:
+ shift_state &= ~CONTROL;
+ break;
+ case 0x3a:
+ shift_state ^= CAPS;
+ break;
+ }
+
+ if (scan & 0x80)
+ return 0; /* ignore break code or 0xe0 etc! */
+ return scan;
+}
+
+int kbd_havekey(void)
+{
+ if (!cur_scan)
+ cur_scan = get_scancode();
+ return cur_scan != 0;
+}
+
+int kbd_ischar(void)
+{
+ if (!kbd_havekey())
+ return 0;
+ if (!key_map[shift_state & SHIFT][cur_scan]) {
+ cur_scan = 0;
+ return 0;
+ }
+ return 1;
+}
+
+int kbd_getc(void)
+{
+ int c;
+
+ while (!kbd_ischar())
+ ;
+ c = key_map[shift_state & SHIFT][cur_scan];
+ if (shift_state & (CONTROL | CAPS)) {
+ if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
+ if (shift_state & CONTROL)
+ c &= 0x1f;
+ else if (shift_state & CAPS)
+ c ^= ('A' ^ 'a');
+ }
+ }
+ cur_scan = 0;
+ return c;
+}
+#endif
diff --git a/src/core/pci.c b/src/core/pci.c
new file mode 100644
index 00000000..8f89d8df
--- /dev/null
+++ b/src/core/pci.c
@@ -0,0 +1,337 @@
+#ifdef CONFIG_PCI
+
+/*
+ * 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.
+ */
+
+#include "etherboot.h"
+#include "pci.h"
+
+/*#define DEBUG 1*/
+
+static void scan_drivers(
+ int type,
+ uint32_t class, uint16_t vendor, uint16_t device,
+ const struct pci_driver *last_driver, struct pci_device *dev)
+{
+ const struct pci_driver *skip_driver = last_driver;
+ /* Assume there is only one match of the correct type */
+ const struct pci_driver *driver;
+
+ for(driver = pci_drivers; driver < pci_drivers_end; driver++) {
+ int i;
+ if (driver->type != type)
+ continue;
+ if (skip_driver) {
+ if (skip_driver == driver)
+ skip_driver = 0;
+ continue;
+ }
+ for(i = 0; i < driver->id_count; i++) {
+ if ((vendor == driver->ids[i].vendor) &&
+ (device == driver->ids[i].dev_id)) {
+
+ dev->driver = driver;
+ dev->name = driver->ids[i].name;
+
+ goto out;
+ }
+ }
+ }
+ if (!class) {
+ goto out;
+ }
+ for(driver = pci_drivers; driver < pci_drivers_end; driver++) {
+ if (driver->type != type)
+ continue;
+ if (skip_driver) {
+ if (skip_driver == driver)
+ skip_driver = 0;
+ continue;
+ }
+ if (last_driver == driver)
+ continue;
+ if ((class >> 8) == driver->class) {
+ dev->driver = driver;
+ dev->name = driver->name;
+ goto out;
+ }
+ }
+ out:
+ return;
+}
+
+void scan_pci_bus(int type, struct pci_device *dev)
+{
+ unsigned int first_bus, first_devfn;
+ const struct pci_driver *first_driver;
+ unsigned int devfn, bus, buses;
+ unsigned char hdr_type = 0;
+ uint32_t class;
+ uint16_t vendor, device;
+ uint32_t l, membase, ioaddr, romaddr;
+ uint8_t irq;
+ int reg;
+
+ first_bus = 0;
+ first_devfn = 0;
+ first_driver = 0;
+ if (dev->driver || dev->use_specified) {
+ first_driver = dev->driver;
+ first_bus = dev->bus;
+ first_devfn = dev->devfn;
+ /* Re read the header type on a restart */
+ pcibios_read_config_byte(first_bus, first_devfn & ~0x7,
+ PCI_HEADER_TYPE, &hdr_type);
+ dev->driver = 0;
+ dev->bus = 0;
+ dev->devfn = 0;
+ }
+
+ /* Scan all PCI buses, until we find our card.
+ * We could be smart only scan the required buses but that
+ * is error prone, and tricky.
+ * By scanning all possible pci buses in order we should find
+ * our card eventually.
+ */
+ buses=256;
+ for (bus = first_bus; bus < buses; ++bus) {
+ for (devfn = first_devfn; devfn < 0xff; ++devfn, first_driver = 0) {
+ if (PCI_FUNC (devfn) == 0)
+ pcibios_read_config_byte(bus, devfn, PCI_HEADER_TYPE, &hdr_type);
+ else if (!(hdr_type & 0x80)) /* not a multi-function device */
+ continue;
+ pcibios_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l);
+ /* some broken boards return 0 if a slot is empty: */
+ if (l == 0xffffffff || l == 0x00000000) {
+ continue;
+ }
+ vendor = l & 0xffff;
+ device = (l >> 16) & 0xffff;
+
+ pcibios_read_config_dword(bus, devfn, PCI_REVISION, &l);
+ class = (l >> 8) & 0xffffff;
+#if DEBUG
+ {
+ int i;
+ printf("%hhx:%hhx.%hhx [%hX/%hX] Class %hX\n",
+ bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
+ vendor, device, class >> 8);
+#if DEBUG > 1
+ for(i = 0; i < 256; i++) {
+ unsigned char byte;
+ if ((i & 0xf) == 0) {
+ printf("%hhx: ", i);
+ }
+ pcibios_read_config_byte(bus, devfn, i, &byte);
+ printf("%hhx ", byte);
+ if ((i & 0xf) == 0xf) {
+ printf("\n");
+ }
+ }
+#endif
+
+ }
+#endif
+ scan_drivers(type, class, vendor, device, first_driver, dev);
+ if (!dev->driver)
+ continue;
+
+ dev->devfn = devfn;
+ dev->bus = bus;
+ dev->class = class;
+ dev->vendor = vendor;
+ dev->dev_id = device;
+
+
+ /* Get the ROM base address */
+ pcibios_read_config_dword(bus, devfn,
+ PCI_ROM_ADDRESS, &romaddr);
+ romaddr >>= 10;
+ dev->romaddr = romaddr;
+
+ /* Get the ``membase'' */
+ pcibios_read_config_dword(bus, devfn,
+ PCI_BASE_ADDRESS_1, &membase);
+ dev->membase = membase;
+
+ /* Get the ``ioaddr'' */
+ for (reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4) {
+ pcibios_read_config_dword(bus, devfn, reg, &ioaddr);
+ if ((ioaddr & PCI_BASE_ADDRESS_IO_MASK) == 0 || (ioaddr & PCI_BASE_ADDRESS_SPACE_IO) == 0)
+ continue;
+
+
+ /* Strip the I/O address out of the returned value */
+ ioaddr &= PCI_BASE_ADDRESS_IO_MASK;
+
+ /* Take the first one or the one that matches in boot ROM address */
+ dev->ioaddr = ioaddr;
+ }
+
+ /* Get the irq */
+ pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &irq);
+ if (irq) {
+ pci_read_config_byte(dev, PCI_INTERRUPT_LINE,
+ &irq);
+ }
+ dev->irq = irq;
+
+#if DEBUG > 2
+ printf("Found %s ROM address %#hx\n",
+ dev->name, romaddr);
+#endif
+ return;
+ }
+ first_devfn = 0;
+ }
+ first_bus = 0;
+}
+
+
+
+/*
+ * Set device to be a busmaster in case BIOS neglected to do so.
+ * Also adjust PCI latency timer to a reasonable value, 32.
+ */
+void adjust_pci_device(struct pci_device *p)
+{
+ unsigned short new_command, pci_command;
+ unsigned char pci_latency;
+
+ pcibios_read_config_word(p->bus, p->devfn, PCI_COMMAND, &pci_command);
+ new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO;
+ if (pci_command != new_command) {
+#if DEBUG > 0
+ printf(
+ "The PCI BIOS has not enabled this device!\n"
+ "Updating PCI command %hX->%hX. pci_bus %hhX pci_device_fn %hhX\n",
+ pci_command, new_command, p->bus, p->devfn);
+#endif
+ pcibios_write_config_word(p->bus, p->devfn, PCI_COMMAND, new_command);
+ }
+ pcibios_read_config_byte(p->bus, p->devfn, PCI_LATENCY_TIMER, &pci_latency);
+ if (pci_latency < 32) {
+#if DEBUG > 0
+ printf("PCI latency timer (CFLT) is unreasonably low at %d. Setting to 32 clocks.\n",
+ pci_latency);
+#endif
+ pcibios_write_config_byte(p->bus, p->devfn, PCI_LATENCY_TIMER, 32);
+ }
+}
+
+/*
+ * Find the start of a pci resource.
+ */
+unsigned long pci_bar_start(struct pci_device *dev, unsigned int index)
+{
+ uint32_t lo, hi;
+ unsigned long bar;
+ pci_read_config_dword(dev, index, &lo);
+ if (lo & PCI_BASE_ADDRESS_SPACE_IO) {
+ bar = lo & PCI_BASE_ADDRESS_IO_MASK;
+ } else {
+ bar = 0;
+ if ((lo & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == PCI_BASE_ADDRESS_MEM_TYPE_64) {
+ pci_read_config_dword(dev, index + 4, &hi);
+ if (hi) {
+#if ULONG_MAX > 0xffffffff
+ bar = hi;
+ bar <<=32;
+#else
+ printf("Unhandled 64bit BAR\n");
+ return -1UL;
+#endif
+ }
+ }
+ bar |= lo & PCI_BASE_ADDRESS_MEM_MASK;
+ }
+ return bar + pcibios_bus_base(dev->bus);
+}
+
+/*
+ * Find the size of a pci resource.
+ */
+unsigned long pci_bar_size(struct pci_device *dev, unsigned int bar)
+{
+ uint32_t start, size;
+ /* Save the original bar */
+ pci_read_config_dword(dev, bar, &start);
+ /* Compute which bits can be set */
+ pci_write_config_dword(dev, bar, ~0);
+ pci_read_config_dword(dev, bar, &size);
+ /* Restore the original size */
+ pci_write_config_dword(dev, bar, start);
+ /* Find the significant bits */
+ if (start & PCI_BASE_ADDRESS_SPACE_IO) {
+ size &= PCI_BASE_ADDRESS_IO_MASK;
+ } else {
+ size &= PCI_BASE_ADDRESS_MEM_MASK;
+ }
+ /* Find the lowest bit set */
+ size = size & ~(size - 1);
+ return size;
+}
+
+/**
+ * pci_find_capability - query for devices' capabilities
+ * @dev: PCI device to query
+ * @cap: capability code
+ *
+ * Tell if a device supports a given PCI capability.
+ * Returns the address of the requested capability structure within the
+ * device's PCI configuration space or 0 in case the device does not
+ * support it. Possible values for @cap:
+ *
+ * %PCI_CAP_ID_PM Power Management
+ *
+ * %PCI_CAP_ID_AGP Accelerated Graphics Port
+ *
+ * %PCI_CAP_ID_VPD Vital Product Data
+ *
+ * %PCI_CAP_ID_SLOTID Slot Identification
+ *
+ * %PCI_CAP_ID_MSI Message Signalled Interrupts
+ *
+ * %PCI_CAP_ID_CHSWP CompactPCI HotSwap
+ */
+int pci_find_capability(struct pci_device *dev, int cap)
+{
+ uint16_t status;
+ uint8_t pos, id;
+ uint8_t hdr_type;
+ int ttl = 48;
+
+ pci_read_config_word(dev, PCI_STATUS, &status);
+ if (!(status & PCI_STATUS_CAP_LIST))
+ return 0;
+ pci_read_config_byte(dev, PCI_HEADER_TYPE, &hdr_type);
+ switch (hdr_type & 0x7F) {
+ case PCI_HEADER_TYPE_NORMAL:
+ case PCI_HEADER_TYPE_BRIDGE:
+ default:
+ pci_read_config_byte(dev, PCI_CAPABILITY_LIST, &pos);
+ break;
+ case PCI_HEADER_TYPE_CARDBUS:
+ pci_read_config_byte(dev, PCI_CB_CAPABILITY_LIST, &pos);
+ break;
+ }
+ while (ttl-- && pos >= 0x40) {
+ pos &= ~3;
+ pci_read_config_byte(dev, pos + PCI_CAP_LIST_ID, &id);
+#if DEBUG > 0
+ printf("Capability: %d\n", id);
+#endif
+ if (id == 0xff)
+ break;
+ if (id == cap)
+ return pos;
+ pci_read_config_byte(dev, pos + PCI_CAP_LIST_NEXT, &pos);
+ }
+ return 0;
+}
+
+#endif /* CONFIG_PCI */
diff --git a/src/core/pci_probe.c b/src/core/pci_probe.c
new file mode 100644
index 00000000..d7d55b2d
--- /dev/null
+++ b/src/core/pci_probe.c
@@ -0,0 +1,70 @@
+#ifdef CONFIG_PCI
+/*
+ * 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.
+ */
+
+#include "etherboot.h"
+#include "nic.h"
+#include "pci.h"
+
+void pci_enumerate(void)
+{
+ const struct pci_driver *driver;
+ for(driver = pci_drivers; driver < pci_drivers_end; driver++) {
+ printf("%s ", driver->name);
+ }
+}
+
+int pci_probe(struct dev *dev, const char *type_name)
+{
+/*
+ * NIC probing is in pci device order, followed by the
+ * link order of the drivers. A driver that matches
+ * on vendor and device id will supersede a driver
+ * that matches on pci class.
+ *
+ * If you want to probe for another device behind the same pci
+ * device just increment index. And the previous probe call
+ * will be repeated.
+ */
+ struct pci_probe_state *state = &dev->state.pci;
+ printf("Probing pci %s...\n", type_name);
+ if (dev->how_probe == PROBE_FIRST) {
+ state->advance = 1;
+ state->dev.driver = 0;
+ state->dev.bus = 0;
+ state->dev.devfn = 0;
+ dev->index = -1;
+ }
+ for(;;) {
+ if ((dev->how_probe != PROBE_AWAKE) && state->advance) {
+ find_pci(dev->type, &state->dev);
+ dev->index = -1;
+ }
+ state->advance = 1;
+
+ if (state->dev.driver == 0)
+ break;
+
+ if (dev->how_probe != PROBE_AWAKE) {
+ dev->type_index++;
+ }
+ dev->devid.bus_type = PCI_BUS_TYPE;
+ dev->devid.vendor_id = htons(state->dev.vendor);
+ dev->devid.device_id = htons(state->dev.dev_id);
+ /* FIXME how do I handle dev->index + PROBE_AGAIN?? */
+
+ printf("[%s]", state->dev.name);
+ if (state->dev.driver->probe(dev, &state->dev)) {
+ state->advance = (dev->index == -1);
+ return PROBE_WORKED;
+ }
+ putchar('\n');
+ }
+ return PROBE_FAILED;
+}
+
+#endif
diff --git a/src/core/pcmcia.c b/src/core/pcmcia.c
new file mode 100644
index 00000000..2a7cf2b8
--- /dev/null
+++ b/src/core/pcmcia.c
@@ -0,0 +1,269 @@
+#ifdef CONFIG_PCMCIA
+
+/*
+ * pcmcia.c
+ *
+ * PCMCIA support routines for etherboot - generic stuff
+ *
+ * This code has partly be taken from the linux kernel sources, .../drivers/pcmcia/
+ * Started & put together by
+ * Anselm Martin Hoffmeister
+ * Stockholm Projekt Computer-Service
+ * Sankt Augustin / Bonn, Germany
+ *
+ * Distributed under GPL2
+ */
+
+/*
+ *
+ *
+ * ******************************
+ * PLEASE DO NOT YET WORK ON THIS
+ * ******************************
+ *
+ * I'm still fixing it up on every end, so we most probably would interfere
+ * at some point. If there's anything obvious or better, not-so-obvious,
+ * please contact me by e-mail: anselm (AT) hoffmeister (DOT) be *THANKS*
+ */
+#include "../include/pcmcia.h"
+#include "../include/i82365.h"
+#define CODE_STATUS "alpha"
+#define CODE_VERSION "0.1.3"
+
+#include "../include/pcmcia-opts.h"
+#include "../include/pcmcia.h"
+
+int sockets; /* AHTODO: Phase this out! */
+u_int pccsocks;
+struct pccsock_t pccsock[MAXPCCSOCKS];
+int inited = -1;
+struct pcc_config_t pccconfig[MAXPCCCONFIGS];
+
+struct driver_interact_t driver[] = {
+#ifdef SUPPORT_I82365
+ { I82365, i82365_interfacer, "Intel_82365" },
+#endif
+};
+
+#define NUM_DRIVERS (sizeof(driver)/(sizeof(struct driver_interact_t)))
+
+void sleepticks(int numticks ) {
+ u_int tmo;
+ for (tmo = currticks()+numticks; currticks() < tmo; ) {
+ poll_interruptions();
+ }
+ return;
+}
+
+int pcmcia_init_all(void) {
+ u_int i, j, k, l, m, n, ui, configs = 0;
+ u_int multicard[8];
+ u_char *uc, upc;
+ if ( PDEBUG > 0 ) printf("Initializing PCMCIA subsystem (code-status: " CODE_STATUS ", Version " CODE_VERSION ")\n");
+ if ( PDEBUG > 2 ) {
+ printf ( "Supporting %d driver(s): ", NUM_DRIVERS );
+ for ( i = 0; i < NUM_DRIVERS; ++i ) {
+ printf ( "[%s] ", driver[i].name );
+ }
+ printf ( "\n" );
+ }
+ pccsocks = 0;
+ sockets = 0;
+ // Init all drivers in the driver[] array:
+ for ( i = 0; i < NUM_DRIVERS; ++i ) {
+ driver[i].f(INIT,0,i,0,0); // init needs no params. It uses pccsocks and pccsock[].
+ // Only i tells it which driver_id itself is.
+ }
+ for ( i = 0; i < pccsocks; ++i ) {
+ printf ( "Socket %d: ", i );
+ if ( pccsock[i].status != HASCARD ) {
+ printf ( "is %s: skipping\n", pccsock[i].status == EMPTY? "empty":"[status unknown]" );
+ continue;
+ }
+ if ( 0 != driver[pccsock[i].drivernum].f(MAPATTRMEM,pccsock[i].internalid,MAP_ATTRMEM_TO, MAP_ATTRMEM_LEN,0 ) ) {
+ printf ("PCMCIA controller failed to map attribute memory.\n**** SEVERE ERROR CONDITION. Skipping controller.\n" );
+ if ( PDEBUG > 2 ) {
+ printf ( "<press key. THIS CONDITION SHOULD BE REPORTED!>\n" ); getchar();
+ }
+ continue;
+ }
+ // parse configuration information
+ uc = ioremap ( MAP_ATTRMEM_TO, MAP_ATTRMEM_LEN );
+ pccsock[i].stringoffset = pccsock[i].configoffset = pccsock[i].stringlength = 0;
+ pccsock[i].type = 0xff;
+ for ( l = 0; l < 8; ++l ) multicard[l] = 0;
+ sleepticks(2);
+ for ( l = ui = 0; ui < 0x800; ui += uc[(2*ui)+2] + 2 ) {
+ if ( uc[(2*ui)] == 0xff ) {
+ break;
+ }
+ // This loop is complete rubbish AFAICS.
+ // But without it, my test system won't come up.
+ // It's too bad to develop on broken hardware
+ // - Anselm
+ }
+ sleepticks(2);
+ configs = 0;
+ inited = -1;
+ for ( l = ui = 0; ui < 0x800; ui += uc[(2*ui)+2] + 2 ) {
+ if ( uc[(2*ui)] == 0xff ) break;
+ else if ( uc[2*ui] == 0x15 ) {
+ for ( k = 2 * ( ui + 2 ); ( uc[k] <= ' ' ) && ( k < ( 2 * ( uc[2*(ui+1)] + ui + 2 ) ) ) ; k += 2 ) { ; }
+ pccsock[i].stringoffset = k;
+ pccsock[i].stringlength = ( 2 * ( ui + 2 + uc[(2*ui)+2] ) - k ) / 2;
+ } else if ( uc[2*ui] == 0x21 ) {
+ pccsock[i].type = uc[(2*ui)+4];
+ } else if ( uc[2*ui] == 0x1a ) { // Configuration map
+ printf ( "\nConfig map 0x1a found [" );
+ for ( k = 0; k < uc[2*(ui+1)]; ++k ) {
+ printf ( "%02x ", uc[2*(ui+k+2)] );
+ }
+ printf ( "]\nHighest config available is %d\n", uc[2*(ui+3)] );
+ m = uc[2*(ui+2)];
+ pccsock[i].configoffset = 0;
+ for ( j = 0; j <= m & 3; ++j ) {
+ pccsock[i].configoffset += uc[2*(ui+4+j)] << (8*j);
+ }
+ pccsock[i].rmask0 = 0;
+ for ( j = 0; j <= ( ( ( m & 0x3c ) >> 2 ) & 3 ); ++j ) {
+ pccsock[i].rmask0 += uc[2*(ui+5+(m&3)+j)] << (8*j);
+ }
+ j = pccsock[i].rmask0;
+ printf ( "Config offset is %x, card has regs: < %s%s%s%s%s>\n", pccsock[i].configoffset,
+ j & 1 ? "COR ":"", j & 2 ? "CCSR ":"", j & 4 ? "PRR ":"", j & 8 ? "SCR ":"", j & 16? "ESR ":"" );
+ printf ( "COR + CCSR contents (si/du) %x %x/%x %x\n", uc[pccsock[i].configoffset+0],
+ uc[pccsock[i].configoffset+2],uc[pccsock[i].configoffset*2],uc[(pccsock[i].configoffset*2)+2] );
+ printf ( " " );
+ } else if ( uc[2*ui] == 0x1b ) { // Configuration data entry
+ //printf ( "Config data 0x1b found [\n" );getchar();
+ for ( k = 0; k < uc[2*(ui+1)]; ++k ) {
+ // printf ( "%02x ", uc[2*(ui+k+2)] );
+ }
+ // Parse this tuple into pccconfig[configs]
+ // printf ( "]\n" );
+ if ( configs == MAXPCCCONFIGS ) continue;
+ k = 2*ui+4;
+ pccconfig[configs].index = uc[k] & 0x3f;
+ if ( uc[k] & 0x80 ) {
+ // printf ( "Special config, unsupp. for now\n" );
+ continue;
+ }
+ k+=2;
+ // printf ( "Features: %2x\n", uc[k] );
+ if ( uc[k] & 0x7 ) {
+ // printf ( "Cannot work with Vcc/Timing configs right now\n" );
+ continue;
+ }
+ pccconfig[configs].iowin = pccconfig[configs].iolen = 0;
+ if ( 0 != ( uc[k] & 0x8 ) ) {
+ k+=2;
+ // printf ( "Reading IO config: " );
+ if ( 0 == ( uc[k] & 0x80 ) ) {
+ // printf ( "Cannot work with auto/io config\n" );
+ continue;
+ }
+ k+=2;
+ if ( 0 != ( uc[k] & 0x0f ) ) {
+ // printf ( "Don't support more than 1 iowin right now\n" );
+ continue;
+ }
+ j = (uc[k] & 0x30) >> 4;
+ m = (uc[k] & 0xc0) >> 6;
+ if ( 3 == j ) ++j;
+ if ( 3 == m ) ++m;
+ k += 2;
+ pccconfig[configs].iowin = 0;
+ pccconfig[configs].iolen = 1;
+ for ( n = 0; n < j; ++n, k+=2 ) {
+ pccconfig[configs].iowin += uc[k] << (n*8);
+ }
+ for ( n = 0; n < m; ++n, k+=2 ) {
+ pccconfig[configs].iolen += uc[k] << (n*8);
+ }
+ // printf ( "io %x len %d (%d)\n", pccconfig[configs].iowin, pccconfig[configs].iolen,configs );
+ }
+ for ( j = 0; j < (uc[k] & 3); ++j ) {
+ // pccconfig[configs].iowin += (uc[k+(2*j)+2]) << (8*j);
+ }
+ ++configs;
+ }
+ }
+ if ( pccsock[i].stringoffset > 0 ) { // If no identifier, it's not a valid CIS (as of documentation...)
+ printf ( "[" );
+ for ( k = 0; ( k < pccsock[i].stringlength ) && ( k < 64 ); ++k ) {
+ j = uc[pccsock[i].stringoffset + 2 * k];
+ printf ( "%c", (j>=' '? j:' ' ) );
+ }
+ printf ("]\n is type %d (", pccsock[i].type );
+ switch ( pccsock[i].type ) {
+ case 0x00:
+ printf ( "MULTI" ); break;
+ case 0x01:
+ printf ( "Memory" ); break;
+ case 0x02:
+ printf ( "Serial" ); break;
+ case 0x03:
+ printf ( "Parallel" ); break;
+ case 0x04:
+ printf ( "Fixed" ); break;
+ case 0x05:
+ printf ( "Video" ); break;
+ case 0x06:
+ printf ( "Network" ); break;
+ case 0x07:
+ printf ( "AIMS" ); break;
+ case 0x08:
+ printf ( "SCSI" ); break;
+ case 0x106: // Special / homebrew to say "Multi/network"
+ printf ( "MULTI, with Network" ); break; // AHTODO find a card for this
+ default:
+ printf ( "UNSUPPORTED/UNKNOWN" );
+ }
+ printf ( ") with %d possible configuration(s)\n", configs );
+ // Now set dependency: If it's Network or multi->network, accept
+ if ( (inited <= 0 ) && (6 == (0xff & pccsock[i].type) ) && (0 < configs ) ) {
+ printf ( "activating this device with ioport %x-%x (config #%d)\n",
+ pccconfig[0].iowin, pccconfig[0].iowin+pccconfig[0].iolen-1, pccconfig[0].index );
+ inited = i;
+ // And unmap attrmem ourselves!
+ printf ( "Activating config..." );
+ if ( m=driver[pccsock[i].drivernum].f(SELECTCONFIG,pccsock[i].internalid,pccconfig[0].index,0,&pccconfig[0]) ) {
+ printf ("Failure(%d)!",m); inited = -1;
+ driver[pccsock[i].drivernum].f(UNMAPATTRMEM,pccsock[i].internalid,0,0,0);
+ }
+ printf ( "done!\n" );
+ continue;
+ }
+ } else {
+ printf ( "unsupported - no identifier string found in CIS\n" );
+ }
+ // unmap the PCMCIA device
+ if ( i != inited ) {
+ if ( 0 != driver[pccsock[i].drivernum].f(UNMAPATTRMEM,pccsock[i].internalid,0,0,0) ) {
+ printf ("PCMCIA controller failed to unmap attribute memory.\n**** SEVERE ERROR CONDITION ****\n" );
+ if ( PDEBUG > 2 ) {
+ printf ( "<press key. THIS CONDITION SHOULD BE REPORTED!>\n" ); getchar();
+ }
+ continue;
+ }
+ }
+ }
+ if ( PDEBUG > 2 ) {
+ printf ( "<press key to exit the pcmcia_init_all routine>\n" );
+ getchar();
+ }
+
+ return 0;
+}
+
+int pcmcia_shutdown_all(void) {
+ int i;
+ //if ( PDEBUG > 2 ) {printf("<press key to continue>\n" ); getchar(); }
+ for ( i = 0; i < pccsocks; ++i ) {
+ driver[pccsock[i].drivernum].f(SHUTDOWN,pccsock[i].internalid,0,0,0);
+ }
+ printf("Shutdown of PCMCIA subsystem completed");
+ return 0;
+}
+
+#endif /* CONFIG_PCMCIA */
diff --git a/src/core/proto_eth_slow.c b/src/core/proto_eth_slow.c
new file mode 100644
index 00000000..d19a43e9
--- /dev/null
+++ b/src/core/proto_eth_slow.c
@@ -0,0 +1,407 @@
+/* Copyright 2004 Linux Networx */
+#ifdef PROTO_LACP
+#if 0
+#include "etherboot.h"
+#include "nic.h"
+#include "timer.h"
+#endif
+
+#define LACP_DEBUG 0
+
+/* Structure definitions originally taken from the linux bond_3ad driver */
+
+#define SLOW_DST_MAC "\x01\x80\xc2\x00\x00\x02"
+static const char slow_dest[] = SLOW_DST_MAC;
+
+
+#define SLOW_SUBTYPE_LACP 1
+#define SLOW_SUBTYPE_MARKER 2
+
+struct slow_header {
+ uint8_t subtype;
+};
+
+struct lacp_info {
+ uint16_t system_priority;
+ uint8_t system[ETH_ALEN];
+ uint16_t key;
+ uint16_t port_priority;
+ uint16_t port;
+ uint8_t state;
+ uint8_t reserved[3];
+} PACKED;
+
+#define LACP_CMP_LEN (2 + 6 + 2 + 2 + 2)
+#define LACP_CP_LEN (2 + 6 + 2 + 2 + 2 + 1)
+
+/* Link Aggregation Control Protocol(LACP) data unit structure(43.4.2.2 in the 802.3ad standard) */
+struct slow_lacp {
+ uint8_t subtype; /* = LACP(= 0x01) */
+ uint8_t version_number;
+ uint8_t tlv_type_actor_info; /* = actor information(type/length/value) */
+#define LACP_TLV_TERMINATOR 0
+#define LACP_TLV_ACTOR 1
+#define LACP_TLV_PARTNER 2
+#define LACP_TLV_COLLECTOR 3
+ uint8_t actor_information_length; /* = 20 */
+ struct lacp_info actor;
+ uint8_t tlv_type_partner_info; /* = partner information */
+ uint8_t partner_information_length; /* = 20 */
+ struct lacp_info partner;
+ uint8_t tlv_type_collector_info; /* = collector information */
+ uint8_t collector_information_length; /* = 16 */
+ uint16_t collector_max_delay;
+ uint8_t reserved_12[12];
+ uint8_t tlv_type_terminator; /* = terminator */
+ uint8_t terminator_length; /* = 0 */
+ uint8_t reserved_50[50]; /* = 0 */
+} PACKED;
+
+/* Marker Protocol Data Unit(PDU) structure(43.5.3.2 in the 802.3ad standard) */
+struct slow_marker {
+ uint8_t subtype; /* = 0x02 (marker PDU) */
+ uint8_t version_number; /* = 0x01 */
+ uint8_t tlv_type;
+#define MARKER_TLV_TERMINATOR 0 /* marker terminator */
+#define MARKER_TLV_INFO 1 /* marker information */
+#define MARKER_TLV_RESPONSE 2 /* marker response information */
+ uint8_t marker_length; /* = 0x16 */
+ uint16_t requester_port; /* The number assigned to the port by the requester */
+ uint8_t requester_system[ETH_ALEN]; /* The requester's system id */
+ uint32_t requester_transaction_id; /* The transaction id allocated by the requester, */
+ uint16_t pad; /* = 0 */
+ uint8_t tlv_type_terminator; /* = 0x00 */
+ uint8_t terminator_length; /* = 0x00 */
+ uint8_t reserved_90[90]; /* = 0 */
+} PACKED;
+
+union slow_union {
+ struct slow_header header;
+ struct slow_lacp lacp;
+ struct slow_marker marker;
+};
+
+#define FAST_PERIODIC_TIME (1*TICKS_PER_SEC)
+#define SLOW_PERIODIC_TIME (30*TICKS_PER_SEC)
+#define SHORT_TIMEOUT_TIME (3*FAST_PERIODIC_TIME)
+#define LONG_TIMEOUT_TIME (3*SLOW_PERIODIC_TIME)
+#define CHURN_DETECTION_TIME (60*TICKS_PER_SEC)
+#define AGGREGATE_WAIT_TIME (2*TICKS_PER_SEC)
+
+#define LACP_ACTIVITY (1 << 0)
+#define LACP_TIMEOUT (1 << 1)
+#define LACP_AGGREGATION (1 << 2)
+#define LACP_SYNCHRONIZATION (1 << 3)
+#define LACP_COLLECTING (1 << 4)
+#define LACP_DISTRIBUTING (1 << 5)
+#define LACP_DEFAULTED (1 << 6)
+#define LACP_EXPIRED (1 << 7)
+
+#define UNSELECTED 0
+#define STANDBY 1
+#define SELECTED 2
+
+
+struct lacp_state {
+ struct slow_lacp pkt;
+ unsigned long current_while_timer; /* Time when the LACP information expires */
+ unsigned long periodic_timer; /* Time when I need to send my partner an update */
+};
+
+static struct lacp_state lacp;
+
+
+#if LACP_DEBUG > 0
+static void print_lacp_state(uint8_t state)
+{
+ printf("%hhx", state);
+ if (state & LACP_ACTIVITY) {
+ printf(" Activity");
+ }
+ if (state & LACP_TIMEOUT) {
+ printf(" Timeout");
+ }
+ if (state & LACP_AGGREGATION) {
+ printf(" Aggregation");
+ }
+ if (state & LACP_SYNCHRONIZATION) {
+ printf(" Syncronization");
+ }
+ if (state & LACP_COLLECTING) {
+ printf(" Collecting");
+ }
+ if (state & LACP_DISTRIBUTING) {
+ printf(" Distributing");
+ }
+ if (state & LACP_DEFAULTED) {
+ printf(" Defaulted");
+ }
+ if (state & LACP_EXPIRED) {
+ printf(" Expired");
+ }
+ printf("\n");
+}
+
+static inline void print_lacpdu(struct slow_lacp *pkt)
+{
+ printf("subtype version: %hhx %hhx\n",
+ pkt->subtype, pkt->version_number);
+ printf("actor_tlv %hhx", pkt->tlv_type_actor_info);
+ printf(" len: %hhx (\n", pkt->actor_information_length);
+ printf(" sys_pri: %hx", ntohs(pkt->actor.system_priority));
+ printf(" mac: %!", pkt->actor.system);
+ printf(" key: %hx", ntohs(pkt->actor.key));
+ printf(" port_pri: %hx", ntohs(pkt->actor.port_priority));
+ printf(" port: %hx\n", ntohs(pkt->actor.port));
+ printf(" state: ");
+ print_lacp_state(pkt->actor.state);
+#if LACP_DEBUG > 1
+ printf(" reserved: %hhx %hhx %hhx\n",
+ pkt->actor.reserved[0], pkt->actor.reserved[1], pkt->actor.reserved[2]);
+#endif
+ printf(")\n");
+ printf("partner_tlv: %hhx", pkt->tlv_type_partner_info);
+ printf(" len: %hhx (\n", pkt->partner_information_length);
+ printf(" sys_pri: %hx", ntohs(pkt->partner.system_priority));
+ printf(" mac: %!", pkt->partner.system);
+ printf(" key: %hx", ntohs(pkt->partner.key));
+ printf(" port_pri: %hx", ntohs(pkt->partner.port_priority));
+ printf(" port: %hx\n", ntohs(pkt->partner.port));
+ printf(" state: ");
+ print_lacp_state(pkt->partner.state);
+#if LACP_DEBUG > 1
+ printf(" reserved: %hhx %hhx %hhx\n",
+ pkt->partner.reserved[0], pkt->partner.reserved[1], pkt->partner.reserved[2]);
+#endif
+ printf(")\n");
+ printf("collector_tlv: %hhx ", pkt->tlv_type_collector_info);
+ printf(" len: %hhx (", pkt->collector_information_length);
+ printf(" max_delay: %hx", ntohs(pkt->collector_max_delay));
+#if LACP_DEBUG > 1
+ printf("reserved_12: %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx\n",
+ pkt->reserved_12[0], pkt->reserved_12[1], pkt->reserved_12[2],
+ pkt->reserved_12[3], pkt->reserved_12[4], pkt->reserved_12[5],
+ pkt->reserved_12[6], pkt->reserved_12[7], pkt->reserved_12[8],
+ pkt->reserved_12[9], pkt->reserved_12[10], pkt->reserved_12[11]);
+#endif
+ printf(" )\n");
+ printf("terminator_tlv: %hhx", pkt->tlv_type_terminator);
+ printf(" len: %hhx ()\n", pkt->terminator_length);
+}
+
+static inline unsigned long lacp_timer_val(unsigned long now, unsigned long when)
+{
+ return when?(when - now)/TICKS_PER_SEC : 0;
+}
+static void print_lacp(const char *which, struct slow_lacp *pkt, unsigned long now)
+{
+ printf("%s\n", which);
+ print_lacpdu(pkt);
+ printf("timers: c %ds p %ds\n",
+ lacp_timer_val(now, lacp.current_while_timer),
+ lacp_timer_val(now, lacp.periodic_timer)
+ );
+ printf("\n");
+}
+#else /* LACP_DEBUG */
+#define print_lacp(which, pkt, now) do {} while(0)
+#endif /* LACP_DEBUG */
+
+static void lacp_init_state(const uint8_t *mac)
+{
+ memset(&lacp, 0, sizeof(lacp));
+
+ /* Initialize the packet constants */
+ lacp.pkt.subtype = 1;
+ lacp.pkt.version_number = 1;
+
+
+ /* The default state of my interface */
+ lacp.pkt.tlv_type_actor_info = LACP_TLV_ACTOR;
+ lacp.pkt.actor_information_length = 0x14;
+ lacp.pkt.actor.system_priority = htons(1);
+ memcpy(lacp.pkt.actor.system, mac, ETH_ALEN);
+ lacp.pkt.actor.key = htons(1);
+ lacp.pkt.actor.port = htons(1);
+ lacp.pkt.actor.port_priority = htons(1);
+ lacp.pkt.actor.state =
+ LACP_SYNCHRONIZATION |
+ LACP_COLLECTING |
+ LACP_DISTRIBUTING |
+ LACP_DEFAULTED;
+
+ /* Set my partner defaults */
+ lacp.pkt.tlv_type_partner_info = LACP_TLV_PARTNER;
+ lacp.pkt.partner_information_length = 0x14;
+ lacp.pkt.partner.system_priority = htons(1);
+ /* memset(lacp.pkt.parnter_system, 0, ETH_ALEN); */
+ lacp.pkt.partner.key = htons(1);
+ lacp.pkt.partner.port = htons(1);
+ lacp.pkt.partner.port_priority = htons(1);
+ lacp.pkt.partner.state =
+ LACP_ACTIVITY |
+ LACP_SYNCHRONIZATION |
+ LACP_COLLECTING |
+ LACP_DISTRIBUTING |
+ LACP_DEFAULTED;
+
+ lacp.pkt.tlv_type_collector_info = LACP_TLV_COLLECTOR;
+ lacp.pkt.collector_information_length = 0x10;
+ lacp.pkt.collector_max_delay = htons(0x8000); /* ???? */
+
+ lacp.pkt.tlv_type_terminator = LACP_TLV_TERMINATOR;
+ lacp.pkt.terminator_length = 0;
+}
+
+#define LACP_NTT_MASK (LACP_ACTIVITY | LACP_TIMEOUT | \
+ LACP_SYNCHRONIZATION | LACP_AGGREGATION)
+
+static inline int lacp_update_ntt(struct slow_lacp *pkt)
+{
+ int ntt = 0;
+ if ((memcmp(&pkt->partner, &lacp.pkt.actor, LACP_CMP_LEN) != 0) ||
+ ((pkt->partner.state & LACP_NTT_MASK) !=
+ (lacp.pkt.actor.state & LACP_NTT_MASK)))
+ {
+ ntt = 1;
+ }
+ return ntt;
+}
+
+static inline void lacp_record_pdu(struct slow_lacp *pkt)
+{
+ memcpy(&lacp.pkt.partner, &pkt->actor, LACP_CP_LEN);
+
+ lacp.pkt.actor.state &= ~LACP_DEFAULTED;
+ lacp.pkt.partner.state &= ~LACP_SYNCHRONIZATION;
+ if ((memcmp(&pkt->partner, &lacp.pkt.actor, LACP_CMP_LEN) == 0) &&
+ ((pkt->partner.state & LACP_AGGREGATION) ==
+ (lacp.pkt.actor.state & LACP_AGGREGATION)))
+ {
+ lacp.pkt.partner.state |= LACP_SYNCHRONIZATION;
+ }
+ if (!(pkt->actor.state & LACP_AGGREGATION)) {
+ lacp.pkt.partner.state |= LACP_SYNCHRONIZATION;
+ }
+
+ /* ACTIVITY? */
+}
+
+static inline int lacp_timer_expired(unsigned long now, unsigned long when)
+{
+ return when && (now > when);
+}
+
+static inline void lacp_start_periodic_timer(unsigned long now)
+{
+ if ((lacp.pkt.partner.state & LACP_ACTIVITY) ||
+ (lacp.pkt.actor.state & LACP_ACTIVITY)) {
+ lacp.periodic_timer = now +
+ (((lacp.pkt.partner.state & LACP_TIMEOUT)?
+ FAST_PERIODIC_TIME : SLOW_PERIODIC_TIME));
+ }
+}
+
+static inline void lacp_start_current_while_timer(unsigned long now)
+{
+ lacp.current_while_timer = now +
+ ((lacp.pkt.actor.state & LACP_TIMEOUT) ?
+ SHORT_TIMEOUT_TIME : LONG_TIMEOUT_TIME);
+
+ lacp.pkt.actor.state &= ~LACP_EXPIRED;
+}
+
+static void send_lacp_reports(unsigned long now, int ntt)
+{
+ if (memcmp(nic.node_addr, lacp.pkt.actor.system, ETH_ALEN) != 0) {
+ lacp_init_state(nic.node_addr);
+ }
+ /* If the remote information has expired I need to take action */
+ if (lacp_timer_expired(now, lacp.current_while_timer)) {
+ if (!(lacp.pkt.actor.state & LACP_EXPIRED)) {
+ lacp.pkt.partner.state &= ~LACP_SYNCHRONIZATION;
+ lacp.pkt.partner.state |= LACP_TIMEOUT;
+ lacp.pkt.actor.state |= LACP_EXPIRED;
+ lacp.current_while_timer = now + SHORT_TIMEOUT_TIME;
+ ntt = 1;
+ }
+ else {
+ lacp_init_state(nic.node_addr);
+ }
+ }
+ /* If the periodic timer has expired I need to transmit */
+ if (lacp_timer_expired(now, lacp.periodic_timer)) {
+ ntt = 1;
+ /* Reset by lacp_start_periodic_timer */
+ }
+ if (ntt) {
+ eth_transmit(slow_dest, ETH_P_SLOW, sizeof(lacp.pkt), &lacp.pkt);
+
+ /* Restart the periodic timer */
+ lacp_start_periodic_timer(now);
+
+ print_lacp("Trasmitted", &lacp.pkt, now);
+ }
+}
+
+static inline void send_eth_slow_reports(unsigned long now)
+{
+ send_lacp_reports(now, 0);
+}
+
+static inline void process_eth_slow(unsigned short ptype, unsigned long now)
+{
+ union slow_union *pkt;
+ if ((ptype != ETH_P_SLOW) ||
+ (nic.packetlen < (ETH_HLEN + sizeof(pkt->header)))) {
+ return;
+ }
+ pkt = (union slow_union *)&nic.packet[ETH_HLEN];
+ if ((pkt->header.subtype == SLOW_SUBTYPE_LACP) &&
+ (nic.packetlen >= ETH_HLEN + sizeof(pkt->lacp))) {
+ int ntt;
+ if (memcmp(nic.node_addr, lacp.pkt.actor.system, ETH_ALEN) != 0) {
+ lacp_init_state(nic.node_addr);
+ }
+ /* As long as nic.packet is 2 byte aligned all is good */
+ print_lacp("Received", &pkt->lacp, now);
+ /* I don't actually implement the MUX or SELECT
+ * machines.
+ *
+ * What logically happens when the client and I
+ * disagree about an aggregator is the current
+ * aggregtator is unselected. The MUX machine places
+ * me in DETACHED. The SELECT machine runs and
+ * reslects the same aggregator. If I go through
+ * these steps fast enough an outside observer can not
+ * notice this.
+ *
+ * Since the process will not generate any noticeable
+ * effect it does not need an implmenetation. This
+ * keeps the code simple and the code and binary
+ * size down.
+ */
+ /* lacp_update_selected(&pkt->lacp); */
+ ntt = lacp_update_ntt(&pkt->lacp);
+ lacp_record_pdu(&pkt->lacp);
+ lacp_start_current_while_timer(now);
+ send_lacp_reports(now, ntt);
+ }
+ /* If we receive a marker information packet return it */
+ else if ((pkt->header.subtype == SLOW_SUBTYPE_MARKER) &&
+ (nic.packetlen >= ETH_HLEN + sizeof(pkt->marker)) &&
+ (pkt->marker.tlv_type == MARKER_TLV_INFO) &&
+ (pkt->marker.marker_length == 0x16))
+ {
+ pkt->marker.tlv_type = MARKER_TLV_RESPONSE;
+ eth_transmit(slow_dest, ETH_P_SLOW,
+ sizeof(pkt->marker), &pkt->marker);
+ }
+
+ }
+#else
+
+#define send_eth_slow_reports(now) do {} while(0)
+#define process_eth_slow(ptype, now) do {} while(0)
+
+#endif
diff --git a/src/core/proto_http.c b/src/core/proto_http.c
new file mode 100644
index 00000000..f2dc9dd1
--- /dev/null
+++ b/src/core/proto_http.c
@@ -0,0 +1,206 @@
+#include "etherboot.h"
+#include "http.h"
+
+#ifdef DOWNLOAD_PROTO_HTTP
+
+/* The block size is currently chosen to be 512 bytes. This means, we can
+ allocate the receive buffer on the stack, but it results in a noticeable
+ performance penalty.
+ This is what needs to be done in order to increase the block size:
+ - size negotiation needs to be implemented in TCP
+ - the buffer needs to be allocated on the heap
+ - path MTU discovery needs to be implemented
+*/ /***/ /* FIXME */
+#define BLOCKSIZE TFTP_DEFAULTSIZE_PACKET
+
+/**************************************************************************
+SEND_TCP_CALLBACK - Send data using TCP
+**************************************************************************/
+struct send_recv_state {
+ int (*fnc)(unsigned char *data, int block, int len, int eof);
+ char *send_buffer;
+ char *recv_buffer;
+ int send_length;
+ int recv_length;
+ int bytes_sent;
+ int block;
+ int bytes_received;
+ enum { RESULT_CODE, HEADER, DATA, ERROR, MOVED } recv_state;
+ int rc;
+ char location[MAX_URL+1];
+};
+
+static int send_tcp_request(int length, void *buffer, void *ptr) {
+ struct send_recv_state *state = (struct send_recv_state *)ptr;
+
+ if (length > state->send_length - state->bytes_sent)
+ length = state->send_length - state->bytes_sent;
+ memcpy(buffer, state->send_buffer + state->bytes_sent, length);
+ state->bytes_sent += length;
+ return (length);
+}
+
+/**************************************************************************
+RECV_TCP_CALLBACK - Receive data using TCP
+**************************************************************************/
+static int recv_tcp_request(int length, const void *buffer, void *ptr) {
+ struct send_recv_state *state = (struct send_recv_state *)ptr;
+
+ /* Assume that the lines in an HTTP header do not straddle a packet */
+ /* boundary. This is probably a reasonable assumption */
+ if (state->recv_state == RESULT_CODE) {
+ while (length > 0) {
+ /* Find HTTP result code */
+ if (*(const char *)buffer == ' ') {
+ const char *ptr = ((const char *)buffer) + 1;
+ int rc = strtoul(ptr, &ptr, 10);
+ if (ptr >= (const char *)buffer + length) {
+ state->recv_state = ERROR;
+ return 0;
+ }
+ state->rc = rc;
+ state->recv_state = HEADER;
+ goto header;
+ }
+ ++(const char *)buffer;
+ length--;
+ }
+ state->recv_state = ERROR;
+ return 0;
+ }
+ if (state->recv_state == HEADER) {
+ header: while (length > 0) {
+ /* Check for HTTP redirect */
+ if (state->rc >= 300 && state->rc < 400 &&
+ !memcmp(buffer, "Location: ", 10)) {
+ char *ptr = state->location;
+ int i;
+ memcpy(ptr, buffer + 10, MAX_URL);
+ for (i = 0; i < MAX_URL && *ptr > ' ';
+ i++, ptr++);
+ *ptr = '\000';
+ state->recv_state = MOVED;
+ return 1;
+ }
+ /* Find beginning of line */
+ while (length > 0) {
+ length--;
+ if (*((const char *)buffer)++ == '\n')
+ break;
+ }
+ /* Check for end of header */
+ if (length >= 2 && !memcmp(buffer, "\r\n", 2)) {
+ state->recv_state = DATA;
+ buffer += 2;
+ length -= 2;
+ break;
+ }
+ }
+ }
+ if (state->recv_state == DATA) {
+ state->bytes_received += length;
+ while (length > 0) {
+ int copy_length = BLOCKSIZE - state->recv_length;
+ if (copy_length > length)
+ copy_length = length;
+ memcpy(state->recv_buffer + state->recv_length,
+ buffer, copy_length);
+ if ((state->recv_length += copy_length) == BLOCKSIZE) {
+ if (!state->fnc(state->recv_buffer,
+ ++state->block, BLOCKSIZE, 0))
+ return 0;
+ state->recv_length = 0;
+ }
+ length -= copy_length;
+ buffer += copy_length;
+ }
+ }
+ return 1;
+}
+
+/**************************************************************************
+HTTP_GET - Get data using HTTP
+**************************************************************************/
+int http(const char *url,
+ int (*fnc)(unsigned char *, unsigned int, unsigned int, int)) {
+ static const char GET[] = "GET /%s HTTP/1.0\r\n\r\n";
+ static char recv_buffer[BLOCKSIZE];
+ in_addr destip;
+ int port;
+ int length;
+ struct send_recv_state state;
+
+ state.fnc = fnc;
+ state.rc = -1;
+ state.block = 0;
+ state.recv_buffer = recv_buffer;
+ length = strlen(url);
+ if (length <= MAX_URL) {
+ memcpy(state.location, url, length+1);
+ destip = arptable[ARP_SERVER].ipaddr;
+ port = url_port;
+ if (port == -1)
+ port = 80;
+ goto first_time;
+
+ do {
+ state.rc = -1;
+ state.block = 0;
+ url = state.location;
+ if (memcmp("http://", url, 7))
+ break;
+ url += 7;
+ length = inet_aton(url, &destip);
+ if (!length) {
+ /* As we do not have support for DNS, assume*/
+ /* that HTTP redirects always point to the */
+ /* same machine */
+ if (state.recv_state == MOVED) {
+ while (*url &&
+ *url != ':' && *url != '/') url++;
+ } else {
+ break;
+ }
+ }
+ if (*(url += length) == ':') {
+ port = strtoul(url, &url, 10);
+ } else {
+ port = 80;
+ }
+ if (!*url)
+ url = "/";
+ if (*url != '/')
+ break;
+ url++;
+
+ first_time:
+ length = strlen(url);
+ state.send_length = sizeof(GET) - 3 + length;
+
+ { char buf[state.send_length + 1];
+ sprintf(state.send_buffer = buf, GET, url);
+ state.bytes_sent = 0;
+
+ state.bytes_received = 0;
+ state.recv_state = RESULT_CODE;
+
+ state.recv_length = 0;
+ tcp_transaction(destip.s_addr, 80, &state,
+ send_tcp_request, recv_tcp_request);
+ }
+ } while (state.recv_state == MOVED);
+ } else {
+ memcpy(state.location, url, MAX_URL);
+ state.location[MAX_URL] = '\000';
+ }
+
+ if (state.rc == 200) {
+ return fnc(recv_buffer, ++state.block, state.recv_length, 1);
+ } else {
+ printf("Failed to download %s (rc = %d)\n",
+ state.location, state.rc);
+ return 0;
+ }
+}
+
+#endif /* DOWNLOAD_PROTO_HTTP */
diff --git a/src/core/proto_slam.c b/src/core/proto_slam.c
new file mode 100644
index 00000000..135384a9
--- /dev/null
+++ b/src/core/proto_slam.c
@@ -0,0 +1,541 @@
+#ifdef DOWNLOAD_PROTO_SLAM
+#include "etherboot.h"
+#include "nic.h"
+
+#define SLAM_PORT 10000
+#define SLAM_MULTICAST_IP ((239<<24)|(255<<16)|(1<<8)|(1<<0))
+#define SLAM_MULTICAST_PORT 10000
+#define SLAM_LOCAL_PORT 10000
+
+/* Set the timeout intervals to at least 1 second so
+ * on a 100Mbit ethernet can receive 10000 packets
+ * in one second.
+ *
+ * The only case that is likely to trigger all of the nodes
+ * firing a nack packet is a slow server. The odds of this
+ * happening could be reduced being slightly smarter and utilizing
+ * the multicast channels for nacks. But that only improves the odds
+ * it doesn't improve the worst case. So unless this proves to be
+ * a common case having the control data going unicast should increase
+ * the odds of the data not being dropped.
+ *
+ * When doing exponential backoff we increase just the timeout
+ * interval and not the base to optimize for throughput. This is only
+ * expected to happen when the server is down. So having some nodes
+ * pinging immediately should get the transmission restarted quickly after a
+ * server restart. The host nic won't be to baddly swamped because of
+ * the random distribution of the nodes.
+ *
+ */
+#define SLAM_INITIAL_MIN_TIMEOUT (TICKS_PER_SEC/3)
+#define SLAM_INITIAL_TIMEOUT_INTERVAL (TICKS_PER_SEC)
+#define SLAM_BASE_MIN_TIMEOUT (2*TICKS_PER_SEC)
+#define SLAM_BASE_TIMEOUT_INTERVAL (4*TICKS_PER_SEC)
+#define SLAM_BACKOFF_LIMIT 5
+#define SLAM_MAX_RETRIES 20
+
+/*** Packets Formats ***
+ * Data Packet:
+ * transaction
+ * total bytes
+ * block size
+ * packet #
+ * data
+ *
+ * Status Request Packet
+ * transaction
+ * total bytes
+ * block size
+ *
+ * Status Packet
+ * received packets
+ * requested packets
+ * received packets
+ * requested packets
+ * ...
+ * received packets
+ * requested packtes
+ * 0
+ */
+
+#define MAX_HDR (7 + 7 + 7) /* transaction, total size, block size */
+#define MIN_HDR (1 + 1 + 1) /* transactino, total size, block size */
+
+#define MAX_SLAM_REQUEST MAX_HDR
+#define MIN_SLAM_REQUEST MIN_HDR
+
+#define MIN_SLAM_DATA (MIN_HDR + 1)
+
+static struct slam_nack {
+ struct iphdr ip;
+ struct udphdr udp;
+ unsigned char data[ETH_MAX_MTU -
+ (sizeof(struct iphdr) + sizeof(struct udphdr))];
+} nack;
+
+struct slam_state {
+ unsigned char hdr[MAX_HDR];
+ unsigned long hdr_len;
+ unsigned long block_size;
+ unsigned long total_bytes;
+ unsigned long total_packets;
+
+ unsigned long received_packets;
+
+ unsigned char *image;
+ unsigned char *bitmap;
+} state;
+
+
+static void init_slam_state(void)
+{
+ state.hdr_len = sizeof(state.hdr);
+ memset(state.hdr, 0, state.hdr_len);
+ state.block_size = 0;
+ state.total_packets = 0;
+
+ state.received_packets = 0;
+
+ state.image = 0;
+ state.bitmap = 0;
+}
+
+struct slam_info {
+ in_addr server_ip;
+ in_addr multicast_ip;
+ in_addr local_ip;
+ uint16_t server_port;
+ uint16_t multicast_port;
+ uint16_t local_port;
+ int (*fnc)(unsigned char *, unsigned int, unsigned int, int);
+ int sent_nack;
+};
+
+#define SLAM_TIMEOUT 0
+#define SLAM_REQUEST 1
+#define SLAM_DATA 2
+static int await_slam(int ival __unused, void *ptr,
+ unsigned short ptype __unused, struct iphdr *ip, struct udphdr *udp)
+{
+ struct slam_info *info = ptr;
+ if (!udp) {
+ return 0;
+ }
+ /* I can receive two kinds of packets here, a multicast data packet,
+ * or a unicast request for information
+ */
+ /* Check for a data request packet */
+ if ((ip->dest.s_addr == arptable[ARP_CLIENT].ipaddr.s_addr) &&
+ (ntohs(udp->dest) == info->local_port) &&
+ (nic.packetlen >=
+ ETH_HLEN +
+ sizeof(struct iphdr) +
+ sizeof(struct udphdr) +
+ MIN_SLAM_REQUEST)) {
+ return SLAM_REQUEST;
+ }
+ /* Check for a multicast data packet */
+ if ((ip->dest.s_addr == info->multicast_ip.s_addr) &&
+ (ntohs(udp->dest) == info->multicast_port) &&
+ (nic.packetlen >=
+ ETH_HLEN +
+ sizeof(struct iphdr) +
+ sizeof(struct udphdr) +
+ MIN_SLAM_DATA)) {
+ return SLAM_DATA;
+ }
+#if 0
+ printf("#");
+ printf("dest: %@ port: %d len: %d\n",
+ ip->dest.s_addr, ntohs(udp->dest), nic.packetlen);
+#endif
+ return 0;
+
+}
+
+static int slam_encode(
+ unsigned char **ptr, unsigned char *end, unsigned long value)
+{
+ unsigned char *data = *ptr;
+ int bytes;
+ bytes = sizeof(value);
+ while ((bytes > 0) && ((0xff & (value >> ((bytes -1)<<3))) == 0)) {
+ bytes--;
+ }
+ if (bytes <= 0) {
+ bytes = 1;
+ }
+ if (data + bytes >= end) {
+ return -1;
+ }
+ if ((0xe0 & (value >> ((bytes -1)<<3))) == 0) {
+ /* packed together */
+ *data = (bytes << 5) | (value >> ((bytes -1)<<3));
+ } else {
+ bytes++;
+ *data = (bytes << 5);
+ }
+ bytes--;
+ data++;
+ while(bytes) {
+ *(data++) = 0xff & (value >> ((bytes -1)<<3));
+ bytes--;
+ }
+ *ptr = data;
+ return 0;
+}
+
+static int slam_skip(unsigned char **ptr, unsigned char *end)
+{
+ int bytes;
+ if (*ptr >= end) {
+ return -1;
+ }
+ bytes = ((**ptr) >> 5) & 7;
+ if (bytes == 0) {
+ return -1;
+ }
+ if (*ptr + bytes >= end) {
+ return -1;
+ }
+ (*ptr) += bytes;
+ return 0;
+
+}
+
+static unsigned long slam_decode(unsigned char **ptr, unsigned char *end, int *err)
+{
+ unsigned long value;
+ unsigned bytes;
+ if (*ptr >= end) {
+ *err = -1;
+ }
+ bytes = ((**ptr) >> 5) & 7;
+ if ((bytes == 0) || (bytes > sizeof(unsigned long))) {
+ *err = -1;
+ return 0;
+ }
+ if ((*ptr) + bytes >= end) {
+ *err = -1;
+ }
+ value = (**ptr) & 0x1f;
+ bytes--;
+ (*ptr)++;
+ while(bytes) {
+ value <<= 8;
+ value |= **ptr;
+ (*ptr)++;
+ bytes--;
+ }
+ return value;
+}
+
+
+static long slam_sleep_interval(int exp)
+{
+ long range;
+ long divisor;
+ long interval;
+ range = SLAM_BASE_TIMEOUT_INTERVAL;
+ if (exp < 0) {
+ divisor = RAND_MAX/SLAM_INITIAL_TIMEOUT_INTERVAL;
+ } else {
+ if (exp > SLAM_BACKOFF_LIMIT)
+ exp = SLAM_BACKOFF_LIMIT;
+ divisor = RAND_MAX/(range << exp);
+ }
+ interval = random()/divisor;
+ if (exp < 0) {
+ interval += SLAM_INITIAL_MIN_TIMEOUT;
+ } else {
+ interval += SLAM_BASE_MIN_TIMEOUT;
+ }
+ return interval;
+}
+
+
+static unsigned char *reinit_slam_state(
+ unsigned char *header, unsigned char *end)
+{
+ unsigned long total_bytes;
+ unsigned long block_size;
+
+ unsigned long bitmap_len;
+ unsigned long max_packet_len;
+ unsigned char *data;
+ int err;
+
+#if 0
+ printf("reinit\n");
+#endif
+ data = header;
+
+ state.hdr_len = 0;
+ err = slam_skip(&data, end); /* transaction id */
+ total_bytes = slam_decode(&data, end, &err);
+ block_size = slam_decode(&data, end, &err);
+ if (err) {
+ printf("ALERT: slam size out of range\n");
+ return 0;
+ }
+ state.block_size = block_size;
+ state.total_bytes = total_bytes;
+ state.total_packets = (total_bytes + block_size - 1)/block_size;
+ state.hdr_len = data - header;
+ state.received_packets = 0;
+
+ data = state.hdr;
+ slam_encode(&data, &state.hdr[sizeof(state.hdr)], state.total_packets);
+ max_packet_len = data - state.hdr;
+ memcpy(state.hdr, header, state.hdr_len);
+
+#if 0
+ printf("block_size: %ld\n", block_size);
+ printf("total_bytes: %ld\n", total_bytes);
+ printf("total_packets: %ld\n", state.total_packets);
+ printf("hdr_len: %ld\n", state.hdr_len);
+ printf("max_packet_len: %ld\n", max_packet_len);
+#endif
+
+ if (state.block_size > ETH_MAX_MTU - (
+ sizeof(struct iphdr) + sizeof(struct udphdr) +
+ state.hdr_len + max_packet_len)) {
+ printf("ALERT: slam blocksize to large\n");
+ return 0;
+ }
+ if (state.bitmap) {
+ forget(state.bitmap);
+ }
+ bitmap_len = (state.total_packets + 1 + 7)/8;
+ state.bitmap = allot(bitmap_len);
+ state.image = allot(total_bytes);
+ if ((unsigned long)state.image < 1024*1024) {
+ printf("ALERT: slam filesize to large for available memory\n");
+ return 0;
+ }
+ memset(state.bitmap, 0, bitmap_len);
+
+ return header + state.hdr_len;
+}
+
+static int slam_recv_data(unsigned char *data)
+{
+ unsigned long packet;
+ unsigned long data_len;
+ int err;
+ struct udphdr *udp;
+ udp = (struct udphdr *)&nic.packet[ETH_HLEN + sizeof(struct iphdr)];
+ err = 0;
+ packet = slam_decode(&data, &nic.packet[nic.packetlen], &err);
+ if (err || (packet > state.total_packets)) {
+ printf("ALERT: Invalid packet number\n");
+ return 0;
+ }
+ /* Compute the expected data length */
+ if (packet != state.total_packets -1) {
+ data_len = state.block_size;
+ } else {
+ data_len = state.total_bytes % state.block_size;
+ }
+ /* If the packet size is wrong drop the packet and then continue */
+ if (ntohs(udp->len) != (data_len + (data - (unsigned char*)udp))) {
+ printf("ALERT: udp packet is not the correct size\n");
+ return 1;
+ }
+ if (nic.packetlen < data_len + (data - nic.packet)) {
+ printf("ALERT: Ethernet packet shorter than data_len\n");
+ return 1;
+ }
+ if (data_len > state.block_size) {
+ data_len = state.block_size;
+ }
+ if (((state.bitmap[packet >> 3] >> (packet & 7)) & 1) == 0) {
+ /* Non duplicate packet */
+ state.bitmap[packet >> 3] |= (1 << (packet & 7));
+ memcpy(state.image + (packet*state.block_size), data, data_len);
+ state.received_packets++;
+ } else {
+#ifdef MDEBUG
+ printf("<DUP>\n");
+#endif
+ }
+ return 1;
+}
+
+static void transmit_nack(unsigned char *ptr, struct slam_info *info)
+{
+ int nack_len;
+ /* Ensure the packet is null terminated */
+ *ptr++ = 0;
+ nack_len = ptr - (unsigned char *)&nack;
+ build_udp_hdr(info->server_ip.s_addr,
+ info->local_port, info->server_port, 1, nack_len, &nack);
+ ip_transmit(nack_len, &nack);
+#if defined(MDEBUG) && 0
+ printf("Sent NACK to %@ bytes: %d have:%ld/%ld\n",
+ info->server_ip, nack_len,
+ state.received_packets, state.total_packets);
+#endif
+}
+
+static void slam_send_nack(struct slam_info *info)
+{
+ unsigned char *ptr, *end;
+ /* Either I timed out or I was explicitly
+ * asked for a request packet
+ */
+ ptr = &nack.data[0];
+ /* Reserve space for the trailling null */
+ end = &nack.data[sizeof(nack.data) -1];
+ if (!state.bitmap) {
+ slam_encode(&ptr, end, 0);
+ slam_encode(&ptr, end, 1);
+ }
+ else {
+ /* Walk the bitmap */
+ unsigned long i;
+ unsigned long len;
+ unsigned long max;
+ int value;
+ int last;
+ /* Compute the last bit and store an inverted trailer */
+ max = state.total_packets;
+ value = ((state.bitmap[(max -1) >> 3] >> ((max -1) & 7) ) & 1);
+ value = !value;
+ state.bitmap[max >> 3] &= ~(1 << (max & 7));
+ state.bitmap[max >> 3] |= value << (max & 7);
+
+ len = 0;
+ last = 1; /* Start with the received packets */
+ for(i = 0; i <= max; i++) {
+ value = (state.bitmap[i>>3] >> (i & 7)) & 1;
+ if (value == last) {
+ len++;
+ } else {
+ if (slam_encode(&ptr, end, len))
+ break;
+ last = value;
+ len = 1;
+ }
+ }
+ }
+ info->sent_nack = 1;
+ transmit_nack(ptr, info);
+}
+
+static void slam_send_disconnect(struct slam_info *info)
+{
+ if (info->sent_nack) {
+ /* A disconnect is a packet with just the null terminator */
+ transmit_nack(&nack.data[0], info);
+ }
+ info->sent_nack = 0;
+}
+
+
+static int proto_slam(struct slam_info *info)
+{
+ int retry;
+ long timeout;
+
+ init_slam_state();
+
+ retry = -1;
+ rx_qdrain();
+ /* Arp for my server */
+ if (arptable[ARP_SERVER].ipaddr.s_addr != info->server_ip.s_addr) {
+ arptable[ARP_SERVER].ipaddr.s_addr = info->server_ip.s_addr;
+ memset(arptable[ARP_SERVER].node, 0, ETH_ALEN);
+ }
+ /* If I'm running over multicast join the multicast group */
+ join_group(IGMP_SERVER, info->multicast_ip.s_addr);
+ for(;;) {
+ unsigned char *header;
+ unsigned char *data;
+ int type;
+ header = data = 0;
+
+ timeout = slam_sleep_interval(retry);
+ type = await_reply(await_slam, 0, info, timeout);
+ /* Compute the timeout for next time */
+ if (type == SLAM_TIMEOUT) {
+ /* If I timeouted recompute the next timeout */
+ if (retry++ > SLAM_MAX_RETRIES) {
+ return 0;
+ }
+ } else {
+ retry = 0;
+ }
+ if ((type == SLAM_DATA) || (type == SLAM_REQUEST)) {
+ /* Check the incomming packet and reinit the data
+ * structures if necessary.
+ */
+ header = &nic.packet[ETH_HLEN +
+ sizeof(struct iphdr) + sizeof(struct udphdr)];
+ data = header + state.hdr_len;
+ if (memcmp(state.hdr, header, state.hdr_len) != 0) {
+ /* Something is fishy reset the transaction */
+ data = reinit_slam_state(header, &nic.packet[nic.packetlen]);
+ if (!data) {
+ return 0;
+ }
+ }
+ }
+ if (type == SLAM_DATA) {
+ if (!slam_recv_data(data)) {
+ return 0;
+ }
+ if (state.received_packets == state.total_packets) {
+ /* We are done get out */
+ break;
+ }
+ }
+ if ((type == SLAM_TIMEOUT) || (type == SLAM_REQUEST)) {
+ /* Either I timed out or I was explicitly
+ * asked by a request packet
+ */
+ slam_send_nack(info);
+ }
+ }
+ slam_send_disconnect(info);
+
+ /* Leave the multicast group */
+ leave_group(IGMP_SERVER);
+ /* FIXME don't overwrite myself */
+ /* load file to correct location */
+ return info->fnc(state.image, 1, state.total_bytes, 1);
+}
+
+
+int url_slam(const char *name, int (*fnc)(unsigned char *, unsigned int, unsigned int, int))
+{
+ struct slam_info info;
+ /* Set the defaults */
+ info.server_ip.s_addr = arptable[ARP_SERVER].ipaddr.s_addr;
+ info.server_port = SLAM_PORT;
+ info.multicast_ip.s_addr = htonl(SLAM_MULTICAST_IP);
+ info.multicast_port = SLAM_MULTICAST_PORT;
+ info.local_ip.s_addr = arptable[ARP_CLIENT].ipaddr.s_addr;
+ info.local_port = SLAM_LOCAL_PORT;
+ info.fnc = fnc;
+ info.sent_nack = 0;
+ /* Now parse the url */
+ if (url_port != -1) {
+ info.server_port = url_port;
+ }
+ if (name[0]) {
+ /* multicast ip */
+ name += inet_aton(name, &info.multicast_ip);
+ if (name[0] == ':') {
+ name++;
+ info.multicast_port = strtoul(name, &name, 10);
+ }
+ }
+ if (name[0]) {
+ printf("\nBad url\n");
+ return 0;
+ }
+ return proto_slam(&info);
+}
+
+#endif /* DOWNLOAD_PROTO_SLAM */
diff --git a/src/core/proto_tftm.c b/src/core/proto_tftm.c
new file mode 100644
index 00000000..8040fc44
--- /dev/null
+++ b/src/core/proto_tftm.c
@@ -0,0 +1,491 @@
+/**************************************************************************
+*
+* proto_tftm.c -- Etherboot Multicast TFTP
+* Written 2003-2003 by Timothy Legge <tlegge@rogers.com>
+*
+* 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 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* This code is based on the DOWNLOAD_PROTO_TFTM section of
+* Etherboot 5.3 core/nic.c and:
+*
+* Anselm Martin Hoffmeister's previous proto_tftm.c multicast work
+* Eric Biederman's proto_slam.c
+*
+* $Revision$
+* $Author$
+* $Date$
+*
+* REVISION HISTORY:
+* ================
+* 09-07-2003 timlegge Release Version, Capable of Multicast Booting
+* 08-30-2003 timlegge Initial version, Assumes consecutive blocks
+*
+* Indent Options: indent -kr -i8
+***************************************************************************/
+
+#ifdef DOWNLOAD_PROTO_TFTM
+#include "etherboot.h"
+#include "nic.h"
+
+//#define TFTM_DEBUG
+#ifdef TFTM_DEBUG
+#define debug(x) printf x
+#else
+#define debug(x)
+#endif
+struct tftm_info {
+ in_addr server_ip;
+ in_addr multicast_ip;
+ in_addr local_ip;
+ uint16_t server_port;
+ uint16_t multicast_port;
+ uint16_t local_port;
+ int (*fnc) (unsigned char *, unsigned int, unsigned int, int);
+ int sent_nack;
+ const char *name; /* Filename */
+};
+
+struct tftm_state {
+ unsigned long block_size;
+ unsigned long total_bytes;
+ unsigned long total_packets;
+ char ismaster;
+ unsigned long received_packets;
+ unsigned char *image;
+ unsigned char *bitmap;
+ char recvd_oack;
+} state;
+
+#define TFTM_PORT 1758
+#define TFTM_MIN_PACKET 1024
+
+
+int opt_get_multicast(struct tftp_t *tr, unsigned short *len,
+ unsigned long *filesize, struct tftm_info *info);
+
+static int await_tftm(int ival, void *ptr, unsigned short ptype __unused,
+ struct iphdr *ip, struct udphdr *udp)
+{
+ struct tftm_info *info = ptr;
+
+ /* Check for Unicast data being received */
+ if (ip->dest.s_addr == arptable[ARP_CLIENT].ipaddr.s_addr) {
+ if (!udp) {
+ return 0;
+ }
+ if (arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr)
+ return 0;
+ if (ntohs(udp->dest) != ival)
+ return 0;
+
+ return 1; /* Unicast Data Received */
+ }
+
+ /* Also check for Multicast data being received */
+ if ((ip->dest.s_addr == info->multicast_ip.s_addr) &&
+ (ntohs(udp->dest) == info->multicast_port) &&
+ (nic.packetlen >= ETH_HLEN + sizeof(struct iphdr) +
+ sizeof(struct udphdr))) {
+ return 1; /* Multicast data received */
+ }
+ return 0;
+}
+
+int proto_tftm(struct tftm_info *info)
+{
+ int retry = 0;
+ static unsigned short iport = 2000;
+ unsigned short oport = 0;
+ unsigned short len, block = 0, prevblock = 0;
+ struct tftp_t *tr;
+ struct tftpreq_t tp;
+ unsigned long filesize = 0;
+
+ state.image = 0;
+ state.bitmap = 0;
+
+ rx_qdrain();
+
+ /* Warning: the following assumes the layout of bootp_t.
+ But that's fixed by the IP, UDP and BOOTP specs. */
+
+ /* Send a tftm-request to the server */
+ tp.opcode = htons(TFTP_RRQ); /* Const for "\0x0" "\0x1" =^= ReadReQuest */
+ len =
+ sizeof(tp.ip) + sizeof(tp.udp) + sizeof(tp.opcode) +
+ sprintf((char *) tp.u.rrq,
+ "%s%coctet%cmulticast%c%cblksize%c%d%ctsize%c",
+ info->name, 0, 0, 0, 0, 0, TFTM_MIN_PACKET, 0, 0) + 1;
+
+ if (!udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, ++iport,
+ TFTM_PORT, len, &tp))
+ return (0);
+
+ /* loop to listen for packets and to receive the file */
+ for (;;) {
+ long timeout;
+#ifdef CONGESTED
+ timeout =
+ rfc2131_sleep_interval(block ? TFTP_REXMT : TIMEOUT,
+ retry);
+#else
+ timeout = rfc2131_sleep_interval(TIMEOUT, retry);
+#endif
+ /* Calls the await_reply function in nic.c which in turn calls
+ await_tftm (1st parameter) as above */
+ if (!await_reply(await_tftm, iport, info, timeout)) {
+ if (!block && retry++ < MAX_TFTP_RETRIES) { /* maybe initial request was lost */
+ if (!udp_transmit
+ (arptable[ARP_SERVER].ipaddr.s_addr,
+ ++iport, TFTM_PORT, len, &tp))
+ return (0);
+ continue;
+ }
+#ifdef CONGESTED
+ if (block && ((retry += TFTP_REXMT) < TFTP_TIMEOUT)) { /* we resend our last ack */
+#ifdef MDEBUG
+ printf("<REXMT>\n");
+#endif
+ debug(("Timed out receiving file"));
+ len =
+ sizeof(tp.ip) + sizeof(tp.udp) +
+ sizeof(tp.opcode) +
+ sprintf((char *) tp.u.rrq,
+ "%s%coctet%cmulticast%c%cblksize%c%d%ctsize%c",
+ info->name, 0, 0, 0, 0, 0,
+ TFTM_MIN_PACKET, 0, 0) + 1;
+
+ udp_transmit
+ (arptable[ARP_SERVER].ipaddr.s_addr,
+ ++iport, TFTM_PORT, len, &tp);
+ continue;
+ }
+#endif
+ break; /* timeout */
+ }
+
+ tr = (struct tftp_t *) &nic.packet[ETH_HLEN];
+
+ if (tr->opcode == ntohs(TFTP_ERROR)) {
+ printf("TFTP error %d (%s)\n",
+ ntohs(tr->u.err.errcode), tr->u.err.errmsg);
+ break;
+ }
+
+ if (tr->opcode == ntohs(TFTP_OACK)) {
+ int i =
+ opt_get_multicast(tr, &len, &filesize, info);
+
+ if (i == 0 || (i != 7 && !state.recvd_oack)) { /* Multicast unsupported */
+ /* Transmit an error message to the server to end the transmission */
+ printf
+ ("TFTM-Server doesn't understand options [blksize tsize multicast]\n");
+ tp.opcode = htons(TFTP_ERROR);
+ tp.u.err.errcode = 8;
+ /*
+ * Warning: the following assumes the layout of bootp_t.
+ * But that's fixed by the IP, UDP and BOOTP specs.
+ */
+ len =
+ sizeof(tp.ip) + sizeof(tp.udp) +
+ sizeof(tp.opcode) +
+ sizeof(tp.u.err.errcode) +
+ /*
+ * Normally bad form to omit the format string, but in this case
+ * the string we are copying from is fixed. sprintf is just being
+ * used as a strcpy and strlen.
+ */
+ sprintf((char *) tp.u.err.errmsg,
+ "RFC2090 error") + 1;
+ udp_transmit(arptable[ARP_SERVER].ipaddr.
+ s_addr, iport,
+ ntohs(tr->udp.src), len, &tp);
+ block = tp.u.ack.block = 0; /* this ensures, that */
+ /* the packet does not get */
+ /* processed as data! */
+ return (0);
+ } else {
+ unsigned long bitmap_len;
+ /* */
+ if (!state.recvd_oack) {
+
+ state.total_packets =
+ 1 + (filesize -
+ (filesize %
+ state.block_size)) /
+ state.block_size;
+ bitmap_len =
+ (state.total_packets + 7) / 8;
+ if (!state.image) {
+ state.bitmap =
+ allot(bitmap_len);
+ state.image =
+ allot(filesize);
+
+ if ((unsigned long) state.
+ image < 1024 * 1024) {
+ printf
+ ("ALERT: tftp filesize to large for available memory\n");
+ return 0;
+ }
+ memset(state.bitmap, 0,
+ bitmap_len);
+ }
+ /* If I'm running over multicast join the multicast group */
+ join_group(IGMP_SERVER,
+ info->multicast_ip.
+ s_addr);
+ }
+ state.recvd_oack = 1;
+ }
+
+
+
+ } else if (tr->opcode == htons(TFTP_DATA)) {
+ unsigned long data_len;
+ unsigned char *data;
+ struct udphdr *udp;
+ udp =
+ (struct udphdr *) &nic.packet[ETH_HLEN +
+ sizeof(struct
+ iphdr)];
+ len =
+ ntohs(tr->udp.len) - sizeof(struct udphdr) - 4;
+ data =
+ nic.packet + ETH_HLEN + sizeof(struct iphdr) +
+ sizeof(struct udphdr) + 4;
+
+ if (len > TFTM_MIN_PACKET) /* shouldn't happen */
+ continue; /* ignore it */
+
+ block = ntohs(tp.u.ack.block = tr->u.data.block);
+
+ if (block > state.total_packets) {
+ printf("ALERT: Invalid packet number\n");
+ continue;
+ }
+
+ /* Compute the expected data length */
+ if (block != state.total_packets) {
+ data_len = state.block_size;
+ } else {
+ data_len = filesize % state.block_size;
+ }
+ /* If the packet size is wrong drop the packet and then continue */
+ if (ntohs(udp->len) !=
+ (data_len + (data - (unsigned char *) udp))) {
+ printf
+ ("ALERT: udp packet is not the correct size: %d\n",
+ block);
+ continue;
+ }
+ if (nic.packetlen < data_len + (data - nic.packet)) {
+ printf
+ ("ALERT: Ethernet packet shorter than data_len: %d\n",
+ block);
+ continue;
+ }
+
+ if (data_len > state.block_size) {
+ data_len = state.block_size;
+ }
+ if (((state.
+ bitmap[block >> 3] >> (block & 7)) & 1) ==
+ 0) {
+ /* Non duplicate packet */
+ state.bitmap[block >> 3] |=
+ (1 << (block & 7));
+ memcpy(state.image +
+ ((block - 1) * state.block_size),
+ data, data_len);
+ state.received_packets++;
+ } else {
+
+/* printf("<DUP>\n"); */
+ }
+ }
+
+ else { /* neither TFTP_OACK, TFTP_DATA nor TFTP_ERROR */
+ break;
+ }
+
+ if (state.received_packets <= state.total_packets) {
+ unsigned long b;
+ unsigned long len;
+ unsigned long max;
+ int value;
+ int last;
+
+ /* Compute the last bit and store an inverted trailer */
+ max = state.total_packets + 1;
+ value =
+ ((state.
+ bitmap[(max - 1) >> 3] >> ((max -
+ 1) & 7)) & 1);
+ value = !value;
+ state.bitmap[max >> 3] &= ~(1 << (max & 7));
+ state.bitmap[max >> 3] |= value << (max & 7);
+
+ len = 0;
+ last = 0; /* Start with the received packets */
+ for (b = 1; b <= max; b++) {
+ value =
+ (state.bitmap[b >> 3] >> (b & 7)) & 1;
+
+ if (value == 0) {
+ tp.u.ack.block = htons(b - 1); /* Acknowledge the previous block */
+ break;
+ }
+ }
+ }
+ if (state.ismaster) {
+ tp.opcode = htons(TFTP_ACK);
+ oport = ntohs(tr->udp.src);
+ udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, iport, oport, TFTP_MIN_PACKET, &tp); /* ack */
+ }
+ if (state.received_packets == state.total_packets) {
+ /* If the client is finished and not the master,
+ * ack the last packet */
+ if (!state.ismaster) {
+ tp.opcode = htons(TFTP_ACK);
+ /* Ack Last packet to end xfer */
+ tp.u.ack.block = htons(state.total_packets);
+ oport = ntohs(tr->udp.src);
+ udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, iport, oport, TFTP_MIN_PACKET, &tp); /* ack */
+ }
+ /* We are done get out */
+ forget(state.bitmap);
+ break;
+ }
+
+ if ((unsigned short) (block - prevblock) != 1) {
+ /* Retransmission or OACK, don't process via callback
+ * and don't change the value of prevblock. */
+ continue;
+ }
+
+ prevblock = block;
+ retry = 0; /* It's the right place to zero the timer? */
+
+ }
+ /* Leave the multicast group */
+ leave_group(IGMP_SERVER);
+ return info->fnc(state.image, 1, filesize, 1);
+}
+
+int url_tftm(const char *name,
+ int (*fnc) (unsigned char *, unsigned int, unsigned int, int))
+{
+
+ int ret;
+ struct tftm_info info;
+
+ /* Set the defaults */
+ info.server_ip.s_addr = arptable[ARP_SERVER].ipaddr.s_addr;
+ info.server_port = TFTM_PORT;
+ info.local_ip.s_addr = arptable[ARP_CLIENT].ipaddr.s_addr;
+ info.local_port = TFTM_PORT; /* Does not matter. So take tftm port too. */
+ info.multicast_ip.s_addr = info.local_ip.s_addr;
+ info.multicast_port = TFTM_PORT;
+ info.fnc = fnc;
+ state.ismaster = 0;
+ info.name = name;
+
+ state.block_size = 0;
+ state.total_bytes = 0;
+ state.total_packets = 0;
+ state.received_packets = 0;
+ state.image = 0;
+ state.bitmap = 0;
+ state.recvd_oack = 0;
+
+ if (name[0] != '/') {
+ /* server ip given, so use it */
+ name += inet_aton(info.name, &info.server_ip);
+ /* No way to specify another port for now */
+ }
+ if (name[0] != '/') {
+ printf("Bad tftm-URI: [%s]\n", info.name);
+ return 0;
+ }
+
+ ret = proto_tftm(&info);
+
+ return ret;
+}
+
+/******************************
+* Parse the multicast options
+*******************************/
+int opt_get_multicast(struct tftp_t *tr, unsigned short *len,
+ unsigned long *filesize, struct tftm_info *info)
+{
+ const char *p = tr->u.oack.data, *e = 0;
+ int i = 0;
+ *len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 2;
+ if (*len > TFTM_MIN_PACKET)
+ return -1;
+ e = p + *len;
+
+ while (*p != '\0' && p < e) {
+ if (!strcasecmp("tsize", p)) {
+ p += 6;
+ if ((*filesize = strtoul(p, &p, 10)) > 0)
+ i |= 4;
+ debug(("\n"));
+ debug(("tsize=%d\n", *filesize));
+ while (p < e && *p)
+ p++;
+ if (p < e)
+ p++;
+ } else if (!strcasecmp("blksize", p)) {
+ i |= 2;
+ p += 8;
+ state.block_size = strtoul(p, &p, 10);
+ if (state.block_size != TFTM_MIN_PACKET) {
+ printf
+ ("TFTM-Server rejected required transfer blocksize %d\n",
+ TFTM_MIN_PACKET);
+ return 0;
+ }
+ debug(("blksize=%d\n", state.block_size));
+ while (p < e && *p)
+ p++;
+ if (p < e)
+ p++;
+ } else if (!strncmp(p, "multicast", 10)) {
+ i |= 1;
+ p += 10;
+ debug(("multicast options: %s\n", p));
+ p += 1 + inet_aton(p, &info->multicast_ip);
+ debug(("multicast ip = %@\n", info->multicast_ip));
+ info->multicast_port = strtoul(p, &p, 10);
+ ++p;
+ debug(("multicast port = %d\n",
+ info->multicast_port));
+ state.ismaster = (*p == '1' ? 1 : 0);
+ debug(("multicast ismaster = %d\n",
+ state.ismaster));
+ while (p < e && *p)
+ p++;
+ if (p < e)
+ p++;
+ }
+ }
+ if (p > e)
+ return 0;
+ return i;
+}
+#endif /* DOWNLOAD_PROTO_TFTP */
diff --git a/src/core/pxe_export.c b/src/core/pxe_export.c
new file mode 100644
index 00000000..0199de28
--- /dev/null
+++ b/src/core/pxe_export.c
@@ -0,0 +1,1424 @@
+/* PXE API interface for Etherboot.
+ *
+ * Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Tags used in this file:
+ *
+ * FIXME : obvious
+ * PXESPEC : question over interpretation of the PXE spec.
+ */
+
+#ifdef PXE_EXPORT
+
+#include "etherboot.h"
+#include "pxe.h"
+#include "pxe_export.h"
+#include "pxe_callbacks.h"
+#include "nic.h"
+#include "pci.h"
+#include "dev.h"
+#include "cpu.h"
+#include "timer.h"
+
+#if TRACE_PXE
+#define DBG(...) printf ( __VA_ARGS__ )
+#else
+#define DBG(...)
+#endif
+
+/* Not sure why this isn't a globally-used structure within Etherboot.
+ * (Because I didn't want to use packed to prevent gcc from aligning
+ * source however it liked. Also nstype is a u16, not a uint. - Ken)
+ */
+typedef struct {
+ char dest[ETH_ALEN];
+ char source[ETH_ALEN];
+ unsigned int nstype;
+} media_header_t;
+static const char broadcast_mac[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF };
+
+/* Global pointer to currently installed PXE stack */
+pxe_stack_t *pxe_stack = NULL;
+
+/* Various startup/shutdown routines. The startup/shutdown call
+ * sequence is incredibly badly defined in the Intel PXE spec, for
+ * example:
+ *
+ * PXENV_UNDI_INITIALIZE says that the parameters used to initialize
+ * the adaptor should be those supplied to the most recent
+ * PXENV_UNDI_STARTUP call. PXENV_UNDI_STARTUP takes no parameters.
+ *
+ * PXENV_UNDI_CLEANUP says that the rest of the API will not be
+ * available after making this call. Figure 3-3 ("Early UNDI API
+ * usage") shows a call to PXENV_UNDI_CLEANUP being followed by a
+ * call to the supposedly now unavailable PXENV_STOP_UNDI.
+ *
+ * PXENV_UNLOAD_BASE_STACK talks about freeing up the memory
+ * occupied by the PXE stack. Figure 4-3 ("PXE IPL") shows a call
+ * to PXENV_STOP_UNDI being made after the call to
+ * PXENV_UNLOAD_BASE_STACK, by which time the entire PXE stack
+ * should have been freed (and, potentially, zeroed).
+ *
+ * Nothing, anywhere, seems to mention who's responsible for freeing
+ * up the base memory allocated for the stack segment. It's not
+ * even clear whether or not this is expected to be in free base
+ * memory rather than claimed base memory.
+ *
+ * Consequently, we adopt a rather defensive strategy, designed to
+ * work with any conceivable sequence of initialisation or shutdown
+ * calls. We have only two things that we care about:
+ *
+ * 1. Have we hooked INT 1A and INT 15,E820(etc.)?
+ * 2. Is the NIC initialised?
+ *
+ * The NIC should never be initialised without the vectors being
+ * hooked, similarly the vectors should never be unhooked with the NIC
+ * still initialised. We do, however, want to be able to have the
+ * vectors hooked with the NIC shutdown. We therefore have three
+ * possible states:
+ *
+ * 1. Ready to unload: interrupts unhooked, NIC shutdown.
+ * 2. Midway: interrupts hooked, NIC shutdown.
+ * 3. Fully ready: interrupts hooked, NIC initialised.
+ *
+ * We provide the three states CAN_UNLOAD, MIDWAY and READY to define
+ * these, and the call pxe_ensure_state() to ensure that the stack is
+ * in the specified state. All our PXE API call implementations
+ * should use this call to ensure that the state is as required for
+ * that PXE API call. This enables us to cope with whatever the
+ * end-user's interpretation of the PXE spec may be. It even allows
+ * for someone calling e.g. PXENV_START_UNDI followed by
+ * PXENV_UDP_WRITE, without bothering with any of the intervening
+ * calls.
+ *
+ * pxe_ensure_state() returns 1 for success, 0 for failure. In the
+ * event of failure (which can arise from e.g. asking for state READY
+ * when we don't know where our NIC is), the error code
+ * PXENV_STATUS_UNDI_INVALID_STATE should be returned to the user.
+ * The macros ENSURE_XXX() can be used to achieve this without lots of
+ * duplicated code.
+ */
+
+/* pxe_[un]hook_stack are architecture-specific and provided in
+ * pxe_callbacks.c
+ */
+
+int pxe_initialise_nic ( void ) {
+ if ( pxe_stack->state >= READY ) return 1;
+
+ /* Check if NIC is initialised. nic.dev.disable is set to 0
+ * when disable() is called, so we use this.
+ */
+ if ( nic.dev.disable ) {
+ /* NIC may have been initialised independently
+ * (e.g. when we set up the stack prior to calling the
+ * NBP).
+ */
+ pxe_stack->state = READY;
+ return 1;
+ }
+
+ /* If we already have a NIC defined, reuse that one with
+ * PROBE_AWAKE. If one was specifed via PXENV_START_UNDI, try
+ * that one first. Otherwise, set PROBE_FIRST.
+ */
+ if ( nic.dev.state.pci.dev.use_specified == 1 ) {
+ nic.dev.how_probe = PROBE_NEXT;
+ DBG ( " initialising NIC specified via START_UNDI" );
+ } else if ( nic.dev.state.pci.dev.driver ) {
+ DBG ( " reinitialising NIC" );
+ nic.dev.how_probe = PROBE_AWAKE;
+ } else {
+ DBG ( " probing for any NIC" );
+ nic.dev.how_probe = PROBE_FIRST;
+ }
+
+ /* Call probe routine to bring up the NIC */
+ if ( eth_probe ( &nic.dev ) != PROBE_WORKED ) {
+ DBG ( " failed" );
+ return 0;
+ }
+ pxe_stack->state = READY;
+ return 1;
+}
+
+int pxe_shutdown_nic ( void ) {
+ if ( pxe_stack->state <= MIDWAY ) return 1;
+
+ eth_irq ( DISABLE );
+ eth_disable();
+ pxe_stack->state = MIDWAY;
+ return 1;
+}
+
+int ensure_pxe_state ( pxe_stack_state_t wanted ) {
+ int success = 1;
+
+ if ( ! pxe_stack ) return 0;
+ if ( wanted >= MIDWAY )
+ success = success & hook_pxe_stack();
+ if ( wanted > MIDWAY ) {
+ success = success & pxe_initialise_nic();
+ } else {
+ success = success & pxe_shutdown_nic();
+ }
+ if ( wanted < MIDWAY )
+ success = success & unhook_pxe_stack();
+ return success;
+}
+
+#define ENSURE_CAN_UNLOAD(structure) if ( ! ensure_pxe_state(CAN_UNLOAD) ) { \
+ structure->Status = PXENV_STATUS_UNDI_INVALID_STATE; \
+ return PXENV_EXIT_FAILURE; }
+#define ENSURE_MIDWAY(structure) if ( ! ensure_pxe_state(MIDWAY) ) { \
+ structure->Status = PXENV_STATUS_UNDI_INVALID_STATE; \
+ return PXENV_EXIT_FAILURE; }
+#define ENSURE_READY(structure) if ( ! ensure_pxe_state(READY) ) { \
+ structure->Status = PXENV_STATUS_UNDI_INVALID_STATE; \
+ return PXENV_EXIT_FAILURE; }
+
+/*****************************************************************************
+ *
+ * Actual PXE API calls
+ *
+ *****************************************************************************/
+
+/* PXENV_START_UNDI
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_start_undi ( t_PXENV_START_UNDI *start_undi ) {
+ unsigned char bus, devfn;
+ struct pci_probe_state *pci = &nic.dev.state.pci;
+ struct dev *dev = &nic.dev;
+
+ DBG ( "PXENV_START_UNDI" );
+ ENSURE_MIDWAY(start_undi);
+
+ /* Record PCI bus & devfn passed by caller, so we know which
+ * NIC they want to use.
+ *
+ * If they don't match our already-existing NIC structure, set
+ * values to ensure that the specified NIC is used at the next
+ * call to pxe_intialise_nic().
+ */
+ bus = ( start_undi->ax >> 8 ) & 0xff;
+ devfn = start_undi->ax & 0xff;
+
+ if ( ( pci->dev.driver == NULL ) ||
+ ( pci->dev.bus != bus ) || ( pci->dev.devfn != devfn ) ) {
+ /* This is quite a bit of a hack and relies on
+ * knowledge of the internal operation of Etherboot's
+ * probe mechanism.
+ */
+ DBG ( " set PCI %hhx:%hhx.%hhx",
+ bus, PCI_SLOT(devfn), PCI_FUNC(devfn) );
+ dev->type = BOOT_NIC;
+ dev->to_probe = PROBE_PCI;
+ memset ( &dev->state, 0, sizeof(dev->state) );
+ pci->advance = 1;
+ pci->dev.use_specified = 1;
+ pci->dev.bus = bus;
+ pci->dev.devfn = devfn;
+ }
+
+ start_undi->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_STARTUP
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_startup ( t_PXENV_UNDI_STARTUP *undi_startup ) {
+ DBG ( "PXENV_UNDI_STARTUP" );
+ ENSURE_MIDWAY(undi_startup);
+
+ undi_startup->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_CLEANUP
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_cleanup ( t_PXENV_UNDI_CLEANUP *undi_cleanup ) {
+ DBG ( "PXENV_UNDI_CLEANUP" );
+ ENSURE_CAN_UNLOAD ( undi_cleanup );
+
+ undi_cleanup->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_INITIALIZE
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_initialize ( t_PXENV_UNDI_INITIALIZE
+ *undi_initialize ) {
+ DBG ( "PXENV_UNDI_INITIALIZE" );
+ ENSURE_MIDWAY ( undi_initialize );
+
+ undi_initialize->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_RESET_ADAPTER
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_reset_adapter ( t_PXENV_UNDI_RESET_ADAPTER
+ *undi_reset_adapter ) {
+ DBG ( "PXENV_UNDI_RESET_ADAPTER" );
+
+ ENSURE_MIDWAY ( undi_reset_adapter );
+ ENSURE_READY ( undi_reset_adapter );
+
+ undi_reset_adapter->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_SHUTDOWN
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_shutdown ( t_PXENV_UNDI_SHUTDOWN *undi_shutdown ) {
+ DBG ( "PXENV_UNDI_SHUTDOWN" );
+ ENSURE_MIDWAY ( undi_shutdown );
+
+ undi_shutdown->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_OPEN
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_open ( t_PXENV_UNDI_OPEN *undi_open ) {
+ DBG ( "PXENV_UNDI_OPEN" );
+ ENSURE_READY ( undi_open );
+
+ /* PXESPEC: This is where we choose to enable interrupts.
+ * Can't actually find where we're meant to in the PXE spec,
+ * but this should work.
+ */
+ eth_irq ( ENABLE );
+
+ undi_open->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_CLOSE
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_close ( t_PXENV_UNDI_CLOSE *undi_close ) {
+ DBG ( "PXENV_UNDI_CLOSE" );
+ ENSURE_MIDWAY ( undi_close );
+
+ undi_close->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_TRANSMIT
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_transmit ( t_PXENV_UNDI_TRANSMIT *undi_transmit ) {
+ t_PXENV_UNDI_TBD *tbd;
+ const char *dest;
+ unsigned int type;
+ unsigned int length;
+ const char *data;
+ media_header_t *media_header;
+
+ DBG ( "PXENV_UNDI_TRANSMIT" );
+ ENSURE_READY ( undi_transmit );
+
+ /* We support only the "immediate" portion of the TBD. Who
+ * knows what Intel's "engineers" were smoking when they came
+ * up with the array of transmit data blocks...
+ */
+ tbd = SEGOFF16_TO_PTR ( undi_transmit->TBD );
+ if ( tbd->DataBlkCount > 0 ) {
+ undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
+ return PXENV_EXIT_FAILURE;
+ }
+ data = SEGOFF16_TO_PTR ( tbd->Xmit );
+ length = tbd->ImmedLength;
+
+ /* If destination is broadcast, we need to supply the MAC address */
+ if ( undi_transmit->XmitFlag == XMT_BROADCAST ) {
+ dest = broadcast_mac;
+ } else {
+ dest = SEGOFF16_TO_PTR ( undi_transmit->DestAddr );
+ }
+
+ /* We can't properly support P_UNKNOWN without rewriting all
+ * the driver transmit() methods, so we cheat: if P_UNKNOWN is
+ * specified we rip the destination address and type out of
+ * the pre-assembled packet, then skip over the header.
+ */
+ switch ( undi_transmit->Protocol ) {
+ case P_IP: type = IP; break;
+ case P_ARP: type = ARP; break;
+ case P_RARP: type = RARP; break;
+ case P_UNKNOWN:
+ media_header = (media_header_t*)data;
+ dest = media_header->dest;
+ type = ntohs ( media_header->nstype );
+ data += ETH_HLEN;
+ length -= ETH_HLEN;
+ break;
+ default:
+ undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ /* Send the packet */
+ eth_transmit ( dest, type, length, data );
+
+ undi_transmit->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_SET_MCAST_ADDRESS
+ *
+ * Status: stub (no PXE multicast support)
+ */
+PXENV_EXIT_t pxenv_undi_set_mcast_address ( t_PXENV_UNDI_SET_MCAST_ADDRESS
+ *undi_set_mcast_address ) {
+ DBG ( "PXENV_UNDI_SET_MCAST_ADDRESS" );
+ /* ENSURE_READY ( undi_set_mcast_address ); */
+ undi_set_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
+ return PXENV_EXIT_FAILURE;
+}
+
+/* PXENV_UNDI_SET_STATION_ADDRESS
+ *
+ * Status: working (deliberately incomplete)
+ */
+PXENV_EXIT_t pxenv_undi_set_station_address ( t_PXENV_UNDI_SET_STATION_ADDRESS
+ *undi_set_station_address ) {
+ DBG ( "PXENV_UNDI_SET_STATION_ADDRESS" );
+ ENSURE_READY ( undi_set_station_address );
+
+ /* We don't offer a facility to set the MAC address; this
+ * would require adding extra code to all the Etherboot
+ * drivers, for very little benefit. If we're setting it to
+ * the current value anyway then return success, otherwise
+ * return UNSUPPORTED.
+ */
+ if ( memcmp ( nic.node_addr,
+ &undi_set_station_address->StationAddress,
+ ETH_ALEN ) == 0 ) {
+ undi_set_station_address->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+ }
+ undi_set_station_address->Status = PXENV_STATUS_UNSUPPORTED;
+ return PXENV_EXIT_FAILURE;
+}
+
+/* PXENV_UNDI_SET_PACKET_FILTER
+ *
+ * Status: won't implement (would require driver API changes for no
+ * real benefit)
+ */
+PXENV_EXIT_t pxenv_undi_set_packet_filter ( t_PXENV_UNDI_SET_PACKET_FILTER
+ *undi_set_packet_filter ) {
+ DBG ( "PXENV_UNDI_SET_PACKET_FILTER" );
+ /* ENSURE_READY ( undi_set_packet_filter ); */
+ undi_set_packet_filter->Status = PXENV_STATUS_UNSUPPORTED;
+ return PXENV_EXIT_FAILURE;
+}
+
+/* PXENV_UNDI_GET_INFORMATION
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_get_information ( t_PXENV_UNDI_GET_INFORMATION
+ *undi_get_information ) {
+ DBG ( "PXENV_UNDI_GET_INFORMATION" );
+ ENSURE_READY ( undi_get_information );
+
+ undi_get_information->BaseIo = nic.ioaddr;
+ undi_get_information->IntNumber = nic.irqno;
+ /* Cheat: assume all cards can cope with this */
+ undi_get_information->MaxTranUnit = ETH_MAX_MTU;
+ /* Cheat: we only ever have Ethernet cards */
+ undi_get_information->HwType = ETHER_TYPE;
+ undi_get_information->HwAddrLen = ETH_ALEN;
+ /* Cheat: assume card is always configured with its permanent
+ * node address. This is a valid assumption within Etherboot
+ * at the time of writing.
+ */
+ memcpy ( &undi_get_information->CurrentNodeAddress, nic.node_addr,
+ ETH_ALEN );
+ memcpy ( &undi_get_information->PermNodeAddress, nic.node_addr,
+ ETH_ALEN );
+ undi_get_information->ROMAddress = nic.rom_info->rom_segment;
+ /* We only provide the ability to receive or transmit a single
+ * packet at a time. This is a bootloader, not an OS.
+ */
+ undi_get_information->RxBufCt = 1;
+ undi_get_information->TxBufCt = 1;
+ undi_get_information->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_GET_STATISTICS
+ *
+ * Status: won't implement (would require driver API changes for no
+ * real benefit)
+ */
+PXENV_EXIT_t pxenv_undi_get_statistics ( t_PXENV_UNDI_GET_STATISTICS
+ *undi_get_statistics ) {
+ DBG ( "PXENV_UNDI_GET_STATISTICS" );
+ /* ENSURE_READY ( undi_get_statistics ); */
+ undi_get_statistics->Status = PXENV_STATUS_UNSUPPORTED;
+ return PXENV_EXIT_FAILURE;
+}
+
+/* PXENV_UNDI_CLEAR_STATISTICS
+ *
+ * Status: won't implement (would require driver API changes for no
+ * real benefit)
+ */
+PXENV_EXIT_t pxenv_undi_clear_statistics ( t_PXENV_UNDI_CLEAR_STATISTICS
+ *undi_clear_statistics ) {
+ DBG ( "PXENV_UNDI_CLEAR_STATISTICS" );
+ /* ENSURE_READY ( undi_clear_statistics ); */
+ undi_clear_statistics->Status = PXENV_STATUS_UNSUPPORTED;
+ return PXENV_EXIT_FAILURE;
+}
+
+/* PXENV_UNDI_INITIATE_DIAGS
+ *
+ * Status: won't implement (would require driver API changes for no
+ * real benefit)
+ */
+PXENV_EXIT_t pxenv_undi_initiate_diags ( t_PXENV_UNDI_INITIATE_DIAGS
+ *undi_initiate_diags ) {
+ DBG ( "PXENV_UNDI_INITIATE_DIAGS" );
+ /* ENSURE_READY ( undi_initiate_diags ); */
+ undi_initiate_diags->Status = PXENV_STATUS_UNSUPPORTED;
+ return PXENV_EXIT_FAILURE;
+}
+
+/* PXENV_UNDI_FORCE_INTERRUPT
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_force_interrupt ( t_PXENV_UNDI_FORCE_INTERRUPT
+ *undi_force_interrupt ) {
+ DBG ( "PXENV_UNDI_FORCE_INTERRUPT" );
+ ENSURE_READY ( undi_force_interrupt );
+
+ eth_irq ( FORCE );
+ undi_force_interrupt->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_GET_MCAST_ADDRESS
+ *
+ * Status: stub (no PXE multicast support)
+ */
+PXENV_EXIT_t pxenv_undi_get_mcast_address ( t_PXENV_UNDI_GET_MCAST_ADDRESS
+ *undi_get_mcast_address ) {
+ DBG ( "PXENV_UNDI_GET_MCAST_ADDRESS" );
+ /* ENSURE_READY ( undi_get_mcast_address ); */
+ undi_get_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
+ return PXENV_EXIT_FAILURE;
+}
+
+/* PXENV_UNDI_GET_NIC_TYPE
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_get_nic_type ( t_PXENV_UNDI_GET_NIC_TYPE
+ *undi_get_nic_type ) {
+ struct dev *dev = &nic.dev;
+
+ DBG ( "PXENV_UNDI_GET_NIC_TYPE" );
+ ENSURE_READY ( undi_get_nic_type );
+
+ if ( dev->to_probe == PROBE_PCI ) {
+ struct pci_device *pci = &dev->state.pci.dev;
+
+ undi_get_nic_type->NicType = PCI_NIC;
+ undi_get_nic_type->info.pci.Vendor_ID = pci->vendor;
+ undi_get_nic_type->info.pci.Dev_ID = pci->dev_id;
+ undi_get_nic_type->info.pci.Base_Class = pci->class >> 8;
+ undi_get_nic_type->info.pci.Sub_Class = pci->class & 0xff;
+ undi_get_nic_type->info.pci.BusDevFunc =
+ ( pci->bus << 8 ) | pci->devfn;
+ /* Cheat: these fields are probably unnecessary, and
+ * would require adding extra code to pci.c.
+ */
+ undi_get_nic_type->info.pci.Prog_Intf = 0;
+ undi_get_nic_type->info.pci.Rev = 0;
+ undi_get_nic_type->info.pci.SubVendor_ID = 0xffff;
+ undi_get_nic_type->info.pci.SubDevice_ID = 0xffff;
+ } else if ( dev->to_probe == PROBE_ISA ) {
+ /* const struct isa_driver *isa = dev->state.isa.driver; */
+
+ undi_get_nic_type->NicType = PnP_NIC;
+ /* Don't think anything fills these fields in, and
+ * probably no-one will ever be interested in them.
+ */
+ undi_get_nic_type->info.pnp.EISA_Dev_ID = 0;
+ undi_get_nic_type->info.pnp.Base_Class = 0;
+ undi_get_nic_type->info.pnp.Sub_Class = 0;
+ undi_get_nic_type->info.pnp.Prog_Intf = 0;
+ undi_get_nic_type->info.pnp.CardSelNum = 0;
+ } else {
+ /* PXESPEC: There doesn't seem to be an "unknown type"
+ * defined.
+ */
+ undi_get_nic_type->NicType = 0;
+ }
+ undi_get_nic_type->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_GET_IFACE_INFO
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_get_iface_info ( t_PXENV_UNDI_GET_IFACE_INFO
+ *undi_get_iface_info ) {
+ DBG ( "PXENV_UNDI_GET_IFACE_INFO" );
+ ENSURE_READY ( undi_get_iface_info );
+
+ /* Just hand back some info, doesn't really matter what it is.
+ * Most PXE stacks seem to take this approach.
+ */
+ sprintf ( undi_get_iface_info->IfaceType, "Etherboot" );
+ undi_get_iface_info->LinkSpeed = 10000000; /* 10 Mbps */
+ undi_get_iface_info->ServiceFlags = 0;
+ memset ( undi_get_iface_info->Reserved, 0,
+ sizeof(undi_get_iface_info->Reserved) );
+ undi_get_iface_info->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_ISR
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_undi_isr ( t_PXENV_UNDI_ISR *undi_isr ) {
+ media_header_t *media_header = (media_header_t*)nic.packet;
+
+ DBG ( "PXENV_UNDI_ISR" );
+ /* We can't call ENSURE_READY, because this could be being
+ * called as part of an interrupt service routine. Instead,
+ * we should simply die if we're not READY.
+ */
+ if ( ( pxe_stack == NULL ) || ( pxe_stack->state < READY ) ) {
+ undi_isr->Status = PXENV_STATUS_UNDI_INVALID_STATE;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ /* Just in case some idiot actually looks at these fields when
+ * we weren't meant to fill them in...
+ */
+ undi_isr->BufferLength = 0;
+ undi_isr->FrameLength = 0;
+ undi_isr->FrameHeaderLength = 0;
+ undi_isr->ProtType = 0;
+ undi_isr->PktType = 0;
+
+ switch ( undi_isr->FuncFlag ) {
+ case PXENV_UNDI_ISR_IN_START :
+ /* Is there a packet waiting? If so, disable
+ * interrupts on the NIC and return "it's ours". Do
+ * *not* necessarily acknowledge the interrupt; this
+ * can happen later when eth_poll(1) is called. As
+ * long as the interrupt is masked off so that it
+ * doesn't immediately retrigger the 8259A then all
+ * should be well.
+ */
+ DBG ( " START" );
+ if ( eth_poll ( 0 ) ) {
+ DBG ( " OURS" );
+ eth_irq ( DISABLE );
+ undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS;
+ } else {
+ DBG ( " NOT_OURS" );
+ undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_NOT_OURS;
+ }
+ break;
+ case PXENV_UNDI_ISR_IN_PROCESS :
+ /* Call poll(), return packet. If no packet, return "done".
+ */
+ DBG ( " PROCESS" );
+ if ( eth_poll ( 1 ) ) {
+ DBG ( " RECEIVE %d", nic.packetlen );
+ if ( nic.packetlen > sizeof(pxe_stack->packet) ) {
+ /* Should never happen */
+ undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
+ undi_isr->Status =
+ PXENV_STATUS_OUT_OF_RESOURCES;
+ return PXENV_EXIT_FAILURE;
+ }
+ undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_RECEIVE;
+ undi_isr->BufferLength = nic.packetlen;
+ undi_isr->FrameLength = nic.packetlen;
+ undi_isr->FrameHeaderLength = ETH_HLEN;
+ memcpy ( pxe_stack->packet, nic.packet, nic.packetlen);
+ PTR_TO_SEGOFF16 ( pxe_stack->packet, undi_isr->Frame );
+ switch ( ntohs(media_header->nstype) ) {
+ case IP : undi_isr->ProtType = P_IP; break;
+ case ARP : undi_isr->ProtType = P_ARP; break;
+ case RARP : undi_isr->ProtType = P_RARP; break;
+ default : undi_isr->ProtType = P_UNKNOWN;
+ }
+ if ( memcmp ( media_header->dest, broadcast_mac,
+ sizeof(broadcast_mac) ) ) {
+ undi_isr->PktType = XMT_BROADCAST;
+ } else {
+ undi_isr->PktType = XMT_DESTADDR;
+ }
+ break;
+ } else {
+ /* No break - fall through to IN_GET_NEXT */
+ }
+ case PXENV_UNDI_ISR_IN_GET_NEXT :
+ /* We only ever return one frame at a time */
+ DBG ( " GET_NEXT DONE" );
+ /* Re-enable interrupts */
+ eth_irq ( ENABLE );
+ /* Force an interrupt if there's a packet still
+ * waiting, since we only handle one packet per
+ * interrupt.
+ */
+ if ( eth_poll ( 0 ) ) {
+ DBG ( " (RETRIGGER)" );
+ eth_irq ( FORCE );
+ }
+ undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
+ break;
+ default :
+ /* Should never happen */
+ undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
+ undi_isr->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ undi_isr->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_STOP_UNDI
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_stop_undi ( t_PXENV_STOP_UNDI *stop_undi ) {
+ DBG ( "PXENV_STOP_UNDI" );
+
+ if ( ! ensure_pxe_state(CAN_UNLOAD) ) {
+ stop_undi->Status = PXENV_STATUS_KEEP_UNDI;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ stop_undi->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_TFTP_OPEN
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_tftp_open ( t_PXENV_TFTP_OPEN *tftp_open ) {
+ struct tftpreq_info_t request;
+ struct tftpblk_info_t block;
+
+ DBG ( "PXENV_TFTP_OPEN" );
+ ENSURE_READY ( tftp_open );
+
+ /* Change server address if different */
+ if ( tftp_open->ServerIPAddress &&
+ tftp_open->ServerIPAddress!=arptable[ARP_SERVER].ipaddr.s_addr ) {
+ memset(arptable[ARP_SERVER].node, 0, ETH_ALEN ); /* kill arp */
+ arptable[ARP_SERVER].ipaddr.s_addr=tftp_open->ServerIPAddress;
+ }
+ /* Ignore gateway address; we can route properly */
+ /* Fill in request structure */
+ request.name = tftp_open->FileName;
+ request.port = ntohs(tftp_open->TFTPPort);
+#ifdef WORK_AROUND_BPBATCH_BUG
+ /* Force use of port 69; BpBatch tries to use port 4 for some
+ * bizarre reason. */
+ request.port = TFTP_PORT;
+#endif
+ request.blksize = tftp_open->PacketSize;
+ DBG ( " %@:%d/%s (%d)", tftp_open->ServerIPAddress,
+ request.port, request.name, request.blksize );
+ if ( !request.blksize ) request.blksize = TFTP_DEFAULTSIZE_PACKET;
+ /* Make request and get first packet */
+ if ( !tftp_block ( &request, &block ) ) {
+ tftp_open->Status = PXENV_STATUS_TFTP_FILE_NOT_FOUND;
+ return PXENV_EXIT_FAILURE;
+ }
+ /* Fill in PacketSize */
+ tftp_open->PacketSize = request.blksize;
+ /* Store first block for later retrieval by TFTP_READ */
+ pxe_stack->tftpdata.magic_cookie = PXE_TFTP_MAGIC_COOKIE;
+ pxe_stack->tftpdata.len = block.len;
+ pxe_stack->tftpdata.eof = block.eof;
+ memcpy ( pxe_stack->tftpdata.data, block.data, block.len );
+
+ tftp_open->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_TFTP_CLOSE
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_tftp_close ( t_PXENV_TFTP_CLOSE *tftp_close ) {
+ DBG ( "PXENV_TFTP_CLOSE" );
+ ENSURE_READY ( tftp_close );
+ tftp_close->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_TFTP_READ
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_tftp_read ( t_PXENV_TFTP_READ *tftp_read ) {
+ struct tftpblk_info_t block;
+
+ DBG ( "PXENV_TFTP_READ" );
+ ENSURE_READY ( tftp_read );
+
+ /* Do we have a block pending */
+ if ( pxe_stack->tftpdata.magic_cookie == PXE_TFTP_MAGIC_COOKIE ) {
+ block.data = pxe_stack->tftpdata.data;
+ block.len = pxe_stack->tftpdata.len;
+ block.eof = pxe_stack->tftpdata.eof;
+ block.block = 1; /* Will be the first block */
+ pxe_stack->tftpdata.magic_cookie = 0;
+ } else {
+ if ( !tftp_block ( NULL, &block ) ) {
+ tftp_read->Status = PXENV_STATUS_TFTP_FILE_NOT_FOUND;
+ return PXENV_EXIT_FAILURE;
+ }
+ }
+
+ /* Return data */
+ tftp_read->PacketNumber = block.block;
+ tftp_read->BufferSize = block.len;
+ memcpy ( SEGOFF16_TO_PTR(tftp_read->Buffer), block.data, block.len );
+ DBG ( " %d to %hx:%hx", block.len, tftp_read->Buffer.segment,
+ tftp_read->Buffer.offset );
+
+ tftp_read->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_TFTP_READ_FILE
+ *
+ * Status: working
+ */
+
+int pxe_tftp_read_block ( unsigned char *data, unsigned int block __unused, unsigned int len, int eof ) {
+ if ( pxe_stack->readfile.buffer ) {
+ if ( pxe_stack->readfile.offset + len >=
+ pxe_stack->readfile.bufferlen ) return -1;
+ memcpy ( pxe_stack->readfile.buffer +
+ pxe_stack->readfile.offset, data, len );
+ }
+ pxe_stack->readfile.offset += len;
+ return eof ? 0 : 1;
+}
+
+PXENV_EXIT_t pxenv_tftp_read_file ( t_PXENV_TFTP_READ_FILE *tftp_read_file ) {
+ int rc;
+
+ DBG ( "PXENV_TFTP_READ_FILE %s to [%x,%x)", tftp_read_file->FileName,
+ tftp_read_file->Buffer, tftp_read_file->Buffer + tftp_read_file->BufferSize );
+ ENSURE_READY ( tftp_read_file );
+
+ /* inserted by Klaus Wittemeier */
+ /* KERNEL_BUF stores the name of the last required file */
+ /* This is a fix to make Microsoft Remote Install Services work (RIS) */
+ memcpy(KERNEL_BUF, tftp_read_file->FileName, sizeof(KERNEL_BUF));
+ /* end of insertion */
+
+ pxe_stack->readfile.buffer = phys_to_virt ( tftp_read_file->Buffer );
+ pxe_stack->readfile.bufferlen = tftp_read_file->BufferSize;
+ pxe_stack->readfile.offset = 0;
+
+ rc = tftp ( tftp_read_file->FileName, pxe_tftp_read_block );
+ if ( rc ) {
+ tftp_read_file->Status = PXENV_STATUS_FAILURE;
+ return PXENV_EXIT_FAILURE;
+ }
+ tftp_read_file->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_TFTP_GET_FSIZE
+ *
+ * Status: working, though ugly (we actually read the whole file,
+ * because it's too ugly to make Etherboot request the tsize option
+ * and hand it to us).
+ */
+PXENV_EXIT_t pxenv_tftp_get_fsize ( t_PXENV_TFTP_GET_FSIZE *tftp_get_fsize ) {
+ int rc;
+
+ DBG ( "PXENV_TFTP_GET_FSIZE" );
+ ENSURE_READY ( tftp_get_fsize );
+
+ pxe_stack->readfile.buffer = NULL;
+ pxe_stack->readfile.bufferlen = 0;
+ pxe_stack->readfile.offset = 0;
+
+ rc = tftp ( tftp_get_fsize->FileName, pxe_tftp_read_block );
+ if ( rc ) {
+ tftp_get_fsize->FileSize = 0;
+ tftp_get_fsize->Status = PXENV_STATUS_FAILURE;
+ return PXENV_EXIT_FAILURE;
+ }
+ tftp_get_fsize->FileSize = pxe_stack->readfile.offset;
+ tftp_get_fsize->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UDP_OPEN
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_udp_open ( t_PXENV_UDP_OPEN *udp_open ) {
+ DBG ( "PXENV_UDP_OPEN" );
+ ENSURE_READY ( udp_open );
+
+ if ( udp_open->src_ip &&
+ udp_open->src_ip != arptable[ARP_CLIENT].ipaddr.s_addr ) {
+ /* Overwrite our IP address */
+ DBG ( " with new IP %@", udp_open->src_ip );
+ arptable[ARP_CLIENT].ipaddr.s_addr = udp_open->src_ip;
+ }
+
+ udp_open->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UDP_CLOSE
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_udp_close ( t_PXENV_UDP_CLOSE *udp_close ) {
+ DBG ( "PXENV_UDP_CLOSE" );
+ ENSURE_READY ( udp_close );
+ udp_close->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UDP_READ
+ *
+ * Status: working
+ */
+int await_pxe_udp ( int ival __unused, void *ptr,
+ unsigned short ptype __unused,
+ struct iphdr *ip, struct udphdr *udp,
+ struct tcphdr *tcp __unused ) {
+ t_PXENV_UDP_READ *udp_read = (t_PXENV_UDP_READ*)ptr;
+ uint16_t d_port;
+ size_t size;
+
+ /* Ignore non-UDP packets */
+ if ( !udp ) {
+ DBG ( " non-UDP" );
+ return 0;
+ }
+
+ /* Check dest_ip */
+ if ( udp_read->dest_ip && ( udp_read->dest_ip != ip->dest.s_addr ) ) {
+ DBG ( " wrong dest IP (got %@, wanted %@)",
+ ip->dest.s_addr, udp_read->dest_ip );
+ return 0;
+ }
+
+ /* Check dest_port */
+ d_port = ntohs ( udp_read->d_port );
+ if ( d_port && ( d_port != ntohs(udp->dest) ) ) {
+ DBG ( " wrong dest port (got %d, wanted %d)",
+ ntohs(udp->dest), d_port );
+ return 0;
+ }
+
+ /* Copy packet to buffer and fill in information */
+ udp_read->src_ip = ip->src.s_addr;
+ udp_read->s_port = udp->src; /* Both in network order */
+ size = ntohs(udp->len) - sizeof(*udp);
+ /* Workaround: NTLDR expects us to fill these in, even though
+ * PXESPEC clearly defines them as input parameters.
+ */
+ udp_read->dest_ip = ip->dest.s_addr;
+ udp_read->d_port = udp->dest;
+ DBG ( " %@:%d->%@:%d (%d)",
+ udp_read->src_ip, ntohs(udp_read->s_port),
+ udp_read->dest_ip, ntohs(udp_read->d_port), size );
+ if ( udp_read->buffer_size < size ) {
+ /* PXESPEC: what error code should we actually return? */
+ DBG ( " buffer too small (%d)", udp_read->buffer_size );
+ udp_read->Status = PXENV_STATUS_OUT_OF_RESOURCES;
+ return 0;
+ }
+ memcpy ( SEGOFF16_TO_PTR ( udp_read->buffer ), &udp->payload, size );
+ udp_read->buffer_size = size;
+
+ return 1;
+}
+
+PXENV_EXIT_t pxenv_udp_read ( t_PXENV_UDP_READ *udp_read ) {
+ DBG ( "PXENV_UDP_READ" );
+ ENSURE_READY ( udp_read );
+
+ /* Use await_reply with a timeout of zero */
+ /* Allow await_reply to change Status if necessary */
+ udp_read->Status = PXENV_STATUS_FAILURE;
+ if ( ! await_reply ( await_pxe_udp, 0, udp_read, 0 ) ) {
+ return PXENV_EXIT_FAILURE;
+ }
+
+ udp_read->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UDP_WRITE
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_udp_write ( t_PXENV_UDP_WRITE *udp_write ) {
+ uint16_t src_port;
+ uint16_t dst_port;
+ struct udppacket *packet = (struct udppacket *)nic.packet;
+ int packet_size;
+
+ DBG ( "PXENV_UDP_WRITE" );
+ ENSURE_READY ( udp_write );
+
+ /* PXE spec says source port is 2069 if not specified */
+ src_port = ntohs(udp_write->src_port);
+ if ( src_port == 0 ) src_port = 2069;
+ dst_port = ntohs(udp_write->dst_port);
+ DBG ( " %d->%@:%d (%d)", src_port, udp_write->ip, dst_port,
+ udp_write->buffer_size );
+
+ /* FIXME: we ignore the gateway specified, since we're
+ * confident of being able to do our own routing. We should
+ * probably allow for multiple gateways.
+ */
+
+ /* Copy payload to packet buffer */
+ packet_size = ( (void*)&packet->payload - (void*)packet )
+ + udp_write->buffer_size;
+ if ( packet_size > ETH_FRAME_LEN ) {
+ udp_write->Status = PXENV_STATUS_OUT_OF_RESOURCES;
+ return PXENV_EXIT_FAILURE;
+ }
+ memcpy ( &packet->payload, SEGOFF16_TO_PTR(udp_write->buffer),
+ udp_write->buffer_size );
+
+ /* Transmit packet */
+ if ( ! udp_transmit ( udp_write->ip, src_port, dst_port,
+ packet_size, packet ) ) {
+ udp_write->Status = PXENV_STATUS_FAILURE;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ udp_write->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNLOAD_STACK
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_unload_stack ( t_PXENV_UNLOAD_STACK *unload_stack ) {
+ int success;
+
+ DBG ( "PXENV_UNLOAD_STACK" );
+ success = ensure_pxe_state ( CAN_UNLOAD );
+
+ /* We need to call cleanup() at some point. The network card
+ * has already been disabled by ENSURE_CAN_UNLOAD(), but for
+ * the sake of completeness we should call the console_fini()
+ * etc. that are part of cleanup().
+ *
+ * There seems to be a lack of consensus on which is the final
+ * PXE API call to make, but it's a fairly safe bet that all
+ * the potential shutdown sequences will include a call to
+ * PXENV_UNLOAD_STACK at some point, so we may as well do it
+ * here.
+ */
+ cleanup();
+
+ if ( ! success ) {
+ unload_stack->Status = PXENV_STATUS_KEEP_ALL;
+ return PXENV_EXIT_FAILURE;
+ }
+
+ unload_stack->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_GET_CACHED_INFO
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_get_cached_info ( t_PXENV_GET_CACHED_INFO
+ *get_cached_info ) {
+ BOOTPLAYER *cached_info = &pxe_stack->cached_info;
+ DBG ( "PXENV_GET_CACHED_INFO %d", get_cached_info->PacketType );
+ ENSURE_READY ( get_cached_info );
+
+ /* Fill in cached_info structure in our pxe_stack */
+
+ /* I don't think there's actually any way we can be called in
+ * the middle of a DHCP request...
+ */
+ cached_info->opcode = BOOTP_REP;
+ /* We only have Ethernet drivers */
+ cached_info->Hardware = ETHER_TYPE;
+ cached_info->Hardlen = ETH_ALEN;
+ /* PXESPEC: "Client sets" says the spec, but who's filling in
+ * this structure? It ain't the client.
+ */
+ cached_info->Gatehops = 0;
+ cached_info->ident = 0;
+ cached_info->seconds = 0;
+ cached_info->Flags = BOOTP_BCAST;
+ /* PXESPEC: What do 'Client' and 'Your' IP address refer to? */
+ cached_info->cip = arptable[ARP_CLIENT].ipaddr.s_addr;
+ cached_info->yip = arptable[ARP_CLIENT].ipaddr.s_addr;
+ cached_info->sip = arptable[ARP_SERVER].ipaddr.s_addr;
+ /* PXESPEC: Does "GIP" mean "Gateway" or "Relay agent"? */
+ cached_info->gip = arptable[ARP_GATEWAY].ipaddr.s_addr;
+ memcpy ( cached_info->CAddr, arptable[ARP_CLIENT].node, ETH_ALEN );
+ /* Nullify server name */
+ cached_info->Sname[0] = '\0';
+ memcpy ( cached_info->bootfile, KERNEL_BUF,
+ sizeof(cached_info->bootfile) );
+ /* Copy DHCP vendor options */
+ memcpy ( &cached_info->vendor.d, BOOTP_DATA_ADDR->bootp_reply.bp_vend,
+ sizeof(cached_info->vendor.d) );
+
+ /* Copy to user-specified buffer, or set pointer to our buffer */
+ get_cached_info->BufferLimit = sizeof(*cached_info);
+ /* PXESPEC: says to test for Buffer == NULL *and* BufferSize =
+ * 0, but what are we supposed to do with a null buffer of
+ * non-zero size?!
+ */
+ if ( IS_NULL_SEGOFF16 ( get_cached_info->Buffer ) ) {
+ /* Point back to our buffer */
+ PTR_TO_SEGOFF16 ( cached_info, get_cached_info->Buffer );
+ get_cached_info->BufferSize = sizeof(*cached_info);
+ } else {
+ /* Copy to user buffer */
+ size_t size = sizeof(*cached_info);
+ void *buffer = SEGOFF16_TO_PTR ( get_cached_info->Buffer );
+ if ( get_cached_info->BufferSize < size )
+ size = get_cached_info->BufferSize;
+ DBG ( " to %x", virt_to_phys ( buffer ) );
+ memcpy ( buffer, cached_info, size );
+ /* PXESPEC: Should we return an error if the user
+ * buffer is too small? We do return the actual size
+ * of the buffer via BufferLimit, so the user does
+ * have a way to detect this already.
+ */
+ }
+
+ get_cached_info->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_RESTART_TFTP
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_restart_tftp ( t_PXENV_RESTART_TFTP *restart_tftp ) {
+ PXENV_EXIT_t tftp_exit;
+
+ DBG ( "PXENV_RESTART_TFTP" );
+ ENSURE_READY ( restart_tftp );
+
+ /* Words cannot describe the complete mismatch between the PXE
+ * specification and any possible version of reality...
+ */
+ restart_tftp->Buffer = PXE_LOAD_ADDRESS; /* Fixed by spec, apparently */
+ restart_tftp->BufferSize = get_free_base_memory() - PXE_LOAD_ADDRESS; /* Near enough */
+ DBG ( "(" );
+ tftp_exit = pxe_api_call ( PXENV_TFTP_READ_FILE, (t_PXENV_ANY*)restart_tftp );
+ DBG ( ")" );
+ if ( tftp_exit != PXENV_EXIT_SUCCESS ) return tftp_exit;
+
+ /* Fire up the new NBP */
+ restart_tftp->Status = xstartpxe();
+
+ /* Not sure what "SUCCESS" actually means, since we can only
+ * return if the new NBP failed to boot...
+ */
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_START_BASE
+ *
+ * Status: won't implement (requires major structural changes)
+ */
+PXENV_EXIT_t pxenv_start_base ( t_PXENV_START_BASE *start_base ) {
+ DBG ( "PXENV_START_BASE" );
+ /* ENSURE_READY ( start_base ); */
+ start_base->Status = PXENV_STATUS_UNSUPPORTED;
+ return PXENV_EXIT_FAILURE;
+}
+
+/* PXENV_STOP_BASE
+ *
+ * Status: working
+ */
+PXENV_EXIT_t pxenv_stop_base ( t_PXENV_STOP_BASE *stop_base ) {
+ DBG ( "PXENV_STOP_BASE" );
+
+ /* The only time we will be called is when the NBP is trying
+ * to shut down the PXE stack. There's nothing we need to do
+ * in this call.
+ */
+
+ stop_base->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* PXENV_UNDI_LOADER
+ *
+ * Status: working
+ *
+ * NOTE: This is not a genuine PXE API call; the loader has a separate
+ * entry point. However, to simplify the mapping of the PXE API to
+ * the internal Etherboot API, both are directed through the same
+ * interface.
+ */
+PXENV_EXIT_t pxenv_undi_loader ( undi_loader_t *loader ) {
+ uint32_t loader_phys = virt_to_phys ( loader );
+
+ DBG ( "PXENV_UNDI_LOADER" );
+
+ /* Set UNDI DS as our real-mode stack */
+ use_undi_ds_for_rm_stack ( loader->undi_ds );
+
+ /* FIXME: These lines are borrowed from main.c. There should
+ * probably be a single initialise() function that does all
+ * this, but it's currently split interestingly between main()
+ * and main_loop()...
+ */
+ console_init();
+ cpu_setup();
+ setup_timers();
+ gateA20_set();
+ print_config();
+ get_memsizes();
+ cleanup();
+ relocate();
+ cleanup();
+ console_init();
+ init_heap();
+
+ /* We have relocated; the loader pointer is now invalid */
+ loader = phys_to_virt ( loader_phys );
+
+ /* Install PXE stack to area specified by NBP */
+ install_pxe_stack ( VIRTUAL ( loader->undi_cs, 0 ) );
+
+ /* Call pxenv_start_undi to set parameters. Why the hell PXE
+ * requires these parameters to be provided twice is beyond
+ * the wit of any sane man. Don't worry if it fails; the NBP
+ * should call PXENV_START_UNDI separately anyway.
+ */
+ pxenv_start_undi ( &loader->start_undi );
+ /* Unhook stack; the loader is not meant to hook int 1a etc,
+ * but the call the pxenv_start_undi will cause it to happen.
+ */
+ ENSURE_CAN_UNLOAD ( loader );
+
+ /* Fill in addresses of !PXE and PXENV+ structures */
+ PTR_TO_SEGOFF16 ( &pxe_stack->pxe, loader->pxe_ptr );
+ PTR_TO_SEGOFF16 ( &pxe_stack->pxenv, loader->pxenv_ptr );
+
+ loader->Status = PXENV_STATUS_SUCCESS;
+ return PXENV_EXIT_SUCCESS;
+}
+
+/* API call dispatcher
+ *
+ * Status: complete
+ */
+PXENV_EXIT_t pxe_api_call ( int opcode, t_PXENV_ANY *params ) {
+ PXENV_EXIT_t ret = PXENV_EXIT_FAILURE;
+
+ /* Set default status in case child routine fails to do so */
+ params->Status = PXENV_STATUS_FAILURE;
+
+ DBG ( "[" );
+
+ /* Hand off to relevant API routine */
+ switch ( opcode ) {
+ case PXENV_START_UNDI:
+ ret = pxenv_start_undi ( &params->start_undi );
+ break;
+ case PXENV_UNDI_STARTUP:
+ ret = pxenv_undi_startup ( &params->undi_startup );
+ break;
+ case PXENV_UNDI_CLEANUP:
+ ret = pxenv_undi_cleanup ( &params->undi_cleanup );
+ break;
+ case PXENV_UNDI_INITIALIZE:
+ ret = pxenv_undi_initialize ( &params->undi_initialize );
+ break;
+ case PXENV_UNDI_RESET_ADAPTER:
+ ret = pxenv_undi_reset_adapter ( &params->undi_reset_adapter );
+ break;
+ case PXENV_UNDI_SHUTDOWN:
+ ret = pxenv_undi_shutdown ( &params->undi_shutdown );
+ break;
+ case PXENV_UNDI_OPEN:
+ ret = pxenv_undi_open ( &params->undi_open );
+ break;
+ case PXENV_UNDI_CLOSE:
+ ret = pxenv_undi_close ( &params->undi_close );
+ break;
+ case PXENV_UNDI_TRANSMIT:
+ ret = pxenv_undi_transmit ( &params->undi_transmit );
+ break;
+ case PXENV_UNDI_SET_MCAST_ADDRESS:
+ ret = pxenv_undi_set_mcast_address (
+ &params->undi_set_mcast_address );
+ break;
+ case PXENV_UNDI_SET_STATION_ADDRESS:
+ ret = pxenv_undi_set_station_address (
+ &params->undi_set_station_address );
+ break;
+ case PXENV_UNDI_SET_PACKET_FILTER:
+ ret = pxenv_undi_set_packet_filter (
+ &params->undi_set_packet_filter );
+ break;
+ case PXENV_UNDI_GET_INFORMATION:
+ ret = pxenv_undi_get_information (
+ &params->undi_get_information );
+ break;
+ case PXENV_UNDI_GET_STATISTICS:
+ ret = pxenv_undi_get_statistics (
+ &params->undi_get_statistics );
+ break;
+ case PXENV_UNDI_CLEAR_STATISTICS:
+ ret = pxenv_undi_clear_statistics (
+ &params->undi_clear_statistics );
+ break;
+ case PXENV_UNDI_INITIATE_DIAGS:
+ ret = pxenv_undi_initiate_diags (
+ &params->undi_initiate_diags );
+ break;
+ case PXENV_UNDI_FORCE_INTERRUPT:
+ ret = pxenv_undi_force_interrupt (
+ &params->undi_force_interrupt );
+ break;
+ case PXENV_UNDI_GET_MCAST_ADDRESS:
+ ret = pxenv_undi_get_mcast_address (
+ &params->undi_get_mcast_address );
+ break;
+ case PXENV_UNDI_GET_NIC_TYPE:
+ ret = pxenv_undi_get_nic_type ( &params->undi_get_nic_type );
+ break;
+ case PXENV_UNDI_GET_IFACE_INFO:
+ ret = pxenv_undi_get_iface_info (
+ &params->undi_get_iface_info );
+ break;
+ case PXENV_UNDI_ISR:
+ ret = pxenv_undi_isr ( &params->undi_isr );
+ break;
+ case PXENV_STOP_UNDI:
+ ret = pxenv_stop_undi ( &params->stop_undi );
+ break;
+ case PXENV_TFTP_OPEN:
+ ret = pxenv_tftp_open ( &params->tftp_open );
+ break;
+ case PXENV_TFTP_CLOSE:
+ ret = pxenv_tftp_close ( &params->tftp_close );
+ break;
+ case PXENV_TFTP_READ:
+ ret = pxenv_tftp_read ( &params->tftp_read );
+ break;
+ case PXENV_TFTP_READ_FILE:
+ ret = pxenv_tftp_read_file ( &params->tftp_read_file );
+ break;
+ case PXENV_TFTP_GET_FSIZE:
+ ret = pxenv_tftp_get_fsize ( &params->tftp_get_fsize );
+ break;
+ case PXENV_UDP_OPEN:
+ ret = pxenv_udp_open ( &params->udp_open );
+ break;
+ case PXENV_UDP_CLOSE:
+ ret = pxenv_udp_close ( &params->udp_close );
+ break;
+ case PXENV_UDP_READ:
+ ret = pxenv_udp_read ( &params->udp_read );
+ break;
+ case PXENV_UDP_WRITE:
+ ret = pxenv_udp_write ( &params->udp_write );
+ break;
+ case PXENV_UNLOAD_STACK:
+ ret = pxenv_unload_stack ( &params->unload_stack );
+ break;
+ case PXENV_GET_CACHED_INFO:
+ ret = pxenv_get_cached_info ( &params->get_cached_info );
+ break;
+ case PXENV_RESTART_TFTP:
+ ret = pxenv_restart_tftp ( &params->restart_tftp );
+ break;
+ case PXENV_START_BASE:
+ ret = pxenv_start_base ( &params->start_base );
+ break;
+ case PXENV_STOP_BASE:
+ ret = pxenv_stop_base ( &params->stop_base );
+ break;
+ case PXENV_UNDI_LOADER:
+ ret = pxenv_undi_loader ( &params->loader );
+ break;
+
+ default:
+ DBG ( "PXENV_UNKNOWN_%hx", opcode );
+ params->Status = PXENV_STATUS_UNSUPPORTED;
+ ret = PXENV_EXIT_FAILURE;
+ break;
+ }
+
+ if ( params->Status != PXENV_STATUS_SUCCESS ) {
+ DBG ( " %hx", params->Status );
+ }
+ if ( ret != PXENV_EXIT_SUCCESS ) {
+ DBG ( ret == PXENV_EXIT_FAILURE ? " err" : " ??" );
+ }
+ DBG ( "]" );
+
+ return ret;
+}
+
+#endif /* PXE_EXPORT */
diff --git a/src/core/relocate.c b/src/core/relocate.c
new file mode 100644
index 00000000..d20846a8
--- /dev/null
+++ b/src/core/relocate.c
@@ -0,0 +1,103 @@
+#ifndef NORELOCATE
+
+#include "etherboot.h"
+
+/* by Eric Biederman */
+
+/* On some platforms etherboot is compiled as a shared library, and we use
+ * the ELF pic support to make it relocateable. This works very nicely
+ * for code, but since no one has implemented PIC data yet pointer
+ * values in variables are a a problem. Global variables are a
+ * pain but the return addresses on the stack are the worst. On these
+ * platforms relocate_to will restart etherboot, to ensure the stack
+ * is reinitialize and hopefully get the global variables
+ * appropriately reinitialized as well.
+ *
+ */
+
+void relocate(void)
+{
+ unsigned long addr, eaddr, size;
+ unsigned i;
+ /* Walk through the memory map and find the highest address
+ * below 4GB that etherboot will fit into. Ensure etherboot
+ * lies entirely within a range with A20=0. This means that
+ * even if something screws up the state of the A20 line, the
+ * etherboot code is still visible and we have a chance to
+ * diagnose the problem.
+ */
+ /* First find the size of etherboot */
+ addr = virt_to_phys(_text);
+ eaddr = virt_to_phys(_end);
+ size = (eaddr - addr + 0xf) & ~0xf;
+
+ /* If the current etherboot is beyond MAX_ADDR pretend it is
+ * at the lowest possible address.
+ */
+ if (eaddr > MAX_ADDR) {
+ eaddr = 0;
+ }
+
+ for(i = 0; i < meminfo.map_count; i++) {
+ unsigned long r_start, r_end;
+ if (meminfo.map[i].type != E820_RAM) {
+ continue;
+ }
+ if (meminfo.map[i].addr > MAX_ADDR) {
+ continue;
+ }
+ if (meminfo.map[i].size > MAX_ADDR) {
+ continue;
+ }
+ r_start = meminfo.map[i].addr;
+ r_end = r_start + meminfo.map[i].size;
+ /* Make the addresses 16 byte (128 bit) aligned */
+ r_start = (r_start + 15) & ~15;
+ r_end = r_end & ~15;
+ if (r_end < r_start) {
+ r_end = MAX_ADDR;
+ }
+ if (r_end < size) {
+ /* Avoid overflow weirdness when r_end - size < 0 */
+ continue;
+ }
+ /* Shrink the range down to use only even megabytes
+ * (i.e. A20=0).
+ */
+ if ( r_end & 0x100000 ) {
+ /* If r_end is in an odd megabyte, round down
+ * r_end to the top of the next even megabyte.
+ */
+ r_end = r_end & ~0xfffff;
+ } else if ( ( r_end - size ) & 0x100000 ) {
+ /* If r_end is in an even megabyte, but the
+ * start of Etherboot would be in an odd
+ * megabyte, round down to the top of the next
+ * even megabyte.
+ */
+ r_end = ( r_end - 0x100000 ) & ~0xfffff;
+ }
+ /* If we have rounded down r_end below r_ start, skip
+ * this block.
+ */
+ if ( r_end < r_start ) {
+ continue;
+ }
+ if (eaddr < r_end - size) {
+ addr = r_end - size;
+ eaddr = r_end;
+ }
+ }
+ if (addr != virt_to_phys(_text)) {
+ unsigned long old_addr = virt_to_phys(_text);
+ printf("Relocating _text from: [%lx,%lx) to [%lx,%lx)\n",
+ old_addr, virt_to_phys(_end),
+ addr, eaddr);
+ arch_relocate_to(addr);
+ cleanup();
+ relocate_to(addr);
+ arch_relocated_from(old_addr);
+ }
+}
+
+#endif /* NORELOCATE */
diff --git a/src/core/serial.c b/src/core/serial.c
new file mode 100644
index 00000000..72207e8d
--- /dev/null
+++ b/src/core/serial.c
@@ -0,0 +1,236 @@
+#include "etherboot.h"
+#include "timer.h"
+#ifdef CONSOLE_SERIAL
+
+/*
+ * The serial port interface routines implement a simple polled i/o
+ * interface to a standard serial port. Due to the space restrictions
+ * for the boot blocks, no BIOS support is used (since BIOS requires
+ * expensive real/protected mode switches), instead the rudimentary
+ * BIOS support is duplicated here.
+ *
+ * The base address and speed for the i/o port are passed from the
+ * Makefile in the COMCONSOLE and CONSPEED preprocessor macros. The
+ * line control parameters are currently hard-coded to 8 bits, no
+ * parity, 1 stop bit (8N1). This can be changed in init_serial().
+ */
+
+static int found = 0;
+
+#if defined(COMCONSOLE)
+#undef UART_BASE
+#define UART_BASE COMCONSOLE
+#endif
+
+#ifndef UART_BASE
+#error UART_BASE not defined
+#endif
+
+#if defined(CONSPEED)
+#undef UART_BAUD
+#define UART_BAUD CONSPEED
+#endif
+
+#ifndef UART_BAUD
+#define UART_BAUD 115200
+#endif
+
+#if ((115200%UART_BAUD) != 0)
+#error Bad ttys0 baud rate
+#endif
+
+#define COMBRD (115200/UART_BAUD)
+
+/* Line Control Settings */
+#ifndef COMPARM
+/* Set 8bit, 1 stop bit, no parity */
+#define COMPARM 0x03
+#endif
+
+#define UART_LCS COMPARM
+
+/* Data */
+#define UART_RBR 0x00
+#define UART_TBR 0x00
+
+/* Control */
+#define UART_IER 0x01
+#define UART_IIR 0x02
+#define UART_FCR 0x02
+#define UART_LCR 0x03
+#define UART_MCR 0x04
+#define UART_DLL 0x00
+#define UART_DLM 0x01
+
+/* Status */
+#define UART_LSR 0x05
+#define UART_LSR_TEMPT 0x40 /* Transmitter empty */
+#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */
+#define UART_LSR_BI 0x10 /* Break interrupt indicator */
+#define UART_LSR_FE 0x08 /* Frame error indicator */
+#define UART_LSR_PE 0x04 /* Parity error indicator */
+#define UART_LSR_OE 0x02 /* Overrun error indicator */
+#define UART_LSR_DR 0x01 /* Receiver data ready */
+
+#define UART_MSR 0x06
+#define UART_SCR 0x07
+
+#if defined(UART_MEM)
+#define uart_readb(addr) readb((addr))
+#define uart_writeb(val,addr) writeb((val),(addr))
+#else
+#define uart_readb(addr) inb((addr))
+#define uart_writeb(val,addr) outb((val),(addr))
+#endif
+
+/*
+ * void serial_putc(int ch);
+ * Write character `ch' to port UART_BASE.
+ */
+void serial_putc(int ch)
+{
+ int i;
+ int status;
+ if (!found) {
+ /* no serial interface */
+ return;
+ }
+ i = 1000; /* timeout */
+ while(--i > 0) {
+ status = uart_readb(UART_BASE + UART_LSR);
+ if (status & UART_LSR_THRE) {
+ /* TX buffer emtpy */
+ uart_writeb(ch, UART_BASE + UART_TBR);
+ break;
+ }
+ mdelay(2);
+ }
+}
+
+/*
+ * int serial_getc(void);
+ * Read a character from port UART_BASE.
+ */
+int serial_getc(void)
+{
+ int status;
+ int ch;
+ do {
+ status = uart_readb(UART_BASE + UART_LSR);
+ } while((status & 1) == 0);
+ ch = uart_readb(UART_BASE + UART_RBR); /* fetch (first) character */
+ ch &= 0x7f; /* remove any parity bits we get */
+ if (ch == 0x7f) { /* Make DEL... look like BS */
+ ch = 0x08;
+ }
+ return ch;
+}
+
+/*
+ * int serial_ischar(void);
+ * If there is a character in the input buffer of port UART_BASE,
+ * return nonzero; otherwise return 0.
+ */
+int serial_ischar(void)
+{
+ int status;
+ if (!found)
+ return 0;
+ status = uart_readb(UART_BASE + UART_LSR); /* line status reg; */
+ return status & 1; /* rx char available */
+}
+
+/*
+ * int serial_init(void);
+ * Initialize port UART_BASE to speed CONSPEED, line settings 8N1.
+ */
+int serial_init(void)
+{
+ int initialized = 0;
+ int status;
+ int divisor, lcs;
+
+ if (found)
+ return 1;
+
+ divisor = COMBRD;
+ lcs = UART_LCS;
+
+
+#ifdef COMPRESERVE
+ lcs = uart_readb(UART_BASE + UART_LCR) & 0x7f;
+ uart_writeb(0x80 | lcs, UART_BASE + UART_LCR);
+ divisor = (uart_readb(UART_BASE + UART_DLM) << 8) | uart_readb(UART_BASE + UART_DLL);
+ uart_writeb(lcs, UART_BASE + UART_LCR);
+#endif
+
+ /* Set Baud Rate Divisor to CONSPEED, and test to see if the
+ * serial port appears to be present.
+ */
+ uart_writeb(0x80 | lcs, UART_BASE + UART_LCR);
+ uart_writeb(0xaa, UART_BASE + UART_DLL);
+ if (uart_readb(UART_BASE + UART_DLL) != 0xaa)
+ goto out;
+ uart_writeb(0x55, UART_BASE + UART_DLL);
+ if (uart_readb(UART_BASE + UART_DLL) != 0x55)
+ goto out;
+ uart_writeb(divisor & 0xff, UART_BASE + UART_DLL);
+ if (uart_readb(UART_BASE + UART_DLL) != (divisor & 0xff))
+ goto out;
+ uart_writeb(0xaa, UART_BASE + UART_DLM);
+ if (uart_readb(UART_BASE + UART_DLM) != 0xaa)
+ goto out;
+ uart_writeb(0x55, UART_BASE + UART_DLM);
+ if (uart_readb(UART_BASE + UART_DLM) != 0x55)
+ goto out;
+ uart_writeb((divisor >> 8) & 0xff, UART_BASE + UART_DLM);
+ if (uart_readb(UART_BASE + UART_DLM) != ((divisor >> 8) & 0xff))
+ goto out;
+ uart_writeb(lcs, UART_BASE + UART_LCR);
+
+ /* disable interrupts */
+ uart_writeb(0x0, UART_BASE + UART_IER);
+
+ /* disable fifo's */
+ uart_writeb(0x00, UART_BASE + UART_FCR);
+
+ /* Set clear to send, so flow control works... */
+ uart_writeb((1<<1), UART_BASE + UART_MCR);
+
+
+ /* Flush the input buffer. */
+ do {
+ /* rx buffer reg
+ * throw away (unconditionally the first time)
+ */
+ uart_readb(UART_BASE + UART_RBR);
+ /* line status reg */
+ status = uart_readb(UART_BASE + UART_LSR);
+ } while(status & UART_LSR_DR);
+ initialized = 1;
+ out:
+ found = initialized;
+ return initialized;
+}
+
+/*
+ * void serial_fini(void);
+ * Cleanup our use of the serial port, in particular flush the
+ * output buffer so we don't accidentially loose characters.
+ */
+void serial_fini(void)
+{
+ int i, status;
+ if (!found) {
+ /* no serial interface */
+ return;
+ }
+ /* Flush the output buffer to avoid dropping characters,
+ * if we are reinitializing the serial port.
+ */
+ i = 10000; /* timeout */
+ do {
+ status = uart_readb(UART_BASE + UART_LSR);
+ } while((--i > 0) && !(status & UART_LSR_TEMPT));
+}
+#endif
diff --git a/src/core/string.c b/src/core/string.c
new file mode 100644
index 00000000..856f955b
--- /dev/null
+++ b/src/core/string.c
@@ -0,0 +1,540 @@
+/*
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * Copyright (C) 2004 Tobias Lorenz
+ *
+ * string handling functions
+ * based on linux/lib/string.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*
+ * stupid library routines.. The optimized versions should generally be found
+ * as inline code in <asm-xx/string.h>
+ *
+ * These are buggy as well..
+ *
+ * * Fri Jun 25 1999, Ingo Oeser <ioe@informatik.tu-chemnitz.de>
+ * - Added strsep() which will replace strtok() soon (because strsep() is
+ * reentrant and should be faster). Use only strsep() in new code, please.
+ */
+
+#include "etherboot.h"
+
+
+/* *** FROM string.c *** */
+
+#ifndef __HAVE_ARCH_STRNICMP
+/**
+ * strnicmp - Case insensitive, length-limited string comparison
+ * @s1: One string
+ * @s2: The other string
+ * @len: the maximum number of characters to compare
+ */
+int strnicmp(const char *s1, const char *s2, size_t len)
+{
+ /* Yes, Virginia, it had better be unsigned */
+ unsigned char c1, c2;
+
+ c1 = 0; c2 = 0;
+ if (len) {
+ do {
+ c1 = *s1; c2 = *s2;
+ s1++; s2++;
+ if (!c1)
+ break;
+ if (!c2)
+ break;
+ if (c1 == c2)
+ continue;
+ c1 = tolower(c1);
+ c2 = tolower(c2);
+ if (c1 != c2)
+ break;
+ } while (--len);
+ }
+ return (int)c1 - (int)c2;
+}
+#endif
+
+char * ___strtok;
+
+#ifndef __HAVE_ARCH_STRCPY
+/**
+ * strcpy - Copy a %NUL terminated string
+ * @dest: Where to copy the string to
+ * @src: Where to copy the string from
+ */
+char * strcpy(char * dest,const char *src)
+{
+ char *tmp = dest;
+
+ while ((*dest++ = *src++) != '\0')
+ /* nothing */;
+ return tmp;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRNCPY
+/**
+ * strncpy - Copy a length-limited, %NUL-terminated string
+ * @dest: Where to copy the string to
+ * @src: Where to copy the string from
+ * @count: The maximum number of bytes to copy
+ *
+ * Note that unlike userspace strncpy, this does not %NUL-pad the buffer.
+ * However, the result is not %NUL-terminated if the source exceeds
+ * @count bytes.
+ */
+char * strncpy(char * dest,const char *src,size_t count)
+{
+ char *tmp = dest;
+
+ while (count-- && (*dest++ = *src++) != '\0')
+ /* nothing */;
+
+ return tmp;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRCAT
+/**
+ * strcat - Append one %NUL-terminated string to another
+ * @dest: The string to be appended to
+ * @src: The string to append to it
+ */
+char * strcat(char * dest, const char * src)
+{
+ char *tmp = dest;
+
+ while (*dest)
+ dest++;
+ while ((*dest++ = *src++) != '\0')
+ ;
+
+ return tmp;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRNCAT
+/**
+ * strncat - Append a length-limited, %NUL-terminated string to another
+ * @dest: The string to be appended to
+ * @src: The string to append to it
+ * @count: The maximum numbers of bytes to copy
+ *
+ * Note that in contrast to strncpy, strncat ensures the result is
+ * terminated.
+ */
+char * strncat(char *dest, const char *src, size_t count)
+{
+ char *tmp = dest;
+
+ if (count) {
+ while (*dest)
+ dest++;
+ while ((*dest++ = *src++)) {
+ if (--count == 0) {
+ *dest = '\0';
+ break;
+ }
+ }
+ }
+
+ return tmp;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRCMP
+/**
+ * strcmp - Compare two strings
+ * @cs: One string
+ * @ct: Another string
+ */
+int strcmp(const char * cs,const char * ct)
+{
+ register signed char __res;
+
+ while (1) {
+ if ((__res = *cs - *ct++) != 0 || !*cs++)
+ break;
+ }
+
+ return __res;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRNCMP
+/**
+ * strncmp - Compare two length-limited strings
+ * @cs: One string
+ * @ct: Another string
+ * @count: The maximum number of bytes to compare
+ */
+int strncmp(const char * cs,const char * ct,size_t count)
+{
+ register signed char __res = 0;
+
+ while (count) {
+ if ((__res = *cs - *ct++) != 0 || !*cs++)
+ break;
+ count--;
+ }
+
+ return __res;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRCHR
+/**
+ * strchr - Find the first occurrence of a character in a string
+ * @s: The string to be searched
+ * @c: The character to search for
+ */
+char * strchr(const char * s, int c)
+{
+ for(; *s != (char) c; ++s)
+ if (*s == '\0')
+ return NULL;
+ return (char *) s;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRRCHR
+/**
+ * strrchr - Find the last occurrence of a character in a string
+ * @s: The string to be searched
+ * @c: The character to search for
+ */
+char * strrchr(const char * s, int c)
+{
+ const char *p = s + strlen(s);
+ do {
+ if (*p == (char)c)
+ return (char *)p;
+ } while (--p >= s);
+ return NULL;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRLEN
+/**
+ * strlen - Find the length of a string
+ * @s: The string to be sized
+ */
+size_t strlen(const char * s)
+{
+ const char *sc;
+
+ for (sc = s; *sc != '\0'; ++sc)
+ /* nothing */;
+ return sc - s;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRNLEN
+/**
+ * strnlen - Find the length of a length-limited string
+ * @s: The string to be sized
+ * @count: The maximum number of bytes to search
+ */
+size_t strnlen(const char * s, size_t count)
+{
+ const char *sc;
+
+ for (sc = s; count-- && *sc != '\0'; ++sc)
+ /* nothing */;
+ return sc - s;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRSPN
+/**
+ * strspn - Calculate the length of the initial substring of @s which only
+ * contain letters in @accept
+ * @s: The string to be searched
+ * @accept: The string to search for
+ */
+size_t strspn(const char *s, const char *accept)
+{
+ const char *p;
+ const char *a;
+ size_t count = 0;
+
+ for (p = s; *p != '\0'; ++p) {
+ for (a = accept; *a != '\0'; ++a) {
+ if (*p == *a)
+ break;
+ }
+ if (*a == '\0')
+ return count;
+ ++count;
+ }
+
+ return count;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRPBRK
+/**
+ * strpbrk - Find the first occurrence of a set of characters
+ * @cs: The string to be searched
+ * @ct: The characters to search for
+ */
+char * strpbrk(const char * cs,const char * ct)
+{
+ const char *sc1,*sc2;
+
+ for( sc1 = cs; *sc1 != '\0'; ++sc1) {
+ for( sc2 = ct; *sc2 != '\0'; ++sc2) {
+ if (*sc1 == *sc2)
+ return (char *) sc1;
+ }
+ }
+ return NULL;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRTOK
+/**
+ * strtok - Split a string into tokens
+ * @s: The string to be searched
+ * @ct: The characters to search for
+ *
+ * WARNING: strtok is deprecated, use strsep instead.
+ */
+char * strtok(char * s,const char * ct)
+{
+ char *sbegin, *send;
+
+ sbegin = s ? s : ___strtok;
+ if (!sbegin) {
+ return NULL;
+ }
+ sbegin += strspn(sbegin,ct);
+ if (*sbegin == '\0') {
+ ___strtok = NULL;
+ return( NULL );
+ }
+ send = strpbrk( sbegin, ct);
+ if (send && *send != '\0')
+ *send++ = '\0';
+ ___strtok = send;
+ return (sbegin);
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRSEP
+/**
+ * strsep - Split a string into tokens
+ * @s: The string to be searched
+ * @ct: The characters to search for
+ *
+ * strsep() updates @s to point after the token, ready for the next call.
+ *
+ * It returns empty tokens, too, behaving exactly like the libc function
+ * of that name. In fact, it was stolen from glibc2 and de-fancy-fied.
+ * Same semantics, slimmer shape. ;)
+ */
+char * strsep(char **s, const char *ct)
+{
+ char *sbegin = *s, *end;
+
+ if (sbegin == NULL)
+ return NULL;
+
+ end = strpbrk(sbegin, ct);
+ if (end)
+ *end++ = '\0';
+ *s = end;
+
+ return sbegin;
+}
+#endif
+
+#ifndef __HAVE_ARCH_MEMSET
+/**
+ * memset - Fill a region of memory with the given value
+ * @s: Pointer to the start of the area.
+ * @c: The byte to fill the area with
+ * @count: The size of the area.
+ *
+ * Do not use memset() to access IO space, use memset_io() instead.
+ */
+void * memset(void * s,int c,size_t count)
+{
+ char *xs = (char *) s;
+
+ while (count--)
+ *xs++ = c;
+
+ return s;
+}
+#endif
+
+#ifndef __HAVE_ARCH_BCOPY
+/**
+ * bcopy - Copy one area of memory to another
+ * @src: Where to copy from
+ * @dest: Where to copy to
+ * @count: The size of the area.
+ *
+ * Note that this is the same as memcpy(), with the arguments reversed.
+ * memcpy() is the standard, bcopy() is a legacy BSD function.
+ *
+ * You should not use this function to access IO space, use memcpy_toio()
+ * or memcpy_fromio() instead.
+ */
+char * bcopy(const char * src, char * dest, int count)
+{
+ char *tmp = dest;
+
+ while (count--)
+ *tmp++ = *src++;
+
+ return dest;
+}
+#endif
+
+#ifndef __HAVE_ARCH_MEMCPY
+/**
+ * memcpy - Copy one area of memory to another
+ * @dest: Where to copy to
+ * @src: Where to copy from
+ * @count: The size of the area.
+ *
+ * You should not use this function to access IO space, use memcpy_toio()
+ * or memcpy_fromio() instead.
+ */
+void * memcpy(void * dest,const void *src,size_t count)
+{
+ char *tmp = (char *) dest, *s = (char *) src;
+
+ while (count--)
+ *tmp++ = *s++;
+
+ return dest;
+}
+#endif
+
+#ifndef __HAVE_ARCH_MEMMOVE
+/**
+ * memmove - Copy one area of memory to another
+ * @dest: Where to copy to
+ * @src: Where to copy from
+ * @count: The size of the area.
+ *
+ * Unlike memcpy(), memmove() copes with overlapping areas.
+ */
+void * memmove(void * dest,const void *src,size_t count)
+{
+ char *tmp, *s;
+
+ if (dest <= src) {
+ tmp = (char *) dest;
+ s = (char *) src;
+ while (count--)
+ *tmp++ = *s++;
+ }
+ else {
+ tmp = (char *) dest + count;
+ s = (char *) src + count;
+ while (count--)
+ *--tmp = *--s;
+ }
+
+ return dest;
+}
+#endif
+
+#ifndef __HAVE_ARCH_MEMCMP
+/**
+ * memcmp - Compare two areas of memory
+ * @cs: One area of memory
+ * @ct: Another area of memory
+ * @count: The size of the area.
+ */
+int memcmp(const void * cs,const void * ct,size_t count)
+{
+ const unsigned char *su1, *su2;
+ int res = 0;
+
+ for( su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--)
+ if ((res = *su1 - *su2) != 0)
+ break;
+ return res;
+}
+#endif
+
+#ifndef __HAVE_ARCH_MEMSCAN
+/**
+ * memscan - Find a character in an area of memory.
+ * @addr: The memory area
+ * @c: The byte to search for
+ * @size: The size of the area.
+ *
+ * returns the address of the first occurrence of @c, or 1 byte past
+ * the area if @c is not found
+ */
+void * memscan(void * addr, int c, size_t size)
+{
+ unsigned char * p = (unsigned char *) addr;
+
+ while (size) {
+ if (*p == c)
+ return (void *) p;
+ p++;
+ size--;
+ }
+ return (void *) p;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRSTR
+/**
+ * strstr - Find the first substring in a %NUL terminated string
+ * @s1: The string to be searched
+ * @s2: The string to search for
+ */
+char * strstr(const char * s1,const char * s2)
+{
+ int l1, l2;
+
+ l2 = strlen(s2);
+ if (!l2)
+ return (char *) s1;
+ l1 = strlen(s1);
+ while (l1 >= l2) {
+ l1--;
+ if (!memcmp(s1,s2,l2))
+ return (char *) s1;
+ s1++;
+ }
+ return NULL;
+}
+#endif
+
+#ifndef __HAVE_ARCH_MEMCHR
+/**
+ * memchr - Find a character in an area of memory.
+ * @s: The memory area
+ * @c: The byte to search for
+ * @n: The size of the area.
+ *
+ * returns the address of the first occurrence of @c, or %NULL
+ * if @c is not found
+ */
+void * memchr(const void *s, int c, size_t n)
+{
+ const unsigned char *p = s;
+ while (n-- != 0) {
+ if ((unsigned char)c == *p++) {
+ return (void *)(p-1);
+ }
+ }
+ return NULL;
+}
+
+#endif
diff --git a/src/core/timer.c b/src/core/timer.c
new file mode 100644
index 00000000..d4d38ad5
--- /dev/null
+++ b/src/core/timer.c
@@ -0,0 +1,30 @@
+/* A couple of routines to implement a low-overhead timer for drivers */
+
+ /*
+ * 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.
+ */
+
+#include "etherboot.h"
+#include "timer.h"
+
+/* Machine Independant timer helper functions */
+
+void mdelay(unsigned int msecs)
+{
+ unsigned int i;
+ for(i = 0; i < msecs; i++) {
+ udelay(1000);
+ poll_interruptions();
+ }
+}
+
+void waiton_timer2(unsigned int ticks)
+{
+ load_timer2(ticks);
+ while(timer2_running()) {
+ poll_interruptions();
+ }
+}
diff --git a/src/core/vsprintf.c b/src/core/vsprintf.c
new file mode 100644
index 00000000..b22ae310
--- /dev/null
+++ b/src/core/vsprintf.c
@@ -0,0 +1,166 @@
+#include "etherboot.h"
+#include <stdarg.h>
+
+#define LONG_SHIFT ((int)((sizeof(unsigned long)*CHAR_BIT) - 4))
+#define INT_SHIFT ((int)((sizeof(unsigned int)*CHAR_BIT) - 4))
+#define SHRT_SHIFT ((int)((sizeof(unsigned short)*CHAR_BIT) - 4))
+#define CHAR_SHIFT ((int)((sizeof(unsigned char)*CHAR_BIT) - 4))
+
+/**************************************************************************
+PRINTF and friends
+
+ Formats:
+ %[#]x - 4 bytes int (8 hex digits, lower case)
+ %[#]X - 4 bytes int (8 hex digits, upper case)
+ %[#]lx - 8 bytes long (16 hex digits, lower case)
+ %[#]lX - 8 bytes long (16 hex digits, upper case)
+ %[#]hx - 2 bytes int (4 hex digits, lower case)
+ %[#]hX - 2 bytes int (4 hex digits, upper case)
+ %[#]hhx - 1 byte int (2 hex digits, lower case)
+ %[#]hhX - 1 byte int (2 hex digits, upper case)
+ - optional # prefixes 0x or 0X
+ %d - decimal int
+ %c - char
+ %s - string
+ %@ - Internet address in ddd.ddd.ddd.ddd notation
+ %! - Ethernet address in xx:xx:xx:xx:xx:xx notation
+ Note: width specification ignored
+**************************************************************************/
+static int vsprintf(char *buf, const char *fmt, va_list args)
+{
+ char *p, *s;
+ s = buf;
+ for ( ; *fmt != '\0'; ++fmt) {
+ if (*fmt != '%') {
+ buf ? *s++ = *fmt : putchar(*fmt);
+ continue;
+ }
+ /* skip width specs */
+ fmt++;
+ while (*fmt >= '0' && *fmt <= '9')
+ fmt++;
+ if (*fmt == '.')
+ fmt++;
+ while (*fmt >= '0' && *fmt <= '9')
+ fmt++;
+ if (*fmt == 's') {
+ for(p = va_arg(args, char *); *p != '\0'; p++)
+ buf ? *s++ = *p : putchar(*p);
+ }
+ else { /* Length of item is bounded */
+ char tmp[40], *q = tmp;
+ int alt = 0;
+ int shift = INT_SHIFT;
+ if (*fmt == '#') {
+ alt = 1;
+ fmt++;
+ }
+ if (*fmt == 'l') {
+ shift = LONG_SHIFT;
+ fmt++;
+ }
+ else if (*fmt == 'h') {
+ shift = SHRT_SHIFT;
+ fmt++;
+ if (*fmt == 'h') {
+ shift = CHAR_SHIFT;
+ fmt++;
+ }
+ }
+
+ /*
+ * Before each format q points to tmp buffer
+ * After each format q points past end of item
+ */
+ if ((*fmt | 0x20) == 'x') {
+ /* With x86 gcc, sizeof(long) == sizeof(int) */
+ unsigned long h;
+ int ncase;
+ if (shift > INT_SHIFT) {
+ h = va_arg(args, unsigned long);
+ } else {
+ h = va_arg(args, unsigned int);
+ }
+ ncase = (*fmt & 0x20);
+ if (alt) {
+ *q++ = '0';
+ *q++ = 'X' | ncase;
+ }
+ for ( ; shift >= 0; shift -= 4)
+ *q++ = "0123456789ABCDEF"[(h >> shift) & 0xF] | ncase;
+ }
+ else if (*fmt == 'd') {
+ char *r;
+ long i;
+ if (shift > INT_SHIFT) {
+ i = va_arg(args, long);
+ } else {
+ i = va_arg(args, int);
+ }
+ if (i < 0) {
+ *q++ = '-';
+ i = -i;
+ }
+ p = q; /* save beginning of digits */
+ do {
+ *q++ = '0' + (i % 10);
+ i /= 10;
+ } while (i);
+ /* reverse digits, stop in middle */
+ r = q; /* don't alter q */
+ while (--r > p) {
+ i = *r;
+ *r = *p;
+ *p++ = i;
+ }
+ }
+ else if (*fmt == '@') {
+ unsigned char *r;
+ union {
+ uint32_t l;
+ unsigned char c[4];
+ } u;
+ u.l = va_arg(args, uint32_t);
+ for (r = &u.c[0]; r < &u.c[4]; ++r)
+ q += sprintf(q, "%d.", *r);
+ --q;
+ }
+ else if (*fmt == '!') {
+ char *r;
+ p = va_arg(args, char *);
+ for (r = p + ETH_ALEN; p < r; ++p)
+ q += sprintf(q, "%hhX:", *p);
+ --q;
+ }
+ else if (*fmt == 'c')
+ *q++ = va_arg(args, int);
+ else
+ *q++ = *fmt;
+ /* now output the saved string */
+ for (p = tmp; p < q; ++p)
+ buf ? *s++ = *p : putchar(*p);
+ }
+ }
+ if (buf)
+ *s = '\0';
+ return (s - buf);
+}
+
+int sprintf(char *buf, const char *fmt, ...)
+{
+ va_list args;
+ int i;
+ va_start(args, fmt);
+ i=vsprintf(buf, fmt, args);
+ va_end(args);
+ return i;
+}
+
+void printf(const char *fmt, ...)
+{
+ va_list args;
+ int i;
+ va_start(args, fmt);
+ i=vsprintf(0, fmt, args);
+ va_end(args);
+}