diff options
Diffstat (limited to 'gpxe/src/core')
52 files changed, 15013 insertions, 0 deletions
diff --git a/gpxe/src/core/abft.c b/gpxe/src/core/abft.c new file mode 100644 index 00000000..af28bbcf --- /dev/null +++ b/gpxe/src/core/abft.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2007 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. + */ + +#include <realmode.h> +#include <gpxe/aoe.h> +#include <gpxe/netdevice.h> +#include <gpxe/abft.h> + +/** @file + * + * AoE Boot Firmware Table + * + */ + +#define abftab __use_data16 ( abftab ) +/** The aBFT used by gPXE */ +struct abft_table __data16 ( abftab ) __attribute__ (( aligned ( 16 ) )) = { + /* ACPI header */ + .acpi = { + .signature = ABFT_SIG, + .length = sizeof ( abftab ), + .revision = 1, + .oem_id = "FENSYS", + .oem_table_id = "gPXE", + }, +}; + +/** + * Fill in all variable portions of aBFT + * + * @v aoe AoE session + */ +void abft_fill_data ( struct aoe_session *aoe ) { + + /* Fill in boot parameters */ + abftab.shelf = aoe->major; + abftab.slot = aoe->minor; + memcpy ( abftab.mac, aoe->netdev->ll_addr, sizeof ( abftab.mac ) ); + + /* Update checksum */ + acpi_fix_checksum ( &abftab.acpi ); + + DBG ( "AoE boot firmware table:\n" ); + DBG_HD ( &abftab, sizeof ( abftab ) ); +} diff --git a/gpxe/src/core/acpi.c b/gpxe/src/core/acpi.c new file mode 100644 index 00000000..94b7b2a1 --- /dev/null +++ b/gpxe/src/core/acpi.c @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2006 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. + */ + +#include <gpxe/acpi.h> + +/** @file + * + * ACPI support functions + * + */ + +/** + * Fix up ACPI table checksum + * + * @v acpi ACPI table header + */ +void acpi_fix_checksum ( struct acpi_description_header *acpi ) { + unsigned int i = 0; + uint8_t sum = 0; + + for ( i = 0 ; i < acpi->length ; i++ ) { + sum += *( ( ( uint8_t * ) acpi ) + i ); + } + acpi->checksum -= sum; +} diff --git a/gpxe/src/core/ansiesc.c b/gpxe/src/core/ansiesc.c new file mode 100644 index 00000000..6b820ada --- /dev/null +++ b/gpxe/src/core/ansiesc.c @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2006 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. + */ + +#include <string.h> +#include <assert.h> +#include <gpxe/ansiesc.h> + +/** @file + * + * ANSI escape sequences + * + */ + +/** + * Call ANSI escape sequence handler + * + * @v handlers List of escape sequence handlers + * @v function Control function identifier + * @v count Parameter count + * @v params Parameter list + */ +static void ansiesc_call_handler ( struct ansiesc_handler *handlers, + unsigned int function, int count, + int params[] ) { + struct ansiesc_handler *handler; + + for ( handler = handlers ; handler->function ; handler++ ) { + if ( handler->function == function ) { + handler->handle ( count, params ); + break; + } + } +} + +/** + * Process character that may be part of ANSI escape sequence + * + * @v ctx ANSI escape sequence context + * @v c Character + * @ret c Original character if not part of escape sequence + * @ret <0 Character was part of escape sequence + * + * ANSI escape sequences will be plucked out of the character stream + * and interpreted; once complete they will be passed to the + * appropriate handler if one exists in this ANSI escape sequence + * context. + * + * In the interests of code size, we are rather liberal about the + * sequences we are prepared to accept as valid. + */ +int ansiesc_process ( struct ansiesc_context *ctx, int c ) { + if ( ctx->count == 0 ) { + if ( c == ESC ) { + /* First byte of CSI : begin escape sequence */ + ctx->count = 1; + memset ( ctx->params, 0xff, sizeof ( ctx->params ) ); + ctx->function = 0; + return -1; + } else { + /* Normal character */ + return c; + } + } else { + if ( c == '[' ) { + /* Second byte of CSI : do nothing */ + } else if ( ( c >= '0' ) && ( c <= '9' ) ) { + /* Parameter Byte : part of a parameter value */ + int *param = &ctx->params[ctx->count - 1]; + if ( *param < 0 ) + *param = 0; + *param = ( ( *param * 10 ) + ( c - '0' ) ); + } else if ( c == ';' ) { + /* Parameter Byte : parameter delimiter */ + ctx->count++; + if ( ctx->count > ( sizeof ( ctx->params ) / + sizeof ( ctx->params[0] ) ) ) { + /* Excessive parameters : abort sequence */ + ctx->count = 0; + DBG ( "Too many parameters in ANSI escape " + "sequence\n" ); + } + } else if ( ( c >= 0x20 ) && ( c <= 0x2f ) ) { + /* Intermediate Byte */ + ctx->function <<= 8; + ctx->function |= c; + } else { + /* Treat as Final Byte. Zero ctx->count before + * calling handler to avoid potential infinite loops. + */ + int count = ctx->count; + ctx->count = 0; + ctx->function <<= 8; + ctx->function |= c; + ansiesc_call_handler ( ctx->handlers, ctx->function, + count, ctx->params ); + } + return -1; + } +} diff --git a/gpxe/src/core/asprintf.c b/gpxe/src/core/asprintf.c new file mode 100644 index 00000000..94d7e7c4 --- /dev/null +++ b/gpxe/src/core/asprintf.c @@ -0,0 +1,47 @@ +#include <stdint.h> +#include <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> + +/** + * Write a formatted string to newly allocated memory. + * + * @v strp Pointer to hold allocated string + * @v fmt Format string + * @v args Arguments corresponding to the format string + * @ret len Length of formatted string + */ +int vasprintf ( char **strp, const char *fmt, va_list args ) { + size_t len; + va_list args_tmp; + + /* Calculate length needed for string */ + va_copy ( args_tmp, args ); + len = ( vsnprintf ( NULL, 0, fmt, args_tmp ) + 1 ); + va_end ( args_tmp ); + + /* Allocate and fill string */ + *strp = malloc ( len ); + if ( ! *strp ) + return -ENOMEM; + return vsnprintf ( *strp, len, fmt, args ); +} + +/** + * Write a formatted string to newly allocated memory. + * + * @v strp Pointer to hold allocated string + * @v fmt Format string + * @v ... Arguments corresponding to the format string + * @ret len Length of formatted string + */ +int asprintf ( char **strp, const char *fmt, ... ) { + va_list args; + int len; + + va_start ( args, fmt ); + len = vasprintf ( strp, fmt, args ); + va_end ( args ); + return len; +} diff --git a/gpxe/src/core/basename.c b/gpxe/src/core/basename.c new file mode 100644 index 00000000..7340c0d5 --- /dev/null +++ b/gpxe/src/core/basename.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2007 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. + */ + +/** + * @file + * + * Get base name of path + * + */ + +#include <string.h> +#include <libgen.h> + +/** + * Return base name from path + * + * @v path Full path + * @ret basename Base name + */ +char * basename ( char *path ) { + char *basename; + + basename = strrchr ( path, '/' ); + return ( basename ? ( basename + 1 ) : path ); +} + +/** + * Return directory name from path + * + * @v path Full path + * @ret dirname Directory name + * + * Note that this function may modify its argument. + */ +char * dirname ( char *path ) { + char *separator; + + separator = strrchr ( path, '/' ); + if ( separator == path ) { + return "/"; + } else if ( separator ) { + *separator = 0; + return path; + } else { + return "."; + } +} diff --git a/gpxe/src/core/bitmap.c b/gpxe/src/core/bitmap.c new file mode 100644 index 00000000..d0266471 --- /dev/null +++ b/gpxe/src/core/bitmap.c @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2007 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. + */ + +#include <errno.h> +#include <gpxe/bitmap.h> + +/** @file + * + * Bitmaps for multicast downloads + * + */ + +/** + * Resize bitmap + * + * @v bitmap Bitmap + * @v new_length New length of bitmap, in bits + * @ret rc Return status code + */ +int bitmap_resize ( struct bitmap *bitmap, unsigned int new_length ) { + unsigned int old_num_blocks; + unsigned int new_num_blocks; + size_t new_size; + bitmap_block_t *new_blocks; + + old_num_blocks = BITMAP_INDEX ( bitmap->length + BITMAP_BLKSIZE - 1 ); + new_num_blocks = BITMAP_INDEX ( new_length + BITMAP_BLKSIZE - 1 ); + + if ( old_num_blocks != new_num_blocks ) { + new_size = ( new_num_blocks * sizeof ( bitmap->blocks[0] ) ); + new_blocks = realloc ( bitmap->blocks, new_size ); + if ( ! new_blocks ) { + DBGC ( bitmap, "Bitmap %p could not resize to %d " + "bits\n", bitmap, new_length ); + return -ENOMEM; + } + bitmap->blocks = new_blocks; + } + bitmap->length = new_length; + + while ( old_num_blocks < new_num_blocks ) { + bitmap->blocks[old_num_blocks++] = 0; + } + + DBGC ( bitmap, "Bitmap %p resized to %d bits\n", bitmap, new_length ); + return 0; +} + +/** + * Test bit in bitmap + * + * @v bitmap Bitmap + * @v bit Bit index + * @ret is_set Bit is set + */ +int bitmap_test ( struct bitmap *bitmap, unsigned int bit ) { + unsigned int index = BITMAP_INDEX ( bit ); + bitmap_block_t mask = BITMAP_MASK ( bit ); + + if ( bit >= bitmap->length ) + return 0; + return ( bitmap->blocks[index] & mask ); +} + +/** + * Set bit in bitmap + * + * @v bitmap Bitmap + * @v bit Bit index + */ +void bitmap_set ( struct bitmap *bitmap, unsigned int bit ) { + unsigned int index = BITMAP_INDEX ( bit ); + bitmap_block_t mask = BITMAP_MASK ( bit ); + + DBGC ( bitmap, "Bitmap %p setting bit %d\n", bitmap, bit ); + + /* Update bitmap */ + bitmap->blocks[index] |= mask; + + /* Update first gap counter */ + while ( bitmap_test ( bitmap, bitmap->first_gap ) ) { + bitmap->first_gap++; + } +} diff --git a/gpxe/src/core/bitops.c b/gpxe/src/core/bitops.c new file mode 100644 index 00000000..53abaaea --- /dev/null +++ b/gpxe/src/core/bitops.c @@ -0,0 +1,11 @@ +#include <strings.h> + +int __flsl ( long x ) { + unsigned long value = x; + int ls = 0; + + for ( ls = 0 ; value ; ls++ ) { + value >>= 1; + } + return ls; +} diff --git a/gpxe/src/core/btext.c b/gpxe/src/core/btext.c new file mode 100644 index 00000000..122d89fb --- /dev/null +++ b/gpxe/src/core/btext.c @@ -0,0 +1,5039 @@ +#if 0 + +/* + * 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 "console.h" +#include <gpxe/init.h> +#include <gpxe/pci.h> + +#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(const unsigned char c, u32 locX, u32 locY); +#if 0 +static void draw_byte_32(const unsigned char *bits, u32 *base, u32 rb); +static void draw_byte_16(const unsigned char *bits, u32 *base, u32 rb); +#endif +static void draw_byte_8(const 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 const 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 + */ +static 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 = 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. + */ + +static 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; + base = bi->logicalDisplayBase; +#if 0 + /* Ummm... which moron wrote this? */ + if (base == 0) + base = bi->dispDeviceBase; +#endif + base += (x + bi->dispDeviceRect[0]) * (bi->dispDeviceDepth >> 3); + base += (y + bi->dispDeviceRect[1]) * bi->dispDeviceRowBytes; + return base; +} + + +static 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)); +} + +static 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 */ + +static 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 +static void BTEXT +btext_drawstring(const char *c) +{ + if (!boot_text_mapped) + return; + while (*c) + btext_drawchar(*c++); +} +static 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(const 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 const char *font = &vga_font[(((u32)c)) * 16]; +#else + unsigned const 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(const 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(const 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(const 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); + } +} + +static void btext_init(void) +{ +#if 0 +// for debug +#define frame_buffer 0xfc000000 +#else + uint32_t frame_buffer;// 0xfc000000 + + struct pci_device dev; + + #warning "pci_find_device_x no longer exists; use find_pci_device instead" + /* pci_find_device_x(0x1002, 0x4752, 0, &dev); */ + if(dev.vendor==0) return; // no fb + + frame_buffer = (uint32_t)dev.membase; +#endif + + btext_setup_display(640, 480, 8, 640,frame_buffer); + btext_clearscreen(); + map_boot_text(); +} +static void btext_putc(int c) +{ + btext_drawchar((unsigned char)c); +} + +struct console_driver btext_console __console_driver = { + .putchar = btext_putc, + .disabled = 1, +}; + +//come from linux/drivers/video/font-8x16.c +/**********************************************/ +/* */ +/* Font file generated by cpi2fnt */ +/* */ +/**********************************************/ + + +static const 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 diff --git a/gpxe/src/core/config.c b/gpxe/src/core/config.c new file mode 100644 index 00000000..ffd11256 --- /dev/null +++ b/gpxe/src/core/config.c @@ -0,0 +1,203 @@ +/* + * 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 "config/general.h" + +/* + * Build ID string calculations + * + */ +#undef XSTR +#undef STR +#define XSTR(s) STR(s) +#define STR(s) #s + +#ifdef BUILD_SERIAL +#include "config/.buildserial.h" +#define BUILD_SERIAL_STR " #" XSTR(BUILD_SERIAL_NUM) +#else +#define BUILD_SERIAL_STR "" +#endif + +#ifdef BUILD_ID +#define BUILD_ID_STR " " BUILD_ID +#else +#define BUILD_ID_STR "" +#endif + +#if defined(BUILD_ID) || defined(BUILD_SERIAL) +#define BUILD_STRING " [build" BUILD_ID_STR BUILD_SERIAL_STR "]" +#else +#define BUILD_STRING "" +#endif + +/* + * Drag in all requested console types + * + * CONSOLE_DUAL sets both CONSOLE_FIRMWARE and CONSOLE_SERIAL for + * legacy compatibility. + * + */ + +#ifdef CONSOLE_DUAL +#undef CONSOLE_FIRMWARE +#define CONSOLE_FIRMWARE 1 +#undef CONSOLE_SERIAL +#define CONSOLE_SERIAL 1 +#endif + +#ifdef CONSOLE_FIRMWARE +REQUIRE_OBJECT ( bios_console ); +#endif +#ifdef CONSOLE_SERIAL +REQUIRE_OBJECT ( serial ); +#endif +#ifdef CONSOLE_DIRECT_VGA +REQUIRE_OBJECT ( video_subr ); +#endif +#ifdef CONSOLE_BTEXT +REQUIRE_OBJECT ( btext ); +#endif +#ifdef CONSOLE_PC_KBD +REQUIRE_OBJECT ( pc_kbd ); +#endif +#ifdef CONSOLE_SYSLOG +REQUIRE_OBJECT ( syslog ); +#endif + +/* + * Drag in all requested timers + */ +#ifdef TIMER_BIOS +REQUIRE_OBJECT ( timer_bios ); +#endif +#ifdef TIMER_RDTSC +REQUIRE_OBJECT ( timer_rdtsc ); +#endif + +/* + * Drag in all requested network protocols + * + */ +#ifdef NET_PROTO_IPV4 +REQUIRE_OBJECT ( ipv4 ); +#endif + +/* + * Drag in all requested download protocols + * + */ +#ifdef DOWNLOAD_PROTO_TFTP +REQUIRE_OBJECT ( tftp ); +#endif +#ifdef DOWNLOAD_PROTO_NFS +REQUIRE_OBJECT ( nfs ); +#endif +#ifdef DOWNLOAD_PROTO_HTTP +REQUIRE_OBJECT ( http ); +#endif +#ifdef DOWNLOAD_PROTO_HTTPS +REQUIRE_OBJECT ( https ); +#endif +#ifdef DOWNLOAD_PROTO_FTP +REQUIRE_OBJECT ( ftp ); +#endif +#ifdef DOWNLOAD_PROTO_TFTM +REQUIRE_OBJECT ( tftm ); +#endif +#ifdef DOWNLOAD_PROTO_SLAM +REQUIRE_OBJECT ( slam ); +#endif + +/* + * Drag in all requested resolvers + * + */ +#ifdef DNS_RESOLVER +REQUIRE_OBJECT ( dns ); +#endif +#ifdef NMB_RESOLVER +REQUIRE_OBJECT ( nmb ); +#endif + +/* + * Drag in all requested image formats + * + */ +#ifdef IMAGE_NBI +REQUIRE_OBJECT ( nbi ); +#endif +#ifdef IMAGE_ELF64 +REQUIRE_OBJECT ( elf64 ); +#endif +#ifdef IMAGE_ELF +REQUIRE_OBJECT ( elf ); +#endif +#ifdef IMAGE_FREEBSD +REQUIRE_OBJECT ( freebsd ); +#endif +#ifdef IMAGE_MULTIBOOT +REQUIRE_OBJECT ( multiboot ); +#endif +#ifdef IMAGE_AOUT +REQUIRE_OBJECT ( aout ); +#endif +#ifdef IMAGE_WINCE +REQUIRE_OBJECT ( wince ); +#endif +#ifdef IMAGE_PXE +REQUIRE_OBJECT ( pxe_image ); +#endif +#ifdef IMAGE_SCRIPT +REQUIRE_OBJECT ( script ); +#endif +#ifdef IMAGE_BZIMAGE +REQUIRE_OBJECT ( bzimage ); +#endif +#ifdef IMAGE_ELTORITO +REQUIRE_OBJECT ( eltorito ); +#endif + +/* + * Drag in all requested commands + * + */ +#ifdef AUTOBOOT_CMD +REQUIRE_OBJECT ( autoboot_cmd ); +#endif +#ifdef NVO_CMD +REQUIRE_OBJECT ( nvo_cmd ); +#endif +#ifdef CONFIG_CMD +REQUIRE_OBJECT ( config_cmd ); +#endif +#ifdef IFMGMT_CMD +REQUIRE_OBJECT ( ifmgmt_cmd ); +#endif +#ifdef ROUTE_CMD +REQUIRE_OBJECT ( route_cmd ); +#endif +#ifdef IMAGE_CMD +REQUIRE_OBJECT ( image_cmd ); +#endif +#ifdef DHCP_CMD +REQUIRE_OBJECT ( dhcp_cmd ); +#endif +#ifdef SANBOOT_CMD +REQUIRE_OBJECT ( sanboot_cmd ); +#endif + +/* + * Drag in miscellaneous objects + * + */ +#ifdef NULL_TRAP +REQUIRE_OBJECT ( nulltrap ); +#endif +#ifdef DUMP_GDBSYM +REQUIRE_OBJECT ( gdbsym ); +#endif diff --git a/gpxe/src/core/console.c b/gpxe/src/core/console.c new file mode 100644 index 00000000..6f55d558 --- /dev/null +++ b/gpxe/src/core/console.c @@ -0,0 +1,137 @@ +#include "stddef.h" +#include "console.h" +#include <gpxe/process.h> + +/** @file */ + +#include "bios.h" + +static struct console_driver console_drivers[0] + __table_start ( struct console_driver, console ); +static struct console_driver console_drivers_end[0] + __table_end ( struct console_driver, console ); + +/** + * Write a single character to each console device. + * + * @v character Character to be written + * @ret None - + * @err None - + * + * The character is written out to all enabled console devices, using + * each device's console_driver::putchar() method. + * + */ +void putchar ( int character ) { + struct console_driver *console; + + /* Automatic LF -> CR,LF translation */ + if ( character == '\n' ) + putchar ( '\r' ); + + for ( console = console_drivers; console < console_drivers_end ; + console++ ) { + if ( ( ! console->disabled ) && console->putchar ) + console->putchar ( character ); + } +} + +/** + * Check to see if any input is available on any console. + * + * @v None - + * @ret console Console device that has input available, if any. + * @ret NULL No console device has input available. + * @err None - + * + * All enabled console devices are checked once for available input + * using each device's console_driver::iskey() method. The first + * console device that has available input will be returned, if any. + * + */ +static struct console_driver * has_input ( void ) { + struct console_driver *console; + + for ( console = console_drivers; console < console_drivers_end ; + console++ ) { + if ( ( ! console->disabled ) && console->iskey ) { + if ( console->iskey () ) + return console; + } + } + return NULL; +} + +/** + * Read a single character from any console. + * + * @v None - + * @ret character Character read from a console. + * @err None - + * + * A character will be read from the first enabled console device that + * has input available using that console's console_driver::getchar() + * method. If no console has input available to be read, this method + * will block. To perform a non-blocking read, use something like + * + * @code + * + * int key = iskey() ? getchar() : -1; + * + * @endcode + * + * The character read will not be echoed back to any console. + * + * @bug We need a cleaner way to pick up cpu_nap(). It makes a + * real-mode call, and so we don't want to use it with LinuxBIOS. + * + */ +int getchar ( void ) { + struct console_driver *console; + int character = 256; + + while ( character == 256 ) { + /* 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(); + + /* Keep processing background tasks while we wait for + * input. + */ + step(); + + console = has_input(); + if ( console && console->getchar ) + character = console->getchar (); + } + + /* CR -> LF translation */ + if ( character == '\r' ) + character = '\n'; + + return character; +} + +/** Check for available input on any console. + * + * @v None - + * @ret True Input is available on a console + * @ret False Input is not available on any console + * @err None - + * + * All enabled console devices are checked once for available input + * using each device's console_driver::iskey() method. If any console + * device has input available, this call will return True. If this + * call returns True, you can then safely call getchar() without + * blocking. + * + */ +int iskey ( void ) { + return has_input() ? 1 : 0; +} diff --git a/gpxe/src/core/cpio.c b/gpxe/src/core/cpio.c new file mode 100644 index 00000000..7d2e8828 --- /dev/null +++ b/gpxe/src/core/cpio.c @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2007 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. + */ + +/** @file + * + * CPIO archives + * + */ + +#include <stdio.h> +#include <string.h> +#include <gpxe/cpio.h> + +/** + * Set field within a CPIO header + * + * @v field Field within CPIO header + * @v value Value to set + */ +void cpio_set_field ( char *field, unsigned long value ) { + char buf[9]; + + snprintf ( buf, sizeof ( buf ), "%08lx", value ); + memcpy ( field, buf, 8 ); +} diff --git a/gpxe/src/core/cwuri.c b/gpxe/src/core/cwuri.c new file mode 100644 index 00000000..c7f01386 --- /dev/null +++ b/gpxe/src/core/cwuri.c @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2007 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. + */ + +#include <stddef.h> +#include <gpxe/uri.h> + +/** @file + * + * Current working URI + * + * Somewhat analogous to the current working directory in a POSIX + * system. + */ + +/** Current working URI */ +struct uri *cwuri = NULL; + +/** + * Change working URI + * + * @v uri New working URI, or NULL + */ +void churi ( struct uri *uri ) { + uri_put ( cwuri ); + cwuri = uri_get ( uri ); +} diff --git a/gpxe/src/core/debug.c b/gpxe/src/core/debug.c new file mode 100644 index 00000000..09830420 --- /dev/null +++ b/gpxe/src/core/debug.c @@ -0,0 +1,195 @@ +#include <stdio.h> +#include <stdint.h> +#include <stdarg.h> +#include <io.h> +#include <console.h> + +void pause ( void ) { + printf ( "\nPress a key" ); + getchar(); + printf ( "\r \r" ); +} + +void more ( void ) { + printf ( "---more---" ); + getchar(); + printf ( "\r \r" ); +} + +/** + * Print row of a hex dump with specified display address + * + * @v dispaddr Display address + * @v data Data to print + * @v len Length of data + * @v offset Starting offset within data + */ +static void dbg_hex_dump_da_row ( unsigned long dispaddr, const void *data, + unsigned long len, unsigned int offset ) { + const uint8_t *bytes = data; + unsigned int i; + uint8_t byte; + + printf ( "%08lx :", ( dispaddr + offset ) ); + for ( i = offset ; i < ( offset + 16 ) ; i++ ) { + if ( i >= len ) { + printf ( " " ); + continue; + } + printf ( " %02x", bytes[i] ); + } + printf ( " : " ); + for ( i = offset ; i < ( offset + 16 ) ; i++ ) { + if ( i >= len ) { + printf ( " " ); + continue; + } + byte = bytes[i]; + if ( ( byte < 0x20 ) || ( byte >= 0x7f ) ) + byte = '.'; + printf ( "%c", byte ); + } + printf ( "\n" ); +} + +/** + * Print hex dump with specified display address + * + * @v dispaddr Display address + * @v data Data to print + * @v len Length of data + */ +void dbg_hex_dump_da ( unsigned long dispaddr, const void *data, + unsigned long len ) { + unsigned int offset; + + for ( offset = 0 ; offset < len ; offset += 16 ) { + dbg_hex_dump_da_row ( dispaddr, data, len, offset ); + } +} + +#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 %p-%p (physical %#lx-%#lx) " + "corrupted\n", + region, region + len, + virt_to_phys ( region ), + virt_to_phys ( region + len ) ); + } + in_corruption = 1; + printf ( "--- offset %#lx ", offset ); + } else if ( ( in_corruption != 0 ) && + ( test == GUARD_SYMBOL ) ) { + /* End of corruption */ + in_corruption = 0; + printf ( "to offset %#lx", offset ); + } + + } + if ( in_corruption != 0 ) { + printf ( "to offset %#zx (end of region)\n", len-1 ); + } + return corrupted; +} + +/** + * Maximum number of separately coloured message streams + * + * Six is the realistic maximum; there are 8 basic ANSI colours, one + * of which will be the terminal default and one of which will be + * invisible on the terminal because it matches the background colour. + */ +#define NUM_AUTO_COLOURS 6 + +/** A colour assigned to an autocolourised debug message stream */ +struct autocolour { + /** Message stream ID */ + unsigned long stream; + /** Last recorded usage */ + unsigned long last_used; +}; + +/** + * Choose colour index for debug autocolourisation + * + * @v stream Message stream ID + * @ret colour Colour ID + */ +static int dbg_autocolour ( unsigned long stream ) { + static struct autocolour acs[NUM_AUTO_COLOURS]; + static unsigned long use; + unsigned int i; + unsigned int oldest; + unsigned int oldest_last_used; + + /* Increment usage iteration counter */ + use++; + + /* Scan through list for a currently assigned colour */ + for ( i = 0 ; i < ( sizeof ( acs ) / sizeof ( acs[0] ) ) ; i++ ) { + if ( acs[i].stream == stream ) { + acs[i].last_used = use; + return i; + } + } + + /* No colour found; evict the oldest from the list */ + oldest = 0; + oldest_last_used = use; + for ( i = 0 ; i < ( sizeof ( acs ) / sizeof ( acs[0] ) ) ; i++ ) { + if ( acs[i].last_used < oldest_last_used ) { + oldest_last_used = acs[i].last_used; + oldest = i; + } + } + acs[oldest].stream = stream; + acs[oldest].last_used = use; + return oldest; +} + +/** + * Select automatic colour for debug messages + * + * @v stream Message stream ID + */ +void dbg_autocolourise ( unsigned long stream ) { + printf ( "\033[%dm", + ( stream ? ( 31 + dbg_autocolour ( stream ) ) : 0 ) ); +} + +/** + * Revert to normal colour + * + */ +void dbg_decolourise ( void ) { + printf ( "\033[0m" ); +} diff --git a/gpxe/src/core/device.c b/gpxe/src/core/device.c new file mode 100644 index 00000000..b1b148e8 --- /dev/null +++ b/gpxe/src/core/device.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2006 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. + */ + +#include <string.h> +#include <gpxe/list.h> +#include <gpxe/tables.h> +#include <gpxe/device.h> + +/** + * @file + * + * Device model + * + */ + +static struct root_device root_devices[0] + __table_start ( struct root_device, root_devices ); +static struct root_device root_devices_end[0] + __table_end ( struct root_device, root_devices ); + +/** Registered root devices */ +static LIST_HEAD ( devices ); + +/** + * Probe a root device + * + * @v rootdev Root device + * @ret rc Return status code + */ +static int rootdev_probe ( struct root_device *rootdev ) { + int rc; + + DBG ( "Adding %s root bus\n", rootdev->dev.name ); + if ( ( rc = rootdev->driver->probe ( rootdev ) ) != 0 ) { + DBG ( "Failed to add %s root bus: %s\n", + rootdev->dev.name, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Remove a root device + * + * @v rootdev Root device + */ +static void rootdev_remove ( struct root_device *rootdev ) { + rootdev->driver->remove ( rootdev ); + DBG ( "Removed %s root bus\n", rootdev->dev.name ); +} + +/** + * Probe all devices + * + * @ret rc Return status code + * + * This initiates probing for all devices in the system. After this + * call, the device hierarchy will be populated, and all hardware + * should be ready to use. + */ +int probe_devices ( void ) { + struct root_device *rootdev; + int rc; + + for ( rootdev = root_devices; rootdev < root_devices_end; rootdev++ ) { + list_add ( &rootdev->dev.siblings, &devices ); + INIT_LIST_HEAD ( &rootdev->dev.children ); + if ( ( rc = rootdev_probe ( rootdev ) ) != 0 ) + list_del ( &rootdev->dev.siblings ); + } + return 0; +} + +/** + * Remove all devices + * + */ +void remove_devices ( void ) { + struct root_device *rootdev; + struct root_device *tmp; + + list_for_each_entry_safe ( rootdev, tmp, &devices, dev.siblings ) { + rootdev_remove ( rootdev ); + list_del ( &rootdev->dev.siblings ); + } +} diff --git a/gpxe/src/core/downloader.c b/gpxe/src/core/downloader.c new file mode 100644 index 00000000..0a443db2 --- /dev/null +++ b/gpxe/src/core/downloader.c @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2007 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. + */ + +#include <stdlib.h> +#include <stdarg.h> +#include <errno.h> +#include <gpxe/xfer.h> +#include <gpxe/open.h> +#include <gpxe/job.h> +#include <gpxe/uaccess.h> +#include <gpxe/umalloc.h> +#include <gpxe/image.h> +#include <gpxe/downloader.h> + +/** @file + * + * Image downloader + * + */ + +/** A downloader */ +struct downloader { + /** Reference count for this object */ + struct refcnt refcnt; + + /** Job control interface */ + struct job_interface job; + /** Data transfer interface */ + struct xfer_interface xfer; + + /** Image to contain downloaded file */ + struct image *image; + /** Current position within image buffer */ + size_t pos; + /** Image registration routine */ + int ( * register_image ) ( struct image *image ); +}; + +/** + * Free downloader object + * + * @v refcnt Downloader reference counter + */ +static void downloader_free ( struct refcnt *refcnt ) { + struct downloader *downloader = + container_of ( refcnt, struct downloader, refcnt ); + + image_put ( downloader->image ); + free ( downloader ); +} + +/** + * Terminate download + * + * @v downloader Downloader + * @v rc Reason for termination + */ +static void downloader_finished ( struct downloader *downloader, int rc ) { + + /* Block further incoming messages */ + job_nullify ( &downloader->job ); + xfer_nullify ( &downloader->xfer ); + + /* Free resources and close interfaces */ + xfer_close ( &downloader->xfer, rc ); + job_done ( &downloader->job, rc ); +} + +/** + * Ensure that download buffer is large enough for the specified size + * + * @v downloader Downloader + * @v len Required minimum size + * @ret rc Return status code + */ +static int downloader_ensure_size ( struct downloader *downloader, + size_t len ) { + userptr_t new_buffer; + + /* If buffer is already large enough, do nothing */ + if ( len <= downloader->image->len ) + return 0; + + DBGC ( downloader, "Downloader %p extending to %zd bytes\n", + downloader, len ); + + /* Extend buffer */ + new_buffer = urealloc ( downloader->image->data, len ); + if ( ! new_buffer ) { + DBGC ( downloader, "Downloader %p could not extend buffer to " + "%zd bytes\n", downloader, len ); + return -ENOBUFS; + } + downloader->image->data = new_buffer; + downloader->image->len = len; + + return 0; +} + +/**************************************************************************** + * + * Job control interface + * + */ + +/** + * Handle kill() event received via job control interface + * + * @v job Downloader job control interface + */ +static void downloader_job_kill ( struct job_interface *job ) { + struct downloader *downloader = + container_of ( job, struct downloader, job ); + + /* Terminate download */ + downloader_finished ( downloader, -ECANCELED ); +} + +/** Downloader job control interface operations */ +static struct job_interface_operations downloader_job_operations = { + .done = ignore_job_done, + .kill = downloader_job_kill, + .progress = ignore_job_progress, +}; + +/**************************************************************************** + * + * Data transfer interface + * + */ + +/** + * Handle deliver_raw() event received via data transfer interface + * + * @v xfer Downloader data transfer interface + * @v iobuf Datagram I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + */ +static int downloader_xfer_deliver_iob ( struct xfer_interface *xfer, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + struct downloader *downloader = + container_of ( xfer, struct downloader, xfer ); + size_t len; + size_t max; + int rc; + + /* Calculate new buffer position */ + if ( meta->whence != SEEK_CUR ) + downloader->pos = 0; + downloader->pos += meta->offset; + + /* Ensure that we have enough buffer space for this data */ + len = iob_len ( iobuf ); + max = ( downloader->pos + len ); + if ( ( rc = downloader_ensure_size ( downloader, max ) ) != 0 ) + goto done; + + /* Copy data to buffer */ + copy_to_user ( downloader->image->data, downloader->pos, + iobuf->data, len ); + + /* Update current buffer position */ + downloader->pos += len; + + done: + free_iob ( iobuf ); + return rc; +} + +/** + * Handle close() event received via data transfer interface + * + * @v xfer Downloader data transfer interface + * @v rc Reason for close + */ +static void downloader_xfer_close ( struct xfer_interface *xfer, int rc ) { + struct downloader *downloader = + container_of ( xfer, struct downloader, xfer ); + + /* Register image if download was successful */ + if ( rc == 0 ) + rc = downloader->register_image ( downloader->image ); + + /* Terminate download */ + downloader_finished ( downloader, rc ); +} + +/** Downloader data transfer interface operations */ +static struct xfer_interface_operations downloader_xfer_operations = { + .close = downloader_xfer_close, + .vredirect = xfer_vopen, + .window = unlimited_xfer_window, + .alloc_iob = default_xfer_alloc_iob, + .deliver_iob = downloader_xfer_deliver_iob, + .deliver_raw = xfer_deliver_as_iob, +}; + +/**************************************************************************** + * + * Instantiator + * + */ + +/** + * Instantiate a downloader + * + * @v job Job control interface + * @v image Image to fill with downloaded file + * @v register_image Image registration routine + * @v type Location type to pass to xfer_open() + * @v ... Remaining arguments to pass to xfer_open() + * @ret rc Return status code + * + * Instantiates a downloader object to download the specified URI into + * the specified image object. If the download is successful, the + * image registration routine @c register_image() will be called. + */ +int create_downloader ( struct job_interface *job, struct image *image, + int ( * register_image ) ( struct image *image ), + int type, ... ) { + struct downloader *downloader; + va_list args; + int rc; + + /* Allocate and initialise structure */ + downloader = zalloc ( sizeof ( *downloader ) ); + if ( ! downloader ) + return -ENOMEM; + downloader->refcnt.free = downloader_free; + job_init ( &downloader->job, &downloader_job_operations, + &downloader->refcnt ); + xfer_init ( &downloader->xfer, &downloader_xfer_operations, + &downloader->refcnt ); + downloader->image = image_get ( image ); + downloader->register_image = register_image; + va_start ( args, type ); + + /* Instantiate child objects and attach to our interfaces */ + if ( ( rc = xfer_vopen ( &downloader->xfer, type, args ) ) != 0 ) + goto err; + + /* Attach parent interface, mortalise self, and return */ + job_plug_plug ( &downloader->job, job ); + ref_put ( &downloader->refcnt ); + va_end ( args ); + return 0; + + err: + downloader_finished ( downloader, rc ); + ref_put ( &downloader->refcnt ); + va_end ( args ); + return rc; +} diff --git a/gpxe/src/core/errno.c b/gpxe/src/core/errno.c new file mode 100644 index 00000000..b4f44cec --- /dev/null +++ b/gpxe/src/core/errno.c @@ -0,0 +1,18 @@ +#include <errno.h> + +/** @file + * + * Error codes + * + * This file provides the global variable #errno. + * + */ + +/** + * Global "last error" number. + * + * This is valid only when a function has just returned indicating a + * failure. + * + */ +int errno; diff --git a/gpxe/src/core/exec.c b/gpxe/src/core/exec.c new file mode 100644 index 00000000..a1c073e3 --- /dev/null +++ b/gpxe/src/core/exec.c @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2006 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. + */ + +#include <stdint.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <getopt.h> +#include <errno.h> +#include <assert.h> +#include <gpxe/tables.h> +#include <gpxe/command.h> + +/** @file + * + * Command execution + * + */ + +static struct command commands[0] + __table_start ( struct command, commands ); +static struct command commands_end[0] + __table_end ( struct command, commands ); + +/* Avoid dragging in getopt.o unless a command really uses it */ +int optind; +int nextchar; + +/** + * Execute command + * + * @v command Command name + * @v argv Argument list + * @ret rc Command exit status + * + * Execute the named command. Unlike a traditional POSIX execv(), + * this function returns the exit status of the command. + */ +int execv ( const char *command, char * const argv[] ) { + struct command *cmd; + int argc; + + /* Count number of arguments */ + for ( argc = 0 ; argv[argc] ; argc++ ) {} + + /* Sanity checks */ + if ( ! command ) { + DBG ( "No command\n" ); + return -EINVAL; + } + if ( ! argc ) { + DBG ( "%s: empty argument list\n", command ); + return -EINVAL; + } + + /* Reset getopt() library ready for use by the command. This + * is an artefact of the POSIX getopt() API within the context + * of Etherboot; see the documentation for reset_getopt() for + * details. + */ + reset_getopt(); + + /* Hand off to command implementation */ + for ( cmd = commands ; cmd < commands_end ; cmd++ ) { + if ( strcmp ( command, cmd->name ) == 0 ) + return cmd->exec ( argc, ( char ** ) argv ); + } + + printf ( "%s: command not found\n", command ); + return -ENOEXEC; +} + +/** + * Split command line into argv array + * + * @v args Command line + * @v argv Argument array to populate, or NULL + * @ret argc Argument count + * + * Splits the command line into whitespace-delimited arguments. If @c + * argv is non-NULL, any whitespace in the command line will be + * replaced with NULs. + */ +static int split_args ( char *args, char * argv[] ) { + int argc = 0; + + while ( 1 ) { + /* Skip over any whitespace / convert to NUL */ + while ( *args == ' ' ) { + if ( argv ) + *args = '\0'; + args++; + } + /* Check for end of line */ + if ( ! *args ) + break; + /* We have found the start of the next argument */ + if ( argv ) + argv[argc] = args; + argc++; + /* Skip to start of next whitespace, if any */ + while ( *args && ( *args != ' ' ) ) { + args++; + } + } + return argc; +} + +/** + * Execute command line + * + * @v command Command line + * @ret rc Command exit status + * + * Execute the named command and arguments. + */ +int system ( const char *command ) { + char *args; + int argc; + int rc = 0; + + /* Obtain temporary modifiable copy of command line */ + args = strdup ( command ); + if ( ! args ) + return -ENOMEM; + + /* Count arguments */ + argc = split_args ( args, NULL ); + + /* Create argv array and execute command */ + if ( argc ) { + char * argv[argc + 1]; + + split_args ( args, argv ); + argv[argc] = NULL; + + if ( argv[0][0] != '#' ) + rc = execv ( argv[0], argv ); + } + + free ( args ); + return rc; +} diff --git a/gpxe/src/core/filter.c b/gpxe/src/core/filter.c new file mode 100644 index 00000000..f9ebe7cf --- /dev/null +++ b/gpxe/src/core/filter.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2007 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. + */ + +#include <gpxe/xfer.h> +#include <gpxe/filter.h> + +/** @file + * + * Data transfer filters + * + */ + +/* + * Pass-through methods to be used by filters which don't want to + * intercept all events. + * + */ + +void filter_close ( struct xfer_interface *xfer, int rc ) { + struct xfer_interface *other = filter_other_half ( xfer ); + + xfer_close ( other, rc ); +} + +int filter_vredirect ( struct xfer_interface *xfer, int type, + va_list args ) { + struct xfer_interface *other = filter_other_half ( xfer ); + + return xfer_vredirect ( other, type, args ); +} + +size_t filter_window ( struct xfer_interface *xfer ) { + struct xfer_interface *other = filter_other_half ( xfer ); + + return xfer_window ( other ); +} + +struct io_buffer * filter_alloc_iob ( struct xfer_interface *xfer, + size_t len ) { + struct xfer_interface *other = filter_other_half ( xfer ); + + return xfer_alloc_iob ( other, len ); +} + +int filter_deliver_iob ( struct xfer_interface *xfer, struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + struct xfer_interface *other = filter_other_half ( xfer ); + + return xfer_deliver_iob_meta ( other, iobuf, meta ); +} + +int filter_deliver_raw ( struct xfer_interface *xfer, const void *data, + size_t len ) { + struct xfer_interface *other = filter_other_half ( xfer ); + + return xfer_deliver_raw ( other, data, len ); +} diff --git a/gpxe/src/core/getkey.c b/gpxe/src/core/getkey.c new file mode 100644 index 00000000..1551cf37 --- /dev/null +++ b/gpxe/src/core/getkey.c @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2006 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. + */ + +#include <console.h> +#include <gpxe/process.h> +#include <gpxe/keys.h> +#include <gpxe/timer.h> + +/** @file + * + * Special key interpretation + * + */ + +#define GETKEY_TIMEOUT ( TICKS_PER_SEC / 4 ) + +/** + * Read character from console if available within timeout period + * + * @v timeout Timeout period, in ticks + * @ret character Character read from console + */ +static int getchar_timeout ( unsigned long timeout ) { + unsigned long expiry = ( currticks() + timeout ); + + while ( currticks() < expiry ) { + step(); + if ( iskey() ) + return getchar(); + } + + return -1; +} + +/** + * Get single keypress + * + * @ret key Key pressed + * + * The returned key will be an ASCII value or a KEY_XXX special + * constant. This function differs from getchar() in that getchar() + * will return "special" keys (e.g. cursor keys) as a series of + * characters forming an ANSI escape sequence. + */ +int getkey ( void ) { + int character; + int key; + + character = getchar(); + if ( character != ESC ) + return character; + + key = 0; + while ( ( character = getchar_timeout ( GETKEY_TIMEOUT ) ) >= 0 ) { + if ( character == '[' ) + continue; + if ( ! key ) + key = KEY_ANSI ( character ); + if ( character >= 0x40 ) + break; + } + + return ( key ? key : ESC ); +} diff --git a/gpxe/src/core/getopt.c b/gpxe/src/core/getopt.c new file mode 100644 index 00000000..6de412bb --- /dev/null +++ b/gpxe/src/core/getopt.c @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2006 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. + */ + +#include <stdint.h> +#include <string.h> +#include <stdio.h> +#include <getopt.h> + +/** @file + * + * Parse command-line options + * + */ + +/** + * Option argument + * + * This will point to the argument for the most recently returned + * option, if applicable. + */ +char *optarg; + +/** + * Current option index + * + * This is an index into the argv[] array. When getopt() returns -1, + * @c optind is the index to the first element that is not an option. + */ +int optind; + +/** + * Current option character index + * + * This is an index into the current element of argv[]. + */ +int nextchar; + +/** + * Unrecognised option + * + * When an unrecognised option is encountered, the actual option + * character is stored in @c optopt. + */ +int optopt; + +/** + * Get option argument from argv[] array + * + * @v argc Argument count + * @v argv Argument list + * @ret argument Option argument, or NULL + * + * Grab the next element of argv[], if it exists and is not an option. + */ +static const char * get_argv_argument ( int argc, char * const argv[] ) { + char *arg; + + /* Don't overrun argv[] */ + if ( optind >= argc ) + return NULL; + arg = argv[optind]; + + /* If next argv element is an option, then it's not usable as + * an argument. + */ + if ( *arg == '-' ) + return NULL; + + /** Consume this argv element, and return it */ + optind++; + return arg; +} + +/** + * Match long option + * + * @v argc Argument count + * @v argv Argument list + * @v opttext Option text within current argv[] element + * @v longopt Long option specification + * @ret option Option to return from getopt() + * @ret matched Found a match for this long option + */ +static int match_long_option ( int argc, char * const argv[], + const char *opttext, + const struct option *longopt, int *option ) { + size_t optlen; + const char *argument = NULL; + + /* Compare option name */ + optlen = strlen ( longopt->name ); + if ( strncmp ( opttext, longopt->name, optlen ) != 0 ) + return 0; + + /* Check for inline argument */ + if ( opttext[optlen] == '=' ) { + argument = &opttext[ optlen + 1 ]; + } else if ( opttext[optlen] ) { + /* Long option with trailing garbage - no match */ + return 0; + } + + /* Consume this argv element */ + optind++; + + /* If we want an argument but don't have one yet, try to grab + * the next argv element + */ + if ( ( longopt->has_arg != no_argument ) && ( ! argument ) ) + argument = get_argv_argument ( argc, argv ); + + /* If we need an argument but don't have one, sulk */ + if ( ( longopt->has_arg == required_argument ) && ( ! argument ) ) { + printf ( "Option \"%s\" requires an argument\n", + longopt->name ); + *option = ':'; + return 1; + } + + /* If we have an argument where we shouldn't have one, sulk */ + if ( ( longopt->has_arg == no_argument ) && argument ) { + printf ( "Option \"%s\" takes no argument\n", longopt->name ); + *option = ':'; + return 1; + } + + /* Store values and return success */ + optarg = ( char * ) argument; + if ( longopt->flag ) { + *(longopt->flag) = longopt->val; + *option = 0; + } else { + *option = longopt->val; + } + return 1; +} + +/** + * Match short option + * + * @v argc Argument count + * @v argv Argument list + * @v opttext Option text within current argv[] element + * @v shortopt Option character from option specification + * @ret option Option to return from getopt() + * @ret matched Found a match for this short option + */ +static int match_short_option ( int argc, char * const argv[], + const char *opttext, int shortopt, + enum getopt_argument_requirement has_arg, + int *option ) { + const char *argument = NULL; + + /* Compare option character */ + if ( *opttext != shortopt ) + return 0; + + /* Consume option character */ + opttext++; + nextchar++; + if ( *opttext ) { + if ( has_arg != no_argument ) { + /* Consume remainder of element as inline argument */ + argument = opttext; + optind++; + nextchar = 0; + } + } else { + /* Reached end of argv element */ + optind++; + nextchar = 0; + } + + /* If we want an argument but don't have one yet, try to grab + * the next argv element + */ + if ( ( has_arg != no_argument ) && ( ! argument ) ) + argument = get_argv_argument ( argc, argv ); + + /* If we need an argument but don't have one, sulk */ + if ( ( has_arg == required_argument ) && ( ! argument ) ) { + printf ( "Option \"%c\" requires an argument\n", shortopt ); + *option = ':'; + return 1; + } + + /* Store values and return success */ + optarg = ( char * ) argument; + *option = shortopt; + return 1; +} + +/** + * Parse command-line options + * + * @v argc Argument count + * @v argv Argument list + * @v optstring Option specification string + * @v longopts Long option specification table + * @ret longindex Index of long option (or NULL) + * @ret option Option found, or -1 for no more options + * + * Note that the caller must arrange for reset_getopt() to be called + * before each set of calls to getopt_long(). In Etherboot, this is + * done automatically by execv(). + */ +int getopt_long ( int argc, char * const argv[], const char *optstring, + const struct option *longopts, int *longindex ) { + const char *opttext = argv[optind]; + const struct option *longopt; + int shortopt; + enum getopt_argument_requirement has_arg; + int option; + + /* Check for end of argv array */ + if ( optind >= argc ) + return -1; + + /* Check for end of options */ + if ( *(opttext++) != '-' ) + return -1; + + /* Check for long options */ + if ( *(opttext++) == '-' ) { + for ( longopt = longopts ; longopt->name ; longopt++ ) { + if ( ! match_long_option ( argc, argv, opttext, + longopt, &option ) ) + continue; + if ( longindex ) + *longindex = ( longopt - longopts ); + return option; + } + optopt = '?'; + printf ( "Unrecognised option \"--%s\"\n", opttext ); + return '?'; + } + + /* Check for short options */ + if ( nextchar < 1 ) + nextchar = 1; + opttext = ( argv[optind] + nextchar ); + while ( ( shortopt = *(optstring++) ) ) { + has_arg = no_argument; + while ( *optstring == ':' ) { + has_arg++; + optstring++; + } + if ( match_short_option ( argc, argv, opttext, shortopt, + has_arg, &option ) ) { + return option; + } + } + optopt = *opttext; + printf ( "Unrecognised option \"-%c\"\n", optopt ); + return '?'; +} diff --git a/gpxe/src/core/hw.c b/gpxe/src/core/hw.c new file mode 100644 index 00000000..65604ee0 --- /dev/null +++ b/gpxe/src/core/hw.c @@ -0,0 +1,74 @@ +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <gpxe/refcnt.h> +#include <gpxe/process.h> +#include <gpxe/xfer.h> +#include <gpxe/open.h> + +/** @file + * + * "Hello World" data source + * + */ + +struct hw { + struct refcnt refcnt; + struct xfer_interface xfer; + struct process process; +}; + +static const char hw_msg[] = "Hello world!\n"; + +static void hw_finished ( struct hw *hw, int rc ) { + xfer_nullify ( &hw->xfer ); + xfer_close ( &hw->xfer, rc ); + process_del ( &hw->process ); +} + +static void hw_xfer_close ( struct xfer_interface *xfer, int rc ) { + struct hw *hw = container_of ( xfer, struct hw, xfer ); + + hw_finished ( hw, rc ); +} + +static struct xfer_interface_operations hw_xfer_operations = { + .close = hw_xfer_close, + .vredirect = ignore_xfer_vredirect, + .window = unlimited_xfer_window, + .alloc_iob = default_xfer_alloc_iob, + .deliver_iob = xfer_deliver_as_raw, + .deliver_raw = ignore_xfer_deliver_raw, +}; + +static void hw_step ( struct process *process ) { + struct hw *hw = container_of ( process, struct hw, process ); + int rc; + + if ( xfer_window ( &hw->xfer ) ) { + rc = xfer_deliver_raw ( &hw->xfer, hw_msg, sizeof ( hw_msg ) ); + hw_finished ( hw, rc ); + } +} + +static int hw_open ( struct xfer_interface *xfer, struct uri *uri __unused ) { + struct hw *hw; + + /* Allocate and initialise structure */ + hw = zalloc ( sizeof ( *hw ) ); + if ( ! hw ) + return -ENOMEM; + xfer_init ( &hw->xfer, &hw_xfer_operations, &hw->refcnt ); + process_init ( &hw->process, hw_step, &hw->refcnt ); + + /* Attach parent interface, mortalise self, and return */ + xfer_plug_plug ( &hw->xfer, xfer ); + ref_put ( &hw->refcnt ); + return 0; +} + +struct uri_opener hw_uri_opener __uri_opener = { + .scheme = "hw", + .open = hw_open, +}; diff --git a/gpxe/src/core/i82365.c b/gpxe/src/core/i82365.c new file mode 100644 index 00000000..c26639e0 --- /dev/null +++ b/gpxe/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/gpxe/src/core/ibft.c b/gpxe/src/core/ibft.c new file mode 100644 index 00000000..fda14704 --- /dev/null +++ b/gpxe/src/core/ibft.c @@ -0,0 +1,341 @@ +/* + * Copyright Fen Systems Ltd. 2007. Portions of this code are derived + * from IBM Corporation Sample Programs. Copyright IBM Corporation + * 2004, 2007. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <byteswap.h> +#include <realmode.h> +#include <gpxe/pci.h> +#include <gpxe/acpi.h> +#include <gpxe/in.h> +#include <gpxe/netdevice.h> +#include <gpxe/dhcp.h> +#include <gpxe/iscsi.h> +#include <gpxe/ibft.h> + +/** @file + * + * iSCSI boot firmware table + * + * The information in this file is derived from the document "iSCSI + * Boot Firmware Table (iBFT)" as published by IBM at + * + * ftp://ftp.software.ibm.com/systems/support/system_x_pdf/ibm_iscsi_boot_firmware_table_v1.02.pdf + * + */ + +#define ibftab __use_data16 ( ibftab ) +/** The iBFT used by gPXE */ +struct gpxe_ibft __data16 ( ibftab ) = { + /* Table header */ + .table = { + /* ACPI header */ + .acpi = { + .signature = IBFT_SIG, + .length = sizeof ( ibftab ), + .revision = 1, + .oem_id = "FENSYS", + .oem_table_id = "gPXE", + }, + /* Control block */ + .control = { + .header = { + .structure_id = IBFT_STRUCTURE_ID_CONTROL, + .version = 1, + .length = sizeof ( ibftab.table.control ), + .flags = 0, + }, + .initiator = offsetof ( typeof ( ibftab ), initiator ), + .nic_0 = offsetof ( typeof ( ibftab ), nic ), + .target_0 = offsetof ( typeof ( ibftab ), target ), + }, + }, + /* iSCSI initiator information */ + .initiator = { + .header = { + .structure_id = IBFT_STRUCTURE_ID_INITIATOR, + .version = 1, + .length = sizeof ( ibftab.initiator ), + .flags = ( IBFT_FL_INITIATOR_BLOCK_VALID | + IBFT_FL_INITIATOR_FIRMWARE_BOOT_SELECTED ), + }, + }, + /* NIC information */ + .nic = { + .header = { + .structure_id = IBFT_STRUCTURE_ID_NIC, + .version = 1, + .length = sizeof ( ibftab.nic ), + .flags = ( IBFT_FL_NIC_BLOCK_VALID | + IBFT_FL_NIC_FIRMWARE_BOOT_SELECTED ), + }, + }, + /* iSCSI target information */ + .target = { + .header = { + .structure_id = IBFT_STRUCTURE_ID_TARGET, + .version = 1, + .length = sizeof ( ibftab.target ), + .flags = ( IBFT_FL_TARGET_BLOCK_VALID | + IBFT_FL_TARGET_FIRMWARE_BOOT_SELECTED ), + }, + }, +}; + +/** + * Fill in an IP address field within iBFT + * + * @v ipaddr IP address field + * @v in IPv4 address + */ +static void ibft_set_ipaddr ( struct ibft_ipaddr *ipaddr, struct in_addr in ) { + memset ( ipaddr, 0, sizeof ( ipaddr ) ); + if ( in.s_addr ) { + ipaddr->in = in; + ipaddr->ones = 0xffff; + } +} + +/** + * Fill in an IP address within iBFT from configuration setting + * + * @v ipaddr IP address field + * @v setting Configuration setting + * @v tag DHCP option tag + */ +static void ibft_set_ipaddr_option ( struct ibft_ipaddr *ipaddr, + struct setting *setting ) { + struct in_addr in = { 0 }; + fetch_ipv4_setting ( NULL, setting, &in ); + ibft_set_ipaddr ( ipaddr, in ); +} + +/** + * Allocate a string within iBFT + * + * @v strings iBFT string block descriptor + * @v string String field to fill in + * @v len Length of string to allocate (excluding NUL) + * @ret rc Return status code + */ +static int ibft_alloc_string ( struct ibft_string_block *strings, + struct ibft_string *string, size_t len ) { + char *dest; + unsigned int remaining; + + dest = ( ( ( char * ) strings->table ) + strings->offset ); + remaining = ( strings->table->acpi.length - strings->offset ); + if ( len >= remaining ) + return -ENOMEM; + + string->offset = strings->offset; + string->length = len; + strings->offset += ( len + 1 ); + return 0; +} + +/** + * Fill in a string field within iBFT + * + * @v strings iBFT string block descriptor + * @v string String field + * @v data String to fill in + * @ret rc Return status code + */ +static int ibft_set_string ( struct ibft_string_block *strings, + struct ibft_string *string, const char *data ) { + size_t len = strlen ( data ); + char *dest; + int rc; + + if ( ( rc = ibft_alloc_string ( strings, string, len ) ) != 0 ) + return rc; + dest = ( ( ( char * ) strings->table ) + string->offset ); + strcpy ( dest, data ); + + return 0; +} + +/** + * Fill in a string field within iBFT from configuration setting + * + * @v strings iBFT string block descriptor + * @v string String field + * @v setting Configuration setting + * @ret rc Return status code + */ +static int ibft_set_string_option ( struct ibft_string_block *strings, + struct ibft_string *string, + struct setting *setting ) { + int len; + char *dest; + int rc; + + len = fetch_setting_len ( NULL, setting ); + if ( len < 0 ) { + string->offset = 0; + string->length = 0; + return 0; + } + + if ( ( rc = ibft_alloc_string ( strings, string, len ) ) != 0 ) + return rc; + dest = ( ( ( char * ) strings->table ) + string->offset ); + fetch_string_setting ( NULL, setting, dest, ( len + 1 ) ); + return 0; +} + +/** + * Fill in NIC portion of iBFT + * + * @v nic NIC portion of iBFT + * @v strings iBFT string block descriptor + * @v netdev Network device + * @ret rc Return status code + */ +static int ibft_fill_nic ( struct ibft_nic *nic, + struct ibft_string_block *strings, + struct net_device *netdev ) { + struct in_addr netmask_addr = { 0 }; + unsigned int netmask_count = 0; + int rc; + + /* Extract values from DHCP configuration */ + ibft_set_ipaddr_option ( &nic->ip_address, &ip_setting ); + ibft_set_ipaddr_option ( &nic->gateway, &gateway_setting ); + ibft_set_ipaddr_option ( &nic->dns[0], &dns_setting ); + if ( ( rc = ibft_set_string_option ( strings, &nic->hostname, + &hostname_setting ) ) != 0 ) + return rc; + + /* Derive subnet mask prefix from subnet mask */ + fetch_ipv4_setting ( NULL, &netmask_setting, &netmask_addr ); + while ( netmask_addr.s_addr ) { + if ( netmask_addr.s_addr & 0x1 ) + netmask_count++; + netmask_addr.s_addr >>= 1; + } + nic->subnet_mask_prefix = netmask_count; + + /* Extract values from net-device configuration */ + memcpy ( nic->mac_address, netdev->ll_addr, + sizeof ( nic->mac_address ) ); + nic->pci_bus_dev_func = netdev->dev->desc.location; + + return 0; +} + +/** + * Fill in Initiator portion of iBFT + * + * @v initiator Initiator portion of iBFT + * @v strings iBFT string block descriptor + * @ret rc Return status code + */ +static int ibft_fill_initiator ( struct ibft_initiator *initiator, + struct ibft_string_block *strings ) { + const char *initiator_iqn = iscsi_initiator_iqn(); + int rc; + + if ( ( rc = ibft_set_string ( strings, &initiator->initiator_name, + initiator_iqn ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Fill in Target portion of iBFT + * + * @v target Target portion of iBFT + * @v strings iBFT string block descriptor + * @v iscsi iSCSI session + * @ret rc Return status code + */ +static int ibft_fill_target ( struct ibft_target *target, + struct ibft_string_block *strings, + struct iscsi_session *iscsi ) { + struct sockaddr_in *sin_target = + ( struct sockaddr_in * ) &iscsi->target_sockaddr; + int rc; + + /* Fill in Target values */ + ibft_set_ipaddr ( &target->ip_address, sin_target->sin_addr ); + target->socket = ntohs ( sin_target->sin_port ); + if ( ( rc = ibft_set_string ( strings, &target->target_name, + iscsi->target_iqn ) ) != 0 ) + return rc; + if ( iscsi->username ) { + if ( ( rc = ibft_set_string ( strings, &target->chap_name, + iscsi->username ) ) != 0 ) + return rc; + } + if ( iscsi->password ) { + if ( ( rc = ibft_set_string ( strings, &target->chap_secret, + iscsi->password ) ) != 0 ) + return rc; + target->chap_type = IBFT_CHAP_ONE_WAY; + } + + return 0; +} + +/** + * Fill in all variable portions of iBFT + * + * @v netdev Network device + * @v initiator_iqn Initiator IQN + * @v st_target Target socket address + * @v target_iqn Target IQN + * @ret rc Return status code + * + */ +int ibft_fill_data ( struct net_device *netdev, + struct iscsi_session *iscsi ) { + struct ibft_string_block strings = { + .table = &ibftab.table, + .offset = offsetof ( typeof ( ibftab ), strings ), + }; + int rc; + + /* Fill in NIC, Initiator and Target portions */ + if ( ( rc = ibft_fill_nic ( &ibftab.nic, &strings, netdev ) ) != 0 ) + return rc; + if ( ( rc = ibft_fill_initiator ( &ibftab.initiator, + &strings ) ) != 0 ) + return rc; + if ( ( rc = ibft_fill_target ( &ibftab.target, &strings, + iscsi ) ) != 0 ) + return rc; + + /* Update checksum */ + acpi_fix_checksum ( &ibftab.table.acpi ); + + return 0; +} diff --git a/gpxe/src/core/image.c b/gpxe/src/core/image.c new file mode 100644 index 00000000..440a68c9 --- /dev/null +++ b/gpxe/src/core/image.c @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2006 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. + */ + +#include <stddef.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <assert.h> +#include <libgen.h> +#include <gpxe/list.h> +#include <gpxe/umalloc.h> +#include <gpxe/uri.h> +#include <gpxe/image.h> + +/** @file + * + * Executable/loadable images + * + */ + +/** List of registered images */ +struct list_head images = LIST_HEAD_INIT ( images ); + +/** List of image types */ +static struct image_type image_types[0] + __table_start ( struct image_type, image_types ); +static struct image_type image_types_end[0] + __table_end ( struct image_type, image_types ); + +/** + * Free executable/loadable image + * + * @v refcnt Reference counter + */ +static void free_image ( struct refcnt *refcnt ) { + struct image *image = container_of ( refcnt, struct image, refcnt ); + + uri_put ( image->uri ); + ufree ( image->data ); + free ( image ); + DBGC ( image, "IMAGE %p freed\n", image ); +} + +/** + * Allocate executable/loadable image + * + * @ret image Executable/loadable image + */ +struct image * alloc_image ( void ) { + struct image *image; + + image = zalloc ( sizeof ( *image ) ); + if ( image ) { + image->refcnt.free = free_image; + } + return image; +} + +/** + * Set image URI + * + * @v image Image + * @v URI New image URI + * @ret rc Return status code + * + * If no name is set, the name will be updated to the base name of the + * URI path (if any). + */ +int image_set_uri ( struct image *image, struct uri *uri ) { + const char *path = uri->path; + + /* Replace URI reference */ + uri_put ( image->uri ); + image->uri = uri_get ( uri ); + + /* Set name if none already specified */ + if ( path && ( ! image->name[0] ) ) + image_set_name ( image, basename ( ( char * ) path ) ); + + return 0; +} + +/** + * Set image command line + * + * @v image Image + * @v cmdline New image command line + * @ret rc Return status code + */ +int image_set_cmdline ( struct image *image, const char *cmdline ) { + free ( image->cmdline ); + image->cmdline = strdup ( cmdline ); + if ( ! image->cmdline ) + return -ENOMEM; + return 0; +} + +/** + * Register executable/loadable image + * + * @v image Executable/loadable image + * @ret rc Return status code + */ +int register_image ( struct image *image ) { + static unsigned int imgindex = 0; + + /* Create image name if it doesn't already have one */ + if ( ! image->name[0] ) { + snprintf ( image->name, sizeof ( image->name ), "img%d", + imgindex++ ); + } + + /* Add to image list */ + image_get ( image ); + list_add_tail ( &image->list, &images ); + DBGC ( image, "IMAGE %p at [%lx,%lx) registered as %s\n", + image, user_to_phys ( image->data, 0 ), + user_to_phys ( image->data, image->len ), image->name ); + + return 0; +} + +/** + * Unregister executable/loadable image + * + * @v image Executable/loadable image + */ +void unregister_image ( struct image *image ) { + list_del ( &image->list ); + image_put ( image ); + DBGC ( image, "IMAGE %p unregistered\n", image ); +} + +/** + * Find image by name + * + * @v name Image name + * @ret image Executable/loadable image, or NULL + */ +struct image * find_image ( const char *name ) { + struct image *image; + + list_for_each_entry ( image, &images, list ) { + if ( strcmp ( image->name, name ) == 0 ) + return image; + } + + return NULL; +} + +/** + * Load executable/loadable image into memory + * + * @v image Executable/loadable image + * @v type Executable/loadable image type + * @ret rc Return status code + */ +static int image_load_type ( struct image *image, struct image_type *type ) { + int rc; + + /* Check image is actually loadable */ + if ( ! type->load ) + return -ENOEXEC; + + /* Try the image loader */ + if ( ( rc = type->load ( image ) ) != 0 ) { + DBGC ( image, "IMAGE %p could not load as %s: %s\n", + image, type->name, strerror ( rc ) ); + return rc; + } + + /* Flag as loaded */ + image->flags |= IMAGE_LOADED; + return 0; +} + +/** + * Load executable/loadable image into memory + * + * @v image Executable/loadable image + * @ret rc Return status code + */ +int image_load ( struct image *image ) { + + assert ( image->type != NULL ); + + return image_load_type ( image, image->type ); +} + +/** + * Autodetect image type and load executable/loadable image into memory + * + * @v image Executable/loadable image + * @ret rc Return status code + */ +int image_autoload ( struct image *image ) { + struct image_type *type; + int rc; + + /* If image already has a type, use it */ + if ( image->type ) + return image_load ( image ); + + /* Otherwise probe for a suitable type */ + for ( type = image_types ; type < image_types_end ; type++ ) { + DBGC ( image, "IMAGE %p trying type %s\n", image, type->name ); + rc = image_load_type ( image, type ); + if ( image->type == NULL ) + continue; + return rc; + } + + DBGC ( image, "IMAGE %p format not recognised\n", image ); + return -ENOEXEC; +} + +/** + * Execute loaded image + * + * @v image Loaded image + * @ret rc Return status code + */ +int image_exec ( struct image *image ) { + struct uri *old_cwuri; + int rc; + + /* Image must be loaded first */ + if ( ! ( image->flags & IMAGE_LOADED ) ) { + DBGC ( image, "IMAGE %p could not execute: not loaded\n", + image ); + return -ENOTTY; + } + + assert ( image->type != NULL ); + + /* Check that image is actually executable */ + if ( ! image->type->exec ) + return -ENOEXEC; + + /* Switch current working directory to be that of the image itself */ + old_cwuri = uri_get ( cwuri ); + churi ( image->uri ); + + /* Try executing the image */ + if ( ( rc = image->type->exec ( image ) ) != 0 ) { + DBGC ( image, "IMAGE %p could not execute: %s\n", + image, strerror ( rc ) ); + goto done; + } + + done: + /* Reset current working directory */ + churi ( old_cwuri ); + uri_put ( old_cwuri ); + + return rc; +} + +/** + * Register and autoload an image + * + * @v image Image + * @ret rc Return status code + */ +int register_and_autoload_image ( struct image *image ) { + int rc; + + if ( ( rc = register_image ( image ) ) != 0 ) + return rc; + + if ( ( rc = image_autoload ( image ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Register and autoexec an image + * + * @v image Image + * @ret rc Return status code + */ +int register_and_autoexec_image ( struct image *image ) { + int rc; + + if ( ( rc = register_and_autoload_image ( image ) ) != 0 ) + return rc; + + if ( ( rc = image_exec ( image ) ) != 0 ) + return rc; + + return 0; +} diff --git a/gpxe/src/core/init.c b/gpxe/src/core/init.c new file mode 100644 index 00000000..ed91bf36 --- /dev/null +++ b/gpxe/src/core/init.c @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2007 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. + */ + +#include <gpxe/device.h> +#include <gpxe/init.h> + +/** @file + * + * Initialisation, startup and shutdown routines + * + */ + +/** Registered initialisation functions */ +static struct init_fn init_fns[0] + __table_start ( struct init_fn, init_fns ); +static struct init_fn init_fns_end[0] + __table_end ( struct init_fn, init_fns ); + +/** Registered startup/shutdown functions */ +static struct startup_fn startup_fns[0] + __table_start ( struct startup_fn, startup_fns ); +static struct startup_fn startup_fns_end[0] + __table_end ( struct startup_fn, startup_fns ); + +/** "startup() has been called" flag */ +static int started = 0; + +/** + * Initialise gPXE + * + * This function performs the one-time-only and irreversible + * initialisation steps, such as initialising the heap. It must be + * called before (almost) any other function. + * + * There is, by definition, no counterpart to this function on the + * shutdown path. + */ +void initialise ( void ) { + struct init_fn *init_fn; + + /* Call registered initialisation functions */ + for ( init_fn = init_fns ; init_fn < init_fns_end ; init_fn++ ) { + init_fn->initialise (); + } +} + +/** + * Start up gPXE + * + * This function performs the repeatable initialisation steps, such as + * probing devices. You may call startup() and shutdown() multiple + * times (as is done via the PXE API when PXENV_START_UNDI is used). + */ +void startup ( void ) { + struct startup_fn *startup_fn; + + if ( started ) + return; + + /* Call registered startup functions */ + for ( startup_fn = startup_fns ; startup_fn < startup_fns_end ; + startup_fn++ ) { + if ( startup_fn->startup ) + startup_fn->startup(); + } + + /* Probe for all devices. Treated separately because nothing + * else will drag in device.o + */ + probe_devices(); + + started = 1; +} + +/** + * Shut down gPXE + * + * This function reverses the actions of startup(), and leaves gPXE in + * a state ready to be removed from memory. You may call startup() + * again after calling shutdown(). + + * Call this function only once, before either exiting main() or + * starting up a non-returnable image. + */ +void shutdown ( void ) { + struct startup_fn *startup_fn; + + if ( ! started ) + return; + + /* Remove all devices */ + remove_devices(); + + /* Call registered shutdown functions (in reverse order) */ + for ( startup_fn = startup_fns_end - 1 ; startup_fn >= startup_fns ; + startup_fn-- ) { + if ( startup_fn->shutdown ) + startup_fn->shutdown(); + } + + started = 0; +} diff --git a/gpxe/src/core/interface.c b/gpxe/src/core/interface.c new file mode 100644 index 00000000..37aabfe0 --- /dev/null +++ b/gpxe/src/core/interface.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2007 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. + */ + +#include <gpxe/interface.h> + +/** @file + * + * Object communication interfaces + * + */ + +/** + * Plug an interface into a new destination interface + * + * @v intf Interface + * @v dest New destination interface + * + * The reference to the existing destination interface is dropped, a + * reference to the new destination interface is obtained, and the + * interface is updated to point to the new destination interface. + * + * Note that there is no "unplug" call; instead you must plug the + * interface into a null interface. + */ +void plug ( struct interface *intf, struct interface *dest ) { + DBGC ( intf, "INTF %p moving from INTF %p to INTF %p\n", + intf, intf->dest, dest ); + intf_put ( intf->dest ); + intf->dest = intf_get ( dest ); +} + +/** + * Plug two interfaces together + * + * @v a Interface A + * @v b Interface B + * + * Plugs interface A into interface B, and interface B into interface + * A. (The basic plug() function is unidirectional; this function is + * merely a shorthand for two calls to plug(), hence the name.) + */ +void plug_plug ( struct interface *a, struct interface *b ) { + plug ( a, b ); + plug ( b, a ); +} diff --git a/gpxe/src/core/iobuf.c b/gpxe/src/core/iobuf.c new file mode 100644 index 00000000..cc4aedea --- /dev/null +++ b/gpxe/src/core/iobuf.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2006 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. + */ + +#include <stdint.h> +#include <errno.h> +#include <gpxe/malloc.h> +#include <gpxe/iobuf.h> + +/** @file + * + * I/O buffers + * + */ + +/** + * Allocate I/O buffer + * + * @v len Required length of buffer + * @ret iobuf I/O buffer, or NULL if none available + * + * The I/O buffer will be physically aligned to a multiple of + * @c IOBUF_SIZE. + */ +struct io_buffer * alloc_iob ( size_t len ) { + struct io_buffer *iobuf = NULL; + void *data; + + /* Pad to minimum length */ + if ( len < IOB_ZLEN ) + len = IOB_ZLEN; + + /* Align buffer length */ + len = ( len + __alignof__( *iobuf ) - 1 ) & + ~( __alignof__( *iobuf ) - 1 ); + + /* Allocate memory for buffer plus descriptor */ + data = malloc_dma ( len + sizeof ( *iobuf ), IOB_ALIGN ); + if ( ! data ) + return NULL; + + iobuf = ( struct io_buffer * ) ( data + len ); + iobuf->head = iobuf->data = iobuf->tail = data; + iobuf->end = iobuf; + return iobuf; +} + +/** + * Free I/O buffer + * + * @v iobuf I/O buffer + */ +void free_iob ( struct io_buffer *iobuf ) { + if ( iobuf ) { + assert ( iobuf->head <= iobuf->data ); + assert ( iobuf->data <= iobuf->tail ); + assert ( iobuf->tail <= iobuf->end ); + free_dma ( iobuf->head, + ( iobuf->end - iobuf->head ) + sizeof ( *iobuf ) ); + } +} + +/** + * Ensure I/O buffer has sufficient headroom + * + * @v iobuf I/O buffer + * @v len Required headroom + * + * This function currently only checks for the required headroom; it + * does not reallocate the I/O buffer if required. If we ever have a + * code path that requires this functionality, it's a fairly trivial + * change to make. + */ +int iob_ensure_headroom ( struct io_buffer *iobuf, size_t len ) { + + if ( iob_headroom ( iobuf ) >= len ) + return 0; + return -ENOBUFS; +} + diff --git a/gpxe/src/core/job.c b/gpxe/src/core/job.c new file mode 100644 index 00000000..6c2faf30 --- /dev/null +++ b/gpxe/src/core/job.c @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2007 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. + */ + +#include <string.h> +#include <errno.h> +#include <gpxe/job.h> + +/** @file + * + * Job control interfaces + * + */ + +void job_done ( struct job_interface *job, int rc ) { + struct job_interface *dest = job_get_dest ( job ); + + job_unplug ( job ); + dest->op->done ( dest, rc ); + job_put ( dest ); +} + +void job_kill ( struct job_interface *job ) { + struct job_interface *dest = job_get_dest ( job ); + + job_unplug ( job ); + dest->op->kill ( dest ); + job_put ( dest ); +} + +/**************************************************************************** + * + * Helper methods + * + * These functions are designed to be used as methods in the + * job_interface_operations table. + * + */ + +void ignore_job_done ( struct job_interface *job __unused, int rc __unused ) { + /* Nothing to do */ +} + +void ignore_job_kill ( struct job_interface *job __unused ) { + /* Nothing to do */ +} + +void ignore_job_progress ( struct job_interface *job __unused, + struct job_progress *progress ) { + memset ( progress, 0, sizeof ( *progress ) ); +} + +/** Null job control interface operations */ +struct job_interface_operations null_job_ops = { + .done = ignore_job_done, + .kill = ignore_job_kill, + .progress = ignore_job_progress, +}; + +/** + * Null job control interface + * + * This is the interface to which job control interfaces are connected + * when unplugged. It will never generate messages, and will silently + * absorb all received messages. + */ +struct job_interface null_job = { + .intf = { + .dest = &null_job.intf, + .refcnt = NULL, + }, + .op = &null_job_ops, +}; diff --git a/gpxe/src/core/linebuf.c b/gpxe/src/core/linebuf.c new file mode 100644 index 00000000..d02f37c3 --- /dev/null +++ b/gpxe/src/core/linebuf.c @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2007 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. + */ + +/** + * @file + * + * Line buffering + * + */ + +#include <stdint.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <gpxe/linebuf.h> + +/** + * Retrieve buffered-up line + * + * @v linebuf Line buffer + * @ret line Buffered line, or NULL if no line ready to read + */ +char * buffered_line ( struct line_buffer *linebuf ) { + return ( linebuf->ready ? linebuf->data : NULL ); +} + +/** + * Discard line buffer contents + * + * @v linebuf Line buffer + */ +void empty_line_buffer ( struct line_buffer *linebuf ) { + free ( linebuf->data ); + linebuf->data = NULL; + linebuf->len = 0; + linebuf->ready = 0; +} + +/** + * Buffer up received data by lines + * + * @v linebuf Line buffer + * @v data New data to add + * @v len Length of new data to add + * @ret len Consumed length, or negative error number + * + * After calling line_buffer(), use buffered_line() to determine + * whether or not a complete line is available. Carriage returns and + * newlines will have been stripped, and the line will be + * NUL-terminated. This buffered line is valid only until the next + * call to line_buffer() (or to empty_line_buffer()). + * + * Note that line buffers use dynamically allocated storage; you + * should call empty_line_buffer() before freeing a @c struct @c + * line_buffer. + */ +ssize_t line_buffer ( struct line_buffer *linebuf, + const char *data, size_t len ) { + const char *eol; + size_t consume; + size_t new_len; + char *new_data; + + /* Free any completed line from previous iteration */ + if ( linebuf->ready ) + empty_line_buffer ( linebuf ); + + /* Search for line terminator */ + if ( ( eol = memchr ( data, '\n', len ) ) ) { + consume = ( eol - data + 1 ); + } else { + consume = len; + } + + /* Reallocate data buffer and copy in new data */ + new_len = ( linebuf->len + consume ); + new_data = realloc ( linebuf->data, ( new_len + 1 ) ); + if ( ! new_data ) + return -ENOMEM; + memcpy ( ( new_data + linebuf->len ), data, consume ); + new_data[new_len] = '\0'; + linebuf->data = new_data; + linebuf->len = new_len; + + /* If we have reached end of line, trim the line and mark as ready */ + if ( eol ) { + linebuf->data[--linebuf->len] = '\0'; /* trim NL */ + if ( linebuf->data[linebuf->len - 1] == '\r' ) + linebuf->data[--linebuf->len] = '\0'; /* trim CR */ + linebuf->ready = 1; + } + + return consume; +} diff --git a/gpxe/src/core/main.c b/gpxe/src/core/main.c new file mode 100644 index 00000000..3295feaf --- /dev/null +++ b/gpxe/src/core/main.c @@ -0,0 +1,40 @@ +/************************************************************************** +gPXE - 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 + +**************************************************************************/ + +#include <gpxe/init.h> +#include <gpxe/shell.h> +#include <gpxe/shell_banner.h> +#include <usr/autoboot.h> + +/** + * Main entry point + * + * @ret rc Return status code + */ +__cdecl int main ( void ) { + + initialise(); + startup(); + + if ( shell_banner() ) + shell(); + else + autoboot(); + + shutdown(); + + return 0; +} diff --git a/gpxe/src/core/malloc.c b/gpxe/src/core/malloc.c new file mode 100644 index 00000000..2d892f42 --- /dev/null +++ b/gpxe/src/core/malloc.c @@ -0,0 +1,384 @@ +/* + * Copyright (C) 2006 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. + */ + +#include <stddef.h> +#include <stdint.h> +#include <string.h> +#include <strings.h> +#include <io.h> +#include <gpxe/list.h> +#include <gpxe/init.h> +#include <gpxe/malloc.h> + +/** @file + * + * Dynamic memory allocation + * + */ + +/** A free block of memory */ +struct memory_block { + /** List of free blocks */ + struct list_head list; + /** Size of this block */ + size_t size; +}; + +#define MIN_MEMBLOCK_SIZE \ + ( ( size_t ) ( 1 << ( fls ( sizeof ( struct memory_block ) - 1 ) ) ) ) + +/** A block of allocated memory complete with size information */ +struct autosized_block { + /** Size of this block */ + size_t size; + /** Remaining data */ + char data[0]; +}; + +/** + * Address for zero-length memory blocks + * + * @c malloc(0) or @c realloc(ptr,0) will return the special value @c + * NOWHERE. Calling @c free(NOWHERE) will have no effect. + * + * This is consistent with the ANSI C standards, which state that + * "either NULL or a pointer suitable to be passed to free()" must be + * returned in these cases. Using a special non-NULL value means that + * the caller can take a NULL return value to indicate failure, + * without first having to check for a requested size of zero. + * + * Code outside of malloc.c do not ever need to refer to the actual + * value of @c NOWHERE; this is an internal definition. + */ +#define NOWHERE ( ( void * ) ~( ( intptr_t ) 0 ) ) + +/** List of free memory blocks */ +static LIST_HEAD ( free_blocks ); + +/** Total amount of free memory */ +size_t freemem; + +/** + * Heap size + * + * Currently fixed at 128kB. + */ +#define HEAP_SIZE ( 128 * 1024 ) + +/** The heap itself */ +static char heap[HEAP_SIZE] __attribute__ (( aligned ( __alignof__(void *) ))); + +/** + * Allocate a memory block + * + * @v size Requested size + * @v align Physical alignment + * @ret ptr Memory block, or NULL + * + * Allocates a memory block @b physically aligned as requested. No + * guarantees are provided for the alignment of the virtual address. + * + * @c align must be a power of two. @c size may not be zero. + */ +void * alloc_memblock ( size_t size, size_t align ) { + struct memory_block *block; + size_t align_mask; + size_t pre_size; + ssize_t post_size; + struct memory_block *pre; + struct memory_block *post; + + /* Round up size to multiple of MIN_MEMBLOCK_SIZE and + * calculate alignment mask. + */ + size = ( size + MIN_MEMBLOCK_SIZE - 1 ) & ~( MIN_MEMBLOCK_SIZE - 1 ); + align_mask = ( align - 1 ) | ( MIN_MEMBLOCK_SIZE - 1 ); + + DBG ( "Allocating %#zx (aligned %#zx)\n", size, align ); + + /* Search through blocks for the first one with enough space */ + list_for_each_entry ( block, &free_blocks, list ) { + pre_size = ( - virt_to_phys ( block ) ) & align_mask; + post_size = block->size - pre_size - size; + if ( post_size >= 0 ) { + /* Split block into pre-block, block, and + * post-block. After this split, the "pre" + * block is the one currently linked into the + * free list. + */ + pre = block; + block = ( ( ( void * ) pre ) + pre_size ); + post = ( ( ( void * ) block ) + size ); + DBG ( "[%p,%p) -> [%p,%p) + [%p,%p)\n", pre, + ( ( ( void * ) pre ) + pre->size ), pre, block, + post, ( ( ( void * ) pre ) + pre->size ) ); + /* If there is a "post" block, add it in to + * the free list. Leak it if it is too small + * (which can happen only at the very end of + * the heap). + */ + if ( ( size_t ) post_size >= MIN_MEMBLOCK_SIZE ) { + post->size = post_size; + list_add ( &post->list, &pre->list ); + } + /* Shrink "pre" block, leaving the main block + * isolated and no longer part of the free + * list. + */ + pre->size = pre_size; + /* If there is no "pre" block, remove it from + * the list. Also remove it (i.e. leak it) if + * it is too small, which can happen only at + * the very start of the heap. + */ + if ( pre_size < MIN_MEMBLOCK_SIZE ) + list_del ( &pre->list ); + /* Update total free memory */ + freemem -= size; + /* Return allocated block */ + DBG ( "Allocated [%p,%p)\n", block, + ( ( ( void * ) block ) + size ) ); + return block; + } + } + + DBG ( "Failed to allocate %#zx (aligned %#zx)\n", size, align ); + return NULL; +} + +/** + * Free a memory block + * + * @v ptr Memory allocated by alloc_memblock(), or NULL + * @v size Size of the memory + * + * If @c ptr is NULL, no action is taken. + */ +void free_memblock ( void *ptr, size_t size ) { + struct memory_block *freeing; + struct memory_block *block; + ssize_t gap_before; + ssize_t gap_after = -1; + + /* Allow for ptr==NULL */ + if ( ! ptr ) + return; + + /* Round up size to match actual size that alloc_memblock() + * would have used. + */ + size = ( size + MIN_MEMBLOCK_SIZE - 1 ) & ~( MIN_MEMBLOCK_SIZE - 1 ); + freeing = ptr; + freeing->size = size; + DBG ( "Freeing [%p,%p)\n", freeing, ( ( ( void * ) freeing ) + size )); + + /* Insert/merge into free list */ + list_for_each_entry ( block, &free_blocks, list ) { + /* Calculate gaps before and after the "freeing" block */ + gap_before = ( ( ( void * ) freeing ) - + ( ( ( void * ) block ) + block->size ) ); + gap_after = ( ( ( void * ) block ) - + ( ( ( void * ) freeing ) + freeing->size ) ); + /* Merge with immediately preceding block, if possible */ + if ( gap_before == 0 ) { + DBG ( "[%p,%p) + [%p,%p) -> [%p,%p)\n", block, + ( ( ( void * ) block ) + block->size ), freeing, + ( ( ( void * ) freeing ) + freeing->size ),block, + ( ( ( void * ) freeing ) + freeing->size ) ); + block->size += size; + list_del ( &block->list ); + freeing = block; + } + /* Stop processing as soon as we reach a following block */ + if ( gap_after >= 0 ) + break; + } + + /* Insert before the immediately following block. If + * possible, merge the following block into the "freeing" + * block. + */ + DBG ( "[%p,%p)\n", freeing, ( ( ( void * ) freeing ) + freeing->size)); + list_add_tail ( &freeing->list, &block->list ); + if ( gap_after == 0 ) { + DBG ( "[%p,%p) + [%p,%p) -> [%p,%p)\n", freeing, + ( ( ( void * ) freeing ) + freeing->size ), block, + ( ( ( void * ) block ) + block->size ), freeing, + ( ( ( void * ) block ) + block->size ) ); + freeing->size += block->size; + list_del ( &block->list ); + } + + /* Update free memory counter */ + freemem += size; +} + +/** + * Reallocate memory + * + * @v old_ptr Memory previously allocated by malloc(), or NULL + * @v new_size Requested size + * @ret new_ptr Allocated memory, or NULL + * + * Allocates memory with no particular alignment requirement. @c + * new_ptr will be aligned to at least a multiple of sizeof(void*). + * If @c old_ptr is non-NULL, then the contents of the newly allocated + * memory will be the same as the contents of the previously allocated + * memory, up to the minimum of the old and new sizes. The old memory + * will be freed. + * + * If allocation fails the previously allocated block is left + * untouched and NULL is returned. + * + * Calling realloc() with a new size of zero is a valid way to free a + * memory block. + */ +void * realloc ( void *old_ptr, size_t new_size ) { + struct autosized_block *old_block; + struct autosized_block *new_block; + size_t old_total_size; + size_t new_total_size; + size_t old_size; + void *new_ptr = NOWHERE; + + /* Allocate new memory if necessary. If allocation fails, + * return without touching the old block. + */ + if ( new_size ) { + new_total_size = ( new_size + + offsetof ( struct autosized_block, data ) ); + new_block = alloc_memblock ( new_total_size, 1 ); + if ( ! new_block ) + return NULL; + new_block->size = new_total_size; + new_ptr = &new_block->data; + } + + /* Copy across relevant part of the old data region (if any), + * then free it. Note that at this point either (a) new_ptr + * is valid, or (b) new_size is 0; either way, the memcpy() is + * valid. + */ + if ( old_ptr && ( old_ptr != NOWHERE ) ) { + old_block = container_of ( old_ptr, struct autosized_block, + data ); + old_total_size = old_block->size; + old_size = ( old_total_size - + offsetof ( struct autosized_block, data ) ); + memcpy ( new_ptr, old_ptr, + ( ( old_size < new_size ) ? old_size : new_size ) ); + free_memblock ( old_block, old_total_size ); + } + + return new_ptr; +} + +/** + * Allocate memory + * + * @v size Requested size + * @ret ptr Memory, or NULL + * + * Allocates memory with no particular alignment requirement. @c ptr + * will be aligned to at least a multiple of sizeof(void*). + */ +void * malloc ( size_t size ) { + return realloc ( NULL, size ); +} + +/** + * Free memory + * + * @v ptr Memory allocated by malloc(), or NULL + * + * Memory allocated with malloc_dma() cannot be freed with free(); it + * must be freed with free_dma() instead. + * + * If @c ptr is NULL, no action is taken. + */ +void free ( void *ptr ) { + realloc ( ptr, 0 ); +} + +/** + * Allocate cleared memory + * + * @v size Requested size + * @ret ptr Allocated memory + * + * Allocate memory as per malloc(), and zero it. + * + * This function name is non-standard, but pretty intuitive. + * zalloc(size) is always equivalent to calloc(1,size) + */ +void * zalloc ( size_t size ) { + void *data; + + data = malloc ( size ); + if ( data ) + memset ( data, 0, size ); + return data; +} + +/** + * Add memory to allocation pool + * + * @v start Start address + * @v end End address + * + * Adds a block of memory [start,end) to the allocation pool. This is + * a one-way operation; there is no way to reclaim this memory. + * + * @c start must be aligned to at least a multiple of sizeof(void*). + */ +void mpopulate ( void *start, size_t len ) { + /* Prevent free_memblock() from rounding up len beyond the end + * of what we were actually given... + */ + free_memblock ( start, ( len & ~( MIN_MEMBLOCK_SIZE - 1 ) ) ); +} + +/** + * Initialise the heap + * + */ +static void init_heap ( void ) { + mpopulate ( heap, sizeof ( heap ) ); +} + +/** Memory allocator initialisation function */ +struct init_fn heap_init_fn __init_fn ( INIT_EARLY ) = { + .initialise = init_heap, +}; + +#if 0 +#include <stdio.h> +/** + * Dump free block list + * + */ +void mdumpfree ( void ) { + struct memory_block *block; + + printf ( "Free block list:\n" ); + list_for_each_entry ( block, &free_blocks, list ) { + printf ( "[%p,%p] (size %#zx)\n", block, + ( ( ( void * ) block ) + block->size ), block->size ); + } +} +#endif diff --git a/gpxe/src/core/misc.c b/gpxe/src/core/misc.c new file mode 100644 index 00000000..1f51272d --- /dev/null +++ b/gpxe/src/core/misc.c @@ -0,0 +1,91 @@ +/************************************************************************** +MISC Support Routines +**************************************************************************/ + +#include <stdlib.h> +#include <byteswap.h> +#include <gpxe/in.h> +#include <gpxe/timer.h> + +/************************************************************************** +INET_ATON - Convert an ascii x.x.x.x to binary form +**************************************************************************/ +int inet_aton ( const char *cp, struct in_addr *inp ) { + const char *p = cp; + 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, ( char ** ) &p, 10); + if ((p == digits_start) || (val > 255)) return 0; + if ( ( j < 3 ) && ( *(p++) != '.' ) ) return 0; + ip = (ip << 8) | val; + } + if ( *p == '\0' ) { + inp->s_addr = htonl(ip); + return 1; + } + return 0; +} + +int isspace ( int c ) { + switch ( c ) { + case ' ': + case '\f': + case '\n': + case '\r': + case '\t': + case '\v': + return 1; + default: + return 0; + } +} + +unsigned long strtoul ( const char *p, char **endp, int base ) { + unsigned long ret = 0; + unsigned int charval; + + while ( isspace ( *p ) ) + p++; + + if ( base == 0 ) { + base = 10; + if ( *p == '0' ) { + p++; + base = 8; + if ( ( *p | 0x20 ) == 'x' ) { + p++; + base = 16; + } + } + } + + while ( 1 ) { + charval = *p; + if ( charval >= 'a' ) { + charval = ( charval - 'a' + 10 ); + } else if ( charval >= 'A' ) { + charval = ( charval - 'A' + 10 ); + } else if ( charval <= '9' ) { + charval = ( charval - '0' ); + } + if ( charval >= ( unsigned int ) base ) + break; + ret = ( ( ret * base ) + charval ); + p++; + } + + if ( endp ) + *endp = ( char * ) p; + + return ( ret ); +} + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/gpxe/src/core/monojob.c b/gpxe/src/core/monojob.c new file mode 100644 index 00000000..ea9bc834 --- /dev/null +++ b/gpxe/src/core/monojob.c @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2007 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. + */ + +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <gpxe/process.h> +#include <console.h> +#include <gpxe/keys.h> +#include <gpxe/job.h> +#include <gpxe/monojob.h> + +/** @file + * + * Single foreground job + * + */ + +static int monojob_rc; + +static void monojob_done ( struct job_interface *job __unused, int rc ) { + monojob_rc = rc; +} + +/** Single foreground job operations */ +static struct job_interface_operations monojob_operations = { + .done = monojob_done, + .kill = ignore_job_kill, + .progress = ignore_job_progress, +}; + +/** Single foreground job */ +struct job_interface monojob = { + .intf = { + .dest = &null_job.intf, + .refcnt = NULL, + }, + .op = &monojob_operations, +}; + +/** + * Wait for single foreground job to complete + * + * @v string Job description to display + * @ret rc Job final status code + */ +int monojob_wait ( const char *string ) { + int key; + int rc; + + printf ( "%s... ", string ); + monojob_rc = -EINPROGRESS; + while ( monojob_rc == -EINPROGRESS ) { + step(); + if ( iskey() ) { + key = getchar(); + switch ( key ) { + case CTRL_C: + job_kill ( &monojob ); + rc = -ECANCELED; + goto done; + default: + break; + } + } + } + rc = monojob_rc; + +done: + if ( rc ) { + printf ( "%s\n", strerror ( rc ) ); + } else { + printf ( "ok\n" ); + } + return rc; +} diff --git a/gpxe/src/core/nvo.c b/gpxe/src/core/nvo.c new file mode 100644 index 00000000..13078022 --- /dev/null +++ b/gpxe/src/core/nvo.c @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2006 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. + */ + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <gpxe/dhcp.h> +#include <gpxe/nvs.h> +#include <gpxe/nvo.h> + +/** @file + * + * Non-volatile stored options + * + */ + +/** + * Calculate checksum over non-volatile stored options + * + * @v nvo Non-volatile options block + * @ret sum Checksum + */ +static unsigned int nvo_checksum ( struct nvo_block *nvo ) { + uint8_t *data = nvo->data; + uint8_t sum = 0; + unsigned int i; + + for ( i = 0 ; i < nvo->total_len ; i++ ) { + sum += *(data++); + } + return sum; +} + +/** + * Load non-volatile stored options from non-volatile storage device + * + * @v nvo Non-volatile options block + * @ret rc Return status code + */ +static int nvo_load ( struct nvo_block *nvo ) { + void *data = nvo->data; + struct nvo_fragment *frag; + int rc; + + /* Read data a fragment at a time */ + for ( frag = nvo->fragments ; frag->len ; frag++ ) { + if ( ( rc = nvs_read ( nvo->nvs, frag->address, data, + frag->len ) ) != 0 ) { + DBGC ( nvo, "NVO %p could not read %zd bytes at " + "%#04x\n", nvo, frag->len, frag->address ); + return rc; + } + data += frag->len; + } + + DBGC ( nvo, "NVO %p loaded from non-volatile storage\n", nvo ); + return 0; +} + +/** + * Save non-volatile stored options back to non-volatile storage device + * + * @v nvo Non-volatile options block + * @ret rc Return status code + */ +static int nvo_save ( struct nvo_block *nvo ) { + void *data = nvo->data; + uint8_t *checksum = data; + struct nvo_fragment *frag; + int rc; + + /* Recalculate checksum */ + *checksum -= nvo_checksum ( nvo ); + + /* Write data a fragment at a time */ + for ( frag = nvo->fragments ; frag->len ; frag++ ) { + if ( ( rc = nvs_write ( nvo->nvs, frag->address, data, + frag->len ) ) != 0 ) { + DBGC ( nvo, "NVO %p could not write %zd bytes at " + "%#04x\n", nvo, frag->len, frag->address ); + return rc; + } + data += frag->len; + } + + DBGC ( nvo, "NVO %p saved to non-volatile storage\n", nvo ); + return 0; +} + +/** + * Parse stored options + * + * @v nvo Non-volatile options block + * + * Verifies that the options data is valid, and configures the DHCP + * options block. If the data is not valid, it is replaced with an + * empty options block. + */ +static void nvo_init_dhcpopts ( struct nvo_block *nvo ) { + uint8_t *options_data; + size_t options_len; + + /* Steal one byte for the checksum */ + options_data = ( nvo->data + 1 ); + options_len = ( nvo->total_len - 1 ); + + /* If checksum fails, or options data starts with a zero, + * assume the whole block is invalid. This should capture the + * case of random initial contents. + */ + if ( ( nvo_checksum ( nvo ) != 0 ) || ( options_data[0] == 0 ) ) { + DBGC ( nvo, "NVO %p has checksum %02x and initial byte %02x; " + "assuming empty\n", nvo, nvo_checksum ( nvo ), + options_data[0] ); + memset ( nvo->data, 0, nvo->total_len ); + } + + dhcpopt_init ( &nvo->dhcpopts, options_data, options_len ); +} + +/** + * Store value of NVO setting + * + * @v settings Settings block + * @v setting Setting to store + * @v data Setting data, or NULL to clear setting + * @v len Length of setting data + * @ret rc Return status code + */ +static int nvo_store ( struct settings *settings, struct setting *setting, + const void *data, size_t len ) { + struct nvo_block *nvo = + container_of ( settings, struct nvo_block, settings ); + int rc; + + /* Update stored options */ + if ( ( rc = dhcpopt_store ( &nvo->dhcpopts, setting->tag, + data, len ) ) != 0 ) { + DBGC ( nvo, "NVO %p could not store %zd bytes: %s\n", + nvo, len, strerror ( rc ) ); + return rc; + } + + /* Save updated options to NVS */ + if ( ( rc = nvo_save ( nvo ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Fetch value of NVO setting + * + * @v settings Settings block + * @v setting Setting to fetch + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + * + * The actual length of the setting will be returned even if + * the buffer was too small. + */ +static int nvo_fetch ( struct settings *settings, struct setting *setting, + void *data, size_t len ) { + struct nvo_block *nvo = + container_of ( settings, struct nvo_block, settings ); + + return dhcpopt_fetch ( &nvo->dhcpopts, setting->tag, data, len ); +} + +/** NVO settings operations */ +static struct settings_operations nvo_settings_operations = { + .store = nvo_store, + .fetch = nvo_fetch, +}; + +/** + * Initialise non-volatile stored options + * + * @v nvo Non-volatile options block + * @v nvs Underlying non-volatile storage device + * @v fragments List of option-containing fragments + * @v refcnt Containing object reference counter, or NULL + */ +void nvo_init ( struct nvo_block *nvo, struct nvs_device *nvs, + struct nvo_fragment *fragments, struct refcnt *refcnt ) { + nvo->nvs = nvs; + nvo->fragments = fragments; + settings_init ( &nvo->settings, &nvo_settings_operations, refcnt, + "nvo" ); +} + +/** + * Register non-volatile stored options + * + * @v nvo Non-volatile options block + * @v parent Parent settings block, or NULL + * @ret rc Return status code + */ +int register_nvo ( struct nvo_block *nvo, struct settings *parent ) { + struct nvo_fragment *fragment = nvo->fragments; + int rc; + + /* Calculate total length of all fragments */ + for ( fragment = nvo->fragments ; fragment->len ; fragment++ ) + nvo->total_len += fragment->len; + + /* Allocate memory for options and read in from NVS */ + nvo->data = malloc ( nvo->total_len ); + if ( ! nvo->data ) { + DBGC ( nvo, "NVO %p could not allocate %zd bytes\n", + nvo, nvo->total_len ); + rc = -ENOMEM; + goto err_malloc; + } + if ( ( rc = nvo_load ( nvo ) ) != 0 ) + goto err_load; + + /* Verify and register options */ + nvo_init_dhcpopts ( nvo ); + if ( ( rc = register_settings ( &nvo->settings, parent ) ) != 0 ) + goto err_register; + + DBGC ( nvo, "NVO %p registered\n", nvo ); + return 0; + + err_register: + err_load: + free ( nvo->data ); + nvo->data = NULL; + err_malloc: + return rc; +} + +/** + * Unregister non-volatile stored options + * + * @v nvo Non-volatile options block + */ +void unregister_nvo ( struct nvo_block *nvo ) { + unregister_settings ( &nvo->settings ); + free ( nvo->data ); + nvo->data = NULL; + DBGC ( nvo, "NVO %p unregistered\n", nvo ); +} diff --git a/gpxe/src/core/open.c b/gpxe/src/core/open.c new file mode 100644 index 00000000..db8d92e6 --- /dev/null +++ b/gpxe/src/core/open.c @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2007 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. + */ + +#include <stdarg.h> +#include <string.h> +#include <errno.h> +#include <gpxe/xfer.h> +#include <gpxe/uri.h> +#include <gpxe/socket.h> +#include <gpxe/open.h> + +/** @file + * + * Data transfer interface opening + * + */ + +/** Registered URI openers */ +static struct uri_opener uri_openers[0] + __table_start ( struct uri_opener, uri_openers ); +static struct uri_opener uri_openers_end[0] + __table_end ( struct uri_opener, uri_openers ); + +/** Registered socket openers */ +static struct socket_opener socket_openers[0] + __table_start ( struct socket_opener, socket_openers ); +static struct socket_opener socket_openers_end[0] + __table_end ( struct socket_opener, socket_openers ); + +/** + * Open URI + * + * @v xfer Data transfer interface + * @v uri URI + * @ret rc Return status code + * + * The URI will be regarded as being relative to the current working + * URI (see churi()). + */ +int xfer_open_uri ( struct xfer_interface *xfer, struct uri *uri ) { + struct uri_opener *opener; + struct uri *resolved_uri; + int rc = -ENOTSUP; + + /* Resolve URI */ + resolved_uri = resolve_uri ( cwuri, uri ); + if ( ! resolved_uri ) + return -ENOMEM; + + /* Find opener which supports this URI scheme */ + for ( opener = uri_openers ; opener < uri_openers_end ; opener++ ) { + if ( strcmp ( resolved_uri->scheme, opener->scheme ) == 0 ) { + rc = opener->open ( xfer, resolved_uri ); + goto done; + } + } + DBGC ( xfer, "XFER %p attempted to open unsupported URI scheme " + "\"%s\"\n", xfer, resolved_uri->scheme ); + + done: + uri_put ( resolved_uri ); + return rc; +} + +/** + * Open URI string + * + * @v xfer Data transfer interface + * @v uri_string URI string (e.g. "http://etherboot.org/kernel") + * @ret rc Return status code + * + * The URI will be regarded as being relative to the current working + * URI (see churi()). + */ +int xfer_open_uri_string ( struct xfer_interface *xfer, + const char *uri_string ) { + struct uri *uri; + int rc; + + DBGC ( xfer, "XFER %p opening URI %s\n", xfer, uri_string ); + + uri = parse_uri ( uri_string ); + if ( ! uri ) + return -ENOMEM; + + rc = xfer_open_uri ( xfer, uri ); + + uri_put ( uri ); + return rc; +} + +/** + * Open socket + * + * @v xfer Data transfer interface + * @v semantics Communication semantics (e.g. SOCK_STREAM) + * @v peer Peer socket address + * @v local Local socket address, or NULL + * @ret rc Return status code + */ +int xfer_open_socket ( struct xfer_interface *xfer, int semantics, + struct sockaddr *peer, struct sockaddr *local ) { + struct socket_opener *opener; + + DBGC ( xfer, "XFER %p opening (%s,%s) socket\n", xfer, + socket_semantics_name ( semantics ), + socket_family_name ( peer->sa_family ) ); + + for ( opener = socket_openers; opener < socket_openers_end; opener++ ){ + if ( ( opener->semantics == semantics ) && + ( opener->family == peer->sa_family ) ) { + return opener->open ( xfer, peer, local ); + } + } + + DBGC ( xfer, "XFER %p attempted to open unsupported socket type " + "(%s,%s)\n", xfer, socket_semantics_name ( semantics ), + socket_family_name ( peer->sa_family ) ); + return -ENOTSUP; +} + +/** + * Open location + * + * @v xfer Data transfer interface + * @v type Location type + * @v args Remaining arguments depend upon location type + * @ret rc Return status code + */ +int xfer_vopen ( struct xfer_interface *xfer, int type, va_list args ) { + switch ( type ) { + case LOCATION_URI_STRING: { + const char *uri_string = va_arg ( args, const char * ); + + return xfer_open_uri_string ( xfer, uri_string ); } + case LOCATION_URI: { + struct uri *uri = va_arg ( args, struct uri * ); + + return xfer_open_uri ( xfer, uri ); } + case LOCATION_SOCKET: { + int semantics = va_arg ( args, int ); + struct sockaddr *peer = va_arg ( args, struct sockaddr * ); + struct sockaddr *local = va_arg ( args, struct sockaddr * ); + + return xfer_open_socket ( xfer, semantics, peer, local ); } + default: + DBGC ( xfer, "XFER %p attempted to open unsupported location " + "type %d\n", xfer, type ); + return -ENOTSUP; + } +} + +/** + * Open location + * + * @v xfer Data transfer interface + * @v type Location type + * @v ... Remaining arguments depend upon location type + * @ret rc Return status code + */ +int xfer_open ( struct xfer_interface *xfer, int type, ... ) { + va_list args; + int rc; + + va_start ( args, type ); + rc = xfer_vopen ( xfer, type, args ); + va_end ( args ); + return rc; +} diff --git a/gpxe/src/core/pc_kbd.c b/gpxe/src/core/pc_kbd.c new file mode 100644 index 00000000..d43357fe --- /dev/null +++ b/gpxe/src/core/pc_kbd.c @@ -0,0 +1,112 @@ +/* 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 + */ + +#include "io.h" +#include "console.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; +} + +static int kbd_havekey(void) +{ + if (!cur_scan) + cur_scan = get_scancode(); + return cur_scan != 0; +} + +static int kbd_ischar(void) +{ + if (!kbd_havekey()) + return 0; + if (!key_map[shift_state & SHIFT][cur_scan]) { + cur_scan = 0; + return 0; + } + return 1; +} + +static 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; +} + +struct console_driver pc_kbd_console __console_driver = { + .getchar = kbd_getc, +}; diff --git a/gpxe/src/core/pcmcia.c b/gpxe/src/core/pcmcia.c new file mode 100644 index 00000000..631971ef --- /dev/null +++ b/gpxe/src/core/pcmcia.c @@ -0,0 +1,267 @@ +#if 0 + +/* + * 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 <stdio.h> +#include <pcmcia.h> +#include <i82365.h> +#define CODE_STATUS "alpha" +#define CODE_VERSION "0.1.3" +#include <pcmcia-opts.h> +#include <console.h> +#include <gpxe/init.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; ) { + } + return; +} + +static void 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(); + } + +} + +static void 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"); +} + +#endif diff --git a/gpxe/src/core/posix_io.c b/gpxe/src/core/posix_io.c new file mode 100644 index 00000000..e0459bdf --- /dev/null +++ b/gpxe/src/core/posix_io.c @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2007 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. + */ + +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <gpxe/list.h> +#include <gpxe/xfer.h> +#include <gpxe/open.h> +#include <gpxe/process.h> +#include <gpxe/posix_io.h> + +/** @file + * + * POSIX-like I/O + * + * These functions provide traditional blocking I/O semantics. They + * are designed to be used by the PXE TFTP API. Because they block, + * they may not be used by most other portions of the gPXE codebase. + */ + +/** An open file */ +struct posix_file { + /** Reference count for this object */ + struct refcnt refcnt; + /** List of open files */ + struct list_head list; + /** File descriptor */ + int fd; + /** Overall status + * + * Set to -EINPROGRESS while data transfer is in progress. + */ + int rc; + /** Data transfer interface */ + struct xfer_interface xfer; + /** Current seek position */ + size_t pos; + /** File size */ + size_t filesize; + /** Received data queue */ + struct list_head data; +}; + +/** List of open files */ +static LIST_HEAD ( posix_files ); + +/** + * Free open file + * + * @v refcnt Reference counter + */ +static void posix_file_free ( struct refcnt *refcnt ) { + struct posix_file *file = + container_of ( refcnt, struct posix_file, refcnt ); + struct io_buffer *iobuf; + struct io_buffer *tmp; + + list_for_each_entry_safe ( iobuf, tmp, &file->data, list ) { + list_del ( &iobuf->list ); + free_iob ( iobuf ); + } + free ( file ); +} + +/** + * Terminate file data transfer + * + * @v file POSIX file + * @v rc Reason for termination + */ +static void posix_file_finished ( struct posix_file *file, int rc ) { + xfer_nullify ( &file->xfer ); + xfer_close ( &file->xfer, rc ); + file->rc = rc; +} + +/** + * Handle close() event + * + * @v xfer POSIX file data transfer interface + * @v rc Reason for close + */ +static void posix_file_xfer_close ( struct xfer_interface *xfer, int rc ) { + struct posix_file *file = + container_of ( xfer, struct posix_file, xfer ); + + posix_file_finished ( file, rc ); +} + +/** + * Handle deliver_iob() event + * + * @v xfer POSIX file data transfer interface + * @v iobuf I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + */ +static int +posix_file_xfer_deliver_iob ( struct xfer_interface *xfer, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + struct posix_file *file = + container_of ( xfer, struct posix_file, xfer ); + + /* Keep track of file position solely for the filesize */ + if ( meta->whence != SEEK_CUR ) + file->pos = 0; + file->pos += meta->offset; + if ( file->filesize < file->pos ) + file->filesize = file->pos; + + if ( iob_len ( iobuf ) ) { + list_add_tail ( &iobuf->list, &file->data ); + } else { + free_iob ( iobuf ); + } + + return 0; +} + +/** POSIX file data transfer interface operations */ +static struct xfer_interface_operations posix_file_xfer_operations = { + .close = posix_file_xfer_close, + .vredirect = xfer_vopen, + .window = unlimited_xfer_window, + .alloc_iob = default_xfer_alloc_iob, + .deliver_iob = posix_file_xfer_deliver_iob, + .deliver_raw = xfer_deliver_as_iob, +}; + +/** + * Identify file by file descriptor + * + * @v fd File descriptor + * @ret file Corresponding file, or NULL + */ +static struct posix_file * posix_fd_to_file ( int fd ) { + struct posix_file *file; + + list_for_each_entry ( file, &posix_files, list ) { + if ( file->fd == fd ) + return file; + } + return NULL; +} + +/** + * Find an available file descriptor + * + * @ret fd File descriptor, or negative error number + */ +static int posix_find_free_fd ( void ) { + int fd; + + for ( fd = POSIX_FD_MIN ; fd <= POSIX_FD_MAX ; fd++ ) { + if ( ! posix_fd_to_file ( fd ) ) + return fd; + } + DBG ( "POSIX could not find free file descriptor\n" ); + return -ENFILE; +} + +/** + * Open file + * + * @v uri_string URI string + * @ret fd File descriptor, or negative error number + */ +int open ( const char *uri_string ) { + struct posix_file *file; + int fd; + int rc; + + /* Find a free file descriptor to use */ + fd = posix_find_free_fd(); + if ( fd < 0 ) + return fd; + + /* Allocate and initialise structure */ + file = zalloc ( sizeof ( *file ) ); + if ( ! file ) + return -ENOMEM; + file->refcnt.free = posix_file_free; + file->fd = fd; + file->rc = -EINPROGRESS; + xfer_init ( &file->xfer, &posix_file_xfer_operations, + &file->refcnt ); + INIT_LIST_HEAD ( &file->data ); + + /* Open URI on data transfer interface */ + if ( ( rc = xfer_open_uri_string ( &file->xfer, uri_string ) ) != 0 ) + goto err; + + /* Wait for open to succeed or fail */ + while ( list_empty ( &file->data ) ) { + step(); + if ( file->rc == 0 ) + break; + if ( file->rc != -EINPROGRESS ) { + rc = file->rc; + goto err; + } + } + + /* Add to list of open files. List takes reference ownership. */ + list_add ( &file->list, &posix_files ); + DBG ( "POSIX opened %s as file %d\n", uri_string, fd ); + return fd; + + err: + posix_file_finished ( file, rc ); + ref_put ( &file->refcnt ); + return rc; +} + +/** + * Check file descriptors for readiness + * + * @v readfds File descriptors to check + * @v wait Wait until data is ready + * @ret nready Number of ready file descriptors + */ +int select ( fd_set *readfds, int wait ) { + struct posix_file *file; + int fd; + + do { + for ( fd = POSIX_FD_MIN ; fd <= POSIX_FD_MAX ; fd++ ) { + if ( ! FD_ISSET ( fd, readfds ) ) + continue; + file = posix_fd_to_file ( fd ); + if ( ! file ) + return -EBADF; + if ( ( list_empty ( &file->data ) ) && + ( file->rc == -EINPROGRESS ) ) + continue; + /* Data is available or status has changed */ + FD_ZERO ( readfds ); + FD_SET ( fd, readfds ); + return 1; + } + step(); + } while ( wait ); + + return 0; +} + +/** + * Read data from file + * + * @v buffer Data buffer + * @v offset Starting offset within data buffer + * @v len Maximum length to read + * @ret len Actual length read, or negative error number + * + * This call is non-blocking; if no data is available to read then + * -EWOULDBLOCK will be returned. + */ +ssize_t read_user ( int fd, userptr_t buffer, off_t offset, size_t max_len ) { + struct posix_file *file; + struct io_buffer *iobuf; + size_t len; + + /* Identify file */ + file = posix_fd_to_file ( fd ); + if ( ! file ) + return -EBADF; + + /* Try to fetch more data if none available */ + if ( list_empty ( &file->data ) ) + step(); + + /* Dequeue at most one received I/O buffer into user buffer */ + list_for_each_entry ( iobuf, &file->data, list ) { + len = iob_len ( iobuf ); + if ( len > max_len ) + len = max_len; + copy_to_user ( buffer, offset, iobuf->data, len ); + iob_pull ( iobuf, len ); + if ( ! iob_len ( iobuf ) ) { + list_del ( &iobuf->list ); + free_iob ( iobuf ); + } + file->pos += len; + assert ( len != 0 ); + return len; + } + + /* If file has completed, return (after returning all data) */ + if ( file->rc != -EINPROGRESS ) { + assert ( list_empty ( &file->data ) ); + return file->rc; + } + + /* No data ready and file still in progress; return -WOULDBLOCK */ + return -EWOULDBLOCK; +} + +/** + * Determine file size + * + * @v fd File descriptor + * @ret size File size, or negative error number + */ +ssize_t fsize ( int fd ) { + struct posix_file *file; + + /* Identify file */ + file = posix_fd_to_file ( fd ); + if ( ! file ) + return -EBADF; + + return file->filesize; +} + +/** + * Close file + * + * @v fd File descriptor + * @ret rc Return status code + */ +int close ( int fd ) { + struct posix_file *file; + + /* Identify file */ + file = posix_fd_to_file ( fd ); + if ( ! file ) + return -EBADF; + + /* Terminate data transfer */ + posix_file_finished ( file, 0 ); + + /* Remove from list of open files and drop reference */ + list_del ( &file->list ); + ref_put ( &file->refcnt ); + return 0; +} diff --git a/gpxe/src/core/process.c b/gpxe/src/core/process.c new file mode 100644 index 00000000..cf931acf --- /dev/null +++ b/gpxe/src/core/process.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2006 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. + */ + +#include <gpxe/list.h> +#include <gpxe/init.h> +#include <gpxe/process.h> + +/** @file + * + * Processes + * + * We implement a trivial form of cooperative multitasking, in which + * all processes share a single stack and address space. + */ + +/** Process run queue */ +static LIST_HEAD ( run_queue ); + +/** Registered permanent processes */ +static struct process processes[0] + __table_start ( struct process, processes ); +static struct process processes_end[0] + __table_end ( struct process, processes ); + +/** + * Add process to process list + * + * @v process Process + */ +void process_add ( struct process *process ) { + DBGC ( process, "PROCESS %p starting\n", process ); + ref_get ( process->refcnt ); + list_add_tail ( &process->list, &run_queue ); +} + +/** + * Remove process from process list + * + * @v process Process + * + * It is safe to call process_del() multiple times; further calls will + * have no effect. + */ +void process_del ( struct process *process ) { + if ( ! list_empty ( &process->list ) ) { + DBGC ( process, "PROCESS %p stopping\n", process ); + list_del ( &process->list ); + INIT_LIST_HEAD ( &process->list ); + ref_put ( process->refcnt ); + } else { + DBGC ( process, "PROCESS %p already stopped\n", process ); + } +} + +/** + * Single-step a single process + * + * This executes a single step of the first process in the run queue, + * and moves the process to the end of the run queue. + */ +void step ( void ) { + struct process *process; + + list_for_each_entry ( process, &run_queue, list ) { + list_del ( &process->list ); + list_add_tail ( &process->list, &run_queue ); + process->step ( process ); + break; + } +} + +/** + * Initialise processes + * + */ +static void init_processes ( void ) { + struct process *process; + + for ( process = processes ; process < processes_end ; process++ ) { + process_add ( process ); + } +} + +/** Process initialiser */ +struct init_fn process_init_fn __init_fn ( INIT_NORMAL ) = { + .initialise = init_processes, +}; diff --git a/gpxe/src/core/proto_eth_slow.c b/gpxe/src/core/proto_eth_slow.c new file mode 100644 index 00000000..b759a713 --- /dev/null +++ b/gpxe/src/core/proto_eth_slow.c @@ -0,0 +1,406 @@ +/* Copyright 2004 Linux Networx */ +#ifdef PROTO_LACP +#if 0 +#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/gpxe/src/core/random.c b/gpxe/src/core/random.c new file mode 100644 index 00000000..d34e763a --- /dev/null +++ b/gpxe/src/core/random.c @@ -0,0 +1,39 @@ +/** @file + * + * Random number generation + * + */ + +#include <stdlib.h> +#include <gpxe/timer.h> + +static int32_t rnd_seed = 0; + +/** + * Seed the pseudo-random number generator + * + * @v seed Seed value + */ +void srandom ( unsigned int seed ) { + rnd_seed = seed; +} + +/** + * Generate a pseudo-random number between 0 and 2147483647L or 2147483562? + * + * @ret rand Pseudo-random number + */ +long int random ( void ) { + int32_t q; + + if ( ! rnd_seed ) /* Initialize linear congruential generator */ + srandom ( currticks() ); + + /* simplified version of the LCG given in Bruce Schneier's + "Applied Cryptography" */ + q = ( rnd_seed / 53668 ); + rnd_seed = ( 40014 * ( rnd_seed - 53668 * q ) - 12211 * q ); + if ( rnd_seed < 0 ) + rnd_seed += 2147483563L; + return rnd_seed; +} diff --git a/gpxe/src/core/refcnt.c b/gpxe/src/core/refcnt.c new file mode 100644 index 00000000..30bb6dea --- /dev/null +++ b/gpxe/src/core/refcnt.c @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2007 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. + */ + +#include <stdlib.h> +#include <gpxe/refcnt.h> + +/** @file + * + * Reference counting + * + */ + +/** + * Increment reference count + * + * @v refcnt Reference counter, or NULL + * @ret refcnt Reference counter + * + * If @c refcnt is NULL, no action is taken. + */ +struct refcnt * ref_get ( struct refcnt *refcnt ) { + + if ( refcnt ) { + refcnt->refcnt++; + DBGC2 ( refcnt, "REFCNT %p incremented to %d\n", + refcnt, refcnt->refcnt ); + } + return refcnt; +} + +/** + * Decrement reference count + * + * @v refcnt Reference counter, or NULL + * + * If the reference count decreases below zero, the object's free() + * method will be called. + * + * If @c refcnt is NULL, no action is taken. + */ +void ref_put ( struct refcnt *refcnt ) { + + if ( ! refcnt ) + return; + + refcnt->refcnt--; + DBGC2 ( refcnt, "REFCNT %p decremented to %d\n", + refcnt, refcnt->refcnt ); + + if ( refcnt->refcnt >= 0 ) + return; + + if ( refcnt->free ) { + DBGC ( refcnt, "REFCNT %p being freed via method %p\n", + refcnt, refcnt->free ); + refcnt->free ( refcnt ); + } else { + DBGC ( refcnt, "REFCNT %p being freed\n", refcnt ); + free ( refcnt ); + } +} diff --git a/gpxe/src/core/resolv.c b/gpxe/src/core/resolv.c new file mode 100644 index 00000000..f4a587f1 --- /dev/null +++ b/gpxe/src/core/resolv.c @@ -0,0 +1,400 @@ +/* + * Copyright (C) 2007 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. + */ + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <gpxe/in.h> +#include <gpxe/xfer.h> +#include <gpxe/open.h> +#include <gpxe/process.h> +#include <gpxe/resolv.h> + +/** @file + * + * Name resolution + * + */ + +/*************************************************************************** + * + * Name resolution interfaces + * + *************************************************************************** + */ + +/** + * Name resolution completed + * + * @v resolv Name resolution interface + * @v sa Completed socket address (if successful) + * @v rc Final status code + */ +void resolv_done ( struct resolv_interface *resolv, struct sockaddr *sa, + int rc ) { + struct resolv_interface *dest = resolv_get_dest ( resolv ); + + resolv_unplug ( resolv ); + dest->op->done ( dest, sa, rc ); + resolv_put ( dest ); +} + +/** + * Ignore name resolution done() event + * + * @v resolv Name resolution interface + * @v sa Completed socket address (if successful) + * @v rc Final status code + */ +void ignore_resolv_done ( struct resolv_interface *resolv __unused, + struct sockaddr *sa __unused, int rc __unused ) { + /* Do nothing */ +} + +/** Null name resolution interface operations */ +struct resolv_interface_operations null_resolv_ops = { + .done = ignore_resolv_done, +}; + +/** Null name resolution interface */ +struct resolv_interface null_resolv = { + .intf = { + .dest = &null_resolv.intf, + .refcnt = NULL, + }, + .op = &null_resolv_ops, +}; + +/*************************************************************************** + * + * Numeric name resolver + * + *************************************************************************** + */ + +/** A numeric name resolver */ +struct numeric_resolv { + /** Reference counter */ + struct refcnt refcnt; + /** Name resolution interface */ + struct resolv_interface resolv; + /** Process */ + struct process process; + /** Completed socket address */ + struct sockaddr sa; + /** Overall status code */ + int rc; +}; + +static void numeric_step ( struct process *process ) { + struct numeric_resolv *numeric = + container_of ( process, struct numeric_resolv, process ); + + resolv_done ( &numeric->resolv, &numeric->sa, numeric->rc ); + process_del ( process ); +} + +static int numeric_resolv ( struct resolv_interface *resolv, + const char *name, struct sockaddr *sa ) { + struct numeric_resolv *numeric; + struct sockaddr_in *sin; + + /* Allocate and initialise structure */ + numeric = zalloc ( sizeof ( *numeric ) ); + if ( ! numeric ) + return -ENOMEM; + resolv_init ( &numeric->resolv, &null_resolv_ops, &numeric->refcnt ); + process_init ( &numeric->process, numeric_step, &numeric->refcnt ); + memcpy ( &numeric->sa, sa, sizeof ( numeric->sa ) ); + + DBGC ( numeric, "NUMERIC %p attempting to resolve \"%s\"\n", + numeric, name ); + + /* Attempt to resolve name */ + sin = ( ( struct sockaddr_in * ) &numeric->sa ); + sin->sin_family = AF_INET; + if ( inet_aton ( name, &sin->sin_addr ) == 0 ) + numeric->rc = -EINVAL; + + /* Attach to parent interface, mortalise self, and return */ + resolv_plug_plug ( &numeric->resolv, resolv ); + ref_put ( &numeric->refcnt ); + return 0; +} + +struct resolver numeric_resolver __resolver ( RESOLV_NUMERIC ) = { + .name = "NUMERIC", + .resolv = numeric_resolv, +}; + +/*************************************************************************** + * + * Name resolution multiplexer + * + *************************************************************************** + */ + +/** Registered name resolvers */ +static struct resolver resolvers[0] + __table_start ( struct resolver, resolvers ); +static struct resolver resolvers_end[0] + __table_end ( struct resolver, resolvers ); + +/** A name resolution multiplexer */ +struct resolv_mux { + /** Reference counter */ + struct refcnt refcnt; + /** Parent name resolution interface */ + struct resolv_interface parent; + + /** Child name resolution interface */ + struct resolv_interface child; + /** Current child resolver */ + struct resolver *resolver; + + /** Socket address to complete */ + struct sockaddr sa; + /** Name to be resolved + * + * Must be at end of structure + */ + char name[0]; +}; + +/** + * Try current child name resolver + * + * @v mux Name resolution multiplexer + * @ret rc Return status code + */ +static int resolv_mux_try ( struct resolv_mux *mux ) { + struct resolver *resolver = mux->resolver; + int rc; + + DBGC ( mux, "RESOLV %p trying method %s\n", mux, resolver->name ); + + if ( ( rc = resolver->resolv ( &mux->child, mux->name, + &mux->sa ) ) != 0 ) { + DBGC ( mux, "RESOLV %p could not use method %s: %s\n", + mux, resolver->name, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Handle done() event from child name resolver + * + * @v resolv Child name resolution interface + * @v sa Completed socket address (if successful) + * @v rc Final status code + */ +static void resolv_mux_done ( struct resolv_interface *resolv, + struct sockaddr *sa, int rc ) { + struct resolv_mux *mux = + container_of ( resolv, struct resolv_mux, child ); + + /* Unplug child */ + resolv_unplug ( &mux->child ); + + /* If this resolution succeeded, stop now */ + if ( rc == 0 ) { + DBGC ( mux, "RESOLV %p succeeded using method %s\n", + mux, mux->resolver->name ); + goto finished; + } + + /* Attempt next child resolver, if possible */ + mux->resolver++; + if ( mux->resolver >= resolvers_end ) { + DBGC ( mux, "RESOLV %p failed to resolve name\n", mux ); + goto finished; + } + if ( ( rc = resolv_mux_try ( mux ) ) != 0 ) + goto finished; + + /* Next resolver is now running */ + return; + + finished: + resolv_done ( &mux->parent, sa, rc ); +} + +/** Name resolution multiplexer operations */ +static struct resolv_interface_operations resolv_mux_child_ops = { + .done = resolv_mux_done, +}; + +/** + * Start name resolution + * + * @v resolv Name resolution interface + * @v name Name to resolve + * @v sa Socket address to complete + * @ret rc Return status code + */ +int resolv ( struct resolv_interface *resolv, const char *name, + struct sockaddr *sa ) { + struct resolv_mux *mux; + size_t name_len = ( strlen ( name ) + 1 ); + int rc; + + /* Allocate and initialise structure */ + mux = zalloc ( sizeof ( *mux ) + name_len ); + if ( ! mux ) + return -ENOMEM; + resolv_init ( &mux->parent, &null_resolv_ops, &mux->refcnt ); + resolv_init ( &mux->child, &resolv_mux_child_ops, &mux->refcnt ); + mux->resolver = resolvers; + memcpy ( &mux->sa, sa, sizeof ( mux->sa ) ); + memcpy ( mux->name, name, name_len ); + + DBGC ( mux, "RESOLV %p attempting to resolve \"%s\"\n", mux, name ); + + /* Start first resolver in chain. There will always be at + * least one resolver (the numeric resolver), so no need to + * check for the zero-resolvers-available case. + */ + if ( ( rc = resolv_mux_try ( mux ) ) != 0 ) + goto err; + + /* Attach parent interface, mortalise self, and return */ + resolv_plug_plug ( &mux->parent, resolv ); + ref_put ( &mux->refcnt ); + return 0; + + err: + ref_put ( &mux->refcnt ); + return rc; +} + +/*************************************************************************** + * + * Named socket opening + * + *************************************************************************** + */ + +/** A named socket */ +struct named_socket { + /** Reference counter */ + struct refcnt refcnt; + /** Data transfer interface */ + struct xfer_interface xfer; + /** Name resolution interface */ + struct resolv_interface resolv; + /** Communication semantics (e.g. SOCK_STREAM) */ + int semantics; + /** Stored local socket address, if applicable */ + struct sockaddr local; + /** Stored local socket address exists */ + int have_local; +}; + +/** Named socket opener data transfer interface operations */ +static struct xfer_interface_operations named_xfer_ops = { + .close = ignore_xfer_close, + .vredirect = ignore_xfer_vredirect, + .window = no_xfer_window, + .alloc_iob = default_xfer_alloc_iob, + .deliver_iob = xfer_deliver_as_raw, + .deliver_raw = ignore_xfer_deliver_raw, +}; + +/** + * Handle done() event + * + * @v resolv Name resolution interface + * @v sa Completed socket address (if successful) + * @v rc Final status code + */ +static void named_resolv_done ( struct resolv_interface *resolv, + struct sockaddr *sa, int rc ) { + struct named_socket *named = + container_of ( resolv, struct named_socket, resolv ); + + /* Unplug resolver and nullify data transfer interface */ + resolv_unplug ( &named->resolv ); + xfer_nullify ( &named->xfer ); + + /* Redirect if name resolution was successful */ + if ( rc == 0 ) { + rc = xfer_redirect ( &named->xfer, LOCATION_SOCKET, + named->semantics, sa, + ( named->have_local ? + &named->local : NULL ) ); + } + + /* Close data transfer interface if redirection failed */ + if ( rc != 0 ) + xfer_close ( &named->xfer, rc ); + + /* Unplug data transfer interface */ + xfer_unplug ( &named->xfer ); +} + +/** Named socket opener name resolution interface operations */ +static struct resolv_interface_operations named_resolv_ops = { + .done = named_resolv_done, +}; + +/** + * Open named socket + * + * @v semantics Communication semantics (e.g. SOCK_STREAM) + * @v peer Peer socket address to complete + * @v name Name to resolve + * @v local Local socket address, or NULL + * @ret rc Return status code + */ +int xfer_open_named_socket ( struct xfer_interface *xfer, int semantics, + struct sockaddr *peer, const char *name, + struct sockaddr *local ) { + struct named_socket *named; + int rc; + + /* Allocate and initialise structure */ + named = zalloc ( sizeof ( *named ) ); + if ( ! named ) + return -ENOMEM; + xfer_init ( &named->xfer, &named_xfer_ops, &named->refcnt ); + resolv_init ( &named->resolv, &named_resolv_ops, &named->refcnt ); + named->semantics = semantics; + if ( local ) { + memcpy ( &named->local, local, sizeof ( named->local ) ); + named->have_local = 1; + } + + DBGC ( named, "RESOLV %p opening named socket \"%s\"\n", + named, name ); + + /* Start name resolution */ + if ( ( rc = resolv ( &named->resolv, name, peer ) ) != 0 ) + goto err; + + /* Attach parent interface, mortalise self, and return */ + xfer_plug_plug ( &named->xfer, xfer ); + ref_put ( &named->refcnt ); + return 0; + + err: + ref_put ( &named->refcnt ); + return rc; +} diff --git a/gpxe/src/core/serial.c b/gpxe/src/core/serial.c new file mode 100644 index 00000000..a5b3f913 --- /dev/null +++ b/gpxe/src/core/serial.c @@ -0,0 +1,268 @@ +/* + * 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(). + */ + +#include "stddef.h" +#include "console.h" +#include <gpxe/init.h> +#include "io.h" +#include <unistd.h> +#include "config/serial.h" + +/* Set default values if none specified */ + +#ifndef COMCONSOLE +#define COMCONSOLE 0x3f8 +#endif + +#ifndef COMSPEED +#define COMSPEED 9600 +#endif + +#ifndef COMDATA +#define COMDATA 8 +#endif + +#ifndef COMPARITY +#define COMPARITY 0 +#endif + +#ifndef COMSTOP +#define COMSTOP 1 +#endif + +#undef UART_BASE +#define UART_BASE ( COMCONSOLE ) + +#undef UART_BAUD +#define UART_BAUD ( COMSPEED ) + +#if ((115200%UART_BAUD) != 0) +#error Bad ttys0 baud rate +#endif + +#define COMBRD (115200/UART_BAUD) + +/* Line Control Settings */ +#define UART_LCS ( ( ( (COMDATA) - 5 ) << 0 ) | \ + ( ( (COMPARITY) ) << 3 ) | \ + ( ( (COMSTOP) - 1 ) << 2 ) ) + +/* 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 + +struct console_driver serial_console __console_driver; + +/* + * void serial_putc(int ch); + * Write character `ch' to port UART_BASE. + */ +static void serial_putc ( int ch ) { + int i; + int status; + 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. + */ +static 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. + */ +static int serial_ischar ( void ) { + int status; + 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 COMSPEED, line settings 8N1. + */ +static void serial_init ( void ) { + int status; + int divisor, lcs; + + DBG ( "Serial port %#x initialising\n", UART_BASE ); + + 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 COMSPEED, 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) { + DBG ( "Serial port %#x UART_DLL failed\n", UART_BASE ); + goto out; + } + uart_writeb(0x55, UART_BASE + UART_DLL); + if (uart_readb(UART_BASE + UART_DLL) != 0x55) { + DBG ( "Serial port %#x UART_DLL failed\n", UART_BASE ); + goto out; + } + uart_writeb(divisor & 0xff, UART_BASE + UART_DLL); + if (uart_readb(UART_BASE + UART_DLL) != (divisor & 0xff)) { + DBG ( "Serial port %#x UART_DLL failed\n", UART_BASE ); + goto out; + } + uart_writeb(0xaa, UART_BASE + UART_DLM); + if (uart_readb(UART_BASE + UART_DLM) != 0xaa) { + DBG ( "Serial port %#x UART_DLM failed\n", UART_BASE ); + goto out; + } + uart_writeb(0x55, UART_BASE + UART_DLM); + if (uart_readb(UART_BASE + UART_DLM) != 0x55) { + DBG ( "Serial port %#x UART_DLM failed\n", UART_BASE ); + goto out; + } + uart_writeb((divisor >> 8) & 0xff, UART_BASE + UART_DLM); + if (uart_readb(UART_BASE + UART_DLM) != ((divisor >> 8) & 0xff)) { + DBG ( "Serial port %#x UART_DLM failed\n", UART_BASE ); + 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) + */ + (void) uart_readb(UART_BASE + UART_RBR); + /* line status reg */ + status = uart_readb(UART_BASE + UART_LSR); + } while(status & UART_LSR_DR); + serial_console.disabled = 0; + out: + return; +} + +/* + * void serial_fini(void); + * Cleanup our use of the serial port, in particular flush the + * output buffer so we don't accidentially lose characters. + */ +static void serial_fini ( void ) { + int i, status; + if (serial_console.disabled) { + /* 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)); + /* Don't mark it as disabled; it's still usable */ +} + +struct console_driver serial_console __console_driver = { + .putchar = serial_putc, + .getchar = serial_getc, + .iskey = serial_ischar, + .disabled = 1, +}; + +/** Serial console startup function */ +struct startup_fn serial_startup_fn __startup_fn ( STARTUP_NORMAL ) = { + .startup = serial_init, + .shutdown = serial_fini, +}; + +/** + * Serial console initialisation function + * + * Initialise console early on so that it is available to capture + * early debug messages. It is safe to call serial_init() multiple + * times. + */ +struct init_fn serial_init_fn __init_fn ( INIT_CONSOLE ) = { + .initialise = serial_init, +}; diff --git a/gpxe/src/core/settings.c b/gpxe/src/core/settings.c new file mode 100644 index 00000000..b793ae68 --- /dev/null +++ b/gpxe/src/core/settings.c @@ -0,0 +1,1037 @@ +/* + * Copyright (C) 2008 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. + */ + +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <byteswap.h> +#include <errno.h> +#include <assert.h> +#include <gpxe/in.h> +#include <gpxe/vsprintf.h> +#include <gpxe/dhcp.h> +#include <gpxe/settings.h> + +/** @file + * + * Configuration settings + * + */ + +/** Registered settings */ +static struct setting settings[0] + __table_start ( struct setting, settings ); +static struct setting settings_end[0] + __table_end ( struct setting, settings ); + +/** Registered setting types */ +static struct setting_type setting_types[0] + __table_start ( struct setting_type, setting_types ); +static struct setting_type setting_types_end[0] + __table_end ( struct setting_type, setting_types ); + +/** Registered settings applicators */ +static struct settings_applicator settings_applicators[0] + __table_start ( struct settings_applicator, settings_applicators ); +static struct settings_applicator settings_applicators_end[0] + __table_end ( struct settings_applicator, settings_applicators ); + +/****************************************************************************** + * + * Registered settings blocks + * + ****************************************************************************** + */ + +/** + * Store value of simple setting + * + * @v options DHCP option block + * @v setting Setting to store + * @v data Setting data, or NULL to clear setting + * @v len Length of setting data + * @ret rc Return status code + */ +int simple_settings_store ( struct settings *settings, struct setting *setting, + const void *data, size_t len ) { + struct simple_settings *simple = + container_of ( settings, struct simple_settings, settings ); + return dhcpopt_extensible_store ( &simple->dhcpopts, setting->tag, + data, len ); +} + +/** + * Fetch value of simple setting + * + * @v options DHCP option block + * @v setting Setting to fetch + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +int simple_settings_fetch ( struct settings *settings, struct setting *setting, + void *data, size_t len ) { + struct simple_settings *simple = + container_of ( settings, struct simple_settings, settings ); + return dhcpopt_fetch ( &simple->dhcpopts, setting->tag, data, len ); +} + +/** Simple settings operations */ +struct settings_operations simple_settings_operations = { + .store = simple_settings_store, + .fetch = simple_settings_fetch, +}; + +/** Root simple settings block */ +struct simple_settings simple_settings_root = { + .settings = { + .refcnt = NULL, + .name = "", + .siblings = + LIST_HEAD_INIT ( simple_settings_root.settings.siblings ), + .children = + LIST_HEAD_INIT ( simple_settings_root.settings.children ), + .op = &simple_settings_operations, + }, +}; + +/** Root settings block */ +#define settings_root simple_settings_root.settings + +/** + * Apply all settings + * + * @ret rc Return status code + */ +static int apply_settings ( void ) { + struct settings_applicator *applicator; + int rc; + + /* Call all settings applicators */ + for ( applicator = settings_applicators ; + applicator < settings_applicators_end ; applicator++ ) { + if ( ( rc = applicator->apply() ) != 0 ) { + DBG ( "Could not apply settings using applicator " + "%p: %s\n", applicator, strerror ( rc ) ); + return rc; + } + } + + return 0; +} + +/** + * Reprioritise settings + * + * @v settings Settings block + * + * Reorders the settings block amongst its siblings according to its + * priority. + */ +static void reprioritise_settings ( struct settings *settings ) { + struct settings *parent = settings->parent; + long priority; + struct settings *tmp; + long tmp_priority; + + /* Stop when we reach the top of the tree */ + if ( ! parent ) + return; + + /* Read priority, if present */ + priority = fetch_intz_setting ( settings, &priority_setting ); + + /* Remove from siblings list */ + list_del ( &settings->siblings ); + + /* Reinsert after any existing blocks which have a higher priority */ + list_for_each_entry ( tmp, &parent->children, siblings ) { + tmp_priority = fetch_intz_setting ( tmp, &priority_setting ); + if ( priority > tmp_priority ) + break; + } + list_add_tail ( &settings->siblings, &tmp->siblings ); + + /* Recurse up the tree */ + reprioritise_settings ( parent ); +} + +/** + * Register settings block + * + * @v settings Settings block + * @v parent Parent settings block, or NULL + * @ret rc Return status code + */ +int register_settings ( struct settings *settings, struct settings *parent ) { + + /* NULL parent => add to settings root */ + assert ( settings != NULL ); + if ( parent == NULL ) + parent = &settings_root; + + /* Add to list of settings */ + ref_get ( settings->refcnt ); + ref_get ( parent->refcnt ); + settings->parent = parent; + list_add_tail ( &settings->siblings, &parent->children ); + DBGC ( settings, "Settings %p registered\n", settings ); + + /* Fix up settings priority */ + reprioritise_settings ( settings ); + + /* Apply potentially-updated settings */ + apply_settings(); + + return 0; +} + +/** + * Unregister settings block + * + * @v settings Settings block + */ +void unregister_settings ( struct settings *settings ) { + + /* Remove from list of settings */ + ref_put ( settings->refcnt ); + ref_put ( settings->parent->refcnt ); + settings->parent = NULL; + list_del ( &settings->siblings ); + DBGC ( settings, "Settings %p unregistered\n", settings ); + + /* Apply potentially-updated settings */ + apply_settings(); +} + +/** + * Find child named settings block + * + * @v parent Parent settings block + * @v name Name within this parent + * @ret settings Settings block, or NULL + */ +struct settings * find_child_settings ( struct settings *parent, + const char *name ) { + struct settings *settings; + size_t len; + + /* Look for a child whose name matches the initial component */ + list_for_each_entry ( settings, &parent->children, siblings ) { + len = strlen ( settings->name ); + if ( strncmp ( name, settings->name, len ) != 0 ) + continue; + if ( name[len] == 0 ) + return settings; + if ( name[len] == '.' ) + return find_child_settings ( settings, + ( name + len + 1 ) ); + } + + return NULL; +} + +/** + * Find named settings block + * + * @v name Name + * @ret settings Settings block, or NULL + */ +struct settings * find_settings ( const char *name ) { + + /* If name is empty, use the root */ + if ( ! *name ) + return &settings_root; + + return find_child_settings ( &settings_root, name ); +} + +/****************************************************************************** + * + * Core settings routines + * + ****************************************************************************** + */ + +/** + * Store value of setting + * + * @v settings Settings block + * @v setting Setting to store + * @v data Setting data, or NULL to clear setting + * @v len Length of setting data + * @ret rc Return status code + */ +int store_setting ( struct settings *settings, struct setting *setting, + const void *data, size_t len ) { + int rc; + + /* Sanity check */ + if ( ! settings ) + return -ENODEV; + + /* Store setting */ + if ( ( rc = settings->op->store ( settings, setting, + data, len ) ) != 0 ) + return rc; + + /* Reprioritise settings if necessary */ + if ( setting_cmp ( setting, &priority_setting ) == 0 ) + reprioritise_settings ( settings ); + + /* If these settings are registered, apply potentially-updated + * settings + */ + for ( ; settings ; settings = settings->parent ) { + if ( settings == &settings_root ) { + if ( ( rc = apply_settings() ) != 0 ) + return rc; + break; + } + } + + return 0; +} + +/** + * Fetch value of setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + * + * The actual length of the setting will be returned even if + * the buffer was too small. + */ +int fetch_setting ( struct settings *settings, struct setting *setting, + void *data, size_t len ) { + struct settings *child; + int ret; + + /* NULL settings implies starting at the global settings root */ + if ( ! settings ) + settings = &settings_root; + + /* Try this block first */ + if ( ( ret = settings->op->fetch ( settings, setting, + data, len ) ) >= 0 ) + return ret; + + /* Recurse into each child block in turn */ + list_for_each_entry ( child, &settings->children, siblings ) { + if ( ( ret = fetch_setting ( child, setting, + data, len ) ) >= 0 ) + return ret; + } + + return -ENOENT; +} + +/** + * Fetch length of setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @ret len Length of setting data, or negative error + * + * This function can also be used as an existence check for the + * setting. + */ +int fetch_setting_len ( struct settings *settings, struct setting *setting ) { + return fetch_setting ( settings, setting, NULL, 0 ); +} + +/** + * Fetch value of string setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v data Buffer to fill with setting string data + * @v len Length of buffer + * @ret len Length of string setting, or negative error + * + * The resulting string is guaranteed to be correctly NUL-terminated. + * The returned length will be the length of the underlying setting + * data. + */ +int fetch_string_setting ( struct settings *settings, struct setting *setting, + char *data, size_t len ) { + memset ( data, 0, len ); + return fetch_setting ( settings, setting, data, ( len - 1 ) ); +} + +/** + * Fetch value of IPv4 address setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v inp IPv4 address to fill in + * @ret len Length of setting, or negative error + */ +int fetch_ipv4_setting ( struct settings *settings, struct setting *setting, + struct in_addr *inp ) { + int len; + + len = fetch_setting ( settings, setting, inp, sizeof ( *inp ) ); + if ( len < 0 ) + return len; + if ( len < ( int ) sizeof ( *inp ) ) + return -ERANGE; + return len; +} + +/** + * Fetch value of signed integer setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v value Integer value to fill in + * @ret len Length of setting, or negative error + */ +int fetch_int_setting ( struct settings *settings, struct setting *setting, + long *value ) { + union { + long value; + uint8_t u8[ sizeof ( long ) ]; + int8_t s8[ sizeof ( long ) ]; + } buf; + int len; + int i; + + buf.value = 0; + len = fetch_setting ( settings, setting, &buf, sizeof ( buf ) ); + if ( len < 0 ) + return len; + if ( len > ( int ) sizeof ( buf ) ) + return -ERANGE; + + *value = ( ( buf.s8[0] >= 0 ) ? 0 : -1L ); + for ( i = 0 ; i < len ; i++ ) { + *value = ( ( *value << 8 ) | buf.u8[i] ); + } + + return len; +} + +/** + * Fetch value of unsigned integer setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v value Integer value to fill in + * @ret len Length of setting, or negative error + */ +int fetch_uint_setting ( struct settings *settings, struct setting *setting, + unsigned long *value ) { + long svalue; + int len; + + len = fetch_int_setting ( settings, setting, &svalue ); + if ( len < 0 ) + return len; + + *value = ( svalue & ( -1UL >> ( sizeof ( long ) - len ) ) ); + + return len; +} + +/** + * Fetch value of signed integer setting, or zero + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @ret value Setting value, or zero + */ +long fetch_intz_setting ( struct settings *settings, struct setting *setting ){ + long value = 0; + + fetch_int_setting ( settings, setting, &value ); + return value; +} + +/** + * Fetch value of unsigned integer setting, or zero + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @ret value Setting value, or zero + */ +unsigned long fetch_uintz_setting ( struct settings *settings, + struct setting *setting ) { + unsigned long value = 0; + + fetch_uint_setting ( settings, setting, &value ); + return value; +} + +/** + * Compare two settings + * + * @v a Setting to compare + * @v b Setting to compare + * @ret 0 Settings are the same + * @ret non-zero Settings are not the same + */ +int setting_cmp ( struct setting *a, struct setting *b ) { + + /* If the settings have tags, compare them */ + if ( a->tag && ( a->tag == b->tag ) ) + return 0; + + /* Otherwise, compare the names */ + return strcmp ( a->name, b->name ); +} + +/****************************************************************************** + * + * Formatted setting routines + * + ****************************************************************************** + */ + +/** + * Store value of typed setting + * + * @v settings Settings block + * @v setting Setting to store + * @v type Settings type + * @v value Formatted setting data, or NULL + * @ret rc Return status code + */ +int storef_setting ( struct settings *settings, struct setting *setting, + const char *value ) { + + /* NULL value implies deletion. Avoid imposing the burden of + * checking for NULL values on each typed setting's storef() + * method. + */ + if ( ! value ) + return delete_setting ( settings, setting ); + + return setting->type->storef ( settings, setting, value ); +} + +/** + * Find named setting + * + * @v name Name + * @ret setting Named setting, or NULL + */ +static struct setting * find_setting ( const char *name ) { + struct setting *setting; + + for ( setting = settings ; setting < settings_end ; setting++ ) { + if ( strcmp ( name, setting->name ) == 0 ) + return setting; + } + return NULL; +} + +/** + * Find setting type + * + * @v name Name + * @ret type Setting type, or NULL + */ +static struct setting_type * find_setting_type ( const char *name ) { + struct setting_type *type; + + for ( type = setting_types ; type < setting_types_end ; type++ ) { + if ( strcmp ( name, type->name ) == 0 ) + return type; + } + return NULL; +} + +/** + * Parse setting name + * + * @v name Name of setting + * @v settings Settings block to fill in + * @v setting Setting to fill in + * @ret rc Return status code + * + * Interprets a name of the form + * "[settings_name/]tag_name[:type_name]" and fills in the appropriate + * fields. + */ +static int parse_setting_name ( const char *name, struct settings **settings, + struct setting *setting ) { + char tmp_name[ strlen ( name ) + 1 ]; + char *settings_name; + char *setting_name; + char *type_name; + struct setting *named_setting; + char *tmp; + + /* Set defaults */ + *settings = &settings_root; + memset ( setting, 0, sizeof ( *setting ) ); + setting->type = &setting_type_hex; + + /* Split name into "[settings_name/]setting_name[:type_name]" */ + memcpy ( tmp_name, name, sizeof ( tmp_name ) ); + if ( ( setting_name = strchr ( tmp_name, '/' ) ) != NULL ) { + *(setting_name++) = 0; + settings_name = tmp_name; + } else { + setting_name = tmp_name; + settings_name = NULL; + } + if ( ( type_name = strchr ( setting_name, ':' ) ) != NULL ) + *(type_name++) = 0; + + /* Identify settings block, if specified */ + if ( settings_name ) { + *settings = find_settings ( settings_name ); + if ( *settings == NULL ) { + DBG ( "Unrecognised settings block \"%s\" in \"%s\"\n", + settings_name, name ); + return -ENODEV; + } + } + + /* Identify tag number */ + if ( ( named_setting = find_setting ( setting_name ) ) != NULL ) { + memcpy ( setting, named_setting, sizeof ( *setting ) ); + } else { + /* Unrecognised name: try to interpret as a tag number */ + tmp = setting_name; + while ( 1 ) { + setting->tag = ( ( setting->tag << 8 ) | + strtoul ( tmp, &tmp, 0 ) ); + if ( *tmp == 0 ) + break; + if ( *tmp != '.' ) { + DBG ( "Invalid setting \"%s\" in \"%s\"\n", + setting_name, name ); + return -ENOENT; + } + tmp++; + } + } + + /* Identify setting type, if specified */ + if ( type_name ) { + setting->type = find_setting_type ( type_name ); + if ( setting->type == NULL ) { + DBG ( "Invalid setting type \"%s\" in \"%s\"\n", + type_name, name ); + return -ENOTSUP; + } + } + + return 0; +} + +/** + * Parse and store value of named setting + * + * @v name Name of setting + * @v value Formatted setting data, or NULL + * @ret rc Return status code + */ +int storef_named_setting ( const char *name, const char *value ) { + struct settings *settings; + struct setting setting; + int rc; + + if ( ( rc = parse_setting_name ( name, &settings, &setting ) ) != 0 ) + return rc; + return storef_setting ( settings, &setting, value ); +} + +/** + * Fetch and format value of named setting + * + * @v name Name of setting + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +int fetchf_named_setting ( const char *name, char *buf, size_t len ) { + struct settings *settings; + struct setting setting; + int rc; + + if ( ( rc = parse_setting_name ( name, &settings, &setting ) ) != 0 ) + return rc; + return fetchf_setting ( settings, &setting, buf, len ); +} + +/****************************************************************************** + * + * Setting types + * + ****************************************************************************** + */ + +/** + * Parse and store value of string setting + * + * @v settings Settings block + * @v setting Setting to store + * @v value Formatted setting data + * @ret rc Return status code + */ +static int storef_string ( struct settings *settings, struct setting *setting, + const char *value ) { + return store_setting ( settings, setting, value, strlen ( value ) ); +} + +/** + * Fetch and format value of string setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +static int fetchf_string ( struct settings *settings, struct setting *setting, + char *buf, size_t len ) { + return fetch_string_setting ( settings, setting, buf, len ); +} + +/** A string setting type */ +struct setting_type setting_type_string __setting_type = { + .name = "string", + .storef = storef_string, + .fetchf = fetchf_string, +}; + +/** + * Parse and store value of IPv4 address setting + * + * @v settings Settings block + * @v setting Setting to store + * @v value Formatted setting data + * @ret rc Return status code + */ +static int storef_ipv4 ( struct settings *settings, struct setting *setting, + const char *value ) { + struct in_addr ipv4; + + if ( inet_aton ( value, &ipv4 ) == 0 ) + return -EINVAL; + return store_setting ( settings, setting, &ipv4, sizeof ( ipv4 ) ); +} + +/** + * Fetch and format value of IPv4 address setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +static int fetchf_ipv4 ( struct settings *settings, struct setting *setting, + char *buf, size_t len ) { + struct in_addr ipv4; + int rc; + + if ( ( rc = fetch_ipv4_setting ( settings, setting, &ipv4 ) ) < 0 ) + return rc; + return snprintf ( buf, len, inet_ntoa ( ipv4 ) ); +} + +/** An IPv4 address setting type */ +struct setting_type setting_type_ipv4 __setting_type = { + .name = "ipv4", + .storef = storef_ipv4, + .fetchf = fetchf_ipv4, +}; + +/** + * Parse and store value of integer setting + * + * @v settings Settings block + * @v setting Setting to store + * @v value Formatted setting data + * @v size Integer size, in bytes + * @ret rc Return status code + */ +static int storef_int ( struct settings *settings, struct setting *setting, + const char *value, unsigned int size ) { + union { + uint32_t num; + uint8_t bytes[4]; + } u; + char *endp; + + u.num = htonl ( strtoul ( value, &endp, 0 ) ); + if ( *endp ) + return -EINVAL; + return store_setting ( settings, setting, + &u.bytes[ sizeof ( u ) - size ], size ); +} + +/** + * Parse and store value of 8-bit integer setting + * + * @v settings Settings block + * @v setting Setting to store + * @v value Formatted setting data + * @v size Integer size, in bytes + * @ret rc Return status code + */ +static int storef_int8 ( struct settings *settings, struct setting *setting, + const char *value ) { + return storef_int ( settings, setting, value, 1 ); +} + +/** + * Parse and store value of 16-bit integer setting + * + * @v settings Settings block + * @v setting Setting to store + * @v value Formatted setting data + * @v size Integer size, in bytes + * @ret rc Return status code + */ +static int storef_int16 ( struct settings *settings, struct setting *setting, + const char *value ) { + return storef_int ( settings, setting, value, 2 ); +} + +/** + * Parse and store value of 32-bit integer setting + * + * @v settings Settings block + * @v setting Setting to store + * @v value Formatted setting data + * @v size Integer size, in bytes + * @ret rc Return status code + */ +static int storef_int32 ( struct settings *settings, struct setting *setting, + const char *value ) { + return storef_int ( settings, setting, value, 4 ); +} + +/** + * Fetch and format value of signed integer setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +static int fetchf_int ( struct settings *settings, struct setting *setting, + char *buf, size_t len ) { + long value; + int rc; + + if ( ( rc = fetch_int_setting ( settings, setting, &value ) ) < 0 ) + return rc; + return snprintf ( buf, len, "%ld", value ); +} + +/** + * Fetch and format value of unsigned integer setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +static int fetchf_uint ( struct settings *settings, struct setting *setting, + char *buf, size_t len ) { + unsigned long value; + int rc; + + if ( ( rc = fetch_uint_setting ( settings, setting, &value ) ) < 0 ) + return rc; + return snprintf ( buf, len, "%#lx", value ); +} + +/** A signed 8-bit integer setting type */ +struct setting_type setting_type_int8 __setting_type = { + .name = "int8", + .storef = storef_int8, + .fetchf = fetchf_int, +}; + +/** A signed 16-bit integer setting type */ +struct setting_type setting_type_int16 __setting_type = { + .name = "int16", + .storef = storef_int16, + .fetchf = fetchf_int, +}; + +/** A signed 32-bit integer setting type */ +struct setting_type setting_type_int32 __setting_type = { + .name = "int32", + .storef = storef_int32, + .fetchf = fetchf_int, +}; + +/** An unsigned 8-bit integer setting type */ +struct setting_type setting_type_uint8 __setting_type = { + .name = "uint8", + .storef = storef_int8, + .fetchf = fetchf_uint, +}; + +/** An unsigned 16-bit integer setting type */ +struct setting_type setting_type_uint16 __setting_type = { + .name = "uint16", + .storef = storef_int16, + .fetchf = fetchf_uint, +}; + +/** An unsigned 32-bit integer setting type */ +struct setting_type setting_type_uint32 __setting_type = { + .name = "uint32", + .storef = storef_int32, + .fetchf = fetchf_uint, +}; + +/** + * Parse and store value of hex string setting + * + * @v settings Settings block + * @v setting Setting to store + * @v value Formatted setting data + * @ret rc Return status code + */ +static int storef_hex ( struct settings *settings, struct setting *setting, + const char *value ) { + char *ptr = ( char * ) value; + uint8_t bytes[ strlen ( value ) ]; /* cannot exceed strlen(value) */ + unsigned int len = 0; + + while ( 1 ) { + bytes[len++] = strtoul ( ptr, &ptr, 16 ); + switch ( *ptr ) { + case '\0' : + return store_setting ( settings, setting, bytes, len ); + case ':' : + ptr++; + break; + default : + return -EINVAL; + } + } +} + +/** + * Fetch and format value of hex string setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +static int fetchf_hex ( struct settings *settings, struct setting *setting, + char *buf, size_t len ) { + int raw_len; + int check_len; + int used = 0; + int i; + + raw_len = fetch_setting_len ( settings, setting ); + if ( raw_len < 0 ) + return raw_len; + + { + uint8_t raw[raw_len]; + + check_len = fetch_setting ( settings, setting, raw, + sizeof ( raw ) ); + assert ( check_len == raw_len ); + + if ( len ) + buf[0] = 0; /* Ensure that a terminating NUL exists */ + for ( i = 0 ; i < raw_len ; i++ ) { + used += ssnprintf ( ( buf + used ), ( len - used ), + "%s%02x", ( used ? ":" : "" ), + raw[i] ); + } + return used; + } +} + +/** A hex-string setting */ +struct setting_type setting_type_hex __setting_type = { + .name = "hex", + .storef = storef_hex, + .fetchf = fetchf_hex, +}; + +/****************************************************************************** + * + * Settings + * + ****************************************************************************** + */ + +/** Hostname setting */ +struct setting hostname_setting __setting = { + .name = "hostname", + .description = "Host name", + .tag = DHCP_HOST_NAME, + .type = &setting_type_string, +}; + +/** Filename setting */ +struct setting filename_setting __setting = { + .name = "filename", + .description = "Boot filename", + .tag = DHCP_BOOTFILE_NAME, + .type = &setting_type_string, +}; + +/** Root path setting */ +struct setting root_path_setting __setting = { + .name = "root-path", + .description = "NFS/iSCSI root path", + .tag = DHCP_ROOT_PATH, + .type = &setting_type_string, +}; + +/** Username setting */ +struct setting username_setting __setting = { + .name = "username", + .description = "User name", + .tag = DHCP_EB_USERNAME, + .type = &setting_type_string, +}; + +/** Password setting */ +struct setting password_setting __setting = { + .name = "password", + .description = "Password", + .tag = DHCP_EB_PASSWORD, + .type = &setting_type_string, +}; + +/** Priority setting */ +struct setting priority_setting __setting = { + .name = "priority", + .description = "Priority of these settings", + .tag = DHCP_EB_PRIORITY, + .type = &setting_type_int8, +}; diff --git a/gpxe/src/core/string.c b/gpxe/src/core/string.c new file mode 100644 index 00000000..2e17bdcb --- /dev/null +++ b/gpxe/src/core/string.c @@ -0,0 +1,353 @@ +/* + * 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 <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +/* *** FROM string.c *** */ + +#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_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_STRCASECMP +int strcasecmp(const char *a, const char *b) +{ + while (*a && *b && (*a & ~0x20) == (*b & ~0x20)) {a++; b++; } + return((*a & ~0x20) - (*b & ~0x20)); +} +#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_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_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_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 + +char * strndup(const char *s, size_t n) +{ + size_t len = strlen(s); + char *new; + + if (len>n) + len = n; + new = malloc(len+1); + if (new) { + new[len] = '\0'; + memcpy(new,s,len); + } + return new; +} + +char * strdup(const char *s) { + return strndup(s, ~((size_t)0)); +} diff --git a/gpxe/src/core/stringextra.c b/gpxe/src/core/stringextra.c new file mode 100644 index 00000000..c2be4fc4 --- /dev/null +++ b/gpxe/src/core/stringextra.c @@ -0,0 +1,273 @@ +/* + * 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. + */ + +/* + * these are the standard string functions that are currently not used by + * any code in etherboot. put into a separate file to avoid linking them in + * with the rest of string.o + * if anything ever does want to use a function of these, consider moving + * the function in question back into string.c + */ + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.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_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_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_STRCSPN +/** + * strcspn - Calculate the length of the initial substring of @s which only + * contain letters not in @reject + * @s: The string to be searched + * @accept: The string to search for + */ +size_t strcspn(const char *s, const char *reject) +{ + const char *p; + const char *r; + size_t count = 0; + + for (p = s; *p != '\0'; ++p) { + for (r = reject; *r != '\0'; ++r) { + if (*p == *r) + 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_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) +{ + return memmove(dest,src,count); +} +#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(const 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 diff --git a/gpxe/src/core/timer.c b/gpxe/src/core/timer.c new file mode 100644 index 00000000..4e047ea7 --- /dev/null +++ b/gpxe/src/core/timer.c @@ -0,0 +1,113 @@ +/* + * core/timer.c + * + * Copyright (C) 2007 Alexey Zaytsev <alexey.zaytsev@gmail.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 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. + */ + +#include <stddef.h> +#include <assert.h> +#include <gpxe/timer.h> + +static struct timer ts_table[0] + __table_start ( struct timer, timers ); +static struct timer ts_table_end[0] + __table_end ( struct timer, timers ); + +/* + * This function may be used in custom timer driver. + * + * This udelay implementation works well if you've got a + * fast currticks(). + */ +void generic_currticks_udelay ( unsigned int usecs ) { + tick_t start; + tick_t elapsed; + + start = currticks(); + do { + /* xxx: Relax the cpu some way. */ + elapsed = ( currticks() - start ); + } while ( elapsed < usecs ); +} + +/** + * Identify timer source + * + * @ret timer Timer source + */ +static struct timer * timer ( void ) { + static struct timer *ts = NULL; + + /* If we have a timer, use it */ + if ( ts ) + return ts; + + /* Scan for a usable timer */ + for ( ts = ts_table ; ts < ts_table_end ; ts++ ) { + if ( ts->init() == 0 ) + return ts; + } + + /* No timer found; we cannot continue */ + assert ( 0 ); + while ( 1 ) {}; +} + +/** + * Read current time + * + * @ret ticks Current time, in ticks + */ +tick_t currticks ( void ) { + tick_t ct; + + ct = timer()->currticks(); + DBG ( "currticks: %ld.%06ld seconds\n", + ct / USECS_IN_SEC, ct % USECS_IN_SEC ); + + return ct; +} + +/** + * Delay + * + * @v usecs Time to delay, in microseconds + */ +void udelay ( unsigned int usecs ) { + timer()->udelay ( usecs ); +} + +/** + * Delay + * + * @v msecs Time to delay, in milliseconds + */ +void mdelay ( unsigned int msecs ) { + while ( msecs-- ) + udelay ( USECS_IN_MSEC ); +} + +/** + * Delay + * + * @v secs Time to delay, in seconds + */ +unsigned int sleep ( unsigned int secs ) { + while ( secs-- ) + mdelay ( MSECS_IN_SEC ); + return 0; +} diff --git a/gpxe/src/core/uri.c b/gpxe/src/core/uri.c new file mode 100644 index 00000000..3b3cf85b --- /dev/null +++ b/gpxe/src/core/uri.c @@ -0,0 +1,383 @@ +/* + * Copyright (C) 2007 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. + */ + +/** @file + * + * Uniform Resource Identifiers + * + */ + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <libgen.h> +#include <gpxe/vsprintf.h> +#include <gpxe/uri.h> + +/** + * Dump URI for debugging + * + * @v uri URI + */ +static void dump_uri ( struct uri *uri ) { + if ( ! uri ) + return; + if ( uri->scheme ) + DBG ( " scheme \"%s\"", uri->scheme ); + if ( uri->opaque ) + DBG ( " opaque \"%s\"", uri->opaque ); + if ( uri->user ) + DBG ( " user \"%s\"", uri->user ); + if ( uri->password ) + DBG ( " password \"%s\"", uri->password ); + if ( uri->host ) + DBG ( " host \"%s\"", uri->host ); + if ( uri->port ) + DBG ( " port \"%s\"", uri->port ); + if ( uri->path ) + DBG ( " path \"%s\"", uri->path ); + if ( uri->query ) + DBG ( " query \"%s\"", uri->query ); + if ( uri->fragment ) + DBG ( " fragment \"%s\"", uri->fragment ); +} + +/** + * Parse URI + * + * @v uri_string URI as a string + * @ret uri URI + * + * Splits a URI into its component parts. The return URI structure is + * dynamically allocated and must eventually be freed by calling + * uri_put(). + */ +struct uri * parse_uri ( const char *uri_string ) { + struct uri *uri; + char *raw; + char *tmp; + char *path = NULL; + char *authority = NULL; + size_t raw_len; + + /* Allocate space for URI struct and a copy of the string */ + raw_len = ( strlen ( uri_string ) + 1 /* NUL */ ); + uri = zalloc ( sizeof ( *uri ) + raw_len ); + if ( ! uri ) + return NULL; + raw = ( ( ( char * ) uri ) + sizeof ( *uri ) ); + + /* Zero URI struct and copy in the raw string */ + memcpy ( raw, uri_string, raw_len ); + + /* Start by chopping off the fragment, if it exists */ + if ( ( tmp = strchr ( raw, '#' ) ) ) { + *(tmp++) = '\0'; + uri->fragment = tmp; + } + + /* Identify absolute/relative URI */ + if ( ( tmp = strchr ( raw, ':' ) ) ) { + /* Absolute URI: identify hierarchical/opaque */ + uri->scheme = raw; + *(tmp++) = '\0'; + if ( *tmp == '/' ) { + /* Absolute URI with hierarchical part */ + path = tmp; + } else { + /* Absolute URI with opaque part */ + uri->opaque = tmp; + } + } else { + /* Relative URI */ + path = raw; + } + + /* If we don't have a path (i.e. we have an absolute URI with + * an opaque portion, we're already finished processing + */ + if ( ! path ) + goto done; + + /* Chop off the query, if it exists */ + if ( ( tmp = strchr ( path, '?' ) ) ) { + *(tmp++) = '\0'; + uri->query = tmp; + } + + /* Identify net/absolute/relative path */ + if ( strncmp ( path, "//", 2 ) == 0 ) { + /* Net path. If this is terminated by the first '/' + * of an absolute path, then we have no space for a + * terminator after the authority field, so shuffle + * the authority down by one byte, overwriting one of + * the two slashes. + */ + authority = ( path + 2 ); + if ( ( tmp = strchr ( authority, '/' ) ) ) { + /* Shuffle down */ + uri->path = tmp; + memmove ( ( authority - 1 ), authority, + ( tmp - authority ) ); + authority--; + *(--tmp) = '\0'; + } + } else { + /* Absolute/relative path */ + uri->path = path; + } + + /* Split authority into user[:password] and host[:port] portions */ + if ( ( tmp = strchr ( authority, '@' ) ) ) { + /* Has user[:password] */ + *(tmp++) = '\0'; + uri->host = tmp; + uri->user = authority; + if ( ( tmp = strchr ( authority, ':' ) ) ) { + /* Has password */ + *(tmp++) = '\0'; + uri->password = tmp; + } + } else { + /* No user:password */ + uri->host = authority; + } + + /* Split host into host[:port] */ + if ( ( tmp = strchr ( uri->host, ':' ) ) ) { + *(tmp++) = '\0'; + uri->port = tmp; + } + + done: + DBG ( "URI \"%s\" split into", uri_string ); + dump_uri ( uri ); + DBG ( "\n" ); + + return uri; +} + +/** + * Get port from URI + * + * @v uri URI, or NULL + * @v default_port Default port to use if none specified in URI + * @ret port Port + */ +unsigned int uri_port ( struct uri *uri, unsigned int default_port ) { + if ( ( ! uri ) || ( ! uri->port ) ) + return default_port; + return ( strtoul ( uri->port, NULL, 0 ) ); +} + +/** + * Unparse URI + * + * @v buf Buffer to fill with URI string + * @v size Size of buffer + * @v uri URI to write into buffer, or NULL + * @ret len Length of URI string + */ +int unparse_uri ( char *buf, size_t size, struct uri *uri ) { + int used = 0; + + DBG ( "URI unparsing" ); + dump_uri ( uri ); + DBG ( "\n" ); + + /* Special-case NULL URI */ + if ( ! uri ) { + if ( size ) + buf[0] = '\0'; + return 0; + } + + /* Special-case opaque URIs */ + if ( uri->opaque ) { + return ssnprintf ( ( buf + used ), ( size - used ), + "%s:%s", uri->scheme, uri->opaque ); + } + + /* scheme:// */ + if ( uri->scheme ) { + used += ssnprintf ( ( buf + used ), ( size - used ), + "%s://", uri->scheme ); + } + + /* [user[:password]@]host[:port] */ + if ( uri->host ) { + if ( uri->user ) { + used += ssnprintf ( ( buf + used ), ( size - used ), + "%s", uri->user ); + if ( uri->password ) { + used += ssnprintf ( ( buf + used ), + ( size - used ), + ":%s", uri->password ); + } + used += ssnprintf ( ( buf + used ), ( size - used ), + "@" ); + } + used += ssnprintf ( ( buf + used ), ( size - used ), "%s", + uri->host ); + if ( uri->port ) { + used += ssnprintf ( ( buf + used ), ( size - used ), + ":%s", uri->port ); + } + } + + /* /path */ + if ( uri->path ) { + used += ssnprintf ( ( buf + used ), ( size - used ), + "%s", uri->path ); + } + + /* ?query */ + if ( uri->query ) { + used += ssnprintf ( ( buf + used ), ( size - used ), + "?%s", uri->query ); + } + + /* #fragment */ + if ( uri->fragment ) { + used += ssnprintf ( ( buf + used ), ( size - used ), + "#%s", uri->fragment ); + } + + return used; +} + +/** + * Duplicate URI + * + * @v uri URI + * @ret uri Duplicate URI + * + * Creates a modifiable copy of a URI. + */ +struct uri * uri_dup ( struct uri *uri ) { + size_t len = ( unparse_uri ( NULL, 0, uri ) + 1 ); + char buf[len]; + + unparse_uri ( buf, len, uri ); + return parse_uri ( buf ); +} + +/** + * Resolve base+relative path + * + * @v base_uri Base path + * @v relative_uri Relative path + * @ret resolved_uri Resolved path + * + * Takes a base path (e.g. "/var/lib/tftpboot/vmlinuz" and a relative + * path (e.g. "initrd.gz") and produces a new path + * (e.g. "/var/lib/tftpboot/initrd.gz"). Note that any non-directory + * portion of the base path will automatically be stripped; this + * matches the semantics used when resolving the path component of + * URIs. + */ +char * resolve_path ( const char *base_path, + const char *relative_path ) { + size_t base_len = ( strlen ( base_path ) + 1 ); + char base_path_copy[base_len]; + char *base_tmp = base_path_copy; + char *resolved; + + /* If relative path is absolute, just re-use it */ + if ( relative_path[0] == '/' ) + return strdup ( relative_path ); + + /* Create modifiable copy of path for dirname() */ + memcpy ( base_tmp, base_path, base_len ); + base_tmp = dirname ( base_tmp ); + + /* Process "./" and "../" elements */ + while ( *relative_path == '.' ) { + relative_path++; + if ( *relative_path == 0 ) { + /* Do nothing */ + } else if ( *relative_path == '/' ) { + relative_path++; + } else if ( *relative_path == '.' ) { + relative_path++; + if ( *relative_path == 0 ) { + base_tmp = dirname ( base_tmp ); + } else if ( *relative_path == '/' ) { + base_tmp = dirname ( base_tmp ); + relative_path++; + } else { + relative_path -= 2; + break; + } + } else { + relative_path--; + break; + } + } + + /* Create and return new path */ + if ( asprintf ( &resolved, "%s%s%s", base_tmp, + ( ( base_tmp[ strlen ( base_tmp ) - 1 ] == '/' ) ? + "" : "/" ), relative_path ) < 0 ) + return NULL; + + return resolved; +} + +/** + * Resolve base+relative URI + * + * @v base_uri Base URI, or NULL + * @v relative_uri Relative URI + * @ret resolved_uri Resolved URI + * + * Takes a base URI (e.g. "http://etherboot.org/kernels/vmlinuz" and a + * relative URI (e.g. "../initrds/initrd.gz") and produces a new URI + * (e.g. "http://etherboot.org/initrds/initrd.gz"). + */ +struct uri * resolve_uri ( struct uri *base_uri, + struct uri *relative_uri ) { + struct uri tmp_uri; + char *tmp_path = NULL; + struct uri *new_uri; + + /* If relative URI is absolute, just re-use it */ + if ( uri_is_absolute ( relative_uri ) || ( ! base_uri ) ) + return uri_get ( relative_uri ); + + /* Mangle URI */ + memcpy ( &tmp_uri, base_uri, sizeof ( tmp_uri ) ); + if ( relative_uri->path ) { + tmp_path = resolve_path ( ( base_uri->path ? + base_uri->path : "/" ), + relative_uri->path ); + tmp_uri.path = tmp_path; + tmp_uri.query = relative_uri->query; + tmp_uri.fragment = relative_uri->fragment; + } else if ( relative_uri->query ) { + tmp_uri.query = relative_uri->query; + tmp_uri.fragment = relative_uri->fragment; + } else if ( relative_uri->fragment ) { + tmp_uri.fragment = relative_uri->fragment; + } + + /* Create demangled URI */ + new_uri = uri_dup ( &tmp_uri ); + free ( tmp_path ); + return new_uri; +} diff --git a/gpxe/src/core/uuid.c b/gpxe/src/core/uuid.c new file mode 100644 index 00000000..dae26c16 --- /dev/null +++ b/gpxe/src/core/uuid.c @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2007 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. + */ + +#include <stdint.h> +#include <stdio.h> +#include <byteswap.h> +#include <gpxe/uuid.h> + +/** @file + * + * Universally unique IDs + * + */ + +/** + * Convert UUID to printable string + * + * @v uuid UUID + * @ret string UUID in canonical form + */ +char * uuid_ntoa ( union uuid *uuid ) { + static char buf[37]; /* "00000000-0000-0000-0000-000000000000" */ + + sprintf ( buf, "%08lx-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", + le32_to_cpu ( uuid->canonical.a ), + le16_to_cpu ( uuid->canonical.b ), + le16_to_cpu ( uuid->canonical.c ), + be16_to_cpu ( uuid->canonical.d ), + uuid->canonical.e[0], uuid->canonical.e[1], + uuid->canonical.e[2], uuid->canonical.e[3], + uuid->canonical.e[4], uuid->canonical.e[5] ); + return buf; +} diff --git a/gpxe/src/core/vsprintf.c b/gpxe/src/core/vsprintf.c new file mode 100644 index 00000000..4457fe4f --- /dev/null +++ b/gpxe/src/core/vsprintf.c @@ -0,0 +1,421 @@ +/* + * Copyright (C) 2006 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. + */ + +#include <stddef.h> +#include <stdarg.h> +#include <stdio.h> +#include <console.h> +#include <errno.h> +#include <gpxe/vsprintf.h> + +/** @file */ + +#define CHAR_LEN 0 /**< "hh" length modifier */ +#define SHORT_LEN 1 /**< "h" length modifier */ +#define INT_LEN 2 /**< no length modifier */ +#define LONG_LEN 3 /**< "l" length modifier */ +#define LONGLONG_LEN 4 /**< "ll" length modifier */ +#define SIZE_T_LEN 5 /**< "z" length modifier */ + +static uint8_t type_sizes[] = { + [CHAR_LEN] = sizeof ( char ), + [SHORT_LEN] = sizeof ( short ), + [INT_LEN] = sizeof ( int ), + [LONG_LEN] = sizeof ( long ), + [LONGLONG_LEN] = sizeof ( long long ), + [SIZE_T_LEN] = sizeof ( size_t ), +}; + +/** + * Use lower-case for hexadecimal digits + * + * Note that this value is set to 0x20 since that makes for very + * efficient calculations. (Bitwise-ORing with @c LCASE converts to a + * lower-case character, for example.) + */ +#define LCASE 0x20 + +/** + * Use "alternate form" + * + * For hexadecimal numbers, this means to add a "0x" or "0X" prefix to + * the number. + */ +#define ALT_FORM 0x02 + +/** + * Format a hexadecimal number + * + * @v end End of buffer to contain number + * @v num Number to format + * @v width Minimum field width + * @ret ptr End of buffer + * + * Fills a buffer in reverse order with a formatted hexadecimal + * number. The number will be zero-padded to the specified width. + * Lower-case and "alternate form" (i.e. "0x" prefix) flags may be + * set. + * + * There must be enough space in the buffer to contain the largest + * number that this function can format. + */ +static char * format_hex ( char *end, unsigned long long num, int width, + int flags ) { + char *ptr = end; + int case_mod; + + /* Generate the number */ + case_mod = flags & LCASE; + do { + *(--ptr) = "0123456789ABCDEF"[ num & 0xf ] | case_mod; + num >>= 4; + } while ( num ); + + /* Zero-pad to width */ + while ( ( end - ptr ) < width ) + *(--ptr) = '0'; + + /* Add "0x" or "0X" if alternate form specified */ + if ( flags & ALT_FORM ) { + *(--ptr) = 'X' | case_mod; + *(--ptr) = '0'; + } + + return ptr; +} + +/** + * Format a decimal number + * + * @v end End of buffer to contain number + * @v num Number to format + * @v width Minimum field width + * @ret ptr End of buffer + * + * Fills a buffer in reverse order with a formatted decimal number. + * The number will be space-padded to the specified width. + * + * There must be enough space in the buffer to contain the largest + * number that this function can format. + */ +static char * format_decimal ( char *end, signed long num, int width ) { + char *ptr = end; + int negative = 0; + + /* Generate the number */ + if ( num < 0 ) { + negative = 1; + num = -num; + } + do { + *(--ptr) = '0' + ( num % 10 ); + num /= 10; + } while ( num ); + + /* Add "-" if necessary */ + if ( negative ) + *(--ptr) = '-'; + + /* Space-pad to width */ + while ( ( end - ptr ) < width ) + *(--ptr) = ' '; + + return ptr; +} + +/** + * Print character via a printf context + * + * @v ctx Context + * @v c Character + * + * Call's the printf_context::handler() method and increments + * printf_context::len. + */ +static inline void cputchar ( struct printf_context *ctx, unsigned int c ) { + ctx->handler ( ctx, c ); + ++ctx->len; +} + +/** + * Write a formatted string to a printf context + * + * @v ctx Context + * @v fmt Format string + * @v args Arguments corresponding to the format string + * @ret len Length of formatted string + */ +size_t vcprintf ( struct printf_context *ctx, const char *fmt, va_list args ) { + int flags; + int width; + uint8_t *length; + char *ptr; + char tmp_buf[32]; /* 32 is enough for all numerical formats. + * Insane width fields could overflow this buffer. */ + + /* Initialise context */ + ctx->len = 0; + + for ( ; *fmt ; fmt++ ) { + /* Pass through ordinary characters */ + if ( *fmt != '%' ) { + cputchar ( ctx, *fmt ); + continue; + } + fmt++; + /* Process flag characters */ + flags = 0; + for ( ; ; fmt++ ) { + if ( *fmt == '#' ) { + flags |= ALT_FORM; + } else if ( *fmt == '0' ) { + /* We always 0-pad hex and space-pad decimal */ + } else { + /* End of flag characters */ + break; + } + } + /* Process field width */ + width = 0; + for ( ; ; fmt++ ) { + if ( ( ( unsigned ) ( *fmt - '0' ) ) < 10 ) { + width = ( width * 10 ) + ( *fmt - '0' ); + } else { + break; + } + } + /* We don't do floating point */ + /* Process length modifier */ + length = &type_sizes[INT_LEN]; + for ( ; ; fmt++ ) { + if ( *fmt == 'h' ) { + length--; + } else if ( *fmt == 'l' ) { + length++; + } else if ( *fmt == 'z' ) { + length = &type_sizes[SIZE_T_LEN]; + } else { + break; + } + } + /* Process conversion specifier */ + ptr = tmp_buf + sizeof ( tmp_buf ) - 1; + *ptr = '\0'; + if ( *fmt == 'c' ) { + cputchar ( ctx, va_arg ( args, unsigned int ) ); + } else if ( *fmt == 's' ) { + ptr = va_arg ( args, char * ); + if ( ! ptr ) + ptr = "<NULL>"; + } else if ( *fmt == 'p' ) { + intptr_t ptrval; + + ptrval = ( intptr_t ) va_arg ( args, void * ); + ptr = format_hex ( ptr, ptrval, width, + ( ALT_FORM | LCASE ) ); + } else if ( ( *fmt & ~0x20 ) == 'X' ) { + unsigned long long hex; + + flags |= ( *fmt & 0x20 ); /* LCASE */ + if ( *length >= sizeof ( unsigned long long ) ) { + hex = va_arg ( args, unsigned long long ); + } else if ( *length >= sizeof ( unsigned long ) ) { + hex = va_arg ( args, unsigned long ); + } else { + hex = va_arg ( args, unsigned int ); + } + ptr = format_hex ( ptr, hex, width, flags ); + } else if ( ( *fmt == 'd' ) || ( *fmt == 'i' ) ){ + signed long decimal; + + if ( *length >= sizeof ( signed long ) ) { + decimal = va_arg ( args, signed long ); + } else { + decimal = va_arg ( args, signed int ); + } + ptr = format_decimal ( ptr, decimal, width ); + } else { + *(--ptr) = *fmt; + } + /* Write out conversion result */ + for ( ; *ptr ; ptr++ ) { + cputchar ( ctx, *ptr ); + } + } + + return ctx->len; +} + +/** Context used by vsnprintf() and friends */ +struct sputc_context { + struct printf_context ctx; + /** Buffer for formatted string (used by printf_sputc()) */ + char *buf; + /** Buffer length (used by printf_sputc()) */ + size_t max_len; +}; + +/** + * Write character to buffer + * + * @v ctx Context + * @v c Character + */ +static void printf_sputc ( struct printf_context *ctx, unsigned int c ) { + struct sputc_context * sctx = + container_of ( ctx, struct sputc_context, ctx ); + + if ( ctx->len < sctx->max_len ) + sctx->buf[ctx->len] = c; +} + +/** + * Write a formatted string to a buffer + * + * @v buf Buffer into which to write the string + * @v size Size of buffer + * @v fmt Format string + * @v args Arguments corresponding to the format string + * @ret len Length of formatted string + * + * If the buffer is too small to contain the string, the returned + * length is the length that would have been written had enough space + * been available. + */ +int vsnprintf ( char *buf, size_t size, const char *fmt, va_list args ) { + struct sputc_context sctx; + size_t len; + size_t end; + + /* Hand off to vcprintf */ + sctx.ctx.handler = printf_sputc; + sctx.buf = buf; + sctx.max_len = size; + len = vcprintf ( &sctx.ctx, fmt, args ); + + /* Add trailing NUL */ + if ( size ) { + end = size - 1; + if ( len < end ) + end = len; + buf[end] = '\0'; + } + + return len; +} + +/** + * Write a formatted string to a buffer + * + * @v buf Buffer into which to write the string + * @v size Size of buffer + * @v fmt Format string + * @v ... Arguments corresponding to the format string + * @ret len Length of formatted string + */ +int snprintf ( char *buf, size_t size, const char *fmt, ... ) { + va_list args; + int i; + + va_start ( args, fmt ); + i = vsnprintf ( buf, size, fmt, args ); + va_end ( args ); + return i; +} + +/** + * Version of vsnprintf() that accepts a signed buffer size + * + * @v buf Buffer into which to write the string + * @v size Size of buffer + * @v fmt Format string + * @v args Arguments corresponding to the format string + * @ret len Length of formatted string + */ +int vssnprintf ( char *buf, ssize_t ssize, const char *fmt, va_list args ) { + + /* Treat negative buffer size as zero buffer size */ + if ( ssize < 0 ) + ssize = 0; + + /* Hand off to vsnprintf */ + return vsnprintf ( buf, ssize, fmt, args ); +} + +/** + * Version of vsnprintf() that accepts a signed buffer size + * + * @v buf Buffer into which to write the string + * @v size Size of buffer + * @v fmt Format string + * @v ... Arguments corresponding to the format string + * @ret len Length of formatted string + */ +int ssnprintf ( char *buf, ssize_t ssize, const char *fmt, ... ) { + va_list args; + int len; + + /* Hand off to vssnprintf */ + va_start ( args, fmt ); + len = vssnprintf ( buf, ssize, fmt, args ); + va_end ( args ); + return len; +} + +/** + * Write character to console + * + * @v ctx Context + * @v c Character + */ +static void printf_putchar ( struct printf_context *ctx __unused, + unsigned int c ) { + putchar ( c ); +} + +/** + * Write a formatted string to the console + * + * @v fmt Format string + * @v args Arguments corresponding to the format string + * @ret len Length of formatted string + */ +int vprintf ( const char *fmt, va_list args ) { + struct printf_context ctx; + + /* Hand off to vcprintf */ + ctx.handler = printf_putchar; + return vcprintf ( &ctx, fmt, args ); +} + +/** + * Write a formatted string to the console. + * + * @v fmt Format string + * @v ... Arguments corresponding to the format string + * @ret len Length of formatted string + */ +int printf ( const char *fmt, ... ) { + va_list args; + int i; + + va_start ( args, fmt ); + i = vprintf ( fmt, args ); + va_end ( args ); + return i; +} diff --git a/gpxe/src/core/xfer.c b/gpxe/src/core/xfer.c new file mode 100644 index 00000000..14c77d64 --- /dev/null +++ b/gpxe/src/core/xfer.c @@ -0,0 +1,405 @@ +/* + * Copyright (C) 2007 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. + */ + +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <gpxe/xfer.h> + +/** @file + * + * Data transfer interfaces + * + */ + +/** + * Close data transfer interface + * + * @v xfer Data transfer interface + * @v rc Reason for close + */ +void xfer_close ( struct xfer_interface *xfer, int rc ) { + struct xfer_interface *dest = xfer_get_dest ( xfer ); + + DBGC ( xfer, "XFER %p->%p close\n", xfer, dest ); + + xfer_unplug ( xfer ); + dest->op->close ( dest, rc ); + xfer_put ( dest ); +} + +/** + * Send redirection event + * + * @v xfer Data transfer interface + * @v type New location type + * @v args Remaining arguments depend upon location type + * @ret rc Return status code + */ +int xfer_vredirect ( struct xfer_interface *xfer, int type, va_list args ) { + struct xfer_interface *dest = xfer_get_dest ( xfer ); + int rc; + + DBGC ( xfer, "XFER %p->%p redirect\n", xfer, dest ); + + rc = dest->op->vredirect ( dest, type, args ); + + if ( rc != 0 ) { + DBGC ( xfer, "XFER %p<-%p redirect: %s\n", xfer, dest, + strerror ( rc ) ); + } + xfer_put ( dest ); + return rc; +} + +/** + * Send redirection event + * + * @v xfer Data transfer interface + * @v type New location type + * @v ... Remaining arguments depend upon location type + * @ret rc Return status code + */ +int xfer_redirect ( struct xfer_interface *xfer, int type, ... ) { + va_list args; + int rc; + + va_start ( args, type ); + rc = xfer_vredirect ( xfer, type, args ); + va_end ( args ); + return rc; +} + +/** + * Check flow control window + * + * @v xfer Data transfer interface + * @ret len Length of window + */ +size_t xfer_window ( struct xfer_interface *xfer ) { + struct xfer_interface *dest = xfer_get_dest ( xfer ); + size_t len; + + len = dest->op->window ( dest ); + + xfer_put ( dest ); + return len; +} + +/** + * Allocate I/O buffer + * + * @v xfer Data transfer interface + * @v len I/O buffer payload length + * @ret iobuf I/O buffer + */ +struct io_buffer * xfer_alloc_iob ( struct xfer_interface *xfer, size_t len ) { + struct xfer_interface *dest = xfer_get_dest ( xfer ); + struct io_buffer *iobuf; + + DBGC ( xfer, "XFER %p->%p alloc_iob %zd\n", xfer, dest, len ); + + iobuf = dest->op->alloc_iob ( dest, len ); + + if ( ! iobuf ) { + DBGC ( xfer, "XFER %p<-%p alloc_iob failed\n", xfer, dest ); + } + xfer_put ( dest ); + return iobuf; +} + +/** + * Deliver datagram as I/O buffer with metadata + * + * @v xfer Data transfer interface + * @v iobuf Datagram I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + */ +int xfer_deliver_iob_meta ( struct xfer_interface *xfer, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + struct xfer_interface *dest = xfer_get_dest ( xfer ); + int rc; + + DBGC ( xfer, "XFER %p->%p deliver_iob %zd\n", xfer, dest, + iob_len ( iobuf ) ); + + rc = dest->op->deliver_iob ( dest, iobuf, meta ); + + if ( rc != 0 ) { + DBGC ( xfer, "XFER %p<-%p deliver_iob: %s\n", xfer, dest, + strerror ( rc ) ); + } + xfer_put ( dest ); + return rc; +} + +/** + * Deliver datagram as I/O buffer with metadata + * + * @v xfer Data transfer interface + * @v iobuf Datagram I/O buffer + * @ret rc Return status code + */ +int xfer_deliver_iob ( struct xfer_interface *xfer, + struct io_buffer *iobuf ) { + static struct xfer_metadata dummy_metadata; + return xfer_deliver_iob_meta ( xfer, iobuf, &dummy_metadata ); +} + +/** + * Deliver datagram as raw data + * + * @v xfer Data transfer interface + * @v iobuf Datagram I/O buffer + * @ret rc Return status code + */ +int xfer_deliver_raw ( struct xfer_interface *xfer, + const void *data, size_t len ) { + struct xfer_interface *dest = xfer_get_dest ( xfer ); + int rc; + + DBGC ( xfer, "XFER %p->%p deliver_raw %p+%zd\n", xfer, dest, + data, len ); + + rc = dest->op->deliver_raw ( dest, data, len ); + + if ( rc != 0 ) { + DBGC ( xfer, "XFER %p<-%p deliver_raw: %s\n", xfer, dest, + strerror ( rc ) ); + } + xfer_put ( dest ); + return rc; +} + +/** + * Deliver formatted string + * + * @v xfer Data transfer interface + * @v format Format string + * @v args Arguments corresponding to the format string + * @ret rc Return status code + */ +int xfer_vprintf ( struct xfer_interface *xfer, const char *format, + va_list args ) { + size_t len; + va_list args_tmp; + + va_copy ( args_tmp, args ); + len = vsnprintf ( NULL, 0, format, args ); + { + char buf[len + 1]; + vsnprintf ( buf, sizeof ( buf ), format, args_tmp ); + va_end ( args_tmp ); + return xfer_deliver_raw ( xfer, buf, len ); + } +} + +/** + * Deliver formatted string + * + * @v xfer Data transfer interface + * @v format Format string + * @v ... Arguments corresponding to the format string + * @ret rc Return status code + */ +int xfer_printf ( struct xfer_interface *xfer, const char *format, ... ) { + va_list args; + int rc; + + va_start ( args, format ); + rc = xfer_vprintf ( xfer, format, args ); + va_end ( args ); + return rc; +} + +/** + * Seek to position + * + * @v xfer Data transfer interface + * @v offset Offset to new position + * @v whence Basis for new position + * @ret rc Return status code + */ +int xfer_seek ( struct xfer_interface *xfer, off_t offset, int whence ) { + struct io_buffer *iobuf; + struct xfer_metadata meta = { + .offset = offset, + .whence = whence, + }; + + DBGC ( xfer, "XFER %p seek %s+%ld\n", xfer, + whence_text ( whence ), offset ); + + /* Allocate and send a zero-length data buffer */ + iobuf = xfer_alloc_iob ( xfer, 0 ); + if ( ! iobuf ) + return -ENOMEM; + return xfer_deliver_iob_meta ( xfer, iobuf, &meta ); +} + +/**************************************************************************** + * + * Helper methods + * + * These functions are designed to be used as methods in the + * xfer_interface_operations table. + * + */ + +/** + * Ignore close() event + * + * @v xfer Data transfer interface + * @v rc Reason for close + */ +void ignore_xfer_close ( struct xfer_interface *xfer __unused, + int rc __unused ) { + /* Nothing to do */ +} + +/** + * Ignore vredirect() event + * + * @v xfer Data transfer interface + * @v type New location type + * @v args Remaining arguments depend upon location type + * @ret rc Return status code + */ +int ignore_xfer_vredirect ( struct xfer_interface *xfer __unused, + int type __unused, va_list args __unused ) { + return 0; +} + +/** + * Unlimited flow control window + * + * @v xfer Data transfer interface + * @ret len Length of window + * + * This handler indicates that the interface is always ready to accept + * data. + */ +size_t unlimited_xfer_window ( struct xfer_interface *xfer __unused ) { + return ~( ( size_t ) 0 ); +} + +/** + * No flow control window + * + * @v xfer Data transfer interface + * @ret len Length of window + * + * This handler indicates that the interface is never ready to accept + * data. + */ +size_t no_xfer_window ( struct xfer_interface *xfer __unused ) { + return 0; +} + +/** + * Allocate I/O buffer + * + * @v xfer Data transfer interface + * @v len I/O buffer payload length + * @ret iobuf I/O buffer + */ +struct io_buffer * +default_xfer_alloc_iob ( struct xfer_interface *xfer __unused, size_t len ) { + return alloc_iob ( len ); +} + +/** + * Deliver datagram as raw data + * + * @v xfer Data transfer interface + * @v iobuf Datagram I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + * + * This function is intended to be used as the deliver() method for + * data transfer interfaces that prefer to handle raw data. + */ +int xfer_deliver_as_raw ( struct xfer_interface *xfer, + struct io_buffer *iobuf, + struct xfer_metadata *meta __unused ) { + int rc; + + rc = xfer->op->deliver_raw ( xfer, iobuf->data, iob_len ( iobuf ) ); + free_iob ( iobuf ); + return rc; +} + +/** + * Deliver datagram as I/O buffer + * + * @v xfer Data transfer interface + * @v data Data buffer + * @v len Length of data buffer + * @ret rc Return status code + * + * This function is intended to be used as the deliver_raw() method + * for data transfer interfaces that prefer to handle I/O buffers. + */ +int xfer_deliver_as_iob ( struct xfer_interface *xfer, + const void *data, size_t len ) { + struct io_buffer *iobuf; + + iobuf = xfer->op->alloc_iob ( xfer, len ); + if ( ! iobuf ) + return -ENOMEM; + + memcpy ( iob_put ( iobuf, len ), data, len ); + return xfer->op->deliver_iob ( xfer, iobuf, NULL ); +} + +/** + * Ignore datagram as raw data event + * + * @v xfer Data transfer interface + * @v data Data buffer + * @v len Length of data buffer + * @ret rc Return status code + */ +int ignore_xfer_deliver_raw ( struct xfer_interface *xfer, + const void *data __unused, size_t len ) { + DBGC ( xfer, "XFER %p %zd bytes delivered %s\n", xfer, len, + ( ( xfer == &null_xfer ) ? + "before connection" : "after termination" ) ); + return 0; +} + +/** Null data transfer interface operations */ +struct xfer_interface_operations null_xfer_ops = { + .close = ignore_xfer_close, + .vredirect = ignore_xfer_vredirect, + .window = unlimited_xfer_window, + .alloc_iob = default_xfer_alloc_iob, + .deliver_iob = xfer_deliver_as_raw, + .deliver_raw = ignore_xfer_deliver_raw, +}; + +/** + * Null data transfer interface + * + * This is the interface to which data transfer interfaces are + * connected when unplugged. It will never generate messages, and + * will silently absorb all received messages. + */ +struct xfer_interface null_xfer = XFER_INIT ( &null_xfer_ops ); |