diff options
author | Yang Zhang <yang.z.zhang@intel.com> | 2015-08-28 09:58:54 +0800 |
---|---|---|
committer | Yang Zhang <yang.z.zhang@intel.com> | 2015-09-01 12:44:00 +0800 |
commit | e44e3482bdb4d0ebde2d8b41830ac2cdb07948fb (patch) | |
tree | 66b09f592c55df2878107a468a91d21506104d3f /qemu/roms/ipxe/src/core | |
parent | 9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 (diff) |
Add qemu 2.4.0
Change-Id: Ic99cbad4b61f8b127b7dc74d04576c0bcbaaf4f5
Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
Diffstat (limited to 'qemu/roms/ipxe/src/core')
82 files changed, 17508 insertions, 0 deletions
diff --git a/qemu/roms/ipxe/src/core/acpi.c b/qemu/roms/ipxe/src/core/acpi.c new file mode 100644 index 000000000..330f50631 --- /dev/null +++ b/qemu/roms/ipxe/src/core/acpi.c @@ -0,0 +1,86 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <errno.h> +#include <ipxe/acpi.h> +#include <ipxe/interface.h> + +/** @file + * + * ACPI support functions + * + */ + +/****************************************************************************** + * + * Utility 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; +} + +/****************************************************************************** + * + * Interface methods + * + ****************************************************************************** + */ + +/** + * Describe object in an ACPI table + * + * @v intf Interface + * @v acpi ACPI table + * @v len Length of ACPI table + * @ret rc Return status code + */ +int acpi_describe ( struct interface *intf, + struct acpi_description_header *acpi, size_t len ) { + struct interface *dest; + acpi_describe_TYPE ( void * ) *op = + intf_get_dest_op ( intf, acpi_describe, &dest ); + void *object = intf_object ( dest ); + int rc; + + if ( op ) { + rc = op ( object, acpi, len ); + } else { + /* Default is to fail to describe */ + rc = -EOPNOTSUPP; + } + + intf_put ( dest ); + return rc; +} diff --git a/qemu/roms/ipxe/src/core/ansicol.c b/qemu/roms/ipxe/src/core/ansicol.c new file mode 100644 index 000000000..142a00f8d --- /dev/null +++ b/qemu/roms/ipxe/src/core/ansicol.c @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdio.h> +#include <errno.h> +#include <assert.h> +#include <ipxe/ansiesc.h> +#include <ipxe/ansicol.h> +#include <config/colour.h> + +/** @file + * + * ANSI colours + * + */ + +/** ANSI colour pair definitions */ +static struct ansicol_pair ansicol_pairs[] = { + [CPAIR_DEFAULT] = { COLOR_DEFAULT, COLOR_DEFAULT }, + [CPAIR_NORMAL] = { COLOR_NORMAL_FG, COLOR_NORMAL_BG }, + [CPAIR_SELECT] = { COLOR_SELECT_FG, COLOR_SELECT_BG }, + [CPAIR_SEPARATOR] = { COLOR_SEPARATOR_FG, COLOR_SEPARATOR_BG }, + [CPAIR_EDIT] = { COLOR_EDIT_FG, COLOR_EDIT_BG }, + [CPAIR_ALERT] = { COLOR_ALERT_FG, COLOR_ALERT_BG }, + [CPAIR_URL] = { COLOR_URL_FG, COLOR_URL_BG }, + [CPAIR_PXE] = { COLOR_PXE_FG, COLOR_PXE_BG }, +}; + +/** + * Set ANSI colour (when no colour definition support is present) + * + * @v colour Colour index + * @v which Foreground/background selector + */ +__weak void ansicol_set ( unsigned int colour, unsigned int which ) { + + /* Colour indices are hardcoded and should never be out of range */ + assert ( colour < 10 ); + + /* Set basic colour */ + printf ( CSI "%c%dm", which, colour ); +} + +/** + * Set ANSI foreground colour + * + * @v colour Colour index + */ +static void ansicol_foreground ( unsigned int colour ) { + ansicol_set ( colour, '3' ); +} + +/** + * Set ANSI background colour + * + * @v colour Colour index + */ +static void ansicol_background ( unsigned int colour ) { + ansicol_set ( colour, '4' ); +} + +/** + * Set ANSI foreground and background colour + * + * @v cpair Colour pair index + */ +void ansicol_set_pair ( unsigned int cpair ) { + struct ansicol_pair *pair; + + /* Colour pair indices are hardcoded and should never be out of range */ + assert ( cpair < ( sizeof ( ansicol_pairs ) / + sizeof ( ansicol_pairs[0] ) ) ); + + /* Set both foreground and background colours */ + pair = &ansicol_pairs[cpair]; + ansicol_foreground ( pair->foreground ); + ansicol_background ( pair->background ); +} + +/** + * Define ANSI colour pair + * + * @v cpair Colour pair index + * @v foreground Foreground colour index + * @v background Background colour index + * @ret rc Return status code + */ +int ansicol_define_pair ( unsigned int cpair, unsigned int foreground, + unsigned int background ) { + struct ansicol_pair *pair; + + /* Fail if colour index is out of range */ + if ( cpair >= ( sizeof ( ansicol_pairs ) / sizeof ( ansicol_pairs[0] ))) + return -EINVAL; + + /* Update colour pair definition */ + pair = &ansicol_pairs[cpair]; + pair->foreground = foreground; + pair->background = background; + DBGC ( &ansicol_pairs[0], "ANSICOL redefined colour pair %d as " + "foreground %d background %d\n", cpair, foreground, background ); + + return 0; +} diff --git a/qemu/roms/ipxe/src/core/ansicoldef.c b/qemu/roms/ipxe/src/core/ansicoldef.c new file mode 100644 index 000000000..dd89f3b70 --- /dev/null +++ b/qemu/roms/ipxe/src/core/ansicoldef.c @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdio.h> +#include <errno.h> +#include <ipxe/ansiesc.h> +#include <ipxe/ansicol.h> +#include <config/colour.h> + +/** @file + * + * ANSI colour definitions + * + */ + +/** + * Construct ANSI colour definition + * + * @v basic Basic colour + * @v rgb 24-bit RGB value (or ANSICOL_NO_RGB) + * @ret ansicol ANSI colour definition + */ +#define ANSICOL_DEFINE( basic, rgb ) ( ( (basic) << 28 ) | (rgb) ) + +/** + * Extract basic colour from ANSI colour definition + * + * @v ansicol ANSI colour definition + * @ret basic Basic colour + */ +#define ANSICOL_BASIC( ansicol ) ( (ansicol) >> 28 ) + +/** + * Extract 24-bit RGB value from ANSI colour definition + * + * @v ansicol ANSI colour definition + * @ret rgb 24-bit RGB value + */ +#define ANSICOL_RGB( ansicol ) ( ( (ansicol) >> 0 ) & 0xffffffUL ) + +/** + * Extract 24-bit RGB value red component from ANSI colour definition + * + * @v ansicol ANSI colour definition + * @ret red Red component + */ +#define ANSICOL_RED( ansicol ) ( ( (ansicol) >> 16 ) & 0xff ) + +/** + * Extract 24-bit RGB value green component from ANSI colour definition + * + * @v ansicol ANSI colour definition + * @ret green Green component + */ +#define ANSICOL_GREEN( ansicol ) ( ( (ansicol) >> 8 ) & 0xff ) + +/** + * Extract 24-bit RGB value blue component from ANSI colour definition + * + * @v ansicol ANSI colour definition + * @ret blue Blue component + */ +#define ANSICOL_BLUE( ansicol ) ( ( (ansicol) >> 0 ) & 0xff ) + +/** + * Construct default ANSI colour definition + * + * @v basic Basic colour + * @ret ansicol ANSI colour definition + * + * Colours default to being just a basic colour. If the colour + * matches the normal UI text background colour, then its basic colour + * value is set to @c ANSICOL_MAGIC. + */ +#define ANSICOL_DEFAULT( basic ) \ + ANSICOL_DEFINE ( ( ( (basic) == COLOR_NORMAL_BG ) ? \ + ANSICOL_MAGIC : (basic) ), \ + ANSICOL_NO_RGB ) + +/** ANSI colour definitions */ +static uint32_t ansicols[] = { + [COLOR_BLACK] = ANSICOL_DEFAULT ( COLOR_BLACK ), + [COLOR_RED] = ANSICOL_DEFAULT ( COLOR_RED ), + [COLOR_GREEN] = ANSICOL_DEFAULT ( COLOR_GREEN ), + [COLOR_YELLOW] = ANSICOL_DEFAULT ( COLOR_YELLOW ), + [COLOR_BLUE] = ANSICOL_DEFAULT ( COLOR_BLUE ), + [COLOR_MAGENTA] = ANSICOL_DEFAULT ( COLOR_MAGENTA ), + [COLOR_CYAN] = ANSICOL_DEFAULT ( COLOR_CYAN ), + [COLOR_WHITE] = ANSICOL_DEFAULT ( COLOR_WHITE ), +}; + +/** Magic basic colour */ +static uint8_t ansicol_magic = COLOR_NORMAL_BG; + +/** + * Define ANSI colour + * + * @v colour Colour index + * @v basic Basic colour + * @v rgb 24-bit RGB value (or ANSICOL_NO_RGB) + * @ret rc Return status code + */ +int ansicol_define ( unsigned int colour, unsigned int basic, uint32_t rgb ) { + uint32_t ansicol; + + /* Fail if colour index is out of range */ + if ( colour >= ( sizeof ( ansicols ) / sizeof ( ansicols[0] ) ) ) + return -EINVAL; + + /* Update colour definition */ + ansicol = ANSICOL_DEFINE ( basic, rgb ); + ansicols[colour] = ansicol; + DBGC ( &ansicols[0], "ANSICOL redefined colour %d as basic %d RGB " + "%#06lx%s\n", colour, ANSICOL_BASIC ( ansicol ), + ANSICOL_RGB ( ansicol ), + ( ( ansicol & ANSICOL_NO_RGB ) ? " [norgb]" : "" ) ); + + return 0; +} + +/** + * Set ANSI colour (using colour definitions) + * + * @v colour Colour index + * @v which Foreground/background selector + */ +void ansicol_set ( unsigned int colour, unsigned int which ) { + uint32_t ansicol; + unsigned int basic; + + /* Use default colour if colour index is out of range */ + if ( colour < ( sizeof ( ansicols ) / sizeof ( ansicols[0] ) ) ) { + ansicol = ansicols[colour]; + } else { + ansicol = ANSICOL_DEFINE ( COLOUR_DEFAULT, ANSICOL_NO_RGB ); + } + + /* If basic colour is out of range, use the magic colour */ + basic = ANSICOL_BASIC ( ansicol ); + if ( basic >= 10 ) + basic = ansicol_magic; + + /* Set basic colour first */ + printf ( CSI "%c%dm", which, basic ); + + /* Set 24-bit RGB colour, if applicable */ + if ( ! ( ansicol & ANSICOL_NO_RGB ) ) { + printf ( CSI "%c8;2;%d;%d;%dm", which, ANSICOL_RED ( ansicol ), + ANSICOL_GREEN ( ansicol ), ANSICOL_BLUE ( ansicol ) ); + } +} + +/** + * Reset magic colour + * + */ +void ansicol_reset_magic ( void ) { + + /* Set to the compile-time default background colour */ + ansicol_magic = COLOR_NORMAL_BG; +} + +/** + * Set magic colour to transparent + * + */ +void ansicol_set_magic_transparent ( void ) { + + /* Set to the console default colour (which will give a + * transparent background on the framebuffer console). + */ + ansicol_magic = COLOR_DEFAULT; +} diff --git a/qemu/roms/ipxe/src/core/ansiesc.c b/qemu/roms/ipxe/src/core/ansiesc.c new file mode 100644 index 000000000..ca9a73ce0 --- /dev/null +++ b/qemu/roms/ipxe/src/core/ansiesc.c @@ -0,0 +1,120 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <string.h> +#include <assert.h> +#include <ipxe/ansiesc.h> + +/** @file + * + * ANSI escape sequences + * + */ + +/** + * Call ANSI escape sequence handler + * + * @v ctx ANSI escape sequence context + * @v function Control function identifier + * @v count Parameter count + * @v params Parameter list + */ +static void ansiesc_call_handler ( struct ansiesc_context *ctx, + unsigned int function, int count, + int params[] ) { + struct ansiesc_handler *handlers = ctx->handlers; + struct ansiesc_handler *handler; + + for ( handler = handlers ; handler->function ; handler++ ) { + if ( handler->function == function ) { + handler->handle ( ctx, 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 ) ) || + ( c == '?' ) ) { + /* 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, ctx->function, + count, ctx->params ); + } + return -1; + } +} diff --git a/qemu/roms/ipxe/src/core/asprintf.c b/qemu/roms/ipxe/src/core/asprintf.c new file mode 100644 index 000000000..03cf45cfc --- /dev/null +++ b/qemu/roms/ipxe/src/core/asprintf.c @@ -0,0 +1,49 @@ +#include <stdint.h> +#include <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** + * 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/qemu/roms/ipxe/src/core/assert.c b/qemu/roms/ipxe/src/core/assert.c new file mode 100644 index 000000000..0791ea7b9 --- /dev/null +++ b/qemu/roms/ipxe/src/core/assert.c @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2011 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** @file + * + * Assertions + * + */ + +#include <assert.h> + +/** Number of assertion failures triggered */ +unsigned int assertion_failures = 0; diff --git a/qemu/roms/ipxe/src/core/base16.c b/qemu/roms/ipxe/src/core/base16.c new file mode 100644 index 000000000..bf9cc21bb --- /dev/null +++ b/qemu/roms/ipxe/src/core/base16.c @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2010 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <ipxe/base16.h> + +/** @file + * + * Base16 encoding + * + */ + +/** + * Base16-encode data + * + * @v raw Raw data + * @v len Length of raw data + * @v encoded Buffer for encoded string + * + * The buffer must be the correct length for the encoded string. Use + * something like + * + * char buf[ base16_encoded_len ( len ) + 1 ]; + * + * (the +1 is for the terminating NUL) to provide a buffer of the + * correct size. + */ +void base16_encode ( const uint8_t *raw, size_t len, char *encoded ) { + const uint8_t *raw_bytes = raw; + char *encoded_bytes = encoded; + size_t remaining = len; + + /* Encode each byte */ + for ( ; remaining-- ; encoded_bytes += 2 ) { + sprintf ( encoded_bytes, "%02x", *(raw_bytes++) ); + } + + /* Ensure terminating NUL exists even if length was zero */ + *encoded_bytes = '\0'; + + DBG ( "Base16-encoded to \"%s\":\n", encoded ); + DBG_HDA ( 0, raw, len ); + assert ( strlen ( encoded ) == base16_encoded_len ( len ) ); +} + +/** + * Decode hexadecimal string + * + * @v encoded Encoded string + * @v separator Byte separator character, or 0 for no separator + * @v data Buffer + * @v len Length of buffer + * @ret len Length of data, or negative error + */ +int hex_decode ( const char *encoded, char separator, void *data, size_t len ) { + uint8_t *out = data; + unsigned int count = 0; + unsigned int sixteens; + unsigned int units; + + while ( *encoded ) { + + /* Check separator, if applicable */ + if ( count && separator && ( ( *(encoded++) != separator ) ) ) + return -EINVAL; + + /* Extract digits. Note that either digit may be NUL, + * which would be interpreted as an invalid value by + * strtoul_charval(); there is therefore no need for an + * explicit end-of-string check. + */ + sixteens = strtoul_charval ( *(encoded++) ); + if ( sixteens >= 16 ) + return -EINVAL; + units = strtoul_charval ( *(encoded++) ); + if ( units >= 16 ) + return -EINVAL; + + /* Store result */ + if ( count < len ) + out[count] = ( ( sixteens << 4 ) | units ); + count++; + + } + return count; +} + +/** + * Base16-decode data + * + * @v encoded Encoded string + * @v raw Raw data + * @ret len Length of raw data, or negative error + * + * The buffer must be large enough to contain the decoded data. Use + * something like + * + * char buf[ base16_decoded_max_len ( encoded ) ]; + * + * to provide a buffer of the correct size. + */ +int base16_decode ( const char *encoded, uint8_t *raw ) { + int len; + + len = hex_decode ( encoded, 0, raw, -1UL ); + if ( len < 0 ) + return len; + + DBG ( "Base16-decoded \"%s\" to:\n", encoded ); + DBG_HDA ( 0, raw, len ); + assert ( len <= ( int ) base16_decoded_max_len ( encoded ) ); + + return len; +} diff --git a/qemu/roms/ipxe/src/core/base64.c b/qemu/roms/ipxe/src/core/base64.c new file mode 100644 index 000000000..bdaf70957 --- /dev/null +++ b/qemu/roms/ipxe/src/core/base64.c @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2009 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdint.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <assert.h> +#include <ipxe/base64.h> + +/** @file + * + * Base64 encoding + * + */ + +static const char base64[64] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/** + * Base64-encode data + * + * @v raw Raw data + * @v len Length of raw data + * @v encoded Buffer for encoded string + * + * The buffer must be the correct length for the encoded string. Use + * something like + * + * char buf[ base64_encoded_len ( len ) + 1 ]; + * + * (the +1 is for the terminating NUL) to provide a buffer of the + * correct size. + */ +void base64_encode ( const uint8_t *raw, size_t len, char *encoded ) { + const uint8_t *raw_bytes = ( ( const uint8_t * ) raw ); + uint8_t *encoded_bytes = ( ( uint8_t * ) encoded ); + size_t raw_bit_len = ( 8 * len ); + unsigned int bit; + unsigned int byte; + unsigned int shift; + unsigned int tmp; + + for ( bit = 0 ; bit < raw_bit_len ; bit += 6 ) { + byte = ( bit / 8 ); + shift = ( bit % 8 ); + tmp = ( raw_bytes[byte] << shift ); + if ( ( byte + 1 ) < len ) + tmp |= ( raw_bytes[ byte + 1 ] >> ( 8 - shift ) ); + tmp = ( ( tmp >> 2 ) & 0x3f ); + *(encoded_bytes++) = base64[tmp]; + } + for ( ; ( bit % 8 ) != 0 ; bit += 6 ) + *(encoded_bytes++) = '='; + *(encoded_bytes++) = '\0'; + + DBG ( "Base64-encoded to \"%s\":\n", encoded ); + DBG_HDA ( 0, raw, len ); + assert ( strlen ( encoded ) == base64_encoded_len ( len ) ); +} + +/** + * Base64-decode string + * + * @v encoded Encoded string + * @v raw Raw data + * @ret len Length of raw data, or negative error + * + * The buffer must be large enough to contain the decoded data. Use + * something like + * + * char buf[ base64_decoded_max_len ( encoded ) ]; + * + * to provide a buffer of the correct size. + */ +int base64_decode ( const char *encoded, uint8_t *raw ) { + const uint8_t *encoded_bytes = ( ( const uint8_t * ) encoded ); + uint8_t *raw_bytes = ( ( uint8_t * ) raw ); + uint8_t encoded_byte; + char *match; + int decoded; + unsigned int bit = 0; + unsigned int pad_count = 0; + size_t len; + + /* Zero the raw data */ + memset ( raw, 0, base64_decoded_max_len ( encoded ) ); + + /* Decode string */ + while ( ( encoded_byte = *(encoded_bytes++) ) ) { + + /* Ignore whitespace characters */ + if ( isspace ( encoded_byte ) ) + continue; + + /* Process pad characters */ + if ( encoded_byte == '=' ) { + if ( pad_count >= 2 ) { + DBG ( "Base64-encoded string \"%s\" has too " + "many pad characters\n", encoded ); + return -EINVAL; + } + pad_count++; + bit -= 2; /* unused_bits = ( 2 * pad_count ) */ + continue; + } + if ( pad_count ) { + DBG ( "Base64-encoded string \"%s\" has invalid pad " + "sequence\n", encoded ); + return -EINVAL; + } + + /* Process normal characters */ + match = strchr ( base64, encoded_byte ); + if ( ! match ) { + DBG ( "Base64-encoded string \"%s\" contains invalid " + "character '%c'\n", encoded, encoded_byte ); + return -EINVAL; + } + decoded = ( match - base64 ); + + /* Add to raw data */ + decoded <<= 2; + raw_bytes[ bit / 8 ] |= ( decoded >> ( bit % 8 ) ); + raw_bytes[ bit / 8 + 1 ] |= ( decoded << ( 8 - ( bit % 8 ) ) ); + bit += 6; + } + + /* Check that we decoded a whole number of bytes */ + if ( ( bit % 8 ) != 0 ) { + DBG ( "Base64-encoded string \"%s\" has invalid bit length " + "%d\n", encoded, bit ); + return -EINVAL; + } + len = ( bit / 8 ); + + DBG ( "Base64-decoded \"%s\" to:\n", encoded ); + DBG_HDA ( 0, raw, len ); + assert ( len <= base64_decoded_max_len ( encoded ) ); + + /* Return length in bytes */ + return ( len ); +} diff --git a/qemu/roms/ipxe/src/core/basename.c b/qemu/roms/ipxe/src/core/basename.c new file mode 100644 index 000000000..b534a7886 --- /dev/null +++ b/qemu/roms/ipxe/src/core/basename.c @@ -0,0 +1,65 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** + * @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/qemu/roms/ipxe/src/core/bitmap.c b/qemu/roms/ipxe/src/core/bitmap.c new file mode 100644 index 000000000..0d1152327 --- /dev/null +++ b/qemu/roms/ipxe/src/core/bitmap.c @@ -0,0 +1,102 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <errno.h> +#include <ipxe/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 ) != 0 ); +} + +/** + * 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/qemu/roms/ipxe/src/core/blockdev.c b/qemu/roms/ipxe/src/core/blockdev.c new file mode 100644 index 000000000..9d118cb2f --- /dev/null +++ b/qemu/roms/ipxe/src/core/blockdev.c @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2010 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <errno.h> +#include <ipxe/interface.h> +#include <ipxe/blockdev.h> + +/** @file + * + * Block devices + * + */ + +/** + * Read from block device + * + * @v control Control interface + * @v data Data interface + * @v lba Starting logical block address + * @v count Number of logical blocks + * @v buffer Data buffer + * @v len Length of data buffer + * @ret rc Return status code + */ +int block_read ( struct interface *control, struct interface *data, + uint64_t lba, unsigned int count, + userptr_t buffer, size_t len ) { + struct interface *dest; + block_read_TYPE ( void * ) *op = + intf_get_dest_op ( control, block_read, &dest ); + void *object = intf_object ( dest ); + int rc; + + if ( op ) { + rc = op ( object, data, lba, count, buffer, len ); + } else { + /* Default is to fail to issue the command */ + rc = -EOPNOTSUPP; + } + + intf_put ( dest ); + return rc; +} + +/** + * Write to block device + * + * @v control Control interface + * @v data Data interface + * @v lba Starting logical block address + * @v count Number of logical blocks + * @v buffer Data buffer + * @v len Length of data buffer + * @ret rc Return status code + */ +int block_write ( struct interface *control, struct interface *data, + uint64_t lba, unsigned int count, + userptr_t buffer, size_t len ) { + struct interface *dest; + block_write_TYPE ( void * ) *op = + intf_get_dest_op ( control, block_write, &dest ); + void *object = intf_object ( dest ); + int rc; + + if ( op ) { + rc = op ( object, data, lba, count, buffer, len ); + } else { + /* Default is to fail to issue the command */ + rc = -EOPNOTSUPP; + } + + intf_put ( dest ); + return rc; +} + +/** + * Read block device capacity + * + * @v control Control interface + * @v data Data interface + * @ret rc Return status code + */ +int block_read_capacity ( struct interface *control, struct interface *data ) { + struct interface *dest; + block_read_capacity_TYPE ( void * ) *op = + intf_get_dest_op ( control, block_read_capacity, &dest ); + void *object = intf_object ( dest ); + int rc; + + if ( op ) { + rc = op ( object, data ); + } else { + /* Default is to fail to issue the command */ + rc = -EOPNOTSUPP; + } + + intf_put ( dest ); + return rc; +} + +/** + * Report block device capacity + * + * @v intf Interface + * @v capacity Block device capacity + */ +void block_capacity ( struct interface *intf, + struct block_device_capacity *capacity ) { + struct interface *dest; + block_capacity_TYPE ( void * ) *op = + intf_get_dest_op ( intf, block_capacity, &dest ); + void *object = intf_object ( dest ); + + if ( op ) { + op ( object, capacity ); + } else { + /* Default is to do nothing */ + } + + intf_put ( dest ); +} diff --git a/qemu/roms/ipxe/src/core/console.c b/qemu/roms/ipxe/src/core/console.c new file mode 100644 index 000000000..141d8f0f0 --- /dev/null +++ b/qemu/roms/ipxe/src/core/console.c @@ -0,0 +1,164 @@ +#include "stddef.h" +#include <ipxe/console.h> +#include <ipxe/process.h> +#include <ipxe/nap.h> + +/** @file */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** Current console usage */ +int console_usage = CONSOLE_USAGE_STDOUT; + +/** Console width */ +unsigned int console_width = CONSOLE_DEFAULT_WIDTH; + +/** Console height */ +unsigned int console_height = CONSOLE_DEFAULT_HEIGHT; + +/** + * Write a single character to each console device + * + * @v character Character to be written + * + * 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_each_table_entry ( console, CONSOLES ) { + if ( ( ! ( console->disabled & CONSOLE_DISABLED_OUTPUT ) ) && + ( console_usage & console->usage ) && + console->putchar ) + console->putchar ( character ); + } +} + +/** + * Check to see if any input is available on any console + * + * @ret console Console device that has input available, or NULL + * + * 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_each_table_entry ( console, CONSOLES ) { + if ( ( ! ( console->disabled & CONSOLE_DISABLED_INPUT ) ) && + console->iskey ) { + if ( console->iskey () ) + return console; + } + } + return NULL; +} + +/** + * Read a single character from any console + * + * @ret character Character read from a console. + * + * 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. + */ +int getchar ( void ) { + struct console_driver *console; + int character; + + while ( 1 ) { + console = has_input(); + if ( console && console->getchar ) { + character = console->getchar (); + break; + } + + /* 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(); + } + + /* CR -> LF translation */ + if ( character == '\r' ) + character = '\n'; + + return character; +} + +/** + * Check for available input on any console + * + * @ret is_available Input is available on a console + * + * 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; +} + +/** + * Configure console + * + * @v config Console configuration + * @ret rc Return status code + * + * The configuration is passed to all configurable consoles, including + * those which are currently disabled. Consoles may choose to enable + * or disable themselves depending upon the configuration. + * + * If configuration fails, then all consoles will be reset. + */ +int console_configure ( struct console_configuration *config ) { + struct console_driver *console; + int rc; + + /* Reset console width and height */ + console_set_size ( CONSOLE_DEFAULT_WIDTH, CONSOLE_DEFAULT_HEIGHT ); + + /* Try to configure each console */ + for_each_table_entry ( console, CONSOLES ) { + if ( ( console->configure ) && + ( ( rc = console->configure ( config ) ) != 0 ) ) + goto err; + } + + return 0; + + err: + /* Reset all consoles, avoiding a potential infinite loop */ + if ( config ) + console_reset(); + return rc; +} diff --git a/qemu/roms/ipxe/src/core/cpio.c b/qemu/roms/ipxe/src/core/cpio.c new file mode 100644 index 000000000..3a5f4d2b6 --- /dev/null +++ b/qemu/roms/ipxe/src/core/cpio.c @@ -0,0 +1,43 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** @file + * + * CPIO archives + * + */ + +#include <stdio.h> +#include <string.h> +#include <ipxe/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/qemu/roms/ipxe/src/core/ctype.c b/qemu/roms/ipxe/src/core/ctype.c new file mode 100644 index 000000000..c812346a0 --- /dev/null +++ b/qemu/roms/ipxe/src/core/ctype.c @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2009 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** + * @file + * + * Character types + * + */ + +#include <ctype.h> + +/** + * Check to see if character is a space + * + * @v c Character + * @ret isspace Character is a space + */ +int isspace ( int c ) { + switch ( c ) { + case ' ' : + case '\f' : + case '\n' : + case '\r' : + case '\t' : + case '\v' : + return 1; + default: + return 0; + } +} diff --git a/qemu/roms/ipxe/src/core/cwuri.c b/qemu/roms/ipxe/src/core/cwuri.c new file mode 100644 index 000000000..5865552a0 --- /dev/null +++ b/qemu/roms/ipxe/src/core/cwuri.c @@ -0,0 +1,49 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stddef.h> +#include <ipxe/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 ) { + struct uri *new_uri = NULL; + + if ( uri ) + new_uri = resolve_uri ( cwuri, uri ); + + uri_put ( cwuri ); + cwuri = new_uri; +} diff --git a/qemu/roms/ipxe/src/core/debug.c b/qemu/roms/ipxe/src/core/debug.c new file mode 100644 index 000000000..7ded47089 --- /dev/null +++ b/qemu/roms/ipxe/src/core/debug.c @@ -0,0 +1,203 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdio.h> +#include <stdint.h> +#include <stdarg.h> +#include <ctype.h> +#include <ipxe/console.h> + +/** + * Print debug message + * + * @v fmt Format string + * @v ... Arguments + */ +void dbg_printf ( const char *fmt, ... ) { + int saved_usage; + va_list args; + + /* Mark console as in use for debugging messages */ + saved_usage = console_set_usage ( CONSOLE_USAGE_DEBUG ); + + /* Print message */ + va_start ( args, fmt ); + vprintf ( fmt, args ); + va_end ( args ); + + /* Restore console usage */ + console_set_usage ( saved_usage ); +} + +/** + * Pause until a key is pressed + * + */ +void dbg_pause ( void ) { + dbg_printf ( "\nPress a key..." ); + getchar(); + dbg_printf ( "\r \r" ); +} + +/** + * Indicate more data to follow and pause until a key is pressed + * + */ +void dbg_more ( void ) { + dbg_printf ( "---more---" ); + getchar(); + dbg_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; + + dbg_printf ( "%08lx :", ( dispaddr + offset ) ); + for ( i = offset ; i < ( offset + 16 ) ; i++ ) { + if ( i >= len ) { + dbg_printf ( " " ); + continue; + } + dbg_printf ( "%c%02x", + ( ( ( i % 16 ) == 8 ) ? '-' : ' ' ), bytes[i] ); + } + dbg_printf ( " : " ); + for ( i = offset ; i < ( offset + 16 ) ; i++ ) { + if ( i >= len ) { + dbg_printf ( " " ); + continue; + } + byte = bytes[i]; + dbg_printf ( "%c", ( isprint ( byte ) ? byte : '.' ) ); + } + dbg_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 ); + } +} + +/** + * Base message stream colour + * + * We default to using 31 (red foreground) as the base colour. + */ +#ifndef DBGCOL_MIN +#define DBGCOL_MIN 31 +#endif + +/** + * 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. + */ +#ifndef DBGCOL_MAX +#define DBGCOL_MAX ( DBGCOL_MIN + 6 - 1 ) +#endif + +/** 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[ DBGCOL_MAX - DBGCOL_MIN + 1 ]; + 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 ) { + dbg_printf ( "\033[%dm", + ( stream ? ( DBGCOL_MIN + dbg_autocolour ( stream ) ) :0)); +} + +/** + * Revert to normal colour + * + */ +void dbg_decolourise ( void ) { + dbg_printf ( "\033[0m" ); +} diff --git a/qemu/roms/ipxe/src/core/debug_md5.c b/qemu/roms/ipxe/src/core/debug_md5.c new file mode 100644 index 000000000..f049ac757 --- /dev/null +++ b/qemu/roms/ipxe/src/core/debug_md5.c @@ -0,0 +1,48 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdio.h> +#include <stdint.h> +#include <ipxe/crypto.h> +#include <ipxe/md5.h> + +/** + * Print an MD5 checksum with specified display address + * + * @v dispaddr Display address + * @v data Data to checksum + * @v len Length of data + */ +void dbg_md5_da ( unsigned long dispaddr, const void *data, + unsigned long len ) { + struct digest_algorithm *digest = &md5_algorithm; + uint8_t digest_ctx[digest->ctxsize]; + uint8_t digest_out[digest->digestsize]; + unsigned int i; + + printf ( "md5sum ( %#08lx, %#lx ) = ", dispaddr, len ); + digest_init ( digest, digest_ctx ); + digest_update ( digest, digest_ctx, data, len ); + digest_final ( digest, digest_ctx, digest_out ); + for ( i = 0 ; i < sizeof ( digest_out ) ; i++ ) + printf ( "%02x", digest_out[i] ); + printf ( "\n" ); +} diff --git a/qemu/roms/ipxe/src/core/device.c b/qemu/roms/ipxe/src/core/device.c new file mode 100644 index 000000000..330f95c5a --- /dev/null +++ b/qemu/roms/ipxe/src/core/device.c @@ -0,0 +1,136 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <string.h> +#include <ipxe/list.h> +#include <ipxe/tables.h> +#include <ipxe/init.h> +#include <ipxe/interface.h> +#include <ipxe/device.h> + +/** + * @file + * + * Device model + * + */ + +/** Registered root devices */ +static LIST_HEAD ( devices ); + +/** Device removal inhibition counter */ +int device_keep_count = 0; + +/** + * 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 + * + * 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. + */ +static void probe_devices ( void ) { + struct root_device *rootdev; + int rc; + + for_each_table_entry ( rootdev, ROOT_DEVICES ) { + list_add ( &rootdev->dev.siblings, &devices ); + INIT_LIST_HEAD ( &rootdev->dev.children ); + if ( ( rc = rootdev_probe ( rootdev ) ) != 0 ) + list_del ( &rootdev->dev.siblings ); + } +} + +/** + * Remove all devices + * + */ +static void remove_devices ( int booting __unused ) { + struct root_device *rootdev; + struct root_device *tmp; + + if ( device_keep_count != 0 ) { + DBG ( "Refusing to remove devices on shutdown\n" ); + return; + } + + list_for_each_entry_safe ( rootdev, tmp, &devices, dev.siblings ) { + rootdev_remove ( rootdev ); + list_del ( &rootdev->dev.siblings ); + } +} + +struct startup_fn startup_devices __startup_fn ( STARTUP_NORMAL ) = { + .startup = probe_devices, + .shutdown = remove_devices, +}; + +/** + * Identify a device behind an interface + * + * @v intf Interface + * @ret device Device, or NULL + */ +struct device * identify_device ( struct interface *intf ) { + struct interface *dest; + identify_device_TYPE ( void * ) *op = + intf_get_dest_op ( intf, identify_device, &dest ); + void *object = intf_object ( dest ); + void *device; + + if ( op ) { + device = op ( object ); + } else { + /* Default is to return NULL */ + device = NULL; + } + + intf_put ( dest ); + return device; +} diff --git a/qemu/roms/ipxe/src/core/downloader.c b/qemu/roms/ipxe/src/core/downloader.c new file mode 100644 index 000000000..ec69db6b1 --- /dev/null +++ b/qemu/roms/ipxe/src/core/downloader.c @@ -0,0 +1,279 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdlib.h> +#include <errno.h> +#include <syslog.h> +#include <ipxe/iobuf.h> +#include <ipxe/xfer.h> +#include <ipxe/open.h> +#include <ipxe/job.h> +#include <ipxe/uaccess.h> +#include <ipxe/umalloc.h> +#include <ipxe/image.h> +#include <ipxe/profile.h> +#include <ipxe/downloader.h> + +/** @file + * + * Image downloader + * + */ + +/** Receive profiler */ +static struct profiler downloader_rx_profiler __profiler = + { .name = "downloader.rx" }; + +/** Data copy profiler */ +static struct profiler downloader_copy_profiler __profiler = + { .name = "downloader.copy" }; + +/** A downloader */ +struct downloader { + /** Reference count for this object */ + struct refcnt refcnt; + + /** Job control interface */ + struct interface job; + /** Data transfer interface */ + struct interface xfer; + + /** Image to contain downloaded file */ + struct image *image; + /** Current position within image buffer */ + size_t pos; +}; + +/** + * 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 ) { + + /* Log download status */ + if ( rc == 0 ) { + syslog ( LOG_NOTICE, "Downloaded \"%s\"\n", + downloader->image->name ); + } else { + syslog ( LOG_ERR, "Download of \"%s\" failed: %s\n", + downloader->image->name, strerror ( rc ) ); + } + + /* Shut down interfaces */ + intf_shutdown ( &downloader->xfer, rc ); + intf_shutdown ( &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 -ENOSPC; + } + downloader->image->data = new_buffer; + downloader->image->len = len; + + return 0; +} + +/**************************************************************************** + * + * Job control interface + * + */ + +/** + * Report progress of download job + * + * @v downloader Downloader + * @v progress Progress report to fill in + * @ret ongoing_rc Ongoing job status code (if known) + */ +static int downloader_progress ( struct downloader *downloader, + struct job_progress *progress ) { + + /* This is not entirely accurate, since downloaded data may + * arrive out of order (e.g. with multicast protocols), but + * it's a reasonable first approximation. + */ + progress->completed = downloader->pos; + progress->total = downloader->image->len; + + return 0; +} + +/**************************************************************************** + * + * Data transfer interface + * + */ + +/** + * Handle received data + * + * @v downloader Downloader + * @v iobuf Datagram I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + */ +static int downloader_xfer_deliver ( struct downloader *downloader, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + size_t len; + size_t max; + int rc; + + /* Start profiling */ + profile_start ( &downloader_rx_profiler ); + + /* Calculate new buffer position */ + if ( meta->flags & XFER_FL_ABS_OFFSET ) + 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 */ + profile_start ( &downloader_copy_profiler ); + copy_to_user ( downloader->image->data, downloader->pos, + iobuf->data, len ); + profile_stop ( &downloader_copy_profiler ); + + /* Update current buffer position */ + downloader->pos += len; + + done: + free_iob ( iobuf ); + if ( rc != 0 ) + downloader_finished ( downloader, rc ); + profile_stop ( &downloader_rx_profiler ); + return rc; +} + +/** Downloader data transfer interface operations */ +static struct interface_operation downloader_xfer_operations[] = { + INTF_OP ( xfer_deliver, struct downloader *, downloader_xfer_deliver ), + INTF_OP ( intf_close, struct downloader *, downloader_finished ), +}; + +/** Downloader data transfer interface descriptor */ +static struct interface_descriptor downloader_xfer_desc = + INTF_DESC ( struct downloader, xfer, downloader_xfer_operations ); + +/**************************************************************************** + * + * Job control interface + * + */ + +/** Downloader job control interface operations */ +static struct interface_operation downloader_job_op[] = { + INTF_OP ( job_progress, struct downloader *, downloader_progress ), + INTF_OP ( intf_close, struct downloader *, downloader_finished ), +}; + +/** Downloader job control interface descriptor */ +static struct interface_descriptor downloader_job_desc = + INTF_DESC ( struct downloader, job, downloader_job_op ); + +/**************************************************************************** + * + * Instantiator + * + */ + +/** + * Instantiate a downloader + * + * @v job Job control interface + * @v image Image to fill with downloaded file + * @ret rc Return status code + * + * Instantiates a downloader object to download the content of the + * specified image from its URI. + */ +int create_downloader ( struct interface *job, struct image *image ) { + struct downloader *downloader; + int rc; + + /* Allocate and initialise structure */ + downloader = zalloc ( sizeof ( *downloader ) ); + if ( ! downloader ) + return -ENOMEM; + ref_init ( &downloader->refcnt, downloader_free ); + intf_init ( &downloader->job, &downloader_job_desc, + &downloader->refcnt ); + intf_init ( &downloader->xfer, &downloader_xfer_desc, + &downloader->refcnt ); + downloader->image = image_get ( image ); + + /* Instantiate child objects and attach to our interfaces */ + if ( ( rc = xfer_open_uri ( &downloader->xfer, image->uri ) ) != 0 ) + goto err; + + /* Attach parent interface, mortalise self, and return */ + intf_plug_plug ( &downloader->job, job ); + ref_put ( &downloader->refcnt ); + return 0; + + err: + downloader_finished ( downloader, rc ); + ref_put ( &downloader->refcnt ); + return rc; +} diff --git a/qemu/roms/ipxe/src/core/edd.c b/qemu/roms/ipxe/src/core/edd.c new file mode 100644 index 000000000..d574ea6c0 --- /dev/null +++ b/qemu/roms/ipxe/src/core/edd.c @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2010 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <errno.h> +#include <ipxe/interface.h> +#include <ipxe/edd.h> + +/** @file + * + * Enhanced Disk Drive specification + * + */ + +/** + * Describe a disk device using EDD + * + * @v intf Interface + * @v type EDD interface type + * @v path EDD device path + * @ret rc Return status code + */ +int edd_describe ( struct interface *intf, struct edd_interface_type *type, + union edd_device_path *path ) { + struct interface *dest; + edd_describe_TYPE ( void * ) *op = + intf_get_dest_op ( intf, edd_describe, &dest ); + void *object = intf_object ( dest ); + int rc; + + if ( op ) { + rc = op ( object, type, path ); + } else { + /* Default is to not support this operation */ + rc = -ENOTSUP; + } + + intf_put ( dest ); + return rc; +} diff --git a/qemu/roms/ipxe/src/core/errno.c b/qemu/roms/ipxe/src/core/errno.c new file mode 100644 index 000000000..06905561f --- /dev/null +++ b/qemu/roms/ipxe/src/core/errno.c @@ -0,0 +1,20 @@ +#include <errno.h> + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** @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/qemu/roms/ipxe/src/core/exec.c b/qemu/roms/ipxe/src/core/exec.c new file mode 100644 index 000000000..1c85705ae --- /dev/null +++ b/qemu/roms/ipxe/src/core/exec.c @@ -0,0 +1,601 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdint.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <unistd.h> +#include <getopt.h> +#include <errno.h> +#include <assert.h> +#include <ipxe/tables.h> +#include <ipxe/command.h> +#include <ipxe/parseopt.h> +#include <ipxe/settings.h> +#include <ipxe/console.h> +#include <ipxe/keys.h> +#include <ipxe/process.h> +#include <ipxe/nap.h> +#include <ipxe/shell.h> + +/** @file + * + * Command execution + * + */ + +/** Shell stop state */ +static int stop_state; + +/** + * Execute command + * + * @v command Command name + * @v argv Argument list + * @ret rc Return status code + * + * 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; + int rc; + + /* Count number of arguments */ + for ( argc = 0 ; argv[argc] ; argc++ ) {} + + /* An empty command is deemed to do nothing, successfully */ + if ( command == NULL ) { + rc = 0; + goto done; + } + + /* Sanity checks */ + if ( argc == 0 ) { + DBG ( "%s: empty argument list\n", command ); + rc = -EINVAL; + goto done; + } + + /* 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_each_table_entry ( cmd, COMMANDS ) { + if ( strcmp ( command, cmd->name ) == 0 ) { + rc = cmd->exec ( argc, ( char ** ) argv ); + goto done; + } + } + + printf ( "%s: command not found\n", command ); + rc = -ENOEXEC; + + done: + /* Store error number, if an error occurred */ + if ( rc ) { + errno = rc; + if ( errno < 0 ) + errno = -errno; + } + + return rc; +} + +/** + * Split command line into tokens + * + * @v command Command line + * @v tokens Token list to populate, or NULL + * @ret count Number of tokens + * + * Splits the command line into whitespace-delimited tokens. If @c + * tokens is non-NULL, any whitespace in the command line will be + * replaced with NULs. + */ +static int split_command ( char *command, char **tokens ) { + int count = 0; + + while ( 1 ) { + /* Skip over any whitespace / convert to NUL */ + while ( isspace ( *command ) ) { + if ( tokens ) + *command = '\0'; + command++; + } + /* Check for end of line */ + if ( ! *command ) + break; + /* We have found the start of the next argument */ + if ( tokens ) + tokens[count] = command; + count++; + /* Skip to start of next whitespace, if any */ + while ( *command && ! isspace ( *command ) ) { + command++; + } + } + return count; +} + +/** + * Process next command only if previous command succeeded + * + * @v rc Status of previous command + * @ret process Process next command + */ +static int process_on_success ( int rc ) { + return ( rc == 0 ); +} + +/** + * Process next command only if previous command failed + * + * @v rc Status of previous command + * @ret process Process next command + */ +static int process_on_failure ( int rc ) { + return ( rc != 0 ); +} + +/** + * Process next command regardless of status from previous command + * + * @v rc Status of previous command + * @ret process Process next command + */ +static int process_always ( int rc __unused ) { + return 1; +} + +/** + * Find command terminator + * + * @v tokens Token list + * @ret process_next "Should next command be processed?" function + * @ret argc Argument count + */ +static int command_terminator ( char **tokens, + int ( **process_next ) ( int rc ) ) { + unsigned int i; + + /* Find first terminating token */ + for ( i = 0 ; tokens[i] ; i++ ) { + if ( tokens[i][0] == '#' ) { + /* Start of a comment */ + break; + } else if ( strcmp ( tokens[i], "||" ) == 0 ) { + /* Short-circuit logical OR */ + *process_next = process_on_failure; + return i; + } else if ( strcmp ( tokens[i], "&&" ) == 0 ) { + /* Short-circuit logical AND */ + *process_next = process_on_success; + return i; + } else if ( strcmp ( tokens[i], ";" ) == 0 ) { + /* Process next command unconditionally */ + *process_next = process_always; + return i; + } + } + + /* End of token list */ + *process_next = NULL; + return i; +} + +/** + * Set shell stop state + * + * @v stop Shell stop state + */ +void shell_stop ( int stop ) { + stop_state = stop; +} + +/** + * Test and consume shell stop state + * + * @v stop Shell stop state to consume + * @v stopped Shell had been stopped + */ +int shell_stopped ( int stop ) { + int stopped; + + /* Test to see if we need to stop */ + stopped = ( stop_state >= stop ); + + /* Consume stop state */ + if ( stop_state <= stop ) + stop_state = 0; + + return stopped; +} + +/** + * Expand settings within a token list + * + * @v argc Argument count + * @v tokens Token list + * @v argv Argument list to fill in + * @ret rc Return status code + */ +static int expand_tokens ( int argc, char **tokens, char **argv ) { + int i; + + /* Expand each token in turn */ + for ( i = 0 ; i < argc ; i++ ) { + argv[i] = expand_settings ( tokens[i] ); + if ( ! argv[i] ) + goto err_expand_settings; + } + + return 0; + + err_expand_settings: + assert ( argv[i] == NULL ); + for ( ; i >= 0 ; i-- ) + free ( argv[i] ); + return -ENOMEM; +} + +/** + * Free an expanded token list + * + * @v argv Argument list + */ +static void free_tokens ( char **argv ) { + + /* Free each expanded argument */ + while ( *argv ) + free ( *(argv++) ); +} + +/** + * Execute command line + * + * @v command Command line + * @ret rc Return status code + * + * Execute the named command and arguments. + */ +int system ( const char *command ) { + int count = split_command ( ( char * ) command, NULL ); + char *all_tokens[ count + 1 ]; + int ( * process_next ) ( int rc ); + char *command_copy; + char **tokens; + int argc; + int process; + int rc = 0; + + /* Create modifiable copy of command */ + command_copy = strdup ( command ); + if ( ! command_copy ) + return -ENOMEM; + + /* Split command into tokens */ + split_command ( command_copy, all_tokens ); + all_tokens[count] = NULL; + + /* Process individual commands */ + process = 1; + for ( tokens = all_tokens ; ; tokens += ( argc + 1 ) ) { + + /* Find command terminator */ + argc = command_terminator ( tokens, &process_next ); + + /* Expand tokens and execute command */ + if ( process ) { + char *argv[ argc + 1 ]; + + /* Expand tokens */ + if ( ( rc = expand_tokens ( argc, tokens, argv ) ) != 0) + break; + argv[argc] = NULL; + + /* Execute command */ + rc = execv ( argv[0], argv ); + + /* Free tokens */ + free_tokens ( argv ); + } + + /* Stop processing, if applicable */ + if ( shell_stopped ( SHELL_STOP_COMMAND ) ) + break; + + /* Stop processing if we have reached the end of the + * command. + */ + if ( ! process_next ) + break; + + /* Determine whether or not to process next command */ + process = process_next ( rc ); + } + + /* Free modified copy of command */ + free ( command_copy ); + + return rc; +} + +/** + * Concatenate arguments + * + * @v args Argument list (NULL-terminated) + * @ret string Concatenated arguments + * + * The returned string is allocated with malloc(). The caller is + * responsible for eventually free()ing this string. + */ +char * concat_args ( char **args ) { + char **arg; + size_t len; + char *string; + char *ptr; + + /* Calculate total string length */ + len = 1 /* NUL */; + for ( arg = args ; *arg ; arg++ ) + len += ( 1 /* possible space */ + strlen ( *arg ) ); + + /* Allocate string */ + string = zalloc ( len ); + if ( ! string ) + return NULL; + + /* Populate string */ + ptr = string; + for ( arg = args ; *arg ; arg++ ) { + ptr += sprintf ( ptr, "%s%s", + ( ( arg == args ) ? "" : " " ), *arg ); + } + assert ( ptr < ( string + len ) ); + + return string; +} + +/** "echo" options */ +struct echo_options { + /** Do not print trailing newline */ + int no_newline; +}; + +/** "echo" option list */ +static struct option_descriptor echo_opts[] = { + OPTION_DESC ( "n", 'n', no_argument, + struct echo_options, no_newline, parse_flag ), +}; + +/** "echo" command descriptor */ +static struct command_descriptor echo_cmd = + COMMAND_DESC ( struct echo_options, echo_opts, 0, MAX_ARGUMENTS, + "[...]" ); + +/** + * "echo" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int echo_exec ( int argc, char **argv ) { + struct echo_options opts; + char *text; + int rc; + + /* Parse options */ + if ( ( rc = parse_options ( argc, argv, &echo_cmd, &opts ) ) != 0 ) + return rc; + + /* Parse text */ + text = concat_args ( &argv[optind] ); + if ( ! text ) + return -ENOMEM; + + /* Print text */ + printf ( "%s%s", text, ( opts.no_newline ? "" : "\n" ) ); + + free ( text ); + return 0; +} + +/** "echo" command */ +struct command echo_command __command = { + .name = "echo", + .exec = echo_exec, +}; + +/** "exit" options */ +struct exit_options {}; + +/** "exit" option list */ +static struct option_descriptor exit_opts[] = {}; + +/** "exit" command descriptor */ +static struct command_descriptor exit_cmd = + COMMAND_DESC ( struct exit_options, exit_opts, 0, 1, "[<status>]" ); + +/** + * "exit" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int exit_exec ( int argc, char **argv ) { + struct exit_options opts; + unsigned int exit_code = 0; + int rc; + + /* Parse options */ + if ( ( rc = parse_options ( argc, argv, &exit_cmd, &opts ) ) != 0 ) + return rc; + + /* Parse exit status, if present */ + if ( optind != argc ) { + if ( ( rc = parse_integer ( argv[optind], &exit_code ) ) != 0 ) + return rc; + } + + /* Stop shell processing */ + shell_stop ( SHELL_STOP_COMMAND_SEQUENCE ); + + return exit_code; +} + +/** "exit" command */ +struct command exit_command __command = { + .name = "exit", + .exec = exit_exec, +}; + +/** "isset" options */ +struct isset_options {}; + +/** "isset" option list */ +static struct option_descriptor isset_opts[] = {}; + +/** "isset" command descriptor */ +static struct command_descriptor isset_cmd = + COMMAND_DESC ( struct isset_options, isset_opts, 1, 1, "<value>" ); + +/** + * "isset" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int isset_exec ( int argc, char **argv ) { + struct isset_options opts; + int rc; + + /* Parse options */ + if ( ( rc = parse_options ( argc, argv, &isset_cmd, &opts ) ) != 0 ) + return rc; + + /* Return success iff argument is non-empty */ + return ( argv[optind][0] ? 0 : -ENOENT ); +} + +/** "isset" command */ +struct command isset_command __command = { + .name = "isset", + .exec = isset_exec, +}; + +/** "iseq" options */ +struct iseq_options {}; + +/** "iseq" option list */ +static struct option_descriptor iseq_opts[] = {}; + +/** "iseq" command descriptor */ +static struct command_descriptor iseq_cmd = + COMMAND_DESC ( struct iseq_options, iseq_opts, 2, 2, + "<value1> <value2>" ); + +/** + * "iseq" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int iseq_exec ( int argc, char **argv ) { + struct iseq_options opts; + int rc; + + /* Parse options */ + if ( ( rc = parse_options ( argc, argv, &iseq_cmd, &opts ) ) != 0 ) + return rc; + + /* Return success iff arguments are equal */ + return ( ( strcmp ( argv[optind], argv[ optind + 1 ] ) == 0 ) ? + 0 : -ERANGE ); +} + +/** "iseq" command */ +struct command iseq_command __command = { + .name = "iseq", + .exec = iseq_exec, +}; + +/** "sleep" options */ +struct sleep_options {}; + +/** "sleep" option list */ +static struct option_descriptor sleep_opts[] = {}; + +/** "sleep" command descriptor */ +static struct command_descriptor sleep_cmd = + COMMAND_DESC ( struct sleep_options, sleep_opts, 1, 1, "<seconds>" ); + +/** + * "sleep" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int sleep_exec ( int argc, char **argv ) { + struct sleep_options opts; + unsigned int seconds; + unsigned long start; + unsigned long delay; + int rc; + + /* Parse options */ + if ( ( rc = parse_options ( argc, argv, &sleep_cmd, &opts ) ) != 0 ) + return rc; + + /* Parse number of seconds */ + if ( ( rc = parse_integer ( argv[optind], &seconds ) ) != 0 ) + return rc; + + /* Delay for specified number of seconds */ + start = currticks(); + delay = ( seconds * TICKS_PER_SEC ); + while ( ( currticks() - start ) <= delay ) { + step(); + if ( iskey() && ( getchar() == CTRL_C ) ) + return -ECANCELED; + cpu_nap(); + } + + return 0; +} + +/** "sleep" command */ +struct command sleep_command __command = { + .name = "sleep", + .exec = sleep_exec, +}; diff --git a/qemu/roms/ipxe/src/core/fbcon.c b/qemu/roms/ipxe/src/core/fbcon.c new file mode 100644 index 000000000..72d6a6789 --- /dev/null +++ b/qemu/roms/ipxe/src/core/fbcon.c @@ -0,0 +1,692 @@ +/* + * Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** @file + * + * Frame buffer console + * + */ + +#include <string.h> +#include <errno.h> +#include <assert.h> +#include <byteswap.h> +#include <ipxe/ansiesc.h> +#include <ipxe/image.h> +#include <ipxe/pixbuf.h> +#include <ipxe/umalloc.h> +#include <ipxe/console.h> +#include <ipxe/fbcon.h> + +/** + * Calculate raw colour value + * + * @v fbcon Frame buffer console + * @v rgb 24-bit RGB value + * @ret raw Raw colour + */ +static uint32_t fbcon_colour ( struct fbcon *fbcon, uint32_t rgb ) { + struct fbcon_colour_map *map = fbcon->map; + uint8_t red = ( rgb >> 16 ); + uint8_t green = ( rgb >> 8 ); + uint8_t blue = ( rgb >> 0 ); + uint32_t mapped; + + mapped = ( ( ( red >> map->red_scale ) << map->red_lsb ) | + ( ( green >> map->green_scale ) << map->green_lsb ) | + ( ( blue >> map->blue_scale ) << map->blue_lsb ) ); + return cpu_to_le32 ( mapped ); +} + +/** + * Calculate ANSI font colour + * + * @v fbcon Frame buffer console + * @v ansicol ANSI colour value (0-based) + * @ret colour Raw colour + */ +static uint32_t fbcon_ansi_colour ( struct fbcon *fbcon, + unsigned int ansicol ) { + uint32_t rgb; + + /* Treat ansicol as 3-bit BGR with intensity 0xaa */ + rgb = ( ( ( ansicol & ( 1 << 0 ) ) ? 0xaa0000 : 0 ) | + ( ( ansicol & ( 1 << 1 ) ) ? 0x00aa00 : 0 ) | + ( ( ansicol & ( 1 << 2 ) ) ? 0x0000aa : 0 ) ); + + return fbcon_colour ( fbcon, rgb ); +} + +/** + * Set default foreground colour + * + * @v fbcon Frame buffer console + */ +static void fbcon_set_default_foreground ( struct fbcon *fbcon ) { + + /* Default to non-bold white foreground */ + fbcon->foreground = fbcon_ansi_colour ( fbcon, 0x7 ); + fbcon->bold = 0; +} + +/** + * Set default background colour + * + * @v fbcon Frame buffer console + */ +static void fbcon_set_default_background ( struct fbcon *fbcon ) { + + /* Default to transparent background */ + fbcon->background = FBCON_TRANSPARENT; +} + +/** + * Clear rows of characters + * + * @v fbcon Frame buffer console + * @v ypos Starting Y position + */ +static void fbcon_clear ( struct fbcon *fbcon, unsigned int ypos ) { + struct fbcon_text_cell cell = { + .foreground = fbcon->foreground, + .background = fbcon->background, + .character = ' ', + }; + size_t offset; + unsigned int xpos; + + /* Clear stored character array */ + for ( ; ypos < fbcon->character.height ; ypos++ ) { + offset = ( ypos * fbcon->character.width * sizeof ( cell ) ); + for ( xpos = 0 ; xpos < fbcon->character.width ; xpos++ ) { + copy_to_user ( fbcon->text.start, offset, &cell, + sizeof ( cell ) ); + offset += sizeof ( cell ); + } + } +} + +/** + * Store character at specified position + * + * @v fbcon Frame buffer console + * @v cell Text cell + * @v xpos X position + * @v ypos Y position + */ +static void fbcon_store ( struct fbcon *fbcon, struct fbcon_text_cell *cell, + unsigned int xpos, unsigned int ypos ) { + size_t offset; + + /* Store cell */ + offset = ( ( ( ypos * fbcon->character.width ) + xpos ) * + sizeof ( *cell ) ); + copy_to_user ( fbcon->text.start, offset, cell, sizeof ( *cell ) ); +} + +/** + * Draw character at specified position + * + * @v fbcon Frame buffer console + * @v cell Text cell + * @v xpos X position + * @v ypos Y position + */ +static void fbcon_draw ( struct fbcon *fbcon, struct fbcon_text_cell *cell, + unsigned int xpos, unsigned int ypos ) { + struct fbcon_font_glyph glyph; + size_t offset; + size_t pixel_len; + size_t skip_len; + unsigned int row; + unsigned int column; + uint8_t bitmask; + int transparent; + void *src; + + /* Get font character */ + copy_from_user ( &glyph, fbcon->font->start, + ( cell->character * sizeof ( glyph ) ), + sizeof ( glyph ) ); + + /* Calculate pixel geometry */ + offset = ( fbcon->indent + + ( ypos * fbcon->character.stride ) + + ( xpos * fbcon->character.len ) ); + pixel_len = fbcon->pixel->len; + skip_len = ( fbcon->pixel->stride - fbcon->character.len ); + + /* Check for transparent background colour */ + transparent = ( cell->background == FBCON_TRANSPARENT ); + + /* Draw character rows */ + for ( row = 0 ; row < FBCON_CHAR_HEIGHT ; row++ ) { + + /* Draw background picture, if applicable */ + if ( transparent ) { + if ( fbcon->picture.start ) { + memcpy_user ( fbcon->start, offset, + fbcon->picture.start, offset, + fbcon->character.len ); + } else { + memset_user ( fbcon->start, offset, 0, + fbcon->character.len ); + } + } + + /* Draw character row */ + for ( column = FBCON_CHAR_WIDTH, bitmask = glyph.bitmask[row] ; + column ; column--, bitmask <<= 1, offset += pixel_len ) { + if ( bitmask & 0x80 ) { + src = &cell->foreground; + } else if ( ! transparent ) { + src = &cell->background; + } else { + continue; + } + copy_to_user ( fbcon->start, offset, src, pixel_len ); + } + + /* Move to next row */ + offset += skip_len; + } +} + +/** + * Redraw all characters + * + * @v fbcon Frame buffer console + */ +static void fbcon_redraw ( struct fbcon *fbcon ) { + struct fbcon_text_cell cell; + size_t offset = 0; + unsigned int xpos; + unsigned int ypos; + + /* Redraw characters */ + for ( ypos = 0 ; ypos < fbcon->character.height ; ypos++ ) { + for ( xpos = 0 ; xpos < fbcon->character.width ; xpos++ ) { + copy_from_user ( &cell, fbcon->text.start, offset, + sizeof ( cell ) ); + fbcon_draw ( fbcon, &cell, xpos, ypos ); + offset += sizeof ( cell ); + } + } +} + +/** + * Scroll screen + * + * @v fbcon Frame buffer console + */ +static void fbcon_scroll ( struct fbcon *fbcon ) { + size_t row_len; + + /* Sanity check */ + assert ( fbcon->ypos == fbcon->character.height ); + + /* Scroll up character array */ + row_len = ( fbcon->character.width * sizeof ( struct fbcon_text_cell )); + memmove_user ( fbcon->text.start, 0, fbcon->text.start, row_len, + ( row_len * ( fbcon->character.height - 1 ) ) ); + fbcon_clear ( fbcon, ( fbcon->character.height - 1 ) ); + + /* Update cursor position */ + fbcon->ypos--; + + /* Redraw all characters */ + fbcon_redraw ( fbcon ); +} + +/** + * Draw character at cursor position + * + * @v fbcon Frame buffer console + * @v show_cursor Show cursor + */ +static void fbcon_draw_cursor ( struct fbcon *fbcon, int show_cursor ) { + struct fbcon_text_cell cell; + size_t offset; + + offset = ( ( ( fbcon->ypos * fbcon->character.width ) + fbcon->xpos ) * + sizeof ( cell ) ); + copy_from_user ( &cell, fbcon->text.start, offset, sizeof ( cell ) ); + if ( show_cursor ) { + cell.background = fbcon->foreground; + cell.foreground = ( ( fbcon->background == FBCON_TRANSPARENT ) ? + 0 : fbcon->background ); + } + fbcon_draw ( fbcon, &cell, fbcon->xpos, fbcon->ypos ); +} + +/** + * Handle ANSI CUP (cursor position) + * + * @v ctx ANSI escape sequence context + * @v count Parameter count + * @v params[0] Row (1 is top) + * @v params[1] Column (1 is left) + */ +static void fbcon_handle_cup ( struct ansiesc_context *ctx, + unsigned int count __unused, int params[] ) { + struct fbcon *fbcon = container_of ( ctx, struct fbcon, ctx ); + int cx = ( params[1] - 1 ); + int cy = ( params[0] - 1 ); + + fbcon_draw_cursor ( fbcon, 0 ); + fbcon->xpos = cx; + if ( fbcon->xpos >= fbcon->character.width ) + fbcon->xpos = 0; + fbcon->ypos = cy; + if ( fbcon->ypos >= fbcon->character.height ) + fbcon->ypos = 0; + fbcon_draw_cursor ( fbcon, fbcon->show_cursor ); +} + +/** + * Handle ANSI ED (erase in page) + * + * @v ctx ANSI escape sequence context + * @v count Parameter count + * @v params[0] Region to erase + */ +static void fbcon_handle_ed ( struct ansiesc_context *ctx, + unsigned int count __unused, + int params[] __unused ) { + struct fbcon *fbcon = container_of ( ctx, struct fbcon, ctx ); + + /* We assume that we always clear the whole screen */ + assert ( params[0] == ANSIESC_ED_ALL ); + + /* Clear character array */ + fbcon_clear ( fbcon, 0 ); + + /* Redraw all characters */ + fbcon_redraw ( fbcon ); + + /* Reset cursor position */ + fbcon->xpos = 0; + fbcon->ypos = 0; + fbcon_draw_cursor ( fbcon, fbcon->show_cursor ); +} + +/** + * Handle ANSI SGR (set graphics rendition) + * + * @v ctx ANSI escape sequence context + * @v count Parameter count + * @v params List of graphic rendition aspects + */ +static void fbcon_handle_sgr ( struct ansiesc_context *ctx, unsigned int count, + int params[] ) { + struct fbcon *fbcon = container_of ( ctx, struct fbcon, ctx ); + uint32_t *custom = NULL; + uint32_t rgb; + unsigned int end; + unsigned int i; + int aspect; + + for ( i = 0 ; i < count ; i++ ) { + + /* Process aspect */ + aspect = params[i]; + if ( aspect == 0 ) { + fbcon_set_default_foreground ( fbcon ); + fbcon_set_default_background ( fbcon ); + } else if ( aspect == 1 ) { + fbcon->bold = fbcon_colour ( fbcon, FBCON_BOLD ); + } else if ( aspect == 22 ) { + fbcon->bold = 0; + } else if ( ( aspect >= 30 ) && ( aspect <= 37 ) ) { + fbcon->foreground = + fbcon_ansi_colour ( fbcon, aspect - 30 ); + } else if ( aspect == 38 ) { + custom = &fbcon->foreground; + } else if ( aspect == 39 ) { + fbcon_set_default_foreground ( fbcon ); + } else if ( ( aspect >= 40 ) && ( aspect <= 47 ) ) { + fbcon->background = + fbcon_ansi_colour ( fbcon, aspect - 40 ); + } else if ( aspect == 48 ) { + custom = &fbcon->background; + } else if ( aspect == 49 ) { + fbcon_set_default_background ( fbcon ); + } + + /* Process custom RGB colour, if applicable + * + * We support the xterm-compatible + * "<ESC>[38;2;<red>;<green>;<blue>m" and + * "<ESC>[48;2;<red>;<green>;<blue>m" sequences. + */ + if ( custom ) { + rgb = 0; + end = ( i + 5 ); + for ( ; ( i < count ) && ( i < end ) ; i++ ) + rgb = ( ( rgb << 8 ) | params[i] ); + *custom = fbcon_colour ( fbcon, rgb ); + custom = NULL; + } + } +} + +/** + * Handle ANSI DECTCEM set (show cursor) + * + * @v ctx ANSI escape sequence context + * @v count Parameter count + * @v params List of graphic rendition aspects + */ +static void fbcon_handle_dectcem_set ( struct ansiesc_context *ctx, + unsigned int count __unused, + int params[] __unused ) { + struct fbcon *fbcon = container_of ( ctx, struct fbcon, ctx ); + + fbcon->show_cursor = 1; + fbcon_draw_cursor ( fbcon, 1 ); +} + +/** + * Handle ANSI DECTCEM reset (hide cursor) + * + * @v ctx ANSI escape sequence context + * @v count Parameter count + * @v params List of graphic rendition aspects + */ +static void fbcon_handle_dectcem_reset ( struct ansiesc_context *ctx, + unsigned int count __unused, + int params[] __unused ) { + struct fbcon *fbcon = container_of ( ctx, struct fbcon, ctx ); + + fbcon->show_cursor = 0; + fbcon_draw_cursor ( fbcon, 0 ); +} + +/** ANSI escape sequence handlers */ +static struct ansiesc_handler fbcon_ansiesc_handlers[] = { + { ANSIESC_CUP, fbcon_handle_cup }, + { ANSIESC_ED, fbcon_handle_ed }, + { ANSIESC_SGR, fbcon_handle_sgr }, + { ANSIESC_DECTCEM_SET, fbcon_handle_dectcem_set }, + { ANSIESC_DECTCEM_RESET, fbcon_handle_dectcem_reset }, + { 0, NULL } +}; + +/** + * Print a character to current cursor position + * + * @v fbcon Frame buffer console + * @v character Character + */ +void fbcon_putchar ( struct fbcon *fbcon, int character ) { + struct fbcon_text_cell cell; + + /* Intercept ANSI escape sequences */ + character = ansiesc_process ( &fbcon->ctx, character ); + if ( character < 0 ) + return; + + /* Handle control characters */ + switch ( character ) { + case '\r': + fbcon_draw_cursor ( fbcon, 0 ); + fbcon->xpos = 0; + break; + case '\n': + fbcon_draw_cursor ( fbcon, 0 ); + fbcon->xpos = 0; + fbcon->ypos++; + break; + case '\b': + fbcon_draw_cursor ( fbcon, 0 ); + if ( fbcon->xpos ) { + fbcon->xpos--; + } else if ( fbcon->ypos ) { + fbcon->xpos = ( fbcon->character.width - 1 ); + fbcon->ypos--; + } + break; + default: + /* Print character at current cursor position */ + cell.foreground = ( fbcon->foreground | fbcon->bold ); + cell.background = fbcon->background; + cell.character = character; + fbcon_store ( fbcon, &cell, fbcon->xpos, fbcon->ypos ); + fbcon_draw ( fbcon, &cell, fbcon->xpos, fbcon->ypos ); + + /* Advance cursor */ + fbcon->xpos++; + if ( fbcon->xpos >= fbcon->character.width ) { + fbcon->xpos = 0; + fbcon->ypos++; + } + break; + } + + /* Scroll screen if necessary */ + if ( fbcon->ypos >= fbcon->character.height ) + fbcon_scroll ( fbcon ); + + /* Show cursor */ + fbcon_draw_cursor ( fbcon, fbcon->show_cursor ); +} + +/** + * Initialise background picture + * + * @v fbcon Frame buffer console + * @v pixbuf Background picture + * @ret rc Return status code + */ +static int fbcon_picture_init ( struct fbcon *fbcon, + struct pixel_buffer *pixbuf ) { + struct fbcon_geometry *pixel = fbcon->pixel; + struct fbcon_picture *picture = &fbcon->picture; + size_t len; + size_t pixbuf_stride; + size_t indent; + size_t pixbuf_indent; + size_t offset; + size_t pixbuf_offset; + uint32_t rgb; + uint32_t raw; + unsigned int x; + unsigned int y; + unsigned int width; + unsigned int height; + int xgap; + int ygap; + int rc; + + /* Allocate buffer */ + len = ( pixel->height * pixel->stride ); + picture->start = umalloc ( len ); + if ( ! picture->start ) { + DBGC ( fbcon, "FBCON %p could not allocate %zd bytes for " + "picture\n", fbcon, len ); + rc = -ENOMEM; + goto err_umalloc; + } + + /* Centre picture on console */ + pixbuf_stride = ( pixbuf->width * sizeof ( rgb ) ); + xgap = ( ( ( int ) ( pixel->width - pixbuf->width ) ) / 2 ); + ygap = ( ( ( int ) ( pixel->height - pixbuf->height ) ) / 2 ); + indent = ( ( ( ( ygap >= 0 ) ? ygap : 0 ) * pixel->stride ) + + ( ( ( xgap >= 0 ) ? xgap : 0 ) * pixel->len ) ); + pixbuf_indent = ( ( ( ( ygap < 0 ) ? -ygap : 0 ) * pixbuf_stride ) + + ( ( ( xgap < 0 ) ? -xgap : 0 ) * sizeof ( rgb ) ) ); + width = pixbuf->width; + if ( width > pixel->width ) + width = pixel->width; + height = pixbuf->height; + if ( height > pixel->height ) + height = pixel->height; + DBGC ( fbcon, "FBCON %p picture is pixel %dx%d at [%d,%d),[%d,%d)\n", + fbcon, width, height, xgap, ( xgap + pixbuf->width ), ygap, + ( ygap + pixbuf->height ) ); + + /* Convert to frame buffer raw format */ + memset_user ( picture->start, 0, 0, len ); + for ( y = 0 ; y < height ; y++ ) { + offset = ( indent + ( y * pixel->stride ) ); + pixbuf_offset = ( pixbuf_indent + ( y * pixbuf_stride ) ); + for ( x = 0 ; x < width ; x++ ) { + copy_from_user ( &rgb, pixbuf->data, pixbuf_offset, + sizeof ( rgb ) ); + raw = fbcon_colour ( fbcon, rgb ); + copy_to_user ( picture->start, offset, &raw, + pixel->len ); + offset += pixel->len; + pixbuf_offset += sizeof ( rgb ); + } + } + + return 0; + + ufree ( picture->start ); + err_umalloc: + return rc; +} + +/** + * Initialise frame buffer console + * + * @v fbcon Frame buffer console + * @v start Start address + * @v pixel Pixel geometry + * @v margin Minimum margin + * @v map Colour mapping + * @v font Font definition + * @v pixbuf Background picture (if any) + * @ret rc Return status code + */ +int fbcon_init ( struct fbcon *fbcon, userptr_t start, + struct fbcon_geometry *pixel, + struct fbcon_margin *margin, + struct fbcon_colour_map *map, + struct fbcon_font *font, + struct pixel_buffer *pixbuf ) { + int width; + int height; + unsigned int xgap; + unsigned int ygap; + int rc; + + /* Initialise data structure */ + memset ( fbcon, 0, sizeof ( *fbcon ) ); + fbcon->start = start; + fbcon->pixel = pixel; + assert ( pixel->len <= sizeof ( uint32_t ) ); + fbcon->map = map; + fbcon->font = font; + fbcon->ctx.handlers = fbcon_ansiesc_handlers; + fbcon->show_cursor = 1; + + /* Derive overall length */ + fbcon->len = ( pixel->height * pixel->stride ); + DBGC ( fbcon, "FBCON %p at [%08lx,%08lx)\n", fbcon, + user_to_phys ( fbcon->start, 0 ), + user_to_phys ( fbcon->start, fbcon->len ) ); + + /* Expand margin to accommodate whole characters */ + width = ( pixel->width - margin->left - margin->right ); + height = ( pixel->height - margin->top - margin->bottom ); + if ( ( width < FBCON_CHAR_WIDTH ) || ( height < FBCON_CHAR_HEIGHT ) ) { + DBGC ( fbcon, "FBCON %p has unusable character area " + "[%d-%d),[%d-%d)\n", fbcon, + margin->left, ( pixel->width - margin->right ), + margin->top, ( pixel->height - margin->bottom ) ); + rc = -EINVAL; + goto err_margin; + } + xgap = ( width % FBCON_CHAR_WIDTH ); + ygap = ( height % FBCON_CHAR_HEIGHT ); + fbcon->margin.left = ( margin->left + ( xgap / 2 ) ); + fbcon->margin.top = ( margin->top + ( ygap / 2 ) ); + fbcon->margin.right = ( margin->right + ( xgap - ( xgap / 2 ) ) ); + fbcon->margin.bottom = ( margin->bottom + ( ygap - ( ygap / 2 ) ) ); + fbcon->indent = ( ( fbcon->margin.top * pixel->stride ) + + ( fbcon->margin.left * pixel->len ) ); + + /* Derive character geometry from pixel geometry */ + fbcon->character.width = ( width / FBCON_CHAR_WIDTH ); + fbcon->character.height = ( height / FBCON_CHAR_HEIGHT ); + fbcon->character.len = ( pixel->len * FBCON_CHAR_WIDTH ); + fbcon->character.stride = ( pixel->stride * FBCON_CHAR_HEIGHT ); + DBGC ( fbcon, "FBCON %p is pixel %dx%d, char %dx%d at " + "[%d-%d),[%d-%d)\n", fbcon, fbcon->pixel->width, + fbcon->pixel->height, fbcon->character.width, + fbcon->character.height, fbcon->margin.left, + ( fbcon->pixel->width - fbcon->margin.right ), + fbcon->margin.top, + ( fbcon->pixel->height - fbcon->margin.bottom ) ); + + /* Set default colours */ + fbcon_set_default_foreground ( fbcon ); + fbcon_set_default_background ( fbcon ); + + /* Allocate and initialise stored character array */ + fbcon->text.start = umalloc ( fbcon->character.width * + fbcon->character.height * + sizeof ( struct fbcon_text_cell ) ); + if ( ! fbcon->text.start ) { + rc = -ENOMEM; + goto err_text; + } + fbcon_clear ( fbcon, 0 ); + + /* Set framebuffer to all black (including margins) */ + memset_user ( fbcon->start, 0, 0, fbcon->len ); + + /* Generate pixel buffer from background image, if applicable */ + if ( pixbuf && ( ( rc = fbcon_picture_init ( fbcon, pixbuf ) ) != 0 ) ) + goto err_picture; + + /* Draw background picture (including margins), if applicable */ + if ( fbcon->picture.start ) { + memcpy_user ( fbcon->start, 0, fbcon->picture.start, 0, + fbcon->len ); + } + + /* Update console width and height */ + console_set_size ( fbcon->character.width, fbcon->character.height ); + + return 0; + + ufree ( fbcon->picture.start ); + err_picture: + ufree ( fbcon->text.start ); + err_text: + err_margin: + return rc; +} + +/** + * Finalise frame buffer console + * + * @v fbcon Frame buffer console + */ +void fbcon_fini ( struct fbcon *fbcon ) { + + ufree ( fbcon->text.start ); + ufree ( fbcon->picture.start ); +} diff --git a/qemu/roms/ipxe/src/core/fnrec.c b/qemu/roms/ipxe/src/core/fnrec.c new file mode 100644 index 000000000..3453c8b6a --- /dev/null +++ b/qemu/roms/ipxe/src/core/fnrec.c @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2010 Stefan Hajnoczi <stefanha@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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ipxe/init.h> +#include <ipxe/uaccess.h> +#include <ipxe/io.h> + +/** @file + * + * Function trace recorder for crash and hang debugging + * + */ + +/** Constant for identifying valid trace buffers */ +#define FNREC_MAGIC ( 'f' << 24 | 'n' << 16 | 'r' << 8 | 'e' ) + +/** Number of trace buffer entries */ +#define FNREC_NUM_ENTRIES 4096 + +/** Trace buffer physical address + * + * Fixed at 17MB + */ +#define FNREC_PHYS_ADDRESS ( 17 * 1024 * 1024 ) + +/** A trace buffer entry */ +struct fnrec_entry { + /** Called function address */ + void *called_fn; + /** Call site */ + void *call_site; + /** Entry count */ + uint16_t entry_count; + /** Exit count */ + uint16_t exit_count; + /** Checksum */ + unsigned long checksum; +}; + +/** A trace buffer */ +struct fnrec_buffer { + /** Constant for identifying valid trace buffers */ + uint32_t magic; + + /** Next trace buffer entry to fill */ + unsigned int idx; + + /** Trace buffer */ + struct fnrec_entry data[FNREC_NUM_ENTRIES] + __attribute__ (( aligned ( 64 ) )); +}; + +/** The trace buffer */ +static struct fnrec_buffer *fnrec_buffer; + +/** + * Test whether the trace buffer is valid + * + * @ret is_valid Buffer is valid + */ +static int fnrec_is_valid ( void ) { + return ( fnrec_buffer && ( fnrec_buffer->magic == FNREC_MAGIC ) ); +} + +/** + * Invalidate the trace buffer + * + */ +static void fnrec_invalidate ( void ) { + fnrec_buffer->magic = 0; +} + +/** + * Reset the trace buffer and clear entries + */ +static void fnrec_reset ( void ) { + memset ( fnrec_buffer, 0, sizeof ( *fnrec_buffer ) ); + fnrec_buffer->magic = FNREC_MAGIC; +} + +/** + * Append an entry to the trace buffer + * + * @v called_fn Called function + * @v call_site Call site + * @ret entry Trace buffer entry + */ +static struct fnrec_entry * fnrec_append ( void *called_fn, void *call_site ) { + struct fnrec_entry *entry; + + /* Re-use existing entry, if possible */ + entry = &fnrec_buffer->data[ fnrec_buffer->idx ]; + if ( ( entry->called_fn == called_fn ) && + ( entry->call_site == call_site ) && + ( entry->entry_count >= entry->exit_count ) ) { + return entry; + } + + /* Otherwise, create a new entry */ + fnrec_buffer->idx = ( ( fnrec_buffer->idx + 1 ) % FNREC_NUM_ENTRIES ); + entry = &fnrec_buffer->data[ fnrec_buffer->idx ]; + entry->called_fn = called_fn; + entry->call_site = call_site; + entry->entry_count = 0; + entry->exit_count = 0; + entry->checksum = ( ( ( unsigned long ) called_fn ) ^ + ( ( unsigned long ) call_site ) ); + return entry; +} + +/** + * Print the contents of the trace buffer in chronological order + */ +static void fnrec_dump ( void ) { + struct fnrec_entry *entry; + unsigned int i; + unsigned int idx; + unsigned long checksum; + + printf ( "fnrec buffer dump:\n" ); + for ( i = 1 ; i <= FNREC_NUM_ENTRIES ; i++ ) { + idx = ( ( fnrec_buffer->idx + i ) % FNREC_NUM_ENTRIES ); + entry = &fnrec_buffer->data[idx]; + if ( ( entry->entry_count == 0 ) && ( entry->exit_count == 0 ) ) + continue; + checksum = ( ( ( ( unsigned long ) entry->called_fn ) ^ + ( ( unsigned long ) entry->call_site ) ) + + entry->entry_count + entry->exit_count ); + printf ( "%p %p %d %d", entry->called_fn, entry->call_site, + entry->entry_count, entry->exit_count ); + if ( entry->checksum != checksum ) { + printf ( " (checksum wrong at phys %08lx)", + virt_to_phys ( entry ) ); + } + printf ( "\n"); + } +} + +/** + * Function tracer initialisation function + */ +static void fnrec_init ( void ) { + + fnrec_buffer = phys_to_virt ( FNREC_PHYS_ADDRESS ); + if ( fnrec_is_valid() ) { + fnrec_invalidate(); + fnrec_dump(); + } else { + printf ( "fnrec buffer not found\n" ); + } + fnrec_reset(); +} + +struct init_fn fnrec_init_fn __init_fn ( INIT_NORMAL ) = { + .initialise = fnrec_init, +}; + +/* + * These functions are called from every C function. The compiler inserts + * these calls when -finstrument-functions is used. + */ +void __cyg_profile_func_enter ( void *called_fn, void *call_site ) { + struct fnrec_entry *entry; + + if ( fnrec_is_valid() ) { + entry = fnrec_append ( called_fn, call_site ); + entry->entry_count++; + entry->checksum++; + mb(); + } +} + +void __cyg_profile_func_exit ( void *called_fn, void *call_site ) { + struct fnrec_entry *entry; + + if ( fnrec_is_valid() ) { + entry = fnrec_append ( called_fn, call_site ); + entry->exit_count++; + entry->checksum++; + mb(); + } +} diff --git a/qemu/roms/ipxe/src/core/gdbserial.c b/qemu/roms/ipxe/src/core/gdbserial.c new file mode 100644 index 000000000..6f78c88bf --- /dev/null +++ b/qemu/roms/ipxe/src/core/gdbserial.c @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2008 Stefan Hajnoczi <stefanha@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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <assert.h> +#include <ipxe/serial.h> +#include <ipxe/gdbstub.h> +#include <ipxe/gdbserial.h> + +struct gdb_transport serial_gdb_transport __gdb_transport; + +static size_t gdbserial_recv ( char *buf, size_t len ) { + assert ( len > 0 ); + buf [ 0 ] = serial_getc(); + return 1; +} + +static void gdbserial_send ( const char *buf, size_t len ) { + while ( len-- > 0 ) { + serial_putc ( *buf++ ); + } +} + +struct gdb_transport serial_gdb_transport __gdb_transport = { + .name = "serial", + .recv = gdbserial_recv, + .send = gdbserial_send, +}; + +struct gdb_transport *gdbserial_configure ( void ) { + return &serial_gdb_transport; +} diff --git a/qemu/roms/ipxe/src/core/gdbstub.c b/qemu/roms/ipxe/src/core/gdbstub.c new file mode 100644 index 000000000..af06118b2 --- /dev/null +++ b/qemu/roms/ipxe/src/core/gdbstub.c @@ -0,0 +1,401 @@ +/* + * Copyright (C) 2008 Stefan Hajnoczi <stefanha@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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** + * @file + * + * GDB stub for remote debugging + * + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <byteswap.h> +#include <ipxe/gdbstub.h> +#include "gdbmach.h" + +enum { + POSIX_EINVAL = 0x1c, /* used to report bad arguments to GDB */ + SIZEOF_PAYLOAD = 256, /* buffer size of GDB payload data */ +}; + +struct gdbstub { + struct gdb_transport *trans; + int exit_handler; /* leave interrupt handler */ + + int signo; + gdbreg_t *regs; + + void ( * parse ) ( struct gdbstub *stub, char ch ); + uint8_t cksum1; + + /* Buffer for payload data when parsing a packet. Once the + * packet has been received, this buffer is used to hold + * the reply payload. */ + char buf [ SIZEOF_PAYLOAD + 4 ]; /* $...PAYLOAD...#XX */ + char *payload; /* start of payload */ + int len; /* length of payload */ +}; + +/* Packet parser states */ +static void gdbstub_state_new ( struct gdbstub *stub, char ch ); +static void gdbstub_state_data ( struct gdbstub *stub, char ch ); +static void gdbstub_state_cksum1 ( struct gdbstub *stub, char ch ); +static void gdbstub_state_cksum2 ( struct gdbstub *stub, char ch ); +static void gdbstub_state_wait_ack ( struct gdbstub *stub, char ch ); + +static uint8_t gdbstub_from_hex_digit ( char ch ) { + return ( isdigit ( ch ) ? ch - '0' : tolower ( ch ) - 'a' + 0xa ) & 0xf; +} + +static uint8_t gdbstub_to_hex_digit ( uint8_t b ) { + b &= 0xf; + return ( b < 0xa ? '0' : 'a' - 0xa ) + b; +} + +/* + * To make reading/writing device memory atomic, we check for + * 2- or 4-byte aligned operations and handle them specially. + */ + +static void gdbstub_from_hex_buf ( char *dst, char *src, int lenbytes ) { + if ( lenbytes == 2 && ( ( unsigned long ) dst & 0x1 ) == 0 ) { + uint16_t i = gdbstub_from_hex_digit ( src [ 2 ] ) << 12 | + gdbstub_from_hex_digit ( src [ 3 ] ) << 8 | + gdbstub_from_hex_digit ( src [ 0 ] ) << 4 | + gdbstub_from_hex_digit ( src [ 1 ] ); + * ( uint16_t * ) dst = cpu_to_le16 ( i ); + } else if ( lenbytes == 4 && ( ( unsigned long ) dst & 0x3 ) == 0 ) { + uint32_t i = gdbstub_from_hex_digit ( src [ 6 ] ) << 28 | + gdbstub_from_hex_digit ( src [ 7 ] ) << 24 | + gdbstub_from_hex_digit ( src [ 4 ] ) << 20 | + gdbstub_from_hex_digit ( src [ 5 ] ) << 16 | + gdbstub_from_hex_digit ( src [ 2 ] ) << 12 | + gdbstub_from_hex_digit ( src [ 3 ] ) << 8 | + gdbstub_from_hex_digit ( src [ 0 ] ) << 4 | + gdbstub_from_hex_digit ( src [ 1 ] ); + * ( uint32_t * ) dst = cpu_to_le32 ( i ); + } else { + while ( lenbytes-- > 0 ) { + *dst++ = gdbstub_from_hex_digit ( src [ 0 ] ) << 4 | + gdbstub_from_hex_digit ( src [ 1 ] ); + src += 2; + } + } +} + +static void gdbstub_to_hex_buf ( char *dst, char *src, int lenbytes ) { + if ( lenbytes == 2 && ( ( unsigned long ) src & 0x1 ) == 0 ) { + uint16_t i = cpu_to_le16 ( * ( uint16_t * ) src ); + dst [ 0 ] = gdbstub_to_hex_digit ( i >> 4 ); + dst [ 1 ] = gdbstub_to_hex_digit ( i ); + dst [ 2 ] = gdbstub_to_hex_digit ( i >> 12 ); + dst [ 3 ] = gdbstub_to_hex_digit ( i >> 8 ); + } else if ( lenbytes == 4 && ( ( unsigned long ) src & 0x3 ) == 0 ) { + uint32_t i = cpu_to_le32 ( * ( uint32_t * ) src ); + dst [ 0 ] = gdbstub_to_hex_digit ( i >> 4 ); + dst [ 1 ] = gdbstub_to_hex_digit ( i ); + dst [ 2 ] = gdbstub_to_hex_digit ( i >> 12 ); + dst [ 3 ] = gdbstub_to_hex_digit ( i >> 8 ); + dst [ 4 ] = gdbstub_to_hex_digit ( i >> 20 ); + dst [ 5 ] = gdbstub_to_hex_digit ( i >> 16); + dst [ 6 ] = gdbstub_to_hex_digit ( i >> 28 ); + dst [ 7 ] = gdbstub_to_hex_digit ( i >> 24 ); + } else { + while ( lenbytes-- > 0 ) { + *dst++ = gdbstub_to_hex_digit ( *src >> 4 ); + *dst++ = gdbstub_to_hex_digit ( *src ); + src++; + } + } +} + +static uint8_t gdbstub_cksum ( char *data, int len ) { + uint8_t cksum = 0; + while ( len-- > 0 ) { + cksum += ( uint8_t ) *data++; + } + return cksum; +} + +static void gdbstub_tx_packet ( struct gdbstub *stub ) { + uint8_t cksum = gdbstub_cksum ( stub->payload, stub->len ); + stub->buf [ 0 ] = '$'; + stub->buf [ stub->len + 1 ] = '#'; + stub->buf [ stub->len + 2 ] = gdbstub_to_hex_digit ( cksum >> 4 ); + stub->buf [ stub->len + 3 ] = gdbstub_to_hex_digit ( cksum ); + stub->trans->send ( stub->buf, stub->len + 4 ); + stub->parse = gdbstub_state_wait_ack; +} + +/* GDB commands */ +static void gdbstub_send_ok ( struct gdbstub *stub ) { + stub->payload [ 0 ] = 'O'; + stub->payload [ 1 ] = 'K'; + stub->len = 2; + gdbstub_tx_packet ( stub ); +} + +static void gdbstub_send_num_packet ( struct gdbstub *stub, char reply, int num ) { + stub->payload [ 0 ] = reply; + stub->payload [ 1 ] = gdbstub_to_hex_digit ( ( char ) num >> 4 ); + stub->payload [ 2 ] = gdbstub_to_hex_digit ( ( char ) num ); + stub->len = 3; + gdbstub_tx_packet ( stub ); +} + +/* Format is arg1,arg2,...,argn:data where argn are hex integers and data is not an argument */ +static int gdbstub_get_packet_args ( struct gdbstub *stub, unsigned long *args, int nargs, int *stop_idx ) { + int i; + char ch = 0; + int argc = 0; + unsigned long val = 0; + for ( i = 1; i < stub->len && argc < nargs; i++ ) { + ch = stub->payload [ i ]; + if ( ch == ':' ) { + break; + } else if ( ch == ',' ) { + args [ argc++ ] = val; + val = 0; + } else { + val = ( val << 4 ) | gdbstub_from_hex_digit ( ch ); + } + } + if ( stop_idx ) { + *stop_idx = i; + } + if ( argc < nargs ) { + args [ argc++ ] = val; + } + return ( ( i == stub->len || ch == ':' ) && argc == nargs ); +} + +static void gdbstub_send_errno ( struct gdbstub *stub, int errno ) { + gdbstub_send_num_packet ( stub, 'E', errno ); +} + +static void gdbstub_report_signal ( struct gdbstub *stub ) { + gdbstub_send_num_packet ( stub, 'S', stub->signo ); +} + +static void gdbstub_read_regs ( struct gdbstub *stub ) { + gdbstub_to_hex_buf ( stub->payload, ( char * ) stub->regs, GDBMACH_SIZEOF_REGS ); + stub->len = GDBMACH_SIZEOF_REGS * 2; + gdbstub_tx_packet ( stub ); +} + +static void gdbstub_write_regs ( struct gdbstub *stub ) { + if ( stub->len != 1 + GDBMACH_SIZEOF_REGS * 2 ) { + gdbstub_send_errno ( stub, POSIX_EINVAL ); + return; + } + gdbstub_from_hex_buf ( ( char * ) stub->regs, &stub->payload [ 1 ], GDBMACH_SIZEOF_REGS ); + gdbstub_send_ok ( stub ); +} + +static void gdbstub_read_mem ( struct gdbstub *stub ) { + unsigned long args [ 2 ]; + if ( !gdbstub_get_packet_args ( stub, args, sizeof args / sizeof args [ 0 ], NULL ) ) { + gdbstub_send_errno ( stub, POSIX_EINVAL ); + return; + } + args [ 1 ] = ( args [ 1 ] < SIZEOF_PAYLOAD / 2 ) ? args [ 1 ] : SIZEOF_PAYLOAD / 2; + gdbstub_to_hex_buf ( stub->payload, ( char * ) args [ 0 ], args [ 1 ] ); + stub->len = args [ 1 ] * 2; + gdbstub_tx_packet ( stub ); +} + +static void gdbstub_write_mem ( struct gdbstub *stub ) { + unsigned long args [ 2 ]; + int colon; + if ( !gdbstub_get_packet_args ( stub, args, sizeof args / sizeof args [ 0 ], &colon ) || + colon >= stub->len || stub->payload [ colon ] != ':' || + ( stub->len - colon - 1 ) % 2 != 0 ) { + gdbstub_send_errno ( stub, POSIX_EINVAL ); + return; + } + gdbstub_from_hex_buf ( ( char * ) args [ 0 ], &stub->payload [ colon + 1 ], ( stub->len - colon - 1 ) / 2 ); + gdbstub_send_ok ( stub ); +} + +static void gdbstub_continue ( struct gdbstub *stub, int single_step ) { + gdbreg_t pc; + if ( stub->len > 1 && gdbstub_get_packet_args ( stub, &pc, 1, NULL ) ) { + gdbmach_set_pc ( stub->regs, pc ); + } + gdbmach_set_single_step ( stub->regs, single_step ); + stub->exit_handler = 1; + /* Reply will be sent when we hit the next breakpoint or interrupt */ +} + +static void gdbstub_breakpoint ( struct gdbstub *stub ) { + unsigned long args [ 3 ]; + int enable = stub->payload [ 0 ] == 'Z' ? 1 : 0; + if ( !gdbstub_get_packet_args ( stub, args, sizeof args / sizeof args [ 0 ], NULL ) ) { + gdbstub_send_errno ( stub, POSIX_EINVAL ); + return; + } + if ( gdbmach_set_breakpoint ( args [ 0 ], args [ 1 ], args [ 2 ], enable ) ) { + gdbstub_send_ok ( stub ); + } else { + /* Not supported */ + stub->len = 0; + gdbstub_tx_packet ( stub ); + } +} + +static void gdbstub_rx_packet ( struct gdbstub *stub ) { + switch ( stub->payload [ 0 ] ) { + case '?': + gdbstub_report_signal ( stub ); + break; + case 'g': + gdbstub_read_regs ( stub ); + break; + case 'G': + gdbstub_write_regs ( stub ); + break; + case 'm': + gdbstub_read_mem ( stub ); + break; + case 'M': + gdbstub_write_mem ( stub ); + break; + case 'c': /* Continue */ + case 'k': /* Kill */ + case 's': /* Step */ + case 'D': /* Detach */ + gdbstub_continue ( stub, stub->payload [ 0 ] == 's' ); + if ( stub->payload [ 0 ] == 'D' ) { + gdbstub_send_ok ( stub ); + } + break; + case 'Z': /* Insert breakpoint */ + case 'z': /* Remove breakpoint */ + gdbstub_breakpoint ( stub ); + break; + default: + stub->len = 0; + gdbstub_tx_packet ( stub ); + break; + } +} + +/* GDB packet parser */ +static void gdbstub_state_new ( struct gdbstub *stub, char ch ) { + if ( ch == '$' ) { + stub->len = 0; + stub->parse = gdbstub_state_data; + } +} + +static void gdbstub_state_data ( struct gdbstub *stub, char ch ) { + if ( ch == '#' ) { + stub->parse = gdbstub_state_cksum1; + } else if ( ch == '$' ) { + stub->len = 0; /* retry new packet */ + } else { + /* If the length exceeds our buffer, let the checksum fail */ + if ( stub->len < SIZEOF_PAYLOAD ) { + stub->payload [ stub->len++ ] = ch; + } + } +} + +static void gdbstub_state_cksum1 ( struct gdbstub *stub, char ch ) { + stub->cksum1 = gdbstub_from_hex_digit ( ch ) << 4; + stub->parse = gdbstub_state_cksum2; +} + +static void gdbstub_state_cksum2 ( struct gdbstub *stub, char ch ) { + uint8_t their_cksum; + uint8_t our_cksum; + + stub->parse = gdbstub_state_new; + their_cksum = stub->cksum1 + gdbstub_from_hex_digit ( ch ); + our_cksum = gdbstub_cksum ( stub->payload, stub->len ); + if ( their_cksum == our_cksum ) { + stub->trans->send ( "+", 1 ); + if ( stub->len > 0 ) { + gdbstub_rx_packet ( stub ); + } + } else { + stub->trans->send ( "-", 1 ); + } +} + +static void gdbstub_state_wait_ack ( struct gdbstub *stub, char ch ) { + if ( ch == '+' ) { + stub->parse = gdbstub_state_new; + } else { + /* This retransmit is very aggressive but necessary to keep + * in sync with GDB. */ + gdbstub_tx_packet ( stub ); + } +} + +static void gdbstub_parse ( struct gdbstub *stub, char ch ) { + stub->parse ( stub, ch ); +} + +static struct gdbstub stub = { + .parse = gdbstub_state_new +}; + +void gdbstub_handler ( int signo, gdbreg_t *regs ) { + char packet [ SIZEOF_PAYLOAD + 4 ]; + size_t len, i; + + /* A transport must be set up */ + if ( !stub.trans ) { + return; + } + + stub.signo = signo; + stub.regs = regs; + stub.exit_handler = 0; + gdbstub_report_signal ( &stub ); + while ( !stub.exit_handler && ( len = stub.trans->recv ( packet, sizeof ( packet ) ) ) > 0 ) { + for ( i = 0; i < len; i++ ) { + gdbstub_parse ( &stub, packet [ i ] ); + } + } +} + +struct gdb_transport *find_gdb_transport ( const char *name ) { + struct gdb_transport *trans; + + for_each_table_entry ( trans, GDB_TRANSPORTS ) { + if ( strcmp ( trans->name, name ) == 0 ) { + return trans; + } + } + return NULL; +} + +void gdbstub_start ( struct gdb_transport *trans ) { + stub.trans = trans; + stub.payload = &stub.buf [ 1 ]; + gdbmach_init(); + gdbmach_breakpoint(); +} diff --git a/qemu/roms/ipxe/src/core/gdbudp.c b/qemu/roms/ipxe/src/core/gdbudp.c new file mode 100644 index 000000000..5977547c8 --- /dev/null +++ b/qemu/roms/ipxe/src/core/gdbudp.c @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2008 Stefan Hajnoczi <stefanha@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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdio.h> +#include <string.h> +#include <byteswap.h> +#include <ipxe/iobuf.h> +#include <ipxe/in.h> +#include <ipxe/if_arp.h> +#include <ipxe/if_ether.h> +#include <ipxe/ip.h> +#include <ipxe/udp.h> +#include <ipxe/netdevice.h> +#include <ipxe/nap.h> +#include <ipxe/gdbstub.h> +#include <ipxe/gdbudp.h> + +/** @file + * + * GDB over UDP transport + * + */ + +enum { + DEFAULT_PORT = 43770, /* UDP listen port */ +}; + +struct gdb_transport udp_gdb_transport __gdb_transport; + +static struct net_device *netdev; +static uint8_t dest_eth[ETH_ALEN]; +static struct sockaddr_in dest_addr; +static struct sockaddr_in source_addr; + +static void gdbudp_ensure_netdev_open ( struct net_device *netdev ) { + /* The device may have been closed between breakpoints */ + assert ( netdev ); + netdev_open ( netdev ); + + /* Strictly speaking, we may need to close the device when leaving the interrupt handler */ +} + +static size_t gdbudp_recv ( char *buf, size_t len ) { + struct io_buffer *iob; + struct ethhdr *ethhdr; + struct arphdr *arphdr; + struct iphdr *iphdr; + struct udp_header *udphdr; + size_t payload_len; + + gdbudp_ensure_netdev_open ( netdev ); + + for ( ; ; ) { + netdev_poll ( netdev ); + while ( ( iob = netdev_rx_dequeue ( netdev ) ) != NULL ) { + /* Ethernet header */ + if ( iob_len ( iob ) < sizeof ( *ethhdr ) ) { + goto bad_packet; + } + ethhdr = iob->data; + iob_pull ( iob, sizeof ( *ethhdr ) ); + + /* Handle ARP requests so the client can find our MAC */ + if ( ethhdr->h_protocol == htons ( ETH_P_ARP ) ) { + arphdr = iob->data; + if ( iob_len ( iob ) < sizeof ( *arphdr ) + 2 * ( ETH_ALEN + sizeof ( struct in_addr ) ) || + arphdr->ar_hrd != htons ( ARPHRD_ETHER ) || + arphdr->ar_pro != htons ( ETH_P_IP ) || + arphdr->ar_hln != ETH_ALEN || + arphdr->ar_pln != sizeof ( struct in_addr ) || + arphdr->ar_op != htons ( ARPOP_REQUEST ) || + * ( uint32_t * ) arp_target_pa ( arphdr ) != source_addr.sin_addr.s_addr ) { + goto bad_packet; + } + + /* Generate an ARP reply */ + arphdr->ar_op = htons ( ARPOP_REPLY ); + memswap ( arp_sender_pa ( arphdr ), arp_target_pa ( arphdr ), sizeof ( struct in_addr ) ); + memcpy ( arp_target_ha ( arphdr ), arp_sender_ha ( arphdr ), ETH_ALEN ); + memcpy ( arp_sender_ha ( arphdr ), netdev->ll_addr, ETH_ALEN ); + + /* Fix up ethernet header */ + ethhdr = iob_push ( iob, sizeof ( *ethhdr ) ); + memcpy ( ethhdr->h_dest, ethhdr->h_source, ETH_ALEN ); + memcpy ( ethhdr->h_source, netdev->ll_addr, ETH_ALEN ); + + netdev_tx ( netdev, iob ); + continue; /* no need to free iob */ + } + + if ( ethhdr->h_protocol != htons ( ETH_P_IP ) ) { + goto bad_packet; + } + + /* IP header */ + if ( iob_len ( iob ) < sizeof ( *iphdr ) ) { + goto bad_packet; + } + iphdr = iob->data; + iob_pull ( iob, sizeof ( *iphdr ) ); + if ( iphdr->protocol != IP_UDP || iphdr->dest.s_addr != source_addr.sin_addr.s_addr ) { + goto bad_packet; + } + + /* UDP header */ + if ( iob_len ( iob ) < sizeof ( *udphdr ) ) { + goto bad_packet; + } + udphdr = iob->data; + if ( udphdr->dest != source_addr.sin_port ) { + goto bad_packet; + } + + /* Learn the remote connection details */ + memcpy ( dest_eth, ethhdr->h_source, ETH_ALEN ); + dest_addr.sin_addr.s_addr = iphdr->src.s_addr; + dest_addr.sin_port = udphdr->src; + + /* Payload */ + payload_len = ntohs ( udphdr->len ); + if ( payload_len < sizeof ( *udphdr ) || payload_len > iob_len ( iob ) ) { + goto bad_packet; + } + payload_len -= sizeof ( *udphdr ); + iob_pull ( iob, sizeof ( *udphdr ) ); + if ( payload_len > len ) { + goto bad_packet; + } + memcpy ( buf, iob->data, payload_len ); + + free_iob ( iob ); + return payload_len; + +bad_packet: + free_iob ( iob ); + } + cpu_nap(); + } +} + +static void gdbudp_send ( const char *buf, size_t len ) { + struct io_buffer *iob; + struct ethhdr *ethhdr; + struct iphdr *iphdr; + struct udp_header *udphdr; + + /* Check that we are connected */ + if ( dest_addr.sin_port == 0 ) { + return; + } + + gdbudp_ensure_netdev_open ( netdev ); + + iob = alloc_iob ( sizeof ( *ethhdr ) + sizeof ( *iphdr ) + sizeof ( *udphdr ) + len ); + if ( !iob ) { + return; + } + + /* Payload */ + iob_reserve ( iob, sizeof ( *ethhdr ) + sizeof ( *iphdr ) + sizeof ( *udphdr ) ); + memcpy ( iob_put ( iob, len ), buf, len ); + + /* UDP header */ + udphdr = iob_push ( iob, sizeof ( *udphdr ) ); + udphdr->src = source_addr.sin_port; + udphdr->dest = dest_addr.sin_port; + udphdr->len = htons ( iob_len ( iob ) ); + udphdr->chksum = 0; /* optional and we are not using it */ + + /* IP header */ + iphdr = iob_push ( iob, sizeof ( *iphdr ) ); + memset ( iphdr, 0, sizeof ( *iphdr ) ); + iphdr->verhdrlen = ( IP_VER | ( sizeof ( *iphdr ) / 4 ) ); + iphdr->service = IP_TOS; + iphdr->len = htons ( iob_len ( iob ) ); + iphdr->ttl = IP_TTL; + iphdr->protocol = IP_UDP; + iphdr->dest.s_addr = dest_addr.sin_addr.s_addr; + iphdr->src.s_addr = source_addr.sin_addr.s_addr; + iphdr->chksum = tcpip_chksum ( iphdr, sizeof ( *iphdr ) ); + + /* Ethernet header */ + ethhdr = iob_push ( iob, sizeof ( *ethhdr ) ); + memcpy ( ethhdr->h_dest, dest_eth, ETH_ALEN ); + memcpy ( ethhdr->h_source, netdev->ll_addr, ETH_ALEN ); + ethhdr->h_protocol = htons ( ETH_P_IP ); + + netdev_tx ( netdev, iob ); +} + +struct gdb_transport *gdbudp_configure ( const char *name, struct sockaddr_in *addr ) { + struct settings *settings; + + /* Release old network device */ + netdev_put ( netdev ); + + netdev = find_netdev ( name ); + if ( !netdev ) { + return NULL; + } + + /* Hold network device */ + netdev_get ( netdev ); + + /* Source UDP port */ + source_addr.sin_port = ( addr && addr->sin_port ) ? addr->sin_port : htons ( DEFAULT_PORT ); + + /* Source IP address */ + if ( addr && addr->sin_addr.s_addr ) { + source_addr.sin_addr.s_addr = addr->sin_addr.s_addr; + } else { + settings = netdev_settings ( netdev ); + fetch_ipv4_setting ( settings, &ip_setting, &source_addr.sin_addr ); + if ( source_addr.sin_addr.s_addr == 0 ) { + netdev_put ( netdev ); + netdev = NULL; + return NULL; + } + } + + return &udp_gdb_transport; +} + +static int gdbudp_init ( int argc, char **argv ) { + if ( argc != 1 ) { + printf ( "udp: missing <interface> argument\n" ); + return 1; + } + + if ( !gdbudp_configure ( argv[0], NULL ) ) { + printf ( "%s: device does not exist or has no IP address\n", argv[0] ); + return 1; + } + return 0; +} + +struct gdb_transport udp_gdb_transport __gdb_transport = { + .name = "udp", + .init = gdbudp_init, + .send = gdbudp_send, + .recv = gdbudp_recv, +}; diff --git a/qemu/roms/ipxe/src/core/getkey.c b/qemu/roms/ipxe/src/core/getkey.c new file mode 100644 index 000000000..d69cfb44b --- /dev/null +++ b/qemu/roms/ipxe/src/core/getkey.c @@ -0,0 +1,87 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <ctype.h> +#include <ipxe/console.h> +#include <ipxe/process.h> +#include <ipxe/keys.h> +#include <ipxe/timer.h> +#include <ipxe/nap.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 (0=indefinite) + * @ret character Character read from console + */ +static int getchar_timeout ( unsigned long timeout ) { + unsigned long start = currticks(); + + while ( ( timeout == 0 ) || ( ( currticks() - start ) < timeout ) ) { + step(); + if ( iskey() ) + return getchar(); + cpu_nap(); + } + + return -1; +} + +/** + * Get single keypress + * + * @v timeout Timeout period, in ticks (0=indefinite) + * @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 ( unsigned long timeout ) { + int character; + unsigned int n = 0; + + character = getchar_timeout ( timeout ); + if ( character != ESC ) + return character; + + while ( ( character = getchar_timeout ( GETKEY_TIMEOUT ) ) >= 0 ) { + if ( character == '[' ) + continue; + if ( isdigit ( character ) ) { + n = ( ( n * 10 ) + ( character - '0' ) ); + continue; + } + if ( character >= 0x40 ) + return KEY_ANSI ( n, character ); + } + + return ESC; +} diff --git a/qemu/roms/ipxe/src/core/getopt.c b/qemu/roms/ipxe/src/core/getopt.c new file mode 100644 index 000000000..abc1edd6c --- /dev/null +++ b/qemu/roms/ipxe/src/core/getopt.c @@ -0,0 +1,279 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#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++) == '-' ) { + /* "--" indicates end of options */ + if ( *opttext == '\0' ) { + optind++; + return -1; + } + 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/qemu/roms/ipxe/src/core/hw.c b/qemu/roms/ipxe/src/core/hw.c new file mode 100644 index 000000000..91736a652 --- /dev/null +++ b/qemu/roms/ipxe/src/core/hw.c @@ -0,0 +1,69 @@ +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ipxe/refcnt.h> +#include <ipxe/process.h> +#include <ipxe/xfer.h> +#include <ipxe/open.h> + +/** @file + * + * "Hello World" data source + * + */ + +struct hw { + struct refcnt refcnt; + struct interface xfer; + struct process process; +}; + +static const char hw_msg[] = "Hello world!\n"; + +static void hw_finished ( struct hw *hw, int rc ) { + intf_shutdown ( &hw->xfer, rc ); + process_del ( &hw->process ); +} + +static void hw_step ( struct hw *hw ) { + int rc; + + if ( xfer_window ( &hw->xfer ) ) { + rc = xfer_deliver_raw ( &hw->xfer, hw_msg, sizeof ( hw_msg ) ); + hw_finished ( hw, rc ); + } +} + +static struct interface_operation hw_xfer_operations[] = { + INTF_OP ( xfer_window_changed, struct hw *, hw_step ), + INTF_OP ( intf_close, struct hw *, hw_finished ), +}; + +static struct interface_descriptor hw_xfer_desc = + INTF_DESC ( struct hw, xfer, hw_xfer_operations ); + +static struct process_descriptor hw_process_desc = + PROC_DESC_ONCE ( struct hw, process, hw_step ); + +static int hw_open ( struct interface *xfer, struct uri *uri __unused ) { + struct hw *hw; + + /* Allocate and initialise structure */ + hw = zalloc ( sizeof ( *hw ) ); + if ( ! hw ) + return -ENOMEM; + ref_init ( &hw->refcnt, NULL ); + intf_init ( &hw->xfer, &hw_xfer_desc, &hw->refcnt ); + process_init ( &hw->process, &hw_process_desc, &hw->refcnt ); + + /* Attach parent interface, mortalise self, and return */ + intf_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/qemu/roms/ipxe/src/core/i82365.c b/qemu/roms/ipxe/src/core/i82365.c new file mode 100644 index 000000000..c26639e0b --- /dev/null +++ b/qemu/roms/ipxe/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/qemu/roms/ipxe/src/core/image.c b/qemu/roms/ipxe/src/core/image.c new file mode 100644 index 000000000..ec4480238 --- /dev/null +++ b/qemu/roms/ipxe/src/core/image.c @@ -0,0 +1,484 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stddef.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <assert.h> +#include <libgen.h> +#include <syslog.h> +#include <ipxe/list.h> +#include <ipxe/umalloc.h> +#include <ipxe/uri.h> +#include <ipxe/image.h> + +/** @file + * + * Executable images + * + */ + +/* Disambiguate the various error causes */ +#define EACCES_UNTRUSTED \ + __einfo_error ( EINFO_EACCES_UNTRUSTED ) +#define EINFO_EACCES_UNTRUSTED \ + __einfo_uniqify ( EINFO_EACCES, 0x01, "Untrusted image" ) +#define EACCES_PERMANENT \ + __einfo_error ( EINFO_EACCES_PERMANENT ) +#define EINFO_EACCES_PERMANENT \ + __einfo_uniqify ( EINFO_EACCES, 0x02, "Trust requirement is permanent" ) + +/** List of registered images */ +struct list_head images = LIST_HEAD_INIT ( images ); + +/** Currently-executing image */ +struct image *current_image; + +/** Current image trust requirement */ +static int require_trusted_images = 0; + +/** Prevent changes to image trust requirement */ +static int require_trusted_images_permanent = 0; + +/** + * Free executable image + * + * @v refcnt Reference counter + */ +static void free_image ( struct refcnt *refcnt ) { + struct image *image = container_of ( refcnt, struct image, refcnt ); + + DBGC ( image, "IMAGE %s freed\n", image->name ); + free ( image->name ); + free ( image->cmdline ); + uri_put ( image->uri ); + ufree ( image->data ); + image_put ( image->replacement ); + free ( image ); +} + +/** + * Allocate executable image + * + * @v uri URI, or NULL + * @ret image Executable image + */ +struct image * alloc_image ( struct uri *uri ) { + const char *name; + struct image *image; + int rc; + + /* Allocate image */ + image = zalloc ( sizeof ( *image ) ); + if ( ! image ) + goto err_alloc; + + /* Initialise image */ + ref_init ( &image->refcnt, free_image ); + if ( uri ) { + image->uri = uri_get ( uri ); + if ( uri->path ) { + name = basename ( ( char * ) uri->path ); + if ( ( rc = image_set_name ( image, name ) ) != 0 ) + goto err_set_name; + } + } + + return image; + + err_set_name: + image_put ( image ); + err_alloc: + return NULL; +} + +/** + * Set image name + * + * @v image Image + * @v name New image name + * @ret rc Return status code + */ +int image_set_name ( struct image *image, const char *name ) { + char *name_copy; + + /* Duplicate name */ + name_copy = strdup ( name ); + if ( ! name_copy ) + return -ENOMEM; + + /* Replace existing name */ + free ( image->name ); + image->name = name_copy; + + return 0; +} + +/** + * Set image command line + * + * @v image Image + * @v cmdline New image command line, or NULL + * @ret rc Return status code + */ +int image_set_cmdline ( struct image *image, const char *cmdline ) { + + free ( image->cmdline ); + image->cmdline = NULL; + if ( cmdline ) { + image->cmdline = strdup ( cmdline ); + if ( ! image->cmdline ) + return -ENOMEM; + } + return 0; +} + +/** + * Register executable image + * + * @v image Executable image + * @ret rc Return status code + */ +int register_image ( struct image *image ) { + static unsigned int imgindex = 0; + char name[8]; /* "imgXXXX" */ + int rc; + + /* Create image name if it doesn't already have one */ + if ( ! image->name ) { + snprintf ( name, sizeof ( name ), "img%d", imgindex++ ); + if ( ( rc = image_set_name ( image, name ) ) != 0 ) + return rc; + } + + /* Avoid ending up with multiple "selected" images on + * re-registration + */ + if ( image_find_selected() ) + image->flags &= ~IMAGE_SELECTED; + + /* Add to image list */ + image_get ( image ); + image->flags |= IMAGE_REGISTERED; + list_add_tail ( &image->list, &images ); + DBGC ( image, "IMAGE %s at [%lx,%lx) registered\n", + image->name, user_to_phys ( image->data, 0 ), + user_to_phys ( image->data, image->len ) ); + + return 0; +} + +/** + * Unregister executable image + * + * @v image Executable image + */ +void unregister_image ( struct image *image ) { + + /* Do nothing unless image is registered */ + if ( ! ( image->flags & IMAGE_REGISTERED ) ) + return; + + DBGC ( image, "IMAGE %s unregistered\n", image->name ); + list_del ( &image->list ); + image->flags &= ~IMAGE_REGISTERED; + image_put ( image ); +} + +/** + * Find image by name + * + * @v name Image name + * @ret image Executable 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; +} + +/** + * Determine image type + * + * @v image Executable image + * @ret rc Return status code + */ +int image_probe ( struct image *image ) { + struct image_type *type; + int rc; + + /* Succeed if we already have a type */ + if ( image->type ) + return 0; + + /* Try each type in turn */ + for_each_table_entry ( type, IMAGE_TYPES ) { + if ( ( rc = type->probe ( image ) ) == 0 ) { + image->type = type; + DBGC ( image, "IMAGE %s is %s\n", + image->name, type->name ); + return 0; + } + DBGC ( image, "IMAGE %s is not %s: %s\n", image->name, + type->name, strerror ( rc ) ); + } + + DBGC ( image, "IMAGE %s format not recognised\n", image->name ); + return -ENOEXEC; +} + +/** + * Execute image + * + * @v image Executable image + * @ret rc Return status code + * + * The image must already be registered. Note that executing an image + * may cause it to unregister itself. The caller must therefore + * assume that the image pointer becomes invalid. + */ +int image_exec ( struct image *image ) { + struct image *saved_current_image; + struct image *replacement = NULL; + struct uri *old_cwuri; + int rc; + + /* Sanity check */ + assert ( image->flags & IMAGE_REGISTERED ); + + /* Switch current working directory to be that of the image itself */ + old_cwuri = uri_get ( cwuri ); + churi ( image->uri ); + + /* Preserve record of any currently-running image */ + saved_current_image = current_image; + + /* Take out a temporary reference to the image. This allows + * the image to unregister itself if necessary, without + * automatically freeing itself. + */ + current_image = image_get ( image ); + + /* Check that this image can be selected for execution */ + if ( ( rc = image_select ( image ) ) != 0 ) + goto err; + + /* Check that image is trusted (if applicable) */ + if ( require_trusted_images && ! ( image->flags & IMAGE_TRUSTED ) ) { + DBGC ( image, "IMAGE %s is not trusted\n", image->name ); + rc = -EACCES_UNTRUSTED; + goto err; + } + + /* Record boot attempt */ + syslog ( LOG_NOTICE, "Executing \"%s\"\n", image->name ); + + /* Try executing the image */ + if ( ( rc = image->type->exec ( image ) ) != 0 ) { + DBGC ( image, "IMAGE %s could not execute: %s\n", + image->name, strerror ( rc ) ); + /* Do not return yet; we still have clean-up to do */ + } + + /* Record result of boot attempt */ + if ( rc == 0 ) { + syslog ( LOG_NOTICE, "Execution of \"%s\" completed\n", + image->name ); + } else { + syslog ( LOG_ERR, "Execution of \"%s\" failed: %s\n", + image->name, strerror ( rc ) ); + } + + /* Pick up replacement image before we drop the original + * image's temporary reference. The replacement image must + * already be registered, so we don't need to hold a temporary + * reference (which would complicate the tail-recursion). + */ + replacement = image->replacement; + if ( replacement ) + assert ( replacement->flags & IMAGE_REGISTERED ); + + err: + /* Unregister image if applicable */ + if ( image->flags & IMAGE_AUTO_UNREGISTER ) + unregister_image ( image ); + + /* Debug message for tail-recursion. Placed here because the + * image_put() may end up freeing the image. + */ + if ( replacement ) { + DBGC ( image, "IMAGE %s replacing self with IMAGE %s\n", + image->name, replacement->name ); + } + + /* Drop temporary reference to the original image */ + image_put ( image ); + + /* Restore previous currently-running image */ + current_image = saved_current_image; + + /* Reset current working directory */ + churi ( old_cwuri ); + uri_put ( old_cwuri ); + + /* Tail-recurse into replacement image, if one exists */ + if ( replacement ) + return image_exec ( replacement ); + + return rc; +} + +/** + * Set replacement image + * + * @v replacement Replacement image + * @ret rc Return status code + * + * The replacement image must already be registered, and must remain + * registered until the currently-executing image returns. + */ +int image_replace ( struct image *replacement ) { + struct image *image = current_image; + int rc; + + /* Sanity check */ + assert ( replacement->flags & IMAGE_REGISTERED ); + + /* Fail unless there is a currently-executing image */ + if ( ! image ) { + rc = -ENOTTY; + DBGC ( replacement, "IMAGE %s cannot replace non-existent " + "image: %s\n", replacement->name, strerror ( rc ) ); + return rc; + } + + /* Check that the replacement image can be executed */ + if ( ( rc = image_probe ( replacement ) ) != 0 ) + return rc; + + /* Clear any existing replacement */ + image_put ( image->replacement ); + + /* Set replacement */ + image->replacement = image_get ( replacement ); + DBGC ( image, "IMAGE %s will replace self with IMAGE %s\n", + image->name, replacement->name ); + + return 0; +} + +/** + * Select image for execution + * + * @v image Executable image + * @ret rc Return status code + */ +int image_select ( struct image *image ) { + struct image *tmp; + int rc; + + /* Unselect all other images */ + for_each_image ( tmp ) + tmp->flags &= ~IMAGE_SELECTED; + + /* Check that this image can be executed */ + if ( ( rc = image_probe ( image ) ) != 0 ) + return rc; + if ( ! image->type->exec ) + return -ENOEXEC; + + /* Mark image as selected */ + image->flags |= IMAGE_SELECTED; + + return 0; +} + +/** + * Find selected image + * + * @ret image Executable image, or NULL + */ +struct image * image_find_selected ( void ) { + struct image *image; + + for_each_image ( image ) { + if ( image->flags & IMAGE_SELECTED ) + return image; + } + return NULL; +} + +/** + * Change image trust requirement + * + * @v require_trusted Require trusted images + * @v permanent Make trust requirement permanent + * @ret rc Return status code + */ +int image_set_trust ( int require_trusted, int permanent ) { + + /* Update trust requirement, if permitted to do so */ + if ( ! require_trusted_images_permanent ) { + require_trusted_images = require_trusted; + require_trusted_images_permanent = permanent; + } + + /* Fail if we attempted to change the trust requirement but + * were not permitted to do so. + */ + if ( require_trusted_images != require_trusted ) + return -EACCES_PERMANENT; + + return 0; +} + +/** + * Create pixel buffer from image + * + * @v image Image + * @v pixbuf Pixel buffer to fill in + * @ret rc Return status code + */ +int image_pixbuf ( struct image *image, struct pixel_buffer **pixbuf ) { + int rc; + + /* Check that this image can be used to create a pixel buffer */ + if ( ( rc = image_probe ( image ) ) != 0 ) + return rc; + if ( ! image->type->pixbuf ) + return -ENOTSUP; + + /* Try creating pixel buffer */ + if ( ( rc = image->type->pixbuf ( image, pixbuf ) ) != 0 ) { + DBGC ( image, "IMAGE %s could not create pixel buffer: %s\n", + image->name, strerror ( rc ) ); + return rc; + } + + return 0; +} diff --git a/qemu/roms/ipxe/src/core/init.c b/qemu/roms/ipxe/src/core/init.c new file mode 100644 index 000000000..7ea0730fa --- /dev/null +++ b/qemu/roms/ipxe/src/core/init.c @@ -0,0 +1,103 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <ipxe/device.h> +#include <ipxe/console.h> +#include <ipxe/init.h> + +/** @file + * + * Initialisation, startup and shutdown routines + * + */ + +/** "startup() has been called" flag */ +static int started = 0; + +/** + * Initialise iPXE + * + * 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_each_table_entry ( init_fn, INIT_FNS ) + init_fn->initialise (); +} + +/** + * Start up iPXE + * + * 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_each_table_entry ( startup_fn, STARTUP_FNS ) { + if ( startup_fn->startup ) + startup_fn->startup(); + } + + started = 1; +} + +/** + * Shut down iPXE + * + * @v flags Shutdown behaviour flags + * + * This function reverses the actions of startup(), and leaves iPXE 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 ( int flags ) { + struct startup_fn *startup_fn; + + if ( ! started ) + return; + + /* Call registered shutdown functions (in reverse order) */ + for_each_table_entry_reverse ( startup_fn, STARTUP_FNS ) { + if ( startup_fn->shutdown ) + startup_fn->shutdown ( flags ); + } + + /* Reset console */ + console_reset(); + + started = 0; +} diff --git a/qemu/roms/ipxe/src/core/interface.c b/qemu/roms/ipxe/src/core/interface.c new file mode 100644 index 000000000..62f4621db --- /dev/null +++ b/qemu/roms/ipxe/src/core/interface.c @@ -0,0 +1,309 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <string.h> +#include <ipxe/interface.h> + +/** @file + * + * Object interfaces + * + */ + +/***************************************************************************** + * + * The null interface + * + */ + +/** + * Close null interface + * + * @v intf Null interface + * @v rc Reason for close + */ +static void null_intf_close ( struct interface *intf __unused, + int rc __unused ) { + + /* Do nothing. In particular, do not call intf_restart(), + * since that would result in an infinite loop. + */ +} + +/** Null interface operations */ +static struct interface_operation null_intf_op[] = { + INTF_OP ( intf_close, struct interface *, null_intf_close ), +}; + +/** Null interface descriptor */ +struct interface_descriptor null_intf_desc = + INTF_DESC_PURE ( null_intf_op ); + +/** The null interface */ +struct interface null_intf = INTF_INIT ( null_intf_desc ); + +/***************************************************************************** + * + * Object interface plumbing + * + */ + +/** + * Plug an object interface into a new destination object interface + * + * @v intf Object interface + * @v dest New destination object 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 intf_plug ( struct interface *intf, struct interface *dest ) { + DBGC ( INTF_COL ( intf ), + "INTF " INTF_INTF_FMT " replug to " INTF_FMT "\n", + INTF_INTF_DBG ( intf, intf->dest ), INTF_DBG ( dest ) ); + intf_get ( dest ); + intf_put ( intf->dest ); + intf->dest = dest; +} + +/** + * Plug two object interfaces together + * + * @v a Object interface A + * @v b Object 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 intf_plug_plug ( struct interface *a, struct interface *b ) { + intf_plug ( a, b ); + intf_plug ( b, a ); +} + +/** + * Unplug an object interface + * + * @v intf Object interface + */ +void intf_unplug ( struct interface *intf ) { + intf_plug ( intf, &null_intf ); +} + +/** + * Ignore all further operations on an object interface + * + * @v intf Object interface + */ +void intf_nullify ( struct interface *intf ) { + intf->desc = &null_intf_desc; +} + +/** + * Increment reference count on an object interface + * + * @v intf Object interface + * @ret intf Object interface + */ +struct interface * intf_get ( struct interface *intf ) { + ref_get ( intf->refcnt ); + return intf; +} + +/** + * Decrement reference count on an object interface + * + * @v intf Object interface + */ +void intf_put ( struct interface *intf ) { + ref_put ( intf->refcnt ); +} + +/** + * Get pointer to object containing object interface + * + * @v intf Object interface + * @ret object Containing object + */ +void * intf_object ( struct interface *intf ) { + return ( ( ( void * ) intf ) - intf->desc->offset ); +} + +/** + * Get pass-through interface + * + * @v intf Object interface + * @ret passthru Pass-through interface, or NULL + */ +static struct interface * intf_get_passthru ( struct interface *intf ) { + struct interface_descriptor *desc = intf->desc; + + if ( desc->passthru_offset ) { + return ( ( ( void * ) intf ) + desc->passthru_offset ); + } else { + return NULL; + } +} + +/** + * Get object interface destination and operation method (without pass-through) + * + * @v intf Object interface + * @v type Operation type + * @ret dest Destination interface + * @ret func Implementing method, or NULL + */ +void * intf_get_dest_op_no_passthru_untyped ( struct interface *intf, + void *type, + struct interface **dest ) { + struct interface_descriptor *desc; + struct interface_operation *op; + unsigned int i; + + *dest = intf_get ( intf->dest ); + desc = (*dest)->desc; + for ( i = desc->num_op, op = desc->op ; i ; i--, op++ ) { + if ( op->type == type ) + return op->func; + } + + return NULL; +} + +/** + * Get object interface destination and operation method + * + * @v intf Object interface + * @v type Operation type + * @ret dest Destination interface + * @ret func Implementing method, or NULL + */ +void * intf_get_dest_op_untyped ( struct interface *intf, void *type, + struct interface **dest ) { + void *func; + + while ( 1 ) { + + /* Search for an implementing method provided by the + * current destination interface. + */ + func = intf_get_dest_op_no_passthru_untyped( intf, type, dest ); + if ( func ) + return func; + + /* Pass through to the underlying interface, if applicable */ + if ( ! ( intf = intf_get_passthru ( *dest ) ) ) + return NULL; + intf_put ( *dest ); + } +} + +/***************************************************************************** + * + * Generic interface operations + * + */ + +/** + * Close an object interface + * + * @v intf Object interface + * @v rc Reason for close + * + * Note that this function merely informs the destination object that + * the interface is about to be closed; it doesn't actually disconnect + * the interface. In most cases, you probably want to use + * intf_shutdown() or intf_restart() instead. + */ +void intf_close ( struct interface *intf, int rc ) { + struct interface *dest; + intf_close_TYPE ( void * ) *op = + intf_get_dest_op ( intf, intf_close, &dest ); + void *object = intf_object ( dest ); + + DBGC ( INTF_COL ( intf ), "INTF " INTF_INTF_FMT " close (%s)\n", + INTF_INTF_DBG ( intf, dest ), strerror ( rc ) ); + + if ( op ) { + op ( object, rc ); + } else { + /* Default is to restart the interface */ + intf_restart ( dest, rc ); + } + + intf_put ( dest ); +} + +/** + * Shut down an object interface + * + * @v intf Object interface + * @v rc Reason for close + * + * Blocks further operations from being received via the interface, + * executes a close operation on the destination interface, and + * unplugs the interface. + */ +void intf_shutdown ( struct interface *intf, int rc ) { + + DBGC ( INTF_COL ( intf ), "INTF " INTF_FMT " shutting down (%s)\n", + INTF_DBG ( intf ), strerror ( rc ) ); + + /* Block further operations */ + intf_nullify ( intf ); + + /* Notify destination of close */ + intf_close ( intf, rc ); + + /* Unplug interface */ + intf_unplug ( intf ); +} + +/** + * Shut down and restart an object interface + * + * @v intf Object interface + * @v rc Reason for close + * + * Shuts down the interface, then unblocks operations that were + * blocked during shutdown. + */ +void intf_restart ( struct interface *intf, int rc ) { + struct interface_descriptor *desc = intf->desc; + + /* Shut down the interface */ + intf_shutdown ( intf, rc ); + + DBGC ( INTF_COL ( intf ), "INTF " INTF_FMT " restarting\n", + INTF_DBG ( intf ) ); + + /* Restore the interface descriptor. Must be done after + * shutdown (rather than inhibiting intf_shutdown() from + * nullifying the descriptor) in order to avoid a potential + * infinite loop as the intf_close() operations on each side + * of the link call each other recursively. + */ + intf->desc = desc; +} diff --git a/qemu/roms/ipxe/src/core/iobuf.c b/qemu/roms/ipxe/src/core/iobuf.c new file mode 100644 index 000000000..afc91d150 --- /dev/null +++ b/qemu/roms/ipxe/src/core/iobuf.c @@ -0,0 +1,202 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdint.h> +#include <strings.h> +#include <errno.h> +#include <ipxe/malloc.h> +#include <ipxe/iobuf.h> + +/** @file + * + * I/O buffers + * + */ + +/** + * Allocate I/O buffer with specified alignment and offset + * + * @v len Required length of buffer + * @v align Physical alignment + * @v offset Offset from physical alignment + * @ret iobuf I/O buffer, or NULL if none available + * + * @c align will be rounded up to the nearest power of two. + */ +struct io_buffer * alloc_iob_raw ( size_t len, size_t align, size_t offset ) { + struct io_buffer *iobuf; + void *data; + + /* Align buffer length to ensure that struct io_buffer is aligned */ + len = ( len + __alignof__ ( *iobuf ) - 1 ) & + ~( __alignof__ ( *iobuf ) - 1 ); + + /* Round up alignment to the nearest power of two */ + align = ( 1 << fls ( align - 1 ) ); + + /* Allocate buffer plus descriptor as a single unit, unless + * doing so will push the total size over the alignment + * boundary. + */ + if ( ( len + sizeof ( *iobuf ) ) <= align ) { + + /* Allocate memory for buffer plus descriptor */ + data = malloc_dma_offset ( len + sizeof ( *iobuf ), align, + offset ); + if ( ! data ) + return NULL; + iobuf = ( data + len ); + + } else { + + /* Allocate memory for buffer */ + data = malloc_dma_offset ( len, align, offset ); + if ( ! data ) + return NULL; + + /* Allocate memory for descriptor */ + iobuf = malloc ( sizeof ( *iobuf ) ); + if ( ! iobuf ) { + free_dma ( data, len ); + return NULL; + } + } + + /* Populate descriptor */ + iobuf->head = iobuf->data = iobuf->tail = data; + iobuf->end = ( data + len ); + + return iobuf; +} + +/** + * 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 on its own size (rounded + * up to the nearest power of two). + */ +struct io_buffer * alloc_iob ( size_t len ) { + + /* Pad to minimum length */ + if ( len < IOB_ZLEN ) + len = IOB_ZLEN; + + /* Align buffer on its own size to avoid potential problems + * with boundary-crossing DMA. + */ + return alloc_iob_raw ( len, len, 0 ); +} + +/** + * Free I/O buffer + * + * @v iobuf I/O buffer + */ +void free_iob ( struct io_buffer *iobuf ) { + size_t len; + + /* Allow free_iob(NULL) to be valid */ + if ( ! iobuf ) + return; + + /* Sanity checks */ + assert ( iobuf->head <= iobuf->data ); + assert ( iobuf->data <= iobuf->tail ); + assert ( iobuf->tail <= iobuf->end ); + + /* Free buffer */ + len = ( iobuf->end - iobuf->head ); + if ( iobuf->end == iobuf ) { + + /* Descriptor is inline */ + free_dma ( iobuf->head, ( len + sizeof ( *iobuf ) ) ); + + } else { + + /* Descriptor is detached */ + free_dma ( iobuf->head, len ); + free ( 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; +} + +/** + * Concatenate I/O buffers into a single buffer + * + * @v list List of I/O buffers + * @ret iobuf Concatenated I/O buffer, or NULL on allocation failure + * + * After a successful concatenation, the list will be empty. + */ +struct io_buffer * iob_concatenate ( struct list_head *list ) { + struct io_buffer *iobuf; + struct io_buffer *tmp; + struct io_buffer *concatenated; + size_t len = 0; + + /* If the list contains only a single entry, avoid an + * unnecessary additional allocation. + */ + if ( list_is_singular ( list ) ) { + iobuf = list_first_entry ( list, struct io_buffer, list ); + INIT_LIST_HEAD ( list ); + return iobuf; + } + + /* Calculate total length */ + list_for_each_entry ( iobuf, list, list ) + len += iob_len ( iobuf ); + + /* Allocate new I/O buffer */ + concatenated = alloc_iob_raw ( len, __alignof__ ( *iobuf ), 0 ); + if ( ! concatenated ) + return NULL; + + /* Move data to new I/O buffer */ + list_for_each_entry_safe ( iobuf, tmp, list, list ) { + list_del ( &iobuf->list ); + memcpy ( iob_put ( concatenated, iob_len ( iobuf ) ), + iobuf->data, iob_len ( iobuf ) ); + free_iob ( iobuf ); + } + + return concatenated; +} diff --git a/qemu/roms/ipxe/src/core/isqrt.c b/qemu/roms/ipxe/src/core/isqrt.c new file mode 100644 index 000000000..35c918d19 --- /dev/null +++ b/qemu/roms/ipxe/src/core/isqrt.c @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2014 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** @file + * + * Integer square root + * + */ + +#include <ipxe/isqrt.h> + +/** + * Find integer square root + * + * @v value Value + * @v isqrt Integer square root of value + */ +unsigned long isqrt ( unsigned long value ) { + unsigned long result = 0; + unsigned long bit = ( 1UL << ( ( 8 * sizeof ( bit ) ) - 2 ) ); + + while ( bit > value ) + bit >>= 2; + while ( bit ) { + if ( value >= ( result + bit ) ) { + value -= ( result + bit ); + result = ( ( result >> 1 ) + bit ); + } else { + result >>= 1; + } + bit >>= 2; + } + return result; +} diff --git a/qemu/roms/ipxe/src/core/job.c b/qemu/roms/ipxe/src/core/job.c new file mode 100644 index 000000000..674bec8b5 --- /dev/null +++ b/qemu/roms/ipxe/src/core/job.c @@ -0,0 +1,63 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <string.h> +#include <errno.h> +#include <ipxe/job.h> + +/** @file + * + * Job control interfaces + * + */ + +/** + * Get job progress + * + * @v intf Object interface + * @v progress Progress data to fill in + * @ret ongoing_rc Ongoing job status code (if known) + */ +int job_progress ( struct interface *intf, struct job_progress *progress ) { + struct interface *dest; + job_progress_TYPE ( void * ) *op = + intf_get_dest_op ( intf, job_progress, &dest ); + void *object = intf_object ( dest ); + int ongoing_rc; + + DBGC ( INTF_COL ( intf ), "INTF " INTF_INTF_FMT " job_progress\n", + INTF_INTF_DBG ( intf, dest ) ); + + /* Initialise progress to zero */ + memset ( progress, 0, sizeof ( *progress ) ); + + if ( op ) { + ongoing_rc = op ( object, progress ); + } else { + /* Default is to leave progress as zero and have no + * known return status code. + */ + ongoing_rc = 0; + } + + intf_put ( dest ); + return ongoing_rc; +} diff --git a/qemu/roms/ipxe/src/core/linebuf.c b/qemu/roms/ipxe/src/core/linebuf.c new file mode 100644 index 000000000..8fb2f86a7 --- /dev/null +++ b/qemu/roms/ipxe/src/core/linebuf.c @@ -0,0 +1,112 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** + * @file + * + * Line buffering + * + */ + +#include <stdint.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <ipxe/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/qemu/roms/ipxe/src/core/lineconsole.c b/qemu/roms/ipxe/src/core/lineconsole.c new file mode 100644 index 000000000..1b6791cf3 --- /dev/null +++ b/qemu/roms/ipxe/src/core/lineconsole.c @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2012 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** @file + * + * Line-based console + * + */ + +#include <stdint.h> +#include <stddef.h> +#include <ipxe/ansiesc.h> +#include <ipxe/lineconsole.h> + +/** + * Print a character to a line-based console + * + * @v character Character to be printed + * @ret print Print line + */ +size_t line_putchar ( struct line_console *line, int character ) { + + /* Strip ANSI escape sequences */ + character = ansiesc_process ( &line->ctx, character ); + if ( character < 0 ) + return 0; + + /* Ignore carriage return */ + if ( character == '\r' ) + return 0; + + /* Treat newline as a terminator */ + if ( character == '\n' ) + character = 0; + + /* Add character to buffer */ + line->buffer[line->index++] = character; + + /* Do nothing more unless we reach end-of-line (or end-of-buffer) */ + if ( ( character != 0 ) && + ( line->index < ( line->len - 1 /* NUL */ ) ) ) { + return 0; + } + + /* Reset to start of buffer */ + line->index = 0; + + return 1; +} diff --git a/qemu/roms/ipxe/src/core/list.c b/qemu/roms/ipxe/src/core/list.c new file mode 100644 index 000000000..77579d69a --- /dev/null +++ b/qemu/roms/ipxe/src/core/list.c @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2012 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 (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** @file + * + * Linked lists + * + */ + +#include <ipxe/list.h> + +void extern_list_add ( struct list_head *new, struct list_head *head ) { + inline_list_add ( new, head ); +} + +void extern_list_add_tail ( struct list_head *new, struct list_head *head ) { + inline_list_add_tail ( new, head ); +} + +void extern_list_del ( struct list_head *list ) { + inline_list_del ( list ); +} + +int extern_list_empty ( const struct list_head *list ) { + return inline_list_empty ( list ); +} + +int extern_list_is_singular ( const struct list_head *list ) { + return inline_list_is_singular ( list ); +} + +int extern_list_is_last ( const struct list_head *list, + const struct list_head *head ) { + return inline_list_is_last ( list, head ); +} + +void extern_list_cut_position ( struct list_head *new, + struct list_head *list, + struct list_head *entry ) { + inline_list_cut_position ( new, list, entry ); +} + +void extern_list_splice ( const struct list_head *list, + struct list_head *entry ) { + inline_list_splice ( list, entry ); +} + +void extern_list_splice_tail ( const struct list_head *list, + struct list_head *entry ) { + inline_list_splice_tail ( list, entry ); +} + +void extern_list_splice_init ( struct list_head *list, + struct list_head *entry ) { + inline_list_splice_init ( list, entry ); +} + +void extern_list_splice_tail_init ( struct list_head *list, + struct list_head *entry ) { + inline_list_splice_tail_init ( list, entry ); +} + +int extern_list_contains ( struct list_head *entry, + struct list_head *head ) { + return inline_list_contains ( entry, head ); +} diff --git a/qemu/roms/ipxe/src/core/log.c b/qemu/roms/ipxe/src/core/log.c new file mode 100644 index 000000000..f160b4fc8 --- /dev/null +++ b/qemu/roms/ipxe/src/core/log.c @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2012 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** @file + * + * System logger + * + */ + +#include <stdarg.h> +#include <syslog.h> +#include <ipxe/console.h> + +/** + * Write message to system log + * + * @v fmt Format string + * @v args Arguments + */ +void log_vprintf ( const char *fmt, va_list args ) { + int saved_usage; + + /* Mark console as in use for log messages */ + saved_usage = console_set_usage ( CONSOLE_USAGE_LOG ); + + /* Print message */ + vprintf ( fmt, args ); + + /* Restore console usage */ + console_set_usage ( saved_usage ); +} + +/** + * Write message to system log + * + * @v fmt Format string + * @v ... Arguments + */ +void log_printf ( const char *fmt, ... ) { + va_list args; + + va_start ( args, fmt ); + log_vprintf ( fmt, args ); + va_end ( args ); +} diff --git a/qemu/roms/ipxe/src/core/main.c b/qemu/roms/ipxe/src/core/main.c new file mode 100644 index 000000000..db09e4c39 --- /dev/null +++ b/qemu/roms/ipxe/src/core/main.c @@ -0,0 +1,43 @@ +/************************************************************************** +iPXE - 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) + +**************************************************************************/ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stddef.h> +#include <stdio.h> +#include <ipxe/init.h> +#include <ipxe/version.h> +#include <usr/autoboot.h> + +/** + * Main entry point + * + * @ret rc Return status code + */ +__asmcall int main ( void ) { + + /* Perform one-time-only initialisation (e.g. heap) */ + initialise(); + + /* Some devices take an unreasonably long time to initialise */ + printf ( "%s initialising devices...", product_short_name ); + startup(); + printf ( "ok\n" ); + + ipxe ( NULL ); + + shutdown_exit(); + + return 0; +} diff --git a/qemu/roms/ipxe/src/core/malloc.c b/qemu/roms/ipxe/src/core/malloc.c new file mode 100644 index 000000000..d9c07495d --- /dev/null +++ b/qemu/roms/ipxe/src/core/malloc.c @@ -0,0 +1,650 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stddef.h> +#include <stdint.h> +#include <string.h> +#include <strings.h> +#include <ipxe/io.h> +#include <ipxe/list.h> +#include <ipxe/init.h> +#include <ipxe/refcnt.h> +#include <ipxe/malloc.h> +#include <valgrind/memcheck.h> + +/** @file + * + * Dynamic memory allocation + * + */ + +/** A free block of memory */ +struct memory_block { + /** Size of this block */ + size_t size; + /** Padding + * + * This padding exists to cover the "count" field of a + * reference counter, in the common case where a reference + * counter is the first element of a dynamically-allocated + * object. It avoids clobbering the "count" field as soon as + * the memory is freed, and so allows for the possibility of + * detecting reference counting errors. + */ + char pad[ offsetof ( struct refcnt, count ) + + sizeof ( ( ( struct refcnt * ) NULL )->count ) ]; + /** List of free blocks */ + struct list_head list; +}; + +#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 512kB. + */ +#define HEAP_SIZE ( 512 * 1024 ) + +/** The heap itself */ +static char heap[HEAP_SIZE] __attribute__ (( aligned ( __alignof__(void *) ))); + +/** + * Mark all blocks in free list as defined + * + */ +static inline void valgrind_make_blocks_defined ( void ) { + struct memory_block *block; + + if ( RUNNING_ON_VALGRIND <= 0 ) + return; + + /* Traverse free block list, marking each block structure as + * defined. Some contortions are necessary to avoid errors + * from list_check(). + */ + + /* Mark block list itself as defined */ + VALGRIND_MAKE_MEM_DEFINED ( &free_blocks, sizeof ( free_blocks ) ); + + /* Mark areas accessed by list_check() as defined */ + VALGRIND_MAKE_MEM_DEFINED ( &free_blocks.prev->next, + sizeof ( free_blocks.prev->next ) ); + VALGRIND_MAKE_MEM_DEFINED ( free_blocks.next, + sizeof ( *free_blocks.next ) ); + VALGRIND_MAKE_MEM_DEFINED ( &free_blocks.next->next->prev, + sizeof ( free_blocks.next->next->prev ) ); + + /* Mark each block in list as defined */ + list_for_each_entry ( block, &free_blocks, list ) { + + /* Mark block as defined */ + VALGRIND_MAKE_MEM_DEFINED ( block, sizeof ( *block ) ); + + /* Mark areas accessed by list_check() as defined */ + VALGRIND_MAKE_MEM_DEFINED ( block->list.next, + sizeof ( *block->list.next ) ); + VALGRIND_MAKE_MEM_DEFINED ( &block->list.next->next->prev, + sizeof ( block->list.next->next->prev ) ); + } +} + +/** + * Mark all blocks in free list as inaccessible + * + */ +static inline void valgrind_make_blocks_noaccess ( void ) { + struct memory_block *block; + struct memory_block *prev = NULL; + + if ( RUNNING_ON_VALGRIND <= 0 ) + return; + + /* Traverse free block list, marking each block structure as + * inaccessible. Some contortions are necessary to avoid + * errors from list_check(). + */ + + /* Mark each block in list as inaccessible */ + list_for_each_entry ( block, &free_blocks, list ) { + + /* Mark previous block (if any) as inaccessible. (Current + * block will be accessed by list_check().) + */ + if ( prev ) + VALGRIND_MAKE_MEM_NOACCESS ( prev, sizeof ( *prev ) ); + prev = block; + + /* At the end of the list, list_check() will end up + * accessing the first list item. Temporarily mark + * this area as defined. + */ + VALGRIND_MAKE_MEM_DEFINED ( &free_blocks.next->prev, + sizeof ( free_blocks.next->prev ) ); + } + /* Mark last block (if any) as inaccessible */ + if ( prev ) + VALGRIND_MAKE_MEM_NOACCESS ( prev, sizeof ( *prev ) ); + + /* Mark as inaccessible the area that was temporarily marked + * as defined to avoid errors from list_check(). + */ + VALGRIND_MAKE_MEM_NOACCESS ( &free_blocks.next->prev, + sizeof ( free_blocks.next->prev ) ); + + /* Mark block list itself as inaccessible */ + VALGRIND_MAKE_MEM_NOACCESS ( &free_blocks, sizeof ( free_blocks ) ); +} + +/** + * Check integrity of the blocks in the free list + * + */ +static inline void check_blocks ( void ) { + struct memory_block *block; + struct memory_block *prev = NULL; + + if ( ! ASSERTING ) + return; + + list_for_each_entry ( block, &free_blocks, list ) { + + /* Check that list structure is intact */ + list_check ( &block->list ); + + /* Check that block size is not too small */ + assert ( block->size >= sizeof ( *block ) ); + assert ( block->size >= MIN_MEMBLOCK_SIZE ); + + /* Check that block does not wrap beyond end of address space */ + assert ( ( ( void * ) block + block->size ) > + ( ( void * ) block ) ); + + /* Check that blocks remain in ascending order, and + * that adjacent blocks have been merged. + */ + if ( prev ) { + assert ( ( ( void * ) block ) > ( ( void * ) prev ) ); + assert ( ( ( void * ) block ) > + ( ( ( void * ) prev ) + prev->size ) ); + } + prev = block; + } +} + +/** + * Discard some cached data + * + * @ret discarded Number of cached items discarded + */ +static unsigned int discard_cache ( void ) { + struct cache_discarder *discarder; + unsigned int discarded; + + for_each_table_entry ( discarder, CACHE_DISCARDERS ) { + discarded = discarder->discard(); + if ( discarded ) + return discarded; + } + return 0; +} + +/** + * Discard all cached data + * + */ +static void discard_all_cache ( void ) { + unsigned int discarded; + + do { + discarded = discard_cache(); + } while ( discarded ); +} + +/** + * Allocate a memory block + * + * @v size Requested size + * @v align Physical alignment + * @v offset Offset from 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, size_t offset ) { + struct memory_block *block; + size_t align_mask; + size_t pre_size; + ssize_t post_size; + struct memory_block *pre; + struct memory_block *post; + struct memory_block *ptr; + + /* Sanity checks */ + assert ( size != 0 ); + assert ( ( align == 0 ) || ( ( align & ( align - 1 ) ) == 0 ) ); + + valgrind_make_blocks_defined(); + check_blocks(); + + /* 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 ); + + DBGC2 ( &heap, "Allocating %#zx (aligned %#zx+%zx)\n", + size, align, offset ); + while ( 1 ) { + /* Search through blocks for the first one with enough space */ + list_for_each_entry ( block, &free_blocks, list ) { + pre_size = ( ( offset - 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 ); + DBGC2 ( &heap, "[%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 ) { + VALGRIND_MAKE_MEM_DEFINED ( post, + sizeof ( *post ) ); + 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 */ + DBGC2 ( &heap, "Allocated [%p,%p)\n", block, + ( ( ( void * ) block ) + size ) ); + ptr = block; + goto done; + } + } + + /* Try discarding some cached data to free up memory */ + if ( ! discard_cache() ) { + /* Nothing available to discard */ + DBGC ( &heap, "Failed to allocate %#zx (aligned " + "%#zx)\n", size, align ); + ptr = NULL; + goto done; + } + } + + done: + check_blocks(); + valgrind_make_blocks_noaccess(); + return ptr; +} + +/** + * 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; + struct memory_block *tmp; + ssize_t gap_before; + ssize_t gap_after = -1; + + /* Allow for ptr==NULL */ + if ( ! ptr ) + return; + + valgrind_make_blocks_defined(); + check_blocks(); + + /* Round up size to match actual size that alloc_memblock() + * would have used. + */ + assert ( size != 0 ); + size = ( size + MIN_MEMBLOCK_SIZE - 1 ) & ~( MIN_MEMBLOCK_SIZE - 1 ); + freeing = ptr; + VALGRIND_MAKE_MEM_DEFINED ( freeing, sizeof ( *freeing ) ); + DBGC2 ( &heap, "Freeing [%p,%p)\n", + freeing, ( ( ( void * ) freeing ) + size ) ); + + /* Check that this block does not overlap the free list */ + if ( ASSERTING ) { + list_for_each_entry ( block, &free_blocks, list ) { + if ( ( ( ( void * ) block ) < + ( ( void * ) freeing + size ) ) && + ( ( void * ) freeing < + ( ( void * ) block + block->size ) ) ) { + assert ( 0 ); + DBGC ( &heap, "Double free of [%p,%p) " + "overlapping [%p,%p) detected from %p\n", + freeing, + ( ( ( void * ) freeing ) + size ), block, + ( ( void * ) block + block->size ), + __builtin_return_address ( 0 ) ); + } + } + } + + /* Insert/merge into free list */ + freeing->size = size; + list_for_each_entry_safe ( block, tmp, &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 ) { + DBGC2 ( &heap, "[%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. + */ + DBGC2 ( &heap, "[%p,%p)\n", + freeing, ( ( ( void * ) freeing ) + freeing->size ) ); + list_add_tail ( &freeing->list, &block->list ); + if ( gap_after == 0 ) { + DBGC2 ( &heap, "[%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; + + check_blocks(); + valgrind_make_blocks_noaccess(); +} + +/** + * 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, 0 ); + if ( ! new_block ) + return NULL; + VALGRIND_MAKE_MEM_UNDEFINED ( new_block, offsetof ( struct autosized_block, data ) ); + new_block->size = new_total_size; + VALGRIND_MAKE_MEM_NOACCESS ( new_block, offsetof ( struct autosized_block, data ) ); + new_ptr = &new_block->data; + VALGRIND_MALLOCLIKE_BLOCK ( new_ptr, new_size, 0, 0 ); + } + + /* 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 ); + VALGRIND_MAKE_MEM_DEFINED ( old_block, offsetof ( struct autosized_block, data ) ); + old_total_size = old_block->size; + assert ( old_total_size != 0 ); + 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 ); + VALGRIND_MAKE_MEM_NOACCESS ( old_block, offsetof ( struct autosized_block, data ) ); + VALGRIND_FREELIKE_BLOCK ( old_ptr, 0 ); + } + + if ( ASSERTED ) { + DBGC ( &heap, "Possible memory corruption detected from %p\n", + __builtin_return_address ( 0 ) ); + } + 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 ) { + void *ptr; + + ptr = realloc ( NULL, size ); + if ( ASSERTED ) { + DBGC ( &heap, "Possible memory corruption detected from %p\n", + __builtin_return_address ( 0 ) ); + } + return ptr; +} + +/** + * 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 ); + if ( ASSERTED ) { + DBGC ( &heap, "Possible memory corruption detected from %p\n", + __builtin_return_address ( 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 ); + if ( ASSERTED ) { + DBGC ( &heap, "Possible memory corruption detected from %p\n", + __builtin_return_address ( 0 ) ); + } + 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 ) { + VALGRIND_MAKE_MEM_NOACCESS ( heap, sizeof ( heap ) ); + mpopulate ( heap, sizeof ( heap ) ); +} + +/** Memory allocator initialisation function */ +struct init_fn heap_init_fn __init_fn ( INIT_EARLY ) = { + .initialise = init_heap, +}; + +/** + * Discard all cached data on shutdown + * + */ +static void shutdown_cache ( int booting __unused ) { + discard_all_cache(); +} + +/** Memory allocator shutdown function */ +struct startup_fn heap_startup_fn __startup_fn ( STARTUP_EARLY ) = { + .shutdown = shutdown_cache, +}; + +#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/qemu/roms/ipxe/src/core/memblock.c b/qemu/roms/ipxe/src/core/memblock.c new file mode 100644 index 000000000..1fd89b871 --- /dev/null +++ b/qemu/roms/ipxe/src/core/memblock.c @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2012 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 (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** @file + * + * Largest memory block + * + */ + +#include <stdint.h> +#include <ipxe/uaccess.h> +#include <ipxe/io.h> +#include <ipxe/memblock.h> + +/** + * Find largest usable memory region + * + * @ret start Start of region + * @ret len Length of region + */ +size_t largest_memblock ( userptr_t *start ) { + struct memory_map memmap; + struct memory_region *region; + physaddr_t max = ~( ( physaddr_t ) 0 ); + physaddr_t region_start; + physaddr_t region_end; + size_t region_len; + unsigned int i; + size_t len = 0; + + /* Avoid returning uninitialised data on error */ + *start = UNULL; + + /* Scan through all memory regions */ + get_memmap ( &memmap ); + for ( i = 0 ; i < memmap.count ; i++ ) { + region = &memmap.regions[i]; + DBG ( "Considering [%llx,%llx)\n", region->start, region->end ); + + /* Truncate block to maximum physical address */ + if ( region->start > max ) { + DBG ( "...starts after maximum address %lx\n", max ); + continue; + } + region_start = region->start; + if ( region->end > max ) { + DBG ( "...end truncated to maximum address %lx\n", max); + region_end = 0; /* =max, given the wraparound */ + } else { + region_end = region->end; + } + region_len = ( region_end - region_start ); + + /* Use largest block */ + if ( region_len > len ) { + DBG ( "...new best block found\n" ); + *start = phys_to_user ( region_start ); + len = region_len; + } + } + + return len; +} diff --git a/qemu/roms/ipxe/src/core/memmap_settings.c b/qemu/roms/ipxe/src/core/memmap_settings.c new file mode 100644 index 000000000..0f6d0abf5 --- /dev/null +++ b/qemu/roms/ipxe/src/core/memmap_settings.c @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <string.h> +#include <errno.h> +#include <byteswap.h> +#include <ipxe/init.h> +#include <ipxe/settings.h> +#include <ipxe/io.h> + +/** @file + * + * Memory map settings + * + * Memory map settings are numerically encoded as: + * + * Bits 31-24 Number of regions, minus one + * Bits 23-16 Starting region + * Bits 15-11 Unused + * Bit 10 Ignore non-existent regions (rather than generating an error) + * Bit 9 Include length + * Bit 8 Include start address + * Bits 7-6 Unused + * Bits 5-0 Scale factor (i.e. right shift count) + */ + +/** + * Construct memory map setting tag + * + * @v start Starting region + * @v count Number of regions + * @v include_start Include start address + * @v include_length Include length + * @v ignore Ignore non-existent regions + * @v scale Scale factor + * @ret tag Setting tag + */ +#define MEMMAP_TAG( start, count, include_start, include_length, \ + ignore, scale ) \ + ( ( (start) << 16 ) | ( ( (count) - 1 ) << 24 ) | \ + ( (ignore) << 10 ) | ( (include_length) << 9 ) | \ + ( (include_start) << 8 ) | (scale) ) + +/** + * Extract number of regions from setting tag + * + * @v tag Setting tag + * @ret count Number of regions + */ +#define MEMMAP_COUNT( tag ) ( ( ( (tag) >> 24 ) & 0xff ) + 1 ) + +/** + * Extract starting region from setting tag + * + * @v tag Setting tag + * @ret start Starting region + */ +#define MEMMAP_START( tag ) ( ( (tag) >> 16 ) & 0xff ) + +/** + * Extract ignore flag from setting tag + * + * @v tag Setting tag + * @ret ignore Ignore non-existent regions + */ +#define MEMMAP_IGNORE_NONEXISTENT( tag ) ( (tag) & 0x00000400UL ) + +/** + * Extract length inclusion flag from setting tag + * + * @v tag Setting tag + * @ret include_length Include length + */ +#define MEMMAP_INCLUDE_LENGTH( tag ) ( (tag) & 0x00000200UL ) + +/** + * Extract start address inclusion flag from setting tag + * + * @v tag Setting tag + * @ret include_start Include start address + */ +#define MEMMAP_INCLUDE_START( tag ) ( (tag) & 0x00000100UL ) + +/** + * Extract scale factor from setting tag + * + * @v tag Setting tag + * @v scale Scale factor + */ +#define MEMMAP_SCALE( tag ) ( (tag) & 0x3f ) + +/** Memory map settings scope */ +static const struct settings_scope memmap_settings_scope; + +/** + * Check applicability of memory map setting + * + * @v settings Settings block + * @v setting Setting + * @ret applies Setting applies within this settings block + */ +static int memmap_settings_applies ( struct settings *settings __unused, + const struct setting *setting ) { + + return ( setting->scope == &memmap_settings_scope ); +} + +/** + * Fetch value of memory map 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 + */ +static int memmap_settings_fetch ( struct settings *settings, + struct setting *setting, + void *data, size_t len ) { + struct memory_map memmap; + struct memory_region *region; + uint64_t result = 0; + unsigned int i; + unsigned int count; + + DBGC ( settings, "MEMMAP start %d count %d %s%s%s%s scale %d\n", + MEMMAP_START ( setting->tag ), MEMMAP_COUNT ( setting->tag ), + ( MEMMAP_INCLUDE_START ( setting->tag ) ? "start" : "" ), + ( ( MEMMAP_INCLUDE_START ( setting->tag ) && + MEMMAP_INCLUDE_LENGTH ( setting->tag ) ) ? "+" : "" ), + ( MEMMAP_INCLUDE_LENGTH ( setting->tag ) ? "length" : "" ), + ( MEMMAP_IGNORE_NONEXISTENT ( setting->tag ) ? " ignore" : "" ), + MEMMAP_SCALE ( setting->tag ) ); + + /* Fetch memory map */ + get_memmap ( &memmap ); + + /* Extract results from memory map */ + count = MEMMAP_COUNT ( setting->tag ); + for ( i = MEMMAP_START ( setting->tag ) ; count-- ; i++ ) { + + /* Check that region exists */ + if ( i >= memmap.count ) { + if ( MEMMAP_IGNORE_NONEXISTENT ( setting->tag ) ) { + continue; + } else { + DBGC ( settings, "MEMMAP region %d does not " + "exist\n", i ); + return -ENOENT; + } + } + + /* Extract results from this region */ + region = &memmap.regions[i]; + if ( MEMMAP_INCLUDE_START ( setting->tag ) ) { + result += region->start; + DBGC ( settings, "MEMMAP %d start %08llx\n", + i, region->start ); + } + if ( MEMMAP_INCLUDE_LENGTH ( setting->tag ) ) { + result += ( region->end - region->start ); + DBGC ( settings, "MEMMAP %d length %08llx\n", + i, ( region->end - region->start ) ); + } + } + + /* Scale result */ + result >>= MEMMAP_SCALE ( setting->tag ); + + /* Return result */ + result = cpu_to_be64 ( result ); + if ( len > sizeof ( result ) ) + len = sizeof ( result ); + memcpy ( data, &result, len ); + + /* Set type if not already specified */ + if ( ! setting->type ) + setting->type = &setting_type_hexraw; + + return sizeof ( result ); +} + +/** Memory map settings operations */ +static struct settings_operations memmap_settings_operations = { + .applies = memmap_settings_applies, + .fetch = memmap_settings_fetch, +}; + +/** Memory map settings */ +static struct settings memmap_settings = { + .refcnt = NULL, + .siblings = LIST_HEAD_INIT ( memmap_settings.siblings ), + .children = LIST_HEAD_INIT ( memmap_settings.children ), + .op = &memmap_settings_operations, + .default_scope = &memmap_settings_scope, +}; + +/** Initialise memory map settings */ +static void memmap_settings_init ( void ) { + int rc; + + if ( ( rc = register_settings ( &memmap_settings, NULL, + "memmap" ) ) != 0 ) { + DBG ( "MEMMAP could not register settings: %s\n", + strerror ( rc ) ); + return; + } +} + +/** Memory map settings initialiser */ +struct init_fn memmap_settings_init_fn __init_fn ( INIT_NORMAL ) = { + .initialise = memmap_settings_init, +}; + +/** Memory map predefined settings */ +const struct setting memsize_setting __setting ( SETTING_MISC, memsize ) = { + .name = "memsize", + .description = "Memory size (in MB)", + .tag = MEMMAP_TAG ( 0, 0x100, 0, 1, 1, 20 ), + .type = &setting_type_int32, + .scope = &memmap_settings_scope, +}; diff --git a/qemu/roms/ipxe/src/core/menu.c b/qemu/roms/ipxe/src/core/menu.c new file mode 100644 index 000000000..8d42e1f83 --- /dev/null +++ b/qemu/roms/ipxe/src/core/menu.c @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2012 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** @file + * + * Menu selection + * + */ + +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <ipxe/list.h> +#include <ipxe/menu.h> + +/** List of all menus */ +static LIST_HEAD ( menus ); + +/** + * Create menu + * + * @v name Menu name, or NULL + * @v title Menu title, or NULL + * @ret menu Menu, or NULL on failure + */ +struct menu * create_menu ( const char *name, const char *title ) { + size_t name_len; + size_t title_len; + size_t len; + struct menu *menu; + char *name_copy; + char *title_copy; + + /* Destroy any existing menu of this name */ + menu = find_menu ( name ); + if ( menu ) + destroy_menu ( menu ); + + /* Use empty title if none given */ + if ( ! title ) + title = ""; + + /* Allocate menu */ + name_len = ( name ? ( strlen ( name ) + 1 /* NUL */ ) : 0 ); + title_len = ( strlen ( title ) + 1 /* NUL */ ); + len = ( sizeof ( *menu ) + name_len + title_len ); + menu = zalloc ( len ); + if ( ! menu ) + return NULL; + name_copy = ( ( void * ) ( menu + 1 ) ); + title_copy = ( name_copy + name_len ); + + /* Initialise menu */ + if ( name ) { + strcpy ( name_copy, name ); + menu->name = name_copy; + } + strcpy ( title_copy, title ); + menu->title = title_copy; + INIT_LIST_HEAD ( &menu->items ); + + /* Add to list of menus */ + list_add_tail ( &menu->list, &menus ); + + DBGC ( menu, "MENU %s created with title \"%s\"\n", + menu->name, menu->title ); + + return menu; +} + +/** + * Add menu item + * + * @v menu Menu + * @v label Label, or NULL + * @v text Text, or NULL + * @v shortcut Shortcut key + * @v is_default Item is the default item + * @ret item Menu item, or NULL on failure + */ +struct menu_item * add_menu_item ( struct menu *menu, const char *label, + const char *text, int shortcut, + int is_default ) { + size_t label_len; + size_t text_len; + size_t len; + struct menu_item *item; + char *label_copy; + char *text_copy; + + /* Use empty text if none given */ + if ( ! text ) + text = ""; + + /* Allocate item */ + label_len = ( label ? ( strlen ( label ) + 1 /* NUL */ ) : 0 ); + text_len = ( strlen ( text ) + 1 /* NUL */ ); + len = ( sizeof ( *item ) + label_len + text_len ); + item = zalloc ( len ); + if ( ! item ) + return NULL; + label_copy = ( ( void * ) ( item + 1 ) ); + text_copy = ( label_copy + label_len ); + + /* Initialise item */ + if ( label ) { + strcpy ( label_copy, label ); + item->label = label_copy; + } + strcpy ( text_copy, text ); + item->text = text_copy; + item->shortcut = shortcut; + item->is_default = is_default; + + /* Add to list of items */ + list_add_tail ( &item->list, &menu->items ); + + return item; +} + +/** + * Destroy menu + * + * @v menu Menu + */ +void destroy_menu ( struct menu *menu ) { + struct menu_item *item; + struct menu_item *tmp; + + /* Remove from list of menus */ + list_del ( &menu->list ); + + /* Free items */ + list_for_each_entry_safe ( item, tmp, &menu->items, list ) { + list_del ( &item->list ); + free ( item ); + } + + /* Free menu */ + free ( menu ); +} + +/** + * Find menu + * + * @v name Menu name, or NULL + * @ret menu Menu, or NULL if not found + */ +struct menu * find_menu ( const char *name ) { + struct menu *menu; + + list_for_each_entry ( menu, &menus, list ) { + if ( ( menu->name == name ) || + ( strcmp ( menu->name, name ) == 0 ) ) { + return menu; + } + } + + return NULL; +} diff --git a/qemu/roms/ipxe/src/core/misc.c b/qemu/roms/ipxe/src/core/misc.c new file mode 100644 index 000000000..eaceddfea --- /dev/null +++ b/qemu/roms/ipxe/src/core/misc.c @@ -0,0 +1,85 @@ +/************************************************************************** +MISC Support Routines +**************************************************************************/ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdlib.h> +#include <ctype.h> +#include <byteswap.h> +#include <ipxe/in.h> +#include <ipxe/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; +} + +unsigned int strtoul_charval ( unsigned int charval ) { + + if ( charval >= 'a' ) { + charval = ( charval - 'a' + 10 ); + } else if ( charval >= 'A' ) { + charval = ( charval - 'A' + 10 ); + } else if ( charval <= '9' ) { + charval = ( charval - '0' ); + } + + return charval; +} + +unsigned long strtoul ( const char *p, char **endp, int base ) { + unsigned long ret = 0; + int negative = 0; + unsigned int charval; + + while ( isspace ( *p ) ) + p++; + + if ( *p == '-' ) { + negative = 1; + p++; + } + + base = strtoul_base ( &p, base ); + + while ( 1 ) { + charval = strtoul_charval ( *p ); + if ( charval >= ( unsigned int ) base ) + break; + ret = ( ( ret * base ) + charval ); + p++; + } + + if ( negative ) + ret = -ret; + + if ( endp ) + *endp = ( char * ) p; + + return ( ret ); +} + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/qemu/roms/ipxe/src/core/monojob.c b/qemu/roms/ipxe/src/core/monojob.c new file mode 100644 index 000000000..820fa31dc --- /dev/null +++ b/qemu/roms/ipxe/src/core/monojob.c @@ -0,0 +1,152 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <ipxe/process.h> +#include <ipxe/console.h> +#include <ipxe/keys.h> +#include <ipxe/job.h> +#include <ipxe/monojob.h> +#include <ipxe/timer.h> + +/** @file + * + * Single foreground job + * + */ + +static int monojob_rc; + +static void monojob_close ( struct interface *intf, int rc ) { + monojob_rc = rc; + intf_restart ( intf, rc ); +} + +static struct interface_operation monojob_intf_op[] = { + INTF_OP ( intf_close, struct interface *, monojob_close ), +}; + +static struct interface_descriptor monojob_intf_desc = + INTF_DESC_PURE ( monojob_intf_op ); + +struct interface monojob = INTF_INIT ( monojob_intf_desc ); + +/** + * Wait for single foreground job to complete + * + * @v string Job description to display, or NULL to be silent + * @v timeout Timeout period, in ticks (0=indefinite) + * @ret rc Job final status code + */ +int monojob_wait ( const char *string, unsigned long timeout ) { + struct job_progress progress; + unsigned long last_keycheck; + unsigned long last_progress; + unsigned long last_display; + unsigned long now; + unsigned long elapsed; + unsigned long completed = 0; + unsigned long scaled_completed; + unsigned long scaled_total; + unsigned int percentage; + int shown_percentage = 0; + int ongoing_rc; + int key; + int rc; + + if ( string ) + printf ( "%s...", string ); + monojob_rc = -EINPROGRESS; + last_keycheck = last_progress = last_display = currticks(); + while ( monojob_rc == -EINPROGRESS ) { + + /* Allow job to progress */ + step(); + now = currticks(); + + /* Check for keypresses. This can be time-consuming, + * so check only once per clock tick. + */ + elapsed = ( now - last_keycheck ); + if ( elapsed ) { + if ( iskey() ) { + key = getchar(); + if ( key == CTRL_C ) { + monojob_rc = -ECANCELED; + break; + } + } + last_keycheck = now; + } + + /* Monitor progress */ + ongoing_rc = job_progress ( &monojob, &progress ); + + /* Reset timeout if progress has been made */ + if ( completed != progress.completed ) + last_progress = now; + completed = progress.completed; + + /* Check for timeout, if applicable */ + elapsed = ( now - last_progress ); + if ( timeout && ( elapsed >= timeout ) ) { + monojob_rc = ( ongoing_rc ? ongoing_rc : -ETIMEDOUT ); + break; + } + + /* Display progress, if applicable */ + elapsed = ( now - last_display ); + if ( string && ( elapsed >= TICKS_PER_SEC ) ) { + if ( shown_percentage ) + printf ( "\b\b\b\b \b\b\b\b" ); + /* Normalise progress figures to avoid overflow */ + scaled_completed = ( progress.completed / 128 ); + scaled_total = ( progress.total / 128 ); + if ( scaled_total ) { + percentage = ( ( 100 * scaled_completed ) / + scaled_total ); + printf ( "%3d%%", percentage ); + shown_percentage = 1; + } else { + printf ( "." ); + shown_percentage = 0; + } + last_display = now; + } + } + rc = monojob_rc; + monojob_close ( &monojob, rc ); + + if ( shown_percentage ) + printf ( "\b\b\b\b \b\b\b\b" ); + + if ( string ) { + if ( rc ) { + printf ( " %s\n", strerror ( rc ) ); + } else { + printf ( " ok\n" ); + } + } + + return rc; +} diff --git a/qemu/roms/ipxe/src/core/null_nap.c b/qemu/roms/ipxe/src/core/null_nap.c new file mode 100644 index 000000000..c886f548e --- /dev/null +++ b/qemu/roms/ipxe/src/core/null_nap.c @@ -0,0 +1,3 @@ +#include <ipxe/nap.h> + +PROVIDE_NAP_INLINE ( null, cpu_nap ); diff --git a/qemu/roms/ipxe/src/core/null_reboot.c b/qemu/roms/ipxe/src/core/null_reboot.c new file mode 100644 index 000000000..a3d5b2ef8 --- /dev/null +++ b/qemu/roms/ipxe/src/core/null_reboot.c @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** + * @file + * + * Null reboot mechanism + * + */ + +#include <stdio.h> +#include <errno.h> +#include <ipxe/reboot.h> + +/** + * Reboot system + * + * @v warm Perform a warm reboot + */ +static void null_reboot ( int warm __unused ) { + + printf ( "Cannot reboot; not implemented\n" ); + while ( 1 ) {} +} + +/** + * Power off system + * + * @ret rc Return status code + */ +static int null_poweroff ( void ) { + + return -ENOTSUP; +} + +PROVIDE_REBOOT ( null, reboot, null_reboot ); +PROVIDE_REBOOT ( null, poweroff, null_poweroff ); diff --git a/qemu/roms/ipxe/src/core/null_sanboot.c b/qemu/roms/ipxe/src/core/null_sanboot.c new file mode 100644 index 000000000..18c0dea84 --- /dev/null +++ b/qemu/roms/ipxe/src/core/null_sanboot.c @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2010 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <errno.h> +#include <ipxe/sanboot.h> + +static int null_san_hook ( struct uri *uri __unused, + unsigned int drive __unused ) { + return -EOPNOTSUPP; +} + +static void null_san_unhook ( unsigned int drive __unused ) { + /* Do nothing */ +} + +static int null_san_boot ( unsigned int drive __unused ) { + return -EOPNOTSUPP; +} + +static int null_san_describe ( unsigned int drive __unused ) { + return -EOPNOTSUPP; +} + +PROVIDE_SANBOOT_INLINE ( null, san_default_drive ); +PROVIDE_SANBOOT ( null, san_hook, null_san_hook ); +PROVIDE_SANBOOT ( null, san_unhook, null_san_unhook ); +PROVIDE_SANBOOT ( null, san_boot, null_san_boot ); +PROVIDE_SANBOOT ( null, san_describe, null_san_describe ); diff --git a/qemu/roms/ipxe/src/core/null_time.c b/qemu/roms/ipxe/src/core/null_time.c new file mode 100644 index 000000000..506c70b52 --- /dev/null +++ b/qemu/roms/ipxe/src/core/null_time.c @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2012 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** @file + * + * Nonexistent time source + * + */ + +#include <ipxe/time.h> + +PROVIDE_TIME_INLINE ( null, time_now ); diff --git a/qemu/roms/ipxe/src/core/nvo.c b/qemu/roms/ipxe/src/core/nvo.c new file mode 100644 index 000000000..e135d2b41 --- /dev/null +++ b/qemu/roms/ipxe/src/core/nvo.c @@ -0,0 +1,324 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ipxe/dhcp.h> +#include <ipxe/nvs.h> +#include <ipxe/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->len ; i++ ) { + sum += *(data++); + } + return sum; +} + +/** + * Reallocate non-volatile stored options block + * + * @v nvo Non-volatile options block + * @v len New length + * @ret rc Return status code + */ +static int nvo_realloc ( struct nvo_block *nvo, size_t len ) { + void *new_data; + + /* Reallocate data */ + new_data = realloc ( nvo->data, len ); + if ( ! new_data ) { + DBGC ( nvo, "NVO %p could not allocate %zd bytes\n", + nvo, len ); + return -ENOMEM; + } + nvo->data = new_data; + nvo->len = len; + + /* Update DHCP option block */ + if ( len ) { + nvo->dhcpopts.data = ( nvo->data + 1 /* checksum */ ); + nvo->dhcpopts.alloc_len = ( len - 1 /* checksum */ ); + } else { + nvo->dhcpopts.data = NULL; + nvo->dhcpopts.used_len = 0; + nvo->dhcpopts.alloc_len = 0; + } + + return 0; +} + +/** + * Reallocate non-volatile stored options DHCP option block + * + * @v options DHCP option block + * @v len New length + * @ret rc Return status code + */ +static int nvo_realloc_dhcpopt ( struct dhcp_options *options, size_t len ) { + struct nvo_block *nvo = + container_of ( options, struct nvo_block, dhcpopts ); + int rc; + + /* Refuse to reallocate if we have no way to resize the block */ + if ( ! nvo->resize ) + return dhcpopt_no_realloc ( options, len ); + + /* Allow one byte for the checksum (if any data is present) */ + if ( len ) + len += 1; + + /* Resize underlying non-volatile options block */ + if ( ( rc = nvo->resize ( nvo, len ) ) != 0 ) { + DBGC ( nvo, "NVO %p could not resize to %zd bytes: %s\n", + nvo, len, strerror ( rc ) ); + return rc; + } + + /* Reallocate in-memory options block */ + if ( ( rc = nvo_realloc ( nvo, len ) ) != 0 ) + return rc; + + return 0; +} + +/** + * 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 ) { + uint8_t *options_data = nvo->dhcpopts.data; + int rc; + + /* Skip reading zero-length NVO fields */ + if ( nvo->len == 0 ) { + DBGC ( nvo, "NVO %p is empty; skipping load\n", nvo ); + return 0; + } + + /* Read data */ + if ( ( rc = nvs_read ( nvo->nvs, nvo->address, nvo->data, + nvo->len ) ) != 0 ) { + DBGC ( nvo, "NVO %p could not read %zd bytes at %#04x: %s\n", + nvo, nvo->len, nvo->address, strerror ( rc ) ); + return rc; + } + + /* 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->len ); + } + + /* Rescan DHCP option block */ + dhcpopt_update_used_len ( &nvo->dhcpopts ); + + 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 ) { + uint8_t *checksum = nvo->data; + int rc; + + /* Recalculate checksum, if applicable */ + if ( nvo->len > 0 ) + *checksum -= nvo_checksum ( nvo ); + + /* Write data */ + if ( ( rc = nvs_write ( nvo->nvs, nvo->address, nvo->data, + nvo->len ) ) != 0 ) { + DBGC ( nvo, "NVO %p could not write %zd bytes at %#04x: %s\n", + nvo, nvo->len, nvo->address, strerror ( rc ) ); + return rc; + } + + DBGC ( nvo, "NVO %p saved to non-volatile storage\n", nvo ); + return 0; +} + +/** + * Check applicability of NVO setting + * + * @v settings Settings block + * @v setting Setting + * @ret applies Setting applies within this settings block + */ +int nvo_applies ( struct settings *settings __unused, + const struct setting *setting ) { + + return ( ( setting->scope == NULL ) && + dhcpopt_applies ( setting->tag ) ); +} + +/** + * 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, const 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 = { + .applies = nvo_applies, + .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 address Address within NVS device + * @v len Length of non-volatile options data + * @v resize Resize method + * @v refcnt Containing object reference counter, or NULL + */ +void nvo_init ( struct nvo_block *nvo, struct nvs_device *nvs, + size_t address, size_t len, + int ( * resize ) ( struct nvo_block *nvo, size_t len ), + struct refcnt *refcnt ) { + nvo->nvs = nvs; + nvo->address = address; + nvo->len = len; + nvo->resize = resize; + dhcpopt_init ( &nvo->dhcpopts, NULL, 0, nvo_realloc_dhcpopt ); + settings_init ( &nvo->settings, &nvo_settings_operations, + refcnt, NULL ); +} + +/** + * 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 ) { + int rc; + + /* Allocate memory for options */ + if ( ( rc = nvo_realloc ( nvo, nvo->len ) ) != 0 ) + goto err_realloc; + + /* Read data from NVS */ + if ( ( rc = nvo_load ( nvo ) ) != 0 ) + goto err_load; + + /* Register settings */ + if ( ( rc = register_settings ( &nvo->settings, parent, + NVO_SETTINGS_NAME ) ) != 0 ) + goto err_register; + + DBGC ( nvo, "NVO %p registered\n", nvo ); + return 0; + + err_register: + err_load: + nvo_realloc ( nvo, 0 ); + err_realloc: + return rc; +} + +/** + * Unregister non-volatile stored options + * + * @v nvo Non-volatile options block + */ +void unregister_nvo ( struct nvo_block *nvo ) { + unregister_settings ( &nvo->settings ); + nvo_realloc ( nvo, 0 ); + DBGC ( nvo, "NVO %p unregistered\n", nvo ); +} diff --git a/qemu/roms/ipxe/src/core/open.c b/qemu/roms/ipxe/src/core/open.c new file mode 100644 index 000000000..b479c2975 --- /dev/null +++ b/qemu/roms/ipxe/src/core/open.c @@ -0,0 +1,228 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdarg.h> +#include <string.h> +#include <errno.h> +#include <ipxe/xfer.h> +#include <ipxe/uri.h> +#include <ipxe/socket.h> +#include <ipxe/open.h> + +/** @file + * + * Data transfer interface opening + * + */ + +/** + * Find opener for URI scheme + * + * @v scheme URI scheme + * @ret opener Opener, or NULL + */ +struct uri_opener * xfer_uri_opener ( const char *scheme ) { + struct uri_opener *opener; + + for_each_table_entry ( opener, URI_OPENERS ) { + if ( strcmp ( scheme, opener->scheme ) == 0 ) + return opener; + } + return NULL; +} + +/** + * Open URI + * + * @v intf 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 interface *intf, struct uri *uri ) { + struct uri_opener *opener; + struct uri *resolved_uri; + int rc; + + /* Resolve URI */ + resolved_uri = resolve_uri ( cwuri, uri ); + if ( ! resolved_uri ) { + rc = -ENOMEM; + goto err_resolve_uri; + } + + /* Find opener which supports this URI scheme */ + opener = xfer_uri_opener ( resolved_uri->scheme ); + if ( ! opener ) { + DBGC ( INTF_COL ( intf ), "INTF " INTF_FMT " attempted to open " + "unsupported URI scheme \"%s\"\n", + INTF_DBG ( intf ), resolved_uri->scheme ); + rc = -ENOTSUP; + goto err_opener; + } + + /* Call opener */ + DBGC ( INTF_COL ( intf ), "INTF " INTF_FMT " opening %s URI\n", + INTF_DBG ( intf ), resolved_uri->scheme ); + if ( ( rc = opener->open ( intf, resolved_uri ) ) != 0 ) { + DBGC ( INTF_COL ( intf ), "INTF " INTF_FMT " could not open: " + "%s\n", INTF_DBG ( intf ), strerror ( rc ) ); + goto err_open; + } + + err_open: + err_opener: + uri_put ( resolved_uri ); + err_resolve_uri: + return rc; +} + +/** + * Open URI string + * + * @v intf Data transfer interface + * @v uri_string URI string (e.g. "http://ipxe.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 interface *intf, + const char *uri_string ) { + struct uri *uri; + int rc; + + DBGC ( INTF_COL ( intf ), "INTF " INTF_FMT " opening URI %s\n", + INTF_DBG ( intf ), uri_string ); + + uri = parse_uri ( uri_string ); + if ( ! uri ) + return -ENOMEM; + + rc = xfer_open_uri ( intf, uri ); + + uri_put ( uri ); + return rc; +} + +/** + * Open socket + * + * @v intf 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 interface *intf, int semantics, + struct sockaddr *peer, struct sockaddr *local ) { + struct socket_opener *opener; + + DBGC ( INTF_COL ( intf ), "INTF " INTF_FMT " opening (%s,%s) socket\n", + INTF_DBG ( intf ), socket_semantics_name ( semantics ), + socket_family_name ( peer->sa_family ) ); + + for_each_table_entry ( opener, SOCKET_OPENERS ) { + if ( ( opener->semantics == semantics ) && + ( opener->family == peer->sa_family ) ) { + return opener->open ( intf, peer, local ); + } + } + + DBGC ( INTF_COL ( intf ), "INTF " INTF_FMT " attempted to open " + "unsupported socket type (%s,%s)\n", + INTF_DBG ( intf ), socket_semantics_name ( semantics ), + socket_family_name ( peer->sa_family ) ); + return -ENOTSUP; +} + +/** + * Open location + * + * @v intf Data transfer interface + * @v type Location type + * @v args Remaining arguments depend upon location type + * @ret rc Return status code + */ +int xfer_vopen ( struct interface *intf, 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 ( intf, uri_string ); } + case LOCATION_URI: { + struct uri *uri = va_arg ( args, struct uri * ); + + return xfer_open_uri ( intf, 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 ( intf, semantics, peer, local ); } + default: + DBGC ( INTF_COL ( intf ), "INTF " INTF_FMT " attempted to " + "open unsupported location type %d\n", + INTF_DBG ( intf ), type ); + return -ENOTSUP; + } +} + +/** + * Open location + * + * @v intf Data transfer interface + * @v type Location type + * @v ... Remaining arguments depend upon location type + * @ret rc Return status code + */ +int xfer_open ( struct interface *intf, int type, ... ) { + va_list args; + int rc; + + va_start ( args, type ); + rc = xfer_vopen ( intf, type, args ); + va_end ( args ); + return rc; +} + +/** + * Reopen location + * + * @v intf Data transfer interface + * @v type Location type + * @v args Remaining arguments depend upon location type + * @ret rc Return status code + * + * This will close the existing connection and open a new connection + * using xfer_vopen(). It is intended to be used as a .vredirect + * method handler. + */ +int xfer_vreopen ( struct interface *intf, int type, va_list args ) { + + /* Close existing connection */ + intf_restart ( intf, 0 ); + + /* Open new location */ + return xfer_vopen ( intf, type, args ); +} diff --git a/qemu/roms/ipxe/src/core/params.c b/qemu/roms/ipxe/src/core/params.c new file mode 100644 index 000000000..93b834419 --- /dev/null +++ b/qemu/roms/ipxe/src/core/params.c @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** @file + * + * Form parameters + * + */ + +#include <stdlib.h> +#include <string.h> +#include <ipxe/params.h> + +/** List of all parameter lists */ +static LIST_HEAD ( parameters ); + +/** + * Free form parameter list + * + * @v refcnt Reference count + */ +static void free_parameters ( struct refcnt *refcnt ) { + struct parameters *params = + container_of ( refcnt, struct parameters, refcnt ); + struct parameter *param; + struct parameter *tmp; + + DBGC ( params, "PARAMS \"%s\" destroyed\n", params->name ); + + /* Free all parameters */ + list_for_each_entry_safe ( param, tmp, ¶ms->entries, list ) { + list_del ( ¶m->list ); + free ( param ); + } + + /* Free parameter list */ + free ( params ); +} + +/** + * Find form parameter list by name + * + * @v name Parameter list name (may be NULL) + * @ret params Parameter list, or NULL if not found + */ +struct parameters * find_parameters ( const char *name ) { + struct parameters *params; + + list_for_each_entry ( params, ¶meters, list ) { + if ( ( params->name == name ) || + ( strcmp ( params->name, name ) == 0 ) ) { + return params; + } + } + return NULL; +} + +/** + * Create form parameter list + * + * @v name Parameter list name (may be NULL) + * @ret params Parameter list, or NULL on failure + */ +struct parameters * create_parameters ( const char *name ) { + struct parameters *params; + size_t name_len; + char *name_copy; + + /* Destroy any existing parameter list of this name */ + params = find_parameters ( name ); + if ( params ) { + claim_parameters ( params ); + params_put ( params ); + } + + /* Allocate parameter list */ + name_len = ( name ? ( strlen ( name ) + 1 /* NUL */ ) : 0 ); + params = zalloc ( sizeof ( *params ) + name_len ); + if ( ! params ) + return NULL; + ref_init ( ¶ms->refcnt, free_parameters ); + name_copy = ( ( void * ) ( params + 1 ) ); + + /* Populate parameter list */ + if ( name ) { + strcpy ( name_copy, name ); + params->name = name_copy; + } + INIT_LIST_HEAD ( ¶ms->entries ); + + /* Add to list of parameter lists */ + list_add_tail ( ¶ms->list, ¶meters ); + + DBGC ( params, "PARAMS \"%s\" created\n", params->name ); + return params; +} + +/** + * Add form parameter + * + * @v params Parameter list + * @v key Parameter key + * @v value Parameter value + * @ret param Parameter, or NULL on failure + */ +struct parameter * add_parameter ( struct parameters *params, + const char *key, const char *value ) { + struct parameter *param; + size_t key_len; + size_t value_len; + char *key_copy; + char *value_copy; + + /* Allocate parameter */ + key_len = ( strlen ( key ) + 1 /* NUL */ ); + value_len = ( strlen ( value ) + 1 /* NUL */ ); + param = zalloc ( sizeof ( *param ) + key_len + value_len ); + if ( ! param ) + return NULL; + key_copy = ( ( void * ) ( param + 1 ) ); + value_copy = ( key_copy + key_len ); + + /* Populate parameter */ + strcpy ( key_copy, key ); + param->key = key_copy; + strcpy ( value_copy, value ); + param->value = value_copy; + + /* Add to list of parameters */ + list_add_tail ( ¶m->list, ¶ms->entries ); + + DBGC ( params, "PARAMS \"%s\" added \"%s\"=\"%s\"\n", + params->name, param->key, param->value ); + return param; +} diff --git a/qemu/roms/ipxe/src/core/parseopt.c b/qemu/roms/ipxe/src/core/parseopt.c new file mode 100644 index 000000000..d268c0594 --- /dev/null +++ b/qemu/roms/ipxe/src/core/parseopt.c @@ -0,0 +1,449 @@ +/* + * Copyright (C) 2010 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <getopt.h> +#include <ipxe/netdevice.h> +#include <ipxe/menu.h> +#include <ipxe/settings.h> +#include <ipxe/params.h> +#include <ipxe/timer.h> +#include <ipxe/parseopt.h> + +/** @file + * + * Command line option parsing + * + */ + +/** Return status code for "--help" option */ +#define ECANCELED_NO_OP __einfo_error ( EINFO_ECANCELED_NO_OP ) +#define EINFO_ECANCELED_NO_OP \ + __einfo_uniqify ( EINFO_ECANCELED, 0x01, "Nothing to do" ) + +/* Disambiguate the various error codes */ +#define EINVAL_INTEGER __einfo_error ( EINFO_EINVAL_INTEGER ) +#define EINFO_EINVAL_INTEGER \ + __einfo_uniqify ( EINFO_EINVAL, 0x01, "Invalid integer value" ) +#define EINVAL_UNKNOWN_OPTION __einfo_error ( EINFO_EINVAL_UNKNOWN_OPTION ) +#define EINFO_EINVAL_UNKNOWN_OPTION \ + __einfo_uniqify ( EINFO_EINVAL, 0x02, "Unrecognised option" ) +#define EINVAL_MISSING_ARGUMENT __einfo_error ( EINFO_EINVAL_MISSING_ARGUMENT ) +#define EINFO_EINVAL_MISSING_ARGUMENT \ + __einfo_uniqify ( EINFO_EINVAL, 0x03, "Missing argument" ) + +/** +* Parse string value + * + * @v text Text + * @ret value String value + * @ret rc Return status code + */ +int parse_string ( char *text, char **value ) { + + /* Sanity check */ + assert ( text != NULL ); + + /* Parse string */ + *value = text; + + return 0; +} + +/** + * Parse integer value + * + * @v text Text + * @ret value Integer value + * @ret rc Return status code + */ +int parse_integer ( char *text, unsigned int *value ) { + char *endp; + + /* Sanity check */ + assert ( text != NULL ); + + /* Parse integer */ + *value = strtoul ( text, &endp, 0 ); + if ( *endp ) { + printf ( "\"%s\": invalid integer value\n", text ); + return -EINVAL_INTEGER; + } + + return 0; +} + +/** + * Parse timeout value (in ms) + * + * @v text Text + * @ret value Integer value + * @ret rc Return status code + */ +int parse_timeout ( char *text, unsigned long *value ) { + unsigned int value_ms; + int rc; + + /* Parse raw integer value */ + if ( ( rc = parse_integer ( text, &value_ms ) ) != 0 ) + return rc; + + /* Convert to a number of timer ticks */ + *value = ( ( value_ms * TICKS_PER_SEC ) / 1000 ); + + return 0; +} + +/** + * Parse network device name + * + * @v text Text + * @ret netdev Network device + * @ret rc Return status code + */ +int parse_netdev ( char *text, struct net_device **netdev ) { + + /* Sanity check */ + assert ( text != NULL ); + + /* Find network device */ + *netdev = find_netdev ( text ); + if ( ! *netdev ) { + printf ( "\"%s\": no such network device\n", text ); + return -ENODEV; + } + + return 0; +} + +/** + * Parse network device configurator name + * + * @v text Text + * @ret configurator Network device configurator + * @ret rc Return status code + */ +int parse_netdev_configurator ( char *text, + struct net_device_configurator **configurator ){ + + /* Sanity check */ + assert ( text != NULL ); + + /* Find network device configurator */ + *configurator = find_netdev_configurator ( text ); + if ( ! *configurator ) { + printf ( "\"%s\": no such configurator\n", text ); + return -ENOTSUP; + } + + return 0; +} + +/** + * Parse menu name + * + * @v text Text + * @ret menu Menu + * @ret rc Return status code + */ +int parse_menu ( char *text, struct menu **menu ) { + + /* Find menu */ + *menu = find_menu ( text ); + if ( ! *menu ) { + if ( text ) { + printf ( "\"%s\": no such menu\n", text ); + } else { + printf ( "No default menu\n" ); + } + return -ENOENT; + } + + return 0; +} + +/** + * Parse flag + * + * @v text Text (ignored) + * @ret flag Flag to set + * @ret rc Return status code + */ +int parse_flag ( char *text __unused, int *flag ) { + + /* Set flag */ + *flag = 1; + + return 0; +} + +/** + * Parse key + * + * @v text Text + * @ret key Key + * @ret rc Return status code + */ +int parse_key ( char *text, unsigned int *key ) { + + /* Interpret single characters as being a literal key character */ + if ( text[0] && ! text[1] ) { + *key = text[0]; + return 0; + } + + /* Otherwise, interpret as an integer */ + return parse_integer ( text, key ); +} + +/** + * Parse settings block name + * + * @v text Text + * @ret value Integer value + * @ret rc Return status code + */ +int parse_settings ( char *text, struct settings **value ) { + + /* Sanity check */ + assert ( text != NULL ); + + /* Parse scope name */ + *value = find_settings ( text ); + if ( ! *value ) { + printf ( "\"%s\": no such scope\n", text ); + return -EINVAL; + } + + return 0; +} + +/** + * Parse setting name + * + * @v text Text + * @v setting Named setting to fill in + * @v get_child Function to find or create child settings block + * @ret rc Return status code + * + * Note that this function modifies the original @c text. + */ +int parse_setting ( char *text, struct named_setting *setting, + get_child_settings_t get_child ) { + int rc; + + /* Sanity check */ + assert ( text != NULL ); + + /* Parse setting name */ + if ( ( rc = parse_setting_name ( text, get_child, &setting->settings, + &setting->setting ) ) != 0 ) { + printf ( "\"%s\": invalid setting\n", text ); + return rc; + } + + return 0; +} + +/** + * Parse existing setting name + * + * @v text Text + * @v setting Named setting to fill in + * @ret rc Return status code + * + * Note that this function modifies the original @c text. + */ +int parse_existing_setting ( char *text, struct named_setting *setting ) { + + return parse_setting ( text, setting, find_child_settings ); +} + +/** + * Parse and autovivify setting name + * + * @v text Text + * @v setting Named setting to fill in + * @ret rc Return status code + * + * Note that this function modifies the original @c text. + */ +int parse_autovivified_setting ( char *text, struct named_setting *setting ) { + + return parse_setting ( text, setting, autovivify_child_settings ); +} + +/** + * Parse form parameter list name + * + * @v text Text + * @ret params Parameter list + * @ret rc Return status code + */ +int parse_parameters ( char *text, struct parameters **params ) { + + /* Find parameter list */ + *params = find_parameters ( text ); + if ( ! *params ) { + if ( text ) { + printf ( "\"%s\": no such parameter list\n", text ); + } else { + printf ( "No default parameter list\n" ); + } + return -ENOENT; + } + + return 0; +} + +/** + * Print command usage message + * + * @v cmd Command descriptor + * @v argv Argument list + */ +void print_usage ( struct command_descriptor *cmd, char **argv ) { + struct option_descriptor *option; + unsigned int i; + int is_optional; + + printf ( "Usage:\n\n %s", argv[0] ); + for ( i = 0 ; i < cmd->num_options ; i++ ) { + option = &cmd->options[i]; + printf ( " [-%c|--%s", option->shortopt, option->longopt ); + if ( option->has_arg ) { + is_optional = ( option->has_arg == optional_argument ); + printf ( " %s<%s>%s", ( is_optional ? "[" : "" ), + option->longopt, ( is_optional ? "]" : "" ) ); + } + printf ( "]" ); + } + if ( cmd->usage ) + printf ( " %s", cmd->usage ); + printf ( "\n\nSee http://ipxe.org/cmd/%s for further information\n", + argv[0] ); +} + +/** + * Reparse command-line options + * + * @v argc Argument count + * @v argv Argument list + * @v cmd Command descriptor + * @v opts Options (already initialised with default values) + * @ret rc Return status code + */ +int reparse_options ( int argc, char **argv, struct command_descriptor *cmd, + void *opts ) { + struct option longopts[ cmd->num_options + 1 /* help */ + 1 /* end */ ]; + char shortopts[ cmd->num_options * 3 /* possible "::" */ + 1 /* "h" */ + + 1 /* NUL */ ]; + unsigned int shortopt_idx = 0; + int ( * parse ) ( char *text, void *value ); + void *value; + unsigned int i; + unsigned int j; + unsigned int num_args; + int c; + int rc; + + /* Construct long and short option lists for getopt_long() */ + memset ( longopts, 0, sizeof ( longopts ) ); + for ( i = 0 ; i < cmd->num_options ; i++ ) { + longopts[i].name = cmd->options[i].longopt; + longopts[i].has_arg = cmd->options[i].has_arg; + longopts[i].val = cmd->options[i].shortopt; + shortopts[shortopt_idx++] = cmd->options[i].shortopt; + assert ( cmd->options[i].has_arg <= optional_argument ); + for ( j = cmd->options[i].has_arg ; j > 0 ; j-- ) + shortopts[shortopt_idx++] = ':'; + } + longopts[i].name = "help"; + longopts[i].val = 'h'; + shortopts[shortopt_idx++] = 'h'; + shortopts[shortopt_idx++] = '\0'; + assert ( shortopt_idx <= sizeof ( shortopts ) ); + DBGC ( cmd, "Command \"%s\" has options \"%s\", %d-%d args, len %d\n", + argv[0], shortopts, cmd->min_args, cmd->max_args, cmd->len ); + + /* Parse options */ + while ( ( c = getopt_long ( argc, argv, shortopts, longopts, + NULL ) ) >= 0 ) { + switch ( c ) { + case 'h' : + /* Print help */ + print_usage ( cmd, argv ); + return -ECANCELED_NO_OP; + case '?' : + /* Print usage message */ + print_usage ( cmd, argv ); + return -EINVAL_UNKNOWN_OPTION; + case ':' : + /* Print usage message */ + print_usage ( cmd, argv ); + return -EINVAL_MISSING_ARGUMENT; + default: + /* Search for an option to parse */ + for ( i = 0 ; i < cmd->num_options ; i++ ) { + if ( c != cmd->options[i].shortopt ) + continue; + parse = cmd->options[i].parse; + value = ( opts + cmd->options[i].offset ); + if ( ( rc = parse ( optarg, value ) ) != 0 ) + return rc; + break; + } + assert ( i < cmd->num_options ); + } + } + + /* Check remaining arguments */ + num_args = ( argc - optind ); + if ( ( num_args < cmd->min_args ) || ( num_args > cmd->max_args ) ) { + print_usage ( cmd, argv ); + return -ERANGE; + } + + return 0; +} + +/** + * Parse command-line options + * + * @v argc Argument count + * @v argv Argument list + * @v cmd Command descriptor + * @v opts Options (may be uninitialised) + * @ret rc Return status code + */ +int parse_options ( int argc, char **argv, struct command_descriptor *cmd, + void *opts ) { + + /* Clear options */ + memset ( opts, 0, cmd->len ); + + return reparse_options ( argc, argv, cmd, opts ); +} diff --git a/qemu/roms/ipxe/src/core/pc_kbd.c b/qemu/roms/ipxe/src/core/pc_kbd.c new file mode 100644 index 000000000..42df755b5 --- /dev/null +++ b/qemu/roms/ipxe/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 <ipxe/io.h> +#include <ipxe/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/qemu/roms/ipxe/src/core/pcmcia.c b/qemu/roms/ipxe/src/core/pcmcia.c new file mode 100644 index 000000000..5fd21f4a9 --- /dev/null +++ b/qemu/roms/ipxe/src/core/pcmcia.c @@ -0,0 +1,269 @@ +#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* + */ + +FILE_LICENCE ( GPL2_ONLY ); + +#include <stdio.h> +#include <pcmcia.h> +#include <i82365.h> +#define CODE_STATUS "alpha" +#define CODE_VERSION "0.1.3" +#include <pcmcia-opts.h> +#include <ipxe/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/qemu/roms/ipxe/src/core/pending.c b/qemu/roms/ipxe/src/core/pending.c new file mode 100644 index 000000000..7bb0c2e00 --- /dev/null +++ b/qemu/roms/ipxe/src/core/pending.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2012 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <errno.h> +#include <ipxe/process.h> +#include <ipxe/timer.h> +#include <ipxe/pending.h> + +/** @file + * + * Pending operations + * + */ + +/** Total count of pending operations */ +int pending_total; + +/** + * Mark an operation as pending + * + * @v pending Pending operation + */ +void pending_get ( struct pending_operation *pending ) { + + pending->count++; + pending_total++; + DBGC ( pending, "PENDING %p incremented to %d (total %d)\n", + pending, pending->count, pending_total ); +} + +/** + * Mark an operation as no longer pending + * + * @v pending Pending operation + */ +void pending_put ( struct pending_operation *pending ) { + + if ( pending->count ) { + pending_total--; + pending->count--; + DBGC ( pending, "PENDING %p decremented to %d (total %d)\n", + pending, pending->count, pending_total ); + } +} diff --git a/qemu/roms/ipxe/src/core/pinger.c b/qemu/roms/ipxe/src/core/pinger.c new file mode 100644 index 000000000..31ea2ce1c --- /dev/null +++ b/qemu/roms/ipxe/src/core/pinger.c @@ -0,0 +1,351 @@ +/* + * Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ipxe/refcnt.h> +#include <ipxe/interface.h> +#include <ipxe/job.h> +#include <ipxe/xfer.h> +#include <ipxe/iobuf.h> +#include <ipxe/open.h> +#include <ipxe/socket.h> +#include <ipxe/retry.h> +#include <ipxe/pinger.h> + +/** @file + * + * ICMP ping sender + * + */ + +/* Disambiguate the various error causes */ +#define EPROTO_LEN __einfo_error ( EINFO_EPROTO_LEN ) +#define EINFO_EPROTO_LEN __einfo_uniqify ( EINFO_EPROTO, 0x01, \ + "Incorrect reply length" ) +#define EPROTO_DATA __einfo_error ( EINFO_EPROTO_DATA ) +#define EINFO_EPROTO_DATA __einfo_uniqify ( EINFO_EPROTO, 0x02, \ + "Incorrect reply data" ) +#define EPROTO_SEQ __einfo_error ( EINFO_EPROTO_SEQ ) +#define EINFO_EPROTO_SEQ __einfo_uniqify ( EINFO_EPROTO, 0x03, \ + "Delayed or out-of-sequence reply" ) + +/** A pinger */ +struct pinger { + /** Reference count */ + struct refcnt refcnt; + + /** Job control interface */ + struct interface job; + /** Data transfer interface */ + struct interface xfer; + + /** Timer */ + struct retry_timer timer; + /** Timeout */ + unsigned long timeout; + + /** Payload length */ + size_t len; + /** Current sequence number */ + uint16_t sequence; + /** Response for current sequence number is still pending */ + int pending; + /** Number of remaining expiry events (zero to continue indefinitely) */ + unsigned int remaining; + /** Return status */ + int rc; + + /** Callback function + * + * @v src Source socket address, or NULL + * @v sequence Sequence number + * @v len Payload length + * @v rc Status code + */ + void ( * callback ) ( struct sockaddr *src, unsigned int sequence, + size_t len, int rc ); +}; + +/** + * Generate payload + * + * @v pinger Pinger + * @v data Data buffer + */ +static void pinger_generate ( struct pinger *pinger, void *data ) { + uint8_t *bytes = data; + unsigned int i; + + /* Generate byte sequence */ + for ( i = 0 ; i < pinger->len ; i++ ) + bytes[i] = ( i & 0xff ); +} + +/** + * Verify payload + * + * @v pinger Pinger + * @v data Data buffer + * @ret rc Return status code + */ +static int pinger_verify ( struct pinger *pinger, const void *data ) { + const uint8_t *bytes = data; + unsigned int i; + + /* Check byte sequence */ + for ( i = 0 ; i < pinger->len ; i++ ) { + if ( bytes[i] != ( i & 0xff ) ) + return -EPROTO_DATA; + } + + return 0; +} + +/** + * Close pinger + * + * @v pinger Pinger + * @v rc Reason for close + */ +static void pinger_close ( struct pinger *pinger, int rc ) { + + /* Stop timer */ + stop_timer ( &pinger->timer ); + + /* Shut down interfaces */ + intf_shutdown ( &pinger->xfer, rc ); + intf_shutdown ( &pinger->job, rc ); +} + +/** + * Handle data transfer window change + * + * @v pinger Pinger + */ +static void pinger_window_changed ( struct pinger *pinger ) { + + /* Do nothing if timer is already running */ + if ( timer_running ( &pinger->timer ) ) + return; + + /* Start timer when window opens for the first time */ + if ( xfer_window ( &pinger->xfer ) ) + start_timer_nodelay ( &pinger->timer ); +} + +/** + * Handle timer expiry + * + * @v timer Timer + * @v over Failure indicator + */ +static void pinger_expired ( struct retry_timer *timer, int over __unused ) { + struct pinger *pinger = container_of ( timer, struct pinger, timer ); + struct xfer_metadata meta; + struct io_buffer *iobuf; + int rc; + + /* If no response has been received, notify the callback function */ + if ( pinger->pending && pinger->callback ) + pinger->callback ( NULL, pinger->sequence, 0, -ETIMEDOUT ); + + /* Check for termination */ + if ( pinger->remaining && ( --pinger->remaining == 0 ) ) { + pinger_close ( pinger, pinger->rc ); + return; + } + + /* Increase sequence number */ + pinger->sequence++; + + /* Restart timer. Do this before attempting to transmit, in + * case the transmission attempt fails. + */ + start_timer_fixed ( &pinger->timer, pinger->timeout ); + pinger->pending = 1; + + /* Allocate I/O buffer */ + iobuf = xfer_alloc_iob ( &pinger->xfer, pinger->len ); + if ( ! iobuf ) { + DBGC ( pinger, "PINGER %p could not allocate I/O buffer\n", + pinger ); + return; + } + + /* Generate payload */ + pinger_generate ( pinger, iob_put ( iobuf, pinger->len ) ); + + /* Generate metadata */ + memset ( &meta, 0, sizeof ( meta ) ); + meta.flags = XFER_FL_ABS_OFFSET; + meta.offset = pinger->sequence; + + /* Transmit packet */ + if ( ( rc = xfer_deliver ( &pinger->xfer, iobuf, &meta ) ) != 0 ) { + DBGC ( pinger, "PINGER %p could not transmit: %s\n", + pinger, strerror ( rc ) ); + return; + } +} + +/** + * Handle received data + * + * @v pinger Pinger + * @v iobuf I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + */ +static int pinger_deliver ( struct pinger *pinger, struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + size_t len = iob_len ( iobuf ); + uint16_t sequence = meta->offset; + int terminate = 0; + int rc; + + /* Clear response pending flag, if applicable */ + if ( sequence == pinger->sequence ) + pinger->pending = 0; + + /* Check for errors */ + if ( len != pinger->len ) { + /* Incorrect length: terminate immediately if we are + * not pinging indefinitely. + */ + DBGC ( pinger, "PINGER %p received incorrect length %zd " + "(expected %zd)\n", pinger, len, pinger->len ); + rc = -EPROTO_LEN; + terminate = ( pinger->remaining != 0 ); + } else if ( ( rc = pinger_verify ( pinger, iobuf->data ) ) != 0 ) { + /* Incorrect data: terminate immediately if we are not + * pinging indefinitely. + */ + DBGC ( pinger, "PINGER %p received incorrect data:\n", pinger ); + DBGC_HDA ( pinger, 0, iobuf->data, iob_len ( iobuf ) ); + terminate = ( pinger->remaining != 0 ); + } else if ( sequence != pinger->sequence ) { + /* Incorrect sequence number (probably a delayed response): + * report via callback but otherwise ignore. + */ + DBGC ( pinger, "PINGER %p received sequence %d (expected %d)\n", + pinger, sequence, pinger->sequence ); + rc = -EPROTO_SEQ; + terminate = 0; + } else { + /* Success: record that a packet was successfully received, + * and terminate if we expect to send no further packets. + */ + rc = 0; + pinger->rc = 0; + terminate = ( pinger->remaining == 1 ); + } + + /* Discard I/O buffer */ + free_iob ( iobuf ); + + /* Notify callback function, if applicable */ + if ( pinger->callback ) + pinger->callback ( meta->src, sequence, len, rc ); + + /* Terminate if applicable */ + if ( terminate ) + pinger_close ( pinger, rc ); + + return rc; +} + +/** Pinger data transfer interface operations */ +static struct interface_operation pinger_xfer_op[] = { + INTF_OP ( xfer_deliver, struct pinger *, pinger_deliver ), + INTF_OP ( xfer_window_changed, struct pinger *, pinger_window_changed ), + INTF_OP ( intf_close, struct pinger *, pinger_close ), +}; + +/** Pinger data transfer interface descriptor */ +static struct interface_descriptor pinger_xfer_desc = + INTF_DESC ( struct pinger, xfer, pinger_xfer_op ); + +/** Pinger job control interface operations */ +static struct interface_operation pinger_job_op[] = { + INTF_OP ( intf_close, struct pinger *, pinger_close ), +}; + +/** Pinger job control interface descriptor */ +static struct interface_descriptor pinger_job_desc = + INTF_DESC ( struct pinger, job, pinger_job_op ); + +/** + * Create pinger + * + * @v job Job control interface + * @v hostname Hostname to ping + * @v timeout Timeout (in ticks) + * @v len Payload length + * @v count Number of packets to send (or zero for no limit) + * @v callback Callback function (or NULL) + * @ret rc Return status code + */ +int create_pinger ( struct interface *job, const char *hostname, + unsigned long timeout, size_t len, unsigned int count, + void ( * callback ) ( struct sockaddr *src, + unsigned int sequence, size_t len, + int rc ) ) { + struct pinger *pinger; + int rc; + + /* Sanity check */ + if ( ! timeout ) + return -EINVAL; + + /* Allocate and initialise structure */ + pinger = zalloc ( sizeof ( *pinger ) ); + if ( ! pinger ) + return -ENOMEM; + ref_init ( &pinger->refcnt, NULL ); + intf_init ( &pinger->job, &pinger_job_desc, &pinger->refcnt ); + intf_init ( &pinger->xfer, &pinger_xfer_desc, &pinger->refcnt ); + timer_init ( &pinger->timer, pinger_expired, &pinger->refcnt ); + pinger->timeout = timeout; + pinger->len = len; + pinger->remaining = ( count ? ( count + 1 /* Initial packet */ ) : 0 ); + pinger->callback = callback; + pinger->rc = -ETIMEDOUT; + + /* Open socket */ + if ( ( rc = xfer_open_named_socket ( &pinger->xfer, SOCK_ECHO, NULL, + hostname, NULL ) ) != 0 ) { + DBGC ( pinger, "PINGER %p could not open socket: %s\n", + pinger, strerror ( rc ) ); + goto err; + } + + /* Attach parent interface, mortalise self, and return */ + intf_plug_plug ( &pinger->job, job ); + ref_put ( &pinger->refcnt ); + return 0; + + err: + pinger_close ( pinger, rc ); + ref_put ( &pinger->refcnt ); + return rc; +} diff --git a/qemu/roms/ipxe/src/core/pixbuf.c b/qemu/roms/ipxe/src/core/pixbuf.c new file mode 100644 index 000000000..48f8e9f9a --- /dev/null +++ b/qemu/roms/ipxe/src/core/pixbuf.c @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** @file + * + * Pixel buffer + * + */ + +#include <stdlib.h> +#include <ipxe/umalloc.h> +#include <ipxe/pixbuf.h> + +/** + * Free pixel buffer + * + * @v refcnt Reference count + */ +static void free_pixbuf ( struct refcnt *refcnt ) { + struct pixel_buffer *pixbuf = + container_of ( refcnt, struct pixel_buffer, refcnt ); + + ufree ( pixbuf->data ); + free ( pixbuf ); +} + +/** + * Allocate pixel buffer + * + * @v width Width + * @h height Height + * @ret pixbuf Pixel buffer, or NULL on failure + */ +struct pixel_buffer * alloc_pixbuf ( unsigned int width, unsigned int height ) { + struct pixel_buffer *pixbuf; + + /* Allocate and initialise structure */ + pixbuf = zalloc ( sizeof ( *pixbuf ) ); + if ( ! pixbuf ) + goto err_alloc_pixbuf; + ref_init ( &pixbuf->refcnt, free_pixbuf ); + pixbuf->width = width; + pixbuf->height = height; + pixbuf->len = ( width * height * sizeof ( uint32_t ) ); + + /* Allocate pixel data buffer */ + pixbuf->data = umalloc ( pixbuf->len ); + if ( ! pixbuf->data ) + goto err_alloc_data; + + return pixbuf; + + err_alloc_data: + pixbuf_put ( pixbuf ); + err_alloc_pixbuf: + return NULL; +} diff --git a/qemu/roms/ipxe/src/core/posix_io.c b/qemu/roms/ipxe/src/core/posix_io.c new file mode 100644 index 000000000..8460d0f51 --- /dev/null +++ b/qemu/roms/ipxe/src/core/posix_io.c @@ -0,0 +1,339 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ipxe/list.h> +#include <ipxe/iobuf.h> +#include <ipxe/xfer.h> +#include <ipxe/open.h> +#include <ipxe/process.h> +#include <ipxe/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 iPXE 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 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 ) { + intf_shutdown ( &file->xfer, rc ); + file->rc = rc; +} + +/** + * Handle deliver_iob() event + * + * @v file POSIX file + * @v iobuf I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + */ +static int posix_file_xfer_deliver ( struct posix_file *file, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + + /* Keep track of file position solely for the filesize */ + if ( meta->flags & XFER_FL_ABS_OFFSET ) + 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 interface_operation posix_file_xfer_operations[] = { + INTF_OP ( xfer_deliver, struct posix_file *, posix_file_xfer_deliver ), + INTF_OP ( intf_close, struct posix_file *, posix_file_finished ), +}; + +/** POSIX file data transfer interface descriptor */ +static struct interface_descriptor posix_file_xfer_desc = + INTF_DESC ( struct posix_file, xfer, posix_file_xfer_operations ); + +/** + * 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; + ref_init ( &file->refcnt, posix_file_free ); + file->fd = fd; + file->rc = -EINPROGRESS; + intf_init ( &file->xfer, &posix_file_xfer_desc, &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/qemu/roms/ipxe/src/core/process.c b/qemu/roms/ipxe/src/core/process.c new file mode 100644 index 000000000..d341a2c37 --- /dev/null +++ b/qemu/roms/ipxe/src/core/process.c @@ -0,0 +1,133 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <ipxe/list.h> +#include <ipxe/init.h> +#include <ipxe/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 ); + +/** + * Get pointer to object containing process + * + * @v process Process + * @ret object Containing object + */ +void * process_object ( struct process *process ) { + return ( ( ( void * ) process ) - process->desc->offset ); +} + +/** + * Add process to process list + * + * @v process Process + * + * It is safe to call process_add() multiple times; further calls will + * have no effect. + */ +void process_add ( struct process *process ) { + if ( ! process_running ( process ) ) { + DBGC ( PROC_COL ( process ), "PROCESS " PROC_FMT + " starting\n", PROC_DBG ( process ) ); + ref_get ( process->refcnt ); + list_add_tail ( &process->list, &run_queue ); + } else { + DBGC ( PROC_COL ( process ), "PROCESS " PROC_FMT + " already started\n", PROC_DBG ( process ) ); + } +} + +/** + * 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 ( process_running ( process ) ) { + DBGC ( PROC_COL ( process ), "PROCESS " PROC_FMT + " stopping\n", PROC_DBG ( process ) ); + list_del ( &process->list ); + INIT_LIST_HEAD ( &process->list ); + ref_put ( process->refcnt ); + } else { + DBGC ( PROC_COL ( process ), "PROCESS " PROC_FMT + " already stopped\n", PROC_DBG ( 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; + struct process_descriptor *desc; + void *object; + + if ( ( process = list_first_entry ( &run_queue, struct process, + list ) ) ) { + ref_get ( process->refcnt ); /* Inhibit destruction mid-step */ + desc = process->desc; + object = process_object ( process ); + if ( desc->reschedule ) { + list_del ( &process->list ); + list_add_tail ( &process->list, &run_queue ); + } else { + process_del ( process ); + } + DBGC2 ( PROC_COL ( process ), "PROCESS " PROC_FMT + " executing\n", PROC_DBG ( process ) ); + desc->step ( object ); + DBGC2 ( PROC_COL ( process ), "PROCESS " PROC_FMT + " finished executing\n", PROC_DBG ( process ) ); + ref_put ( process->refcnt ); /* Allow destruction */ + } +} + +/** + * Initialise processes + * + */ +static void init_processes ( void ) { + struct process *process; + + for_each_table_entry ( process, PERMANENT_PROCESSES ) + process_add ( process ); +} + +/** Process initialiser */ +struct init_fn process_init_fn __init_fn ( INIT_NORMAL ) = { + .initialise = init_processes, +}; diff --git a/qemu/roms/ipxe/src/core/profile.c b/qemu/roms/ipxe/src/core/profile.c new file mode 100644 index 000000000..150e6b273 --- /dev/null +++ b/qemu/roms/ipxe/src/core/profile.c @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2014 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdint.h> +#include <stdio.h> +#include <strings.h> +#include <assert.h> +#include <ipxe/isqrt.h> +#include <ipxe/profile.h> + +/** @file + * + * Profiling + * + * The profiler computes basic statistics (mean, variance, and + * standard deviation) for the samples which it records. Note that + * these statistics need not be completely accurate; it is sufficient + * to give a rough approximation. + * + * The algorithm for updating the mean and variance estimators is from + * The Art of Computer Programming (via Wikipedia), with adjustments + * to avoid the use of floating-point instructions. + */ + +/** Accumulated time excluded from profiling */ +unsigned long profile_excluded; + +/** + * Format a hex fraction (for debugging) + * + * @v value Value + * @v shift Bit shift + * @ret string Formatted hex fraction + */ +static const char * profile_hex_fraction ( signed long long value, + unsigned int shift ) { + static char buf[23] = "-"; /* -0xXXXXXXXXXXXXXXXX.XX + NUL */ + unsigned long long int_part; + uint8_t frac_part; + char *ptr; + + if ( value < 0 ) { + value = -value; + ptr = &buf[0]; + } else { + ptr = &buf[1]; + } + int_part = ( value >> shift ); + frac_part = ( value >> ( shift - ( 8 * sizeof ( frac_part ) ) ) ); + snprintf ( &buf[1], ( sizeof ( buf ) - 1 ), "%#llx.%02x", + int_part, frac_part ); + return ptr; +} + +/** + * Calculate bit shift for mean sample value + * + * @v profiler Profiler + * @ret shift Bit shift + */ +static inline unsigned int profile_mean_shift ( struct profiler *profiler ) { + + return ( ( ( 8 * sizeof ( profiler->mean ) ) - 1 ) /* MSB */ + - 1 /* Leave sign bit unused */ + - profiler->mean_msb ); +} + +/** + * Calculate bit shift for accumulated variance value + * + * @v profiler Profiler + * @ret shift Bit shift + */ +static inline unsigned int profile_accvar_shift ( struct profiler *profiler ) { + + return ( ( ( 8 * sizeof ( profiler->accvar ) ) - 1 ) /* MSB */ + - 1 /* Leave top bit unused */ + - profiler->accvar_msb ); +} + +/** + * Update profiler with a new sample + * + * @v profiler Profiler + * @v sample Sample value + */ +void profile_update ( struct profiler *profiler, unsigned long sample ) { + unsigned int sample_msb; + unsigned int mean_shift; + unsigned int delta_shift; + signed long pre_delta; + signed long post_delta; + signed long long accvar_delta; + unsigned int accvar_delta_shift; + unsigned int accvar_delta_msb; + unsigned int accvar_shift; + + /* Our scaling logic assumes that sample values never overflow + * a signed long (i.e. that the high bit is always zero). + */ + assert ( ( ( signed ) sample ) >= 0 ); + + /* Update sample count */ + profiler->count++; + + /* Adjust mean sample value scale if necessary. Skip if + * sample is zero (in which case flsl(sample)-1 would + * underflow): in the case of a zero sample we have no need to + * adjust the scale anyway. + */ + if ( sample ) { + sample_msb = ( flsl ( sample ) - 1 ); + if ( profiler->mean_msb < sample_msb ) { + profiler->mean >>= ( sample_msb - profiler->mean_msb ); + profiler->mean_msb = sample_msb; + } + } + + /* Scale sample to internal units */ + mean_shift = profile_mean_shift ( profiler ); + sample <<= mean_shift; + + /* Update mean */ + pre_delta = ( sample - profiler->mean ); + profiler->mean += ( pre_delta / ( ( signed ) profiler->count ) ); + post_delta = ( sample - profiler->mean ); + delta_shift = mean_shift; + DBGC ( profiler, "PROFILER %p sample %#lx mean %s", profiler, + ( sample >> mean_shift ), + profile_hex_fraction ( profiler->mean, mean_shift ) ); + DBGC ( profiler, " pre %s", + profile_hex_fraction ( pre_delta, delta_shift ) ); + DBGC ( profiler, " post %s\n", + profile_hex_fraction ( post_delta, delta_shift ) ); + + /* Scale both deltas to fit in half of an unsigned long long + * to avoid potential overflow on multiplication. Note that + * shifting a signed quantity is "implementation-defined" + * behaviour in the C standard, but gcc documents that it will + * always perform sign extension. + */ + if ( sizeof ( pre_delta ) > ( sizeof ( accvar_delta ) / 2 ) ) { + unsigned int shift = ( 8 * ( sizeof ( pre_delta ) - + ( sizeof ( accvar_delta ) / 2 ) )); + pre_delta >>= shift; + post_delta >>= shift; + delta_shift -= shift; + } + + /* Update variance, if applicable. Skip if either delta is + * zero (in which case flsl(delta)-1 would underflow): in the + * case of a zero delta there is no change to the accumulated + * variance anyway. + */ + if ( pre_delta && post_delta ) { + + /* Calculate variance delta */ + accvar_delta = ( ( ( signed long long ) pre_delta ) * + ( ( signed long long ) post_delta ) ); + accvar_delta_shift = ( 2 * delta_shift ); + assert ( accvar_delta > 0 ); + + /* Calculate variance delta MSB, using flsl() on each + * delta individually to provide an upper bound rather + * than requiring the existence of flsll(). + */ + accvar_delta_msb = ( flsll ( accvar_delta ) - 1 ); + if ( accvar_delta_msb > accvar_delta_shift ) { + accvar_delta_msb -= accvar_delta_shift; + } else { + accvar_delta_msb = 0; + } + + /* Adjust scales as necessary */ + if ( profiler->accvar_msb < accvar_delta_msb ) { + /* Rescale accumulated variance */ + profiler->accvar >>= ( accvar_delta_msb - + profiler->accvar_msb ); + profiler->accvar_msb = accvar_delta_msb; + } else { + /* Rescale variance delta */ + accvar_delta >>= ( profiler->accvar_msb - + accvar_delta_msb ); + accvar_delta_shift -= ( profiler->accvar_msb - + accvar_delta_msb ); + } + + /* Scale delta to internal units */ + accvar_shift = profile_accvar_shift ( profiler ); + accvar_delta <<= ( accvar_shift - accvar_delta_shift ); + + /* Accumulate variance */ + profiler->accvar += accvar_delta; + + /* Adjust scale if necessary */ + if ( profiler->accvar & + ( 1ULL << ( ( 8 * sizeof ( profiler->accvar ) ) - 1 ) ) ) { + profiler->accvar >>= 1; + profiler->accvar_msb++; + accvar_delta >>= 1; + accvar_shift--; + } + + DBGC ( profiler, "PROFILER %p accvar %s", profiler, + profile_hex_fraction ( profiler->accvar, accvar_shift )); + DBGC ( profiler, " delta %s\n", + profile_hex_fraction ( accvar_delta, accvar_shift ) ); + } +} + +/** + * Get mean sample value + * + * @v profiler Profiler + * @ret mean Mean sample value + */ +unsigned long profile_mean ( struct profiler *profiler ) { + unsigned int mean_shift = profile_mean_shift ( profiler ); + + /* Round to nearest and scale down to original units */ + return ( ( profiler->mean + ( 1UL << ( mean_shift - 1 ) ) ) + >> mean_shift ); +} + +/** + * Get sample variance + * + * @v profiler Profiler + * @ret variance Sample variance + */ +unsigned long profile_variance ( struct profiler *profiler ) { + unsigned int accvar_shift = profile_accvar_shift ( profiler ); + + /* Variance is zero if fewer than two samples exist (avoiding + * division by zero error). + */ + if ( profiler->count < 2 ) + return 0; + + /* Calculate variance, round to nearest, and scale to original units */ + return ( ( ( profiler->accvar / ( profiler->count - 1 ) ) + + ( 1ULL << ( accvar_shift - 1 ) ) ) >> accvar_shift ); +} + +/** + * Get sample standard deviation + * + * @v profiler Profiler + * @ret stddev Sample standard deviation + */ +unsigned long profile_stddev ( struct profiler *profiler ) { + + return isqrt ( profile_variance ( profiler ) ); +} diff --git a/qemu/roms/ipxe/src/core/random.c b/qemu/roms/ipxe/src/core/random.c new file mode 100644 index 000000000..8824dca3a --- /dev/null +++ b/qemu/roms/ipxe/src/core/random.c @@ -0,0 +1,41 @@ +/** @file + * + * Random number generation + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdlib.h> +#include <ipxe/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/qemu/roms/ipxe/src/core/refcnt.c b/qemu/roms/ipxe/src/core/refcnt.c new file mode 100644 index 000000000..68a86120e --- /dev/null +++ b/qemu/roms/ipxe/src/core/refcnt.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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdlib.h> +#include <ipxe/refcnt.h> + +/** @file + * + * Reference counting + * + */ + +/** + * Increment reference count + * + * @v refcnt Reference counter, or NULL + * + * If @c refcnt is NULL, no action is taken. + */ +void ref_increment ( struct refcnt *refcnt ) { + + if ( refcnt ) { + refcnt->count++; + DBGC2 ( refcnt, "REFCNT %p incremented to %d\n", + refcnt, refcnt->count ); + } +} + +/** + * 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_decrement ( struct refcnt *refcnt ) { + + if ( ! refcnt ) + return; + + refcnt->count--; + DBGC2 ( refcnt, "REFCNT %p decremented to %d\n", + refcnt, refcnt->count ); + + if ( refcnt->count >= 0 ) + return; + + if ( refcnt->count < -1 ) { + DBGC ( refcnt, "REFCNT %p decremented too far (%d)!\n", + refcnt, refcnt->count ); + /* Avoid multiple calls to free(), which typically + * result in memory corruption that is very hard to + * track down. + */ + 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 ); + } +} + +/** + * Do not free reference-counted object + * + * @v refcnt Reference counter + * + * This is meant for initializing a reference counter structure in a + * statically allocated object. + */ +void ref_no_free ( struct refcnt *refcnt __unused ) { + /* Do nothing */ +} diff --git a/qemu/roms/ipxe/src/core/resolv.c b/qemu/roms/ipxe/src/core/resolv.c new file mode 100644 index 000000000..d59a8c0ad --- /dev/null +++ b/qemu/roms/ipxe/src/core/resolv.c @@ -0,0 +1,423 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ipxe/xfer.h> +#include <ipxe/open.h> +#include <ipxe/process.h> +#include <ipxe/socket.h> +#include <ipxe/resolv.h> + +/** @file + * + * Name resolution + * + */ + +/*************************************************************************** + * + * Name resolution interfaces + * + *************************************************************************** + */ + +/** + * Name resolved + * + * @v intf Object interface + * @v sa Completed socket address (if successful) + */ +void resolv_done ( struct interface *intf, struct sockaddr *sa ) { + struct interface *dest; + resolv_done_TYPE ( void * ) *op = + intf_get_dest_op ( intf, resolv_done, &dest ); + void *object = intf_object ( dest ); + + DBGC ( INTF_COL ( intf ), "INTF " INTF_INTF_FMT " resolv_done\n", + INTF_INTF_DBG ( intf, dest ) ); + + if ( op ) { + op ( object, sa ); + } else { + /* Default is to ignore resolutions */ + } + + intf_put ( dest ); +} + +/*************************************************************************** + * + * Numeric name resolver + * + *************************************************************************** + */ + +/** A numeric name resolver */ +struct numeric_resolv { + /** Reference counter */ + struct refcnt refcnt; + /** Name resolution interface */ + struct interface resolv; + /** Process */ + struct process process; + /** Completed socket address */ + struct sockaddr sa; + /** Overall status code */ + int rc; +}; + +static void numeric_step ( struct numeric_resolv *numeric ) { + + if ( numeric->rc == 0 ) + resolv_done ( &numeric->resolv, &numeric->sa ); + intf_shutdown ( &numeric->resolv, numeric->rc ); +} + +static struct process_descriptor numeric_process_desc = + PROC_DESC_ONCE ( struct numeric_resolv, process, numeric_step ); + +static int numeric_resolv ( struct interface *resolv, + const char *name, struct sockaddr *sa ) { + struct numeric_resolv *numeric; + + /* Allocate and initialise structure */ + numeric = zalloc ( sizeof ( *numeric ) ); + if ( ! numeric ) + return -ENOMEM; + ref_init ( &numeric->refcnt, NULL ); + intf_init ( &numeric->resolv, &null_intf_desc, &numeric->refcnt ); + process_init ( &numeric->process, &numeric_process_desc, + &numeric->refcnt ); + memcpy ( &numeric->sa, sa, sizeof ( numeric->sa ) ); + + /* Attempt to resolve name */ + numeric->rc = sock_aton ( name, &numeric->sa ); + + /* Attach to parent interface, mortalise self, and return */ + intf_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 + * + *************************************************************************** + */ + +/** A name resolution multiplexer */ +struct resolv_mux { + /** Reference counter */ + struct refcnt refcnt; + /** Parent name resolution interface */ + struct interface parent; + + /** Child name resolution interface */ + struct 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 resmux_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; +} + +/** + * Child resolved name + * + * @v mux Name resolution multiplexer + * @v sa Completed socket address + */ +static void resmux_child_resolv_done ( struct resolv_mux *mux, + struct sockaddr *sa ) { + + DBGC ( mux, "RESOLV %p resolved \"%s\" to %s using method %s\n", + mux, mux->name, sock_ntoa ( sa ), mux->resolver->name ); + + /* Pass resolution to parent */ + resolv_done ( &mux->parent, sa ); +} + +/** + * Child finished resolution + * + * @v mux Name resolution multiplexer + * @v rc Return status code + */ +static void resmux_child_close ( struct resolv_mux *mux, int rc ) { + + /* Restart child interface */ + intf_restart ( &mux->child, rc ); + + /* 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 >= table_end ( RESOLVERS ) ) { + DBGC ( mux, "RESOLV %p failed to resolve name\n", mux ); + goto finished; + } + if ( ( rc = resmux_try ( mux ) ) != 0 ) + goto finished; + + /* Next resolver is now running */ + return; + + finished: + intf_shutdown ( &mux->parent, rc ); +} + +/** Name resolution multiplexer child interface operations */ +static struct interface_operation resmux_child_op[] = { + INTF_OP ( resolv_done, struct resolv_mux *, resmux_child_resolv_done ), + INTF_OP ( intf_close, struct resolv_mux *, resmux_child_close ), +}; + +/** Name resolution multiplexer child interface descriptor */ +static struct interface_descriptor resmux_child_desc = + INTF_DESC ( struct resolv_mux, child, resmux_child_op ); + +/** + * 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 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; + ref_init ( &mux->refcnt, NULL ); + intf_init ( &mux->parent, &null_intf_desc, &mux->refcnt ); + intf_init ( &mux->child, &resmux_child_desc, &mux->refcnt ); + mux->resolver = table_start ( RESOLVERS ); + if ( sa ) + 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 = resmux_try ( mux ) ) != 0 ) + goto err; + + /* Attach parent interface, mortalise self, and return */ + intf_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 interface xfer; + /** Name resolution interface */ + struct 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; +}; + +/** + * Terminate named socket opener + * + * @v named Named socket + * @v rc Reason for termination + */ +static void named_close ( struct named_socket *named, int rc ) { + /* Shut down interfaces */ + intf_shutdown ( &named->resolv, rc ); + intf_shutdown ( &named->xfer, rc ); +} + +/** + * Check flow control window + * + * @v named Named socket + * @ret len Length of window + */ +static size_t named_window ( struct named_socket *named __unused ) { + /* Not ready for data until we have redirected away */ + return 0; +} + +/** Named socket opener data transfer interface operations */ +static struct interface_operation named_xfer_ops[] = { + INTF_OP ( xfer_window, struct named_socket *, named_window ), + INTF_OP ( intf_close, struct named_socket *, named_close ), +}; + +/** Named socket opener data transfer interface descriptor */ +static struct interface_descriptor named_xfer_desc = + INTF_DESC ( struct named_socket, xfer, named_xfer_ops ); + +/** + * Name resolved + * + * @v named Named socket + * @v sa Completed socket address + */ +static void named_resolv_done ( struct named_socket *named, + struct sockaddr *sa ) { + int rc; + + /* Nullify data transfer interface */ + intf_nullify ( &named->xfer ); + + /* Redirect data-xfer interface */ + if ( ( rc = xfer_redirect ( &named->xfer, LOCATION_SOCKET, + named->semantics, sa, + ( named->have_local ? + &named->local : NULL ) ) ) != 0 ) { + /* Redirection failed - do not unplug data-xfer interface */ + DBGC ( named, "NAMED %p could not redirect: %s\n", + named, strerror ( rc ) ); + } else { + /* Redirection succeeded - unplug data-xfer interface */ + DBGC ( named, "NAMED %p redirected successfully\n", named ); + intf_unplug ( &named->xfer ); + } + + /* Terminate named socket opener */ + named_close ( named, rc ); +} + +/** Named socket opener resolver interface operations */ +static struct interface_operation named_resolv_op[] = { + INTF_OP ( intf_close, struct named_socket *, named_close ), + INTF_OP ( resolv_done, struct named_socket *, named_resolv_done ), +}; + +/** Named socket opener resolver interface descriptor */ +static struct interface_descriptor named_resolv_desc = + INTF_DESC ( struct named_socket, resolv, named_resolv_op ); + +/** + * 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 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; + ref_init ( &named->refcnt, NULL ); + intf_init ( &named->xfer, &named_xfer_desc, &named->refcnt ); + intf_init ( &named->resolv, &named_resolv_desc, &named->refcnt ); + named->semantics = semantics; + if ( local ) { + memcpy ( &named->local, local, sizeof ( named->local ) ); + named->have_local = 1; + } + + DBGC ( named, "NAMED %p opening \"%s\"\n", + named, name ); + + /* Start name resolution */ + if ( ( rc = resolv ( &named->resolv, name, peer ) ) != 0 ) + goto err; + + /* Attach parent interface, mortalise self, and return */ + intf_plug_plug ( &named->xfer, xfer ); + ref_put ( &named->refcnt ); + return 0; + + err: + ref_put ( &named->refcnt ); + return rc; +} diff --git a/qemu/roms/ipxe/src/core/serial.c b/qemu/roms/ipxe/src/core/serial.c new file mode 100644 index 000000000..7e4460ab9 --- /dev/null +++ b/qemu/roms/ipxe/src/core/serial.c @@ -0,0 +1,259 @@ +/* + * 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(). + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include "stddef.h" +#include <ipxe/init.h> +#include <ipxe/io.h> +#include <unistd.h> +#include <ipxe/serial.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 + +/* Boolean for the state of serial driver initialization */ +int serial_initialized = 0; + +/* + * void serial_putc(int ch); + * Write character `ch' to port UART_BASE. + */ +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. + */ +int serial_getc ( void ) { + int status; + int ch; + do { + status = uart_readb(UART_BASE + UART_LSR); + } while((status & 1) == 0); + ch = uart_readb(UART_BASE + UART_RBR); /* fetch (first) character */ + ch &= 0x7f; /* remove any parity bits we get */ + if (ch == 0x7f) { /* Make DEL... look like BS */ + ch = 0x08; + } + return ch; +} + +/* + * int serial_ischar(void); + * If there is a character in the input buffer of port UART_BASE, + * return nonzero; otherwise return 0. + */ +int serial_ischar ( void ) { + int status; + 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); + + /* enable fifos */ + uart_writeb(0x01, 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); + + /* Note that serial support has been initialized */ + serial_initialized = 1; + 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 ( int flags __unused ) { + int i, status; + /* 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 */ +} + +/** + * Serial driver initialisation function + * + * Initialise serial port early on so that it is available to capture + * early debug messages. + */ +struct init_fn serial_init_fn __init_fn ( INIT_SERIAL ) = { + .initialise = serial_init, +}; + +/** Serial driver startup function */ +struct startup_fn serial_startup_fn __startup_fn ( STARTUP_EARLY ) = { + .shutdown = serial_fini, +}; diff --git a/qemu/roms/ipxe/src/core/serial_console.c b/qemu/roms/ipxe/src/core/serial_console.c new file mode 100644 index 000000000..de9b84ca7 --- /dev/null +++ b/qemu/roms/ipxe/src/core/serial_console.c @@ -0,0 +1,42 @@ +#include <ipxe/init.h> +#include <ipxe/serial.h> +#include <ipxe/console.h> +#include <config/console.h> + +/** @file + * + * Serial console + * + */ + +/* Set default console usage if applicable */ +#if ! ( defined ( CONSOLE_SERIAL ) && CONSOLE_EXPLICIT ( CONSOLE_SERIAL ) ) +#undef CONSOLE_SERIAL +#define CONSOLE_SERIAL ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG ) +#endif + +struct console_driver serial_console __console_driver; + +static void serial_console_init ( void ) { + /* + * Check if serial driver initialization is done. + * If so, it's time to enable the serial console. + */ + if ( serial_initialized ) + serial_console.disabled = 0; +} + +struct console_driver serial_console __console_driver = { + .putchar = serial_putc, + .getchar = serial_getc, + .iskey = serial_ischar, + .disabled = CONSOLE_DISABLED, + .usage = CONSOLE_SERIAL, +}; + +/** + * Serial console initialisation function + */ +struct init_fn serial_console_init_fn __init_fn ( INIT_CONSOLE ) = { + .initialise = serial_console_init, +}; diff --git a/qemu/roms/ipxe/src/core/settings.c b/qemu/roms/ipxe/src/core/settings.c new file mode 100644 index 000000000..5e16b27d0 --- /dev/null +++ b/qemu/roms/ipxe/src/core/settings.c @@ -0,0 +1,2556 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#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 <ipxe/in.h> +#include <ipxe/ip.h> +#include <ipxe/ipv6.h> +#include <ipxe/vsprintf.h> +#include <ipxe/dhcp.h> +#include <ipxe/uuid.h> +#include <ipxe/uri.h> +#include <ipxe/base16.h> +#include <ipxe/pci.h> +#include <ipxe/init.h> +#include <ipxe/version.h> +#include <ipxe/settings.h> + +/** @file + * + * Configuration settings + * + */ + +/****************************************************************************** + * + * Generic settings blocks + * + ****************************************************************************** + */ + +/** + * A generic setting + * + */ +struct generic_setting { + /** List of generic settings */ + struct list_head list; + /** Setting */ + struct setting setting; + /** Size of setting name */ + size_t name_len; + /** Size of setting data */ + size_t data_len; +}; + +/** + * Get generic setting name + * + * @v generic Generic setting + * @ret name Generic setting name + */ +static inline void * generic_setting_name ( struct generic_setting *generic ) { + return ( ( ( void * ) generic ) + sizeof ( *generic ) ); +} + +/** + * Get generic setting data + * + * @v generic Generic setting + * @ret data Generic setting data + */ +static inline void * generic_setting_data ( struct generic_setting *generic ) { + return ( ( ( void * ) generic ) + sizeof ( *generic ) + + generic->name_len ); +} + +/** + * Find generic setting + * + * @v generics Generic settings block + * @v setting Setting to find + * @ret generic Generic setting, or NULL + */ +static struct generic_setting * +find_generic_setting ( struct generic_settings *generics, + const struct setting *setting ) { + struct generic_setting *generic; + + list_for_each_entry ( generic, &generics->list, list ) { + if ( setting_cmp ( &generic->setting, setting ) == 0 ) + return generic; + } + return NULL; +} + +/** + * Store value of generic 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 generic_settings_store ( struct settings *settings, + const struct setting *setting, + const void *data, size_t len ) { + struct generic_settings *generics = + container_of ( settings, struct generic_settings, settings ); + struct generic_setting *old; + struct generic_setting *new = NULL; + size_t name_len; + + /* Identify existing generic setting, if any */ + old = find_generic_setting ( generics, setting ); + + /* Create new generic setting, if required */ + if ( len ) { + /* Allocate new generic setting */ + name_len = ( strlen ( setting->name ) + 1 ); + new = zalloc ( sizeof ( *new ) + name_len + len ); + if ( ! new ) + return -ENOMEM; + + /* Populate new generic setting */ + new->name_len = name_len; + new->data_len = len; + memcpy ( &new->setting, setting, sizeof ( new->setting ) ); + new->setting.name = generic_setting_name ( new ); + memcpy ( generic_setting_name ( new ), + setting->name, name_len ); + memcpy ( generic_setting_data ( new ), data, len ); + } + + /* Delete existing generic setting, if any */ + if ( old ) { + list_del ( &old->list ); + free ( old ); + } + + /* Add new setting to list, if any */ + if ( new ) + list_add ( &new->list, &generics->list ); + + return 0; +} + +/** + * Fetch value of generic 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 + */ +int generic_settings_fetch ( struct settings *settings, + struct setting *setting, + void *data, size_t len ) { + struct generic_settings *generics = + container_of ( settings, struct generic_settings, settings ); + struct generic_setting *generic; + + /* Find generic setting */ + generic = find_generic_setting ( generics, setting ); + if ( ! generic ) + return -ENOENT; + + /* Copy out generic setting data */ + if ( len > generic->data_len ) + len = generic->data_len; + memcpy ( data, generic_setting_data ( generic ), len ); + + /* Set setting type, if not yet specified */ + if ( ! setting->type ) + setting->type = generic->setting.type; + + return generic->data_len; +} + +/** + * Clear generic settings block + * + * @v settings Settings block + */ +void generic_settings_clear ( struct settings *settings ) { + struct generic_settings *generics = + container_of ( settings, struct generic_settings, settings ); + struct generic_setting *generic; + struct generic_setting *tmp; + + list_for_each_entry_safe ( generic, tmp, &generics->list, list ) { + list_del ( &generic->list ); + free ( generic ); + } + assert ( list_empty ( &generics->list ) ); +} + +/** Generic settings operations */ +struct settings_operations generic_settings_operations = { + .store = generic_settings_store, + .fetch = generic_settings_fetch, + .clear = generic_settings_clear, +}; + +/****************************************************************************** + * + * Registered settings blocks + * + ****************************************************************************** + */ + +/** Root generic settings block */ +struct generic_settings generic_settings_root = { + .settings = { + .refcnt = NULL, + .name = "", + .siblings = + LIST_HEAD_INIT ( generic_settings_root.settings.siblings ), + .children = + LIST_HEAD_INIT ( generic_settings_root.settings.children ), + .op = &generic_settings_operations, + }, + .list = LIST_HEAD_INIT ( generic_settings_root.list ), +}; + +/** Root settings block */ +#define settings_root generic_settings_root.settings + +/** Autovivified settings block */ +struct autovivified_settings { + /** Reference count */ + struct refcnt refcnt; + /** Generic settings block */ + struct generic_settings generic; +}; + +/** + * Free autovivified settings block + * + * @v refcnt Reference count + */ +static void autovivified_settings_free ( struct refcnt *refcnt ) { + struct autovivified_settings *autovivified = + container_of ( refcnt, struct autovivified_settings, refcnt ); + + generic_settings_clear ( &autovivified->generic.settings ); + free ( autovivified ); +} + +/** + * Find child 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; + + /* Find target parent settings block */ + parent = settings_target ( parent ); + + /* Treat empty name as meaning "this block" */ + if ( ! *name ) + return parent; + + /* Look for child with matching name */ + list_for_each_entry ( settings, &parent->children, siblings ) { + if ( strcmp ( settings->name, name ) == 0 ) + return settings_target ( settings ); + } + + return NULL; +} + +/** + * Find or create child settings block + * + * @v parent Parent settings block + * @v name Name within this parent + * @ret settings Settings block, or NULL + */ +struct settings * autovivify_child_settings ( struct settings *parent, + const char *name ) { + struct { + struct autovivified_settings autovivified; + char name[ strlen ( name ) + 1 /* NUL */ ]; + } *new_child; + struct settings *settings; + + /* Find target parent settings block */ + parent = settings_target ( parent ); + + /* Return existing settings, if existent */ + if ( ( settings = find_child_settings ( parent, name ) ) != NULL ) + return settings; + + /* Create new generic settings block */ + new_child = zalloc ( sizeof ( *new_child ) ); + if ( ! new_child ) { + DBGC ( parent, "Settings %p could not create child %s\n", + parent, name ); + return NULL; + } + memcpy ( new_child->name, name, sizeof ( new_child->name ) ); + ref_init ( &new_child->autovivified.refcnt, + autovivified_settings_free ); + generic_settings_init ( &new_child->autovivified.generic, + &new_child->autovivified.refcnt ); + settings = &new_child->autovivified.generic.settings; + register_settings ( settings, parent, new_child->name ); + return settings; +} + +/** + * Return settings block name + * + * @v settings Settings block + * @ret name Settings block name + */ +const char * settings_name ( struct settings *settings ) { + static char buf[16]; + char tmp[ sizeof ( buf ) ]; + + /* Find target settings block */ + settings = settings_target ( settings ); + + /* Construct name */ + for ( buf[2] = buf[0] = 0 ; settings ; settings = settings->parent ) { + memcpy ( tmp, buf, sizeof ( tmp ) ); + snprintf ( buf, sizeof ( buf ), ".%s%s", settings->name, tmp ); + } + return ( buf + 2 ); +} + +/** + * Parse settings block name + * + * @v name Name + * @v get_child Function to find or create child settings block + * @ret settings Settings block, or NULL + */ +static struct settings * +parse_settings_name ( const char *name, get_child_settings_t get_child ) { + struct settings *settings = &settings_root; + char name_copy[ strlen ( name ) + 1 ]; + char *subname; + char *remainder; + + /* Create modifiable copy of name */ + memcpy ( name_copy, name, sizeof ( name_copy ) ); + remainder = name_copy; + + /* Parse each name component in turn */ + while ( remainder ) { + subname = remainder; + remainder = strchr ( subname, '.' ); + if ( remainder ) + *(remainder++) = '\0'; + settings = get_child ( settings, subname ); + if ( ! settings ) + break; + } + + return settings; +} + +/** + * Find settings block + * + * @v name Name + * @ret settings Settings block, or NULL + */ +struct settings * find_settings ( const char *name ) { + + return parse_settings_name ( name, find_child_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_each_table_entry ( applicator, SETTINGS_APPLICATORS ) { + 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 + * @v name Settings block name + * @ret rc Return status code + */ +int register_settings ( struct settings *settings, struct settings *parent, + const char *name ) { + struct settings *old_settings; + + /* Sanity check */ + assert ( settings != NULL ); + + /* Find target parent settings block */ + parent = settings_target ( parent ); + + /* Apply settings block name */ + settings->name = name; + + /* Remove any existing settings with the same name */ + if ( ( old_settings = find_child_settings ( parent, settings->name ) )) + unregister_settings ( old_settings ); + + /* 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 (\"%s\") registered\n", + settings, settings_name ( 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 ) { + struct settings *child; + struct settings *tmp; + + /* Unregister child settings */ + list_for_each_entry_safe ( child, tmp, &settings->children, siblings ) { + unregister_settings ( child ); + } + + DBGC ( settings, "Settings %p (\"%s\") unregistered\n", + settings, settings_name ( settings ) ); + + /* Remove from list of settings */ + ref_put ( settings->parent->refcnt ); + settings->parent = NULL; + list_del ( &settings->siblings ); + ref_put ( settings->refcnt ); + + /* Apply potentially-updated settings */ + apply_settings(); +} + +/****************************************************************************** + * + * Core settings routines + * + ****************************************************************************** + */ + +/** + * Redirect to target settings block + * + * @v settings Settings block, or NULL + * @ret settings Underlying settings block + */ +struct settings * settings_target ( struct settings *settings ) { + + /* NULL settings implies the global settings root */ + if ( ! settings ) + settings = &settings_root; + + /* Redirect to underlying settings block, if applicable */ + if ( settings->op->redirect ) + return settings->op->redirect ( settings ); + + /* Otherwise, return this settings block */ + return settings; +} + +/** + * Check applicability of setting + * + * @v settings Settings block + * @v setting Setting + * @ret applies Setting applies within this settings block + */ +int setting_applies ( struct settings *settings, + const struct setting *setting ) { + + /* Find target settings block */ + settings = settings_target ( settings ); + + /* Check applicability of setting */ + return ( settings->op->applies ? + settings->op->applies ( settings, setting ) : 1 ); +} + +/** + * Find setting applicable to settings block, if any + * + * @v settings Settings block + * @v setting Setting + * @ret setting Applicable setting, if any + */ +static const struct setting * +applicable_setting ( struct settings *settings, const struct setting *setting ){ + const struct setting *applicable; + + /* If setting is already applicable, use it */ + if ( setting_applies ( settings, setting ) ) + return setting; + + /* Otherwise, look for a matching predefined setting which does apply */ + for_each_table_entry ( applicable, SETTINGS ) { + if ( ( setting_cmp ( setting, applicable ) == 0 ) && + ( setting_applies ( settings, applicable ) ) ) + return applicable; + } + + return NULL; +} + +/** + * Store value of setting + * + * @v settings Settings block, or NULL + * @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, const struct setting *setting, + const void *data, size_t len ) { + int rc; + + /* Find target settings block */ + settings = settings_target ( settings ); + + /* Fail if setting does not apply to this settings block */ + if ( ! setting_applies ( settings, setting ) ) + return -ENOTTY; + + /* Sanity check */ + if ( ! settings->op->store ) + return -ENOTSUP; + + /* 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 setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v origin Origin of setting to fill in, or NULL + * @v fetched Fetched setting to fill in, or NULL + * @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, const struct setting *setting, + struct settings **origin, struct setting *fetched, + void *data, size_t len ) { + const struct setting *applicable; + struct settings *child; + struct setting tmp; + int ret; + + /* Avoid returning uninitialised data on error */ + memset ( data, 0, len ); + if ( origin ) + *origin = NULL; + if ( fetched ) + memcpy ( fetched, setting, sizeof ( *fetched ) ); + + /* Find target settings block */ + settings = settings_target ( settings ); + + /* Sanity check */ + if ( ! settings->op->fetch ) + return -ENOTSUP; + + /* Try this block first, if an applicable setting exists */ + if ( ( applicable = applicable_setting ( settings, setting ) ) ) { + + /* Create modifiable copy of setting */ + memcpy ( &tmp, applicable, sizeof ( tmp ) ); + if ( ( ret = settings->op->fetch ( settings, &tmp, + data, len ) ) >= 0 ) { + + /* Default to string type, if not yet specified */ + if ( ! tmp.type ) + tmp.type = &setting_type_string; + + /* Record origin, if applicable */ + if ( origin ) + *origin = settings; + + /* Record fetched setting, if applicable */ + if ( fetched ) + memcpy ( fetched, &tmp, sizeof ( *fetched ) ); + + return ret; + } + } + + /* Recurse into each child block in turn */ + list_for_each_entry ( child, &settings->children, siblings ) { + if ( ( ret = fetch_setting ( child, setting, origin, fetched, + data, len ) ) >= 0 ) + return ret; + } + + return -ENOENT; +} + +/** + * Fetch allocated copy of setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v origin Origin of setting to fill in, or NULL + * @v fetched Fetched setting to fill in, or NULL + * @v data Buffer to allocate and fill with setting data + * @v alloc Allocation function + * @ret len Length of setting, or negative error + * + * The caller is responsible for eventually freeing the allocated + * buffer. + */ +static int fetch_setting_alloc ( struct settings *settings, + const struct setting *setting, + struct settings **origin, + struct setting *fetched, + void **data, + void * ( * alloc ) ( size_t len ) ) { + struct settings *tmp_origin; + struct setting tmp_fetched; + int len; + int check_len; + + /* Use local buffers if necessary */ + if ( ! origin ) + origin = &tmp_origin; + if ( ! fetched ) + fetched = &tmp_fetched; + + /* Avoid returning uninitialised data on error */ + *data = NULL; + + /* Check existence, and fetch setting length */ + len = fetch_setting ( settings, setting, origin, fetched, NULL, 0 ); + if ( len < 0 ) + return len; + + /* Allocate buffer */ + *data = alloc ( len ); + if ( ! *data ) + return -ENOMEM; + + /* Fetch setting value */ + check_len = fetch_setting ( *origin, fetched, NULL, NULL, *data, len ); + assert ( check_len == len ); + return len; +} + +/** + * Fetch copy of setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v origin Origin of setting to fill in, or NULL + * @v fetched Fetched setting to fill in, or NULL + * @v data Buffer to allocate and fill with setting data + * @ret len Length of setting, or negative error + * + * The caller is responsible for eventually freeing the allocated + * buffer. + */ +int fetch_setting_copy ( struct settings *settings, + const struct setting *setting, + struct settings **origin, struct setting *fetched, + void **data ) { + + return fetch_setting_alloc ( settings, setting, origin, fetched, + data, malloc ); +} + +/** + * 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 string data + * @v len Length of buffer + * @ret len Length of setting, or negative error + */ +int fetch_raw_setting ( struct settings *settings, + const struct setting *setting, + void *data, size_t len ) { + + return fetch_setting ( settings, setting, NULL, NULL, data, len ); +} + +/** + * Fetch value of setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v data Buffer to allocate and fill with setting data + * @ret len Length of setting, or negative error + * + * The caller is responsible for eventually freeing the allocated + * buffer. + */ +int fetch_raw_setting_copy ( struct settings *settings, + const struct setting *setting, + void **data ) { + + return fetch_setting_copy ( settings, setting, NULL, NULL, data ); +} + +/** + * 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, + const struct setting *setting, + char *data, size_t len ) { + + memset ( data, 0, len ); + return fetch_raw_setting ( settings, setting, data, + ( ( len > 0 ) ? ( len - 1 ) : 0 ) ); +} + +/** + * Allocate memory for copy of string setting + * + * @v len Length of setting + * @ret ptr Allocated memory + */ +static void * fetch_string_setting_copy_alloc ( size_t len ) { + return zalloc ( len + 1 /* NUL */ ); +} + +/** + * Fetch value of string setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v data Buffer to allocate and fill with setting string data + * @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. The caller is responsible for eventually freeing the + * allocated buffer. + */ +int fetch_string_setting_copy ( struct settings *settings, + const struct setting *setting, char **data ) { + + return fetch_setting_alloc ( settings, setting, NULL, NULL, + ( ( void ** ) data ), + fetch_string_setting_copy_alloc ); +} + +/** + * Fetch value of IPv4 address setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v inp IPv4 addresses to fill in + * @v count Maximum number of IPv4 addresses + * @ret len Length of setting, or negative error + */ +int fetch_ipv4_array_setting ( struct settings *settings, + const struct setting *setting, + struct in_addr *inp, unsigned int count ) { + int len; + + len = fetch_raw_setting ( settings, setting, inp, + ( sizeof ( *inp ) * count ) ); + if ( len < 0 ) + return len; + if ( ( len % sizeof ( *inp ) ) != 0 ) + return -ERANGE; + return len; +} + +/** + * 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, + const struct setting *setting, + struct in_addr *inp ) { + + return fetch_ipv4_array_setting ( settings, setting, inp, 1 ); +} + +/** + * Fetch value of IPv6 address setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v inp IPv6 addresses to fill in + * @v count Maximum number of IPv6 addresses + * @ret len Length of setting, or negative error + */ +int fetch_ipv6_array_setting ( struct settings *settings, + const struct setting *setting, + struct in6_addr *inp, unsigned int count ) { + int len; + + len = fetch_raw_setting ( settings, setting, inp, + ( sizeof ( *inp ) * count ) ); + if ( len < 0 ) + return len; + if ( ( len % sizeof ( *inp ) ) != 0 ) + return -ERANGE; + return len; +} + +/** + * Fetch value of IPv6 address setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v inp IPv6 address to fill in + * @ret len Length of setting, or negative error + */ +int fetch_ipv6_setting ( struct settings *settings, + const struct setting *setting, + struct in6_addr *inp ) { + + return fetch_ipv6_array_setting ( settings, setting, inp, 1 ); +} + +/** + * Extract numeric value of setting + * + * @v is_signed Treat value as a signed integer + * @v raw Raw setting data + * @v len Length of raw setting data + * @ret value Numeric value + * @ret len Length of setting, or negative error + */ +static int numeric_setting_value ( int is_signed, const void *raw, size_t len, + unsigned long *value ) { + const uint8_t *unsigned_bytes = raw; + const int8_t *signed_bytes = raw; + int is_negative; + unsigned int i; + uint8_t pad; + uint8_t byte; + + /* Convert to host-ordered longs */ + is_negative = ( len && ( signed_bytes[0] < 0 ) ); + *value = ( ( is_signed && is_negative ) ? -1L : 0 ); + pad = *value; + for ( i = 0 ; i < len ; i++ ) { + byte = unsigned_bytes[i]; + *value = ( ( *value << 8 ) | byte ); + if ( ( ( i + sizeof ( *value ) ) < len ) && ( byte != pad ) ) + return -ERANGE; + } + + return len; +} + +/** + * Fetch value of numeric 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_numeric_setting ( struct settings *settings, + const struct setting *setting, + unsigned long *value, int is_signed ) { + unsigned long tmp; + int len; + + /* Avoid returning uninitialised data on error */ + *value = 0; + + /* Fetch raw (network-ordered, variable-length) setting */ + len = fetch_raw_setting ( settings, setting, &tmp, sizeof ( tmp ) ); + if ( len < 0 ) + return len; + + /* Extract numeric value */ + return numeric_setting_value ( is_signed, &tmp, len, value ); +} + +/** + * 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, + const struct setting *setting, + long *value ) { + + return fetch_numeric_setting ( settings, setting, + ( ( unsigned long * ) value ), 1 ); +} + +/** + * 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, + const struct setting *setting, + unsigned long *value ) { + + return fetch_numeric_setting ( settings, setting, value, 0 ); +} + +/** + * 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, + const struct setting *setting ) { + unsigned long value; + + fetch_numeric_setting ( settings, setting, &value, 1 ); + 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, + const struct setting *setting ) { + unsigned long value; + + fetch_numeric_setting ( settings, setting, &value, 0 ); + return value; +} + +/** + * Fetch value of UUID setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v uuid UUID to fill in + * @ret len Length of setting, or negative error + */ +int fetch_uuid_setting ( struct settings *settings, + const struct setting *setting, + union uuid *uuid ) { + int len; + + len = fetch_raw_setting ( settings, setting, uuid, sizeof ( *uuid ) ); + if ( len < 0 ) + return len; + if ( len != sizeof ( *uuid ) ) + return -ERANGE; + return len; +} + +/** + * Clear settings block + * + * @v settings Settings block + */ +void clear_settings ( struct settings *settings ) { + + /* Find target settings block */ + settings = settings_target ( settings ); + + /* Clear settings, if applicable */ + if ( settings->op->clear ) + settings->op->clear ( settings ); +} + +/** + * 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 ( const struct setting *a, const struct setting *b ) { + + /* If the settings have tags, compare them */ + if ( a->tag && ( a->tag == b->tag ) && ( a->scope == b->scope ) ) + return 0; + + /* Otherwise, if the settings have names, compare them */ + if ( a->name && b->name && a->name[0] ) + return strcmp ( a->name, b->name ); + + /* Otherwise, return a non-match */ + return ( ! 0 ); +} + +/****************************************************************************** + * + * Formatted setting routines + * + ****************************************************************************** + */ + +/** + * Format setting value as a string + * + * @v type Setting type + * @v raw Raw setting value + * @v raw_len Length of raw setting value + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +int setting_format ( const struct setting_type *type, const void *raw, + size_t raw_len, char *buf, size_t len ) { + + /* Sanity check */ + if ( ! type->format ) + return -ENOTSUP; + + return type->format ( type, raw, raw_len, buf, len ); +} + +/** + * Parse formatted string to setting value + * + * @v type Setting type + * @v value Formatted setting value + * @v buf Buffer to contain raw value + * @v len Length of buffer + * @ret len Length of raw value, or negative error + */ +int setting_parse ( const struct setting_type *type, const char *value, + void *buf, size_t len ) { + + /* Sanity check */ + if ( ! type->parse ) + return -ENOTSUP; + + return type->parse ( type, value, buf, len ); +} + +/** + * Convert setting value to number + * + * @v type Setting type + * @v raw Raw setting value + * @v raw_len Length of raw setting value + * @ret value Numeric value + * @ret rc Return status code + */ +int setting_numerate ( const struct setting_type *type, const void *raw, + size_t raw_len, unsigned long *value ) { + + /* Sanity check */ + if ( ! type->numerate ) + return -ENOTSUP; + + return type->numerate ( type, raw, raw_len, value ); +} + +/** + * Convert number to setting value + * + * @v type Setting type + * @v value Numeric value + * @v buf Buffer to contain raw value + * @v len Length of buffer + * @ret len Length of raw value, or negative error + */ +int setting_denumerate ( const struct setting_type *type, unsigned long value, + void *buf, size_t len ) { + + /* Sanity check */ + if ( ! type->denumerate ) + return -ENOTSUP; + + return type->denumerate ( type, value, buf, len ); +} + +/** + * Fetch formatted value of setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v origin Origin of setting to fill in, or NULL + * @v fetched Fetched setting to fill in, or NULL + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +int fetchf_setting ( struct settings *settings, const struct setting *setting, + struct settings **origin, struct setting *fetched, + char *buf, size_t len ) { + struct setting tmp_fetched; + void *raw; + int raw_len; + int ret; + + /* Use local buffers if necessary */ + if ( ! fetched ) + fetched = &tmp_fetched; + + /* Fetch raw value */ + raw_len = fetch_setting_copy ( settings, setting, origin, fetched, + &raw ); + if ( raw_len < 0 ) { + ret = raw_len; + goto err_fetch_copy; + } + + /* Sanity check */ + assert ( fetched->type != NULL ); + + /* Format setting */ + if ( ( ret = setting_format ( fetched->type, raw, raw_len, buf, + len ) ) < 0 ) + goto err_format; + + err_format: + free ( raw ); + err_fetch_copy: + return ret; +} + +/** + * Fetch copy of formatted value of setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v origin Origin of setting to fill in, or NULL + * @v fetched Fetched setting to fill in, or NULL + * @v value Buffer to allocate and fill with formatted value + * @ret len Length of formatted value, or negative error + * + * The caller is responsible for eventually freeing the allocated + * buffer. + */ +int fetchf_setting_copy ( struct settings *settings, + const struct setting *setting, + struct settings **origin, struct setting *fetched, + char **value ) { + struct settings *tmp_origin; + struct setting tmp_fetched; + int len; + int check_len; + + /* Use local buffers if necessary */ + if ( ! origin ) + origin = &tmp_origin; + if ( ! fetched ) + fetched = &tmp_fetched; + + /* Avoid returning uninitialised data on error */ + *value = NULL; + + /* Check existence, and fetch formatted value length */ + len = fetchf_setting ( settings, setting, origin, fetched, NULL, 0 ); + if ( len < 0 ) + return len; + + /* Allocate buffer */ + *value = zalloc ( len + 1 /* NUL */ ); + if ( ! *value ) + return -ENOMEM; + + /* Fetch formatted value */ + check_len = fetchf_setting ( *origin, fetched, NULL, NULL, *value, + ( len + 1 /* NUL */ ) ); + assert ( check_len == len ); + return len; +} + +/** + * Store formatted value of setting + * + * @v settings Settings block + * @v setting Setting to store + * @v value Formatted setting data, or NULL + * @ret rc Return status code + */ +int storef_setting ( struct settings *settings, const struct setting *setting, + const char *value ) { + void *raw; + int raw_len; + int check_len; + int rc; + + /* NULL value or empty string implies deletion */ + if ( ( ! value ) || ( ! value[0] ) ) + return delete_setting ( settings, setting ); + + /* Sanity check */ + assert ( setting->type != NULL ); + + /* Get raw value length */ + raw_len = setting_parse ( setting->type, value, NULL, 0 ); + if ( raw_len < 0 ) { + rc = raw_len; + goto err_raw_len; + } + + /* Allocate buffer for raw value */ + raw = malloc ( raw_len ); + if ( ! raw ) { + rc = -ENOMEM; + goto err_alloc_raw; + } + + /* Parse formatted value */ + check_len = setting_parse ( setting->type, value, raw, raw_len ); + assert ( check_len == raw_len ); + + /* Store raw value */ + if ( ( rc = store_setting ( settings, setting, raw, raw_len ) ) != 0 ) + goto err_store; + + err_store: + free ( raw ); + err_alloc_raw: + err_raw_len: + return rc; +} + +/** + * Fetch numeric value of setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v origin Origin of setting to fill in, or NULL + * @v fetched Fetched setting to fill in, or NULL + * @v value Numeric value to fill in + * @ret rc Return status code + */ +int fetchn_setting ( struct settings *settings, const struct setting *setting, + struct settings **origin, struct setting *fetched, + unsigned long *value ) { + struct setting tmp_fetched; + void *raw; + int raw_len; + int rc; + + /* Use local buffers if necessary */ + if ( ! fetched ) + fetched = &tmp_fetched; + + /* Fetch raw value */ + raw_len = fetch_setting_copy ( settings, setting, origin, fetched, + &raw ); + if ( raw_len < 0 ) { + rc = raw_len; + goto err_fetch_copy; + } + + /* Sanity check */ + assert ( fetched->type != NULL ); + + /* Numerate setting */ + if ( ( rc = setting_numerate ( fetched->type, raw, raw_len, + value ) ) < 0 ) + goto err_numerate; + + err_numerate: + free ( raw ); + err_fetch_copy: + return rc; +} + +/** + * Store numeric value of setting + * + * @v settings Settings block + * @v setting Setting + * @v value Numeric value + * @ret rc Return status code + */ +int storen_setting ( struct settings *settings, const struct setting *setting, + unsigned long value ) { + void *raw; + int raw_len; + int check_len; + int rc; + + /* Sanity check */ + assert ( setting->type != NULL ); + + /* Get raw value length */ + raw_len = setting_denumerate ( setting->type, value, NULL, 0 ); + if ( raw_len < 0 ) { + rc = raw_len; + goto err_raw_len; + } + + /* Allocate buffer for raw value */ + raw = malloc ( raw_len ); + if ( ! raw ) { + rc = -ENOMEM; + goto err_alloc_raw; + } + + /* Denumerate value */ + check_len = setting_denumerate ( setting->type, value, raw, raw_len ); + assert ( check_len == raw_len ); + + /* Store raw value */ + if ( ( rc = store_setting ( settings, setting, raw, raw_len ) ) != 0 ) + goto err_store; + + err_store: + free ( raw ); + err_alloc_raw: + err_raw_len: + return rc; +} + +/****************************************************************************** + * + * Named settings + * + ****************************************************************************** + */ + +/** + * Find predefined setting + * + * @v name Name + * @ret setting Setting, or NULL + */ +struct setting * find_setting ( const char *name ) { + struct setting *setting; + + for_each_table_entry ( setting, SETTINGS ) { + if ( strcmp ( name, setting->name ) == 0 ) + return setting; + } + return NULL; +} + +/** + * Parse setting name as tag number + * + * @v name Name + * @ret tag Tag number, or 0 if not a valid number + */ +static unsigned int parse_setting_tag ( const char *name ) { + char *tmp = ( ( char * ) name ); + unsigned int tag = 0; + + while ( 1 ) { + tag = ( ( tag << 8 ) | strtoul ( tmp, &tmp, 0 ) ); + if ( *tmp == 0 ) + return tag; + if ( *tmp != '.' ) + return 0; + tmp++; + } +} + +/** + * Find setting type + * + * @v name Name + * @ret type Setting type, or NULL + */ +static const struct setting_type * find_setting_type ( const char *name ) { + const struct setting_type *type; + + for_each_table_entry ( type, SETTING_TYPES ) { + if ( strcmp ( name, type->name ) == 0 ) + return type; + } + return NULL; +} + +/** + * Parse setting name + * + * @v name Name of setting + * @v get_child Function to find or create child settings block + * @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. + * + * Note that on success, this function will have modified the original + * setting @c name. + */ +int parse_setting_name ( char *name, get_child_settings_t get_child, + struct settings **settings, struct setting *setting ) { + char *settings_name; + char *setting_name; + char *type_name; + struct setting *predefined; + int rc; + + /* Set defaults */ + *settings = &settings_root; + memset ( setting, 0, sizeof ( *setting ) ); + setting->name = ""; + + /* Split name into "[settings_name/]setting_name[:type_name]" */ + if ( ( setting_name = strchr ( name, '/' ) ) != NULL ) { + *(setting_name++) = 0; + settings_name = name; + } else { + setting_name = name; + settings_name = NULL; + } + if ( ( type_name = strchr ( setting_name, ':' ) ) != NULL ) + *(type_name++) = 0; + + /* Identify settings block, if specified */ + if ( settings_name ) { + *settings = parse_settings_name ( settings_name, get_child ); + if ( *settings == NULL ) { + DBG ( "Unrecognised settings block \"%s\" in \"%s\"\n", + settings_name, name ); + rc = -ENODEV; + goto err; + } + } + + /* Identify setting */ + setting->tag = parse_setting_tag ( setting_name ); + setting->scope = (*settings)->default_scope; + setting->name = setting_name; + for_each_table_entry ( predefined, SETTINGS ) { + /* Matches a predefined setting; use that setting */ + if ( setting_cmp ( predefined, setting ) == 0 ) { + memcpy ( setting, predefined, sizeof ( *setting ) ); + break; + } + } + + /* 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 ); + rc = -ENOTSUP; + goto err; + } + } + + return 0; + + err: + /* Restore original name */ + if ( settings_name ) + *( setting_name - 1 ) = '/'; + if ( type_name ) + *( type_name - 1 ) = ':'; + return rc; +} + +/** + * Return full setting name + * + * @v settings Settings block, or NULL + * @v setting Setting + * @v buf Buffer + * @v len Length of buffer + * @ret len Length of setting name, or negative error + */ +int setting_name ( struct settings *settings, const struct setting *setting, + char *buf, size_t len ) { + const char *name; + + settings = settings_target ( settings ); + name = settings_name ( settings ); + return snprintf ( buf, len, "%s%s%s:%s", name, ( name[0] ? "/" : "" ), + setting->name, setting->type->name ); +} + +/****************************************************************************** + * + * Setting types + * + ****************************************************************************** + */ + +/** + * Parse string setting value + * + * @v type Setting type + * @v value Formatted setting value + * @v buf Buffer to contain raw value + * @v len Length of buffer + * @ret len Length of raw value, or negative error + */ +static int parse_string_setting ( const struct setting_type *type __unused, + const char *value, void *buf, size_t len ) { + size_t raw_len = strlen ( value ); /* Exclude terminating NUL */ + + /* Copy string to buffer */ + if ( len > raw_len ) + len = raw_len; + memcpy ( buf, value, len ); + + return raw_len; +} + +/** + * Format string setting value + * + * @v type Setting type + * @v raw Raw setting value + * @v raw_len Length of raw setting value + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +static int format_string_setting ( const struct setting_type *type __unused, + const void *raw, size_t raw_len, char *buf, + size_t len ) { + + /* Copy string to buffer, and terminate */ + memset ( buf, 0, len ); + if ( len > raw_len ) + len = raw_len; + memcpy ( buf, raw, len ); + + return raw_len; +} + +/** A string setting type */ +const struct setting_type setting_type_string __setting_type = { + .name = "string", + .parse = parse_string_setting, + .format = format_string_setting, +}; + +/** A URI-encoded string setting type + * + * This setting type is obsolete; the name ":uristring" is retained to + * avoid breaking existing scripts. + */ +const struct setting_type setting_type_uristring __setting_type = { + .name = "uristring", + .parse = parse_string_setting, + .format = format_string_setting, +}; + +/** + * Parse IPv4 address setting value (when IPv4 support is not present) + * + * @v type Setting type + * @v value Formatted setting value + * @v buf Buffer to contain raw value + * @v len Length of buffer + * @ret len Length of raw value, or negative error + */ +__weak int parse_ipv4_setting ( const struct setting_type *type __unused, + const char *value __unused, void *buf __unused, + size_t len __unused ) { + return -ENOTSUP; +} + +/** + * Format IPv4 address setting value (when IPv4 support is not present) + * + * @v type Setting type + * @v raw Raw setting value + * @v raw_len Length of raw setting value + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +__weak int format_ipv4_setting ( const struct setting_type *type __unused, + const void *raw __unused, + size_t raw_len __unused, char *buf __unused, + size_t len __unused ) { + return -ENOTSUP; +} + +/** An IPv4 address setting type */ +const struct setting_type setting_type_ipv4 __setting_type = { + .name = "ipv4", + .parse = parse_ipv4_setting, + .format = format_ipv4_setting, +}; + +/** + * Parse IPv6 address setting value (when IPv6 support is not present) + * + * @v type Setting type + * @v value Formatted setting value + * @v buf Buffer to contain raw value + * @v len Length of buffer + * @ret len Length of raw value, or negative error + */ +__weak int parse_ipv6_setting ( const struct setting_type *type __unused, + const char *value __unused, void *buf __unused, + size_t len __unused ) { + return -ENOTSUP; +} + +/** + * Format IPv6 address setting value (when IPv6 support is not present) + * + * @v type Setting type + * @v raw Raw setting value + * @v raw_len Length of raw setting value + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +__weak int format_ipv6_setting ( const struct setting_type *type __unused, + const void *raw __unused, + size_t raw_len __unused, char *buf __unused, + size_t len __unused ) { + return -ENOTSUP; +} + +/** An IPv6 address setting type */ +const struct setting_type setting_type_ipv6 __setting_type = { + .name = "ipv6", + .parse = parse_ipv6_setting, + .format = format_ipv6_setting, +}; + +/** IPv6 settings scope */ +const struct settings_scope ipv6_scope; + +/** + * Integer setting type indices + * + * These indexes are defined such that (1<<index) gives the width of + * the integer, in bytes. + */ +enum setting_type_int_index { + SETTING_TYPE_INT8 = 0, + SETTING_TYPE_INT16 = 1, + SETTING_TYPE_INT32 = 2, +}; + +/** + * Integer setting type names + * + * These names exist as a static array in order to allow the type's + * integer size and signedness to be determined from the type's name. + * Note that there are no separate entries for the signed integer + * types: the name pointers simply point to the second character of + * the relevant string. + */ +static const char setting_type_int_name[][8] = { + [SETTING_TYPE_INT8] = "uint8", + [SETTING_TYPE_INT16] = "uint16", + [SETTING_TYPE_INT32] = "uint32", +}; + +/** + * Get unsigned integer setting type name + * + * @v index Integer setting type index + * @ret name Setting type name + */ +#define SETTING_TYPE_UINT_NAME( index ) setting_type_int_name[index] + +/** + * Get signed integer setting type name + * + * @v index Integer setting type index + * @ret name Setting type name + */ +#define SETTING_TYPE_INT_NAME( index ) ( setting_type_int_name[index] + 1 ) + +/** + * Get integer setting type index + * + * @v type Setting type + * @ret index Integer setting type index + */ +static unsigned int setting_type_int_index ( const struct setting_type *type ) { + + return ( ( type->name - setting_type_int_name[0] ) / + sizeof ( setting_type_int_name[0] ) ); +} + +/** + * Get integer setting type width + * + * @v type Setting type + * @ret index Integer setting type width + */ +static unsigned int setting_type_int_width ( const struct setting_type *type ) { + + return ( 1 << setting_type_int_index ( type ) ); +} + +/** + * Get integer setting type signedness + * + * @v type Setting type + * @ret is_signed Integer setting type is signed + */ +static int setting_type_int_is_signed ( const struct setting_type *type ) { + return ( ( type->name - setting_type_int_name[0] ) & 1 ); +} + +/** + * Convert number to setting value + * + * @v type Setting type + * @v value Numeric value + * @v buf Buffer to contain raw value + * @v len Length of buffer + * @ret len Length of raw value, or negative error + */ +static int denumerate_int_setting ( const struct setting_type *type, + unsigned long value, void *buf, + size_t len ) { + unsigned int size = setting_type_int_width ( type ); + union { + uint32_t num; + uint8_t bytes[4]; + } u; + + u.num = htonl ( value ); + if ( len > size ) + len = size; + memcpy ( buf, &u.bytes[ sizeof ( u ) - size ], len ); + + return size; +} + +/** + * Convert setting value to number + * + * @v type Setting type + * @v raw Raw setting value + * @v raw_len Length of raw setting value + * @v value Numeric value to fill in + * @ret rc Return status code + */ +static int numerate_int_setting ( const struct setting_type *type, + const void *raw, size_t raw_len, + unsigned long *value ) { + int is_signed = setting_type_int_is_signed ( type ); + int check_len; + + /* Extract numeric value */ + check_len = numeric_setting_value ( is_signed, raw, raw_len, value ); + if ( check_len < 0 ) + return check_len; + assert ( check_len == ( int ) raw_len ); + + return 0; +} + +/** + * Parse integer setting value + * + * @v type Setting type + * @v value Formatted setting value + * @v buf Buffer to contain raw value + * @v len Length of buffer + * @ret len Length of raw value, or negative error + */ +static int parse_int_setting ( const struct setting_type *type, + const char *value, void *buf, size_t len ) { + char *endp; + unsigned long num_value; + + /* Parse value */ + num_value = strtoul ( value, &endp, 0 ); + if ( *endp ) + return -EINVAL; + + return type->denumerate ( type, num_value, buf, len ); +} + +/** + * Format signed integer setting value + * + * @v type Setting type + * @v raw Raw setting value + * @v raw_len Length of raw setting value + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +static int format_int_setting ( const struct setting_type *type, + const void *raw, size_t raw_len, + char *buf, size_t len ) { + unsigned long value; + int ret; + + /* Extract numeric value */ + if ( ( ret = type->numerate ( type, raw, raw_len, &value ) ) < 0 ) + return ret; + + /* Format value */ + return snprintf ( buf, len, "%ld", value ); +} + +/** + * Format unsigned integer setting value + * + * @v type Setting type + * @v raw Raw setting value + * @v raw_len Length of raw setting value + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +static int format_uint_setting ( const struct setting_type *type, + const void *raw, size_t raw_len, + char *buf, size_t len ) { + unsigned long value; + int ret; + + /* Extract numeric value */ + if ( ( ret = type->numerate ( type, raw, raw_len, &value ) ) < 0 ) + return ret; + + /* Format value */ + return snprintf ( buf, len, "%#lx", value ); +} + +/** + * Define a signed integer setting type + * + * @v index Integer setting type index + * @ret type Setting type + */ +#define SETTING_TYPE_INT( index ) { \ + .name = SETTING_TYPE_INT_NAME ( index ), \ + .parse = parse_int_setting, \ + .format = format_int_setting, \ + .denumerate = denumerate_int_setting, \ + .numerate = numerate_int_setting, \ +} + +/** + * Define an unsigned integer setting type + * + * @v index Integer setting type index + * @ret type Setting type + */ +#define SETTING_TYPE_UINT( index ) { \ + .name = SETTING_TYPE_UINT_NAME ( index ), \ + .parse = parse_int_setting, \ + .format = format_uint_setting, \ + .denumerate = denumerate_int_setting, \ + .numerate = numerate_int_setting, \ +} + +/** A signed 8-bit integer setting type */ +const struct setting_type setting_type_int8 __setting_type = + SETTING_TYPE_INT ( SETTING_TYPE_INT8 ); + +/** A signed 16-bit integer setting type */ +const struct setting_type setting_type_int16 __setting_type = + SETTING_TYPE_INT ( SETTING_TYPE_INT16 ); + +/** A signed 32-bit integer setting type */ +const struct setting_type setting_type_int32 __setting_type = + SETTING_TYPE_INT ( SETTING_TYPE_INT32 ); + +/** An unsigned 8-bit integer setting type */ +const struct setting_type setting_type_uint8 __setting_type = + SETTING_TYPE_UINT ( SETTING_TYPE_INT8 ); + +/** An unsigned 16-bit integer setting type */ +const struct setting_type setting_type_uint16 __setting_type = + SETTING_TYPE_UINT ( SETTING_TYPE_INT16 ); + +/** An unsigned 32-bit integer setting type */ +const struct setting_type setting_type_uint32 __setting_type = + SETTING_TYPE_UINT ( SETTING_TYPE_INT32 ); + +/** + * Format hex string setting value + * + * @v delimiter Byte delimiter + * @v raw Raw setting value + * @v raw_len Length of raw setting value + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +static int format_hex_setting ( const char *delimiter, const void *raw, + size_t raw_len, char *buf, size_t len ) { + const uint8_t *bytes = raw; + int used = 0; + unsigned int i; + + 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 ? delimiter : "" ), + bytes[i] ); + } + return used; +} + +/** + * Parse hex string setting value (using colon delimiter) + * + * @v type Setting type + * @v value Formatted setting value + * @v buf Buffer to contain raw value + * @v len Length of buffer + * @v size Integer size, in bytes + * @ret len Length of raw value, or negative error + */ +static int parse_hex_setting ( const struct setting_type *type __unused, + const char *value, void *buf, size_t len ) { + return hex_decode ( value, ':', buf, len ); +} + +/** + * Format hex string setting value (using colon delimiter) + * + * @v type Setting type + * @v raw Raw setting value + * @v raw_len Length of raw setting value + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +static int format_hex_colon_setting ( const struct setting_type *type __unused, + const void *raw, size_t raw_len, + char *buf, size_t len ) { + return format_hex_setting ( ":", raw, raw_len, buf, len ); +} + +/** + * Parse hex string setting value (using hyphen delimiter) + * + * @v type Setting type + * @v value Formatted setting value + * @v buf Buffer to contain raw value + * @v len Length of buffer + * @v size Integer size, in bytes + * @ret len Length of raw value, or negative error + */ +static int parse_hex_hyphen_setting ( const struct setting_type *type __unused, + const char *value, void *buf, + size_t len ) { + return hex_decode ( value, '-', buf, len ); +} + +/** + * Format hex string setting value (using hyphen delimiter) + * + * @v type Setting type + * @v raw Raw setting value + * @v raw_len Length of raw setting value + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +static int format_hex_hyphen_setting ( const struct setting_type *type __unused, + const void *raw, size_t raw_len, + char *buf, size_t len ) { + return format_hex_setting ( "-", raw, raw_len, buf, len ); +} + +/** + * Parse hex string setting value (using no delimiter) + * + * @v type Setting type + * @v value Formatted setting value + * @v buf Buffer to contain raw value + * @v len Length of buffer + * @v size Integer size, in bytes + * @ret len Length of raw value, or negative error + */ +static int parse_hex_raw_setting ( const struct setting_type *type __unused, + const char *value, void *buf, size_t len ) { + return hex_decode ( value, 0, buf, len ); +} + +/** + * Format hex string setting value (using no delimiter) + * + * @v type Setting type + * @v raw Raw setting value + * @v raw_len Length of raw setting value + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +static int format_hex_raw_setting ( const struct setting_type *type __unused, + const void *raw, size_t raw_len, + char *buf, size_t len ) { + return format_hex_setting ( "", raw, raw_len, buf, len ); +} + +/** A hex-string setting (colon-delimited) */ +const struct setting_type setting_type_hex __setting_type = { + .name = "hex", + .parse = parse_hex_setting, + .format = format_hex_colon_setting, +}; + +/** A hex-string setting (hyphen-delimited) */ +const struct setting_type setting_type_hexhyp __setting_type = { + .name = "hexhyp", + .parse = parse_hex_hyphen_setting, + .format = format_hex_hyphen_setting, +}; + +/** A hex-string setting (non-delimited) */ +const struct setting_type setting_type_hexraw __setting_type = { + .name = "hexraw", + .parse = parse_hex_raw_setting, + .format = format_hex_raw_setting, +}; + +/** + * Format UUID setting value + * + * @v type Setting type + * @v raw Raw setting value + * @v raw_len Length of raw setting value + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +static int format_uuid_setting ( const struct setting_type *type __unused, + const void *raw, size_t raw_len, char *buf, + size_t len ) { + const union uuid *uuid = raw; + + /* Range check */ + if ( raw_len != sizeof ( *uuid ) ) + return -ERANGE; + + /* Format value */ + return snprintf ( buf, len, "%s", uuid_ntoa ( uuid ) ); +} + +/** UUID setting type */ +const struct setting_type setting_type_uuid __setting_type = { + .name = "uuid", + .format = format_uuid_setting, +}; + +/** + * Format PCI bus:dev.fn setting value + * + * @v type Setting type + * @v raw Raw setting value + * @v raw_len Length of raw setting value + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +static int format_busdevfn_setting ( const struct setting_type *type __unused, + const void *raw, size_t raw_len, char *buf, + size_t len ) { + unsigned long busdevfn; + int check_len; + + /* Extract numeric value */ + check_len = numeric_setting_value ( 0, raw, raw_len, &busdevfn ); + if ( check_len < 0 ) + return check_len; + assert ( check_len == ( int ) raw_len ); + + /* Format value */ + return snprintf ( buf, len, "%02lx:%02lx.%lx", PCI_BUS ( busdevfn ), + PCI_SLOT ( busdevfn ), PCI_FUNC ( busdevfn ) ); +} + +/** PCI bus:dev.fn setting type */ +const struct setting_type setting_type_busdevfn __setting_type = { + .name = "busdevfn", + .format = format_busdevfn_setting, +}; + +/****************************************************************************** + * + * Setting expansion + * + ****************************************************************************** + */ + +/** + * Expand variables within string + * + * @v string String + * @ret expstr Expanded string + * + * The expanded string is allocated with malloc() and the caller must + * eventually free() it. + */ +char * expand_settings ( const char *string ) { + struct settings *settings; + struct setting setting; + char *expstr; + char *start; + char *end; + char *head; + char *name; + char *tail; + char *value; + char *tmp; + int new_len; + int rc; + + /* Obtain temporary modifiable copy of string */ + expstr = strdup ( string ); + if ( ! expstr ) + return NULL; + + /* Expand while expansions remain */ + while ( 1 ) { + + head = expstr; + + /* Locate setting to be expanded */ + start = NULL; + end = NULL; + for ( tmp = expstr ; *tmp ; tmp++ ) { + if ( ( tmp[0] == '$' ) && ( tmp[1] == '{' ) ) + start = tmp; + if ( start && ( tmp[0] == '}' ) ) { + end = tmp; + break; + } + } + if ( ! end ) + break; + *start = '\0'; + name = ( start + 2 ); + *end = '\0'; + tail = ( end + 1 ); + + /* Expand setting */ + if ( ( rc = parse_setting_name ( name, find_child_settings, + &settings, + &setting ) ) != 0 ) { + /* Treat invalid setting names as empty */ + value = NULL; + } else { + /* Fetch and format setting value. Ignore + * errors; treat non-existent settings as empty. + */ + fetchf_setting_copy ( settings, &setting, NULL, NULL, + &value ); + } + + /* Construct expanded string and discard old string */ + tmp = expstr; + new_len = asprintf ( &expstr, "%s%s%s", + head, ( value ? value : "" ), tail ); + free ( value ); + free ( tmp ); + if ( new_len < 0 ) + return NULL; + } + + return expstr; +} + +/****************************************************************************** + * + * Settings + * + ****************************************************************************** + */ + +/** Hostname setting */ +const struct setting hostname_setting __setting ( SETTING_HOST, hostname ) = { + .name = "hostname", + .description = "Host name", + .tag = DHCP_HOST_NAME, + .type = &setting_type_string, +}; + +/** Domain name setting */ +const struct setting domain_setting __setting ( SETTING_IP_EXTRA, domain ) = { + .name = "domain", + .description = "DNS domain", + .tag = DHCP_DOMAIN_NAME, + .type = &setting_type_string, +}; + +/** TFTP server setting */ +const struct setting next_server_setting __setting ( SETTING_BOOT,next-server)={ + .name = "next-server", + .description = "TFTP server", + .tag = DHCP_EB_SIADDR, + .type = &setting_type_ipv4, +}; + +/** Filename setting */ +const struct setting filename_setting __setting ( SETTING_BOOT, filename ) = { + .name = "filename", + .description = "Boot filename", + .tag = DHCP_BOOTFILE_NAME, + .type = &setting_type_string, +}; + +/** Root path setting */ +const struct setting root_path_setting __setting ( SETTING_SANBOOT, root-path)={ + .name = "root-path", + .description = "SAN root path", + .tag = DHCP_ROOT_PATH, + .type = &setting_type_string, +}; + +/** Username setting */ +const struct setting username_setting __setting ( SETTING_AUTH, username ) = { + .name = "username", + .description = "User name", + .tag = DHCP_EB_USERNAME, + .type = &setting_type_string, +}; + +/** Password setting */ +const struct setting password_setting __setting ( SETTING_AUTH, password ) = { + .name = "password", + .description = "Password", + .tag = DHCP_EB_PASSWORD, + .type = &setting_type_string, +}; + +/** Priority setting */ +const struct setting priority_setting __setting ( SETTING_MISC, priority ) = { + .name = "priority", + .description = "Settings priority", + .tag = DHCP_EB_PRIORITY, + .type = &setting_type_int8, +}; + +/** DHCP user class setting */ +const struct setting user_class_setting __setting ( SETTING_HOST_EXTRA, + user-class ) = { + .name = "user-class", + .description = "DHCP user class", + .tag = DHCP_USER_CLASS_ID, + .type = &setting_type_string, +}; + +/****************************************************************************** + * + * Built-in settings block + * + ****************************************************************************** + */ + +/** Built-in setting scope */ +const struct settings_scope builtin_scope; + +/** + * Fetch error number setting + * + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int errno_fetch ( void *data, size_t len ) { + uint32_t content; + + /* Return current error */ + content = htonl ( errno ); + if ( len > sizeof ( content ) ) + len = sizeof ( content ); + memcpy ( data, &content, len ); + return sizeof ( content ); +} + +/** Error number setting */ +const struct setting errno_setting __setting ( SETTING_MISC, errno ) = { + .name = "errno", + .description = "Last error", + .type = &setting_type_uint32, + .scope = &builtin_scope, +}; + +/** Error number built-in setting */ +struct builtin_setting errno_builtin_setting __builtin_setting = { + .setting = &errno_setting, + .fetch = errno_fetch, +}; + +/** + * Fetch build architecture setting + * + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int buildarch_fetch ( void *data, size_t len ) { + static const char buildarch[] = _S2 ( ARCH ); + + strncpy ( data, buildarch, len ); + return ( sizeof ( buildarch ) - 1 /* NUL */ ); +} + +/** Build architecture setting */ +const struct setting buildarch_setting __setting ( SETTING_MISC, buildarch ) = { + .name = "buildarch", + .description = "Build architecture", + .type = &setting_type_string, + .scope = &builtin_scope, +}; + +/** Build architecture built-in setting */ +struct builtin_setting buildarch_builtin_setting __builtin_setting = { + .setting = &buildarch_setting, + .fetch = buildarch_fetch, +}; + +/** + * Fetch platform setting + * + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int platform_fetch ( void *data, size_t len ) { + static const char platform[] = _S2 ( PLATFORM ); + + strncpy ( data, platform, len ); + return ( sizeof ( platform ) - 1 /* NUL */ ); +} + +/** Platform setting */ +const struct setting platform_setting __setting ( SETTING_MISC, platform ) = { + .name = "platform", + .description = "Platform", + .type = &setting_type_string, + .scope = &builtin_scope, +}; + +/** Platform built-in setting */ +struct builtin_setting platform_builtin_setting __builtin_setting = { + .setting = &platform_setting, + .fetch = platform_fetch, +}; + +/** + * Fetch version setting + * + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int version_fetch ( void *data, size_t len ) { + strncpy ( data, product_version, len ); + return ( strlen ( product_version ) ); +} + +/** Version setting */ +const struct setting version_setting __setting ( SETTING_MISC, version ) = { + .name = "version", + .description = "Version", + .type = &setting_type_string, + .scope = &builtin_scope, +}; + +/** Version built-in setting */ +struct builtin_setting version_builtin_setting __builtin_setting = { + .setting = &version_setting, + .fetch = version_fetch, +}; + +/** + * Fetch built-in 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 + */ +static int builtin_fetch ( struct settings *settings __unused, + struct setting *setting, + void *data, size_t len ) { + struct builtin_setting *builtin; + + for_each_table_entry ( builtin, BUILTIN_SETTINGS ) { + if ( setting_cmp ( setting, builtin->setting ) == 0 ) + return builtin->fetch ( data, len ); + } + return -ENOENT; +} + +/** + * Check applicability of built-in setting + * + * @v settings Settings block + * @v setting Setting + * @ret applies Setting applies within this settings block + */ +static int builtin_applies ( struct settings *settings __unused, + const struct setting *setting ) { + + return ( setting->scope == &builtin_scope ); +} + +/** Built-in settings operations */ +static struct settings_operations builtin_settings_operations = { + .applies = builtin_applies, + .fetch = builtin_fetch, +}; + +/** Built-in settings */ +static struct settings builtin_settings = { + .refcnt = NULL, + .siblings = LIST_HEAD_INIT ( builtin_settings.siblings ), + .children = LIST_HEAD_INIT ( builtin_settings.children ), + .op = &builtin_settings_operations, +}; + +/** Initialise built-in settings */ +static void builtin_init ( void ) { + int rc; + + if ( ( rc = register_settings ( &builtin_settings, NULL, + "builtin" ) ) != 0 ) { + DBG ( "Could not register built-in settings: %s\n", + strerror ( rc ) ); + return; + } +} + +/** Built-in settings initialiser */ +struct init_fn builtin_init_fn __init_fn ( INIT_NORMAL ) = { + .initialise = builtin_init, +}; diff --git a/qemu/roms/ipxe/src/core/string.c b/qemu/roms/ipxe/src/core/string.c new file mode 100644 index 000000000..e53c283c2 --- /dev/null +++ b/qemu/roms/ipxe/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. + */ + +FILE_LICENCE ( GPL2_ONLY ); + +/* + * 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 = strnlen(s,n); + char *new; + + 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/qemu/roms/ipxe/src/core/stringextra.c b/qemu/roms/ipxe/src/core/stringextra.c new file mode 100644 index 000000000..0a509852e --- /dev/null +++ b/qemu/roms/ipxe/src/core/stringextra.c @@ -0,0 +1,275 @@ +/* + * 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. + */ + +FILE_LICENCE ( GPL2_ONLY ); + +/* + * 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/qemu/roms/ipxe/src/core/strtoull.c b/qemu/roms/ipxe/src/core/strtoull.c new file mode 100644 index 000000000..00986eef0 --- /dev/null +++ b/qemu/roms/ipxe/src/core/strtoull.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk> + * Copyright (C) 2010 Piotr JaroszyÅ„ski <p.jaroszynski@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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdlib.h> +#include <ctype.h> + +/* + * Despite being exactly the same as strtoul() except the long long instead of + * long it ends up being much bigger so provide a separate implementation in a + * separate object so that it won't be linked in if not used. + */ +unsigned long long strtoull ( const char *p, char **endp, int base ) { + unsigned long long ret = 0; + int negative = 0; + unsigned int charval; + + while ( isspace ( *p ) ) + p++; + + if ( *p == '-' ) { + negative = 1; + p++; + } + + base = strtoul_base ( &p, base ); + + while ( 1 ) { + charval = strtoul_charval ( *p ); + if ( charval >= ( unsigned int ) base ) + break; + ret = ( ( ret * base ) + charval ); + p++; + } + + if ( negative ) + ret = -ret; + + if ( endp ) + *endp = ( char * ) p; + + return ( ret ); +} diff --git a/qemu/roms/ipxe/src/core/time.c b/qemu/roms/ipxe/src/core/time.c new file mode 100644 index 000000000..f70e1981d --- /dev/null +++ b/qemu/roms/ipxe/src/core/time.c @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2012 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <time.h> + +/** @file + * + * Date and time + * + * POSIX:2008 section 4.15 defines "seconds since the Epoch" as an + * abstract measure approximating the number of seconds that have + * elapsed since the Epoch, excluding leap seconds. The formula given + * is + * + * tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 + + * (tm_year-70)*31536000 + ((tm_year-69)/4)*86400 - + * ((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400 + * + * This calculation assumes that leap years occur in each year that is + * either divisible by 4 but not divisible by 100, or is divisible by + * 400. + */ + +/** Days of week (for debugging) */ +static const char *weekdays[] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" +}; + +/** + * Determine whether or not year is a leap year + * + * @v tm_year Years since 1900 + * @v is_leap_year Year is a leap year + */ +static int is_leap_year ( int tm_year ) { + int leap_year = 0; + + if ( ( tm_year % 4 ) == 0 ) + leap_year = 1; + if ( ( tm_year % 100 ) == 0 ) + leap_year = 0; + if ( ( tm_year % 400 ) == 100 ) + leap_year = 1; + + return leap_year; +} + +/** + * Calculate number of leap years since 1900 + * + * @v tm_year Years since 1900 + * @v num_leap_years Number of leap years + */ +static int leap_years_to_end ( int tm_year ) { + int leap_years = 0; + + leap_years += ( tm_year / 4 ); + leap_years -= ( tm_year / 100 ); + leap_years += ( ( tm_year + 300 ) / 400 ); + + return leap_years; +} + +/** + * Calculate day of week + * + * @v tm_year Years since 1900 + * @v tm_mon Month of year [0,11] + * @v tm_day Day of month [1,31] + */ +static int day_of_week ( int tm_year, int tm_mon, int tm_mday ) { + static const uint8_t offset[12] = + { 1, 4, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5 }; + int pseudo_year = tm_year; + + if ( tm_mon < 2 ) + pseudo_year--; + return ( ( pseudo_year + leap_years_to_end ( pseudo_year ) + + offset[tm_mon] + tm_mday ) % 7 ); +} + +/** Days from start of year until start of months (in non-leap years) */ +static const uint16_t days_to_month_start[] = + { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; + +/** + * Calculate seconds since the Epoch + * + * @v tm Broken-down time + * @ret time Seconds since the Epoch + */ +time_t mktime ( struct tm *tm ) { + int days_since_epoch; + int seconds_since_day; + time_t seconds; + + /* Calculate day of year */ + tm->tm_yday = ( ( tm->tm_mday - 1 ) + + days_to_month_start[ tm->tm_mon ] ); + if ( ( tm->tm_mon >= 2 ) && is_leap_year ( tm->tm_year ) ) + tm->tm_yday++; + + /* Calculate day of week */ + tm->tm_wday = day_of_week ( tm->tm_year, tm->tm_mon, tm->tm_mday ); + + /* Calculate seconds since the Epoch */ + days_since_epoch = ( tm->tm_yday + ( 365 * tm->tm_year ) - 25567 + + leap_years_to_end ( tm->tm_year - 1 ) ); + seconds_since_day = + ( ( ( ( tm->tm_hour * 60 ) + tm->tm_min ) * 60 ) + tm->tm_sec ); + seconds = ( ( ( ( time_t ) days_since_epoch ) * ( ( time_t ) 86400 ) ) + + seconds_since_day ); + + DBGC ( &weekdays, "TIME %04d-%02d-%02d %02d:%02d:%02d => %lld (%s, " + "day %d)\n", ( tm->tm_year + 1900 ), ( tm->tm_mon + 1 ), + tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, seconds, + weekdays[ tm->tm_wday ], tm->tm_yday ); + + return seconds; +} diff --git a/qemu/roms/ipxe/src/core/timer.c b/qemu/roms/ipxe/src/core/timer.c new file mode 100644 index 000000000..18c2b2849 --- /dev/null +++ b/qemu/roms/ipxe/src/core/timer.c @@ -0,0 +1,43 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <unistd.h> + +/** + * Delay for a fixed number of milliseconds + * + * @v msecs Number of milliseconds for which to delay + */ +void mdelay ( unsigned long msecs ) { + while ( msecs-- ) + udelay ( 1000 ); +} + +/** + * Delay for a fixed number of seconds + * + * @v secs Number of seconds for which to delay + */ +unsigned int sleep ( unsigned int secs ) { + while ( secs-- ) + mdelay ( 1000 ); + return 0; +} diff --git a/qemu/roms/ipxe/src/core/uri.c b/qemu/roms/ipxe/src/core/uri.c new file mode 100644 index 000000000..9ec21cee4 --- /dev/null +++ b/qemu/roms/ipxe/src/core/uri.c @@ -0,0 +1,680 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** @file + * + * Uniform Resource Identifiers + * + */ + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <libgen.h> +#include <ctype.h> +#include <ipxe/vsprintf.h> +#include <ipxe/params.h> +#include <ipxe/uri.h> + +/** + * Decode URI field (in place) + * + * @v string String + * + * URI decoding can never increase the length of a string; we can + * therefore safely decode in place. + */ +static void uri_decode ( char *string ) { + char *dest = string; + char hexbuf[3]; + char *hexbuf_end; + char c; + char decoded; + unsigned int skip; + + /* Copy string, decoding escaped characters as necessary */ + do { + c = *(string++); + if ( c == '%' ) { + snprintf ( hexbuf, sizeof ( hexbuf ), "%s", string ); + decoded = strtoul ( hexbuf, &hexbuf_end, 16 ); + skip = ( hexbuf_end - hexbuf ); + string += skip; + if ( skip ) + c = decoded; + } + *(dest++) = c; + } while ( c ); +} + +/** + * Check if character should be escaped within a URI field + * + * @v c Character + * @v field URI field index + * @ret escaped Character should be escaped + */ +static int uri_character_escaped ( char c, unsigned int field ) { + + /* Non-printing characters and whitespace should always be + * escaped, since they cannot sensibly be displayed as part of + * a coherent URL string. (This test also catches control + * characters such as CR and LF, which could affect the + * operation of line-based protocols such as HTTP.) + * + * We should also escape characters which would alter the + * interpretation of the URL if not escaped, i.e. characters + * which have significance to the URL parser. We should not + * blindly escape all such characters, because this would lead + * to some very strange-looking URLs (e.g. if we were to + * always escape '/' as "%2F" even within the URI path). + * + * We do not need to be perfect. Our primary role is as a + * consumer of URIs rather than a producer; the main situation + * in which we produce a URI string is for display to a human + * user, who can probably tolerate some variance from the + * formal specification. The only situation in which we + * currently produce a URI string to be consumed by a computer + * is when constructing an HTTP request URI, which contains + * only the path and query fields. + * + * We can therefore sacrifice some correctness for the sake of + * code size. For example, colons within the URI host should + * be escaped unless they form part of an IPv6 literal + * address; doing this correctly would require the URI + * formatter to be aware of whether or not the URI host + * contained an IPv4 address, an IPv6 address, or a host name. + * We choose to simplify and never escape colons within the + * URI host field: in the event of a pathological hostname + * containing colons, this could potentially produce a URI + * string which could not be reparsed. + * + * After excluding non-printing characters, whitespace, and + * '%', the full set of characters with significance to the + * URL parser is "/#:@?". We choose for each URI field which + * of these require escaping in our use cases. + */ + static const char *escaped[URI_FIELDS] = { + /* Scheme: escape everything */ + [URI_SCHEME] = "/#:@?", + /* Opaque part: escape characters which would affect + * the reparsing of the URI, allowing everything else + * (e.g. ':', which will appear in iSCSI URIs). + */ + [URI_OPAQUE] = "/#", + /* User name: escape everything */ + [URI_USER] = "/#:@?", + /* Password: escape everything */ + [URI_PASSWORD] = "/#:@?", + /* Host name: escape everything except ':', which may + * appear as part of an IPv6 literal address. + */ + [URI_HOST] = "/#@?", + /* Port number: escape everything */ + [URI_PORT] = "/#:@?", + /* Path: escape everything except '/', which usually + * appears within paths. + */ + [URI_PATH] = "#:@?", + /* Query: escape everything except '/', which + * sometimes appears within queries. + */ + [URI_QUERY] = "#:@?", + /* Fragment: escape everything */ + [URI_FRAGMENT] = "/#:@?", + }; + + return ( /* Always escape non-printing characters and whitespace */ + ( ! isprint ( c ) ) || ( c == ' ' ) || + /* Always escape '%' */ + ( c == '%' ) || + /* Escape field-specific characters */ + strchr ( escaped[field], c ) ); +} + +/** + * Encode URI field + * + * @v uri URI + * @v field URI field index + * @v buf Buffer to contain encoded string + * @v len Length of buffer + * @ret len Length of encoded string (excluding NUL) + */ +size_t uri_encode ( const char *string, unsigned int field, + char *buf, ssize_t len ) { + ssize_t remaining = len; + size_t used; + char c; + + /* Ensure encoded string is NUL-terminated even if empty */ + if ( len > 0 ) + buf[0] = '\0'; + + /* Copy string, escaping as necessary */ + while ( ( c = *(string++) ) ) { + if ( uri_character_escaped ( c, field ) ) { + used = ssnprintf ( buf, remaining, "%%%02X", c ); + } else { + used = ssnprintf ( buf, remaining, "%c", c ); + } + buf += used; + remaining -= used; + } + + return ( len - remaining ); +} + +/** + * Dump URI for debugging + * + * @v uri URI + */ +static void uri_dump ( const struct uri *uri ) { + + if ( ! uri ) + return; + if ( uri->scheme ) + DBGC ( uri, " scheme \"%s\"", uri->scheme ); + if ( uri->opaque ) + DBGC ( uri, " opaque \"%s\"", uri->opaque ); + if ( uri->user ) + DBGC ( uri, " user \"%s\"", uri->user ); + if ( uri->password ) + DBGC ( uri, " password \"%s\"", uri->password ); + if ( uri->host ) + DBGC ( uri, " host \"%s\"", uri->host ); + if ( uri->port ) + DBGC ( uri, " port \"%s\"", uri->port ); + if ( uri->path ) + DBGC ( uri, " path \"%s\"", uri->path ); + if ( uri->query ) + DBGC ( uri, " query \"%s\"", uri->query ); + if ( uri->fragment ) + DBGC ( uri, " fragment \"%s\"", uri->fragment ); + if ( uri->params ) + DBGC ( uri, " params \"%s\"", uri->params->name ); +} + +/** + * Free URI + * + * @v refcnt Reference count + */ +static void uri_free ( struct refcnt *refcnt ) { + struct uri *uri = container_of ( refcnt, struct uri, refcnt ); + + params_put ( uri->params ); + free ( uri ); +} + +/** + * 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; + struct parameters *params; + char *raw; + char *tmp; + char *path; + char *authority; + size_t raw_len; + unsigned int field; + + /* 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; + ref_init ( &uri->refcnt, uri_free ); + raw = ( ( ( void * ) uri ) + sizeof ( *uri ) ); + + /* Copy in the raw string */ + memcpy ( raw, uri_string, raw_len ); + + /* Identify the parameter list, if present */ + if ( ( tmp = strstr ( raw, "##params" ) ) ) { + *tmp = '\0'; + tmp += 8 /* "##params" */; + params = find_parameters ( *tmp ? ( tmp + 1 ) : NULL ); + if ( params ) { + uri->params = claim_parameters ( params ); + } else { + /* Ignore non-existent submission blocks */ + } + } + + /* Chop 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; + path = NULL; + } + } 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; + } + + /* If we have no path remaining, then we're already finished + * processing. + */ + if ( ! path[0] ) + goto done; + + /* 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; + authority = NULL; + } + + /* If we don't have an authority (i.e. we have a non-net + * path), we're already finished processing + */ + if ( ! authority ) + goto done; + + /* 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 ( ( uri->host[ strlen ( uri->host ) - 1 ] != ']' ) && + ( tmp = strrchr ( uri->host, ':' ) ) ) { + *(tmp++) = '\0'; + uri->port = tmp; + } + + /* Decode fields in-place */ + for ( field = 0 ; field < URI_FIELDS ; field++ ) { + if ( uri_field ( uri, field ) ) + uri_decode ( ( char * ) uri_field ( uri, field ) ); + } + + done: + DBGC ( uri, "URI parsed \"%s\" to", uri_string ); + uri_dump ( uri ); + DBGC ( uri, "\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 ( const struct uri *uri, unsigned int default_port ) { + + if ( ( ! uri ) || ( ! uri->port ) ) + return default_port; + + return ( strtoul ( uri->port, NULL, 0 ) ); +} + +/** + * Format URI + * + * @v uri URI + * @v buf Buffer to fill with URI string + * @v size Size of buffer + * @ret len Length of URI string + */ +size_t format_uri ( const struct uri *uri, char *buf, size_t len ) { + static const char prefixes[URI_FIELDS] = { + [URI_OPAQUE] = ':', + [URI_PASSWORD] = ':', + [URI_PORT] = ':', + [URI_PATH] = '/', + [URI_QUERY] = '?', + [URI_FRAGMENT] = '#', + }; + char prefix; + size_t used = 0; + unsigned int field; + + /* Ensure buffer is NUL-terminated */ + if ( len ) + buf[0] = '\0'; + + /* Special-case NULL URI */ + if ( ! uri ) + return 0; + + /* Generate fields */ + for ( field = 0 ; field < URI_FIELDS ; field++ ) { + + /* Skip non-existent fields */ + if ( ! uri_field ( uri, field ) ) + continue; + + /* Prefix this field, if applicable */ + prefix = prefixes[field]; + if ( ( field == URI_HOST ) && ( uri->user != NULL ) ) + prefix = '@'; + if ( ( field == URI_PATH ) && ( uri->path[0] == '/' ) ) + prefix = '\0'; + if ( prefix ) { + used += ssnprintf ( ( buf + used ), ( len - used ), + "%c", prefix ); + } + + /* Encode this field */ + used += uri_encode ( uri_field ( uri, field ), field, + ( buf + used ), ( len - used ) ); + + /* Suffix this field, if applicable */ + if ( ( field == URI_SCHEME ) && ( ! uri->opaque ) ) { + used += ssnprintf ( ( buf + used ), ( len - used ), + "://" ); + } + } + + if ( len ) { + DBGC ( uri, "URI formatted" ); + uri_dump ( uri ); + DBGC ( uri, " to \"%s%s\"\n", buf, + ( ( used > len ) ? "<TRUNCATED>" : "" ) ); + } + + return used; +} + +/** + * Format URI + * + * @v uri URI + * @ret string URI string, or NULL on failure + * + * The caller is responsible for eventually freeing the allocated + * memory. + */ +char * format_uri_alloc ( const struct uri *uri ) { + size_t len; + char *string; + + len = ( format_uri ( uri, NULL, 0 ) + 1 /* NUL */ ); + string = malloc ( len ); + if ( string ) + format_uri ( uri, string, len ); + return string; +} + +/** + * Copy URI fields + * + * @v src Source URI + * @v dest Destination URI, or NULL to calculate length + * @ret len Length of raw URI + */ +static size_t uri_copy_fields ( const struct uri *src, struct uri *dest ) { + size_t len = sizeof ( *dest ); + char *out = ( ( void * ) dest + len ); + unsigned int field; + size_t field_len; + + /* Copy existent fields */ + for ( field = 0 ; field < URI_FIELDS ; field++ ) { + + /* Skip non-existent fields */ + if ( ! uri_field ( src, field ) ) + continue; + + /* Calculate field length */ + field_len = ( strlen ( uri_field ( src, field ) ) + + 1 /* NUL */ ); + len += field_len; + + /* Copy field, if applicable */ + if ( dest ) { + memcpy ( out, uri_field ( src, field ), field_len ); + uri_field ( dest, field ) = out; + out += field_len; + } + } + return len; +} + +/** + * Duplicate URI + * + * @v uri URI + * @ret uri Duplicate URI + * + * Creates a modifiable copy of a URI. + */ +struct uri * uri_dup ( const struct uri *uri ) { + struct uri *dup; + size_t len; + + /* Allocate new URI */ + len = uri_copy_fields ( uri, NULL ); + dup = zalloc ( len ); + if ( ! dup ) + return NULL; + ref_init ( &dup->refcnt, uri_free ); + + /* Copy fields */ + uri_copy_fields ( uri, dup ); + + /* Copy parameters */ + dup->params = params_get ( uri->params ); + + DBGC ( uri, "URI duplicated" ); + uri_dump ( uri ); + DBGC ( uri, "\n" ); + + return dup; +} + +/** + * 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://ipxe.org/kernels/vmlinuz" and a + * relative URI (e.g. "../initrds/initrd.gz") and produces a new URI + * (e.g. "http://ipxe.org/initrds/initrd.gz"). + */ +struct uri * resolve_uri ( const 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; + tmp_uri.params = relative_uri->params; + } else if ( relative_uri->query ) { + tmp_uri.query = relative_uri->query; + tmp_uri.fragment = relative_uri->fragment; + tmp_uri.params = relative_uri->params; + } else if ( relative_uri->fragment ) { + tmp_uri.fragment = relative_uri->fragment; + tmp_uri.params = relative_uri->params; + } else if ( relative_uri->params ) { + tmp_uri.params = relative_uri->params; + } + + /* Create demangled URI */ + new_uri = uri_dup ( &tmp_uri ); + free ( tmp_path ); + return new_uri; +} + +/** + * Construct TFTP URI from next-server and filename + * + * @v next_server Next-server address + * @v filename Filename + * @ret uri URI, or NULL on failure + * + * TFTP filenames specified via the DHCP next-server field often + * contain characters such as ':' or '#' which would confuse the + * generic URI parser. We provide a mechanism for directly + * constructing a TFTP URI from the next-server and filename. + */ +struct uri * tftp_uri ( struct in_addr next_server, const char *filename ) { + struct uri uri; + + memset ( &uri, 0, sizeof ( uri ) ); + uri.scheme = "tftp"; + uri.host = inet_ntoa ( next_server ); + uri.path = filename; + return uri_dup ( &uri ); +} diff --git a/qemu/roms/ipxe/src/core/uuid.c b/qemu/roms/ipxe/src/core/uuid.c new file mode 100644 index 000000000..27a249da8 --- /dev/null +++ b/qemu/roms/ipxe/src/core/uuid.c @@ -0,0 +1,51 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdint.h> +#include <stdio.h> +#include <byteswap.h> +#include <ipxe/uuid.h> + +/** @file + * + * Universally unique IDs + * + */ + +/** + * Convert UUID to printable string + * + * @v uuid UUID + * @ret string UUID in canonical form + */ +char * uuid_ntoa ( const union uuid *uuid ) { + static char buf[37]; /* "00000000-0000-0000-0000-000000000000" */ + + sprintf ( buf, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", + be32_to_cpu ( uuid->canonical.a ), + be16_to_cpu ( uuid->canonical.b ), + be16_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/qemu/roms/ipxe/src/core/version.c b/qemu/roms/ipxe/src/core/version.c new file mode 100644 index 000000000..1e1e9daca --- /dev/null +++ b/qemu/roms/ipxe/src/core/version.c @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2012 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 (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** @file + * + * Version number + * + */ + +#include <wchar.h> +#include <ipxe/features.h> +#include <ipxe/version.h> +#include <config/general.h> + +/** + * Create wide-character version of string + * + * @v string String + * @ret wstring Wide-character version of string + */ +#define WSTRING( string ) _WSTRING ( string ) +#define _WSTRING( string ) L ## string + +/** Version number feature */ +FEATURE_VERSION ( VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH ); + +/** Build timestamp (generated by linker) */ +extern char _build_timestamp[]; + +/** Build ID (generated by linker) */ +extern char _build_id[]; + +/** Build timestamp */ +unsigned long build_timestamp = ( ( unsigned long ) _build_timestamp ); + +/** Build ID */ +unsigned long build_id = ( ( unsigned long ) _build_id ); + +/** Product major version */ +const int product_major_version = VERSION_MAJOR; + +/** Product minor version */ +const int product_minor_version = VERSION_MINOR; + +/** Product version string */ +const char product_version[] = VERSION; + +/** Product name string */ +const char product_name[] = PRODUCT_NAME; + +/** Product short name string */ +const char product_short_name[] = PRODUCT_SHORT_NAME; + +/** Build name string */ +const char build_name[] = BUILD_NAME; + +/** Wide-character product version string */ +const wchar_t product_wversion[] = WSTRING ( VERSION ); + +/** Wide-character product name string */ +const wchar_t product_wname[] = WSTRING ( PRODUCT_NAME ); + +/** Wide-character product short name string */ +const wchar_t product_short_wname[] = WSTRING ( PRODUCT_SHORT_NAME ); + +/** Wide-character build name string */ +const wchar_t build_wname[] = WSTRING ( BUILD_NAME ); + +/** Copy of build name string within ".prefix" */ +const char build_name_prefix[] __attribute__ (( section ( ".prefix.name" ) )) + = BUILD_NAME; diff --git a/qemu/roms/ipxe/src/core/vsprintf.c b/qemu/roms/ipxe/src/core/vsprintf.c new file mode 100644 index 000000000..54811b11b --- /dev/null +++ b/qemu/roms/ipxe/src/core/vsprintf.c @@ -0,0 +1,466 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stddef.h> +#include <stdarg.h> +#include <stdio.h> +#include <errno.h> +#include <wchar.h> +#include <ipxe/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 + +/** + * Use zero padding + * + * Note that this value is set to 0x10 since that allows the pad + * character to be calculated as @c 0x20|(flags&ZPAD) + */ +#define ZPAD 0x10 + +/** + * Format a hexadecimal number + * + * @v end End of buffer to contain number + * @v num Number to format + * @v width Minimum field width + * @v flags Format flags + * @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 = ( flags & LCASE ); + int pad = ( ( flags & ZPAD ) | ' ' ); + + /* Generate the number */ + do { + *(--ptr) = "0123456789ABCDEF"[ num & 0xf ] | case_mod; + num >>= 4; + } while ( num ); + + /* Pad to width */ + while ( ( end - ptr ) < width ) + *(--ptr) = pad; + + /* 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 + * @v flags Format flags + * @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, + int flags ) { + char *ptr = end; + int negative = 0; + int zpad = ( flags & ZPAD ); + int pad = ( zpad | ' ' ); + + /* Generate the number */ + if ( num < 0 ) { + negative = 1; + num = -num; + } + do { + *(--ptr) = '0' + ( num % 10 ); + num /= 10; + } while ( num ); + + /* Add "-" if necessary */ + if ( negative && ( ! zpad ) ) + *(--ptr) = '-'; + + /* Pad to width */ + while ( ( end - ptr ) < width ) + *(--ptr) = pad; + + /* Add "-" if necessary */ + if ( negative && zpad ) + *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 char 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. */ + wchar_t *wptr; + + /* 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' ) { + flags |= ZPAD; + } 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'; + wptr = NULL; + if ( *fmt == 'c' ) { + if ( length < &type_sizes[LONG_LEN] ) { + cputchar ( ctx, va_arg ( args, unsigned int ) ); + } else { + wchar_t wc; + size_t len; + + wc = va_arg ( args, wint_t ); + len = wcrtomb ( tmp_buf, wc, NULL ); + tmp_buf[len] = '\0'; + ptr = tmp_buf; + } + } else if ( *fmt == 's' ) { + if ( length < &type_sizes[LONG_LEN] ) { + ptr = va_arg ( args, char * ); + } else { + wptr = va_arg ( args, wchar_t * ); + } + if ( ( ptr == NULL ) && ( wptr == NULL ) ) + 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, flags ); + } else { + *(--ptr) = *fmt; + } + /* Write out conversion result */ + if ( wptr == NULL ) { + for ( ; *ptr ; ptr++ ) { + cputchar ( ctx, *ptr ); + } + } else { + for ( ; *wptr ; wptr++ ) { + size_t len = wcrtomb ( tmp_buf, *wptr, NULL ); + for ( ptr = tmp_buf ; len-- ; 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/qemu/roms/ipxe/src/core/wchar.c b/qemu/roms/ipxe/src/core/wchar.c new file mode 100644 index 000000000..7fabca470 --- /dev/null +++ b/qemu/roms/ipxe/src/core/wchar.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2012 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** + * @file + * + * Wide-character strings + * + */ + +#include <wchar.h> + +/** + * Calculate length of wide-character string + * + * @v string String + * @ret len Length (excluding terminating NUL) + */ +size_t wcslen ( const wchar_t *string ) { + size_t len = 0; + + while ( *(string++) ) + len++; + return len; +} diff --git a/qemu/roms/ipxe/src/core/xfer.c b/qemu/roms/ipxe/src/core/xfer.c new file mode 100644 index 000000000..8d4bc9f53 --- /dev/null +++ b/qemu/roms/ipxe/src/core/xfer.c @@ -0,0 +1,367 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <ipxe/iobuf.h> +#include <ipxe/xfer.h> +#include <ipxe/open.h> + +/** @file + * + * Data transfer interfaces + * + */ + +/** + * Dummy transfer metadata + * + * This gets passed to xfer_interface::deliver() and equivalents when + * no metadata is available. + */ +static struct xfer_metadata dummy_metadata; + +/***************************************************************************** + * + * Data transfer interface operations + * + */ + +/** + * Send redirection event + * + * @v intf 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 interface *intf, int type, va_list args ) { + struct interface tmp = INTF_INIT ( null_intf_desc ); + struct interface *dest; + xfer_vredirect_TYPE ( void * ) *op = + intf_get_dest_op_no_passthru ( intf, xfer_vredirect, &dest ); + void *object = intf_object ( dest ); + int rc; + + DBGC ( INTF_COL ( intf ), "INTF " INTF_INTF_FMT " redirect\n", + INTF_INTF_DBG ( intf, dest ) ); + + if ( op ) { + rc = op ( object, type, args ); + } else { + /* Default is to reopen the interface as instructed, + * then send xfer_window_changed() messages to both + * new child and parent interfaces. Since our + * original child interface is likely to be closed and + * unplugged as a result of the call to + * xfer_vreopen(), we create a temporary interface in + * order to be able to send xfer_window_changed() to + * the parent. + */ + intf_plug ( &tmp, dest ); + rc = xfer_vreopen ( dest, type, args ); + if ( rc == 0 ) { + xfer_window_changed ( dest ); + xfer_window_changed ( &tmp ); + } + intf_unplug ( &tmp ); + } + + if ( rc != 0 ) { + DBGC ( INTF_COL ( intf ), "INTF " INTF_INTF_FMT " redirect " + "failed: %s\n", INTF_INTF_DBG ( intf, dest ), + strerror ( rc ) ); + } + + intf_put ( dest ); + return rc; +} + +/** + * Check flow control window + * + * @v intf Data transfer interface + * @ret len Length of window + */ +size_t xfer_window ( struct interface *intf ) { + struct interface *dest; + xfer_window_TYPE ( void * ) *op = + intf_get_dest_op ( intf, xfer_window, &dest ); + void *object = intf_object ( dest ); + size_t len; + + if ( op ) { + len = op ( object ); + } else { + /* Default is to provide an unlimited window */ + len = ~( ( size_t ) 0 ); + } + + intf_put ( dest ); + return len; +} + +/** + * Report change of flow control window + * + * @v intf Data transfer interface + * + * Note that this method is used to indicate only unsolicited changes + * in the flow control window. In particular, this method must not be + * called as part of the response to xfer_deliver(), since that could + * easily lead to an infinite loop. Callers of xfer_deliver() should + * assume that the flow control window will have changed without + * generating an xfer_window_changed() message. + */ +void xfer_window_changed ( struct interface *intf ) { + struct interface *dest; + xfer_window_changed_TYPE ( void * ) *op = + intf_get_dest_op ( intf, xfer_window_changed, &dest ); + void *object = intf_object ( dest ); + + if ( op ) { + op ( object ); + } else { + /* Default is to do nothing */ + } + + intf_put ( dest ); +} + +/** + * Allocate I/O buffer + * + * @v intf Data transfer interface + * @v len I/O buffer payload length + * @ret iobuf I/O buffer + */ +struct io_buffer * xfer_alloc_iob ( struct interface *intf, size_t len ) { + struct interface *dest; + xfer_alloc_iob_TYPE ( void * ) *op = + intf_get_dest_op ( intf, xfer_alloc_iob, &dest ); + void *object = intf_object ( dest ); + struct io_buffer *iobuf; + + DBGC ( INTF_COL ( intf ), "INTF " INTF_INTF_FMT " alloc_iob %zd\n", + INTF_INTF_DBG ( intf, dest ), len ); + + if ( op ) { + iobuf = op ( object, len ); + } else { + /* Default is to allocate an I/O buffer with no + * reserved space. + */ + iobuf = alloc_iob ( len ); + } + + if ( ! iobuf ) { + DBGC ( INTF_COL ( intf ), "INTF " INTF_INTF_FMT " alloc_iob " + "failed\n", INTF_INTF_DBG ( intf, dest ) ); + } + + intf_put ( dest ); + return iobuf; +} + +/** + * Deliver datagram + * + * @v intf Data transfer interface + * @v iobuf Datagram I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + */ +int xfer_deliver ( struct interface *intf, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + struct interface *dest; + xfer_deliver_TYPE ( void * ) *op = + intf_get_dest_op ( intf, xfer_deliver, &dest ); + void *object = intf_object ( dest ); + int rc; + + DBGC ( INTF_COL ( intf ), "INTF " INTF_INTF_FMT " deliver %zd\n", + INTF_INTF_DBG ( intf, dest ), iob_len ( iobuf ) ); + + if ( op ) { + rc = op ( object, iobuf, meta ); + } else { + /* Default is to discard the I/O buffer */ + free_iob ( iobuf ); + rc = -EPIPE; + } + + if ( rc != 0 ) { + DBGC ( INTF_COL ( intf ), "INTF " INTF_INTF_FMT + " deliver failed: %s\n", + INTF_INTF_DBG ( intf, dest ), strerror ( rc ) ); + } + + intf_put ( dest ); + return rc; +} + +/***************************************************************************** + * + * Data transfer interface helper functions + * + */ + +/** + * Send redirection event + * + * @v intf Data transfer interface + * @v type New location type + * @v ... Remaining arguments depend upon location type + * @ret rc Return status code + */ +int xfer_redirect ( struct interface *intf, int type, ... ) { + va_list args; + int rc; + + va_start ( args, type ); + rc = xfer_vredirect ( intf, type, args ); + va_end ( args ); + return rc; +} + +/** + * Deliver datagram as I/O buffer without metadata + * + * @v intf Data transfer interface + * @v iobuf Datagram I/O buffer + * @ret rc Return status code + */ +int xfer_deliver_iob ( struct interface *intf, struct io_buffer *iobuf ) { + return xfer_deliver ( intf, iobuf, &dummy_metadata ); +} + +/** + * Deliver datagram as raw data + * + * @v intf Data transfer interface + * @v data Data + * @v len Length of data + * @v meta Data transfer metadata + * @ret rc Return status code + */ +int xfer_deliver_raw_meta ( struct interface *intf, const void *data, + size_t len, struct xfer_metadata *meta ) { + struct io_buffer *iobuf; + + iobuf = xfer_alloc_iob ( intf, len ); + if ( ! iobuf ) + return -ENOMEM; + + memcpy ( iob_put ( iobuf, len ), data, len ); + return xfer_deliver ( intf, iobuf, meta ); +} + +/** + * Deliver datagram as raw data without metadata + * + * @v intf Data transfer interface + * @v data Data + * @v len Length of data + * @ret rc Return status code + */ +int xfer_deliver_raw ( struct interface *intf, const void *data, size_t len ) { + return xfer_deliver_raw_meta ( intf, data, len, &dummy_metadata ); +} + +/** + * Deliver formatted string + * + * @v intf Data transfer interface + * @v format Format string + * @v args Arguments corresponding to the format string + * @ret rc Return status code + */ +int xfer_vprintf ( struct interface *intf, const char *format, + va_list args ) { + va_list args_tmp; + char *buf; + int len; + int rc; + + /* Create temporary string */ + va_copy ( args_tmp, args ); + len = vasprintf ( &buf, format, args ); + if ( len < 0 ) { + rc = len; + goto err_asprintf; + } + va_end ( args_tmp ); + + /* Transmit string */ + if ( ( rc = xfer_deliver_raw ( intf, buf, len ) ) != 0 ) + goto err_deliver; + + err_deliver: + free ( buf ); + err_asprintf: + return rc; +} + +/** + * Deliver formatted string + * + * @v intf Data transfer interface + * @v format Format string + * @v ... Arguments corresponding to the format string + * @ret rc Return status code + */ +int xfer_printf ( struct interface *intf, const char *format, ... ) { + va_list args; + int rc; + + va_start ( args, format ); + rc = xfer_vprintf ( intf, format, args ); + va_end ( args ); + return rc; +} + +/** + * Seek to position + * + * @v intf Data transfer interface + * @v offset Offset to new position + * @ret rc Return status code + */ +int xfer_seek ( struct interface *intf, off_t offset ) { + struct io_buffer *iobuf; + struct xfer_metadata meta = { + .flags = XFER_FL_ABS_OFFSET, + .offset = offset, + }; + + DBGC ( INTF_COL ( intf ), "INTF " INTF_FMT " seek to %ld\n", + INTF_DBG ( intf ), offset ); + + /* Allocate and send a zero-length data buffer */ + iobuf = xfer_alloc_iob ( intf, 0 ); + if ( ! iobuf ) + return -ENOMEM; + + return xfer_deliver ( intf, iobuf, &meta ); +} diff --git a/qemu/roms/ipxe/src/core/xferbuf.c b/qemu/roms/ipxe/src/core/xferbuf.c new file mode 100644 index 000000000..a0457feee --- /dev/null +++ b/qemu/roms/ipxe/src/core/xferbuf.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2012 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 (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ipxe/xfer.h> +#include <ipxe/iobuf.h> +#include <ipxe/xferbuf.h> + +/** @file + * + * Data transfer buffer + * + */ + +/** + * Finish using data transfer buffer + * + * @v xferbuf Data transfer buffer + */ +void xferbuf_done ( struct xfer_buffer *xferbuf ) { + free ( xferbuf->data ); + xferbuf->data = NULL; + xferbuf->len = 0; + xferbuf->pos = 0; +} + +/** + * Ensure that data transfer buffer is large enough for the specified size + * + * @v xferbuf Data transfer buffer + * @v len Required minimum size + * @ret rc Return status code + */ +static int xferbuf_ensure_size ( struct xfer_buffer *xferbuf, size_t len ) { + void *new_data; + + /* If buffer is already large enough, do nothing */ + if ( len <= xferbuf->len ) + return 0; + + /* Extend buffer */ + new_data = realloc ( xferbuf->data, len ); + if ( ! new_data ) { + DBGC ( xferbuf, "XFERBUF %p could not extend buffer to " + "%zd bytes\n", xferbuf, len ); + return -ENOSPC; + } + xferbuf->data = new_data; + xferbuf->len = len; + + return 0; +} + +/** + * Add received data to data transfer buffer + * + * @v xferbuf Data transfer buffer + * @v iobuf I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + */ +int xferbuf_deliver ( struct xfer_buffer *xferbuf, struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + size_t len; + size_t max; + int rc; + + /* Calculate new buffer position */ + if ( meta->flags & XFER_FL_ABS_OFFSET ) + xferbuf->pos = 0; + xferbuf->pos += meta->offset; + + /* Ensure that we have enough buffer space for this data */ + len = iob_len ( iobuf ); + max = ( xferbuf->pos + len ); + if ( ( rc = xferbuf_ensure_size ( xferbuf, max ) ) != 0 ) + goto done; + + /* Copy data to buffer */ + memcpy ( ( xferbuf->data + xferbuf->pos ), iobuf->data, len ); + + /* Update current buffer position */ + xferbuf->pos += len; + + done: + free_iob ( iobuf ); + return rc; +} |