diff options
author | Michael Brown | 2005-03-08 19:53:11 +0100 |
---|---|---|
committer | Michael Brown | 2005-03-08 19:53:11 +0100 |
commit | 3d6123e69ab879c72ff489afc5bf93ef0b7a94ce (patch) | |
tree | 9f3277569153a550fa8d81ebd61bd88f266eb8da /src/core | |
download | ipxe-3d6123e69ab879c72ff489afc5bf93ef0b7a94ce.tar.gz ipxe-3d6123e69ab879c72ff489afc5bf93ef0b7a94ce.tar.xz ipxe-3d6123e69ab879c72ff489afc5bf93ef0b7a94ce.zip |
Initial revision
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/btext.c | 5196 | ||||
-rw-r--r-- | src/core/config.c | 161 | ||||
-rw-r--r-- | src/core/disk.c | 283 | ||||
-rw-r--r-- | src/core/dns_resolver.c | 419 | ||||
-rw-r--r-- | src/core/elf_loader.c | 658 | ||||
-rw-r--r-- | src/core/heap.c | 168 | ||||
-rw-r--r-- | src/core/i82365.c | 656 | ||||
-rw-r--r-- | src/core/isa_probe.c | 66 | ||||
-rw-r--r-- | src/core/isapnp.c | 382 | ||||
-rw-r--r-- | src/core/main.c | 529 | ||||
-rw-r--r-- | src/core/misc.c | 415 | ||||
-rw-r--r-- | src/core/nfs.c | 610 | ||||
-rw-r--r-- | src/core/nic.c | 1778 | ||||
-rw-r--r-- | src/core/osloader.c | 365 | ||||
-rw-r--r-- | src/core/pc_kbd.c | 108 | ||||
-rw-r--r-- | src/core/pci.c | 337 | ||||
-rw-r--r-- | src/core/pci_probe.c | 70 | ||||
-rw-r--r-- | src/core/pcmcia.c | 269 | ||||
-rw-r--r-- | src/core/proto_eth_slow.c | 407 | ||||
-rw-r--r-- | src/core/proto_http.c | 206 | ||||
-rw-r--r-- | src/core/proto_slam.c | 541 | ||||
-rw-r--r-- | src/core/proto_tftm.c | 491 | ||||
-rw-r--r-- | src/core/pxe_export.c | 1424 | ||||
-rw-r--r-- | src/core/relocate.c | 103 | ||||
-rw-r--r-- | src/core/serial.c | 236 | ||||
-rw-r--r-- | src/core/string.c | 540 | ||||
-rw-r--r-- | src/core/timer.c | 30 | ||||
-rw-r--r-- | src/core/vsprintf.c | 166 |
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 ( ¶ms->start_undi ); + break; + case PXENV_UNDI_STARTUP: + ret = pxenv_undi_startup ( ¶ms->undi_startup ); + break; + case PXENV_UNDI_CLEANUP: + ret = pxenv_undi_cleanup ( ¶ms->undi_cleanup ); + break; + case PXENV_UNDI_INITIALIZE: + ret = pxenv_undi_initialize ( ¶ms->undi_initialize ); + break; + case PXENV_UNDI_RESET_ADAPTER: + ret = pxenv_undi_reset_adapter ( ¶ms->undi_reset_adapter ); + break; + case PXENV_UNDI_SHUTDOWN: + ret = pxenv_undi_shutdown ( ¶ms->undi_shutdown ); + break; + case PXENV_UNDI_OPEN: + ret = pxenv_undi_open ( ¶ms->undi_open ); + break; + case PXENV_UNDI_CLOSE: + ret = pxenv_undi_close ( ¶ms->undi_close ); + break; + case PXENV_UNDI_TRANSMIT: + ret = pxenv_undi_transmit ( ¶ms->undi_transmit ); + break; + case PXENV_UNDI_SET_MCAST_ADDRESS: + ret = pxenv_undi_set_mcast_address ( + ¶ms->undi_set_mcast_address ); + break; + case PXENV_UNDI_SET_STATION_ADDRESS: + ret = pxenv_undi_set_station_address ( + ¶ms->undi_set_station_address ); + break; + case PXENV_UNDI_SET_PACKET_FILTER: + ret = pxenv_undi_set_packet_filter ( + ¶ms->undi_set_packet_filter ); + break; + case PXENV_UNDI_GET_INFORMATION: + ret = pxenv_undi_get_information ( + ¶ms->undi_get_information ); + break; + case PXENV_UNDI_GET_STATISTICS: + ret = pxenv_undi_get_statistics ( + ¶ms->undi_get_statistics ); + break; + case PXENV_UNDI_CLEAR_STATISTICS: + ret = pxenv_undi_clear_statistics ( + ¶ms->undi_clear_statistics ); + break; + case PXENV_UNDI_INITIATE_DIAGS: + ret = pxenv_undi_initiate_diags ( + ¶ms->undi_initiate_diags ); + break; + case PXENV_UNDI_FORCE_INTERRUPT: + ret = pxenv_undi_force_interrupt ( + ¶ms->undi_force_interrupt ); + break; + case PXENV_UNDI_GET_MCAST_ADDRESS: + ret = pxenv_undi_get_mcast_address ( + ¶ms->undi_get_mcast_address ); + break; + case PXENV_UNDI_GET_NIC_TYPE: + ret = pxenv_undi_get_nic_type ( ¶ms->undi_get_nic_type ); + break; + case PXENV_UNDI_GET_IFACE_INFO: + ret = pxenv_undi_get_iface_info ( + ¶ms->undi_get_iface_info ); + break; + case PXENV_UNDI_ISR: + ret = pxenv_undi_isr ( ¶ms->undi_isr ); + break; + case PXENV_STOP_UNDI: + ret = pxenv_stop_undi ( ¶ms->stop_undi ); + break; + case PXENV_TFTP_OPEN: + ret = pxenv_tftp_open ( ¶ms->tftp_open ); + break; + case PXENV_TFTP_CLOSE: + ret = pxenv_tftp_close ( ¶ms->tftp_close ); + break; + case PXENV_TFTP_READ: + ret = pxenv_tftp_read ( ¶ms->tftp_read ); + break; + case PXENV_TFTP_READ_FILE: + ret = pxenv_tftp_read_file ( ¶ms->tftp_read_file ); + break; + case PXENV_TFTP_GET_FSIZE: + ret = pxenv_tftp_get_fsize ( ¶ms->tftp_get_fsize ); + break; + case PXENV_UDP_OPEN: + ret = pxenv_udp_open ( ¶ms->udp_open ); + break; + case PXENV_UDP_CLOSE: + ret = pxenv_udp_close ( ¶ms->udp_close ); + break; + case PXENV_UDP_READ: + ret = pxenv_udp_read ( ¶ms->udp_read ); + break; + case PXENV_UDP_WRITE: + ret = pxenv_udp_write ( ¶ms->udp_write ); + break; + case PXENV_UNLOAD_STACK: + ret = pxenv_unload_stack ( ¶ms->unload_stack ); + break; + case PXENV_GET_CACHED_INFO: + ret = pxenv_get_cached_info ( ¶ms->get_cached_info ); + break; + case PXENV_RESTART_TFTP: + ret = pxenv_restart_tftp ( ¶ms->restart_tftp ); + break; + case PXENV_START_BASE: + ret = pxenv_start_base ( ¶ms->start_base ); + break; + case PXENV_STOP_BASE: + ret = pxenv_stop_base ( ¶ms->stop_base ); + break; + case PXENV_UNDI_LOADER: + ret = pxenv_undi_loader ( ¶ms->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); +} |