diff options
| author | fishsoupisgood <github@madingley.org> | 2019-04-29 01:17:54 +0100 | 
|---|---|---|
| committer | fishsoupisgood <github@madingley.org> | 2019-05-27 03:43:43 +0100 | 
| commit | 3f2546b2ef55b661fd8dd69682b38992225e86f6 (patch) | |
| tree | 65ca85f13617aee1dce474596800950f266a456c /roms/ipxe/src/arch/x86/core | |
| download | qemu-master.tar.gz qemu-master.tar.bz2 qemu-master.zip | |
Diffstat (limited to 'roms/ipxe/src/arch/x86/core')
| -rw-r--r-- | roms/ipxe/src/arch/x86/core/cpuid.c | 137 | ||||
| -rw-r--r-- | roms/ipxe/src/arch/x86/core/cpuid_settings.c | 274 | ||||
| -rw-r--r-- | roms/ipxe/src/arch/x86/core/debugcon.c | 86 | ||||
| -rw-r--r-- | roms/ipxe/src/arch/x86/core/linux/linux_api.c | 110 | ||||
| -rw-r--r-- | roms/ipxe/src/arch/x86/core/linux/linux_strerror.c | 169 | ||||
| -rw-r--r-- | roms/ipxe/src/arch/x86/core/pcidirect.c | 48 | ||||
| -rw-r--r-- | roms/ipxe/src/arch/x86/core/x86_bigint.c | 91 | ||||
| -rw-r--r-- | roms/ipxe/src/arch/x86/core/x86_io.c | 102 | ||||
| -rw-r--r-- | roms/ipxe/src/arch/x86/core/x86_string.c | 190 | ||||
| -rw-r--r-- | roms/ipxe/src/arch/x86/core/x86_tcpip.c | 169 | 
10 files changed, 1376 insertions, 0 deletions
| diff --git a/roms/ipxe/src/arch/x86/core/cpuid.c b/roms/ipxe/src/arch/x86/core/cpuid.c new file mode 100644 index 00000000..5908f441 --- /dev/null +++ b/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/roms/ipxe/src/arch/x86/core/cpuid_settings.c b/roms/ipxe/src/arch/x86/core/cpuid_settings.c new file mode 100644 index 00000000..42dea933 --- /dev/null +++ b/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/roms/ipxe/src/arch/x86/core/debugcon.c b/roms/ipxe/src/arch/x86/core/debugcon.c new file mode 100644 index 00000000..263cb4af --- /dev/null +++ b/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/roms/ipxe/src/arch/x86/core/linux/linux_api.c b/roms/ipxe/src/arch/x86/core/linux/linux_api.c new file mode 100644 index 00000000..0bed9fd5 --- /dev/null +++ b/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/roms/ipxe/src/arch/x86/core/linux/linux_strerror.c b/roms/ipxe/src/arch/x86/core/linux/linux_strerror.c new file mode 100644 index 00000000..24c9b773 --- /dev/null +++ b/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/roms/ipxe/src/arch/x86/core/pcidirect.c b/roms/ipxe/src/arch/x86/core/pcidirect.c new file mode 100644 index 00000000..dbc8317b --- /dev/null +++ b/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/roms/ipxe/src/arch/x86/core/x86_bigint.c b/roms/ipxe/src/arch/x86/core/x86_bigint.c new file mode 100644 index 00000000..418ac230 --- /dev/null +++ b/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/roms/ipxe/src/arch/x86/core/x86_io.c b/roms/ipxe/src/arch/x86/core/x86_io.c new file mode 100644 index 00000000..9b2d2d93 --- /dev/null +++ b/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/roms/ipxe/src/arch/x86/core/x86_string.c b/roms/ipxe/src/arch/x86/core/x86_string.c new file mode 100644 index 00000000..d48347c9 --- /dev/null +++ b/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/roms/ipxe/src/arch/x86/core/x86_tcpip.c b/roms/ipxe/src/arch/x86/core/x86_tcpip.c new file mode 100644 index 00000000..8a4ce515 --- /dev/null +++ b/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 ); +} | 
