summaryrefslogtreecommitdiffstats
path: root/qemu/roms/ipxe/src/core
diff options
context:
space:
mode:
authorYang Zhang <yang.z.zhang@intel.com>2015-08-28 09:58:54 +0800
committerYang Zhang <yang.z.zhang@intel.com>2015-09-01 12:44:00 +0800
commite44e3482bdb4d0ebde2d8b41830ac2cdb07948fb (patch)
tree66b09f592c55df2878107a468a91d21506104d3f /qemu/roms/ipxe/src/core
parent9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 (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')
-rw-r--r--qemu/roms/ipxe/src/core/acpi.c86
-rw-r--r--qemu/roms/ipxe/src/core/ansicol.c122
-rw-r--r--qemu/roms/ipxe/src/core/ansicoldef.c191
-rw-r--r--qemu/roms/ipxe/src/core/ansiesc.c120
-rw-r--r--qemu/roms/ipxe/src/core/asprintf.c49
-rw-r--r--qemu/roms/ipxe/src/core/assert.c31
-rw-r--r--qemu/roms/ipxe/src/core/base16.c135
-rw-r--r--qemu/roms/ipxe/src/core/base64.c161
-rw-r--r--qemu/roms/ipxe/src/core/basename.c65
-rw-r--r--qemu/roms/ipxe/src/core/bitmap.c102
-rw-r--r--qemu/roms/ipxe/src/core/blockdev.c139
-rw-r--r--qemu/roms/ipxe/src/core/console.c164
-rw-r--r--qemu/roms/ipxe/src/core/cpio.c43
-rw-r--r--qemu/roms/ipxe/src/core/ctype.c49
-rw-r--r--qemu/roms/ipxe/src/core/cwuri.c49
-rw-r--r--qemu/roms/ipxe/src/core/debug.c203
-rw-r--r--qemu/roms/ipxe/src/core/debug_md5.c48
-rw-r--r--qemu/roms/ipxe/src/core/device.c136
-rw-r--r--qemu/roms/ipxe/src/core/downloader.c279
-rw-r--r--qemu/roms/ipxe/src/core/edd.c57
-rw-r--r--qemu/roms/ipxe/src/core/errno.c20
-rw-r--r--qemu/roms/ipxe/src/core/exec.c601
-rw-r--r--qemu/roms/ipxe/src/core/fbcon.c692
-rw-r--r--qemu/roms/ipxe/src/core/fnrec.c203
-rw-r--r--qemu/roms/ipxe/src/core/gdbserial.c49
-rw-r--r--qemu/roms/ipxe/src/core/gdbstub.c401
-rw-r--r--qemu/roms/ipxe/src/core/gdbudp.c260
-rw-r--r--qemu/roms/ipxe/src/core/getkey.c87
-rw-r--r--qemu/roms/ipxe/src/core/getopt.c279
-rw-r--r--qemu/roms/ipxe/src/core/hw.c69
-rw-r--r--qemu/roms/ipxe/src/core/i82365.c656
-rw-r--r--qemu/roms/ipxe/src/core/image.c484
-rw-r--r--qemu/roms/ipxe/src/core/init.c103
-rw-r--r--qemu/roms/ipxe/src/core/interface.c309
-rw-r--r--qemu/roms/ipxe/src/core/iobuf.c202
-rw-r--r--qemu/roms/ipxe/src/core/isqrt.c52
-rw-r--r--qemu/roms/ipxe/src/core/job.c63
-rw-r--r--qemu/roms/ipxe/src/core/linebuf.c112
-rw-r--r--qemu/roms/ipxe/src/core/lineconsole.c67
-rw-r--r--qemu/roms/ipxe/src/core/list.c84
-rw-r--r--qemu/roms/ipxe/src/core/log.c63
-rw-r--r--qemu/roms/ipxe/src/core/main.c43
-rw-r--r--qemu/roms/ipxe/src/core/malloc.c650
-rw-r--r--qemu/roms/ipxe/src/core/memblock.c81
-rw-r--r--qemu/roms/ipxe/src/core/memmap_settings.c240
-rw-r--r--qemu/roms/ipxe/src/core/menu.c178
-rw-r--r--qemu/roms/ipxe/src/core/misc.c85
-rw-r--r--qemu/roms/ipxe/src/core/monojob.c152
-rw-r--r--qemu/roms/ipxe/src/core/null_nap.c3
-rw-r--r--qemu/roms/ipxe/src/core/null_reboot.c55
-rw-r--r--qemu/roms/ipxe/src/core/null_sanboot.c46
-rw-r--r--qemu/roms/ipxe/src/core/null_time.c30
-rw-r--r--qemu/roms/ipxe/src/core/nvo.c324
-rw-r--r--qemu/roms/ipxe/src/core/open.c228
-rw-r--r--qemu/roms/ipxe/src/core/params.c153
-rw-r--r--qemu/roms/ipxe/src/core/parseopt.c449
-rw-r--r--qemu/roms/ipxe/src/core/pc_kbd.c112
-rw-r--r--qemu/roms/ipxe/src/core/pcmcia.c269
-rw-r--r--qemu/roms/ipxe/src/core/pending.c62
-rw-r--r--qemu/roms/ipxe/src/core/pinger.c351
-rw-r--r--qemu/roms/ipxe/src/core/pixbuf.c75
-rw-r--r--qemu/roms/ipxe/src/core/posix_io.c339
-rw-r--r--qemu/roms/ipxe/src/core/process.c133
-rw-r--r--qemu/roms/ipxe/src/core/profile.c272
-rw-r--r--qemu/roms/ipxe/src/core/random.c41
-rw-r--r--qemu/roms/ipxe/src/core/refcnt.c99
-rw-r--r--qemu/roms/ipxe/src/core/resolv.c423
-rw-r--r--qemu/roms/ipxe/src/core/serial.c259
-rw-r--r--qemu/roms/ipxe/src/core/serial_console.c42
-rw-r--r--qemu/roms/ipxe/src/core/settings.c2556
-rw-r--r--qemu/roms/ipxe/src/core/string.c353
-rw-r--r--qemu/roms/ipxe/src/core/stringextra.c275
-rw-r--r--qemu/roms/ipxe/src/core/strtoull.c60
-rw-r--r--qemu/roms/ipxe/src/core/time.c138
-rw-r--r--qemu/roms/ipxe/src/core/timer.c43
-rw-r--r--qemu/roms/ipxe/src/core/uri.c680
-rw-r--r--qemu/roms/ipxe/src/core/uuid.c51
-rw-r--r--qemu/roms/ipxe/src/core/version.c89
-rw-r--r--qemu/roms/ipxe/src/core/vsprintf.c466
-rw-r--r--qemu/roms/ipxe/src/core/wchar.c43
-rw-r--r--qemu/roms/ipxe/src/core/xfer.c367
-rw-r--r--qemu/roms/ipxe/src/core/xferbuf.c108
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, &params->entries, list ) {
+ list_del ( &param->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, &parameters, 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 ( &params->refcnt, free_parameters );
+ name_copy = ( ( void * ) ( params + 1 ) );
+
+ /* Populate parameter list */
+ if ( name ) {
+ strcpy ( name_copy, name );
+ params->name = name_copy;
+ }
+ INIT_LIST_HEAD ( &params->entries );
+
+ /* Add to list of parameter lists */
+ list_add_tail ( &params->list, &parameters );
+
+ 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 ( &param->list, &params->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;
+}