summaryrefslogtreecommitdiffstats
path: root/qemu/roms/ipxe/src/arch/x86/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/arch/x86/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/arch/x86/core')
-rw-r--r--qemu/roms/ipxe/src/arch/x86/core/cpuid.c137
-rw-r--r--qemu/roms/ipxe/src/arch/x86/core/cpuid_settings.c274
-rw-r--r--qemu/roms/ipxe/src/arch/x86/core/debugcon.c86
-rw-r--r--qemu/roms/ipxe/src/arch/x86/core/linux/linux_api.c110
-rw-r--r--qemu/roms/ipxe/src/arch/x86/core/linux/linux_strerror.c169
-rw-r--r--qemu/roms/ipxe/src/arch/x86/core/pcidirect.c48
-rw-r--r--qemu/roms/ipxe/src/arch/x86/core/x86_bigint.c91
-rw-r--r--qemu/roms/ipxe/src/arch/x86/core/x86_io.c102
-rw-r--r--qemu/roms/ipxe/src/arch/x86/core/x86_string.c190
-rw-r--r--qemu/roms/ipxe/src/arch/x86/core/x86_tcpip.c169
10 files changed, 1376 insertions, 0 deletions
diff --git a/qemu/roms/ipxe/src/arch/x86/core/cpuid.c b/qemu/roms/ipxe/src/arch/x86/core/cpuid.c
new file mode 100644
index 000000000..5908f4419
--- /dev/null
+++ b/qemu/roms/ipxe/src/arch/x86/core/cpuid.c
@@ -0,0 +1,137 @@
+/*
+ * 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 <string.h>
+#include <ipxe/cpuid.h>
+
+/** @file
+ *
+ * x86 CPU feature detection
+ *
+ */
+
+/**
+ * Check whether or not CPUID instruction is supported
+ *
+ * @ret is_supported CPUID instruction is supported
+ */
+int cpuid_is_supported ( void ) {
+ unsigned long original;
+ unsigned long inverted;
+
+ __asm__ ( "pushf\n\t"
+ "pushf\n\t"
+ "pop %0\n\t"
+ "mov %0,%1\n\t"
+ "xor %2,%1\n\t"
+ "push %1\n\t"
+ "popf\n\t"
+ "pushf\n\t"
+ "pop %1\n\t"
+ "popf\n\t"
+ : "=&r" ( original ), "=&r" ( inverted )
+ : "ir" ( CPUID_FLAG ) );
+ return ( ( original ^ inverted ) & CPUID_FLAG );
+}
+
+/**
+ * Get Intel-defined x86 CPU features
+ *
+ * @v features x86 CPU features to fill in
+ */
+static void x86_intel_features ( struct x86_features *features ) {
+ uint32_t max_level;
+ uint32_t discard_a;
+ uint32_t discard_b;
+ uint32_t discard_c;
+ uint32_t discard_d;
+
+ /* Check that features are available via CPUID */
+ cpuid ( CPUID_VENDOR_ID, &max_level, &discard_b, &discard_c,
+ &discard_d );
+ if ( max_level < CPUID_FEATURES ) {
+ DBGC ( features, "CPUID has no Intel-defined features (max "
+ "level %08x)\n", max_level );
+ return;
+ }
+
+ /* Get features */
+ cpuid ( CPUID_FEATURES, &discard_a, &discard_b,
+ &features->intel.ecx, &features->intel.edx );
+ DBGC ( features, "CPUID Intel features: %%ecx=%08x, %%edx=%08x\n",
+ features->intel.ecx, features->intel.edx );
+
+}
+
+/**
+ * Get AMD-defined x86 CPU features
+ *
+ * @v features x86 CPU features to fill in
+ */
+static void x86_amd_features ( struct x86_features *features ) {
+ uint32_t max_level;
+ uint32_t discard_a;
+ uint32_t discard_b;
+ uint32_t discard_c;
+ uint32_t discard_d;
+
+ /* Check that features are available via CPUID */
+ cpuid ( CPUID_AMD_MAX_FN, &max_level, &discard_b, &discard_c,
+ &discard_d );
+ if ( ( max_level & CPUID_AMD_CHECK_MASK ) != CPUID_AMD_CHECK ) {
+ DBGC ( features, "CPUID has no extended functions\n" );
+ return;
+ }
+ if ( max_level < CPUID_AMD_FEATURES ) {
+ DBGC ( features, "CPUID has no AMD-defined features (max "
+ "level %08x)\n", max_level );
+ return;
+ }
+
+ /* Get features */
+ cpuid ( CPUID_AMD_FEATURES, &discard_a, &discard_b,
+ &features->amd.ecx, &features->amd.edx );
+ DBGC ( features, "CPUID AMD features: %%ecx=%08x, %%edx=%08x\n",
+ features->amd.ecx, features->amd.edx );
+}
+
+/**
+ * Get x86 CPU features
+ *
+ * @v features x86 CPU features to fill in
+ */
+void x86_features ( struct x86_features *features ) {
+
+ /* Clear all features */
+ memset ( features, 0, sizeof ( *features ) );
+
+ /* Check that CPUID instruction is available */
+ if ( ! cpuid_is_supported() ) {
+ DBGC ( features, "CPUID instruction is not supported\n" );
+ return;
+ }
+
+ /* Get Intel-defined features */
+ x86_intel_features ( features );
+
+ /* Get AMD-defined features */
+ x86_amd_features ( features );
+}
diff --git a/qemu/roms/ipxe/src/arch/x86/core/cpuid_settings.c b/qemu/roms/ipxe/src/arch/x86/core/cpuid_settings.c
new file mode 100644
index 000000000..42dea9336
--- /dev/null
+++ b/qemu/roms/ipxe/src/arch/x86/core/cpuid_settings.c
@@ -0,0 +1,274 @@
+/*
+ * 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/cpuid.h>
+
+/** @file
+ *
+ * x86 CPUID settings
+ *
+ * CPUID settings are numerically encoded as:
+ *
+ * Bit 31 Extended function
+ * Bits 30-28 Unused
+ * Bits 27-24 Number of consecutive functions to call, minus one
+ * Bit 23 Return result as little-endian (used for strings)
+ * Bits 22-18 Unused
+ * Bits 17-16 Number of registers in register array, minus one
+ * Bits 15-8 Array of register indices. First entry in array is in
+ * bits 9-8. Indices are 0-%eax, 1-%ebx, 2-%ecx, 3-%edx.
+ * Bits 7-0 Starting function number (excluding "extended" bit)
+ *
+ * This encoding scheme is designed to allow the common case of
+ * extracting a single register from a single function to be encoded
+ * using "cpuid/<register>.<function>", e.g. "cpuid/2.0x80000001" to
+ * retrieve the value of %ecx from calling CPUID with %eax=0x80000001.
+ */
+
+/** CPUID setting tag register indices */
+enum cpuid_registers {
+ CPUID_EAX = 0,
+ CPUID_EBX = 1,
+ CPUID_ECX = 2,
+ CPUID_EDX = 3,
+};
+
+/**
+ * Construct CPUID setting tag
+ *
+ * @v function Starting function number
+ * @v num_functions Number of consecutive functions
+ * @v little_endian Return result as little-endian
+ * @v num_registers Number of registers in register array
+ * @v register1 First register in register array (or zero, if empty)
+ * @v register2 Second register in register array (or zero, if empty)
+ * @v register3 Third register in register array (or zero, if empty)
+ * @v register4 Fourth register in register array (or zero, if empty)
+ * @ret tag Setting tag
+ */
+#define CPUID_TAG( function, num_functions, little_endian, num_registers, \
+ register1, register2, register3, register4 ) \
+ ( (function) | ( ( (num_functions) - 1 ) << 24 ) | \
+ ( (little_endian) << 23 ) | ( ( (num_registers) - 1) << 16 ) | \
+ ( (register1) << 8 ) | ( (register2) << 10 ) | \
+ ( (register3) << 12 ) | ( (register4) << 14 ) )
+
+/**
+ * Extract endianness from CPUID setting tag
+ *
+ * @v tag Setting tag
+ * @ret little_endian Result should be returned as little-endian
+ */
+#define CPUID_LITTLE_ENDIAN( tag ) ( (tag) & 0x00800000UL )
+
+/**
+ * Extract starting function number from CPUID setting tag
+ *
+ * @v tag Setting tag
+ * @ret function Starting function number
+ */
+#define CPUID_FUNCTION( tag ) ( (tag) & 0x800000ffUL )
+
+/**
+ * Extract number of consecutive functions from CPUID setting tag
+ *
+ * @v tag Setting tag
+ * @ret num_functions Number of consecutive functions
+ */
+#define CPUID_NUM_FUNCTIONS( tag ) ( ( ( (tag) >> 24 ) & 0xf ) + 1 )
+
+/**
+ * Extract register array from CPUID setting tag
+ *
+ * @v tag Setting tag
+ * @ret registers Register array
+ */
+#define CPUID_REGISTERS( tag ) ( ( (tag) >> 8 ) & 0xff )
+
+/**
+ * Extract number of registers from CPUID setting tag
+ *
+ * @v tag Setting tag
+ * @ret num_registers Number of registers within register array
+ */
+#define CPUID_NUM_REGISTERS( tag ) ( ( ( (tag) >> 16 ) & 0x3 ) + 1 )
+
+/** CPUID settings scope */
+static const struct settings_scope cpuid_settings_scope;
+
+/**
+ * Check applicability of CPUID setting
+ *
+ * @v settings Settings block
+ * @v setting Setting
+ * @ret applies Setting applies within this settings block
+ */
+static int cpuid_settings_applies ( struct settings *settings __unused,
+ const struct setting *setting ) {
+
+ return ( setting->scope == &cpuid_settings_scope );
+}
+
+/**
+ * Fetch value of CPUID 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 cpuid_settings_fetch ( struct settings *settings,
+ struct setting *setting,
+ void *data, size_t len ) {
+ uint32_t function;
+ uint32_t max_function;
+ uint32_t num_functions;
+ uint32_t registers;
+ uint32_t num_registers;
+ uint32_t buf[4];
+ uint32_t output;
+ uint32_t discard_b;
+ uint32_t discard_c;
+ uint32_t discard_d;
+ size_t frag_len;
+ size_t result_len = 0;
+
+ /* Fail unless CPUID is supported */
+ if ( ! cpuid_is_supported() ) {
+ DBGC ( settings, "CPUID not supported\n" );
+ return -ENOTSUP;
+ }
+
+ /* Find highest supported function number within this set */
+ function = CPUID_FUNCTION ( setting->tag );
+ cpuid ( function & CPUID_EXTENDED, &max_function, &discard_b,
+ &discard_c, &discard_d );
+
+ /* Fail if maximum function number is meaningless (e.g. if we
+ * are attempting to call an extended function on a CPU which
+ * does not support them).
+ */
+ if ( ( max_function & CPUID_AMD_CHECK_MASK ) !=
+ ( function & CPUID_AMD_CHECK_MASK ) ) {
+ DBGC ( settings, "CPUID invalid maximum function\n" );
+ return -ENOTSUP;
+ }
+
+ /* Call each function in turn */
+ num_functions = CPUID_NUM_FUNCTIONS ( setting->tag );
+ for ( ; num_functions-- ; function++ ) {
+
+ /* Fail if this function is not supported */
+ if ( function > max_function ) {
+ DBGC ( settings, "CPUID function %#08x not supported\n",
+ function );
+ return -ENOTSUP;
+ }
+
+ /* Issue CPUID */
+ cpuid ( function, &buf[CPUID_EAX], &buf[CPUID_EBX],
+ &buf[CPUID_ECX], &buf[CPUID_EDX] );
+ DBGC ( settings, "CPUID %#08x => %#08x:%#08x:%#08x:%#08x\n",
+ function, buf[0], buf[1], buf[2], buf[3] );
+
+ /* Copy results to buffer */
+ registers = CPUID_REGISTERS ( setting->tag );
+ num_registers = CPUID_NUM_REGISTERS ( setting->tag );
+ for ( ; num_registers-- ; registers >>= 2 ) {
+ output = buf[ registers & 0x3 ];
+ if ( ! CPUID_LITTLE_ENDIAN ( setting->tag ) )
+ output = cpu_to_be32 ( output );
+ frag_len = sizeof ( output );
+ if ( frag_len > len )
+ frag_len = len;
+ memcpy ( data, &output, frag_len );
+ data += frag_len;
+ len -= frag_len;
+ result_len += sizeof ( output );
+ }
+ }
+
+ /* Set type if not already specified */
+ if ( ! setting->type )
+ setting->type = &setting_type_hexraw;
+
+ return result_len;
+}
+
+/** CPUID settings operations */
+static struct settings_operations cpuid_settings_operations = {
+ .applies = cpuid_settings_applies,
+ .fetch = cpuid_settings_fetch,
+};
+
+/** CPUID settings */
+static struct settings cpuid_settings = {
+ .refcnt = NULL,
+ .siblings = LIST_HEAD_INIT ( cpuid_settings.siblings ),
+ .children = LIST_HEAD_INIT ( cpuid_settings.children ),
+ .op = &cpuid_settings_operations,
+ .default_scope = &cpuid_settings_scope,
+};
+
+/** Initialise CPUID settings */
+static void cpuid_settings_init ( void ) {
+ int rc;
+
+ if ( ( rc = register_settings ( &cpuid_settings, NULL,
+ "cpuid" ) ) != 0 ) {
+ DBG ( "CPUID could not register settings: %s\n",
+ strerror ( rc ) );
+ return;
+ }
+}
+
+/** CPUID settings initialiser */
+struct init_fn cpuid_settings_init_fn __init_fn ( INIT_NORMAL ) = {
+ .initialise = cpuid_settings_init,
+};
+
+/** CPU vendor setting */
+const struct setting cpuvendor_setting __setting ( SETTING_HOST_EXTRA,
+ cpuvendor ) = {
+ .name = "cpuvendor",
+ .description = "CPU vendor",
+ .tag = CPUID_TAG ( CPUID_VENDOR_ID, 1, 1, 3,
+ CPUID_EBX, CPUID_EDX, CPUID_ECX, 0 ),
+ .type = &setting_type_string,
+ .scope = &cpuid_settings_scope,
+};
+
+/** CPU model setting */
+const struct setting cpumodel_setting __setting ( SETTING_HOST_EXTRA,
+ cpumodel ) = {
+ .name = "cpumodel",
+ .description = "CPU model",
+ .tag = CPUID_TAG ( CPUID_MODEL, 3, 1, 4,
+ CPUID_EAX, CPUID_EBX, CPUID_ECX, CPUID_EDX ),
+ .type = &setting_type_string,
+ .scope = &cpuid_settings_scope,
+};
diff --git a/qemu/roms/ipxe/src/arch/x86/core/debugcon.c b/qemu/roms/ipxe/src/arch/x86/core/debugcon.c
new file mode 100644
index 000000000..263cb4af1
--- /dev/null
+++ b/qemu/roms/ipxe/src/arch/x86/core/debugcon.c
@@ -0,0 +1,86 @@
+/*
+ * 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
+ *
+ * Debug port console
+ *
+ * The debug port is supported by bochs (via the "port_e9_hack"
+ * configuration file directive) and by qemu (via the "-debugcon"
+ * command-line option).
+ */
+
+#include <stdint.h>
+#include <ipxe/io.h>
+#include <ipxe/console.h>
+#include <ipxe/init.h>
+#include <config/console.h>
+
+/** Debug port */
+#define DEBUG_PORT 0xe9
+
+/** Debug port installation check magic value */
+#define DEBUG_PORT_CHECK 0xe9
+
+/* Set default console usage if applicable */
+#if ! ( defined ( CONSOLE_DEBUGCON ) && CONSOLE_EXPLICIT ( CONSOLE_DEBUGCON ) )
+#undef CONSOLE_DEBUGCON
+#define CONSOLE_DEBUGCON ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_TUI )
+#endif
+
+/**
+ * Print a character to debug port console
+ *
+ * @v character Character to be printed
+ */
+static void debugcon_putchar ( int character ) {
+
+ /* Write character to debug port */
+ outb ( character, DEBUG_PORT );
+}
+
+/** Debug port console driver */
+struct console_driver debugcon_console __console_driver = {
+ .putchar = debugcon_putchar,
+ .usage = CONSOLE_DEBUGCON,
+};
+
+/**
+ * Initialise debug port console
+ *
+ */
+static void debugcon_init ( void ) {
+ uint8_t check;
+
+ /* Check if console is present */
+ check = inb ( DEBUG_PORT );
+ if ( check != DEBUG_PORT_CHECK ) {
+ DBG ( "Debug port not present; disabling console\n" );
+ debugcon_console.disabled = CONSOLE_DISABLED;
+ }
+}
+
+/**
+ * Debug port console initialisation function
+ */
+struct init_fn debugcon_init_fn __init_fn ( INIT_EARLY ) = {
+ .initialise = debugcon_init,
+};
diff --git a/qemu/roms/ipxe/src/arch/x86/core/linux/linux_api.c b/qemu/roms/ipxe/src/arch/x86/core/linux/linux_api.c
new file mode 100644
index 000000000..0bed9fd57
--- /dev/null
+++ b/qemu/roms/ipxe/src/arch/x86/core/linux/linux_api.c
@@ -0,0 +1,110 @@
+/*
+ * 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 );
+
+/** @file
+ *
+ * Implementation of most of the linux API.
+ */
+
+#include <linux_api.h>
+
+#include <stdarg.h>
+#include <asm/unistd.h>
+#include <string.h>
+
+int linux_open ( const char *pathname, int flags ) {
+ return linux_syscall ( __NR_open, pathname, flags );
+}
+
+int linux_close ( int fd ) {
+ return linux_syscall ( __NR_close, fd );
+}
+
+off_t linux_lseek ( int fd, off_t offset, int whence ) {
+ return linux_syscall ( __NR_lseek, fd, offset, whence );
+}
+
+__kernel_ssize_t linux_read ( int fd, void *buf, __kernel_size_t count ) {
+ return linux_syscall ( __NR_read, fd, buf, count );
+}
+
+__kernel_ssize_t linux_write ( int fd, const void *buf,
+ __kernel_size_t count ) {
+ return linux_syscall ( __NR_write, fd, buf, count );
+}
+
+int linux_fcntl ( int fd, int cmd, ... ) {
+ long arg;
+ va_list list;
+
+ va_start ( list, cmd );
+ arg = va_arg ( list, long );
+ va_end ( list );
+
+ return linux_syscall ( __NR_fcntl, fd, cmd, arg );
+}
+
+int linux_ioctl ( int fd, int request, ... ) {
+ void *arg;
+ va_list list;
+
+ va_start ( list, request );
+ arg = va_arg ( list, void * );
+ va_end ( list );
+
+ return linux_syscall ( __NR_ioctl, fd, request, arg );
+}
+
+int linux_poll ( struct pollfd *fds, nfds_t nfds, int timeout ) {
+ return linux_syscall ( __NR_poll, fds, nfds, timeout );
+}
+
+int linux_nanosleep ( const struct timespec *req, struct timespec *rem ) {
+ return linux_syscall ( __NR_nanosleep, req, rem );
+}
+
+int linux_usleep ( useconds_t usec ) {
+ struct timespec ts = {
+ .tv_sec = ( ( long ) ( usec / 1000000 ) ),
+ .tv_nsec = ( ( long ) ( usec % 1000000 ) * 1000UL ),
+ };
+
+ return linux_nanosleep ( &ts, NULL );
+}
+
+int linux_gettimeofday ( struct timeval *tv, struct timezone *tz ) {
+ return linux_syscall ( __NR_gettimeofday, tv, tz );
+}
+
+void * linux_mmap ( void *addr, __kernel_size_t length, int prot, int flags,
+ int fd, __kernel_off_t offset ) {
+ return ( void * ) linux_syscall ( __SYSCALL_mmap, addr, length, prot,
+ flags, fd, offset );
+}
+
+void * linux_mremap ( void *old_address, __kernel_size_t old_size,
+ __kernel_size_t new_size, int flags ) {
+ return ( void * ) linux_syscall ( __NR_mremap, old_address, old_size,
+ new_size, flags );
+}
+
+int linux_munmap ( void *addr, __kernel_size_t length ) {
+ return linux_syscall ( __NR_munmap, addr, length );
+}
diff --git a/qemu/roms/ipxe/src/arch/x86/core/linux/linux_strerror.c b/qemu/roms/ipxe/src/arch/x86/core/linux/linux_strerror.c
new file mode 100644
index 000000000..24c9b7738
--- /dev/null
+++ b/qemu/roms/ipxe/src/arch/x86/core/linux/linux_strerror.c
@@ -0,0 +1,169 @@
+/*
+ * 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);
+
+/** @file
+ *
+ * linux_strerror implementation
+ */
+
+#include <linux_api.h>
+#include <stdio.h>
+
+/** Error names from glibc */
+static const char *errors[] = {
+ "Success",
+ "Operation not permitted",
+ "No such file or directory",
+ "No such process",
+ "Interrupted system call",
+ "Input/output error",
+ "No such device or address",
+ "Argument list too long",
+ "Exec format error",
+ "Bad file descriptor",
+ "No child processes",
+ "Resource temporarily unavailable",
+ "Cannot allocate memory",
+ "Permission denied",
+ "Bad address",
+ "Block device required",
+ "Device or resource busy",
+ "File exists",
+ "Invalid cross-device link",
+ "No such device",
+ "Not a directory",
+ "Is a directory",
+ "Invalid argument",
+ "Too many open files in system",
+ "Too many open files",
+ "Inappropriate ioctl for device",
+ "Text file busy",
+ "File too large",
+ "No space left on device",
+ "Illegal seek",
+ "Read-only file system",
+ "Too many links",
+ "Broken pipe",
+ "Numerical argument out of domain",
+ "Numerical result out of range",
+ "Resource deadlock avoided",
+ "File name too long",
+ "No locks available",
+ "Function not implemented",
+ "Directory not empty",
+ "Too many levels of symbolic links",
+ "",
+ "No message of desired type",
+ "Identifier removed",
+ "Channel number out of range",
+ "Level 2 not synchronized",
+ "Level 3 halted",
+ "Level 3 reset",
+ "Link number out of range",
+ "Protocol driver not attached",
+ "No CSI structure available",
+ "Level 2 halted",
+ "Invalid exchange",
+ "Invalid request descriptor",
+ "Exchange full",
+ "No anode",
+ "Invalid request code",
+ "Invalid slot",
+ "",
+ "Bad font file format",
+ "Device not a stream",
+ "No data available",
+ "Timer expired",
+ "Out of streams resources",
+ "Machine is not on the network",
+ "Package not installed",
+ "Object is remote",
+ "Link has been severed",
+ "Advertise error",
+ "Srmount error",
+ "Communication error on send",
+ "Protocol error",
+ "Multihop attempted",
+ "RFS specific error",
+ "Bad message",
+ "Value too large for defined data type",
+ "Name not unique on network",
+ "File descriptor in bad state",
+ "Remote address changed",
+ "Can not access a needed shared library",
+ "Accessing a corrupted shared library",
+ ".lib section in a.out corrupted",
+ "Attempting to link in too many shared libraries",
+ "Cannot exec a shared library directly",
+ "Invalid or incomplete multibyte or wide character",
+ "Interrupted system call should be restarted",
+ "Streams pipe error",
+ "Too many users",
+ "Socket operation on non-socket",
+ "Destination address required",
+ "Message too long",
+ "Protocol wrong type for socket",
+ "Protocol not available",
+ "Protocol not supported",
+ "Socket type not supported",
+ "Operation not supported",
+ "Protocol family not supported",
+ "Address family not supported by protocol",
+ "Address already in use",
+ "Cannot assign requested address",
+ "Network is down",
+ "Network is unreachable",
+ "Network dropped connection on reset",
+ "Software caused connection abort",
+ "Connection reset by peer",
+ "No buffer space available",
+ "Transport endpoint is already connected",
+ "Transport endpoint is not connected",
+ "Cannot send after transport endpoint shutdown",
+ "Too many references: cannot splice",
+ "Connection timed out",
+ "Connection refused",
+ "Host is down",
+ "No route to host",
+ "Operation already in progress",
+ "Operation now in progress",
+ "Stale NFS file handle",
+ "Structure needs cleaning",
+ "Not a XENIX named type file",
+ "No XENIX semaphores available",
+ "Is a named type file",
+ "Remote I/O error",
+ "Disk quota exceeded",
+ "No medium found",
+ "Wrong medium type",
+};
+
+const char *linux_strerror(int errnum)
+{
+ static char errbuf[64];
+ static int errors_size = sizeof(errors) / sizeof(*errors);
+
+ if (errnum >= errors_size || errnum < 0) {
+ snprintf(errbuf, sizeof(errbuf), "Error %#08x", errnum);
+ return errbuf;
+ } else {
+ return errors[errnum];
+ }
+}
diff --git a/qemu/roms/ipxe/src/arch/x86/core/pcidirect.c b/qemu/roms/ipxe/src/arch/x86/core/pcidirect.c
new file mode 100644
index 000000000..dbc8317b8
--- /dev/null
+++ b/qemu/roms/ipxe/src/arch/x86/core/pcidirect.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 <ipxe/io.h>
+#include <ipxe/pci.h>
+
+/** @file
+ *
+ * PCI configuration space access via Type 1 accesses
+ *
+ */
+
+/**
+ * Prepare for Type 1 PCI configuration space access
+ *
+ * @v pci PCI device
+ * @v where Location within PCI configuration space
+ */
+void pcidirect_prepare ( struct pci_device *pci, int where ) {
+ outl ( ( 0x80000000 | ( pci->busdevfn << 8 ) | ( where & ~3 ) ),
+ PCIDIRECT_CONFIG_ADDRESS );
+}
+
+PROVIDE_PCIAPI_INLINE ( direct, pci_num_bus );
+PROVIDE_PCIAPI_INLINE ( direct, pci_read_config_byte );
+PROVIDE_PCIAPI_INLINE ( direct, pci_read_config_word );
+PROVIDE_PCIAPI_INLINE ( direct, pci_read_config_dword );
+PROVIDE_PCIAPI_INLINE ( direct, pci_write_config_byte );
+PROVIDE_PCIAPI_INLINE ( direct, pci_write_config_word );
+PROVIDE_PCIAPI_INLINE ( direct, pci_write_config_dword );
diff --git a/qemu/roms/ipxe/src/arch/x86/core/x86_bigint.c b/qemu/roms/ipxe/src/arch/x86/core/x86_bigint.c
new file mode 100644
index 000000000..418ac2309
--- /dev/null
+++ b/qemu/roms/ipxe/src/arch/x86/core/x86_bigint.c
@@ -0,0 +1,91 @@
+/*
+ * 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 <stdint.h>
+#include <string.h>
+#include <ipxe/bigint.h>
+
+/** @file
+ *
+ * Big integer support
+ */
+
+/**
+ * Multiply big integers
+ *
+ * @v multiplicand0 Element 0 of big integer to be multiplied
+ * @v multiplier0 Element 0 of big integer to be multiplied
+ * @v result0 Element 0 of big integer to hold result
+ * @v size Number of elements
+ */
+void bigint_multiply_raw ( const uint32_t *multiplicand0,
+ const uint32_t *multiplier0,
+ uint32_t *result0, unsigned int size ) {
+ const bigint_t ( size ) __attribute__ (( may_alias )) *multiplicand =
+ ( ( const void * ) multiplicand0 );
+ const bigint_t ( size ) __attribute__ (( may_alias )) *multiplier =
+ ( ( const void * ) multiplier0 );
+ bigint_t ( size * 2 ) __attribute__ (( may_alias )) *result =
+ ( ( void * ) result0 );
+ unsigned int i;
+ unsigned int j;
+ uint32_t multiplicand_element;
+ uint32_t multiplier_element;
+ uint32_t *result_elements;
+ uint32_t discard_a;
+ uint32_t discard_d;
+ long index;
+
+ /* Zero result */
+ memset ( result, 0, sizeof ( *result ) );
+
+ /* Multiply integers one element at a time */
+ for ( i = 0 ; i < size ; i++ ) {
+ multiplicand_element = multiplicand->element[i];
+ for ( j = 0 ; j < size ; j++ ) {
+ multiplier_element = multiplier->element[j];
+ result_elements = &result->element[ i + j ];
+ /* Perform a single multiply, and add the
+ * resulting double-element into the result,
+ * carrying as necessary. The carry can
+ * never overflow beyond the end of the
+ * result, since:
+ *
+ * a < 2^{n}, b < 2^{n} => ab < 2^{2n}
+ */
+ __asm__ __volatile__ ( "mull %4\n\t"
+ "addl %%eax, (%5,%2,4)\n\t"
+ "adcl %%edx, 4(%5,%2,4)\n\t"
+ "\n1:\n\t"
+ "adcl $0, 8(%5,%2,4)\n\t"
+ "inc %2\n\t"
+ /* Does not affect CF */
+ "jc 1b\n\t"
+ : "=&a" ( discard_a ),
+ "=&d" ( discard_d ),
+ "=&r" ( index )
+ : "0" ( multiplicand_element ),
+ "g" ( multiplier_element ),
+ "r" ( result_elements ),
+ "2" ( 0 ) );
+ }
+ }
+}
diff --git a/qemu/roms/ipxe/src/arch/x86/core/x86_io.c b/qemu/roms/ipxe/src/arch/x86/core/x86_io.c
new file mode 100644
index 000000000..9b2d2d935
--- /dev/null
+++ b/qemu/roms/ipxe/src/arch/x86/core/x86_io.c
@@ -0,0 +1,102 @@
+/*
+ * 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 <ipxe/io.h>
+#include <ipxe/x86_io.h>
+
+/** @file
+ *
+ * iPXE I/O API for x86
+ *
+ */
+
+/**
+ * Read 64-bit qword from memory-mapped device
+ *
+ * @v io_addr I/O address
+ * @ret data Value read
+ *
+ * This routine uses MMX instructions.
+ */
+static __unused uint64_t i386_readq ( volatile uint64_t *io_addr ) {
+ uint64_t data;
+ __asm__ __volatile__ ( "pushl %%edx\n\t"
+ "pushl %%eax\n\t"
+ "movq (%1), %%mm0\n\t"
+ "movq %%mm0, (%%esp)\n\t"
+ "popl %%eax\n\t"
+ "popl %%edx\n\t"
+ "emms\n\t"
+ : "=A" ( data ) : "r" ( io_addr ) );
+ return data;
+}
+
+/**
+ * Write 64-bit qword to memory-mapped device
+ *
+ * @v data Value to write
+ * @v io_addr I/O address
+ *
+ * This routine uses MMX instructions.
+ */
+static __unused void i386_writeq ( uint64_t data, volatile uint64_t *io_addr ) {
+ __asm__ __volatile__ ( "pushl %%edx\n\t"
+ "pushl %%eax\n\t"
+ "movq (%%esp), %%mm0\n\t"
+ "movq %%mm0, (%1)\n\t"
+ "popl %%eax\n\t"
+ "popl %%edx\n\t"
+ "emms\n\t"
+ : : "A" ( data ), "r" ( io_addr ) );
+}
+
+PROVIDE_IOAPI_INLINE ( x86, phys_to_bus );
+PROVIDE_IOAPI_INLINE ( x86, bus_to_phys );
+PROVIDE_IOAPI_INLINE ( x86, ioremap );
+PROVIDE_IOAPI_INLINE ( x86, iounmap );
+PROVIDE_IOAPI_INLINE ( x86, io_to_bus );
+PROVIDE_IOAPI_INLINE ( x86, readb );
+PROVIDE_IOAPI_INLINE ( x86, readw );
+PROVIDE_IOAPI_INLINE ( x86, readl );
+PROVIDE_IOAPI_INLINE ( x86, writeb );
+PROVIDE_IOAPI_INLINE ( x86, writew );
+PROVIDE_IOAPI_INLINE ( x86, writel );
+PROVIDE_IOAPI_INLINE ( x86, inb );
+PROVIDE_IOAPI_INLINE ( x86, inw );
+PROVIDE_IOAPI_INLINE ( x86, inl );
+PROVIDE_IOAPI_INLINE ( x86, outb );
+PROVIDE_IOAPI_INLINE ( x86, outw );
+PROVIDE_IOAPI_INLINE ( x86, outl );
+PROVIDE_IOAPI_INLINE ( x86, insb );
+PROVIDE_IOAPI_INLINE ( x86, insw );
+PROVIDE_IOAPI_INLINE ( x86, insl );
+PROVIDE_IOAPI_INLINE ( x86, outsb );
+PROVIDE_IOAPI_INLINE ( x86, outsw );
+PROVIDE_IOAPI_INLINE ( x86, outsl );
+PROVIDE_IOAPI_INLINE ( x86, iodelay );
+PROVIDE_IOAPI_INLINE ( x86, mb );
+#ifdef __x86_64__
+PROVIDE_IOAPI_INLINE ( x86, readq );
+PROVIDE_IOAPI_INLINE ( x86, writeq );
+#else
+PROVIDE_IOAPI ( x86, readq, i386_readq );
+PROVIDE_IOAPI ( x86, writeq, i386_writeq );
+#endif
diff --git a/qemu/roms/ipxe/src/arch/x86/core/x86_string.c b/qemu/roms/ipxe/src/arch/x86/core/x86_string.c
new file mode 100644
index 000000000..d48347c96
--- /dev/null
+++ b/qemu/roms/ipxe/src/arch/x86/core/x86_string.c
@@ -0,0 +1,190 @@
+/*
+ * 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
+ *
+ * Optimised string operations
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <string.h>
+
+/**
+ * Copy memory area
+ *
+ * @v dest Destination address
+ * @v src Source address
+ * @v len Length
+ * @ret dest Destination address
+ */
+void * __attribute__ (( noinline )) __memcpy ( void *dest, const void *src,
+ size_t len ) {
+ void *edi = dest;
+ const void *esi = src;
+ int discard_ecx;
+
+ /* We often do large dword-aligned and dword-length block
+ * moves. Using movsl rather than movsb speeds these up by
+ * around 32%.
+ */
+ __asm__ __volatile__ ( "rep movsl"
+ : "=&D" ( edi ), "=&S" ( esi ),
+ "=&c" ( discard_ecx )
+ : "0" ( edi ), "1" ( esi ), "2" ( len >> 2 )
+ : "memory" );
+ __asm__ __volatile__ ( "rep movsb"
+ : "=&D" ( edi ), "=&S" ( esi ),
+ "=&c" ( discard_ecx )
+ : "0" ( edi ), "1" ( esi ), "2" ( len & 3 )
+ : "memory" );
+ return dest;
+}
+
+/**
+ * Copy memory area backwards
+ *
+ * @v dest Destination address
+ * @v src Source address
+ * @v len Length
+ * @ret dest Destination address
+ */
+void * __attribute__ (( noinline )) __memcpy_reverse ( void *dest,
+ const void *src,
+ size_t len ) {
+ void *edi = ( dest + len - 1 );
+ const void *esi = ( src + len - 1 );
+ int discard_ecx;
+
+ /* Assume memmove() is not performance-critical, and perform a
+ * bytewise copy for simplicity.
+ */
+ __asm__ __volatile__ ( "std\n\t"
+ "rep movsb\n\t"
+ "cld\n\t"
+ : "=&D" ( edi ), "=&S" ( esi ),
+ "=&c" ( discard_ecx )
+ : "0" ( edi ), "1" ( esi ),
+ "2" ( len )
+ : "memory" );
+ return dest;
+}
+
+
+/**
+ * Copy (possibly overlapping) memory area
+ *
+ * @v dest Destination address
+ * @v src Source address
+ * @v len Length
+ * @ret dest Destination address
+ */
+void * __memmove ( void *dest, const void *src, size_t len ) {
+
+ if ( dest <= src ) {
+ return __memcpy ( dest, src, len );
+ } else {
+ return __memcpy_reverse ( dest, src, len );
+ }
+}
+
+/**
+ * Swap memory areas
+ *
+ * @v dest Destination address
+ * @v src Source address
+ * @v len Length
+ * @ret dest Destination address
+ */
+void * memswap ( void *dest, void *src, size_t len ) {
+ size_t discard_c;
+ int discard;
+
+ __asm__ __volatile__ ( "\n1:\n\t"
+ "dec %2\n\t"
+ "js 2f\n\t"
+ "movb (%0,%2), %b3\n\t"
+ "xchgb (%1,%2), %b3\n\t"
+ "movb %b3, (%0,%2)\n\t"
+ "jmp 1b\n\t"
+ "2:\n\t"
+ : "=r" ( src ), "=r" ( dest ),
+ "=&c" ( discard_c ), "=&q" ( discard )
+ : "0" ( src ), "1" ( dest ), "2" ( len )
+ : "memory" );
+
+ return dest;
+}
+
+/**
+ * Calculate length of string
+ *
+ * @v string String
+ * @ret len Length (excluding NUL)
+ */
+size_t strlen ( const char *string ) {
+ const char *discard_D;
+ size_t len_plus_one;
+
+ __asm__ __volatile__ ( "repne scasb\n\t"
+ "not %1\n\t"
+ : "=&D" ( discard_D ), "=&c" ( len_plus_one )
+ : "0" ( string ), "1" ( -1UL ), "a" ( 0 ) );
+
+ return ( len_plus_one - 1 );
+}
+
+/**
+ * Compare strings (up to a specified length)
+ *
+ * @v str1 First string
+ * @v str2 Second string
+ * @v len Maximum length
+ * @ret diff Difference
+ */
+int strncmp ( const char *str1, const char *str2, size_t len ) {
+ const void *discard_S;
+ const void *discard_D;
+ size_t discard_c;
+ int diff;
+
+ __asm__ __volatile__ ( "\n1:\n\t"
+ "dec %2\n\t"
+ "js 2f\n\t"
+ "lodsb\n\t"
+ "scasb\n\t"
+ "jne 3f\n\t"
+ "testb %b3, %b3\n\t"
+ "jnz 1b\n\t"
+ /* Equal */
+ "\n2:\n\t"
+ "xor %3, %3\n\t"
+ "jmp 4f\n\t"
+ /* Not equal; CF indicates difference */
+ "\n3:\n\t"
+ "sbb %3, %3\n\t"
+ "orb $1, %b3\n\t"
+ "\n4:\n\t"
+ : "=&S" ( discard_S ), "=&D" ( discard_D ),
+ "=&c" ( discard_c ), "=&a" ( diff )
+ : "0" ( str1 ), "1" ( str2 ), "2" ( len ) );
+
+ return diff;
+}
diff --git a/qemu/roms/ipxe/src/arch/x86/core/x86_tcpip.c b/qemu/roms/ipxe/src/arch/x86/core/x86_tcpip.c
new file mode 100644
index 000000000..8a4ce5152
--- /dev/null
+++ b/qemu/roms/ipxe/src/arch/x86/core/x86_tcpip.c
@@ -0,0 +1,169 @@
+/*
+ * 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
+ *
+ * TCP/IP checksum
+ *
+ */
+
+#include <limits.h>
+#include <ipxe/tcpip.h>
+
+extern char x86_tcpip_loop_end[];
+
+/**
+ * Calculate continued TCP/IP checkum
+ *
+ * @v partial Checksum of already-summed data, in network byte order
+ * @v data Data buffer
+ * @v len Length of data buffer
+ * @ret cksum Updated checksum, in network byte order
+ */
+uint16_t x86_tcpip_continue_chksum ( uint16_t partial,
+ const void *data, size_t len ) {
+ unsigned long sum = ( ( ~partial ) & 0xffff );
+ unsigned long initial_word_count;
+ unsigned long loop_count;
+ unsigned long loop_partial_count;
+ unsigned long final_word_count;
+ unsigned long final_byte;
+ unsigned long discard_S;
+ unsigned long discard_c;
+ unsigned long discard_a;
+ unsigned long discard_r1;
+ unsigned long discard_r2;
+
+ /* Calculate number of initial 16-bit words required to bring
+ * the main loop into alignment. (We don't care about the
+ * speed for data aligned to less than 16 bits, since this
+ * situation won't occur in practice.)
+ */
+ if ( len >= sizeof ( sum ) ) {
+ initial_word_count = ( ( -( ( intptr_t ) data ) &
+ ( sizeof ( sum ) - 1 ) ) >> 1 );
+ } else {
+ initial_word_count = 0;
+ }
+ len -= ( initial_word_count * 2 );
+
+ /* Calculate number of iterations of the main loop. This loop
+ * processes native machine words (32-bit or 64-bit), and is
+ * unrolled 16 times. We calculate an overall iteration
+ * count, and a starting point for the first iteration.
+ */
+ loop_count = ( len / ( sizeof ( sum ) * 16 ) );
+ loop_partial_count =
+ ( ( len % ( sizeof ( sum ) * 16 ) ) / sizeof ( sum ) );
+
+ /* Calculate number of 16-bit words remaining after the main
+ * loop completes.
+ */
+ final_word_count = ( ( len % sizeof ( sum ) ) / 2 );
+
+ /* Calculate whether or not a final byte remains at the end */
+ final_byte = ( len & 1 );
+
+ /* Calculate the checksum */
+ __asm__ ( /* Calculate position at which to jump into the
+ * unrolled loop.
+ */
+ "imul $( -x86_tcpip_loop_step_size ), %4\n\t"
+ "add %5, %4\n\t"
+
+ /* Clear carry flag before starting checksumming */
+ "clc\n\t"
+
+ /* Checksum initial words */
+ "jmp 2f\n\t"
+ "\n1:\n\t"
+ "lodsw\n\t"
+ "adcw %w2, %w0\n\t"
+ "\n2:\n\t"
+ "loop 1b\n\t"
+
+ /* Main "lods;adc" loop, unrolled x16 */
+ "mov %12, %3\n\t"
+ "jmp *%4\n\t"
+ "\nx86_tcpip_loop_start:\n\t"
+ "lods%z2\n\tadc %2, %0\n\t"
+ "lods%z2\n\tadc %2, %0\n\t"
+ "lods%z2\n\tadc %2, %0\n\t"
+ "lods%z2\n\tadc %2, %0\n\t"
+ "lods%z2\n\tadc %2, %0\n\t"
+ "lods%z2\n\tadc %2, %0\n\t"
+ "lods%z2\n\tadc %2, %0\n\t"
+ "lods%z2\n\tadc %2, %0\n\t"
+ "lods%z2\n\tadc %2, %0\n\t"
+ "lods%z2\n\tadc %2, %0\n\t"
+ "lods%z2\n\tadc %2, %0\n\t"
+ "lods%z2\n\tadc %2, %0\n\t"
+ "lods%z2\n\tadc %2, %0\n\t"
+ "lods%z2\n\tadc %2, %0\n\t"
+ "lods%z2\n\tadc %2, %0\n\t"
+ "lods%z2\n\tadc %2, %0\n\t"
+ "\nx86_tcpip_loop_end:\n\t"
+ "loop x86_tcpip_loop_start\n\t"
+ ".equ x86_tcpip_loop_step_size, "
+ " ( ( x86_tcpip_loop_end - x86_tcpip_loop_start ) >> 4 )\n\t"
+
+ /* Checksum remaining whole words */
+ "mov %13, %3\n\t"
+ "jmp 2f\n\t"
+ "\n1:\n\t"
+ "lodsw\n\t"
+ "adcw %w2, %w0\n\t"
+ "\n2:\n\t"
+ "loop 1b\n\t"
+
+ /* Checksum final byte if applicable */
+ "mov %14, %3\n\t"
+ "loop 1f\n\t"
+ "adcb (%1), %b0\n\t"
+ "adcb $0, %h0\n\t"
+ "\n1:\n\t"
+
+ /* Fold down to a uint16_t */
+ "push %0\n\t"
+ "popw %w0\n\t"
+ "popw %w2\n\t"
+ "adcw %w2, %w0\n\t"
+#if ULONG_MAX > 0xffffffffUL /* 64-bit only */
+ "popw %w2\n\t"
+ "adcw %w2, %w0\n\t"
+ "popw %w2\n\t"
+ "adcw %w2, %w0\n\t"
+#endif /* 64-bit only */
+
+ /* Consume CF */
+ "adcw $0, %w0\n\t"
+ "adcw $0, %w0\n\t"
+
+ : "=&Q" ( sum ), "=&S" ( discard_S ), "=&a" ( discard_a ),
+ "=&c" ( discard_c ), "=&r" ( discard_r1 ),
+ "=&r" ( discard_r2 )
+ : "0" ( sum ), "1" ( data ), "2" ( 0 ),
+ "3" ( initial_word_count + 1 ), "4" ( loop_partial_count ),
+ "5" ( x86_tcpip_loop_end ), "g" ( loop_count + 1 ),
+ "g" ( final_word_count + 1 ), "g" ( final_byte ) );
+
+ return ( ~sum & 0xffff );
+}