diff --git a/src/Makefile b/src/Makefile index 2fce34f9..d0cd183f 100644 --- a/src/Makefile +++ b/src/Makefile @@ -61,7 +61,7 @@ ZLIB_DIR := /usr SRCDIRS := SRCDIRS += libgcc SRCDIRS += core -SRCDIRS += net net/tcp net/udp net/infiniband net/80211 +SRCDIRS += net net/oncrpc net/tcp net/udp net/infiniband net/80211 SRCDIRS += image SRCDIRS += drivers/bus SRCDIRS += drivers/net @@ -108,7 +108,7 @@ INCDIRS += include . # helpfully suggestive message # ALL := bin/blib.a bin/ipxe.dsk bin/ipxe.lkrn bin/ipxe.iso \ - bin/ipxe.usb bin/undionly.kpxe bin/rtl8139.rom + bin/ipxe.usb bin/ipxe.pxe bin/undionly.kpxe bin/rtl8139.rom all : $(ALL) @$(ECHO) '===========================================================' @$(ECHO) diff --git a/src/Makefile.housekeeping b/src/Makefile.housekeeping index 80b70f5c..8603bfd6 100644 --- a/src/Makefile.housekeeping +++ b/src/Makefile.housekeeping @@ -1011,15 +1011,20 @@ $(BIN)/%.nodeps : $(BIN)/%.tmp # Get licensing verdict for the specified target # define licensable_deps_list - $(filter-out config/local/%.h,$(call deps_list,$(1))) + $(filter-out config/local/%.h,\ + $(filter-out $(BIN)/.%.list,\ + $(call deps_list,$(1)))) endef define unlicensed_deps_list $(shell grep -L FILE_LICENCE $(call licensable_deps_list,$(1))) endef define licence_list - $(patsubst __licence_%,%,\ - $(filter __licence_%,$(shell $(NM) $(1) | cut -d" " -f3))) + $(sort $(foreach LICENCE,\ + $(filter __licence__%,$(shell $(NM) $(1) | cut -d" " -f3)),\ + $(word 2,$(subst __, ,$(LICENCE))))) endef +$(BIN)/%.licence_list : $(BIN)/%.tmp + $(Q)$(ECHO) $(call licence_list,$<) $(BIN)/%.licence : $(BIN)/%.tmp $(QM)$(ECHO) " [LICENCE] $@" $(Q)$(if $(strip $(call unlicensed_deps_list,$<)),\ diff --git a/src/arch/i386/interface/pcbios/apm.c b/src/arch/i386/interface/pcbios/apm.c new file mode 100644 index 00000000..3b13e1cd --- /dev/null +++ b/src/arch/i386/interface/pcbios/apm.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2013 Marin Hannache . + * + * 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 + * + * Advanced Power Management + * + */ + +#include +#include +#include + +/** + * Power off the computer using APM + * + * @ret rc Return status code + */ +static int apm_poweroff ( void ) { + uint16_t apm_version; + uint16_t apm_signature; + uint16_t apm_flags; + uint16_t carry; + + /* APM check */ + __asm__ __volatile__ ( REAL_CODE ( "int $0x15\n\t" + "adc %%edx,0\n\t" ) + : "=a" ( apm_version ), "=b" ( apm_signature ), + "=c" ( apm_flags ), "=d" ( carry ) + : "a" ( 0x5300 ), "b" ( 0x0000 ), + "d" ( 0x0000 ) ); + if ( carry ) { + DBG ( "APM not present\n" ); + return -ENOTSUP; + } + if ( apm_signature != 0x504d ) { /* signature 'PM' */ + DBG ( "APM not present\n" ); + return -ENOTSUP; + } + if ( apm_version < 0x0101 ) { /* Need version 1.1+ */ + DBG ( "APM 1.1+ not supported\n" ); + return -ENOTSUP; + } + if ( ( apm_flags & 0x8 ) == 0x8 ) { + DBG ( "APM power management disabled\n" ); + return -EPERM; + } + DBG2 ( "APM check completed\n" ); + + /* APM initialisation */ + __asm__ __volatile__ ( REAL_CODE ( "int $0x15\n\t" + "adc %%edx,0\n\t" ) + : "=d" ( carry ) + : "a" ( 0x5301 ), "b" ( 0x0000 ), + "d" ( 0x0000 ) ); + if ( carry ) { + DBG ( "APM initialisation failed\n" ); + return -EIO; + } + DBG2 ( "APM initialisation completed\n" ); + + /* Set APM driver version */ + __asm__ __volatile__ ( REAL_CODE ( "int $0x15\n\t" + "adc %%edx,0\n\t" ) + : "=d" ( carry ) + : "a" ( 0x530e ), "b" ( 0x0000 ), + "c" ( 0x0101 ), "d" ( 0x0000 ) ); + if ( carry ) { + DBG ( "APM setting driver version failed\n" ); + return -EIO; + } + DBG2 ( "APM driver version set\n" ); + + /* Setting power state to off */ + __asm__ __volatile__ ( REAL_CODE ( "int $0x15\n\t" + "adc %%edx,0\n\t" ) + : "=d" ( carry ) + : "a" ( 0x5307 ), "b" ( 0x0001 ), + "c" ( 0x0003 ), "d" ( 0x0000) ); + if ( carry ) { + DBG ( "APM setting power state failed\n" ); + return -ENOTTY; + } + + /* Should never happen */ + return -ECANCELED; +} + +PROVIDE_REBOOT ( pcbios, poweroff, apm_poweroff ); diff --git a/src/arch/i386/interface/vmware/guestinfo.c b/src/arch/i386/interface/vmware/guestinfo.c index da013ca2..7fa41b86 100644 --- a/src/arch/i386/interface/vmware/guestinfo.c +++ b/src/arch/i386/interface/vmware/guestinfo.c @@ -58,7 +58,7 @@ static int guestinfo_fetch_type ( struct settings *settings, strlen ( parent_name ) + 1 /* "." */ + strlen ( setting->name ) + 1 /* "." */ + ( type ? strlen ( type->name ) : 0 ) + 1 /* NUL */ ]; - struct setting *named_setting; + struct setting *predefined; char *info; int info_len; int check_len; @@ -82,9 +82,8 @@ static int guestinfo_fetch_type ( struct settings *settings, /* Determine default type if necessary */ if ( ! type ) { - named_setting = find_setting ( setting->name ); - type = ( named_setting ? - named_setting->type : &setting_type_string ); + predefined = find_setting ( setting->name ); + type = ( predefined ? predefined->type : &setting_type_string ); } assert ( type != NULL ); @@ -115,7 +114,7 @@ static int guestinfo_fetch_type ( struct settings *settings, settings, &command[9] /* Skip "info-get " */, info ); /* Parse GuestInfo value according to type */ - ret = type->parse ( info, data, len ); + ret = setting_parse ( type, info, data, len ); if ( ret < 0 ) { DBGC ( settings, "GuestInfo %p could not parse \"%s\" as %s: " "%s\n", settings, info, type->name, strerror ( ret ) ); diff --git a/src/arch/i386/scripts/i386-kir.lds b/src/arch/i386/scripts/i386-kir.lds index 338d6ee8..66bf804e 100644 --- a/src/arch/i386/scripts/i386-kir.lds +++ b/src/arch/i386/scripts/i386-kir.lds @@ -98,6 +98,8 @@ SECTIONS { *(.data) *(.data.*) KEEP(*(SORT(.tbl.*))) /* Various tables. See include/tables.h */ + KEEP(*(.provided)) + KEEP(*(.provided.*)) _edata16_progbits = .; } .bss16 : AT ( _data16_load_offset + __bss16 ) { diff --git a/src/arch/i386/scripts/i386.lds b/src/arch/i386/scripts/i386.lds index fb763656..98f95cb2 100644 --- a/src/arch/i386/scripts/i386.lds +++ b/src/arch/i386/scripts/i386.lds @@ -109,6 +109,8 @@ SECTIONS { *(.data) *(.data.*) KEEP(*(SORT(.tbl.*))) /* Various tables. See include/tables.h */ + KEEP(*(.provided)) + KEEP(*(.provided.*)) _mtextdata = .; } .bss.textdata (NOLOAD) : AT ( _end_lma ) { *(.bss) diff --git a/src/arch/i386/scripts/linux.lds b/src/arch/i386/scripts/linux.lds index 64c9b97f..9f2eeaf3 100644 --- a/src/arch/i386/scripts/linux.lds +++ b/src/arch/i386/scripts/linux.lds @@ -53,6 +53,8 @@ SECTIONS { *(.data) *(.data.*) KEEP(*(SORT(.tbl.*))) + KEEP(*(.provided)) + KEEP(*(.provided.*)) _edata = .; } diff --git a/src/arch/x86/core/cpuid.c b/src/arch/x86/core/cpuid.c index 96f79409..5908f441 100644 --- a/src/arch/x86/core/cpuid.c +++ b/src/arch/x86/core/cpuid.c @@ -33,7 +33,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); * * @ret is_supported CPUID instruction is supported */ -static int cpuid_is_supported ( void ) { +int cpuid_is_supported ( void ) { unsigned long original; unsigned long inverted; @@ -52,24 +52,6 @@ static int cpuid_is_supported ( void ) { return ( ( original ^ inverted ) & CPUID_FLAG ); } -/** - * Issue CPUID instruction - * - * @v operation CPUID operation - * @v eax Output via %eax - * @v ebx Output via %ebx - * @v ecx Output via %ecx - * @v edx Output via %edx - */ -static inline __attribute__ (( always_inline )) void -cpuid ( uint32_t operation, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, - uint32_t *edx ) { - - __asm__ ( "cpuid" - : "=a" ( *eax ), "=b" ( *ebx ), "=c" ( *ecx ), "=d" ( *edx ) - : "0" ( operation ) ); -} - /** * Get Intel-defined x86 CPU features * diff --git a/src/arch/x86/core/cpuid_settings.c b/src/arch/x86/core/cpuid_settings.c new file mode 100644 index 00000000..e2ec9bde --- /dev/null +++ b/src/arch/x86/core/cpuid_settings.c @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2013 Michael Brown . + * + * 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 +#include +#include +#include +#include +#include + +/** @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/.", 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 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, + 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, +}; + +/** CPUID predefined settings */ +struct setting cpuid_predefined_settings[] __setting ( SETTING_HOST_EXTRA ) = { + { + .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, + }, + { + .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/src/arch/x86/core/linux/linux_api.c b/src/arch/x86/core/linux/linux_api.c index c8a09b7d..0bed9fd5 100644 --- a/src/arch/x86/core/linux/linux_api.c +++ b/src/arch/x86/core/linux/linux_api.c @@ -37,6 +37,10 @@ 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 ); } diff --git a/src/arch/x86/include/bits/errfile.h b/src/arch/x86/include/bits/errfile.h index 5f676c87..d9cf5c45 100644 --- a/src/arch/x86/include/bits/errfile.h +++ b/src/arch/x86/include/bits/errfile.h @@ -19,6 +19,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_vmware ( ERRFILE_ARCH | ERRFILE_CORE | 0x00080000 ) #define ERRFILE_guestrpc ( ERRFILE_ARCH | ERRFILE_CORE | 0x00090000 ) #define ERRFILE_guestinfo ( ERRFILE_ARCH | ERRFILE_CORE | 0x000a0000 ) +#define ERRFILE_apm ( ERRFILE_ARCH | ERRFILE_CORE | 0x000b0000 ) #define ERRFILE_bootsector ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00000000 ) #define ERRFILE_bzimage ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00010000 ) @@ -45,6 +46,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_timer_bios ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00010000 ) #define ERRFILE_cpuid_cmd ( ERRFILE_ARCH | ERRFILE_OTHER | 0x00000000 ) +#define ERRFILE_cpuid_settings ( ERRFILE_ARCH | ERRFILE_OTHER | 0x00010000 ) /** @} */ diff --git a/src/arch/x86/include/ipxe/cpuid.h b/src/arch/x86/include/ipxe/cpuid.h index 9705137c..2f78dfca 100644 --- a/src/arch/x86/include/ipxe/cpuid.h +++ b/src/arch/x86/include/ipxe/cpuid.h @@ -30,6 +30,9 @@ struct x86_features { /** CPUID support flag */ #define CPUID_FLAG 0x00200000UL +/** CPUID extended function */ +#define CPUID_EXTENDED 0x80000000UL + /** Get vendor ID and largest standard function */ #define CPUID_VENDOR_ID 0x00000000UL @@ -48,6 +51,28 @@ struct x86_features { /** Get extended features */ #define CPUID_AMD_FEATURES 0x80000001UL +/** Get CPU model */ +#define CPUID_MODEL 0x80000002UL + +/** + * Issue CPUID instruction + * + * @v operation CPUID operation + * @v eax Output via %eax + * @v ebx Output via %ebx + * @v ecx Output via %ecx + * @v edx Output via %edx + */ +static inline __attribute__ (( always_inline )) void +cpuid ( uint32_t operation, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, + uint32_t *edx ) { + + __asm__ ( "cpuid" + : "=a" ( *eax ), "=b" ( *ebx ), "=c" ( *ecx ), "=d" ( *edx ) + : "0" ( operation ) ); +} + +extern int cpuid_is_supported ( void ); extern void x86_features ( struct x86_features *features ); #endif /* _IPXE_CPUID_H */ diff --git a/src/arch/x86/include/valgrind/memcheck.h b/src/arch/x86/include/valgrind/memcheck.h index 46d23432..7d4b56d3 100644 --- a/src/arch/x86/include/valgrind/memcheck.h +++ b/src/arch/x86/include/valgrind/memcheck.h @@ -60,6 +60,8 @@ #ifndef __MEMCHECK_H #define __MEMCHECK_H +FILE_LICENCE ( BSD3 ); + /* This file is for inclusion into client (your!) code. diff --git a/src/arch/x86/include/valgrind/valgrind.h b/src/arch/x86/include/valgrind/valgrind.h index d72754b0..d48bbcca 100644 --- a/src/arch/x86/include/valgrind/valgrind.h +++ b/src/arch/x86/include/valgrind/valgrind.h @@ -73,6 +73,8 @@ #ifndef __VALGRIND_H #define __VALGRIND_H +FILE_LICENCE ( BSD3 ); + /* ------------------------------------------------------------------ */ /* VERSION NUMBER OF VALGRIND */ diff --git a/src/arch/x86/scripts/efi.lds b/src/arch/x86/scripts/efi.lds index 1a16c29b..f1049f24 100644 --- a/src/arch/x86/scripts/efi.lds +++ b/src/arch/x86/scripts/efi.lds @@ -55,6 +55,8 @@ SECTIONS { *(.data) *(.data.*) KEEP(*(SORT(.tbl.*))) /* Various tables. See include/tables.h */ + KEEP(*(.provided)) + KEEP(*(.provided.*)) _edata = .; } diff --git a/src/arch/x86_64/scripts/linux.lds b/src/arch/x86_64/scripts/linux.lds index 85e548ff..47db2174 100644 --- a/src/arch/x86_64/scripts/linux.lds +++ b/src/arch/x86_64/scripts/linux.lds @@ -53,6 +53,8 @@ SECTIONS { *(.data) *(.data.*) KEEP(*(SORT(.tbl.*))) + KEEP(*(.provided)) + KEEP(*(.provided.*)) _edata = .; } diff --git a/src/config/config.c b/src/config/config.c index bd1d9885..c8cb5399 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -10,6 +10,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include /** @file * @@ -128,6 +129,9 @@ REQUIRE_OBJECT ( https ); #ifdef DOWNLOAD_PROTO_FTP REQUIRE_OBJECT ( ftp ); #endif +#ifdef DOWNLOAD_PROTO_NFS +REQUIRE_OBJECT ( nfs_open ); +#endif #ifdef DOWNLOAD_PROTO_SLAM REQUIRE_OBJECT ( slam ); #endif @@ -241,6 +245,9 @@ REQUIRE_OBJECT ( lotest_cmd ); #ifdef VLAN_CMD REQUIRE_OBJECT ( vlan_cmd ); #endif +#ifdef POWEROFF_CMD +REQUIRE_OBJECT ( poweroff_cmd ); +#endif #ifdef REBOOT_CMD REQUIRE_OBJECT ( reboot_cmd ); #endif @@ -253,6 +260,9 @@ REQUIRE_OBJECT ( sync_cmd ); #ifdef NSLOOKUP_CMD REQUIRE_OBJECT ( nslookup_cmd ); #endif +#ifdef PCI_CMD +REQUIRE_OBJECT ( pci_cmd ); +#endif /* * Drag in miscellaneous objects @@ -293,9 +303,22 @@ REQUIRE_OBJECT ( tap ); REQUIRE_OBJECT ( efi_bofm ); #endif /* BOFM_EFI */ #endif /* CONFIG_BOFM */ + +/* + * Drag in relevant settings sources + */ +#ifdef PCI_SETTINGS +REQUIRE_OBJECT ( pci_settings ); +#endif #ifdef VMWARE_SETTINGS REQUIRE_OBJECT ( guestinfo ); -#endif /* VMWARE_SETTINGS */ +#endif +#ifdef CPUID_SETTINGS +REQUIRE_OBJECT ( cpuid_settings ); +#endif +#ifdef MEMMAP_SETTINGS +REQUIRE_OBJECT ( memmap_settings ); +#endif /* * Drag in selected keyboard map diff --git a/src/config/defaults/efi.h b/src/config/defaults/efi.h index 7385aeab..4276d936 100644 --- a/src/config/defaults/efi.h +++ b/src/config/defaults/efi.h @@ -7,6 +7,8 @@ * */ +FILE_LICENCE ( GPL2_OR_LATER ); + #define UACCESS_EFI #define IOAPI_X86 #define PCIAPI_EFI diff --git a/src/config/defaults/linux.h b/src/config/defaults/linux.h index 666db6b8..bc5ba785 100644 --- a/src/config/defaults/linux.h +++ b/src/config/defaults/linux.h @@ -7,6 +7,8 @@ * */ +FILE_LICENCE ( GPL2_OR_LATER ); + #define CONSOLE_LINUX #define TIMER_LINUX #define UACCESS_LINUX @@ -17,6 +19,7 @@ #define ENTROPY_LINUX #define TIME_LINUX #define REBOOT_NULL +#define PCIAPI_LINUX #define DRIVERS_LINUX diff --git a/src/config/general.h b/src/config/general.h index b2249597..03bd5eda 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -59,6 +59,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define DOWNLOAD_PROTO_HTTPS /* Secure Hypertext Transfer Protocol */ #undef DOWNLOAD_PROTO_FTP /* File Transfer Protocol */ #undef DOWNLOAD_PROTO_SLAM /* Scalable Local Area Multicast */ +#undef DOWNLOAD_PROTO_NFS /* Network File System Protocol */ /* * SAN boot protocols diff --git a/src/config/settings.h b/src/config/settings.h new file mode 100644 index 00000000..b06f2901 --- /dev/null +++ b/src/config/settings.h @@ -0,0 +1,19 @@ +#ifndef CONFIG_SETTINGS_H +#define CONFIG_SETTINGS_H + +/** @file + * + * Configuration settings sources + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#define PCI_SETTINGS /* PCI device settings */ +//#define CPUID_SETTINGS /* CPUID settings */ +//#define MEMMAP_SETTINGS /* Memory map settings */ +//#define VMWARE_SETTINGS /* VMware GuestInfo settings */ + +#include + +#endif /* CONFIG_SETTINGS_H */ diff --git a/src/config/sideband.h b/src/config/sideband.h index 52339993..2e2a8d41 100644 --- a/src/config/sideband.h +++ b/src/config/sideband.h @@ -10,7 +10,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); //#define CONFIG_BOFM /* IBM's BladeCenter Open Fabric Manager */ -//#define VMWARE_SETTINGS /* VMware GuestInfo settings */ #include diff --git a/src/core/base16.c b/src/core/base16.c index 1f0e536c..2544bd7f 100644 --- a/src/core/base16.c +++ b/src/core/base16.c @@ -51,15 +51,61 @@ void base16_encode ( const uint8_t *raw, size_t len, char *encoded ) { 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 * @@ -75,33 +121,15 @@ void base16_encode ( const uint8_t *raw, size_t len, char *encoded ) { * to provide a buffer of the correct size. */ int base16_decode ( const char *encoded, uint8_t *raw ) { - const char *encoded_bytes = encoded; - uint8_t *raw_bytes = raw; - char buf[3]; - char *endp; - size_t len; + int len; - while ( encoded_bytes[0] ) { - if ( ! encoded_bytes[1] ) { - DBG ( "Base16-encoded string \"%s\" has invalid " - "length\n", encoded ); - return -EINVAL; - } - memcpy ( buf, encoded_bytes, 2 ); - buf[2] = '\0'; - *(raw_bytes++) = strtoul ( buf, &endp, 16 ); - if ( *endp != '\0' ) { - DBG ( "Base16-encoded string \"%s\" has invalid " - "byte \"%s\"\n", encoded, buf ); - return -EINVAL; - } - encoded_bytes += 2; - } - len = ( raw_bytes - raw ); + 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 <= base16_decoded_max_len ( encoded ) ); - return ( len ); + return len; } diff --git a/src/core/errno.c b/src/core/errno.c index b4f44cec..06905561 100644 --- a/src/core/errno.c +++ b/src/core/errno.c @@ -1,5 +1,7 @@ #include +FILE_LICENCE ( GPL2_OR_LATER ); + /** @file * * Error codes diff --git a/src/core/memmap_settings.c b/src/core/memmap_settings.c new file mode 100644 index 00000000..f06b8750 --- /dev/null +++ b/src/core/memmap_settings.c @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2013 Michael Brown . + * + * 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 +#include +#include +#include +#include +#include + +/** @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 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, + 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 */ +struct setting memmap_predefined_settings[] __setting ( SETTING_MISC ) = { + { + .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/src/core/misc.c b/src/core/misc.c index 11342481..eaceddfe 100644 --- a/src/core/misc.c +++ b/src/core/misc.c @@ -33,6 +33,19 @@ int inet_aton ( const char *cp, struct in_addr *inp ) { 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; diff --git a/src/core/null_reboot.c b/src/core/null_reboot.c index 8e3ed0bb..a3d5b2ef 100644 --- a/src/core/null_reboot.c +++ b/src/core/null_reboot.c @@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); */ #include +#include #include /** @@ -40,4 +41,15 @@ static void null_reboot ( int warm __unused ) { 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/src/core/parseopt.c b/src/core/parseopt.c index 659d20ee..65ad4ec5 100644 --- a/src/core/parseopt.c +++ b/src/core/parseopt.c @@ -28,6 +28,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include /** @file @@ -59,7 +60,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); * @ret value String value * @ret rc Return status code */ -int parse_string ( const char *text, const char **value ) { +int parse_string ( char *text, char **value ) { /* Sanity check */ assert ( text != NULL ); @@ -77,7 +78,7 @@ int parse_string ( const char *text, const char **value ) { * @ret value Integer value * @ret rc Return status code */ -int parse_integer ( const char *text, unsigned int *value ) { +int parse_integer ( char *text, unsigned int *value ) { char *endp; /* Sanity check */ @@ -100,7 +101,7 @@ int parse_integer ( const char *text, unsigned int *value ) { * @ret netdev Network device * @ret rc Return status code */ -int parse_netdev ( const char *text, struct net_device **netdev ) { +int parse_netdev ( char *text, struct net_device **netdev ) { /* Sanity check */ assert ( text != NULL ); @@ -122,7 +123,7 @@ int parse_netdev ( const char *text, struct net_device **netdev ) { * @ret menu Menu * @ret rc Return status code */ -int parse_menu ( const char *text, struct menu **menu ) { +int parse_menu ( char *text, struct menu **menu ) { /* Find menu */ *menu = find_menu ( text ); @@ -145,7 +146,7 @@ int parse_menu ( const char *text, struct menu **menu ) { * @ret flag Flag to set * @ret rc Return status code */ -int parse_flag ( const char *text __unused, int *flag ) { +int parse_flag ( char *text __unused, int *flag ) { /* Set flag */ *flag = 1; @@ -160,7 +161,7 @@ int parse_flag ( const char *text __unused, int *flag ) { * @ret key Key * @ret rc Return status code */ -int parse_key ( const char *text, unsigned int *key ) { +int parse_key ( char *text, unsigned int *key ) { /* Interpret single characters as being a literal key character */ if ( text[0] && ! text[1] ) { @@ -172,6 +173,83 @@ int parse_key ( const char *text, unsigned int *key ) { 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 ); +} + /** * Print command usage message * @@ -198,7 +276,7 @@ int reparse_options ( int argc, char **argv, struct command_descriptor *cmd, char shortopts[ cmd->num_options * 3 /* possible "::" */ + 1 /* "h" */ + 1 /* NUL */ ]; unsigned int shortopt_idx = 0; - int ( * parse ) ( const char *text, void *value ); + int ( * parse ) ( char *text, void *value ); void *value; unsigned int i; unsigned int j; diff --git a/src/core/settings.c b/src/core/settings.c index 54d48d32..87de0d1b 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -32,7 +32,10 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include +#include #include +#include #include /** @file @@ -259,7 +262,7 @@ static void autovivified_settings_free ( struct refcnt *refcnt ) { } /** - * Find child named settings block + * Find child settings block * * @v parent Parent settings block * @v name Name within this parent @@ -269,6 +272,9 @@ 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; @@ -276,27 +282,30 @@ struct settings * find_child_settings ( struct settings *parent, /* Look for child with matching name */ list_for_each_entry ( settings, &parent->children, siblings ) { if ( strcmp ( settings->name, name ) == 0 ) - return settings; + return settings_target ( settings ); } return NULL; } /** - * Find or create child named settings block + * Find or create child settings block * * @v parent Parent settings block * @v name Name within this parent * @ret settings Settings block, or NULL */ -static struct settings * autovivify_child_settings ( struct settings *parent, - const char *name ) { +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; @@ -328,6 +337,10 @@ 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 ); @@ -343,9 +356,7 @@ const char * settings_name ( struct settings *settings ) { * @ret settings Settings block, or NULL */ static struct settings * -parse_settings_name ( const char *name, - struct settings * ( * get_child ) ( struct settings *, - const char * ) ) { +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; @@ -357,20 +368,11 @@ parse_settings_name ( const char *name, /* Parse each name component in turn */ while ( remainder ) { - struct net_device *netdev; - subname = remainder; remainder = strchr ( subname, '.' ); if ( remainder ) *(remainder++) = '\0'; - - /* Special case "netX" root settings block */ - if ( ( subname == name_copy ) && ! strcmp ( subname, "netX" ) && - ( ( netdev = last_opened_netdev() ) != NULL ) ) - settings = get_child ( settings, netdev->name ); - else - settings = get_child ( settings, subname ); - + settings = get_child ( settings, subname ); if ( ! settings ) break; } @@ -379,7 +381,7 @@ parse_settings_name ( const char *name, } /** - * Find named settings block + * Find settings block * * @v name Name * @ret settings Settings block, or NULL @@ -458,10 +460,11 @@ int register_settings ( struct settings *settings, struct settings *parent, const char *name ) { struct settings *old_settings; - /* NULL parent => add to settings root */ + /* Sanity check */ assert ( settings != NULL ); - if ( parent == NULL ) - parent = &settings_root; + + /* Find target parent settings block */ + parent = settings_target ( parent ); /* Apply settings block name */ settings->name = name; @@ -521,6 +524,26 @@ void unregister_settings ( struct settings *settings ) { ****************************************************************************** */ +/** + * 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 * @@ -530,6 +553,10 @@ void unregister_settings ( struct settings *settings ) { */ int setting_applies ( struct settings *settings, 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 ); } @@ -547,9 +574,8 @@ int store_setting ( struct settings *settings, struct setting *setting, const void *data, size_t len ) { int rc; - /* NULL settings implies storing into the global settings root */ - if ( ! settings ) - settings = &settings_root; + /* Find target settings block */ + settings = settings_target ( settings ); /* Fail if tag does not apply to this settings block */ if ( ! setting_applies ( settings, setting ) ) @@ -607,9 +633,8 @@ static int fetch_setting_and_origin ( struct settings *settings, if ( origin ) *origin = NULL; - /* NULL settings implies starting at the global settings root */ - if ( ! settings ) - settings = &settings_root; + /* Find target settings block */ + settings = settings_target ( settings ); /* Sanity check */ if ( ! settings->op->fetch ) @@ -697,24 +722,19 @@ int fetch_setting_len ( struct settings *settings, struct setting *setting ) { * * The caller is responsible for eventually freeing the allocated * buffer. - * - * To allow the caller to distinguish between a non-existent setting - * and an error in allocating memory for the copy, this function will - * return success (and a NULL buffer pointer) for a non-existent - * setting. */ int fetch_setting_copy ( struct settings *settings, struct setting *setting, void **data ) { int len; - int check_len = 0; + int check_len; /* Avoid returning uninitialised data on error */ *data = NULL; - /* Fetch setting length, and return success if non-existent */ + /* Check existence, and fetch setting length */ len = fetch_setting_len ( settings, setting ); if ( len < 0 ) - return 0; + return len; /* Allocate buffer */ *data = malloc ( len ); @@ -830,38 +850,60 @@ int fetch_ipv4_setting ( struct settings *settings, struct setting *setting, /** * 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 signed_value Value, when interpreted as a signed integer - * @ret unsigned_value Value, when interpreted as an unsigned integer + * @ret value Numeric value * @ret len Length of setting, or negative error */ -static int numeric_setting_value ( const void *raw, size_t len, - signed long *signed_value, - unsigned long *unsigned_value ) { +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; - /* Range check */ - if ( len > sizeof ( long ) ) - return -ERANGE; - /* Convert to host-ordered longs */ is_negative = ( len && ( signed_bytes[0] < 0 ) ); - *signed_value = ( is_negative ? -1L : 0 ); - *unsigned_value = 0; + *value = ( ( is_signed && is_negative ) ? -1L : 0 ); + pad = *value; for ( i = 0 ; i < len ; i++ ) { byte = unsigned_bytes[i]; - *signed_value = ( ( *signed_value << 8 ) | byte ); - *unsigned_value = ( ( *unsigned_value << 8 ) | byte ); + *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, 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_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 * @@ -872,20 +914,9 @@ static int numeric_setting_value ( const void *raw, size_t len, */ int fetch_int_setting ( struct settings *settings, struct setting *setting, long *value ) { - unsigned long dummy; - long tmp; - int len; - /* Avoid returning uninitialised data on error */ - *value = 0; - - /* Fetch raw (network-ordered, variable-length) setting */ - len = fetch_setting ( settings, setting, &tmp, sizeof ( tmp ) ); - if ( len < 0 ) - return len; - - /* Extract numeric value */ - return numeric_setting_value ( &tmp, len, value, &dummy ); + return fetch_numeric_setting ( settings, setting, + ( ( unsigned long * ) value ), 1 ); } /** @@ -898,20 +929,8 @@ int fetch_int_setting ( struct settings *settings, struct setting *setting, */ int fetch_uint_setting ( struct settings *settings, struct setting *setting, unsigned long *value ) { - signed long dummy; - long tmp; - int len; - /* Avoid returning uninitialised data on error */ - *value = 0; - - /* Fetch raw (network-ordered, variable-length) setting */ - len = fetch_setting ( settings, setting, &tmp, sizeof ( tmp ) ); - if ( len < 0 ) - return len; - - /* Extract numeric value */ - return numeric_setting_value ( &tmp, len, &dummy, value ); + return fetch_numeric_setting ( settings, setting, value, 0 ); } /** @@ -922,9 +941,9 @@ int fetch_uint_setting ( struct settings *settings, struct setting *setting, * @ret value Setting value, or zero */ long fetch_intz_setting ( struct settings *settings, struct setting *setting ){ - long value; + unsigned long value; - fetch_int_setting ( settings, setting, &value ); + fetch_numeric_setting ( settings, setting, &value, 1 ); return value; } @@ -939,7 +958,7 @@ unsigned long fetch_uintz_setting ( struct settings *settings, struct setting *setting ) { unsigned long value; - fetch_uint_setting ( settings, setting, &value ); + fetch_numeric_setting ( settings, setting, &value, 0 ); return value; } @@ -969,6 +988,11 @@ int fetch_uuid_setting ( struct settings *settings, struct setting *setting, * @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 ); } @@ -1003,39 +1027,152 @@ int setting_cmp ( struct setting *a, struct setting *b ) { */ /** - * Fetch and format value of setting + * 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 ( 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 ( 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 ( 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 ( 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 type Settings type * @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, struct setting *setting, char *buf, size_t len ) { + void *raw; int raw_len; - int check_len; - int rc; + int ret; /* Fetch raw value */ - raw_len = fetch_setting_len ( settings, setting ); + raw_len = fetch_setting_copy ( settings, setting, &raw ); if ( raw_len < 0 ) { - rc = raw_len; - return rc; - } else { - uint8_t raw[raw_len]; - - /* Fetch raw value */ - check_len = fetch_setting ( settings, setting, raw, - sizeof ( raw ) ); - if ( check_len < 0 ) - return check_len; - assert ( check_len == raw_len ); - - /* Format value */ - return setting->type->format ( raw, sizeof ( raw ), buf, len ); + ret = raw_len; + goto err_fetch_copy; } + + /* Sanity check */ + assert ( setting->type != NULL ); + + /* Format setting */ + if ( ( ret = setting_format ( setting->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 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, struct setting *setting, + char **value ) { + int len; + int check_len; + + /* Avoid returning uninitialised data on error */ + *value = NULL; + + /* Check existence, and fetch formatted value length */ + len = fetchf_setting ( settings, setting, 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 ( settings, setting, *value, + ( len + 1 /* NUL */ ) ); + assert ( check_len == len ); + return len; } /** @@ -1048,6 +1185,7 @@ int fetchf_setting ( struct settings *settings, struct setting *setting, */ int storef_setting ( struct settings *settings, struct setting *setting, const char *value ) { + void *raw; int raw_len; int check_len; int rc; @@ -1056,21 +1194,118 @@ int storef_setting ( struct settings *settings, struct setting *setting, if ( ( ! value ) || ( ! value[0] ) ) return delete_setting ( settings, setting ); - /* Parse formatted value */ - raw_len = setting->type->parse ( value, NULL, 0 ); + /* 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; - return rc; - } else { - uint8_t raw[raw_len]; - - /* Parse formatted value */ - check_len = setting->type->parse ( value, raw, sizeof ( raw ) ); - assert ( check_len == raw_len ); - - /* Store raw value */ - return store_setting ( settings, setting, raw, sizeof ( raw ) ); + 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 value Numeric value to fill in + * @ret rc Return status code + */ +int fetchn_setting ( struct settings *settings, struct setting *setting, + unsigned long *value ) { + void *raw; + int raw_len; + int rc; + + /* Fetch raw value */ + raw_len = fetch_setting_copy ( settings, setting, &raw ); + if ( raw_len < 0 ) { + rc = raw_len; + goto err_fetch_copy; + } + + /* Sanity check */ + assert ( setting->type != NULL ); + + /* Numerate setting */ + if ( ( rc = setting_numerate ( setting->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, 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; } /****************************************************************************** @@ -1081,10 +1316,10 @@ int storef_setting ( struct settings *settings, struct setting *setting, */ /** - * Find named setting + * Find predefined setting * * @v name Name - * @ret setting Named setting, or NULL + * @ret setting Setting, or NULL */ struct setting * find_setting ( const char *name ) { struct setting *setting; @@ -1139,42 +1374,34 @@ static struct setting_type * find_setting_type ( const char *name ) { * @v get_child Function to find or create child settings block * @v settings Settings block to fill in * @v setting Setting to fill in - * @v default_type Default type to use, if none specified - * @v tmp_name Buffer for copy of setting name * @ret rc Return status code * * Interprets a name of the form * "[settings_name/]tag_name[:type_name]" and fills in the appropriate * fields. * - * The @c tmp_name buffer must be large enough to hold a copy of the - * setting name. + * Note that on success, this function will have modified the original + * setting @c name. */ -static int -parse_setting_name ( const char *name, - struct settings * ( * get_child ) ( struct settings *, - const char * ), - struct settings **settings, struct setting *setting, - struct setting_type *default_type, - char *tmp_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 *named_setting; + struct setting *predefined; + int rc; /* Set defaults */ *settings = &settings_root; memset ( setting, 0, sizeof ( *setting ) ); setting->name = ""; - setting->type = default_type; /* Split name into "[settings_name/]setting_name[:type_name]" */ - strcpy ( tmp_name, name ); - if ( ( setting_name = strchr ( tmp_name, '/' ) ) != NULL ) { + if ( ( setting_name = strchr ( name, '/' ) ) != NULL ) { *(setting_name++) = 0; - settings_name = tmp_name; + settings_name = name; } else { - setting_name = tmp_name; + setting_name = name; settings_name = NULL; } if ( ( type_name = strchr ( setting_name, ':' ) ) != NULL ) @@ -1186,7 +1413,8 @@ parse_setting_name ( const char *name, if ( *settings == NULL ) { DBG ( "Unrecognised settings block \"%s\" in \"%s\"\n", settings_name, name ); - return -ENODEV; + rc = -ENODEV; + goto err; } } @@ -1194,10 +1422,10 @@ parse_setting_name ( const char *name, setting->tag = parse_setting_tag ( setting_name ); setting->scope = (*settings)->default_scope; setting->name = setting_name; - for_each_table_entry ( named_setting, SETTINGS ) { - /* Matches a defined named setting; use that setting */ - if ( setting_cmp ( named_setting, setting ) == 0 ) { - memcpy ( setting, named_setting, sizeof ( *setting ) ); + 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; } } @@ -1208,11 +1436,20 @@ parse_setting_name ( const char *name, if ( setting->type == NULL ) { DBG ( "Invalid setting type \"%s\" in \"%s\"\n", type_name, name ); - return -ENOTSUP; + rc = -ENOTSUP; + goto err; } } return 0; + + err: + /* Restore original name */ + if ( settings_name ) + *( setting_name - 1 ) = '/'; + if ( type_name ) + *( type_name - 1 ) = ':'; + return rc; } /** @@ -1228,148 +1465,12 @@ int setting_name ( struct settings *settings, struct setting *setting, char *buf, size_t len ) { const char *name; - if ( ! settings ) - settings = &settings_root; - + settings = settings_target ( settings ); name = settings_name ( settings ); return snprintf ( buf, len, "%s%s%s:%s", name, ( name[0] ? "/" : "" ), setting->name, setting->type->name ); } -/** - * Store value of named setting - * - * @v name Name of setting - * @v default_type Default type to use, if none specified - * @v data Setting data, or NULL to clear setting - * @v len Length of setting data - * @ret rc Return status code - */ -int store_named_setting ( const char *name, struct setting_type *default_type, - const void *data, size_t len ) { - struct settings *settings; - struct setting setting; - char tmp_name[ strlen ( name ) + 1 ]; - int rc; - - /* Parse setting name */ - if ( ( rc = parse_setting_name ( name, autovivify_child_settings, - &settings, &setting, default_type, - tmp_name ) ) != 0 ) - return rc; - - /* Store setting */ - if ( ( rc = store_setting ( settings, &setting, data, len ) ) != 0 ) - return rc; - - return 0; -} - -/** - * Parse and store value of named setting - * - * @v name Name of setting - * @v default_type Default type to use, if none specified - * @v value Formatted setting data, or NULL - * @ret rc Return status code - */ -int storef_named_setting ( const char *name, struct setting_type *default_type, - const char *value ) { - struct settings *settings; - struct setting setting; - char tmp_name[ strlen ( name ) + 1 ]; - int rc; - - /* Parse setting name */ - if ( ( rc = parse_setting_name ( name, autovivify_child_settings, - &settings, &setting, default_type, - tmp_name ) ) != 0 ) - return rc; - - /* Store setting */ - if ( ( rc = storef_setting ( settings, &setting, value ) ) != 0 ) - return rc; - - return 0; -} - -/** - * Fetch and format value of named setting - * - * @v name Name of setting - * @v name_buf Buffer to contain canonicalised name - * @v name_len Length of canonicalised name buffer - * @v value_buf Buffer to contain formatted value - * @v value_len Length of formatted value buffer - * @ret len Length of formatted value, or negative error - */ -int fetchf_named_setting ( const char *name, - char *name_buf, size_t name_len, - char *value_buf, size_t value_len ) { - struct settings *settings; - struct setting setting; - struct settings *origin; - char tmp_name[ strlen ( name ) + 1 ]; - int len; - int rc; - - /* Parse setting name */ - if ( ( rc = parse_setting_name ( name, find_child_settings, &settings, - &setting, NULL, tmp_name ) ) != 0 ) - return rc; - - /* Fetch setting */ - if ( ( len = fetchf_setting ( settings, &setting, value_buf, - value_len ) ) < 0 ) - return len; - - /* Construct setting name */ - origin = fetch_setting_origin ( settings, &setting ); - assert ( origin != NULL ); - setting_name ( origin, &setting, name_buf, name_len ); - - return len; -} - -/** - * Fetch and format copy of value of named setting - * - * @v name Name of setting - * @v data 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. - * - * To allow the caller to distinguish between a non-existent setting - * and an error in allocating memory for the copy, this function will - * return success (and a NULL buffer pointer) for a non-existent - * setting. - */ -int fetchf_named_setting_copy ( const char *name, char **data ) { - int len; - int check_len; - - /* Avoid returning uninitialised data on error */ - *data = NULL; - - /* Fetch formatted value length, and return success if non-existent */ - len = fetchf_named_setting ( name, NULL, 0, NULL, 0 ); - if ( len < 0 ) - return 0; - - /* Allocate buffer */ - *data = malloc ( len + 1 /* NUL */ ); - if ( ! *data ) - return -ENOMEM; - - /* Fetch formatted value */ - check_len = fetchf_named_setting ( name, NULL, 0, *data, - ( len + 1 /* NUL */ ) ); - assert ( check_len == len ); - return len; -} - /****************************************************************************** * * Setting types @@ -1380,12 +1481,14 @@ int fetchf_named_setting_copy ( const char *name, char **data ) { /** * 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 char *value, void *buf, size_t len ) { +static int parse_string_setting ( 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 */ @@ -1399,13 +1502,15 @@ static int parse_string_setting ( const char *value, void *buf, size_t 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 void *raw, size_t raw_len, char *buf, +static int format_string_setting ( struct setting_type *type __unused, + const void *raw, size_t raw_len, char *buf, size_t len ) { /* Copy string to buffer, and terminate */ @@ -1427,13 +1532,14 @@ struct setting_type setting_type_string __setting_type = { /** * Parse URI-encoded 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_uristring_setting ( const char *value, void *buf, - size_t len ) { +static int parse_uristring_setting ( struct setting_type *type __unused, + const char *value, void *buf, size_t len ){ char tmp[ len + 1 /* NUL */ ]; size_t raw_len; @@ -1451,13 +1557,15 @@ static int parse_uristring_setting ( const char *value, void *buf, /** * Format URI-encoded 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_uristring_setting ( const void *raw, size_t raw_len, +static int format_uristring_setting ( struct setting_type *type __unused, + const void *raw, size_t raw_len, char *buf, size_t len ) { char tmp[ raw_len + 1 /* NUL */ ]; @@ -1479,12 +1587,14 @@ struct setting_type setting_type_uristring __setting_type = { /** * Parse IPv4 address 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_ipv4_setting ( const char *value, void *buf, size_t len ) { +static int parse_ipv4_setting ( struct setting_type *type __unused, + const char *value, void *buf, size_t len ) { struct in_addr ipv4; /* Parse IPv4 address */ @@ -1502,13 +1612,15 @@ static int parse_ipv4_setting ( const char *value, void *buf, size_t len ) { /** * Format IPv4 address 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_ipv4_setting ( const void *raw, size_t raw_len, char *buf, +static int format_ipv4_setting ( struct setting_type *type __unused, + const void *raw, size_t raw_len, char *buf, size_t len ) { const struct in_addr *ipv4 = raw; @@ -1525,28 +1637,100 @@ struct setting_type setting_type_ipv4 __setting_type = { }; /** - * Parse integer setting value + * Integer setting type indices * - * @v value Formatted setting value + * These indexes are defined such that (1<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 ( 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 ( 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 - * @v size Integer size, in bytes * @ret len Length of raw value, or negative error */ -static int parse_int_setting ( const char *value, void *buf, size_t len, - unsigned int size ) { +static int denumerate_int_setting ( 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; - char *endp; - /* Parse value */ - u.num = htonl ( strtoul ( value, &endp, 0 ) ); - if ( *endp ) - return -EINVAL; - - /* Copy to buffer */ + u.num = htonl ( value ); if ( len > size ) len = size; memcpy ( buf, &u.bytes[ sizeof ( u ) - size ], len ); @@ -1555,64 +1739,69 @@ static int parse_int_setting ( const char *value, void *buf, size_t len, } /** - * Parse 8-bit integer setting value + * Convert setting value to number * - * @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 + * @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 parse_int8_setting ( const char *value, void *buf, size_t len ) { - return parse_int_setting ( value, buf, len, sizeof ( uint8_t ) ); +static int numerate_int_setting ( 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 16-bit integer setting value + * 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 - * @v size Integer size, in bytes * @ret len Length of raw value, or negative error */ -static int parse_int16_setting ( const char *value, void *buf, size_t len ) { - return parse_int_setting ( value, buf, len, sizeof ( uint16_t ) ); -} +static int parse_int_setting ( struct setting_type *type, const char *value, + void *buf, size_t len ) { + char *endp; + unsigned long num_value; -/** - * Parse 32-bit integer setting value - * - * @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_int32_setting ( const char *value, void *buf, size_t len ) { - return parse_int_setting ( value, buf, len, sizeof ( uint32_t ) ); + /* 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 void *raw, size_t raw_len, char *buf, - size_t len ) { - signed long value; - unsigned long dummy; - int check_len; +static int format_int_setting ( struct setting_type *type, const void *raw, + size_t raw_len, char *buf, size_t len ) { + unsigned long value; + int ret; /* Extract numeric value */ - check_len = numeric_setting_value ( raw, raw_len, &value, &dummy ); - if ( check_len < 0 ) - return check_len; - assert ( check_len == ( int ) raw_len ); + if ( ( ret = type->numerate ( type, raw, raw_len, &value ) ) < 0 ) + return ret; /* Format value */ return snprintf ( buf, len, "%ld", value ); @@ -1621,113 +1810,90 @@ static int format_int_setting ( const void *raw, size_t raw_len, char *buf, /** * 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 void *raw, size_t raw_len, char *buf, - size_t len ) { - signed long dummy; +static int format_uint_setting ( struct setting_type *type, const void *raw, + size_t raw_len, char *buf, size_t len ) { unsigned long value; - int check_len; + int ret; /* Extract numeric value */ - check_len = numeric_setting_value ( raw, raw_len, &dummy, &value ); - if ( check_len < 0 ) - return check_len; - assert ( check_len == ( int ) raw_len ); + if ( ( ret = type->numerate ( type, raw, raw_len, &value ) ) < 0 ) + return ret; /* Format value */ return snprintf ( buf, len, "%#lx", value ); } -/** A signed 8-bit integer setting type */ -struct setting_type setting_type_int8 __setting_type = { - .name = "int8", - .parse = parse_int8_setting, - .format = format_int_setting, -}; - -/** A signed 16-bit integer setting type */ -struct setting_type setting_type_int16 __setting_type = { - .name = "int16", - .parse = parse_int16_setting, - .format = format_int_setting, -}; - -/** A signed 32-bit integer setting type */ -struct setting_type setting_type_int32 __setting_type = { - .name = "int32", - .parse = parse_int32_setting, - .format = format_int_setting, -}; - -/** An unsigned 8-bit integer setting type */ -struct setting_type setting_type_uint8 __setting_type = { - .name = "uint8", - .parse = parse_int8_setting, - .format = format_uint_setting, -}; - -/** An unsigned 16-bit integer setting type */ -struct setting_type setting_type_uint16 __setting_type = { - .name = "uint16", - .parse = parse_int16_setting, - .format = format_uint_setting, -}; - -/** An unsigned 32-bit integer setting type */ -struct setting_type setting_type_uint32 __setting_type = { - .name = "uint32", - .parse = parse_int32_setting, - .format = format_uint_setting, -}; +/** + * 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, \ +} /** - * Parse hex string setting value + * Define an unsigned integer 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 + * @v index Integer setting type index + * @ret type Setting type */ -static int parse_hex_setting ( const char *value, void *buf, size_t len ) { - char *ptr = ( char * ) value; - uint8_t *bytes = buf; - unsigned int count = 0; - uint8_t byte; - - while ( 1 ) { - byte = strtoul ( ptr, &ptr, 16 ); - if ( count++ < len ) - *bytes++ = byte; - switch ( *ptr ) { - case '\0' : - return count; - case ':' : - case '-' : - ptr++; - break; - default : - return -EINVAL; - } - } +#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 */ +struct setting_type setting_type_int8 __setting_type = + SETTING_TYPE_INT ( SETTING_TYPE_INT8 ); + +/** A signed 16-bit integer setting type */ +struct setting_type setting_type_int16 __setting_type = + SETTING_TYPE_INT ( SETTING_TYPE_INT16 ); + +/** A signed 32-bit integer setting type */ +struct setting_type setting_type_int32 __setting_type = + SETTING_TYPE_INT ( SETTING_TYPE_INT32 ); + +/** An unsigned 8-bit integer setting type */ +struct setting_type setting_type_uint8 __setting_type = + SETTING_TYPE_UINT ( SETTING_TYPE_INT8 ); + +/** An unsigned 16-bit integer setting type */ +struct setting_type setting_type_uint16 __setting_type = + SETTING_TYPE_UINT ( SETTING_TYPE_INT16 ); + +/** An unsigned 32-bit integer setting type */ +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 - * @v delimiter Byte delimiter * @ret len Length of formatted value, or negative error */ -static int format_hex_setting ( const void *raw, size_t raw_len, char *buf, - size_t len, const char *delimiter ) { +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; @@ -1742,32 +1908,98 @@ static int format_hex_setting ( const void *raw, size_t raw_len, char *buf, 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 ( 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 void *raw, size_t raw_len, +static int format_hex_colon_setting ( 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, ":" ); + 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 ( 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 void *raw, size_t raw_len, +static int format_hex_hyphen_setting ( 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, "-" ); + 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 ( 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 ( 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) */ @@ -1780,33 +2012,29 @@ struct setting_type setting_type_hex __setting_type = { /** A hex-string setting (hyphen-delimited) */ struct setting_type setting_type_hexhyp __setting_type = { .name = "hexhyp", - .parse = parse_hex_setting, + .parse = parse_hex_hyphen_setting, .format = format_hex_hyphen_setting, }; -/** - * Parse UUID setting value - * - * @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_uuid_setting ( const char *value __unused, - void *buf __unused, size_t len __unused ) { - return -ENOTSUP; -} +/** A hex-string setting (non-delimited) */ +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 void *raw, size_t raw_len, char *buf, +static int format_uuid_setting ( struct setting_type *type __unused, + const void *raw, size_t raw_len, char *buf, size_t len ) { const union uuid *uuid = raw; @@ -1821,10 +2049,42 @@ static int format_uuid_setting ( const void *raw, size_t raw_len, char *buf, /** UUID setting type */ struct setting_type setting_type_uuid __setting_type = { .name = "uuid", - .parse = parse_uuid_setting, .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 ( 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 */ +struct setting_type setting_type_busdevfn __setting_type = { + .name = "busdevfn", + .format = format_busdevfn_setting, +}; + /****************************************************************************** * * Setting expansion @@ -1842,15 +2102,18 @@ struct setting_type setting_type_uuid __setting_type = { * 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; - int setting_len; - int new_len; + char *value; char *tmp; + int new_len; + int rc; /* Obtain temporary modifiable copy of string */ expstr = strdup ( string ); @@ -1880,27 +2143,27 @@ char * expand_settings ( const char *string ) { *end = '\0'; tail = ( end + 1 ); - /* Determine setting length */ - setting_len = fetchf_named_setting ( name, NULL, 0, NULL, 0 ); - if ( setting_len < 0 ) - setting_len = 0; /* Treat error as empty setting */ - - /* Read setting into temporary buffer */ - { - char setting_buf[ setting_len + 1 ]; - - setting_buf[0] = '\0'; - fetchf_named_setting ( name, NULL, 0, setting_buf, - sizeof ( setting_buf ) ); - - /* Construct expanded string and discard old string */ - tmp = expstr; - new_len = asprintf ( &expstr, "%s%s%s", - head, setting_buf, tail ); - free ( tmp ); - if ( new_len < 0 ) - return NULL; + /* 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, &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; @@ -2070,11 +2333,32 @@ static int platform_fetch ( void *data, size_t len ) { return ( sizeof ( platform ) - 1 /* NUL */ ); } +/** Version setting */ +struct setting version_setting __setting ( SETTING_MISC ) = { + .name = "version", + .description = "Version", + .type = &setting_type_string, + .scope = &builtin_scope, +}; + +/** + * 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 ) ); +} + /** List of built-in setting operations */ static struct builtin_setting_operation builtin_setting_operations[] = { { &errno_setting, errno_fetch }, { &buildarch_setting, buildarch_fetch }, { &platform_setting, platform_fetch }, + { &version_setting, version_fetch }, }; /** diff --git a/src/crypto/clientcert.c b/src/crypto/clientcert.c index 5ce1f6c1..6f6bf113 100644 --- a/src/crypto/clientcert.c +++ b/src/crypto/clientcert.c @@ -116,7 +116,6 @@ static int clientcert_apply_settings ( void ) { static void *cert = NULL; static void *key = NULL; int len; - int rc; /* Allow client certificate to be overridden only if * not explicitly specified at build time. @@ -129,14 +128,8 @@ static int clientcert_apply_settings ( void ) { /* Fetch new client certificate, if any */ free ( cert ); - len = fetch_setting_copy ( NULL, &cert_setting, &cert ); - if ( len < 0 ) { - rc = len; - DBGC ( &client_certificate, "CLIENTCERT cannot fetch " - "client certificate: %s\n", strerror ( rc ) ); - return rc; - } - if ( cert ) { + if ( ( len = fetch_setting_copy ( NULL, &cert_setting, + &cert ) ) >= 0 ) { client_certificate.data = cert; client_certificate.len = len; } @@ -147,14 +140,8 @@ static int clientcert_apply_settings ( void ) { /* Fetch new client private key, if any */ free ( key ); - len = fetch_setting_copy ( NULL, &privkey_setting, &key ); - if ( len < 0 ) { - rc = len; - DBGC ( &client_certificate, "CLIENTCERT cannot fetch " - "client private key: %s\n", strerror ( rc ) ); - return rc; - } - if ( key ) { + if ( ( len = fetch_setting_copy ( NULL, &privkey_setting, + &key ) ) >= 0 ) { client_private_key.data = key; client_private_key.len = len; } diff --git a/src/crypto/rootcert.c b/src/crypto/rootcert.c index 30ca170f..2aa31334 100644 --- a/src/crypto/rootcert.c +++ b/src/crypto/rootcert.c @@ -91,7 +91,6 @@ struct x509_root root_certificates = { static void rootcert_init ( void ) { void *external = NULL; int len; - int rc; /* Allow trusted root certificates to be overridden only if * not explicitly specified at build time. @@ -101,21 +100,8 @@ static void rootcert_init ( void ) { /* Fetch copy of "trust" setting, if it exists. This * memory will never be freed. */ - len = fetch_setting_copy ( NULL, &trust_setting, &external ); - if ( len < 0 ) { - rc = len; - DBGC ( &root_certificates, "ROOTCERT cannot fetch " - "trusted root certificate fingerprints: %s\n", - strerror ( rc ) ); - /* No way to prevent startup; fail safe by - * trusting no certificates. - */ - root_certificates.count = 0; - return; - } - - /* Use certificates from "trust" setting, if present */ - if ( external ) { + if ( ( len = fetch_setting_copy ( NULL, &trust_setting, + &external ) ) >= 0 ) { root_certificates.fingerprints = external; root_certificates.count = ( len / FINGERPRINT_LEN ); } diff --git a/src/drivers/bus/pci.c b/src/drivers/bus/pci.c index 7bd353d8..4a8d00b5 100644 --- a/src/drivers/bus/pci.c +++ b/src/drivers/bus/pci.c @@ -171,8 +171,20 @@ void adjust_pci_device ( struct pci_device *pci ) { * @ret rc Return status code */ int pci_read_config ( struct pci_device *pci ) { + uint16_t busdevfn; + uint8_t hdrtype; uint32_t tmp; + /* Ignore all but the first function on non-multifunction devices */ + if ( PCI_FUNC ( pci->busdevfn ) != 0 ) { + busdevfn = pci->busdevfn; + pci->busdevfn = PCI_FIRST_FUNC ( pci->busdevfn ); + pci_read_config_byte ( pci, PCI_HEADER_TYPE, &hdrtype ); + pci->busdevfn = busdevfn; + if ( ! ( hdrtype & 0x80 ) ) + return -ENODEV; + } + /* Check for physical device presence */ pci_read_config_dword ( pci, PCI_VENDOR_ID, &tmp ); if ( ( tmp == 0xffffffff ) || ( tmp == 0 ) ) @@ -203,6 +215,32 @@ int pci_read_config ( struct pci_device *pci ) { return 0; } +/** + * Find next device on PCI bus + * + * @v pci PCI device to fill in + * @v busdevfn Starting bus:dev.fn address + * @ret busdevfn Bus:dev.fn address of next PCI device, or negative error + */ +int pci_find_next ( struct pci_device *pci, unsigned int busdevfn ) { + static unsigned int end; + int rc; + + /* Determine number of PCI buses */ + if ( ! end ) + end = PCI_BUSDEVFN ( pci_num_bus(), 0, 0 ); + + /* Find next PCI device, if any */ + for ( ; busdevfn < end ; busdevfn++ ) { + memset ( pci, 0, sizeof ( *pci ) ); + pci_init ( pci, busdevfn ); + if ( ( rc = pci_read_config ( pci ) ) == 0 ) + return busdevfn; + } + + return -ENODEV; +} + /** * Find driver for PCI device * @@ -276,14 +314,10 @@ void pci_remove ( struct pci_device *pci ) { */ static int pcibus_probe ( struct root_device *rootdev ) { struct pci_device *pci = NULL; - unsigned int num_bus; - unsigned int busdevfn; - uint8_t hdrtype = 0; + int busdevfn = 0; int rc; - num_bus = pci_num_bus(); - for ( busdevfn = 0 ; busdevfn < PCI_BUSDEVFN ( num_bus, 0, 0 ) ; - busdevfn++ ) { + for ( busdevfn = 0 ; 1 ; busdevfn++ ) { /* Allocate struct pci_device */ if ( ! pci ) @@ -292,22 +326,11 @@ static int pcibus_probe ( struct root_device *rootdev ) { rc = -ENOMEM; goto err; } - memset ( pci, 0, sizeof ( *pci ) ); - pci_init ( pci, busdevfn ); - - /* Skip all but the first function on - * non-multifunction cards - */ - if ( PCI_FUNC ( busdevfn ) == 0 ) { - pci_read_config_byte ( pci, PCI_HEADER_TYPE, - &hdrtype ); - } else if ( ! ( hdrtype & 0x80 ) ) { - continue; - } - /* Read device configuration */ - if ( ( rc = pci_read_config ( pci ) ) != 0 ) - continue; + /* Find next PCI device, if any */ + busdevfn = pci_find_next ( pci, busdevfn ); + if ( busdevfn < 0 ) + break; /* Look for a driver */ if ( ( rc = pci_find_driver ( pci ) ) != 0 ) { diff --git a/src/drivers/bus/pci_settings.c b/src/drivers/bus/pci_settings.c new file mode 100644 index 00000000..a4d7b933 --- /dev/null +++ b/src/drivers/bus/pci_settings.c @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2013 Michael Brown . + * + * 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 +#include +#include +#include +#include + +/** @file + * + * PCI device settings + * + */ + +/** PCI device settings scope */ +static struct settings_scope pci_settings_scope; + +/** + * Check applicability of PCI device setting + * + * @v settings Settings block + * @v setting Setting + * @ret applies Setting applies within this settings block + */ +static int pci_settings_applies ( struct settings *settings __unused, + struct setting *setting ) { + + return ( setting->scope == &pci_settings_scope ); +} + +/** + * Fetch value of PCI device 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 pci_settings_fetch ( struct settings *settings __unused, + struct setting *setting, + void *data, size_t len ) { + struct pci_device pci; + unsigned int tag_busdevfn; + unsigned int tag_offset; + unsigned int tag_len; + unsigned int i; + + /* Extract busdevfn, offset, and length from tag */ + tag_busdevfn = ( ( setting->tag >> 16 ) & 0xffff ); + tag_offset = ( ( setting->tag >> 8 ) & 0xff ); + tag_len = ( ( setting->tag >> 0 ) & 0xff ); + + /* Locate PCI device */ + memset ( &pci, 0, sizeof ( pci ) ); + pci_init ( &pci, tag_busdevfn ); + DBG ( PCI_FMT " reading %#02x+%#x\n", PCI_ARGS ( &pci ), + tag_offset, tag_len ); + + /* Read data one byte at a time, in reverse order (since PCI + * is little-endian and iPXE settings are essentially + * big-endian). + */ + tag_offset += tag_len; + for ( i = 0 ; ( ( i < tag_len ) && ( i < len ) ); i++ ) { + pci_read_config_byte ( &pci, --tag_offset, data++ ); + } + + /* Set type to ":hexraw" if not already specified */ + if ( ! setting->type ) + setting->type = &setting_type_hexraw; + + return tag_len; +} + +/** PCI device settings operations */ +static struct settings_operations pci_settings_operations = { + .applies = pci_settings_applies, + .fetch = pci_settings_fetch, +}; + +/** PCI device settings */ +static struct settings pci_settings = { + .refcnt = NULL, + .siblings = LIST_HEAD_INIT ( pci_settings.siblings ), + .children = LIST_HEAD_INIT ( pci_settings.children ), + .op = &pci_settings_operations, + .default_scope = &pci_settings_scope, +}; + +/** Initialise PCI device settings */ +static void pci_settings_init ( void ) { + int rc; + + if ( ( rc = register_settings ( &pci_settings, NULL, "pci" ) ) != 0 ) { + DBG ( "PCI could not register settings: %s\n", + strerror ( rc ) ); + return; + } +} + +/** PCI device settings initialiser */ +struct init_fn pci_settings_init_fn __init_fn ( INIT_NORMAL ) = { + .initialise = pci_settings_init, +}; diff --git a/src/drivers/net/ath/ath.h b/src/drivers/net/ath/ath.h index 5a19a19c..42ad59f7 100644 --- a/src/drivers/net/ath/ath.h +++ b/src/drivers/net/ath/ath.h @@ -20,6 +20,8 @@ #ifndef ATH_H #define ATH_H +FILE_LICENCE ( BSD2 ); + #include #include diff --git a/src/drivers/net/ath/ath9k/ani.h b/src/drivers/net/ath/ath9k/ani.h index 9a333b5f..dbd4d4d5 100644 --- a/src/drivers/net/ath/ath9k/ani.h +++ b/src/drivers/net/ath/ath9k/ani.h @@ -20,6 +20,8 @@ #ifndef ANI_H #define ANI_H +FILE_LICENCE ( BSD2 ); + #define HAL_PROCESS_ANI 0x00000001 #define DO_ANI(ah) (((ah)->proc_phyerr & HAL_PROCESS_ANI) && ah->curchan) diff --git a/src/drivers/net/ath/ath9k/ar5008_initvals.h b/src/drivers/net/ath/ath9k/ar5008_initvals.h index 479aed54..fcc15565 100644 --- a/src/drivers/net/ath/ath9k/ar5008_initvals.h +++ b/src/drivers/net/ath/ath9k/ar5008_initvals.h @@ -14,6 +14,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +FILE_LICENCE ( BSD2 ); + static const u32 ar5416Modes[][6] = { {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0}, {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0}, diff --git a/src/drivers/net/ath/ath9k/ar9001_initvals.h b/src/drivers/net/ath/ath9k/ar9001_initvals.h index 7dca4744..6c1ccd50 100644 --- a/src/drivers/net/ath/ath9k/ar9001_initvals.h +++ b/src/drivers/net/ath/ath9k/ar9001_initvals.h @@ -14,6 +14,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +FILE_LICENCE ( BSD2 ); + static const u32 ar5416Modes_9100[][6] = { {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0}, {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0}, diff --git a/src/drivers/net/ath/ath9k/ar9002_initvals.h b/src/drivers/net/ath/ath9k/ar9002_initvals.h index dd121513..d7a5ac09 100644 --- a/src/drivers/net/ath/ath9k/ar9002_initvals.h +++ b/src/drivers/net/ath/ath9k/ar9002_initvals.h @@ -14,6 +14,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +FILE_LICENCE ( BSD2 ); + static const u32 ar9280Modes_9280_2[][6] = { {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0}, {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0}, diff --git a/src/drivers/net/ath/ath9k/ar9002_phy.h b/src/drivers/net/ath/ath9k/ar9002_phy.h index 453af6dc..71d9162c 100644 --- a/src/drivers/net/ath/ath9k/ar9002_phy.h +++ b/src/drivers/net/ath/ath9k/ar9002_phy.h @@ -16,6 +16,8 @@ #ifndef AR9002_PHY_H #define AR9002_PHY_H +FILE_LICENCE ( BSD2 ); + #define AR_PHY_TEST 0x9800 #define PHY_AGC_CLR 0x10000000 #define RFSILENT_BB 0x00002000 diff --git a/src/drivers/net/ath/ath9k/ar9003_eeprom.h b/src/drivers/net/ath/ath9k/ar9003_eeprom.h index 393f2de5..f0387923 100644 --- a/src/drivers/net/ath/ath9k/ar9003_eeprom.h +++ b/src/drivers/net/ath/ath9k/ar9003_eeprom.h @@ -20,6 +20,8 @@ #ifndef AR9003_EEPROM_H #define AR9003_EEPROM_H +FILE_LICENCE ( BSD2 ); + #define AR9300_EEP_VER 0xD000 #define AR9300_EEP_VER_MINOR_MASK 0xFFF #define AR9300_EEP_MINOR_VER_1 0x1 diff --git a/src/drivers/net/ath/ath9k/ath9k.h b/src/drivers/net/ath/ath9k/ath9k.h index d2dce9b7..36dc97e9 100644 --- a/src/drivers/net/ath/ath9k/ath9k.h +++ b/src/drivers/net/ath/ath9k/ath9k.h @@ -20,6 +20,8 @@ #ifndef ATH9K_H #define ATH9K_H +FILE_LICENCE ( BSD2 ); + #include "common.h" /* diff --git a/src/drivers/net/ath/ath9k/ath9k_ar9002_hw.c b/src/drivers/net/ath/ath9k/ath9k_ar9002_hw.c index cc4edece..85d0c7de 100644 --- a/src/drivers/net/ath/ath9k/ath9k_ar9002_hw.c +++ b/src/drivers/net/ath/ath9k/ath9k_ar9002_hw.c @@ -17,6 +17,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +FILE_LICENCE ( BSD2 ); + #include "hw.h" #include "ar5008_initvals.h" #include "ar9001_initvals.h" diff --git a/src/drivers/net/ath/ath9k/ath9k_init.c b/src/drivers/net/ath/ath9k/ath9k_init.c index 93fbe773..03de7701 100644 --- a/src/drivers/net/ath/ath9k/ath9k_init.c +++ b/src/drivers/net/ath/ath9k/ath9k_init.c @@ -17,6 +17,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +FILE_LICENCE ( BSD2 ); + #include #include #include diff --git a/src/drivers/net/ath/ath9k/calib.h b/src/drivers/net/ath/ath9k/calib.h index 5bf50f4a..b811accf 100644 --- a/src/drivers/net/ath/ath9k/calib.h +++ b/src/drivers/net/ath/ath9k/calib.h @@ -20,6 +20,8 @@ #ifndef CALIB_H #define CALIB_H +FILE_LICENCE ( BSD2 ); + #include "hw.h" #define AR_PHY_CCA_FILTERWINDOW_LENGTH_INIT 3 diff --git a/src/drivers/net/ath/ath9k/common.h b/src/drivers/net/ath/ath9k/common.h index 009f8036..0fe3b5be 100644 --- a/src/drivers/net/ath/ath9k/common.h +++ b/src/drivers/net/ath/ath9k/common.h @@ -17,6 +17,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +FILE_LICENCE ( BSD2 ); + #include "../ath.h" #include "hw.h" diff --git a/src/drivers/net/ath/ath9k/eeprom.h b/src/drivers/net/ath/ath9k/eeprom.h index b451ebd7..8a48d6e5 100644 --- a/src/drivers/net/ath/ath9k/eeprom.h +++ b/src/drivers/net/ath/ath9k/eeprom.h @@ -20,6 +20,8 @@ #ifndef EEPROM_H #define EEPROM_H +FILE_LICENCE ( BSD2 ); + #define AR_EEPROM_MODAL_SPURS 5 #include "../ath.h" diff --git a/src/drivers/net/ath/ath9k/hw-ops.h b/src/drivers/net/ath/ath9k/hw-ops.h index 65cff3b0..51c7b08e 100644 --- a/src/drivers/net/ath/ath9k/hw-ops.h +++ b/src/drivers/net/ath/ath9k/hw-ops.h @@ -17,6 +17,8 @@ #ifndef ATH9K_HW_OPS_H #define ATH9K_HW_OPS_H +FILE_LICENCE ( BSD2 ); + #include "hw.h" /* Hardware core and driver accessible callbacks */ diff --git a/src/drivers/net/ath/ath9k/hw.h b/src/drivers/net/ath/ath9k/hw.h index c10df388..05107469 100644 --- a/src/drivers/net/ath/ath9k/hw.h +++ b/src/drivers/net/ath/ath9k/hw.h @@ -20,6 +20,8 @@ #ifndef HW_H #define HW_H +FILE_LICENCE ( BSD2 ); + #include #include "mac.h" diff --git a/src/drivers/net/ath/ath9k/mac.h b/src/drivers/net/ath/ath9k/mac.h index 5899230e..0c0a7594 100644 --- a/src/drivers/net/ath/ath9k/mac.h +++ b/src/drivers/net/ath/ath9k/mac.h @@ -20,6 +20,8 @@ #ifndef MAC_H #define MAC_H +FILE_LICENCE ( BSD2 ); + #include #define RXSTATUS_RATE(ah, ads) (AR_SREV_5416_20_OR_LATER(ah) ? \ diff --git a/src/drivers/net/ath/ath9k/phy.h b/src/drivers/net/ath/ath9k/phy.h index 8b380305..28f59ecd 100644 --- a/src/drivers/net/ath/ath9k/phy.h +++ b/src/drivers/net/ath/ath9k/phy.h @@ -17,6 +17,8 @@ #ifndef PHY_H #define PHY_H +FILE_LICENCE ( BSD2 ); + #define CHANSEL_DIV 15 #define CHANSEL_2G(_freq) (((_freq) * 0x10000) / CHANSEL_DIV) #define CHANSEL_5G(_freq) (((_freq) * 0x8000) / CHANSEL_DIV) diff --git a/src/drivers/net/ath/ath9k/reg.h b/src/drivers/net/ath/ath9k/reg.h index c18ee992..67762b6d 100644 --- a/src/drivers/net/ath/ath9k/reg.h +++ b/src/drivers/net/ath/ath9k/reg.h @@ -17,6 +17,8 @@ #ifndef REG_H #define REG_H +FILE_LICENCE ( BSD2 ); + #include "../reg.h" #define AR_CR 0x0008 diff --git a/src/drivers/net/ath/reg.h b/src/drivers/net/ath/reg.h index 484a8f1c..7982f434 100644 --- a/src/drivers/net/ath/reg.h +++ b/src/drivers/net/ath/reg.h @@ -20,6 +20,8 @@ #ifndef ATH_REGISTERS_H #define ATH_REGISTERS_H +FILE_LICENCE ( BSD2 ); + #define AR_MIBC 0x0040 #define AR_MIBC_COW 0x00000001 #define AR_MIBC_FMC 0x00000002 diff --git a/src/drivers/net/ath/regd.h b/src/drivers/net/ath/regd.h index 6954c5f3..fd09a0c8 100644 --- a/src/drivers/net/ath/regd.h +++ b/src/drivers/net/ath/regd.h @@ -20,6 +20,8 @@ #ifndef REGD_H #define REGD_H +FILE_LICENCE ( BSD2 ); + #include "ath.h" enum ctl_group { diff --git a/src/drivers/net/intel.c b/src/drivers/net/intel.c index c3a7d407..569f3911 100644 --- a/src/drivers/net/intel.c +++ b/src/drivers/net/intel.c @@ -247,10 +247,15 @@ static int intel_fetch_mac ( struct intel_nic *intel, uint8_t *hw_addr ) { */ static void __attribute__ (( unused )) intel_diag ( struct intel_nic *intel ) { - DBGC ( intel, "INTEL %p TDH=%04x TDT=%04x RDH=%04x RDT=%04x\n", intel, + DBGC ( intel, "INTEL %p TX %04x(%02x)/%04x(%02x) " + "RX %04x(%02x)/%04x(%02x)\n", intel, + ( intel->tx.cons & 0xffff ), readl ( intel->regs + intel->tx.reg + INTEL_xDH ), + ( intel->tx.prod & 0xffff ), readl ( intel->regs + intel->tx.reg + INTEL_xDT ), + ( intel->rx.cons & 0xffff ), readl ( intel->regs + intel->rx.reg + INTEL_xDH ), + ( intel->rx.prod & 0xffff ), readl ( intel->regs + intel->rx.reg + INTEL_xDT ) ); } diff --git a/src/drivers/net/realtek.c b/src/drivers/net/realtek.c index 498c233f..867efbcd 100644 --- a/src/drivers/net/realtek.c +++ b/src/drivers/net/realtek.c @@ -907,13 +907,15 @@ static void realtek_poll_rx ( struct net_device *netdev ) { len = ( le16_to_cpu ( rx->length ) & RTL_DESC_SIZE_MASK ); iob_put ( iobuf, ( len - 4 /* strip CRC */ ) ); - DBGC2 ( rtl, "REALTEK %p RX %d complete (length %zd)\n", - rtl, rx_idx, len ); - /* Hand off to network stack */ if ( rx->flags & cpu_to_le16 ( RTL_DESC_RES ) ) { + DBGC ( rtl, "REALTEK %p RX %d error (length %zd, " + "flags %04x)\n", rtl, rx_idx, len, + le16_to_cpu ( rx->flags ) ); netdev_rx_err ( netdev, iobuf, -EIO ); } else { + DBGC2 ( rtl, "REALTEK %p RX %d complete (length " + "%zd)\n", rtl, rx_idx, len ); netdev_rx ( netdev, iobuf ); } rtl->rx.cons++; diff --git a/src/drivers/net/realtek.h b/src/drivers/net/realtek.h index 1cd85d95..a17f963f 100644 --- a/src/drivers/net/realtek.h +++ b/src/drivers/net/realtek.h @@ -226,7 +226,8 @@ enum realtek_legacy_status { #define RTL_NUM_RX_DESC 4 /** Receive buffer length */ -#define RTL_RX_MAX_LEN ( ETH_FRAME_LEN + 4 /* VLAN */ + 4 /* CRC */ ) +#define RTL_RX_MAX_LEN \ + ( ETH_FRAME_LEN + 4 /* VLAN */ + 4 /* CRC */ + 4 /* extra space */ ) /** A Realtek descriptor ring */ struct realtek_ring { diff --git a/src/drivers/net/rhine.c b/src/drivers/net/rhine.c new file mode 100644 index 00000000..42bc124e --- /dev/null +++ b/src/drivers/net/rhine.c @@ -0,0 +1,787 @@ +/* + * Copyright (C) 2012 Adrian Jamroz + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rhine.h" + +/** @file + * + * VIA Rhine network driver + * + */ + +/****************************************************************************** + * + * MII interface + * + ****************************************************************************** + */ + +/** + * Read from MII register + * + * @v mii MII interface + * @v reg Register address + * @ret value Data read, or negative error + */ +static int rhine_mii_read ( struct mii_interface *mii, unsigned int reg ) { + struct rhine_nic *rhn = container_of ( mii, struct rhine_nic, mii ); + unsigned int timeout = RHINE_TIMEOUT_US; + uint8_t cr; + + DBGC2 ( rhn, "RHINE %p MII read reg %d\n", rhn, reg ); + + /* Initiate read */ + writeb ( reg, rhn->regs + RHINE_MII_ADDR ); + cr = readb ( rhn->regs + RHINE_MII_CR ); + writeb ( ( cr | RHINE_MII_CR_RDEN ), rhn->regs + RHINE_MII_CR ); + + /* Wait for read to complete */ + while ( timeout-- ) { + udelay ( 1 ); + cr = readb ( rhn->regs + RHINE_MII_CR ); + if ( ! ( cr & RHINE_MII_CR_RDEN ) ) + return readw ( rhn->regs + RHINE_MII_RDWR ); + } + + DBGC ( rhn, "RHINE %p MII read timeout\n", rhn ); + return -ETIMEDOUT; +} + +/** + * Write to MII register + * + * @v mii MII interface + * @v reg Register address + * @v data Data to write + * @ret rc Return status code + */ +static int rhine_mii_write ( struct mii_interface *mii, unsigned int reg, + unsigned int data ) { + struct rhine_nic *rhn = container_of ( mii, struct rhine_nic, mii ); + unsigned int timeout = RHINE_TIMEOUT_US; + uint8_t cr; + + DBGC2 ( rhn, "RHINE %p MII write reg %d data 0x%04x\n", + rhn, reg, data ); + + /* Initiate write */ + writeb ( reg, rhn->regs + RHINE_MII_ADDR ); + writew ( data, rhn->regs + RHINE_MII_RDWR ); + cr = readb ( rhn->regs + RHINE_MII_CR ); + writeb ( ( cr | RHINE_MII_CR_WREN ), rhn->regs + RHINE_MII_CR ); + + /* Wait for write to complete */ + while ( timeout-- ) { + udelay ( 1 ); + cr = readb ( rhn->regs + RHINE_MII_CR ); + if ( ! ( cr & RHINE_MII_CR_WREN ) ) + return 0; + } + + DBGC ( rhn, "RHINE %p MII write timeout\n", rhn ); + return -ETIMEDOUT; +} + +/** Rhine MII operations */ +static struct mii_operations rhine_mii_operations = { + .read = rhine_mii_read, + .write = rhine_mii_write, +}; + +/** + * Enable auto-polling + * + * @v rhn Rhine device + * @ret rc Return status code + * + * This is voodoo. There seems to be no documentation on exactly what + * we are waiting for, or why we have to do anything other than simply + * turn the feature on. + */ +static int rhine_mii_autopoll ( struct rhine_nic *rhn ) { + unsigned int timeout = RHINE_TIMEOUT_US; + uint8_t addr; + + /* Initiate auto-polling */ + writeb ( MII_BMSR, rhn->regs + RHINE_MII_ADDR ); + writeb ( RHINE_MII_CR_AUTOPOLL, rhn->regs + RHINE_MII_CR ); + + /* Wait for auto-polling to complete */ + while ( timeout-- ) { + udelay ( 1 ); + addr = readb ( rhn->regs + RHINE_MII_ADDR ); + if ( ! ( addr & RHINE_MII_ADDR_MDONE ) ) { + writeb ( ( MII_BMSR | RHINE_MII_ADDR_MSRCEN ), + rhn->regs + RHINE_MII_ADDR ); + return 0; + } + } + + DBGC ( rhn, "RHINE %p MII auto-poll timeout\n", rhn ); + return -ETIMEDOUT; +} + +/****************************************************************************** + * + * Device reset + * + ****************************************************************************** + */ + +/** + * Reset hardware + * + * @v rhn Rhine device + * @ret rc Return status code + * + * We're using PIO because this might reset the MMIO enable bit. + */ +static int rhine_reset ( struct rhine_nic *rhn ) { + unsigned int timeout = RHINE_TIMEOUT_US; + uint8_t cr1; + + DBGC ( rhn, "RHINE %p reset\n", rhn ); + + /* Initiate reset */ + outb ( RHINE_CR1_RESET, rhn->ioaddr + RHINE_CR1 ); + + /* Wait for reset to complete */ + while ( timeout-- ) { + udelay ( 1 ); + cr1 = inb ( rhn->ioaddr + RHINE_CR1 ); + if ( ! ( cr1 & RHINE_CR1_RESET ) ) + return 0; + } + + DBGC ( rhn, "RHINE %p reset timeout\n", rhn ); + return -ETIMEDOUT; +} + +/** + * Enable MMIO register access + * + * @v rhn Rhine device + * @v revision Card revision + */ +static void rhine_enable_mmio ( struct rhine_nic *rhn, int revision ) { + uint8_t conf; + + if ( revision < RHINE_REVISION_OLD ) { + conf = inb ( rhn->ioaddr + RHINE_CHIPCFG_A ); + outb ( ( conf | RHINE_CHIPCFG_A_MMIO ), + rhn->ioaddr + RHINE_CHIPCFG_A ); + } else { + conf = inb ( rhn->ioaddr + RHINE_CHIPCFG_D ); + outb ( ( conf | RHINE_CHIPCFG_D_MMIO ), + rhn->ioaddr + RHINE_CHIPCFG_D ); + } +} + +/** + * Reload EEPROM contents + * + * @v rhn Rhine device + * @ret rc Return status code + * + * We're using PIO because this might reset the MMIO enable bit. + */ +static int rhine_reload_eeprom ( struct rhine_nic *rhn ) { + unsigned int timeout = RHINE_TIMEOUT_US; + uint8_t eeprom; + + /* Initiate reload */ + eeprom = inb ( rhn->ioaddr + RHINE_EEPROM_CTRL ); + outb ( ( eeprom | RHINE_EEPROM_CTRL_RELOAD ), + rhn->ioaddr + RHINE_EEPROM_CTRL ); + + /* Wait for reload to complete */ + while ( timeout-- ) { + udelay ( 1 ); + eeprom = inb ( rhn->ioaddr + RHINE_EEPROM_CTRL ); + if ( ! ( eeprom & RHINE_EEPROM_CTRL_RELOAD ) ) + return 0; + } + + DBGC ( rhn, "RHINE %p EEPROM reload timeout\n", rhn ); + return -ETIMEDOUT; +} + +/****************************************************************************** + * + * Link state + * + ****************************************************************************** + */ + +/** + * Check link state + * + * @v netdev Network device + */ +static void rhine_check_link ( struct net_device *netdev ) { + struct rhine_nic *rhn = netdev->priv; + uint8_t mii_sr; + + /* Read MII status register */ + mii_sr = readb ( rhn->regs + RHINE_MII_SR ); + DBGC ( rhn, "RHINE %p link status %02x\n", rhn, mii_sr ); + + /* Report link state */ + if ( ! ( mii_sr & RHINE_MII_SR_LINKPOLL ) ) { + netdev_link_up ( netdev ); + } else if ( mii_sr & RHINE_MII_SR_PHYERR ) { + netdev_link_err ( netdev, -EIO ); + } else { + netdev_link_down ( netdev ); + } +} + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Create descriptor ring + * + * @v rhn Rhine device + * @v ring Descriptor ring + * @ret rc Return status code + */ +static int rhine_create_ring ( struct rhine_nic *rhn, + struct rhine_ring *ring ) { + size_t len = ( ring->count * sizeof ( ring->desc[0] ) ); + struct rhine_descriptor *next; + physaddr_t address; + unsigned int i; + + /* Allocate descriptors */ + ring->desc = malloc_dma ( len, RHINE_RING_ALIGN ); + if ( ! ring->desc ) + return -ENOMEM; + + /* Initialise descriptor ring */ + memset ( ring->desc, 0, len ); + for ( i = 0 ; i < ring->count ; i++ ) { + next = &ring->desc[ ( i + 1 ) % ring->count ]; + ring->desc[i].next = cpu_to_le32 ( virt_to_bus ( next ) ); + } + + /* Program ring address */ + address = virt_to_bus ( ring->desc ); + writel ( address, rhn->regs + ring->reg ); + + DBGC ( rhn, "RHINE %p ring %02x is at [%08llx,%08llx)\n", + rhn, ring->reg, ( ( unsigned long long ) address ), + ( ( unsigned long long ) address + len ) ); + + return 0; +} + +/** + * Destroy descriptor ring + * + * @v rhn Rhine device + * @v ring Descriptor ring + */ +static void rhine_destroy_ring ( struct rhine_nic *rhn, + struct rhine_ring *ring ) { + size_t len = ( ring->count * sizeof ( ring->desc[0] ) ); + + /* Clear ring address */ + writel ( 0, rhn->regs + ring->reg ); + + /* Free descriptor ring */ + free_dma ( ring->desc, len ); + ring->desc = NULL; + ring->prod = 0; + ring->cons = 0; +} + +/** + * Refill RX descriptor ring + * + * @v rhn Rhine device + */ +static void rhine_refill_rx ( struct rhine_nic *rhn ) { + struct rhine_descriptor *desc; + struct io_buffer *iobuf; + unsigned int rx_idx; + physaddr_t address; + + while ( ( rhn->rx.prod - rhn->rx.cons ) < RHINE_RXDESC_NUM ) { + + /* Allocate I/O buffer */ + iobuf = alloc_iob ( RHINE_RX_MAX_LEN ); + if ( ! iobuf ) { + /* Wait for next refill */ + return; + } + + /* Populate next receive descriptor */ + rx_idx = ( rhn->rx.prod++ % RHINE_RXDESC_NUM ); + desc = &rhn->rx.desc[rx_idx]; + address = virt_to_bus ( iobuf->data ); + desc->buffer = cpu_to_le32 ( address ); + desc->des1 = + cpu_to_le32 ( RHINE_DES1_SIZE ( RHINE_RX_MAX_LEN - 1) | + RHINE_DES1_CHAIN | RHINE_DES1_IC ); + wmb(); + desc->des0 = cpu_to_le32 ( RHINE_DES0_OWN ); + + /* Record I/O buffer */ + rhn->rx_iobuf[rx_idx] = iobuf; + + DBGC2 ( rhn, "RHINE %p RX %d is [%llx,%llx)\n", rhn, rx_idx, + ( ( unsigned long long ) address ), + ( ( unsigned long long ) address + RHINE_RX_MAX_LEN ) ); + } +} + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int rhine_open ( struct net_device *netdev ) { + struct rhine_nic *rhn = netdev->priv; + int rc; + + /* Create transmit ring */ + if ( ( rc = rhine_create_ring ( rhn, &rhn->tx ) ) != 0 ) + goto err_create_tx; + + /* Create receive ring */ + if ( ( rc = rhine_create_ring ( rhn, &rhn->rx ) ) != 0 ) + goto err_create_rx; + + /* Set receive configuration */ + writeb ( ( RHINE_RCR_PHYS_ACCEPT | RHINE_RCR_BCAST_ACCEPT | + RHINE_RCR_RUNT_ACCEPT ), rhn->regs + RHINE_RCR ); + + /* Enable link status monitoring */ + if ( ( rc = rhine_mii_autopoll ( rhn ) ) != 0 ) + goto err_mii_autopoll; + + /* Some cards need an extra delay(observed with VT6102) */ + mdelay ( 10 ); + + /* Enable RX/TX of packets */ + writeb ( ( RHINE_CR0_STARTNIC | RHINE_CR0_RXEN | RHINE_CR0_TXEN ), + rhn->regs + RHINE_CR0 ); + + /* Enable auto polling and full duplex operation */ + rhn->cr1 = RHINE_CR1_FDX; + writeb ( rhn->cr1, rhn->regs + RHINE_CR1 ); + + /* Refill RX ring */ + rhine_refill_rx ( rhn ); + + /* Update link state */ + rhine_check_link ( netdev ); + + return 0; + + err_mii_autopoll: + rhine_destroy_ring ( rhn, &rhn->rx ); + err_create_rx: + rhine_destroy_ring ( rhn, &rhn->tx ); + err_create_tx: + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void rhine_close ( struct net_device *netdev ) { + struct rhine_nic *rhn = netdev->priv; + unsigned int i; + + /* Disable interrupts */ + writeb ( 0, RHINE_IMR0 ); + writeb ( 0, RHINE_IMR1 ); + + /* Stop card, clear RXON and TXON bits */ + writeb ( RHINE_CR0_STOPNIC, rhn->regs + RHINE_CR0 ); + + /* Destroy receive ring */ + rhine_destroy_ring ( rhn, &rhn->rx ); + + /* Discard any unused receive buffers */ + for ( i = 0 ; i < RHINE_RXDESC_NUM ; i++ ) { + if ( rhn->rx_iobuf[i] ) + free_iob ( rhn->rx_iobuf[i] ); + rhn->rx_iobuf[i] = NULL; + } + + /* Destroy transmit ring */ + rhine_destroy_ring ( rhn, &rhn->tx ); +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int rhine_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct rhine_nic *rhn = netdev->priv; + struct rhine_descriptor *desc; + physaddr_t address; + unsigned int tx_idx; + + /* Get next transmit descriptor */ + if ( ( rhn->tx.prod - rhn->tx.cons ) >= RHINE_TXDESC_NUM ) + return -ENOBUFS; + tx_idx = ( rhn->tx.prod++ % RHINE_TXDESC_NUM ); + desc = &rhn->tx.desc[tx_idx]; + + /* Pad and align packet */ + iob_pad ( iobuf, ETH_ZLEN ); + address = virt_to_bus ( iobuf->data ); + + /* Populate transmit descriptor */ + desc->buffer = cpu_to_le32 ( address ); + desc->des1 = cpu_to_le32 ( RHINE_DES1_IC | RHINE_TDES1_STP | + RHINE_TDES1_EDP | RHINE_DES1_CHAIN | + RHINE_DES1_SIZE ( iob_len ( iobuf ) ) ); + wmb(); + desc->des0 = cpu_to_le32 ( RHINE_DES0_OWN ); + wmb(); + + /* Notify card that there are packets ready to transmit */ + writeb ( ( rhn->cr1 | RHINE_CR1_TXPOLL ), rhn->regs + RHINE_CR1 ); + + DBGC2 ( rhn, "RHINE %p TX %d is [%llx,%llx)\n", rhn, tx_idx, + ( ( unsigned long long ) address ), + ( ( unsigned long long ) address + iob_len ( iobuf ) ) ); + + return 0; +} + +/** + * Poll for completed packets + * + * @v netdev Network device + */ +static void rhine_poll_tx ( struct net_device *netdev ) { + struct rhine_nic *rhn = netdev->priv; + struct rhine_descriptor *desc; + unsigned int tx_idx; + uint32_t des0; + + /* Check for completed packets */ + while ( rhn->tx.cons != rhn->tx.prod ) { + + /* Get next transmit descriptor */ + tx_idx = ( rhn->tx.cons % RHINE_TXDESC_NUM ); + desc = &rhn->tx.desc[tx_idx]; + + /* Stop if descriptor is still in use */ + if ( desc->des0 & cpu_to_le32 ( RHINE_DES0_OWN ) ) + return; + + /* Complete TX descriptor */ + des0 = le32_to_cpu ( desc->des0 ); + if ( des0 & RHINE_TDES0_TERR ) { + DBGC ( rhn, "RHINE %p TX %d error (DES0 %08x)\n", + rhn, tx_idx, des0 ); + netdev_tx_complete_next_err ( netdev, -EIO ); + } else { + DBGC2 ( rhn, "RHINE %p TX %d complete\n", rhn, tx_idx ); + netdev_tx_complete_next ( netdev ); + } + rhn->tx.cons++; + } +} + +/** + * Poll for received packets + * + * @v netdev Network device + */ +static void rhine_poll_rx ( struct net_device *netdev ) { + struct rhine_nic *rhn = netdev->priv; + struct rhine_descriptor *desc; + struct io_buffer *iobuf; + unsigned int rx_idx; + uint32_t des0; + size_t len; + + /* Check for received packets */ + while ( rhn->rx.cons != rhn->rx.prod ) { + + /* Get next receive descriptor */ + rx_idx = ( rhn->rx.cons % RHINE_RXDESC_NUM ); + desc = &rhn->rx.desc[rx_idx]; + + /* Stop if descriptor is still in use */ + if ( desc->des0 & cpu_to_le32 ( RHINE_DES0_OWN ) ) + return; + + /* Populate I/O buffer */ + iobuf = rhn->rx_iobuf[rx_idx]; + rhn->rx_iobuf[rx_idx] = NULL; + des0 = le32_to_cpu ( desc->des0 ); + len = ( RHINE_DES0_GETSIZE ( des0 ) - 4 /* strip CRC */ ); + iob_put ( iobuf, len ); + + /* Hand off to network stack */ + if ( des0 & RHINE_RDES0_RXOK ) { + DBGC2 ( rhn, "RHINE %p RX %d complete (length %zd)\n", + rhn, rx_idx, len ); + netdev_rx ( netdev, iobuf ); + } else { + DBGC ( rhn, "RHINE %p RX %d error (length %zd, DES0 " + "%08x)\n", rhn, rx_idx, len, des0 ); + netdev_rx_err ( netdev, iobuf, -EIO ); + } + rhn->rx.cons++; + } +} + +/** + * Poll for completed and received packets + * + * @v netdev Network device + */ +static void rhine_poll ( struct net_device *netdev ) { + struct rhine_nic *rhn = netdev->priv; + uint8_t isr0; + uint8_t isr1; + + /* Read and acknowledge interrupts */ + isr0 = readb ( rhn->regs + RHINE_ISR0 ); + isr1 = readb ( rhn->regs + RHINE_ISR1 ); + if ( isr0 ) + writeb ( isr0, rhn->regs + RHINE_ISR0 ); + if ( isr1 ) + writeb ( isr1, rhn->regs + RHINE_ISR1 ); + + /* Report unexpected errors */ + if ( ( isr0 & ( RHINE_ISR0_MIBOVFL | RHINE_ISR0_PCIERR | + RHINE_ISR0_RXRINGERR | RHINE_ISR0_TXRINGERR ) ) || + ( isr1 & ( RHINE_ISR1_GPI | RHINE_ISR1_TXABORT | + RHINE_ISR1_RXFIFOOVFL | RHINE_ISR1_RXFIFOUNFL | + RHINE_ISR1_TXFIFOUNFL ) ) ) { + DBGC ( rhn, "RHINE %p unexpected ISR0 %02x ISR1 %02x\n", + rhn, isr0, isr1 ); + /* Report as a TX error */ + netdev_tx_err ( netdev, NULL, -EIO ); + } + + /* Poll for TX completions, if applicable */ + if ( isr0 & ( RHINE_ISR0_TXDONE | RHINE_ISR0_TXERR ) ) + rhine_poll_tx ( netdev ); + + /* Poll for RX completions, if applicable */ + if ( isr0 & ( RHINE_ISR0_RXDONE | RHINE_ISR0_RXERR ) ) + rhine_poll_rx ( netdev ); + + /* Handle RX buffer exhaustion */ + if ( isr1 & RHINE_ISR1_RXNOBUF ) { + rhine_poll_rx ( netdev ); + netdev_rx_err ( netdev, NULL, -ENOBUFS ); + } + + /* Check link state, if applicable */ + if ( isr1 & RHINE_ISR1_PORTSTATE ) + rhine_check_link ( netdev ); + + /* Refill RX ring */ + rhine_refill_rx ( rhn ); +} + +/** + * Enable or disable interrupts + * + * @v netdev Network device + * @v enable Interrupts should be enabled + */ +static void rhine_irq ( struct net_device *netdev, int enable ) { + struct rhine_nic *nic = netdev->priv; + + if ( enable ) { + /* Enable interrupts */ + writeb ( 0xff, nic->regs + RHINE_IMR0 ); + writeb ( 0xff, nic->regs + RHINE_IMR1 ); + } else { + /* Disable interrupts */ + writeb ( 0, nic->regs + RHINE_IMR0 ); + writeb ( 0, nic->regs + RHINE_IMR1 ); + } +} + +/** Rhine network device operations */ +static struct net_device_operations rhine_operations = { + .open = rhine_open, + .close = rhine_close, + .transmit = rhine_transmit, + .poll = rhine_poll, + .irq = rhine_irq, +}; + +/****************************************************************************** + * + * PCI interface + * + ****************************************************************************** + */ + +/** + * Probe PCI device + * + * @v pci PCI device + * @ret rc Return status code + */ +static int rhine_probe ( struct pci_device *pci ) { + struct net_device *netdev; + struct rhine_nic *rhn; + uint8_t revision; + unsigned int i; + int rc; + + /* Allocate and initialise net device */ + netdev = alloc_etherdev ( sizeof ( *rhn ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &rhine_operations ); + rhn = netdev->priv; + pci_set_drvdata ( pci, netdev ); + netdev->dev = &pci->dev; + memset ( rhn, 0, sizeof ( *rhn ) ); + rhine_init_ring ( &rhn->tx, RHINE_TXDESC_NUM, RHINE_TXQUEUE_BASE ); + rhine_init_ring ( &rhn->rx, RHINE_RXDESC_NUM, RHINE_RXQUEUE_BASE ); + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map registers */ + rhn->regs = ioremap ( pci->membase, RHINE_BAR_SIZE ); + rhn->ioaddr = pci->ioaddr; + DBGC ( rhn, "RHINE %p regs at %08lx, I/O at %04lx\n", rhn, + pci->membase, pci->ioaddr ); + + /* Reset the NIC */ + if ( ( rc = rhine_reset ( rhn ) ) != 0 ) + goto err_reset; + + /* Reload EEPROM */ + if ( ( rc = rhine_reload_eeprom ( rhn ) ) != 0 ) + goto err_reload_eeprom; + + /* Read card revision and enable MMIO */ + pci_read_config_byte ( pci, PCI_REVISION, &revision ); + DBGC ( rhn, "RHINE %p revision %#02x detected\n", rhn, revision ); + rhine_enable_mmio ( rhn, revision ); + + /* Read MAC address */ + for ( i = 0 ; i < ETH_ALEN ; i++ ) + netdev->hw_addr[i] = readb ( rhn->regs + RHINE_MAC + i ); + + /* Initialise and reset MII interface */ + mii_init ( &rhn->mii, &rhine_mii_operations ); + if ( ( rc = mii_reset ( &rhn->mii ) ) != 0 ) { + DBGC ( rhn, "RHINE %p could not reset MII: %s\n", + rhn, strerror ( rc ) ); + goto err_mii_reset; + } + DBGC ( rhn, "RHINE PHY vendor %04x device %04x\n", + rhine_mii_read ( &rhn->mii, 0x02 ), + rhine_mii_read ( &rhn->mii, 0x03 ) ); + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register_netdev; + + /* Set initial link state */ + rhine_check_link ( netdev ); + + return 0; + + err_register_netdev: + err_mii_reset: + err_reload_eeprom: + rhine_reset ( rhn ); + err_reset: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void rhine_remove ( struct pci_device *pci ) { + struct net_device *netdev = pci_get_drvdata ( pci ); + struct rhine_nic *nic = netdev->priv; + + /* Unregister network device */ + unregister_netdev ( netdev ); + + /* Reset card */ + rhine_reset ( nic ); + + /* Free network device */ + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** Rhine PCI device IDs */ +static struct pci_device_id rhine_nics[] = { + PCI_ROM ( 0x1106, 0x3065, "dlink-530tx", "VIA VT6102", 0 ), + PCI_ROM ( 0x1106, 0x3106, "vt6105", "VIA VT6105", 0 ), + PCI_ROM ( 0x1106, 0x3043, "dlink-530tx-old", "VIA VT3043", 0 ), + PCI_ROM ( 0x1106, 0x3053, "vt6105m", "VIA VT6105M", 0 ), + PCI_ROM ( 0x1106, 0x6100, "via-rhine-old", "VIA 86C100A", 0 ) +}; + +/** Rhine PCI driver */ +struct pci_driver rhine_driver __pci_driver = { + .ids = rhine_nics, + .id_count = ( sizeof ( rhine_nics ) / sizeof ( rhine_nics[0] ) ), + .probe = rhine_probe, + .remove = rhine_remove, +}; diff --git a/src/drivers/net/rhine.h b/src/drivers/net/rhine.h new file mode 100644 index 00000000..b26f9ae7 --- /dev/null +++ b/src/drivers/net/rhine.h @@ -0,0 +1,250 @@ +#ifndef _RHINE_H +#define _RHINE_H + +/** @file + * + * VIA Rhine network driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** Rhine BAR size */ +#define RHINE_BAR_SIZE 256 + +/** Default timeout */ +#define RHINE_TIMEOUT_US 10000 + +/** Rhine descriptor format */ +struct rhine_descriptor { + uint32_t des0; + uint32_t des1; + uint32_t buffer; + uint32_t next; +} __attribute__ (( packed )); + +#define RHINE_DES0_OWN (1 << 31) /*< Owned descriptor */ +#define RHINE_DES1_IC (1 << 23) /*< Generate interrupt */ +#define RHINE_TDES1_EDP (1 << 22) /*< End of packet */ +#define RHINE_TDES1_STP (1 << 21) /*< Start of packet */ +#define RHINE_TDES1_TCPCK (1 << 20) /*< HW TCP checksum */ +#define RHINE_TDES1_UDPCK (1 << 19) /*< HW UDP checksum */ +#define RHINE_TDES1_IPCK (1 << 18) /*< HW IP checksum */ +#define RHINE_TDES1_TAG (1 << 17) /*< Tagged frame */ +#define RHINE_TDES1_CRC (1 << 16) /*< No CRC */ +#define RHINE_DES1_CHAIN (1 << 15) /*< Chained descriptor */ +#define RHINE_DES1_SIZE(_x) ((_x) & 0x7ff) /*< Frame size */ +#define RHINE_DES0_GETSIZE(_x) (((_x) >> 16) & 0x7ff) + +#define RHINE_RDES0_RXOK (1 << 15) +#define RHINE_RDES0_VIDHIT (1 << 14) +#define RHINE_RDES0_MAR (1 << 13) +#define RHINE_RDES0_BAR (1 << 12) +#define RHINE_RDES0_PHY (1 << 11) +#define RHINE_RDES0_CHN (1 << 10) +#define RHINE_RDES0_STP (1 << 9) +#define RHINE_RDES0_EDP (1 << 8) +#define RHINE_RDES0_BUFF (1 << 7) +#define RHINE_RDES0_FRAG (1 << 6) +#define RHINE_RDES0_RUNT (1 << 5) +#define RHINE_RDES0_LONG (1 << 4) +#define RHINE_RDES0_FOV (1 << 3) +#define RHINE_RDES0_FAE (1 << 2) +#define RHINE_RDES0_CRCE (1 << 1) +#define RHINE_RDES0_RERR (1 << 0) + +#define RHINE_TDES0_TERR (1 << 15) +#define RHINE_TDES0_UDF (1 << 11) +#define RHINE_TDES0_CRS (1 << 10) +#define RHINE_TDES0_OWC (1 << 9) +#define RHINE_TDES0_ABT (1 << 8) +#define RHINE_TDES0_CDH (1 << 7) +#define RHINE_TDES0_COLS (1 << 4) +#define RHINE_TDES0_NCR(_x) ((_x) & 0xf) + +#define RHINE_RING_ALIGN 4 + +/** Rhine descriptor rings sizes */ +#define RHINE_RXDESC_NUM 4 +#define RHINE_TXDESC_NUM 8 +#define RHINE_RX_MAX_LEN 1536 + +/** Rhine MAC address registers */ +#define RHINE_MAC 0x00 + +/** Receive control register */ +#define RHINE_RCR 0x06 +#define RHINE_RCR_FIFO_TRSH(_x) (((_x) & 0x7) << 5) /*< RX FIFO threshold */ +#define RHINE_RCR_PHYS_ACCEPT (1 << 4) /*< Accept matching PA */ +#define RHINE_RCR_BCAST_ACCEPT (1 << 3) /*< Accept broadcast */ +#define RHINE_RCR_MCAST_ACCEPT (1 << 2) /*< Accept multicast */ +#define RHINE_RCR_RUNT_ACCEPT (1 << 1) /*< Accept runt frames */ +#define RHINE_RCR_ERR_ACCEPT (1 << 0) /*< Accept erroneous frames */ + +/** Transmit control register */ +#define RHINE_TCR 0x07 +#define RHINE_TCR_LOOPBACK(_x) (((_x) & 0x3) << 1) /*< Transmit loop mode */ +#define RHINE_TCR_TAGGING (1 << 0) /*< 802.1P/Q packet tagging */ + +/** Command 0 register */ +#define RHINE_CR0 0x08 +#define RHINE_CR0_RXSTART (1 << 6) +#define RHINE_CR0_TXSTART (1 << 5) +#define RHINE_CR0_TXEN (1 << 4) /*< Transmit enable */ +#define RHINE_CR0_RXEN (1 << 3) /*< Receive enable */ +#define RHINE_CR0_STOPNIC (1 << 2) /*< Stop NIC */ +#define RHINE_CR0_STARTNIC (1 << 1) /*< Start NIC */ + +/** Command 1 register */ +#define RHINE_CR1 0x09 +#define RHINE_CR1_RESET (1 << 7) /*< Software reset */ +#define RHINE_CR1_RXPOLL (1 << 6) /*< Receive poll demand */ +#define RHINE_CR1_TXPOLL (1 << 5) /*< Xmit poll demand */ +#define RHINE_CR1_AUTOPOLL (1 << 3) /*< Disable autopoll */ +#define RHINE_CR1_FDX (1 << 2) /*< Full duplex */ +#define RIHNE_CR1_ACCUNI (1 << 1) /*< Disable accept unicast */ + +/** Transmit queue wake register */ +#define RHINE_TXQUEUE_WAKE 0x0a + +/** Interrupt service 0 */ +#define RHINE_ISR0 0x0c +#define RHINE_ISR0_MIBOVFL (1 << 7) +#define RHINE_ISR0_PCIERR (1 << 6) +#define RHINE_ISR0_RXRINGERR (1 << 5) +#define RHINE_ISR0_TXRINGERR (1 << 4) +#define RHINE_ISR0_TXERR (1 << 3) +#define RHINE_ISR0_RXERR (1 << 2) +#define RHINE_ISR0_TXDONE (1 << 1) +#define RHINE_ISR0_RXDONE (1 << 0) + +/** Interrupt service 1 */ +#define RHINE_ISR1 0x0d +#define RHINE_ISR1_GPI (1 << 7) +#define RHINE_ISR1_PORTSTATE (1 << 6) +#define RHINE_ISR1_TXABORT (1 << 5) +#define RHINE_ISR1_RXNOBUF (1 << 4) +#define RHINE_ISR1_RXFIFOOVFL (1 << 3) +#define RHINE_ISR1_RXFIFOUNFL (1 << 2) +#define RHINE_ISR1_TXFIFOUNFL (1 << 1) +#define RHINE_ISR1_EARLYRX (1 << 0) + +/** Interrupt enable mask register 0 */ +#define RHINE_IMR0 0x0e + +/** Interrupt enable mask register 1 */ +#define RHINE_IMR1 0x0f + +/** RX queue descriptor base address */ +#define RHINE_RXQUEUE_BASE 0x18 + +/** TX queue 0 descriptor base address */ +#define RHINE_TXQUEUE_BASE 0x1c + +/** MII configuration */ +#define RHINE_MII_CFG 0x6c + +/** MII status register */ +#define RHINE_MII_SR 0x6d +#define RHINE_MII_SR_PHYRST (1 << 7) /*< PHY reset */ +#define RHINE_MII_SR_LINKNWAY (1 << 4) /*< Link status after N-Way */ +#define RHINE_MII_SR_PHYERR (1 << 3) /*< PHY device error */ +#define RHINE_MII_SR_DUPLEX (1 << 2) /*< Duplex mode after N-Way */ +#define RHINE_MII_SR_LINKPOLL (1 << 1) /*< Link status after poll */ +#define RHINE_MII_SR_LINKSPD (1 << 0) /*< Link speed after N-Way */ + +/** MII bus control 0 register */ +#define RHINE_MII_BCR0 0x6e + +/** MII bus control 1 register */ +#define RHINE_MII_BCR1 0x6f + +/** MII control register */ +#define RHINE_MII_CR 0x70 +#define RHINE_MII_CR_AUTOPOLL (1 << 7) /*< MII auto polling */ +#define RHINE_MII_CR_RDEN (1 << 6) /*< PHY read enable */ +#define RHINE_MII_CR_WREN (1 << 5) /*< PHY write enable */ +#define RHINE_MII_CR_DIRECT (1 << 4) /*< Direct programming mode */ +#define RHINE_MII_CR_MDIOOUT (1 << 3) /*< MDIO output enable */ + +/** MII port address */ +#define RHINE_MII_ADDR 0x71 +#define RHINE_MII_ADDR_MSRCEN (1 << 6) +#define RHINE_MII_ADDR_MDONE (1 << 5) + +/** MII read/write data */ +#define RHINE_MII_RDWR 0x72 + +/** EERPOM control/status register */ +#define RHINE_EEPROM_CTRL 0x74 +#define RHINE_EEPROM_CTRL_STATUS (1 << 7) /*< EEPROM status */ +#define RHINE_EEPROM_CTRL_RELOAD (1 << 5) /*< EEPROM reload */ + +/** Chip configuration A */ +#define RHINE_CHIPCFG_A 0x78 +/* MMIO enable. Only valid for Rhine I. Reserved on later boards */ +#define RHINE_CHIPCFG_A_MMIO (1 << 5) + +/** Chip configuration B */ +#define RHINE_CHIPCFG_B 0x79 + +/** Chip configuation C */ +#define RHINE_CHIPCFG_C 0x7a + +/** Chip configuration D */ +#define RHINE_CHIPCFG_D 0x7b +/* MMIO enable. Only valid on Rhine II and later. GPIOEN on Rhine I */ +#define RHINE_CHIPCFG_D_MMIO (1 << 7) + +#define RHINE_REVISION_OLD 0x20 + +/** A VIA Rhine descriptor ring */ +struct rhine_ring { + /** Descriptors */ + struct rhine_descriptor *desc; + /** Producer index */ + unsigned int prod; + /** Consumer index */ + unsigned int cons; + + /** Number of descriptors */ + unsigned int count; + /** Register address */ + unsigned int reg; +}; + +/** + * Initialise descriptor ring + * + * @v ring Descriptor ring + * @v count Number of descriptors (must be a power of 2) + * @v reg Register address + */ +static inline __attribute__ (( always_inline)) void +rhine_init_ring ( struct rhine_ring *ring, unsigned int count, + unsigned int reg ) { + ring->count = count; + ring->reg = reg; +} + +/** A VIA Rhine network card */ +struct rhine_nic { + /** I/O address (some PIO access is always required) */ + unsigned long ioaddr; + /** Registers */ + void *regs; + /** Cached value of CR1 (to avoid read-modify-write on fast path) */ + uint8_t cr1; + + /** MII interface */ + struct mii_interface mii; + + /** Transmit descriptor ring */ + struct rhine_ring tx; + /** Receive descriptor ring */ + struct rhine_ring rx; + /** Receive I/O buffers */ + struct io_buffer *rx_iobuf[RHINE_RXDESC_NUM]; +}; + +#endif /* _RHINE_H */ diff --git a/src/drivers/net/velocity.c b/src/drivers/net/velocity.c new file mode 100644 index 00000000..6d518520 --- /dev/null +++ b/src/drivers/net/velocity.c @@ -0,0 +1,807 @@ +/* + * Copyright (C) 2012 Adrian Jamróz + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "velocity.h" + +#define velocity_setbit(_reg, _mask) writeb ( readb ( _reg ) | _mask, _reg ) +#define virt_to_le32bus(x) ( cpu_to_le32 ( virt_to_bus ( x ) ) ) + +/** @file + * + * VIA Velocity network driver + * + */ + +/****************************************************************************** + * + * MII interface + * + ****************************************************************************** + */ + +/** + * Stop MII auto-polling + * + * @v vlc Velocity device + * @ret rc Return status code + */ +static int velocity_autopoll_stop ( struct velocity_nic *vlc ) { + int timeout = VELOCITY_TIMEOUT_US; + + /* Disable MII auto polling */ + writeb ( 0, vlc->regs + VELOCITY_MIICR ); + + /* Wait for disabling to take effect */ + while ( timeout-- ) { + udelay ( 1 ); + if ( readb ( vlc->regs + VELOCITY_MIISR ) & + VELOCITY_MIISR_IDLE ) + return 0; + } + + DBGC ( vlc, "MII autopoll stop timeout\n" ); + return -ETIMEDOUT; +} + +/** + * Start MII auto-polling + * + * @v vlc Velocity device + * @ret rc Return status code + */ +static int velocity_autopoll_start ( struct velocity_nic *vlc ) { + int timeout = VELOCITY_TIMEOUT_US; + + /* Enable MII auto polling */ + writeb ( VELOCITY_MIICR_MAUTO, vlc->regs + VELOCITY_MIICR ); + + /* Wait for enabling to take effect */ + while ( timeout-- ) { + udelay ( 1 ); + if ( ( readb ( vlc->regs + VELOCITY_MIISR ) & + VELOCITY_MIISR_IDLE ) == 0 ) + return 0; + } + + DBGC ( vlc, "MII autopoll start timeout\n" ); + return -ETIMEDOUT; +} + +/** + * Read from MII register + * + * @v mii MII interface + * @v reg Register address + * @ret value Data read, or negative error + */ +static int velocity_mii_read ( struct mii_interface *mii, unsigned int reg ) { + struct velocity_nic *vlc = + container_of ( mii, struct velocity_nic, mii ); + int timeout = VELOCITY_TIMEOUT_US; + int result; + + DBGC2 ( vlc, "VELOCITY %p MII read reg %d\n", vlc, reg ); + + /* Disable autopolling before we can access MII */ + velocity_autopoll_stop ( vlc ); + + /* Send read command and address */ + writeb ( reg, vlc->regs + VELOCITY_MIIADDR ); + velocity_setbit ( vlc->regs + VELOCITY_MIICR, VELOCITY_MIICR_RCMD ); + + /* Wait for read to complete */ + while ( timeout-- ) { + udelay ( 1 ); + if ( ( readb ( vlc->regs + VELOCITY_MIICR ) & + VELOCITY_MIICR_RCMD ) == 0 ) { + result = readw ( vlc->regs + VELOCITY_MIIDATA ); + velocity_autopoll_start ( vlc ); + return result; + } + } + + /* Restart autopolling */ + velocity_autopoll_start ( vlc ); + + DBGC ( vlc, "MII read timeout\n" ); + return -ETIMEDOUT; +} + +/** + * Write to MII register + * + * @v mii MII interface + * @v reg Register address + * @v data Data to write + * @ret rc Return status code + */ +static int velocity_mii_write ( struct mii_interface *mii, unsigned int reg, + unsigned int data) { + struct velocity_nic *vlc = + container_of ( mii, struct velocity_nic, mii ); + int timeout = VELOCITY_TIMEOUT_US; + + DBGC2 ( vlc, "VELOCITY %p MII write reg %d data 0x%04x\n", + vlc, reg, data ); + + /* Disable autopolling before we can access MII */ + velocity_autopoll_stop ( vlc ); + + /* Send write command, data and destination register */ + writeb ( reg, vlc->regs + VELOCITY_MIIADDR ); + writew ( data, vlc->regs + VELOCITY_MIIDATA ); + velocity_setbit ( vlc->regs + VELOCITY_MIICR, VELOCITY_MIICR_WCMD ); + + /* Wait for write to complete */ + while ( timeout-- ) { + udelay ( 1 ); + if ( ( readb ( vlc->regs + VELOCITY_MIICR ) & + VELOCITY_MIICR_WCMD ) == 0 ) { + velocity_autopoll_start ( vlc ); + return 0; + } + } + + /* Restart autopolling */ + velocity_autopoll_start ( vlc ); + + DBGC ( vlc, "MII write timeout\n" ); + return -ETIMEDOUT; +} + +/** Velocity MII operations */ +static struct mii_operations velocity_mii_operations = { + .read = velocity_mii_read, + .write = velocity_mii_write, +}; + +/** + * Set Link speed + * + * @v vlc Velocity device + */ +static void velocity_set_link ( struct velocity_nic *vlc ) { + int tmp; + + /* Advertise 1000MBit */ + tmp = velocity_mii_read ( &vlc->mii, MII_CTRL1000 ); + tmp |= ADVERTISE_1000FULL | ADVERTISE_1000HALF; + velocity_mii_write ( &vlc->mii, MII_CTRL1000, tmp ); + + /* Enable GBit operation in MII Control Register */ + tmp = velocity_mii_read ( &vlc->mii, MII_BMCR ); + tmp |= BMCR_SPEED1000; + velocity_mii_write ( &vlc->mii, MII_BMCR, tmp ); +} + +/****************************************************************************** + * + * Device reset + * + ****************************************************************************** + */ + +/** + * Reload eeprom contents + * + * @v vlc Velocity device + */ +static int velocity_reload_eeprom ( struct velocity_nic *vlc ) { + int timeout = VELOCITY_TIMEOUT_US; + + /* Initiate reload */ + velocity_setbit ( vlc->regs + VELOCITY_EECSR, VELOCITY_EECSR_RELOAD ); + + /* Wait for reload to complete */ + while ( timeout-- ) { + udelay ( 1 ); + if ( ( readb ( vlc->regs + VELOCITY_EECSR ) & + VELOCITY_EECSR_RELOAD ) == 0 ) + return 0; + } + + DBGC ( vlc, "VELOCITY %p EEPROM reload timeout\n", vlc ); + return -ETIMEDOUT; +} + +/** + * Reset hardware + * + * @v vlc Velocity device + * @ret rc Return status code + */ +static int velocity_reset ( struct velocity_nic *vlc ) { + int timeout = VELOCITY_TIMEOUT_US; + uint8_t tmp; + + DBGC ( vlc, "VELOCITY %p reset\n", vlc ); + + /* clear sticky Power state bits */ + tmp = readb ( vlc->regs + VELOCITY_STICKY ); + tmp &= ~( VELOCITY_STICKY_DS0 | VELOCITY_STICKY_DS1 ); + writeb ( tmp, vlc->regs + VELOCITY_STICKY ); + + /* clear PACPI, which might have been enabled by the EEPROM reload */ + tmp = readb ( vlc->regs + VELOCITY_CFGA ); + tmp &= ~VELOCITY_CFGA_PACPI; + writeb ( tmp, vlc->regs + VELOCITY_CFGA ); + + velocity_setbit ( vlc->regs + VELOCITY_CRS1, VELOCITY_CR1_SFRST ); + + /* Wait for reset to complete */ + while ( timeout-- ) { + udelay ( 1 ); + if ( ( readb ( vlc->regs + VELOCITY_CRS1 ) & + VELOCITY_CR1_SFRST ) == 0 ) + return 0; + } + + return -EINVAL; +} + +/****************************************************************************** + * + * Link state + * + ****************************************************************************** + */ + +/** + * Check link state + * + * @v netdev Network device + */ +static void velocity_check_link ( struct net_device *netdev ) { + struct velocity_nic *vlc = netdev->priv; + + if ( readb ( vlc->regs + VELOCITY_PHYSTS0 ) & VELOCITY_PHYSTS0_LINK ) { + netdev_link_up ( netdev ); + DBGC ( vlc, "VELOCITY %p link up\n", vlc ); + } else { + netdev_link_down ( netdev ); + DBGC ( vlc, "VELOCITY %p link down\n", vlc ); + } + + /* The card disables auto-poll after a link change */ + velocity_autopoll_start ( vlc ); +} + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Allocate descriptor rings + * + * @v vlc Velocity device + * @ret rc Return status code + */ +static int velocity_alloc_rings ( struct velocity_nic *vlc ) { + int rc = 0; + + /* Allocate RX descriptor ring */ + vlc->rx_prod = 0; + vlc->rx_cons = 0; + vlc->rx_commit = 0; + vlc->rx_ring = malloc_dma ( VELOCITY_RXDESC_SIZE, VELOCITY_RING_ALIGN ); + if ( ! vlc->rx_ring ) + return -ENOMEM; + + memset ( vlc->rx_ring, 0, VELOCITY_RXDESC_SIZE ); + + DBGC2 ( vlc, "VELOCITY %p RX ring start address: %p(phys: %#08lx)\n", + vlc, vlc->rx_ring, virt_to_bus ( vlc->rx_ring ) ); + + /* Allocate TX descriptor ring */ + vlc->tx_prod = 0; + vlc->tx_cons = 0; + vlc->tx_ring = malloc_dma ( VELOCITY_TXDESC_SIZE, VELOCITY_RING_ALIGN ); + if ( ! vlc->tx_ring ) { + rc = -ENOMEM; + goto err_tx_alloc; + } + + memset ( vlc->tx_ring, 0, VELOCITY_TXDESC_SIZE ); + + /* Send RX ring to the card */ + writel ( virt_to_bus ( vlc->rx_ring ), + vlc->regs + VELOCITY_RXDESC_ADDR_LO ); + writew ( VELOCITY_RXDESC_NUM - 1, vlc->regs + VELOCITY_RXDESCNUM ); + + /* Send TX ring to the card */ + writel ( virt_to_bus ( vlc->tx_ring ), + vlc->regs + VELOCITY_TXDESC_ADDR_LO0 ); + writew ( VELOCITY_TXDESC_NUM - 1, vlc->regs + VELOCITY_TXDESCNUM ); + + DBGC2 ( vlc, "VELOCITY %p TX ring start address: %p(phys: %#08lx)\n", + vlc, vlc->tx_ring, virt_to_bus ( vlc->tx_ring ) ); + + return 0; + +err_tx_alloc: + free_dma ( vlc->rx_ring, VELOCITY_RXDESC_SIZE ); + return rc; +} + +/** + * Refill receive descriptor ring + * + * @v vlc Velocity device + */ +static void velocity_refill_rx ( struct velocity_nic *vlc ) { + struct velocity_rx_descriptor *desc; + struct io_buffer *iobuf; + int rx_idx, i = 0; + + /* Check for new packets */ + while ( ( vlc->rx_prod - vlc->rx_cons ) < VELOCITY_RXDESC_NUM ) { + iobuf = alloc_iob ( VELOCITY_RX_MAX_LEN ); + + /* Memory pressure: try again next poll */ + if ( ! iobuf ) + break; + + rx_idx = ( vlc->rx_prod++ % VELOCITY_RXDESC_NUM ); + desc = &vlc->rx_ring[rx_idx]; + + /* Set descrptor fields */ + desc->des1 = 0; + desc->addr = virt_to_le32bus ( iobuf-> data ); + desc->des2 = cpu_to_le32 ( + VELOCITY_DES2_SIZE ( VELOCITY_RX_MAX_LEN - 1 ) | + VELOCITY_DES2_IC ); + + vlc->rx_buffs[rx_idx] = iobuf; + i++; + + /* Return RX descriptors in blocks of 4 (hw requirement) */ + if ( rx_idx % 4 == 3 ) { + int j; + for (j = 0; j < 4; j++) { + desc = &vlc->rx_ring[rx_idx - j]; + desc->des0 = cpu_to_le32 ( VELOCITY_DES0_OWN ); + } + vlc->rx_commit += 4; + } + } + + wmb(); + + if ( vlc->rx_commit ) { + writew ( vlc->rx_commit, + vlc->regs + VELOCITY_RXDESC_RESIDUECNT ); + vlc->rx_commit = 0; + } + + if ( i > 0 ) + DBGC2 ( vlc, "VELOCITY %p refilled %d RX descriptors\n", + vlc, i ); +} + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int velocity_open ( struct net_device *netdev ) { + struct velocity_nic *vlc = netdev->priv; + int rc; + + DBGC ( vlc, "VELOCITY %p open\n", vlc ); + DBGC ( vlc, "VELOCITY %p regs at: %p\n", vlc, vlc->regs ); + + /* Allocate descriptor rings */ + if ( ( rc = velocity_alloc_rings ( vlc ) ) != 0 ) + return rc; + + velocity_refill_rx ( vlc ); + + /* Enable TX/RX queue */ + writew ( VELOCITY_TXQCSRS_RUN0, vlc->regs + VELOCITY_TXQCSRS ); + writew ( VELOCITY_RXQCSR_RUN | VELOCITY_RXQCSR_WAK, + vlc->regs + VELOCITY_RXQCSRS ); + + /* Enable interrupts */ + writeb ( 0xff, vlc->regs + VELOCITY_IMR0 ); + writeb ( 0xff, vlc->regs + VELOCITY_IMR1 ); + + /* Start MAC */ + writeb ( VELOCITY_CR0_STOP, vlc->regs + VELOCITY_CRC0 ); + writeb ( VELOCITY_CR1_DPOLL, vlc->regs + VELOCITY_CRC0 ); + writeb ( VELOCITY_CR0_START | VELOCITY_CR0_TXON | VELOCITY_CR0_RXON, + vlc->regs + VELOCITY_CRS0 ); + + /* Receive all packets */ + writeb ( 0xff, vlc->regs + VELOCITY_RCR ); + + /* Set initial link state */ + velocity_check_link ( netdev ); + + velocity_autopoll_start ( vlc ); + + DBGC2 ( vlc, "VELOCITY %p CR3 %02x\n", + vlc, readb ( vlc->regs + 0x0B ) ); + + return 0; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void velocity_close ( struct net_device *netdev ) { + struct velocity_nic *vlc = netdev->priv; + int i; + + /* Stop NIC */ + writeb ( VELOCITY_CR0_TXON | VELOCITY_CR0_RXON, + vlc->regs + VELOCITY_CRC0 ); + writeb ( VELOCITY_CR0_STOP, vlc->regs + VELOCITY_CRS0 ); + + /* Clear RX ring information */ + writel ( 0, vlc->regs + VELOCITY_RXDESC_ADDR_LO ); + writew ( 0, vlc->regs + VELOCITY_RXDESCNUM ); + + /* Destroy RX ring */ + free_dma ( vlc->rx_ring, VELOCITY_RXDESC_SIZE ); + vlc->rx_ring = NULL; + vlc->rx_prod = 0; + vlc->rx_cons = 0; + + /* Discard receive buffers */ + for ( i = 0 ; i < VELOCITY_RXDESC_NUM ; i++ ) { + if ( vlc->rx_buffs[i] ) + free_iob ( vlc->rx_buffs[i] ); + vlc->rx_buffs[i] = NULL; + } + + /* Clear TX ring information */ + writel ( 0, vlc->regs + VELOCITY_TXDESC_ADDR_LO0 ); + writew ( 0, vlc->regs + VELOCITY_TXDESCNUM ); + + /* Destroy TX ring */ + free_dma ( vlc->tx_ring, VELOCITY_TXDESC_SIZE ); + vlc->tx_ring = NULL; + vlc->tx_prod = 0; + vlc->tx_cons = 0; +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int velocity_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct velocity_nic *vlc = netdev->priv; + struct velocity_tx_descriptor *desc; + unsigned int tx_idx; + + /* Pad packet to minimum length */ + iob_pad ( iobuf, ETH_ZLEN ); + + tx_idx = ( vlc->tx_prod++ % VELOCITY_TXDESC_NUM ); + desc = &vlc->tx_ring[tx_idx]; + + /* Set packet size and transfer ownership to NIC */ + desc->des0 = cpu_to_le32 ( VELOCITY_DES0_OWN | + VELOCITY_DES2_SIZE ( iob_len ( iobuf ) ) ); + /* Data in first desc fragment, only desc for packet, generate INT */ + desc->des1 = cpu_to_le32 ( VELOCITY_DES1_FRAG ( 1 ) | + VELOCITY_DES1_TCPLS | + VELOCITY_DES1_INTR ); + + desc->frags[0].addr = virt_to_le32bus ( iobuf->data ); + desc->frags[0].des2 = cpu_to_le32 ( + VELOCITY_DES2_SIZE ( iob_len ( iobuf ) ) ); + + wmb(); + + /* Initiate TX */ + velocity_setbit ( vlc->regs + VELOCITY_TXQCSRS, VELOCITY_TXQCSRS_WAK0 ); + + DBGC2 ( vlc, "VELOCITY %p tx_prod=%d desc=%p iobuf=%p len=%zd\n", + vlc, tx_idx, desc, iobuf->data, iob_len ( iobuf ) ); + + return 0; +} + +/** + * Poll for received packets. + * + * @v vlc Velocity device + */ +static void velocity_poll_rx ( struct velocity_nic *vlc ) { + struct velocity_rx_descriptor *desc; + struct io_buffer *iobuf; + int rx_idx; + size_t len; + uint32_t des0; + + /* Check for packets */ + while ( vlc->rx_cons != vlc->rx_prod ) { + rx_idx = ( vlc->rx_cons % VELOCITY_RXDESC_NUM ); + desc = &vlc->rx_ring[rx_idx]; + + des0 = cpu_to_le32 ( desc->des0 ); + + /* Return if descriptor still in use */ + if ( des0 & VELOCITY_DES0_OWN ) + return; + + iobuf = vlc->rx_buffs[rx_idx]; + + /* Get length, strip CRC */ + len = VELOCITY_DES0_RMBC ( des0 ) - 4; + iob_put ( iobuf, len ); + + DBGC2 ( vlc, "VELOCITY %p got packet on idx=%d (prod=%d), len %zd\n", + vlc, rx_idx, vlc->rx_prod % VELOCITY_RXDESC_NUM, len ); + + if ( des0 & VELOCITY_DES0_RX_ERR ) { + /* Report receive error */ + netdev_rx_err ( vlc->netdev, iobuf, -EINVAL ); + DBGC ( vlc, "VELOCITY %p receive error, status: %02x\n", + vlc, des0 ); + } else if ( des0 & VELOCITY_DES0_RXOK ) { + /* Report receive success */ + netdev_rx( vlc->netdev, iobuf ); + } else { + /* Card indicated neither success nor failure + * Technically this shouldn't happen, but we saw it + * in debugging once. */ + DBGC ( vlc, "VELOCITY %p RX neither ERR nor OK: %04x\n", + vlc, des0 ); + DBGC ( vlc, "packet len: %zd\n", len ); + DBGC_HD ( vlc, iobuf->data, 64 ); + + /* we don't know what it is, treat is as an error */ + netdev_rx_err ( vlc->netdev, iobuf, -EINVAL ); + } + + vlc->rx_cons++; + } +} + +/** + * Poll for completed packets. + * + * @v vlc Velocity device + */ +static void velocity_poll_tx ( struct velocity_nic *vlc ) { + struct velocity_tx_descriptor *desc; + int tx_idx; + + /* Check for packets */ + while ( vlc->tx_cons != vlc->tx_prod ) { + tx_idx = ( vlc->tx_cons % VELOCITY_TXDESC_NUM ); + desc = &vlc->tx_ring[tx_idx]; + + /* Return if descriptor still in use */ + if ( le32_to_cpu ( desc->des0 ) & VELOCITY_DES0_OWN ) + return; + + /* Report errors */ + if ( le32_to_cpu ( desc->des0 ) & VELOCITY_DES0_TERR ) { + netdev_tx_complete_next_err ( vlc->netdev, -EINVAL ); + return; + } + + netdev_tx_complete_next ( vlc->netdev ); + + DBGC2 ( vlc, "VELOCITY %p poll_tx cons=%d prod=%d tsr=%04x\n", + vlc, tx_idx, vlc->tx_prod % VELOCITY_TXDESC_NUM, + ( desc->des0 & 0xffff ) ); + vlc->tx_cons++; + } +} + +/** + * Poll for completed and received packets + * + * @v netdev Network device + */ +static void velocity_poll ( struct net_device *netdev ) { + struct velocity_nic *vlc = netdev->priv; + uint8_t isr1; + + isr1 = readb ( vlc->regs + VELOCITY_ISR1 ); + + /* ACK interrupts */ + writew ( 0xFFFF, vlc->regs + VELOCITY_ISR0 ); + + /* Check for competed packets */ + velocity_poll_rx ( vlc ); + velocity_poll_tx ( vlc ); + + if ( isr1 & VELOCITY_ISR1_SRCI ) { + /* Update linkstate */ + DBGC2 ( vlc, "VELOCITY %p link status interrupt\n", vlc ); + velocity_check_link ( netdev ); + } + + velocity_refill_rx ( vlc ); + + /* deal with potential RX stall caused by RX ring underrun */ + writew ( VELOCITY_RXQCSR_RUN | VELOCITY_RXQCSR_WAK, + vlc->regs + VELOCITY_RXQCSRS ); +} + +/** + * Enable or disable interrupts + * + * @v netdev Network device + * @v enable Interrupts should be enabled + */ +static void velocity_irq ( struct net_device *netdev, int enable ) { + struct velocity_nic *vlc = netdev->priv; + + DBGC ( vlc, "VELOCITY %p interrupts %s\n", vlc, + enable ? "enable" : "disable" ); + + if (enable) { + /* Enable interrupts */ + writeb ( VELOCITY_CR3_GINTMSK1, vlc->regs + VELOCITY_CRS3 ); + } else { + /* Disable interrupts */ + writeb ( VELOCITY_CR3_GINTMSK1, vlc->regs + VELOCITY_CRC3 ); + } +} + +/** Velocity network device operations */ +static struct net_device_operations velocity_operations = { + .open = velocity_open, + .close = velocity_close, + .transmit = velocity_transmit, + .poll = velocity_poll, + .irq = velocity_irq, +}; + +/****************************************************************************** + * + * PCI interface + * + ****************************************************************************** + */ + +/** + * Probe PCI device + * + * @v pci PCI device + * @ret rc Return status code + */ +static int velocity_probe ( struct pci_device *pci ) { + struct net_device *netdev; + struct velocity_nic *vlc; + int rc; + + /* Allocate and initialise net device */ + netdev = alloc_etherdev ( sizeof ( *vlc ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &velocity_operations ); + vlc = netdev->priv; + pci_set_drvdata ( pci, netdev ); + netdev->dev = &pci->dev; + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map registers */ + vlc->regs = ioremap ( pci->membase, VELOCITY_BAR_SIZE ); + vlc->netdev = netdev; + + /* Reset the NIC */ + if ( ( rc = velocity_reset ( vlc ) ) != 0 ) + goto err_reset; + + /* Reload EEPROM */ + if ( ( rc = velocity_reload_eeprom ( vlc ) ) != 0 ) + goto err_reset; + + /* Get MAC address */ + netdev->hw_addr[0] = readb ( vlc->regs + VELOCITY_MAC0 ); + netdev->hw_addr[1] = readb ( vlc->regs + VELOCITY_MAC1 ); + netdev->hw_addr[2] = readb ( vlc->regs + VELOCITY_MAC2 ); + netdev->hw_addr[3] = readb ( vlc->regs + VELOCITY_MAC3 ); + netdev->hw_addr[4] = readb ( vlc->regs + VELOCITY_MAC4 ); + netdev->hw_addr[5] = readb ( vlc->regs + VELOCITY_MAC5 ); + + /* Initialise and reset MII interface */ + mii_init ( &vlc->mii, &velocity_mii_operations ); + if ( ( rc = mii_reset ( &vlc->mii ) ) != 0 ) { + DBGC ( vlc, "VELOCITY %p could not reset MII: %s\n", + vlc, strerror ( rc ) ); + goto err_mii_reset; + } + + /* Enable proper link advertising */ + velocity_set_link ( vlc ); + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register_netdev; + + return 0; + + err_register_netdev: + err_mii_reset: + velocity_reset ( vlc ); + err_reset: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void velocity_remove ( struct pci_device *pci ) { + struct net_device *netdev = pci_get_drvdata ( pci ); + struct velocity_nic *vlc = netdev->priv; + + /* Unregister network device */ + unregister_netdev ( netdev ); + + /* Reset card */ + velocity_reset ( vlc ); + + /* Free network device */ + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** Velocity PCI device IDs */ +static struct pci_device_id velocity_nics[] = { + PCI_ROM ( 0x1106, 0x3119, "vt6122", "VIA Velocity", 0 ), +}; + +/** Velocity PCI driver */ +struct pci_driver velocity_driver __pci_driver = { + .ids = velocity_nics, + .id_count = ( sizeof ( velocity_nics ) / sizeof ( velocity_nics[0] ) ), + .probe = velocity_probe, + .remove = velocity_remove, +}; diff --git a/src/drivers/net/velocity.h b/src/drivers/net/velocity.h new file mode 100644 index 00000000..04e6a146 --- /dev/null +++ b/src/drivers/net/velocity.h @@ -0,0 +1,356 @@ +#ifndef _VELOCITY_H +#define _VELOCITY_H + +/** @file + * + * VIA Velocity network driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** Skeleton BAR size */ +#define VELOCITY_BAR_SIZE 256 + +/** Default timeout */ +#define VELOCITY_TIMEOUT_US 10 * 1000 + +struct velocity_frag { + uint32_t addr; + uint32_t des2; +} __attribute__ ((packed)); + +/** Velocity descriptor format */ +struct velocity_tx_descriptor { + uint32_t des0; + uint32_t des1; + /* We only use the first fragment, the HW requires us to have 7 */ + struct velocity_frag frags[7]; +} __attribute__ ((packed)); + +struct velocity_rx_descriptor { + uint32_t des0; + uint32_t des1; + uint32_t addr; + uint32_t des2; +} __attribute__ ((packed)); + +#define VELOCITY_DES0_RMBC(_n) (((_n) >> 16) & 0x1fff) +#define VELOCITY_DES0_OWN (1 << 31) +#define VELOCITY_DES0_TERR (1 << 15) +#define VELOCITY_DES0_RXOK (1 << 15) +#define VELOCITY_DES0_FDX (1 << 14) +#define VELOCITY_DES0_GMII (1 << 13) +#define VELOCITY_DES0_LNKFL (1 << 12) +#define VELOCITY_DES0_SHDN (1 << 10) +#define VELOCITY_DES0_CRS (1 << 9) +#define VELOCITY_DES0_CDH (1 << 8) +#define VELOCITY_DES0_ABT (1 << 7) +#define VELOCITY_DES0_OWT (1 << 6) +#define VELOCITY_DES0_OWC (1 << 5) +#define VELOCITY_DES0_COLS (1 << 4) + +#define VELOCITY_DES0_RXSHDN (1 << 30) +#define VELOCITY_DES0_RXER (1 << 5) +#define VELOCITY_DES0_RLE (1 << 4) +#define VELOCITY_DES0_CE (1 << 3) +#define VELOCITY_DES0_FAE (1 << 2) +#define VELOCITY_DES0_CRC (1 << 1) +#define VELOCITY_DES0_RX_ERR ( VELOCITY_DES0_RXER | \ + VELOCITY_DES0_RLE | \ + VELOCITY_DES0_CE | \ + VELOCITY_DES0_FAE | \ + VELOCITY_DES0_CRC ) + +/** TX descriptor fragment number */ +#define VELOCITY_DES1_FRAG(_n) (((_n + 1) & 0xf) << 28) +#define VELOCITY_DES1_TCPLS ((1 << 24) | (1 << 25)) +#define VELOCITY_DES1_INTR (1 << 23) +#define VELOCITY_DES1_PIC (1 << 22) +#define VELOCITY_DES1_VETAG (1 << 21) +#define VELOCITY_DES1_IPCK (1 << 20) +#define VELOCITY_DES1_UDPCK (1 << 19) +#define VELOCITY_DES1_TCPCK (1 << 18) +#define VELOCITY_DES1_JMBO (1 << 17) +#define VELOCITY_DES1_CRC (1 << 16) + +#define VELOCITY_DES2_IC (1 << 31) +#define VELOCITY_DES2_SIZE(_n) (((_n) & 0x1fff) << 16) + +/** Number of receive descriptors + * + * Must be a multiple of 4 (hardware requirement). + */ +#define VELOCITY_RXDESC_NUM 8 +#define VELOCITY_RXDESC_SIZE \ + ( VELOCITY_RXDESC_NUM * sizeof ( struct velocity_rx_descriptor ) ) + +/** Number of transmit descriptors */ +#define VELOCITY_TXDESC_NUM 8 +#define VELOCITY_TXDESC_SIZE \ + ( VELOCITY_TXDESC_NUM * sizeof ( struct velocity_tx_descriptor ) ) + +/** Descriptor alignment */ +#define VELOCITY_RING_ALIGN 64 + +/** Receive buffer length */ +#define VELOCITY_RX_MAX_LEN 1536 + +/** MAC address registers */ +#define VELOCITY_MAC0 0x00 +#define VELOCITY_MAC1 0x01 +#define VELOCITY_MAC2 0x02 +#define VELOCITY_MAC3 0x03 +#define VELOCITY_MAC4 0x04 +#define VELOCITY_MAC5 0x05 + +/** Receive control register */ +#define VELOCITY_RCR 0x06 +#define RHINE_RCR_SYMERR_ACCEPT (1 << 7) /*< Accept symbol error */ +#define RHINE_RCR_FILTER_ACCEPT (1 << 6) /*< Accept based on filter */ +#define RHINE_RCR_LONG_ACCEPT (1 << 5) /*< Accept long packets */ +#define RHINE_RCR_PROMISC (1 << 4) /*< Promiscuous mode */ +#define RHINE_RCR_BCAST_ACCEPT (1 << 3) /*< Accept broadcast */ +#define RHINE_RCR_MCAST_ACCEPT (1 << 2) /*< Accept multicast */ +#define RHINE_RCR_RUNT_ACCEPT (1 << 1) /*< Accept runt frames */ +#define RHINE_RCR_ERR_ACCEPT (1 << 0) /*< Accept erroneous frames */ + +/** Transmit control register */ +#define VELOCITY_TCR 0x07 +#define VELOCITY_TCR_LB0 (1 << 0) /*< Loopback control */ +#define VELOCITY_TCR_LB1 (1 << 1) /*< Loopback control */ +#define VELOCITY_TCR_COLTMC0 (1 << 2) /*< Collision retry control */ +#define VELOCITY_TCR_COLTMC1 (1 << 3) /*< Collision retry control */ + +/** Command register 0 (set) */ +#define VELOCITY_CRS0 0x08 +#define VELOCITY_CR0_TXON (1 << 3) /*< Transmit enable */ +#define VELOCITY_CR0_RXON (1 << 2) /*< Receive enable */ +#define VELOCITY_CR0_STOP (1 << 1) /*< Stop NIC */ +#define VELOCITY_CR0_START (1 << 0) /*< Start NIC */ + +/** Command register 1 (set) */ +#define VELOCITY_CRS1 0x09 +#define VELOCITY_CR1_SFRST (1 << 7) /*< Software reset */ +#define VELOCITY_CR1_TM1EN (1 << 6) /*< Perioding software counting */ +#define VELOCITY_CR1_TM0EN (1 << 5) /*< Single-shot software counting */ +#define VELOCITY_CR1_DPOLL (1 << 3) /*< Disable auto polling */ +#define VELOCITY_CR1_DISAU (1 << 0) /*< Unicast reception disable */ + +/** Command register 2 (set) */ +#define VELOCITY_CRS2 0x0A +#define VELOCITY_CR2_XONEN (1 << 7) /*< XON/XOFF mode enable */ +#define VELOCITY_CR2_FDXTFCEN (1 << 6) /*< FDX flow control TX */ +#define VELOCITY_CR2_FDXRFCEN (1 << 5) +#define VELOCITY_CR2_HDXFCEN (1 << 4) + +/** Command register 3 (set) */ +#define VELOCITY_CRS3 0x0B +#define VELOCITY_CR3_FOSRST (1 << 6) +#define VELOCITY_CR3_FPHYRST (1 << 5) +#define VELOCITY_CR3_DIAG (1 << 4) +#define VELOCITY_CR3_INTPCTL (1 << 2) +#define VELOCITY_CR3_GINTMSK1 (1 << 1) +#define VELOCITY_CR3_SWPEND (1 << 0) + +/** Command register 0 (clear) */ +#define VELOCITY_CRC0 0x0C + +/** Command register 1 (clear) */ +#define VELOCITY_CRC1 0x0D + +/** Command register 2 (clear */ +#define VELOCITY_CRC2 0x0E + +/** Command register 3 (clear */ +#define VELOCITY_CRC3 0x0F +#define VELOCITY_CAM0 0x10 +#define VELOCITY_CAM1 0x11 +#define VELOCITY_CAM2 0x12 +#define VELOCITY_CAM3 0x13 +#define VELOCITY_CAM4 0x14 +#define VELOCITY_CAM5 0x15 +#define VELOCITY_CAM6 0x16 +#define VELOCITY_CAM7 0x17 +#define VELOCITY_TXDESC_HI 0x18 /* Hi part of 64bit txdesc base addr */ +#define VELOCITY_DATABUF_HI 0x1D /* Hi part of 64bit data buffer addr */ +#define VELOCITY_INTCTL0 0x20 /* interrupt control register */ +#define VELOCITY_RXSUPPTHR 0x20 +#define VELOCITY_TXSUPPTHR 0x20 +#define VELOCITY_INTHOLDOFF 0x20 +#define VELOCITY_INTCTL1 0x21 /* interrupt control register */ +#define VELOCITY_TXHOSTERR 0x22 /* TX host error status */ +#define VELOCITY_RXHOSTERR 0x23 /* RX host error status */ + +/** Interrupt status register 0 */ +#define VELOCITY_ISR0 0x24 +#define VELOCITY_ISR0_PTX3 (1 << 7) +#define VELOCITY_ISR0_PTX2 (1 << 6) +#define VELOCITY_ISR0_PTX1 (1 << 5) +#define VELOCITY_ISR0_PTX0 (1 << 4) +#define VELOCITY_ISR0_PTXI (1 << 3) +#define VELOCITY_ISR0_PRXI (1 << 2) +#define VELOCITY_ISR0_PPTXI (1 << 1) +#define VELOCITY_ISR0_PPRXI (1 << 0) + +/** Interrupt status register 1 */ +#define VELOCITY_ISR1 0x25 +#define VELOCITY_ISR1_SRCI (1 << 7) +#define VELOCITY_ISR1_LSTPEI (1 << 6) +#define VELOCITY_ISR1_LSTEI (1 << 5) +#define VELOCITY_ISR1_OVFL (1 << 4) +#define VELOCITY_ISR1_FLONI (1 << 3) +#define VELOCITY_ISR1_RACEI (1 << 2) + +/** Interrupt status register 2 */ +#define VELOCITY_ISR2 0x26 +#define VELOCITY_ISR2_HFLD (1 << 7) +#define VELOCITY_ISR2_UDPI (1 << 6) +#define VELOCITY_ISR2_MIBFI (1 << 5) +#define VELOCITY_ISR2_SHDNII (1 << 4) +#define VELOCITY_ISR2_PHYI (1 << 3) +#define VELOCITY_ISR2_PWEI (1 << 2) +#define VELOCITY_ISR2_TMR1I (1 << 1) +#define VELOCITY_ISR2_TMR0I (1 << 0) + +/** Interrupt status register 3 */ +#define VELOCITY_ISR3 0x27 + +/** Interrupt mask register 0 */ +#define VELOCITY_IMR0 0x28 + +/** Interrupt mask register 1 */ +#define VELOCITY_IMR1 0x29 + +/** Interrupt mask register 2 */ +#define VELOCITY_IMR2 0x2a + +/** Interrupt mask register 3 */ +#define VELOCITY_IMR3 0x2b + +#define VELOCITY_TXSTS_PORT 0x2C /* Transmit status port (???) */ +#define VELOCITY_TXQCSRS 0x30 /* TX queue ctl/status set */ + +#define VELOCITY_TXQCSRS_DEAD3 (1 << 15) +#define VELOCITY_TXQCSRS_WAK3 (1 << 14) +#define VELOCITY_TXQCSRS_ACT3 (1 << 13) +#define VELOCITY_TXQCSRS_RUN3 (1 << 12) +#define VELOCITY_TXQCSRS_DEAD2 (1 << 11) +#define VELOCITY_TXQCSRS_WAK2 (1 << 10) +#define VELOCITY_TXQCSRS_ACT2 (1 << 9) +#define VELOCITY_TXQCSRS_RUN2 (1 << 8) +#define VELOCITY_TXQCSRS_DEAD1 (1 << 7) +#define VELOCITY_TXQCSRS_WAK1 (1 << 6) +#define VELOCITY_TXQCSRS_ACT1 (1 << 5) +#define VELOCITY_TXQCSRS_RUN1 (1 << 4) +#define VELOCITY_TXQCSRS_DEAD0 (1 << 3) +#define VELOCITY_TXQCSRS_WAK0 (1 << 2) +#define VELOCITY_TXQCSRS_ACT0 (1 << 1) +#define VELOCITY_TXQCSRS_RUN0 (1 << 0) + +#define VELOCITY_RXQCSRS 0x32 /* RX queue ctl/status set */ +#define VELOCITY_RXQCSRC 0x36 + +#define VELOCITY_RXQCSR_DEAD (1 << 3) +#define VELOCITY_RXQCSR_WAK (1 << 2) +#define VELOCITY_RXQCSR_ACT (1 << 1) +#define VELOCITY_RXQCSR_RUN (1 << 0) + +#define VELOCITY_TXQCSRC 0x34 /* TX queue ctl/status clear */ +#define VELOCITY_RXQCSRC 0x36 /* RX queue ctl/status clear */ +#define VELOCITY_RXDESC_ADDR_LO 0x38 /* RX desc base addr (lo 32 bits) */ +#define VELOCITY_RXDESC_CONSIDX 0x3C /* Current RX descriptor index */ +#define VELOCITY_TXQTIMER 0x3E /* TX queue timer pend register */ +#define VELOCITY_RXQTIMER 0x3F /* RX queue timer pend register */ +#define VELOCITY_TXDESC_ADDR_LO0 0x40 /* TX desc0 base addr (lo 32 bits) */ +#define VELOCITY_TXDESC_ADDR_LO1 0x44 /* TX desc1 base addr (lo 32 bits) */ +#define VELOCITY_TXDESC_ADDR_LO2 0x48 /* TX desc2 base addr (lo 32 bits) */ +#define VELOCITY_TXDESC_ADDR_LO3 0x4C /* TX desc3 base addr (lo 32 bits) */ +#define VELOCITY_RXDESCNUM 0x50 /* Size of RX desc ring */ +#define VELOCITY_TXDESCNUM 0x52 /* Size of TX desc ring */ +#define VELOCITY_TXDESC_CONSIDX0 0x54 /* Current TX descriptor index */ +#define VELOCITY_TXDESC_CONSIDX1 0x56 /* Current TX descriptor index */ +#define VELOCITY_TXDESC_CONSIDX2 0x58 /* Current TX descriptor index */ +#define VELOCITY_TXDESC_CONSIDX3 0x5A /* Current TX descriptor index */ +#define VELOCITY_TX_PAUSE_TIMER 0x5C /* TX pause frame timer */ +#define VELOCITY_RXDESC_RESIDUECNT 0x5E /* RX descriptor residue count */ +#define VELOCITY_FIFOTEST0 0x60 /* FIFO test register */ +#define VELOCITY_FIFOTEST1 0x64 /* FIFO test register */ +#define VELOCITY_CAMADDR 0x68 /* CAM address register */ +#define VELOCITY_CAMCTL 0x69 /* CAM control register */ +#define VELOCITY_MIICFG 0x6C /* MII port config register */ +#define VELOCITY_MIISR 0x6D /* MII port status register */ +#define VELOCITY_MIISR_IDLE (1 << 7) +#define VELOCITY_PHYSTS0 0x6E /* PHY status register */ +#define VELOCITY_PHYSTS0_LINK (1 << 6) +#define VELOCITY_PHYSTS1 0x6F /* PHY status register */ +#define VELOCITY_MIICR 0x70 /* MII command register */ +#define VELOCITY_MIICR_MAUTO (1 << 7) +#define VELOCITY_MIICR_RCMD (1 << 6) +#define VELOCITY_MIICR_WCMD (1 << 5) +#define VELOCITY_MIICR_MDPM (1 << 4) +#define VELOCITY_MIICR_MOUT (1 << 3) +#define VELOCITY_MIICR_MDO (1 << 2) +#define VELOCITY_MIICR_MDI (1 << 1) +#define VELOCITY_MIICR_MDC (1 << 0) + +#define VELOCITY_MIIADDR 0x71 /* MII address register */ +#define VELOCITY_MIIDATA 0x72 /* MII data register */ +#define VELOCITY_SSTIMER 0x74 /* single-shot timer */ +#define VELOCITY_PTIMER 0x76 /* periodic timer */ +#define VELOCITY_DMACFG0 0x7C /* DMA config 0 */ +#define VELOCITY_DMACFG1 0x7D /* DMA config 1 */ +#define VELOCITY_RXCFG 0x7E /* MAC RX config */ +#define VELOCITY_TXCFG 0x7F /* MAC TX config */ +#define VELOCITY_SWEEDATA 0x85 /* EEPROM software loaded data */ + +/** Chip Configuration Register A */ +#define VELOCITY_CFGA 0x78 +#define VELOCITY_CFGA_PACPI (1 << 0) + +/** Power Management Sticky Register */ +#define VELOCITY_STICKY 0x83 +#define VELOCITY_STICKY_DS0 (1 << 0) +#define VELOCITY_STICKY_DS1 (1 << 1) + +#define VELOCITY_EEWRDAT 0x8C /* EEPROM embedded write */ +#define VELOCITY_EECSUM 0x92 /* EEPROM checksum */ +#define VELOCITY_EECSR 0x93 /* EEPROM control/status */ +#define VELOCITY_EECSR_RELOAD (1 << 5) +#define VELOCITY_EERDDAT 0x94 /* EEPROM embedded read */ +#define VELOCITY_EEADDR 0x96 /* EEPROM address */ +#define VELOCITY_EECMD 0x97 /* EEPROM embedded command */ + +/** A Velocity network card */ +struct velocity_nic { + /** Registers */ + void *regs; + /** MII interface */ + struct mii_interface mii; + /** Netdev */ + struct net_device *netdev; + + /** Receive descriptor ring */ + struct velocity_rx_descriptor *rx_ring; + /** Receive I/O buffers */ + struct io_buffer *rx_buffs[VELOCITY_RXDESC_NUM]; + /** Receive producer index */ + unsigned int rx_prod; + /** Receive consumer index */ + unsigned int rx_cons; + /** Receive commit number + * + * Used to fullfill the hardware requirement of returning receive buffers + * to the hardware only in blocks of 4. + */ + unsigned int rx_commit; + + /** Transmit descriptor ring */ + struct velocity_tx_descriptor *tx_ring; + /** Transmit producer index */ + unsigned int tx_prod; + /** Transmit consumer index */ + unsigned int tx_cons; +}; + +#endif /* _VELOCITY_H */ diff --git a/src/drivers/net/via-rhine.c b/src/drivers/net/via-rhine.c deleted file mode 100644 index f3bb4e01..00000000 --- a/src/drivers/net/via-rhine.c +++ /dev/null @@ -1,1447 +0,0 @@ -/* rhine.c:Fast Ethernet driver for Linux. */ -/* - Adapted 09-jan-2000 by Paolo Marini (paolom@prisma-eng.it) - - originally written by Donald Becker. - - This software may be used and distributed according to the terms - of the GNU Public License (GPL), incorporated herein by reference. - Drivers derived from this code also fall under the GPL and must retain - this authorship and copyright notice. - - Under no circumstances are the authors responsible for - the proper functioning of this software, nor do the authors assume any - responsibility for damages incurred with its use. - - This driver is designed for the VIA VT86C100A Rhine-II PCI Fast Ethernet - controller. - -*/ - -static const char *version = "rhine.c v1.0.2 2004-10-29\n"; - -/* A few user-configurable values. */ - -// max time out delay time -#define W_MAX_TIMEOUT 0x0FFFU - -/* Size of the in-memory receive ring. */ -#define RX_BUF_LEN_IDX 3 /* 0==8K, 1==16K, 2==32K, 3==64K */ -#define RX_BUF_LEN (8192 << RX_BUF_LEN_IDX) - -/* Size of the Tx bounce buffers -- must be at least (dev->mtu+14+4). */ -#define TX_BUF_SIZE 1536 -#define RX_BUF_SIZE 1536 - -/* PCI Tuning Parameters - Threshold is bytes transferred to chip before transmission starts. */ -#define TX_FIFO_THRESH 256 /* In bytes, rounded down to 32 byte units. */ - -/* The following settings are log_2(bytes)-4: 0 == 16 bytes .. 6==1024. */ -#define RX_FIFO_THRESH 4 /* Rx buffer level before first PCI xfer. */ -#define RX_DMA_BURST 4 /* Maximum PCI burst, '4' is 256 bytes */ -#define TX_DMA_BURST 4 - -/* Operational parameters that usually are not changed. */ -/* Time in jiffies before concluding the transmitter is hung. */ -#define TX_TIMEOUT ((2000*HZ)/1000) - -#include "etherboot.h" -#include "nic.h" -#include -#include - -/* define all ioaddr */ - -#define byPAR0 ioaddr -#define byRCR ioaddr + 6 -#define byTCR ioaddr + 7 -#define byCR0 ioaddr + 8 -#define byCR1 ioaddr + 9 -#define byISR0 ioaddr + 0x0c -#define byISR1 ioaddr + 0x0d -#define byIMR0 ioaddr + 0x0e -#define byIMR1 ioaddr + 0x0f -#define byMAR0 ioaddr + 0x10 -#define byMAR1 ioaddr + 0x11 -#define byMAR2 ioaddr + 0x12 -#define byMAR3 ioaddr + 0x13 -#define byMAR4 ioaddr + 0x14 -#define byMAR5 ioaddr + 0x15 -#define byMAR6 ioaddr + 0x16 -#define byMAR7 ioaddr + 0x17 -#define dwCurrentRxDescAddr ioaddr + 0x18 -#define dwCurrentTxDescAddr ioaddr + 0x1c -#define dwCurrentRDSE0 ioaddr + 0x20 -#define dwCurrentRDSE1 ioaddr + 0x24 -#define dwCurrentRDSE2 ioaddr + 0x28 -#define dwCurrentRDSE3 ioaddr + 0x2c -#define dwNextRDSE0 ioaddr + 0x30 -#define dwNextRDSE1 ioaddr + 0x34 -#define dwNextRDSE2 ioaddr + 0x38 -#define dwNextRDSE3 ioaddr + 0x3c -#define dwCurrentTDSE0 ioaddr + 0x40 -#define dwCurrentTDSE1 ioaddr + 0x44 -#define dwCurrentTDSE2 ioaddr + 0x48 -#define dwCurrentTDSE3 ioaddr + 0x4c -#define dwNextTDSE0 ioaddr + 0x50 -#define dwNextTDSE1 ioaddr + 0x54 -#define dwNextTDSE2 ioaddr + 0x58 -#define dwNextTDSE3 ioaddr + 0x5c -#define dwCurrRxDMAPtr ioaddr + 0x60 -#define dwCurrTxDMAPtr ioaddr + 0x64 -#define byMPHY ioaddr + 0x6c -#define byMIISR ioaddr + 0x6d -#define byBCR0 ioaddr + 0x6e -#define byBCR1 ioaddr + 0x6f -#define byMIICR ioaddr + 0x70 -#define byMIIAD ioaddr + 0x71 -#define wMIIDATA ioaddr + 0x72 -#define byEECSR ioaddr + 0x74 -#define byTEST ioaddr + 0x75 -#define byGPIO ioaddr + 0x76 -#define byCFGA ioaddr + 0x78 -#define byCFGB ioaddr + 0x79 -#define byCFGC ioaddr + 0x7a -#define byCFGD ioaddr + 0x7b -#define wTallyCntMPA ioaddr + 0x7c -#define wTallyCntCRC ioaddr + 0x7d -#define bySTICKHW ioaddr + 0x83 -#define byWOLcrClr ioaddr + 0xA4 -#define byWOLcgClr ioaddr + 0xA7 -#define byPwrcsrClr ioaddr + 0xAC - -/*--------------------- Exioaddr Definitions -------------------------*/ - -/* - * Bits in the RCR register - */ - -#define RCR_RRFT2 0x80 -#define RCR_RRFT1 0x40 -#define RCR_RRFT0 0x20 -#define RCR_PROM 0x10 -#define RCR_AB 0x08 -#define RCR_AM 0x04 -#define RCR_AR 0x02 -#define RCR_SEP 0x01 - -/* - * Bits in the TCR register - */ - -#define TCR_RTSF 0x80 -#define TCR_RTFT1 0x40 -#define TCR_RTFT0 0x20 -#define TCR_OFSET 0x08 -#define TCR_LB1 0x04 /* loopback[1] */ -#define TCR_LB0 0x02 /* loopback[0] */ - -/* - * Bits in the CR0 register - */ - -#define CR0_RDMD 0x40 /* rx descriptor polling demand */ -#define CR0_TDMD 0x20 /* tx descriptor polling demand */ -#define CR0_TXON 0x10 -#define CR0_RXON 0x08 -#define CR0_STOP 0x04 /* stop NIC, default = 1 */ -#define CR0_STRT 0x02 /* start NIC */ -#define CR0_INIT 0x01 /* start init process */ - - -/* - * Bits in the CR1 register - */ - -#define CR1_SFRST 0x80 /* software reset */ -#define CR1_RDMD1 0x40 /* RDMD1 */ -#define CR1_TDMD1 0x20 /* TDMD1 */ -#define CR1_KEYPAG 0x10 /* turn on par/key */ -#define CR1_DPOLL 0x08 /* disable rx/tx auto polling */ -#define CR1_FDX 0x04 /* full duplex mode */ -#define CR1_ETEN 0x02 /* early tx mode */ -#define CR1_EREN 0x01 /* early rx mode */ - -/* - * Bits in the CR register - */ - -#define CR_RDMD 0x0040 /* rx descriptor polling demand */ -#define CR_TDMD 0x0020 /* tx descriptor polling demand */ -#define CR_TXON 0x0010 -#define CR_RXON 0x0008 -#define CR_STOP 0x0004 /* stop NIC, default = 1 */ -#define CR_STRT 0x0002 /* start NIC */ -#define CR_INIT 0x0001 /* start init process */ -#define CR_SFRST 0x8000 /* software reset */ -#define CR_RDMD1 0x4000 /* RDMD1 */ -#define CR_TDMD1 0x2000 /* TDMD1 */ -#define CR_KEYPAG 0x1000 /* turn on par/key */ -#define CR_DPOLL 0x0800 /* disable rx/tx auto polling */ -#define CR_FDX 0x0400 /* full duplex mode */ -#define CR_ETEN 0x0200 /* early tx mode */ -#define CR_EREN 0x0100 /* early rx mode */ - -/* - * Bits in the IMR0 register - */ - -#define IMR0_CNTM 0x80 -#define IMR0_BEM 0x40 -#define IMR0_RUM 0x20 -#define IMR0_TUM 0x10 -#define IMR0_TXEM 0x08 -#define IMR0_RXEM 0x04 -#define IMR0_PTXM 0x02 -#define IMR0_PRXM 0x01 - -/* define imrshadow */ - -#define IMRShadow 0x5AFF - -/* - * Bits in the IMR1 register - */ - -#define IMR1_INITM 0x80 -#define IMR1_SRCM 0x40 -#define IMR1_NBFM 0x10 -#define IMR1_PRAIM 0x08 -#define IMR1_RES0M 0x04 -#define IMR1_ETM 0x02 -#define IMR1_ERM 0x01 - -/* - * Bits in the ISR register - */ - -#define ISR_INITI 0x8000 -#define ISR_SRCI 0x4000 -#define ISR_ABTI 0x2000 -#define ISR_NORBF 0x1000 -#define ISR_PKTRA 0x0800 -#define ISR_RES0 0x0400 -#define ISR_ETI 0x0200 -#define ISR_ERI 0x0100 -#define ISR_CNT 0x0080 -#define ISR_BE 0x0040 -#define ISR_RU 0x0020 -#define ISR_TU 0x0010 -#define ISR_TXE 0x0008 -#define ISR_RXE 0x0004 -#define ISR_PTX 0x0002 -#define ISR_PRX 0x0001 - -/* - * Bits in the ISR0 register - */ - -#define ISR0_CNT 0x80 -#define ISR0_BE 0x40 -#define ISR0_RU 0x20 -#define ISR0_TU 0x10 -#define ISR0_TXE 0x08 -#define ISR0_RXE 0x04 -#define ISR0_PTX 0x02 -#define ISR0_PRX 0x01 - -/* - * Bits in the ISR1 register - */ - -#define ISR1_INITI 0x80 -#define ISR1_SRCI 0x40 -#define ISR1_NORBF 0x10 -#define ISR1_PKTRA 0x08 -#define ISR1_ETI 0x02 -#define ISR1_ERI 0x01 - -/* ISR ABNORMAL CONDITION */ - -#define ISR_ABNORMAL ISR_BE+ISR_RU+ISR_TU+ISR_CNT+ISR_NORBF+ISR_PKTRA - -/* - * Bits in the MIISR register - */ - -#define MIISR_MIIERR 0x08 -#define MIISR_MRERR 0x04 -#define MIISR_LNKFL 0x02 -#define MIISR_SPEED 0x01 - -/* - * Bits in the MIICR register - */ - -#define MIICR_MAUTO 0x80 -#define MIICR_RCMD 0x40 -#define MIICR_WCMD 0x20 -#define MIICR_MDPM 0x10 -#define MIICR_MOUT 0x08 -#define MIICR_MDO 0x04 -#define MIICR_MDI 0x02 -#define MIICR_MDC 0x01 - -/* - * Bits in the EECSR register - */ - -#define EECSR_EEPR 0x80 /* eeprom programed status, 73h means programed */ -#define EECSR_EMBP 0x40 /* eeprom embedded programming */ -#define EECSR_AUTOLD 0x20 /* eeprom content reload */ -#define EECSR_DPM 0x10 /* eeprom direct programming */ -#define EECSR_CS 0x08 /* eeprom CS pin */ -#define EECSR_SK 0x04 /* eeprom SK pin */ -#define EECSR_DI 0x02 /* eeprom DI pin */ -#define EECSR_DO 0x01 /* eeprom DO pin */ - -/* - * Bits in the BCR0 register - */ - -#define BCR0_CRFT2 0x20 -#define BCR0_CRFT1 0x10 -#define BCR0_CRFT0 0x08 -#define BCR0_DMAL2 0x04 -#define BCR0_DMAL1 0x02 -#define BCR0_DMAL0 0x01 - -/* - * Bits in the BCR1 register - */ - -#define BCR1_CTSF 0x20 -#define BCR1_CTFT1 0x10 -#define BCR1_CTFT0 0x08 -#define BCR1_POT2 0x04 -#define BCR1_POT1 0x02 -#define BCR1_POT0 0x01 - -/* - * Bits in the CFGA register - */ - -#define CFGA_EELOAD 0x80 /* enable eeprom embedded and direct programming */ -#define CFGA_JUMPER 0x40 -#define CFGA_MTGPIO 0x08 -#define CFGA_T10EN 0x02 -#define CFGA_AUTO 0x01 - -/* - * Bits in the CFGB register - */ - -#define CFGB_PD 0x80 -#define CFGB_POLEN 0x02 -#define CFGB_LNKEN 0x01 - -/* - * Bits in the CFGC register - */ - -#define CFGC_M10TIO 0x80 -#define CFGC_M10POL 0x40 -#define CFGC_PHY1 0x20 -#define CFGC_PHY0 0x10 -#define CFGC_BTSEL 0x08 -#define CFGC_BPS2 0x04 /* bootrom select[2] */ -#define CFGC_BPS1 0x02 /* bootrom select[1] */ -#define CFGC_BPS0 0x01 /* bootrom select[0] */ - -/* - * Bits in the CFGD register - */ - -#define CFGD_GPIOEN 0x80 -#define CFGD_DIAG 0x40 -#define CFGD_MAGIC 0x10 -#define CFGD_RANDOM 0x08 -#define CFGD_CFDX 0x04 -#define CFGD_CEREN 0x02 -#define CFGD_CETEN 0x01 - -/* Bits in RSR */ -#define RSR_RERR 0x00000001 -#define RSR_CRC 0x00000002 -#define RSR_FAE 0x00000004 -#define RSR_FOV 0x00000008 -#define RSR_LONG 0x00000010 -#define RSR_RUNT 0x00000020 -#define RSR_SERR 0x00000040 -#define RSR_BUFF 0x00000080 -#define RSR_EDP 0x00000100 -#define RSR_STP 0x00000200 -#define RSR_CHN 0x00000400 -#define RSR_PHY 0x00000800 -#define RSR_BAR 0x00001000 -#define RSR_MAR 0x00002000 -#define RSR_RXOK 0x00008000 -#define RSR_ABNORMAL RSR_RERR+RSR_LONG+RSR_RUNT - -/* Bits in TSR */ -#define TSR_NCR0 0x00000001 -#define TSR_NCR1 0x00000002 -#define TSR_NCR2 0x00000004 -#define TSR_NCR3 0x00000008 -#define TSR_COLS 0x00000010 -#define TSR_CDH 0x00000080 -#define TSR_ABT 0x00000100 -#define TSR_OWC 0x00000200 -#define TSR_CRS 0x00000400 -#define TSR_UDF 0x00000800 -#define TSR_TBUFF 0x00001000 -#define TSR_SERR 0x00002000 -#define TSR_JAB 0x00004000 -#define TSR_TERR 0x00008000 -#define TSR_ABNORMAL TSR_TERR+TSR_OWC+TSR_ABT+TSR_JAB+TSR_CRS -#define TSR_OWN_BIT 0x80000000 - -#define CB_DELAY_LOOP_WAIT 10 /* 10ms */ -/* enabled mask value of irq */ - -#define W_IMR_MASK_VALUE 0x1BFF /* initial value of IMR */ - -/* Ethernet address filter type */ -#define PKT_TYPE_DIRECTED 0x0001 /* obsolete, directed address is always accepted */ -#define PKT_TYPE_MULTICAST 0x0002 -#define PKT_TYPE_ALL_MULTICAST 0x0004 -#define PKT_TYPE_BROADCAST 0x0008 -#define PKT_TYPE_PROMISCUOUS 0x0020 -#define PKT_TYPE_LONG 0x2000 -#define PKT_TYPE_RUNT 0x4000 -#define PKT_TYPE_ERROR 0x8000 /* accept error packets, e.g. CRC error */ - -/* Loopback mode */ - -#define NIC_LB_NONE 0x00 -#define NIC_LB_INTERNAL 0x01 -#define NIC_LB_PHY 0x02 /* MII or Internal-10BaseT loopback */ - -#define TX_RING_SIZE 2 -#define RX_RING_SIZE 2 -#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer. */ - -#define PCI_REG_MODE3 0x53 -#define MODE3_MIION 0x04 /* in PCI_REG_MOD3 OF PCI space */ - -enum rhine_revs { - VT86C100A = 0x00, - VTunknown0 = 0x20, - VT6102 = 0x40, - VT8231 = 0x50, /* Integrated MAC */ - VT8233 = 0x60, /* Integrated MAC */ - VT8235 = 0x74, /* Integrated MAC */ - VT8237 = 0x78, /* Integrated MAC */ - VTunknown1 = 0x7C, - VT6105 = 0x80, - VT6105_B0 = 0x83, - VT6105L = 0x8A, - VT6107 = 0x8C, - VTunknown2 = 0x8E, - VT6105M = 0x90, -}; - -/* Transmit and receive descriptors definition */ - -struct rhine_tx_desc -{ - union VTC_tx_status_tag - { - struct - { - unsigned long ncro:1; - unsigned long ncr1:1; - unsigned long ncr2:1; - unsigned long ncr3:1; - unsigned long cols:1; - unsigned long reserve_1:2; - unsigned long cdh:1; - unsigned long abt:1; - unsigned long owc:1; - unsigned long crs:1; - unsigned long udf:1; - unsigned long tbuff:1; - unsigned long serr:1; - unsigned long jab:1; - unsigned long terr:1; - unsigned long reserve_2:15; - unsigned long own_bit:1; - } - bits; - unsigned long lw; - } - tx_status; - - union VTC_tx_ctrl_tag - { - struct - { - unsigned long tx_buf_size:11; - unsigned long extend_tx_buf_size:4; - unsigned long chn:1; - unsigned long crc:1; - unsigned long reserve_1:4; - unsigned long stp:1; - unsigned long edp:1; - unsigned long ic:1; - unsigned long reserve_2:8; - } - bits; - unsigned long lw; - } - tx_ctrl; - - unsigned long buf_addr_1:32; - unsigned long buf_addr_2:32; - -}; - -struct rhine_rx_desc -{ - union VTC_rx_status_tag - { - struct - { - unsigned long rerr:1; - unsigned long crc_error:1; - unsigned long fae:1; - unsigned long fov:1; - unsigned long toolong:1; - unsigned long runt:1; - unsigned long serr:1; - unsigned long buff:1; - unsigned long edp:1; - unsigned long stp:1; - unsigned long chn:1; - unsigned long phy:1; - unsigned long bar:1; - unsigned long mar:1; - unsigned long reserve_1:1; - unsigned long rxok:1; - unsigned long frame_length:11; - unsigned long reverve_2:4; - unsigned long own_bit:1; - } - bits; - unsigned long lw; - } - rx_status; - - union VTC_rx_ctrl_tag - { - struct - { - unsigned long rx_buf_size:11; - unsigned long extend_rx_buf_size:4; - unsigned long reserved_1:17; - } - bits; - unsigned long lw; - } - rx_ctrl; - - unsigned long buf_addr_1:32; - unsigned long buf_addr_2:32; - -}; - -struct { - char txbuf[TX_RING_SIZE * PKT_BUF_SZ + 32]; - char rxbuf[RX_RING_SIZE * PKT_BUF_SZ + 32]; - char txdesc[TX_RING_SIZE * sizeof (struct rhine_tx_desc) + 32]; - char rxdesc[RX_RING_SIZE * sizeof (struct rhine_rx_desc) + 32]; -} rhine_buffers __shared; - -/* The I/O extent. */ -#define rhine_TOTAL_SIZE 0x80 - -#ifdef HAVE_DEVLIST -struct netdev_entry rhine_drv = - { "rhine", rhine_probe, rhine_TOTAL_SIZE, NULL }; -#endif - -static int rhine_debug = 1; - -/* - Theory of Operation - -I. Board Compatibility - -This driver is designed for the VIA 86c100A Rhine-II PCI Fast Ethernet -controller. - -II. Board-specific settings - -Boards with this chip are functional only in a bus-master PCI slot. - -Many operational settings are loaded from the EEPROM to the Config word at -offset 0x78. This driver assumes that they are correct. -If this driver is compiled to use PCI memory space operations the EEPROM -must be configured to enable memory ops. - -III. Driver operation - -IIIa. Ring buffers - -This driver uses two statically allocated fixed-size descriptor lists -formed into rings by a branch from the final descriptor to the beginning of -the list. The ring sizes are set at compile time by RX/TX_RING_SIZE. - -IIIb/c. Transmit/Receive Structure - -This driver attempts to use a zero-copy receive and transmit scheme. - -Alas, all data buffers are required to start on a 32 bit boundary, so -the driver must often copy transmit packets into bounce buffers. - -The driver allocates full frame size skbuffs for the Rx ring buffers at -open() time and passes the skb->data field to the chip as receive data -buffers. When an incoming frame is less than RX_COPYBREAK bytes long, -a fresh skbuff is allocated and the frame is copied to the new skbuff. -When the incoming frame is larger, the skbuff is passed directly up the -protocol stack. Buffers consumed this way are replaced by newly allocated -skbuffs in the last phase of netdev_rx(). - -The RX_COPYBREAK value is chosen to trade-off the memory wasted by -using a full-sized skbuff for small frames vs. the copying costs of larger -frames. New boards are typically used in generously configured machines -and the underfilled buffers have negligible impact compared to the benefit of -a single allocation size, so the default value of zero results in never -copying packets. When copying is done, the cost is usually mitigated by using -a combined copy/checksum routine. Copying also preloads the cache, which is -most useful with small frames. - -Since the VIA chips are only able to transfer data to buffers on 32 bit -boundaries, the the IP header at offset 14 in an ethernet frame isn't -longword aligned for further processing. Copying these unaligned buffers -has the beneficial effect of 16-byte aligning the IP header. - -IIId. Synchronization - -The driver runs as two independent, single-threaded flows of control. One -is the send-packet routine, which enforces single-threaded use by the -dev->tbusy flag. The other thread is the interrupt handler, which is single -threaded by the hardware and interrupt handling software. - -The send packet thread has partial control over the Tx ring and 'dev->tbusy' -flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next -queue slot is empty, it clears the tbusy flag when finished otherwise it sets -the 'lp->tx_full' flag. - -The interrupt handler has exclusive control over the Rx ring and records stats -from the Tx ring. After reaping the stats, it marks the Tx queue entry as -empty by incrementing the dirty_tx mark. Iff the 'lp->tx_full' flag is set, it -clears both the tx_full and tbusy flags. - -IV. Notes - -IVb. References - -Preliminary VT86C100A manual from http://www.via.com.tw/ -http://cesdis.gsfc.nasa.gov/linux/misc/100mbps.html -http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html - -IVc. Errata - -The VT86C100A manual is not reliable information. -The chip does not handle unaligned transmit or receive buffers, resulting -in significant performance degradation for bounce buffer copies on transmit -and unaligned IP headers on receive. -The chip does not pad to minimum transmit length. - -*/ - -/* The rest of these values should never change. */ -#define NUM_TX_DESC 2 /* Number of Tx descriptor registers. */ - -static struct rhine_private -{ - char devname[8]; /* Used only for kernel debugging. */ - const char *product_name; - struct rhine_rx_desc *rx_ring; - struct rhine_tx_desc *tx_ring; - char *rx_buffs[RX_RING_SIZE]; - char *tx_buffs[TX_RING_SIZE]; - - /* temporary Rx buffers. */ - - int chip_id; - int chip_revision; - unsigned short ioaddr; - unsigned int cur_rx, cur_tx; /* The next free and used entries */ - unsigned int dirty_rx, dirty_tx; - /* The saved address of a sent-in-place packet/buffer, for skfree(). */ - struct sk_buff *tx_skbuff[TX_RING_SIZE]; - unsigned char mc_filter[8]; /* Current multicast filter. */ - char phys[4]; /* MII device addresses. */ - unsigned int tx_full:1; /* The Tx queue is full. */ - unsigned int full_duplex:1; /* Full-duplex operation requested. */ - unsigned int default_port:4; /* Last dev->if_port value. */ - unsigned int media2:4; /* Secondary monitored media port. */ - unsigned int medialock:1; /* Don't sense media type. */ - unsigned int mediasense:1; /* Media sensing in progress. */ -} -rhine; - -static void rhine_probe1 (struct nic *nic, struct pci_device *pci, int ioaddr, - int chip_id, int options); -static int QueryAuto (int); -static int ReadMII (int byMIIIndex, int); -static void WriteMII (char, char, char, int); -static void MIIDelay (void); -static void rhine_init_ring (struct nic *dev); -static void rhine_disable (struct nic *nic); -static void rhine_reset (struct nic *nic); -static int rhine_poll (struct nic *nic, int retrieve); -static void rhine_transmit (struct nic *nic, const char *d, unsigned int t, - unsigned int s, const char *p); -static void reload_eeprom(int ioaddr); - - -static void reload_eeprom(int ioaddr) -{ - int i; - outb(0x20, byEECSR); - /* Typically 2 cycles to reload. */ - for (i = 0; i < 150; i++) - if (! (inb(byEECSR) & 0x20)) - break; -} -/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ -static void -rhine_init_ring (struct nic *nic) -{ - struct rhine_private *tp = (struct rhine_private *) nic->priv_data; - int i; - - tp->tx_full = 0; - tp->cur_rx = tp->cur_tx = 0; - tp->dirty_rx = tp->dirty_tx = 0; - - for (i = 0; i < RX_RING_SIZE; i++) - { - - tp->rx_ring[i].rx_status.bits.own_bit = 1; - tp->rx_ring[i].rx_ctrl.bits.rx_buf_size = 1536; - - tp->rx_ring[i].buf_addr_1 = virt_to_bus (tp->rx_buffs[i]); - tp->rx_ring[i].buf_addr_2 = virt_to_bus (&tp->rx_ring[i + 1]); - /* printf("[%d]buf1=%hX,buf2=%hX",i,tp->rx_ring[i].buf_addr_1,tp->rx_ring[i].buf_addr_2); */ - } - /* Mark the last entry as wrapping the ring. */ - /* tp->rx_ring[i-1].rx_ctrl.bits.rx_buf_size =1518; */ - tp->rx_ring[i - 1].buf_addr_2 = virt_to_bus (&tp->rx_ring[0]); - /*printf("[%d]buf1=%hX,buf2=%hX",i-1,tp->rx_ring[i-1].buf_addr_1,tp->rx_ring[i-1].buf_addr_2); */ - - /* The Tx buffer descriptor is filled in as needed, but we - do need to clear the ownership bit. */ - - for (i = 0; i < TX_RING_SIZE; i++) - { - - tp->tx_ring[i].tx_status.lw = 0; - tp->tx_ring[i].tx_ctrl.lw = 0x00e08000; - tp->tx_ring[i].buf_addr_1 = virt_to_bus (tp->tx_buffs[i]); - tp->tx_ring[i].buf_addr_2 = virt_to_bus (&tp->tx_ring[i + 1]); - /* printf("[%d]buf1=%hX,buf2=%hX",i,tp->tx_ring[i].buf_addr_1,tp->tx_ring[i].buf_addr_2); */ - } - - tp->tx_ring[i - 1].buf_addr_2 = virt_to_bus (&tp->tx_ring[0]); - /* printf("[%d]buf1=%hX,buf2=%hX",i,tp->tx_ring[i-1].buf_addr_1,tp->tx_ring[i-1].buf_addr_2); */ -} - -int -QueryAuto (int ioaddr) -{ - int byMIIIndex; - int MIIReturn; - - int advertising,mii_reg5; - int negociated; - - byMIIIndex = 0x04; - MIIReturn = ReadMII (byMIIIndex, ioaddr); - advertising=MIIReturn; - - byMIIIndex = 0x05; - MIIReturn = ReadMII (byMIIIndex, ioaddr); - mii_reg5=MIIReturn; - - negociated=mii_reg5 & advertising; - - if ( (negociated & 0x100) || (negociated & 0x1C0) == 0x40 ) - return 1; - else - return 0; - -} - -int -ReadMII (int byMIIIndex, int ioaddr) -{ - int ReturnMII; - char byMIIAdrbak; - char byMIICRbak; - char byMIItemp; - unsigned long ct; - - byMIIAdrbak = inb (byMIIAD); - byMIICRbak = inb (byMIICR); - outb (byMIICRbak & 0x7f, byMIICR); - MIIDelay (); - - outb (byMIIIndex, byMIIAD); - MIIDelay (); - - outb (inb (byMIICR) | 0x40, byMIICR); - - byMIItemp = inb (byMIICR); - byMIItemp = byMIItemp & 0x40; - - ct = currticks(); - while (byMIItemp != 0 && ct + 2*1000 < currticks()) - { - byMIItemp = inb (byMIICR); - byMIItemp = byMIItemp & 0x40; - } - MIIDelay (); - - ReturnMII = inw (wMIIDATA); - - outb (byMIIAdrbak, byMIIAD); - outb (byMIICRbak, byMIICR); - MIIDelay (); - - return (ReturnMII); - -} - -void -WriteMII (char byMIISetByte, char byMIISetBit, char byMIIOP, int ioaddr) -{ - int ReadMIItmp; - int MIIMask; - char byMIIAdrbak; - char byMIICRbak; - char byMIItemp; - unsigned long ct; - - - byMIIAdrbak = inb (byMIIAD); - - byMIICRbak = inb (byMIICR); - outb (byMIICRbak & 0x7f, byMIICR); - MIIDelay (); - outb (byMIISetByte, byMIIAD); - MIIDelay (); - - outb (inb (byMIICR) | 0x40, byMIICR); - - byMIItemp = inb (byMIICR); - byMIItemp = byMIItemp & 0x40; - - ct = currticks(); - while (byMIItemp != 0 && ct + 2*1000 < currticks()) - { - byMIItemp = inb (byMIICR); - byMIItemp = byMIItemp & 0x40; - } - MIIDelay (); - - ReadMIItmp = inw (wMIIDATA); - MIIMask = 0x0001; - MIIMask = MIIMask << byMIISetBit; - - - if (byMIIOP == 0) - { - MIIMask = ~MIIMask; - ReadMIItmp = ReadMIItmp & MIIMask; - } - else - { - ReadMIItmp = ReadMIItmp | MIIMask; - - } - outw (ReadMIItmp, wMIIDATA); - MIIDelay (); - - outb (inb (byMIICR) | 0x20, byMIICR); - byMIItemp = inb (byMIICR); - byMIItemp = byMIItemp & 0x20; - - ct = currticks(); - while (byMIItemp != 0 && ct + 2*1000 < currticks()) - { - byMIItemp = inb (byMIICR); - byMIItemp = byMIItemp & 0x20; - } - MIIDelay (); - - outb (byMIIAdrbak & 0x7f, byMIIAD); - outb (byMIICRbak, byMIICR); - MIIDelay (); - -} - -void -MIIDelay (void) -{ - int i; - for (i = 0; i < 0x7fff; i++) - { - ( void ) inb (0x61); - ( void ) inb (0x61); - ( void ) inb (0x61); - ( void ) inb (0x61); - } -} - -/* Offsets to the device registers. */ -enum register_offsets { - StationAddr=0x00, RxConfig=0x06, TxConfig=0x07, ChipCmd=0x08, - IntrStatus=0x0C, IntrEnable=0x0E, - MulticastFilter0=0x10, MulticastFilter1=0x14, - RxRingPtr=0x18, TxRingPtr=0x1C, GFIFOTest=0x54, - MIIPhyAddr=0x6C, MIIStatus=0x6D, PCIBusConfig=0x6E, - MIICmd=0x70, MIIRegAddr=0x71, MIIData=0x72, MACRegEEcsr=0x74, - ConfigA=0x78, ConfigB=0x79, ConfigC=0x7A, ConfigD=0x7B, - RxMissed=0x7C, RxCRCErrs=0x7E, MiscCmd=0x81, - StickyHW=0x83, IntrStatus2=0x84, WOLcrClr=0xA4, WOLcgClr=0xA7, - PwrcsrClr=0xAC, -}; - -/* Bits in the interrupt status/mask registers. */ -enum intr_status_bits { - IntrRxDone=0x0001, IntrRxErr=0x0004, IntrRxEmpty=0x0020, - IntrTxDone=0x0002, IntrTxError=0x0008, IntrTxUnderrun=0x0210, - IntrPCIErr=0x0040, - IntrStatsMax=0x0080, IntrRxEarly=0x0100, - IntrRxOverflow=0x0400, IntrRxDropped=0x0800, IntrRxNoBuf=0x1000, - IntrTxAborted=0x2000, IntrLinkChange=0x4000, - IntrRxWakeUp=0x8000, - IntrNormalSummary=0x0003, IntrAbnormalSummary=0xC260, - IntrTxDescRace=0x080000, /* mapped from IntrStatus2 */ - IntrTxErrSummary=0x082218, -}; -#define DEFAULT_INTR (IntrRxDone | IntrRxErr | IntrRxEmpty| IntrRxOverflow | \ - IntrRxDropped | IntrRxNoBuf) - -/*************************************************************************** - IRQ - PXE IRQ Handler -***************************************************************************/ -void rhine_irq ( struct nic *nic, irq_action_t action ) { - struct rhine_private *tp = (struct rhine_private *) nic->priv_data; - /* Enable interrupts by setting the interrupt mask. */ - unsigned int intr_status; - - switch ( action ) { - case DISABLE : - case ENABLE : - intr_status = inw(nic->ioaddr + IntrStatus); - /* On Rhine-II, Bit 3 indicates Tx descriptor write-back race. */ - - /* added comment by guard */ - /* For supporting VT6107, please use revision id to recognize different chips in driver */ - // if (tp->chip_id == 0x3065) - if( tp->chip_revision < 0x80 && tp->chip_revision >=0x40 ) - intr_status |= inb(nic->ioaddr + IntrStatus2) << 16; - intr_status = (intr_status & ~DEFAULT_INTR); - if ( action == ENABLE ) - intr_status = intr_status | DEFAULT_INTR; - outw(intr_status, nic->ioaddr + IntrEnable); - break; - case FORCE : - outw(0x0010, nic->ioaddr + 0x84); - break; - } -} - -static struct nic_operations rhine_operations; - -static int -rhine_probe ( struct nic *nic, struct pci_device *pci ) { - - struct rhine_private *tp = (struct rhine_private *) nic->priv_data; - - if (!pci->ioaddr) - return 0; - - rhine_probe1 (nic, pci, pci->ioaddr, pci->device, -1); - - adjust_pci_device ( pci ); - - rhine_reset (nic); - - nic->nic_op = &rhine_operations; - - nic->irqno = pci->irq; - nic->ioaddr = tp->ioaddr; - - return 1; -} - -static void set_rx_mode(struct nic *nic __unused) { - struct rhine_private *tp = (struct rhine_private *) nic->priv_data; - unsigned char rx_mode; - int ioaddr = tp->ioaddr; - - /* ! IFF_PROMISC */ - outl(0xffffffff, byMAR0); - outl(0xffffffff, byMAR4); - rx_mode = 0x0C; - - outb(0x60 /* thresh */ | rx_mode, byRCR ); -} - -static void -rhine_probe1 (struct nic *nic, struct pci_device *pci, int ioaddr, int chip_id, int options) -{ - struct rhine_private *tp; - static int did_version = 0; /* Already printed version info. */ - unsigned int i, ww; - unsigned int timeout; - int FDXFlag; - int byMIIvalue, LineSpeed, MIICRbak; - uint8_t revision_id; - unsigned char mode3_reg; - - if (rhine_debug > 0 && did_version++ == 0) - printf ("%s",version); - - // get revision id. - pci_read_config_byte(pci, PCI_REVISION, &revision_id); - - /* D-Link provided reset code (with comment additions) */ - if (revision_id >= 0x40) { - unsigned char byOrgValue; - - if(rhine_debug > 0) - printf("Enabling Sticky Bit Workaround for Chip_id: 0x%hX\n" - , chip_id); - /* clear sticky bit before reset & read ethernet address */ - byOrgValue = inb(bySTICKHW); - byOrgValue = byOrgValue & 0xFC; - outb(byOrgValue, bySTICKHW); - - /* (bits written are cleared?) */ - /* disable force PME-enable */ - outb(0x80, byWOLcgClr); - /* disable power-event config bit */ - outb(0xFF, byWOLcrClr); - /* clear power status (undocumented in vt6102 docs?) */ - outb(0xFF, byPwrcsrClr); - - } - - /* Reset the chip to erase previous misconfiguration. */ - outw(CR_SFRST, byCR0); - // if vt3043 delay after reset - if (revision_id <0x40) { - udelay(10000); - } - // polling till software reset complete - // W_MAX_TIMEOUT is the timeout period - for(ww = 0; ww < W_MAX_TIMEOUT; ww++) { - if ((inw(byCR0) & CR_SFRST) == 0) - break; - } - - // issue AUTOLoad in EECSR to reload eeprom - outb(0x20, byEECSR ); - - // if vt3065 delay after reset - if (revision_id >=0x40) { - // delay 8ms to let MAC stable - mdelay(8); - /* - * for 3065D, EEPROM reloaded will cause bit 0 in MAC_REG_CFGA - * turned on. it makes MAC receive magic packet - * automatically. So, we turn it off. (D-Link) - */ - outb(inb(byCFGA) & 0xFE, byCFGA); - } - - /* turn on bit2 in PCI configuration register 0x53 , only for 3065*/ - if (revision_id >= 0x40) { - pci_read_config_byte(pci, PCI_REG_MODE3, &mode3_reg); - pci_write_config_byte(pci, PCI_REG_MODE3, mode3_reg|MODE3_MIION); - } - - - /* back off algorithm ,disable the right-most 4-bit off CFGD*/ - outb(inb(byCFGD) & (~(CFGD_RANDOM | CFGD_CFDX | CFGD_CEREN | CFGD_CETEN)), byCFGD); - - /* reload eeprom */ - reload_eeprom(ioaddr); - - /* Perhaps this should be read from the EEPROM? */ - for (i = 0; i < ETH_ALEN; i++) - nic->node_addr[i] = inb (byPAR0 + i); - - DBG ( "IO address %#hX Ethernet Address: %s\n", ioaddr, eth_ntoa ( nic->node_addr ) ); - - /* restart MII auto-negotiation */ - WriteMII (0, 9, 1, ioaddr); - printf ("Analyzing Media type,this may take several seconds... "); - for (i = 0; i < 5; i++) - { - /* need to wait 1 millisecond - we will round it up to 50-100ms */ - timeout = currticks() + 2; - for (timeout = currticks() + 2; currticks() < timeout;) - /* nothing */; - if (ReadMII (1, ioaddr) & 0x0020) - break; - } - printf ("OK.\n"); - -#if 0 - /* JJM : for Debug */ - printf("MII : Address %hhX ",inb(ioaddr+0x6c)); - { - unsigned char st1,st2,adv1,adv2,l1,l2; - - st1=ReadMII(1,ioaddr)>>8; - st2=ReadMII(1,ioaddr)&0xFF; - adv1=ReadMII(4,ioaddr)>>8; - adv2=ReadMII(4,ioaddr)&0xFF; - l1=ReadMII(5,ioaddr)>>8; - l2=ReadMII(5,ioaddr)&0xFF; - printf(" status 0x%hhX%hhX, advertising 0x%hhX%hhX, link 0x%hhX%hhX\n", st1,st2,adv1,adv2,l1,l2); - } -#endif - - - /* query MII to know LineSpeed,duplex mode */ - byMIIvalue = inb (ioaddr + 0x6d); - LineSpeed = byMIIvalue & MIISR_SPEED; - if (LineSpeed != 0) //JJM - { - printf ("Linespeed=10Mbs"); - } - else - { - printf ("Linespeed=100Mbs"); - } - - FDXFlag = QueryAuto (ioaddr); - if (FDXFlag == 1) - { - printf (" Fullduplex\n"); - outw (CR_FDX, byCR0); - } - else - { - printf (" Halfduplex\n"); - } - - - /* set MII 10 FULL ON, only apply in vt3043 */ - if(chip_id == 0x3043) - WriteMII (0x17, 1, 1, ioaddr); - - /* turn on MII link change */ - MIICRbak = inb (byMIICR); - outb (MIICRbak & 0x7F, byMIICR); - MIIDelay (); - outb (0x41, byMIIAD); - MIIDelay (); - - /* while((inb(byMIIAD)&0x20)==0) ; */ - outb (MIICRbak | 0x80, byMIICR); - - nic->priv_data = &rhine; - tp = &rhine; - tp->chip_id = chip_id; - tp->ioaddr = ioaddr; - tp->phys[0] = -1; - tp->chip_revision = revision_id; - - /* The lower four bits are the media type. */ - if (options > 0) - { - tp->full_duplex = (options & 16) ? 1 : 0; - tp->default_port = options & 15; - if (tp->default_port) - tp->medialock = 1; - } - return; -} - -static void -rhine_disable ( struct nic *nic ) { - - struct rhine_private *tp = (struct rhine_private *) nic->priv_data; - int ioaddr = tp->ioaddr; - - rhine_reset(nic); - - printf ("rhine disable\n"); - /* Switch to loopback mode to avoid hardware races. */ - outb(0x60 | 0x01, byTCR); - /* Stop the chip's Tx and Rx processes. */ - outw(CR_STOP, byCR0); -} - -/************************************************************************** -ETH_RESET - Reset adapter -***************************************************************************/ -static void -rhine_reset (struct nic *nic) -{ - struct rhine_private *tp = (struct rhine_private *) nic->priv_data; - int ioaddr = tp->ioaddr; - int i, j; - int FDXFlag, CRbak; - void *rx_ring_tmp; - void *tx_ring_tmp; - void *rx_bufs_tmp; - void *tx_bufs_tmp; - unsigned long rx_ring_tmp1; - unsigned long tx_ring_tmp1; - unsigned long rx_bufs_tmp1; - unsigned long tx_bufs_tmp1; - - /* printf ("rhine_reset\n"); */ - /* Soft reset the chip. */ - /*outb(CmdReset, ioaddr + ChipCmd); */ - - tx_bufs_tmp = rhine_buffers.txbuf; - tx_ring_tmp = rhine_buffers.txdesc; - rx_bufs_tmp = rhine_buffers.rxbuf; - rx_ring_tmp = rhine_buffers.rxdesc; - - /* tune RD TD 32 byte alignment */ - rx_ring_tmp1 = virt_to_bus ( rx_ring_tmp ); - j = (rx_ring_tmp1 + 32) & (~0x1f); - /* printf ("txring[%d]", j); */ - tp->rx_ring = (struct rhine_rx_desc *) bus_to_virt (j); - - tx_ring_tmp1 = virt_to_bus ( tx_ring_tmp ); - j = (tx_ring_tmp1 + 32) & (~0x1f); - tp->tx_ring = (struct rhine_tx_desc *) bus_to_virt (j); - /* printf ("rxring[%X]", j); */ - - - tx_bufs_tmp1 = virt_to_bus ( tx_bufs_tmp ); - j = (int) (tx_bufs_tmp1 + 32) & (~0x1f); - tx_bufs_tmp = bus_to_virt (j); - /* printf ("txb[%X]", j); */ - - rx_bufs_tmp1 = virt_to_bus ( rx_bufs_tmp ); - j = (int) (rx_bufs_tmp1 + 32) & (~0x1f); - rx_bufs_tmp = bus_to_virt (j); - /* printf ("rxb[%X][%X]", rx_bufs_tmp1, j); */ - - for (i = 0; i < RX_RING_SIZE; i++) - { - tp->rx_buffs[i] = (char *) rx_bufs_tmp; - /* printf("r[%X]",tp->rx_buffs[i]); */ - rx_bufs_tmp += 1536; - } - - for (i = 0; i < TX_RING_SIZE; i++) - { - tp->tx_buffs[i] = (char *) tx_bufs_tmp; - /* printf("t[%X]",tp->tx_buffs[i]); */ - tx_bufs_tmp += 1536; - } - - /* software reset */ - outb (CR1_SFRST, byCR1); - MIIDelay (); - - /* printf ("init ring"); */ - rhine_init_ring (nic); - /*write TD RD Descriptor to MAC */ - outl (virt_to_bus (tp->rx_ring), dwCurrentRxDescAddr); - outl (virt_to_bus (tp->tx_ring), dwCurrentTxDescAddr); - - /* Setup Multicast */ - set_rx_mode(nic); - - /* set TCR RCR threshold to store and forward*/ - outb (0x3e, byBCR0); - outb (0x38, byBCR1); - outb (0x2c, byRCR); - outb (0x60, byTCR); - /* Set Fulldupex */ - FDXFlag = QueryAuto (ioaddr); - if (FDXFlag == 1) - { - outb (CFGD_CFDX, byCFGD); - outw (CR_FDX, byCR0); - } - - /* KICK NIC to WORK */ - CRbak = inw (byCR0); - CRbak = CRbak & 0xFFFB; /* not CR_STOP */ - outw ((CRbak | CR_STRT | CR_TXON | CR_RXON | CR_DPOLL), byCR0); - - /* disable all known interrupt */ - outw (0, byIMR0); -} -/* Beware of PCI posted writes */ -#define IOSYNC do { inb(nic->ioaddr + StationAddr); } while (0) - -static int -rhine_poll (struct nic *nic, int retrieve) -{ - struct rhine_private *tp = (struct rhine_private *) nic->priv_data; - int rxstatus, good = 0;; - - if (tp->rx_ring[tp->cur_rx].rx_status.bits.own_bit == 0) - { - unsigned int intr_status; - /* There is a packet ready */ - if(!retrieve) - return 1; - - intr_status = inw(nic->ioaddr + IntrStatus); - /* On Rhine-II, Bit 3 indicates Tx descriptor write-back race. */ -#if 0 - if (tp->chip_id == 0x3065) - intr_status |= inb(nic->ioaddr + IntrStatus2) << 16; -#endif - /* Acknowledge all of the current interrupt sources ASAP. */ - if (intr_status & IntrTxDescRace) - outb(0x08, nic->ioaddr + IntrStatus2); - outw(intr_status & 0xffff, nic->ioaddr + IntrStatus); - IOSYNC; - - rxstatus = tp->rx_ring[tp->cur_rx].rx_status.lw; - if ((rxstatus & 0x0300) != 0x0300) - { - printf("rhine_poll: bad status\n"); - } - else if (rxstatus & (RSR_ABNORMAL)) - { - printf ("rxerr[%X]\n", rxstatus); - } - else - good = 1; - - if (good) - { - nic->packetlen = tp->rx_ring[tp->cur_rx].rx_status.bits.frame_length; - memcpy (nic->packet, tp->rx_buffs[tp->cur_rx], nic->packetlen); - /* printf ("Packet RXed\n"); */ - } - tp->rx_ring[tp->cur_rx].rx_status.bits.own_bit = 1; - tp->cur_rx++; - tp->cur_rx = tp->cur_rx % RX_RING_SIZE; - } - /* Acknowledge all of the current interrupt sources ASAP. */ - outw(DEFAULT_INTR & ~IntrRxDone, nic->ioaddr + IntrStatus); - - IOSYNC; - - return good; -} - -static void -rhine_transmit (struct nic *nic, - const char *d, unsigned int t, unsigned int s, const char *p) -{ - struct rhine_private *tp = (struct rhine_private *) nic->priv_data; - int ioaddr = tp->ioaddr; - int entry; - unsigned char CR1bak; - unsigned char CR0bak; - unsigned int nstype; - unsigned long ct; - - - /*printf ("rhine_transmit\n"); */ - /* setup ethernet header */ - - - /* Calculate the next Tx descriptor entry. */ - entry = tp->cur_tx % TX_RING_SIZE; - - memcpy (tp->tx_buffs[entry], d, ETH_ALEN); /* dst */ - memcpy (tp->tx_buffs[entry] + ETH_ALEN, nic->node_addr, ETH_ALEN); /* src */ - - nstype=htons(t); - memcpy(tp->tx_buffs[entry] + 2 * ETH_ALEN, (char*)&nstype, 2); - - memcpy (tp->tx_buffs[entry] + ETH_HLEN, p, s); - s += ETH_HLEN; - while (s < ETH_ZLEN) - *((char *) tp->tx_buffs[entry] + (s++)) = 0; - - tp->tx_ring[entry].tx_ctrl.bits.tx_buf_size = s; - - tp->tx_ring[entry].tx_status.bits.own_bit = 1; - - - CR1bak = inb (byCR1); - - CR1bak = CR1bak | CR1_TDMD1; - /*printf("tdsw=[%X]",tp->tx_ring[entry].tx_status.lw); */ - /*printf("tdcw=[%X]",tp->tx_ring[entry].tx_ctrl.lw); */ - /*printf("tdbuf1=[%X]",tp->tx_ring[entry].buf_addr_1); */ - /*printf("tdbuf2=[%X]",tp->tx_ring[entry].buf_addr_2); */ - /*printf("td1=[%X]",inl(dwCurrentTDSE0)); */ - /*printf("td2=[%X]",inl(dwCurrentTDSE1)); */ - /*printf("td3=[%X]",inl(dwCurrentTDSE2)); */ - /*printf("td4=[%X]",inl(dwCurrentTDSE3)); */ - - outb (CR1bak, byCR1); - do - { - ct = currticks(); - /* Wait until transmit is finished or timeout*/ - while((tp->tx_ring[entry].tx_status.bits.own_bit !=0) && - ct + 10*1000 < currticks()) - ; - - if(tp->tx_ring[entry].tx_status.bits.terr == 0) - break; - - if(tp->tx_ring[entry].tx_status.bits.abt == 1) - { - // turn on TX - CR0bak = inb(byCR0); - CR0bak = CR0bak|CR_TXON; - outb(CR0bak,byCR0); - } - }while(0); - tp->cur_tx++; - - /*outw(IMRShadow,byIMR0); */ - /*dev_kfree_skb(tp->tx_skbuff[entry], FREE_WRITE); */ - /*tp->tx_skbuff[entry] = 0; */ -} - -static struct nic_operations rhine_operations = { - .connect = dummy_connect, - .poll = rhine_poll, - .transmit = rhine_transmit, - .irq = rhine_irq, - -}; - -static struct pci_device_id rhine_nics[] = { -PCI_ROM(0x1106, 0x3065, "dlink-530tx", "VIA 6102", 0), -PCI_ROM(0x1106, 0x3106, "via-rhine-6105", "VIA 6105", 0), -PCI_ROM(0x1106, 0x3043, "dlink-530tx-old", "VIA 3043", 0), /* Rhine-I 86c100a */ -PCI_ROM(0x1106, 0x3053, "via6105m", "VIA 6105M", 0), -PCI_ROM(0x1106, 0x6100, "via-rhine-old", "VIA 86C100A", 0), /* Rhine-II */ -}; - -PCI_DRIVER ( rhine_driver, rhine_nics, PCI_NO_CLASS ); - -DRIVER ( "VIA 86C100", nic_driver, pci_driver, rhine_driver, - rhine_probe, rhine_disable ); - -/* EOF via-rhine.c */ - -/* - * Local variables: - * c-basic-offset: 8 - * c-indent-level: 8 - * tab-width: 8 - * End: - */ diff --git a/src/drivers/net/via-velocity.c b/src/drivers/net/via-velocity.c deleted file mode 100644 index e98e81a2..00000000 --- a/src/drivers/net/via-velocity.c +++ /dev/null @@ -1,1927 +0,0 @@ -/************************************************************************** -* via-velocity.c: Etherboot device driver for the VIA 6120 Gigabit -* Changes for Etherboot port: -* Copyright (c) 2006 by Timothy Legge -* -* 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. -* -* This driver is based on: -* via-velocity.c: VIA Velocity VT6120, VT6122 Ethernet driver -* The changes are (c) Copyright 2004, Red Hat Inc. -* -* Additional fixes and clean up: Francois Romieu -* -* Original code: -* Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. -* All rights reserved. -* Author: Chuang Liang-Shing, AJ Jiang -* -* Linux Driver Version 2.6.15.4 -* -* REVISION HISTORY: -* ================ -* -* v1.0 03-06-2006 timlegge Initial port of Linux driver -* -* Indent Options: indent -kr -i8 -*************************************************************************/ - -FILE_LICENCE ( GPL2_OR_LATER ); - -#include "etherboot.h" -#include "nic.h" -#include -#include - -#include "via-velocity.h" - -typedef int pci_power_t; - -#define PCI_D0 ((int) 0) -#define PCI_D1 ((int) 1) -#define PCI_D2 ((int) 2) -#define PCI_D3hot ((int) 3) -#define PCI_D3cold ((int) 4) -#define PCI_POWER_ERROR ((int) -1) - - -/* Condensed operations for readability. */ -#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr)) -#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr)) - -//FIXME: Move to pci.c -int pci_set_power_state(struct pci_device *dev, int state); - -/* FIXME: Move BASE to the private structure */ -static u32 BASE; - -/* NIC specific static variables go here */ -#define VELOCITY_PARAM(N,D) \ - static const int N[MAX_UNITS]=OPTION_DEFAULT; -/* MODULE_PARM(N, "1-" __MODULE_STRING(MAX_UNITS) "i");\ - MODULE_PARM_DESC(N, D); */ - -VELOCITY_PARAM(RxDescriptors, "Number of receive descriptors"); -VELOCITY_PARAM(TxDescriptors, "Number of transmit descriptors"); - - -#define VLAN_ID_MIN 0 -#define VLAN_ID_MAX 4095 -#define VLAN_ID_DEF 0 -/* VID_setting[] is used for setting the VID of NIC. - 0: default VID. - 1-4094: other VIDs. -*/ -VELOCITY_PARAM(VID_setting, "802.1Q VLAN ID"); - -#define RX_THRESH_MIN 0 -#define RX_THRESH_MAX 3 -#define RX_THRESH_DEF 0 -/* rx_thresh[] is used for controlling the receive fifo threshold. - 0: indicate the rxfifo threshold is 128 bytes. - 1: indicate the rxfifo threshold is 512 bytes. - 2: indicate the rxfifo threshold is 1024 bytes. - 3: indicate the rxfifo threshold is store & forward. -*/ -VELOCITY_PARAM(rx_thresh, "Receive fifo threshold"); - -#define DMA_LENGTH_MIN 0 -#define DMA_LENGTH_MAX 7 -#define DMA_LENGTH_DEF 0 - -/* DMA_length[] is used for controlling the DMA length - 0: 8 DWORDs - 1: 16 DWORDs - 2: 32 DWORDs - 3: 64 DWORDs - 4: 128 DWORDs - 5: 256 DWORDs - 6: SF(flush till emply) - 7: SF(flush till emply) -*/ -VELOCITY_PARAM(DMA_length, "DMA length"); - -#define TAGGING_DEF 0 -/* enable_tagging[] is used for enabling 802.1Q VID tagging. - 0: disable VID seeting(default). - 1: enable VID setting. -*/ -VELOCITY_PARAM(enable_tagging, "Enable 802.1Q tagging"); - -#define IP_ALIG_DEF 0 -/* IP_byte_align[] is used for IP header DWORD byte aligned - 0: indicate the IP header won't be DWORD byte aligned.(Default) . - 1: indicate the IP header will be DWORD byte aligned. - In some environment, the IP header should be DWORD byte aligned, - or the packet will be droped when we receive it. (eg: IPVS) -*/ -VELOCITY_PARAM(IP_byte_align, "Enable IP header dword aligned"); - -#define TX_CSUM_DEF 1 -/* txcsum_offload[] is used for setting the checksum offload ability of NIC. - (We only support RX checksum offload now) - 0: disable csum_offload[checksum offload - 1: enable checksum offload. (Default) -*/ -VELOCITY_PARAM(txcsum_offload, "Enable transmit packet checksum offload"); - -#define FLOW_CNTL_DEF 1 -#define FLOW_CNTL_MIN 1 -#define FLOW_CNTL_MAX 5 - -/* flow_control[] is used for setting the flow control ability of NIC. - 1: hardware deafult - AUTO (default). Use Hardware default value in ANAR. - 2: enable TX flow control. - 3: enable RX flow control. - 4: enable RX/TX flow control. - 5: disable -*/ -VELOCITY_PARAM(flow_control, "Enable flow control ability"); - -#define MED_LNK_DEF 0 -#define MED_LNK_MIN 0 -#define MED_LNK_MAX 4 -/* speed_duplex[] is used for setting the speed and duplex mode of NIC. - 0: indicate autonegotiation for both speed and duplex mode - 1: indicate 100Mbps half duplex mode - 2: indicate 100Mbps full duplex mode - 3: indicate 10Mbps half duplex mode - 4: indicate 10Mbps full duplex mode - - Note: - if EEPROM have been set to the force mode, this option is ignored - by driver. -*/ -VELOCITY_PARAM(speed_duplex, "Setting the speed and duplex mode"); - -#define VAL_PKT_LEN_DEF 0 -/* ValPktLen[] is used for setting the checksum offload ability of NIC. - 0: Receive frame with invalid layer 2 length (Default) - 1: Drop frame with invalid layer 2 length -*/ -VELOCITY_PARAM(ValPktLen, "Receiving or Drop invalid 802.3 frame"); - -#define WOL_OPT_DEF 0 -#define WOL_OPT_MIN 0 -#define WOL_OPT_MAX 7 -/* wol_opts[] is used for controlling wake on lan behavior. - 0: Wake up if recevied a magic packet. (Default) - 1: Wake up if link status is on/off. - 2: Wake up if recevied an arp packet. - 4: Wake up if recevied any unicast packet. - Those value can be sumed up to support more than one option. -*/ -VELOCITY_PARAM(wol_opts, "Wake On Lan options"); - -#define INT_WORKS_DEF 20 -#define INT_WORKS_MIN 10 -#define INT_WORKS_MAX 64 - -VELOCITY_PARAM(int_works, "Number of packets per interrupt services"); - -/* The descriptors for this card are required to be aligned on -64 byte boundaries. As the align attribute does not guarantee alignment -greater than the alignment of the start address (which for Etherboot -is 16 bytes of alignment) it requires some extra steps. Add 64 to the -size of the array and the init_ring adjusts the alignment */ - -/* Define the TX Descriptor */ -static u8 tx_ring[TX_DESC_DEF * sizeof(struct tx_desc) + 64]; - -/* Create a static buffer of size PKT_BUF_SZ for each TX Descriptor. -All descriptors point to a part of this buffer */ -static u8 txb[(TX_DESC_DEF * PKT_BUF_SZ) + 64]; - -/* Define the RX Descriptor */ -static u8 rx_ring[RX_DESC_DEF * sizeof(struct rx_desc) + 64]; - -/* Create a static buffer of size PKT_BUF_SZ for each RX Descriptor - All descriptors point to a part of this buffer */ -static u8 rxb[(RX_DESC_DEF * PKT_BUF_SZ) + 64]; - -static void velocity_init_info(struct pci_device *pdev, - struct velocity_info *vptr, - struct velocity_info_tbl *info); -static int velocity_get_pci_info(struct velocity_info *, - struct pci_device *pdev); -static int velocity_open(struct nic *nic, struct pci_device *pci); - -static int velocity_soft_reset(struct velocity_info *vptr); -static void velocity_init_cam_filter(struct velocity_info *vptr); -static void mii_init(struct velocity_info *vptr, u32 mii_status); -static u32 velocity_get_opt_media_mode(struct velocity_info *vptr); -static void velocity_print_link_status(struct velocity_info *vptr); -static void safe_disable_mii_autopoll(struct mac_regs *regs); -static void enable_flow_control_ability(struct velocity_info *vptr); -static void enable_mii_autopoll(struct mac_regs *regs); -static int velocity_mii_read(struct mac_regs *, u8 byIdx, u16 * pdata); -static int velocity_mii_write(struct mac_regs *, u8 byMiiAddr, u16 data); -static u32 mii_check_media_mode(struct mac_regs *regs); -static u32 check_connection_type(struct mac_regs *regs); -static int velocity_set_media_mode(struct velocity_info *vptr, - u32 mii_status); - - -/* - * Internal board variants. At the moment we have only one - */ - -static struct velocity_info_tbl chip_info_table[] = { - {CHIP_TYPE_VT6110, - "VIA Networking Velocity Family Gigabit Ethernet Adapter", 256, 1, - 0x00FFFFFFUL}, - {0, NULL, 0, 0, 0} -}; - -/** - * velocity_set_int_opt - parser for integer options - * @opt: pointer to option value - * @val: value the user requested (or -1 for default) - * @min: lowest value allowed - * @max: highest value allowed - * @def: default value - * @name: property name - * @dev: device name - * - * Set an integer property in the module options. This function does - * all the verification and checking as well as reporting so that - * we don't duplicate code for each option. - */ - -static void velocity_set_int_opt(int *opt, int val, int min, int max, - int def, char *name, const char *devname) -{ - if (val == -1) { - printf("%s: set value of parameter %s to %d\n", - devname, name, def); - *opt = def; - } else if (val < min || val > max) { - printf - ("%s: the value of parameter %s is invalid, the valid range is (%d-%d)\n", - devname, name, min, max); - *opt = def; - } else { - printf("%s: set value of parameter %s to %d\n", - devname, name, val); - *opt = val; - } -} - -/** - * velocity_set_bool_opt - parser for boolean options - * @opt: pointer to option value - * @val: value the user requested (or -1 for default) - * @def: default value (yes/no) - * @flag: numeric value to set for true. - * @name: property name - * @dev: device name - * - * Set a boolean property in the module options. This function does - * all the verification and checking as well as reporting so that - * we don't duplicate code for each option. - */ - -static void velocity_set_bool_opt(u32 * opt, int val, int def, u32 flag, - char *name, const char *devname) -{ - (*opt) &= (~flag); - if (val == -1) { - printf("%s: set parameter %s to %s\n", - devname, name, def ? "TRUE" : "FALSE"); - *opt |= (def ? flag : 0); - } else if (val < 0 || val > 1) { - printf - ("%s: the value of parameter %s is invalid, the valid range is (0-1)\n", - devname, name); - *opt |= (def ? flag : 0); - } else { - printf("%s: set parameter %s to %s\n", - devname, name, val ? "TRUE" : "FALSE"); - *opt |= (val ? flag : 0); - } -} - -/** - * velocity_get_options - set options on device - * @opts: option structure for the device - * @index: index of option to use in module options array - * @devname: device name - * - * Turn the module and command options into a single structure - * for the current device - */ - -static void velocity_get_options(struct velocity_opt *opts, int index, - const char *devname) -{ - - /* FIXME Do the options need to be configurable */ - velocity_set_int_opt(&opts->rx_thresh, -1, RX_THRESH_MIN, - RX_THRESH_MAX, RX_THRESH_DEF, "rx_thresh", - devname); - velocity_set_int_opt(&opts->DMA_length, DMA_length[index], - DMA_LENGTH_MIN, DMA_LENGTH_MAX, - DMA_LENGTH_DEF, "DMA_length", devname); - velocity_set_int_opt(&opts->numrx, RxDescriptors[index], - RX_DESC_MIN, RX_DESC_MAX, RX_DESC_DEF, - "RxDescriptors", devname); - velocity_set_int_opt(&opts->numtx, TxDescriptors[index], - TX_DESC_MIN, TX_DESC_MAX, TX_DESC_DEF, - "TxDescriptors", devname); - velocity_set_int_opt(&opts->vid, VID_setting[index], VLAN_ID_MIN, - VLAN_ID_MAX, VLAN_ID_DEF, "VID_setting", - devname); - velocity_set_bool_opt(&opts->flags, enable_tagging[index], - TAGGING_DEF, VELOCITY_FLAGS_TAGGING, - "enable_tagging", devname); - velocity_set_bool_opt(&opts->flags, txcsum_offload[index], - TX_CSUM_DEF, VELOCITY_FLAGS_TX_CSUM, - "txcsum_offload", devname); - velocity_set_int_opt(&opts->flow_cntl, flow_control[index], - FLOW_CNTL_MIN, FLOW_CNTL_MAX, FLOW_CNTL_DEF, - "flow_control", devname); - velocity_set_bool_opt(&opts->flags, IP_byte_align[index], - IP_ALIG_DEF, VELOCITY_FLAGS_IP_ALIGN, - "IP_byte_align", devname); - velocity_set_bool_opt(&opts->flags, ValPktLen[index], - VAL_PKT_LEN_DEF, VELOCITY_FLAGS_VAL_PKT_LEN, - "ValPktLen", devname); - velocity_set_int_opt((void *) &opts->spd_dpx, speed_duplex[index], - MED_LNK_MIN, MED_LNK_MAX, MED_LNK_DEF, - "Media link mode", devname); - velocity_set_int_opt((int *) &opts->wol_opts, wol_opts[index], - WOL_OPT_MIN, WOL_OPT_MAX, WOL_OPT_DEF, - "Wake On Lan options", devname); - velocity_set_int_opt((int *) &opts->int_works, int_works[index], - INT_WORKS_MIN, INT_WORKS_MAX, INT_WORKS_DEF, - "Interrupt service works", devname); - opts->numrx = (opts->numrx & ~3); -} - -/** - * velocity_init_cam_filter - initialise CAM - * @vptr: velocity to program - * - * Initialize the content addressable memory used for filters. Load - * appropriately according to the presence of VLAN - */ - -static void velocity_init_cam_filter(struct velocity_info *vptr) -{ - struct mac_regs *regs = vptr->mac_regs; - - /* Turn on MCFG_PQEN, turn off MCFG_RTGOPT */ - WORD_REG_BITS_SET(MCFG_PQEN, MCFG_RTGOPT, ®s->MCFG); - WORD_REG_BITS_ON(MCFG_VIDFR, ®s->MCFG); - - /* Disable all CAMs */ - memset(vptr->vCAMmask, 0, sizeof(u8) * 8); - memset(vptr->mCAMmask, 0, sizeof(u8) * 8); - mac_set_cam_mask(regs, vptr->vCAMmask, VELOCITY_VLAN_ID_CAM); - mac_set_cam_mask(regs, vptr->mCAMmask, VELOCITY_MULTICAST_CAM); - - /* Enable first VCAM */ - if (vptr->flags & VELOCITY_FLAGS_TAGGING) { - /* If Tagging option is enabled and VLAN ID is not zero, then - turn on MCFG_RTGOPT also */ - if (vptr->options.vid != 0) - WORD_REG_BITS_ON(MCFG_RTGOPT, ®s->MCFG); - - mac_set_cam(regs, 0, (u8 *) & (vptr->options.vid), - VELOCITY_VLAN_ID_CAM); - vptr->vCAMmask[0] |= 1; - mac_set_cam_mask(regs, vptr->vCAMmask, - VELOCITY_VLAN_ID_CAM); - } else { - u16 temp = 0; - mac_set_cam(regs, 0, (u8 *) & temp, VELOCITY_VLAN_ID_CAM); - temp = 1; - mac_set_cam_mask(regs, (u8 *) & temp, - VELOCITY_VLAN_ID_CAM); - } -} - -static inline void velocity_give_many_rx_descs(struct velocity_info *vptr) -{ - struct mac_regs *regs = vptr->mac_regs; - int avail, dirty, unusable; - - /* - * RD number must be equal to 4X per hardware spec - * (programming guide rev 1.20, p.13) - */ - if (vptr->rd_filled < 4) - return; - - wmb(); - - unusable = vptr->rd_filled & 0x0003; - dirty = vptr->rd_dirty - unusable; - for (avail = vptr->rd_filled & 0xfffc; avail; avail--) { - dirty = (dirty > 0) ? dirty - 1 : vptr->options.numrx - 1; -// printf("return dirty: %d\n", dirty); - vptr->rd_ring[dirty].rdesc0.owner = OWNED_BY_NIC; - } - - writew(vptr->rd_filled & 0xfffc, ®s->RBRDU); - vptr->rd_filled = unusable; -} - -static int velocity_rx_refill(struct velocity_info *vptr) -{ - int dirty = vptr->rd_dirty, done = 0, ret = 0; - -// printf("rx_refill - rd_curr = %d, dirty = %d\n", vptr->rd_curr, dirty); - do { - struct rx_desc *rd = vptr->rd_ring + dirty; - - /* Fine for an all zero Rx desc at init time as well */ - if (rd->rdesc0.owner == OWNED_BY_NIC) - break; -// printf("rx_refill - after owner %d\n", dirty); - - rd->inten = 1; - rd->pa_high = 0; - rd->rdesc0.len = cpu_to_le32(vptr->rx_buf_sz);; - - done++; - dirty = (dirty < vptr->options.numrx - 1) ? dirty + 1 : 0; - } while (dirty != vptr->rd_curr); - - if (done) { -// printf("\nGive Back Desc\n"); - vptr->rd_dirty = dirty; - vptr->rd_filled += done; - velocity_give_many_rx_descs(vptr); - } - - return ret; -} - -extern void hex_dump(const char *data, const unsigned int len); -/************************************************************************** -POLL - Wait for a frame -***************************************************************************/ -static int velocity_poll(struct nic *nic, int retrieve) -{ - /* Work out whether or not there's an ethernet packet ready to - * read. Return 0 if not. - */ - - int rd_curr = vptr->rd_curr % RX_DESC_DEF; - struct rx_desc *rd = &(vptr->rd_ring[rd_curr]); - - if (rd->rdesc0.owner == OWNED_BY_NIC) - return 0; - rmb(); - - if ( ! retrieve ) return 1; - - /* - * Don't drop CE or RL error frame although RXOK is off - */ - if ((rd->rdesc0.RSR & RSR_RXOK) - || (!(rd->rdesc0.RSR & RSR_RXOK) - && (rd->rdesc0.RSR & (RSR_CE | RSR_RL)))) { - - nic->packetlen = rd->rdesc0.len; - // ptr->rxb + (rd_curr * PKT_BUF_SZ) - memcpy(nic->packet, bus_to_virt(rd->pa_low), - nic->packetlen - 4); - - vptr->rd_curr++; - vptr->rd_curr = vptr->rd_curr % RX_DESC_DEF; - velocity_rx_refill(vptr); - return 1; /* Remove this line once this method is implemented */ - } - return 0; -} - -#define TX_TIMEOUT (1000); -/************************************************************************** -TRANSMIT - Transmit a frame -***************************************************************************/ -static void velocity_transmit(struct nic *nic, const char *dest, /* Destination */ - unsigned int type, /* Type */ - unsigned int size, /* size */ - const char *packet) -{ /* Packet */ - u16 nstype; - u32 to; - u8 *ptxb; - unsigned int pktlen; - struct tx_desc *td_ptr; - - int entry = vptr->td_curr % TX_DESC_DEF; - td_ptr = &(vptr->td_rings[entry]); - - /* point to the current txb incase multiple tx_rings are used */ - ptxb = vptr->txb + (entry * PKT_BUF_SZ); - memcpy(ptxb, dest, ETH_ALEN); /* Destination */ - memcpy(ptxb + ETH_ALEN, nic->node_addr, ETH_ALEN); /* Source */ - nstype = htons((u16) type); /* Type */ - memcpy(ptxb + 2 * ETH_ALEN, (u8 *) & nstype, 2); /* Type */ - memcpy(ptxb + ETH_HLEN, packet, size); - - td_ptr->tdesc1.TCPLS = TCPLS_NORMAL; - td_ptr->tdesc1.TCR = TCR0_TIC; - td_ptr->td_buf[0].queue = 0; - - size += ETH_HLEN; - while (size < ETH_ZLEN) /* pad to min length */ - ptxb[size++] = '\0'; - - if (size < ETH_ZLEN) { -// printf("Padd that packet\n"); - pktlen = ETH_ZLEN; -// memcpy(ptxb, skb->data, skb->len); - memset(ptxb + size, 0, ETH_ZLEN - size); - - vptr->td_rings[entry].tdesc0.pktsize = pktlen; - vptr->td_rings[entry].td_buf[0].pa_low = virt_to_bus(ptxb); - vptr->td_rings[entry].td_buf[0].pa_high &= - cpu_to_le32(0xffff0000UL); - vptr->td_rings[entry].td_buf[0].bufsize = - vptr->td_rings[entry].tdesc0.pktsize; - vptr->td_rings[entry].tdesc1.CMDZ = 2; - } else { -// printf("Correct size packet\n"); - td_ptr->tdesc0.pktsize = size; - td_ptr->td_buf[0].pa_low = virt_to_bus(ptxb); - td_ptr->td_buf[0].pa_high = 0; - td_ptr->td_buf[0].bufsize = td_ptr->tdesc0.pktsize; -// tdinfo->nskb_dma = 1; - td_ptr->tdesc1.CMDZ = 2; - } - - if (vptr->flags & VELOCITY_FLAGS_TAGGING) { - td_ptr->tdesc1.pqinf.VID = (vptr->options.vid & 0xfff); - td_ptr->tdesc1.pqinf.priority = 0; - td_ptr->tdesc1.pqinf.CFI = 0; - td_ptr->tdesc1.TCR |= TCR0_VETAG; - } - - vptr->td_curr = (entry + 1); - - { - - int prev = entry - 1; - - if (prev < 0) - prev = TX_DESC_DEF - 1; - td_ptr->tdesc0.owner |= OWNED_BY_NIC; - td_ptr = &(vptr->td_rings[prev]); - td_ptr->td_buf[0].queue = 1; - mac_tx_queue_wake(vptr->mac_regs, 0); - - } - - to = currticks() + TX_TIMEOUT; - while ((td_ptr->tdesc0.owner & OWNED_BY_NIC) && (currticks() < to)); /* wait */ - - if (currticks() >= to) { - printf("TX Time Out"); - } - -} - -/************************************************************************** -DISABLE - Turn off ethernet interface -***************************************************************************/ -static void velocity_disable(struct nic *nic __unused) -{ - /* put the card in its initial state */ - /* This function serves 3 purposes. - * This disables DMA and interrupts so we don't receive - * unexpected packets or interrupts from the card after - * etherboot has finished. - * This frees resources so etherboot may use - * this driver on another interface - * This allows etherboot to reinitialize the interface - * if something is something goes wrong. - */ - struct mac_regs *regs = vptr->mac_regs; - mac_disable_int(regs); - writel(CR0_STOP, ®s->CR0Set); - writew(0xFFFF, ®s->TDCSRClr); - writeb(0xFF, ®s->RDCSRClr); - safe_disable_mii_autopoll(regs); - mac_clear_isr(regs); - - /* Power down the chip */ -// pci_set_power_state(vptr->pdev, PCI_D3hot); - - vptr->flags &= (~VELOCITY_FLAGS_OPENED); -} - -/************************************************************************** -IRQ - handle interrupts -***************************************************************************/ -static void velocity_irq(struct nic *nic __unused, irq_action_t action) -{ - /* This routine is somewhat optional. Etherboot itself - * doesn't use interrupts, but they are required under some - * circumstances when we're acting as a PXE stack. - * - * If you don't implement this routine, the only effect will - * be that your driver cannot be used via Etherboot's UNDI - * API. This won't affect programs that use only the UDP - * portion of the PXE API, such as pxelinux. - */ - - switch (action) { - case DISABLE: - case ENABLE: - /* Set receive interrupt enabled/disabled state */ - /* - outb ( action == ENABLE ? IntrMaskEnabled : IntrMaskDisabled, - nic->ioaddr + IntrMaskRegister ); - */ - break; - case FORCE: - /* Force NIC to generate a receive interrupt */ - /* - outb ( ForceInterrupt, nic->ioaddr + IntrForceRegister ); - */ - break; - } -} - -static struct nic_operations velocity_operations = { - .connect = dummy_connect, - .poll = velocity_poll, - .transmit = velocity_transmit, - .irq = velocity_irq, -}; - -/************************************************************************** -PROBE - Look for an adapter, this routine's visible to the outside -***************************************************************************/ -static int velocity_probe( struct nic *nic, struct pci_device *pci) -{ - int ret, i; - struct mac_regs *regs; - - printf("via-velocity.c: Found %s Vendor=0x%hX Device=0x%hX\n", - pci->id->name, pci->vendor, pci->device); - - /* point to private storage */ - vptr = &vptx; - info = chip_info_table; - - velocity_init_info(pci, vptr, info); - -//FIXME: pci_enable_device(pci); -//FIXME: pci_set_power_state(pci, PCI_D0); - - ret = velocity_get_pci_info(vptr, pci); - if (ret < 0) { - printf("Failed to find PCI device.\n"); - return 0; - } - - regs = ioremap(vptr->memaddr, vptr->io_size); - if (regs == NULL) { - printf("Unable to remap io\n"); - return 0; - } - - vptr->mac_regs = regs; - - BASE = vptr->ioaddr; - - printf("Chip ID: %hX\n", vptr->chip_id); - - for (i = 0; i < 6; i++) - nic->node_addr[i] = readb(®s->PAR[i]); - - DBG ( "%s: %s at ioaddr %#hX\n", pci->id->name, eth_ntoa ( nic->node_addr ), - (unsigned int) BASE ); - - velocity_get_options(&vptr->options, 0, pci->id->name); - - /* - * Mask out the options cannot be set to the chip - */ - vptr->options.flags &= 0x00FFFFFFUL; //info->flags = 0x00FFFFFFUL; - - /* - * Enable the chip specified capbilities - */ - - vptr->flags = - vptr->options. - flags | (0x00FFFFFFUL /*info->flags */ & 0xFF000000UL); - - vptr->wol_opts = vptr->options.wol_opts; - vptr->flags |= VELOCITY_FLAGS_WOL_ENABLED; - - vptr->phy_id = MII_GET_PHY_ID(vptr->mac_regs); - - if (vptr->flags & VELOCITY_FLAGS_TX_CSUM) { - printf("features missing\n"); - } - - /* and leave the chip powered down */ -// FIXME: pci_set_power_state(pci, PCI_D3hot); - - check_connection_type(vptr->mac_regs); - velocity_open(nic, pci); - - /* store NIC parameters */ - nic->nic_op = &velocity_operations; - return 1; -} - -//#define IORESOURCE_IO 0x00000100 /* Resource type */ - -/** - * velocity_init_info - init private data - * @pdev: PCI device - * @vptr: Velocity info - * @info: Board type - * - * Set up the initial velocity_info struct for the device that has been - * discovered. - */ - -static void velocity_init_info(struct pci_device *pdev, - struct velocity_info *vptr, - struct velocity_info_tbl *info) -{ - memset(vptr, 0, sizeof(struct velocity_info)); - - vptr->pdev = pdev; - vptr->chip_id = info->chip_id; - vptr->io_size = info->io_size; - vptr->num_txq = info->txqueue; - vptr->multicast_limit = MCAM_SIZE; - - printf - ("chip_id: 0x%hX, io_size: %d, num_txq %d, multicast_limit: %d\n", - vptr->chip_id, (unsigned int) vptr->io_size, vptr->num_txq, - vptr->multicast_limit); - printf("Name: %s\n", info->name); - -// spin_lock_init(&vptr->lock); -// INIT_LIST_HEAD(&vptr->list); -} - -/** - * velocity_get_pci_info - retrieve PCI info for device - * @vptr: velocity device - * @pdev: PCI device it matches - * - * Retrieve the PCI configuration space data that interests us from - * the kernel PCI layer - */ - -#define IORESOURCE_IO 0x00000100 /* Resource type */ -#define IORESOURCE_PREFETCH 0x00001000 /* No side effects */ - -#define IORESOURCE_MEM 0x00000200 -#define BAR_0 0 -#define BAR_1 1 -#define BAR_5 5 -#define PCI_BASE_ADDRESS_SPACE 0x01 /* 0 = memory, 1 = I/O */ -#define PCI_BASE_ADDRESS_SPACE_IO 0x01 -#define PCI_BASE_ADDRESS_SPACE_MEMORY 0x00 -#define PCI_BASE_ADDRESS_MEM_TYPE_MASK 0x06 -#define PCI_BASE_ADDRESS_MEM_TYPE_32 0x00 /* 32 bit address */ -#define PCI_BASE_ADDRESS_MEM_TYPE_1M 0x02 /* Below 1M [obsolete] */ -#define PCI_BASE_ADDRESS_MEM_TYPE_64 0x04 /* 64 bit address */ -#define PCI_BASE_ADDRESS_MEM_PREFETCH 0x08 /* prefetchable? */ -//#define PCI_BASE_ADDRESS_MEM_MASK (~0x0fUL) -// #define PCI_BASE_ADDRESS_IO_MASK (~0x03UL) - -unsigned long pci_resource_flags(struct pci_device *pdev, unsigned int bar) -{ - uint32_t l, sz; - unsigned long flags = 0; - - pci_read_config_dword(pdev, bar, &l); - pci_write_config_dword(pdev, bar, ~0); - pci_read_config_dword(pdev, bar, &sz); - pci_write_config_dword(pdev, bar, l); - - if (!sz || sz == 0xffffffff) - printf("Weird size\n"); - if (l == 0xffffffff) - l = 0; - if ((l & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY) { - /* sz = pci_size(l, sz, PCI_BASE_ADDRESS_MEM_MASK); - if (!sz) - continue; - res->start = l & PCI_BASE_ADDRESS_MEM_MASK; - */ flags |= l & ~PCI_BASE_ADDRESS_MEM_MASK; - printf("Memory Resource\n"); - } else { - // sz = pci_size(l, sz, PCI_BASE_ADDRESS_IO_MASK & 0xffff); - /// if (!sz) - /// continue; -// res->start = l & PCI_BASE_ADDRESS_IO_MASK; - flags |= l & ~PCI_BASE_ADDRESS_IO_MASK; - printf("I/O Resource\n"); - } - if (flags & PCI_BASE_ADDRESS_SPACE_IO) { - printf("Why is it here\n"); - flags |= IORESOURCE_IO; - } else { - printf("here\n"); -//flags &= ~IORESOURCE_IO; - } - - - if (flags & PCI_BASE_ADDRESS_MEM_PREFETCH) - flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH; - - - return flags; -} -static int velocity_get_pci_info(struct velocity_info *vptr, - struct pci_device *pdev) -{ - if (pci_read_config_byte(pdev, PCI_REVISION_ID, &vptr->rev_id) < 0) { - printf("DEBUG: pci_read_config_byte failed\n"); - return -1; - } - - adjust_pci_device(pdev); - - vptr->ioaddr = pci_bar_start(pdev, PCI_BASE_ADDRESS_0); - vptr->memaddr = pci_bar_start(pdev, PCI_BASE_ADDRESS_1); - - printf("Looking for I/O Resource - Found:"); - if (! - (pci_resource_flags(pdev, PCI_BASE_ADDRESS_0) & IORESOURCE_IO)) - { - printf - ("DEBUG: region #0 is not an I/O resource, aborting.\n"); - return -1; - } - - printf("Looking for Memory Resource - Found:"); - if ((pci_resource_flags(pdev, PCI_BASE_ADDRESS_1) & IORESOURCE_IO)) { - printf("DEBUG: region #1 is an I/O resource, aborting.\n"); - return -1; - } - - if (pci_bar_size(pdev, PCI_BASE_ADDRESS_1) < 256) { - printf("DEBUG: region #1 is too small.\n"); - return -1; - } - vptr->pdev = pdev; - - return 0; -} - -/** - * velocity_print_link_status - link status reporting - * @vptr: velocity to report on - * - * Turn the link status of the velocity card into a kernel log - * description of the new link state, detailing speed and duplex - * status - */ - -static void velocity_print_link_status(struct velocity_info *vptr) -{ - - if (vptr->mii_status & VELOCITY_LINK_FAIL) { - printf("failed to detect cable link\n"); - } else if (vptr->options.spd_dpx == SPD_DPX_AUTO) { - printf("Link autonegation"); - - if (vptr->mii_status & VELOCITY_SPEED_1000) - printf(" speed 1000M bps"); - else if (vptr->mii_status & VELOCITY_SPEED_100) - printf(" speed 100M bps"); - else - printf(" speed 10M bps"); - - if (vptr->mii_status & VELOCITY_DUPLEX_FULL) - printf(" full duplex\n"); - else - printf(" half duplex\n"); - } else { - printf("Link forced"); - switch (vptr->options.spd_dpx) { - case SPD_DPX_100_HALF: - printf(" speed 100M bps half duplex\n"); - break; - case SPD_DPX_100_FULL: - printf(" speed 100M bps full duplex\n"); - break; - case SPD_DPX_10_HALF: - printf(" speed 10M bps half duplex\n"); - break; - case SPD_DPX_10_FULL: - printf(" speed 10M bps full duplex\n"); - break; - default: - break; - } - } -} - -/** - * velocity_rx_reset - handle a receive reset - * @vptr: velocity we are resetting - * - * Reset the ownership and status for the receive ring side. - * Hand all the receive queue to the NIC. - */ - -static void velocity_rx_reset(struct velocity_info *vptr) -{ - - struct mac_regs *regs = vptr->mac_regs; - int i; - -//ptr->rd_dirty = vptr->rd_filled = vptr->rd_curr = 0; - - /* - * Init state, all RD entries belong to the NIC - */ - for (i = 0; i < vptr->options.numrx; ++i) - vptr->rd_ring[i].rdesc0.owner = OWNED_BY_NIC; - - writew(RX_DESC_DEF, ®s->RBRDU); - writel(virt_to_le32desc(vptr->rd_ring), ®s->RDBaseLo); - writew(0, ®s->RDIdx); - writew(RX_DESC_DEF - 1, ®s->RDCSize); -} - -/** - * velocity_init_registers - initialise MAC registers - * @vptr: velocity to init - * @type: type of initialisation (hot or cold) - * - * Initialise the MAC on a reset or on first set up on the - * hardware. - */ - -static void velocity_init_registers(struct nic *nic, - struct velocity_info *vptr, - enum velocity_init_type type) -{ - struct mac_regs *regs = vptr->mac_regs; - int i, mii_status; - - mac_wol_reset(regs); - - switch (type) { - case VELOCITY_INIT_RESET: - case VELOCITY_INIT_WOL: - -//netif_stop_queue(vptr->dev); - - /* - * Reset RX to prevent RX pointer not on the 4X location - */ - velocity_rx_reset(vptr); - mac_rx_queue_run(regs); - mac_rx_queue_wake(regs); - - mii_status = velocity_get_opt_media_mode(vptr); - - if (velocity_set_media_mode(vptr, mii_status) != - VELOCITY_LINK_CHANGE) { - velocity_print_link_status(vptr); - if (!(vptr->mii_status & VELOCITY_LINK_FAIL)) - printf("Link Failed\n"); -// netif_wake_queue(vptr->dev); - } - - enable_flow_control_ability(vptr); - - mac_clear_isr(regs); - writel(CR0_STOP, ®s->CR0Clr); - //writel((CR0_DPOLL | CR0_TXON | CR0_RXON | CR0_STRT), - writel((CR0_DPOLL | CR0_TXON | CR0_RXON | CR0_STRT), - ®s->CR0Set); - break; - - case VELOCITY_INIT_COLD: - default: - /* - * Do reset - */ - velocity_soft_reset(vptr); - mdelay(5); - - mac_eeprom_reload(regs); - for (i = 0; i < 6; i++) { - writeb(nic->node_addr[i], &(regs->PAR[i])); - } - /* - * clear Pre_ACPI bit. - */ - BYTE_REG_BITS_OFF(CFGA_PACPI, &(regs->CFGA)); - mac_set_rx_thresh(regs, vptr->options.rx_thresh); - mac_set_dma_length(regs, vptr->options.DMA_length); - - writeb(WOLCFG_SAM | WOLCFG_SAB, ®s->WOLCFGSet); - /* - * Back off algorithm use original IEEE standard - */ - BYTE_REG_BITS_SET(CFGB_OFSET, - (CFGB_CRANDOM | CFGB_CAP | CFGB_MBA | - CFGB_BAKOPT), ®s->CFGB); - - /* - * Init CAM filter - */ - velocity_init_cam_filter(vptr); - - /* - * Set packet filter: Receive directed and broadcast address - */ -//FIXME Multicast velocity_set_multi(nic); - - /* - * Enable MII auto-polling - */ - enable_mii_autopoll(regs); - - vptr->int_mask = INT_MASK_DEF; - - writel(virt_to_le32desc(vptr->rd_ring), ®s->RDBaseLo); - writew(vptr->options.numrx - 1, ®s->RDCSize); - mac_rx_queue_run(regs); - mac_rx_queue_wake(regs); - - writew(vptr->options.numtx - 1, ®s->TDCSize); - -// for (i = 0; i < vptr->num_txq; i++) { - writel(virt_to_le32desc(vptr->td_rings), - &(regs->TDBaseLo[0])); - mac_tx_queue_run(regs, 0); -// } - - init_flow_control_register(vptr); - - writel(CR0_STOP, ®s->CR0Clr); - writel((CR0_DPOLL | CR0_TXON | CR0_RXON | CR0_STRT), - ®s->CR0Set); - - mii_status = velocity_get_opt_media_mode(vptr); -// netif_stop_queue(vptr->dev); - - mii_init(vptr, mii_status); - - if (velocity_set_media_mode(vptr, mii_status) != - VELOCITY_LINK_CHANGE) { - velocity_print_link_status(vptr); - if (!(vptr->mii_status & VELOCITY_LINK_FAIL)) - printf("Link Faaailll\n"); -// netif_wake_queue(vptr->dev); - } - - enable_flow_control_ability(vptr); - mac_hw_mibs_init(regs); - mac_write_int_mask(vptr->int_mask, regs); - mac_clear_isr(regs); - - - } - velocity_print_link_status(vptr); -} - -/** - * velocity_soft_reset - soft reset - * @vptr: velocity to reset - * - * Kick off a soft reset of the velocity adapter and then poll - * until the reset sequence has completed before returning. - */ - -static int velocity_soft_reset(struct velocity_info *vptr) -{ - struct mac_regs *regs = vptr->mac_regs; - unsigned int i = 0; - - writel(CR0_SFRST, ®s->CR0Set); - - for (i = 0; i < W_MAX_TIMEOUT; i++) { - udelay(5); - if (!DWORD_REG_BITS_IS_ON(CR0_SFRST, ®s->CR0Set)) - break; - } - - if (i == W_MAX_TIMEOUT) { - writel(CR0_FORSRST, ®s->CR0Set); - /* FIXME: PCI POSTING */ - /* delay 2ms */ - mdelay(2); - } - return 0; -} - -/** - * velocity_init_rings - set up DMA rings - * @vptr: Velocity to set up - * - * Allocate PCI mapped DMA rings for the receive and transmit layer - * to use. - */ - -static int velocity_init_rings(struct velocity_info *vptr) -{ - - int idx; - - vptr->rd_curr = 0; - vptr->td_curr = 0; - memset(vptr->td_rings, 0, TX_DESC_DEF * sizeof(struct tx_desc)); - memset(vptr->rd_ring, 0, RX_DESC_DEF * sizeof(struct rx_desc)); -// memset(vptr->tx_buffs, 0, TX_DESC_DEF * PKT_BUF_SZ); - - - for (idx = 0; idx < RX_DESC_DEF; idx++) { - vptr->rd_ring[idx].rdesc0.RSR = 0; - vptr->rd_ring[idx].rdesc0.len = 0; - vptr->rd_ring[idx].rdesc0.reserved = 0; - vptr->rd_ring[idx].rdesc0.owner = 0; - vptr->rd_ring[idx].len = cpu_to_le32(vptr->rx_buf_sz); - vptr->rd_ring[idx].inten = 1; - vptr->rd_ring[idx].pa_low = - virt_to_bus(vptr->rxb + (RX_DESC_DEF * idx)); - vptr->rd_ring[idx].pa_high = 0; - vptr->rd_ring[idx].rdesc0.owner = OWNED_BY_NIC; - } - -/* for (i = 0; idx < TX_DESC_DEF; idx++ ) { - vptr->td_rings[idx].tdesc1.TCPLS = TCPLS_NORMAL; - vptr->td_rings[idx].tdesc1.TCR = TCR0_TIC; - vptr->td_rings[idx].td_buf[0].queue = 0; - vptr->td_rings[idx].tdesc0.owner = ~OWNED_BY_NIC; - vptr->td_rings[idx].tdesc0.pktsize = 0; - vptr->td_rings[idx].td_buf[0].pa_low = cpu_to_le32(virt_to_bus(vptr->txb + (idx * PKT_BUF_SZ))); - vptr->td_rings[idx].td_buf[0].pa_high = 0; - vptr->td_rings[idx].td_buf[0].bufsize = 0; - vptr->td_rings[idx].tdesc1.CMDZ = 2; - } -*/ - return 0; -} - -/** - * velocity_open - interface activation callback - * @dev: network layer device to open - * - * Called when the network layer brings the interface up. Returns - * a negative posix error code on failure, or zero on success. - * - * All the ring allocation and set up is done on open for this - * adapter to minimise memory usage when inactive - */ - -#define PCI_BYTE_REG_BITS_ON(x,i,p) do{\ - u8 byReg;\ - pci_read_config_byte((p), (i), &(byReg));\ - (byReg) |= (x);\ - pci_write_config_byte((p), (i), (byReg));\ -} while (0) - -// -// Registers in the PCI configuration space -// -#define PCI_REG_COMMAND 0x04 // -#define PCI_REG_MODE0 0x60 // -#define PCI_REG_MODE1 0x61 // -#define PCI_REG_MODE2 0x62 // -#define PCI_REG_MODE3 0x63 // -#define PCI_REG_DELAY_TIMER 0x64 // - -// Bits in the (MODE2, 0x62) register -// -#define MODE2_PCEROPT 0x80 // take PCI bus ERror as a fatal and shutdown from software control -#define MODE2_TXQ16 0x40 // TX write-back Queue control. 0->32 entries available in Tx write-back queue, 1->16 entries -#define MODE2_TXPOST 0x08 // (Not support in VT3119) -#define MODE2_AUTOOPT 0x04 // (VT3119 GHCI without such behavior) -#define MODE2_MODE10T 0x02 // used to control tx Threshold for 10M case -#define MODE2_TCPLSOPT 0x01 // TCP large send field update disable, hardware will not update related fields, leave it to software. - -// -// Bits in the MODE3 register -// -#define MODE3_MIION 0x04 // MII symbol codine error detect enable ?? - -// Bits in the (COMMAND, 0x04) register -#define COMMAND_BUSM 0x04 -#define COMMAND_WAIT 0x80 -static int velocity_open(struct nic *nic, struct pci_device *pci __unused) -{ - u8 diff; - u32 TxPhyAddr, RxPhyAddr; - u32 TxBufPhyAddr, RxBufPhyAddr; - vptr->TxDescArrays = tx_ring; - if (vptr->TxDescArrays == NULL) - printf("Allot Error"); - - /* Tx Descriptor needs 64 bytes alignment; */ - TxPhyAddr = virt_to_bus(vptr->TxDescArrays); - printf("Unaligned Address : %X\n", TxPhyAddr); - diff = 64 - (TxPhyAddr - ((TxPhyAddr >> 6) << 6)); - TxPhyAddr += diff; - vptr->td_rings = (struct tx_desc *) (vptr->TxDescArrays + diff); - - printf("Aligned Address: %lX\n", virt_to_bus(vptr->td_rings)); - vptr->tx_buffs = txb; - /* Rx Buffer needs 64 bytes alignment; */ - TxBufPhyAddr = virt_to_bus(vptr->tx_buffs); - diff = 64 - (TxBufPhyAddr - ((TxBufPhyAddr >> 6) << 6)); - TxBufPhyAddr += diff; - vptr->txb = (unsigned char *) (vptr->tx_buffs + diff); - - vptr->RxDescArrays = rx_ring; - /* Rx Descriptor needs 64 bytes alignment; */ - RxPhyAddr = virt_to_bus(vptr->RxDescArrays); - diff = 64 - (RxPhyAddr - ((RxPhyAddr >> 6) << 6)); - RxPhyAddr += diff; - vptr->rd_ring = (struct rx_desc *) (vptr->RxDescArrays + diff); - - vptr->rx_buffs = rxb; - /* Rx Buffer needs 64 bytes alignment; */ - RxBufPhyAddr = virt_to_bus(vptr->rx_buffs); - diff = 64 - (RxBufPhyAddr - ((RxBufPhyAddr >> 6) << 6)); - RxBufPhyAddr += diff; - vptr->rxb = (unsigned char *) (vptr->rx_buffs + diff); - - if (vptr->RxDescArrays == NULL || vptr->RxDescArrays == NULL) { - printf("Allocate tx_ring or rd_ring failed\n"); - return 0; - } - - vptr->rx_buf_sz = PKT_BUF_SZ; -/* - // turn this on to avoid retry forever - PCI_BYTE_REG_BITS_ON(MODE2_PCEROPT, PCI_REG_MODE2, pci); - // for some legacy BIOS and OS don't open BusM - // bit in PCI configuration space. So, turn it on. - PCI_BYTE_REG_BITS_ON(COMMAND_BUSM, PCI_REG_COMMAND, pci); - // turn this on to detect MII coding error - PCI_BYTE_REG_BITS_ON(MODE3_MIION, PCI_REG_MODE3, pci); - */ - velocity_init_rings(vptr); - - /* Ensure chip is running */ -//FIXME: pci_set_power_state(vptr->pdev, PCI_D0); - - velocity_init_registers(nic, vptr, VELOCITY_INIT_COLD); - mac_write_int_mask(0, vptr->mac_regs); -// _int(vptr->mac_regs); - //mac_enable_int(vptr->mac_regs); - - vptr->flags |= VELOCITY_FLAGS_OPENED; - return 1; - -} - -/* - * MII access , media link mode setting functions - */ - - -/** - * mii_init - set up MII - * @vptr: velocity adapter - * @mii_status: links tatus - * - * Set up the PHY for the current link state. - */ - -static void mii_init(struct velocity_info *vptr, u32 mii_status __unused) -{ - u16 BMCR; - - switch (PHYID_GET_PHY_ID(vptr->phy_id)) { - case PHYID_CICADA_CS8201: - /* - * Reset to hardware default - */ - MII_REG_BITS_OFF((ANAR_ASMDIR | ANAR_PAUSE), MII_REG_ANAR, - vptr->mac_regs); - /* - * Turn on ECHODIS bit in NWay-forced full mode and turn it - * off it in NWay-forced half mode for NWay-forced v.s. - * legacy-forced issue. - */ - if (vptr->mii_status & VELOCITY_DUPLEX_FULL) - MII_REG_BITS_ON(TCSR_ECHODIS, MII_REG_TCSR, - vptr->mac_regs); - else - MII_REG_BITS_OFF(TCSR_ECHODIS, MII_REG_TCSR, - vptr->mac_regs); - /* - * Turn on Link/Activity LED enable bit for CIS8201 - */ - MII_REG_BITS_ON(PLED_LALBE, MII_REG_PLED, vptr->mac_regs); - break; - case PHYID_VT3216_32BIT: - case PHYID_VT3216_64BIT: - /* - * Reset to hardware default - */ - MII_REG_BITS_ON((ANAR_ASMDIR | ANAR_PAUSE), MII_REG_ANAR, - vptr->mac_regs); - /* - * Turn on ECHODIS bit in NWay-forced full mode and turn it - * off it in NWay-forced half mode for NWay-forced v.s. - * legacy-forced issue - */ - if (vptr->mii_status & VELOCITY_DUPLEX_FULL) - MII_REG_BITS_ON(TCSR_ECHODIS, MII_REG_TCSR, - vptr->mac_regs); - else - MII_REG_BITS_OFF(TCSR_ECHODIS, MII_REG_TCSR, - vptr->mac_regs); - break; - - case PHYID_MARVELL_1000: - case PHYID_MARVELL_1000S: - /* - * Assert CRS on Transmit - */ - MII_REG_BITS_ON(PSCR_ACRSTX, MII_REG_PSCR, vptr->mac_regs); - /* - * Reset to hardware default - */ - MII_REG_BITS_ON((ANAR_ASMDIR | ANAR_PAUSE), MII_REG_ANAR, - vptr->mac_regs); - break; - default: - ; - } - velocity_mii_read(vptr->mac_regs, MII_REG_BMCR, &BMCR); - if (BMCR & BMCR_ISO) { - BMCR &= ~BMCR_ISO; - velocity_mii_write(vptr->mac_regs, MII_REG_BMCR, BMCR); - } -} - -/** - * safe_disable_mii_autopoll - autopoll off - * @regs: velocity registers - * - * Turn off the autopoll and wait for it to disable on the chip - */ - -static void safe_disable_mii_autopoll(struct mac_regs *regs) -{ - u16 ww; - - /* turn off MAUTO */ - writeb(0, ®s->MIICR); - for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { - udelay(1); - if (BYTE_REG_BITS_IS_ON(MIISR_MIDLE, ®s->MIISR)) - break; - } -} - -/** - * enable_mii_autopoll - turn on autopolling - * @regs: velocity registers - * - * Enable the MII link status autopoll feature on the Velocity - * hardware. Wait for it to enable. - */ - -static void enable_mii_autopoll(struct mac_regs *regs) -{ - unsigned int ii; - - writeb(0, &(regs->MIICR)); - writeb(MIIADR_SWMPL, ®s->MIIADR); - - for (ii = 0; ii < W_MAX_TIMEOUT; ii++) { - udelay(1); - if (BYTE_REG_BITS_IS_ON(MIISR_MIDLE, ®s->MIISR)) - break; - } - - writeb(MIICR_MAUTO, ®s->MIICR); - - for (ii = 0; ii < W_MAX_TIMEOUT; ii++) { - udelay(1); - if (!BYTE_REG_BITS_IS_ON(MIISR_MIDLE, ®s->MIISR)) - break; - } - -} - -/** - * velocity_mii_read - read MII data - * @regs: velocity registers - * @index: MII register index - * @data: buffer for received data - * - * Perform a single read of an MII 16bit register. Returns zero - * on success or -ETIMEDOUT if the PHY did not respond. - */ - -static int velocity_mii_read(struct mac_regs *regs, u8 index, u16 * data) -{ - u16 ww; - - /* - * Disable MIICR_MAUTO, so that mii addr can be set normally - */ - safe_disable_mii_autopoll(regs); - - writeb(index, ®s->MIIADR); - - BYTE_REG_BITS_ON(MIICR_RCMD, ®s->MIICR); - - for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { - if (!(readb(®s->MIICR) & MIICR_RCMD)) - break; - } - - *data = readw(®s->MIIDATA); - - enable_mii_autopoll(regs); - if (ww == W_MAX_TIMEOUT) - return -1; - return 0; -} - -/** - * velocity_mii_write - write MII data - * @regs: velocity registers - * @index: MII register index - * @data: 16bit data for the MII register - * - * Perform a single write to an MII 16bit register. Returns zero - * on success or -ETIMEDOUT if the PHY did not respond. - */ - -static int velocity_mii_write(struct mac_regs *regs, u8 mii_addr, u16 data) -{ - u16 ww; - - /* - * Disable MIICR_MAUTO, so that mii addr can be set normally - */ - safe_disable_mii_autopoll(regs); - - /* MII reg offset */ - writeb(mii_addr, ®s->MIIADR); - /* set MII data */ - writew(data, ®s->MIIDATA); - - /* turn on MIICR_WCMD */ - BYTE_REG_BITS_ON(MIICR_WCMD, ®s->MIICR); - - /* W_MAX_TIMEOUT is the timeout period */ - for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { - udelay(5); - if (!(readb(®s->MIICR) & MIICR_WCMD)) - break; - } - enable_mii_autopoll(regs); - - if (ww == W_MAX_TIMEOUT) - return -1; - return 0; -} - -/** - * velocity_get_opt_media_mode - get media selection - * @vptr: velocity adapter - * - * Get the media mode stored in EEPROM or module options and load - * mii_status accordingly. The requested link state information - * is also returned. - */ - -static u32 velocity_get_opt_media_mode(struct velocity_info *vptr) -{ - u32 status = 0; - - switch (vptr->options.spd_dpx) { - case SPD_DPX_AUTO: - status = VELOCITY_AUTONEG_ENABLE; - break; - case SPD_DPX_100_FULL: - status = VELOCITY_SPEED_100 | VELOCITY_DUPLEX_FULL; - break; - case SPD_DPX_10_FULL: - status = VELOCITY_SPEED_10 | VELOCITY_DUPLEX_FULL; - break; - case SPD_DPX_100_HALF: - status = VELOCITY_SPEED_100; - break; - case SPD_DPX_10_HALF: - status = VELOCITY_SPEED_10; - break; - } - vptr->mii_status = status; - return status; -} - -/** - * mii_set_auto_on - autonegotiate on - * @vptr: velocity - * - * Enable autonegotation on this interface - */ - -static void mii_set_auto_on(struct velocity_info *vptr) -{ - if (MII_REG_BITS_IS_ON(BMCR_AUTO, MII_REG_BMCR, vptr->mac_regs)) - MII_REG_BITS_ON(BMCR_REAUTO, MII_REG_BMCR, vptr->mac_regs); - else - MII_REG_BITS_ON(BMCR_AUTO, MII_REG_BMCR, vptr->mac_regs); -} - - -/* -static void mii_set_auto_off(struct velocity_info * vptr) -{ - MII_REG_BITS_OFF(BMCR_AUTO, MII_REG_BMCR, vptr->mac_regs); -} -*/ - -/** - * set_mii_flow_control - flow control setup - * @vptr: velocity interface - * - * Set up the flow control on this interface according to - * the supplied user/eeprom options. - */ - -static void set_mii_flow_control(struct velocity_info *vptr) -{ - /*Enable or Disable PAUSE in ANAR */ - switch (vptr->options.flow_cntl) { - case FLOW_CNTL_TX: - MII_REG_BITS_OFF(ANAR_PAUSE, MII_REG_ANAR, vptr->mac_regs); - MII_REG_BITS_ON(ANAR_ASMDIR, MII_REG_ANAR, vptr->mac_regs); - break; - - case FLOW_CNTL_RX: - MII_REG_BITS_ON(ANAR_PAUSE, MII_REG_ANAR, vptr->mac_regs); - MII_REG_BITS_ON(ANAR_ASMDIR, MII_REG_ANAR, vptr->mac_regs); - break; - - case FLOW_CNTL_TX_RX: - MII_REG_BITS_ON(ANAR_PAUSE, MII_REG_ANAR, vptr->mac_regs); - MII_REG_BITS_ON(ANAR_ASMDIR, MII_REG_ANAR, vptr->mac_regs); - break; - - case FLOW_CNTL_DISABLE: - MII_REG_BITS_OFF(ANAR_PAUSE, MII_REG_ANAR, vptr->mac_regs); - MII_REG_BITS_OFF(ANAR_ASMDIR, MII_REG_ANAR, - vptr->mac_regs); - break; - default: - break; - } -} - -/** - * velocity_set_media_mode - set media mode - * @mii_status: old MII link state - * - * Check the media link state and configure the flow control - * PHY and also velocity hardware setup accordingly. In particular - * we need to set up CD polling and frame bursting. - */ - -static int velocity_set_media_mode(struct velocity_info *vptr, - u32 mii_status) -{ - struct mac_regs *regs = vptr->mac_regs; - - vptr->mii_status = mii_check_media_mode(vptr->mac_regs); - - /* Set mii link status */ - set_mii_flow_control(vptr); - - if (PHYID_GET_PHY_ID(vptr->phy_id) == PHYID_CICADA_CS8201) { - MII_REG_BITS_ON(AUXCR_MDPPS, MII_REG_AUXCR, - vptr->mac_regs); - } - - /* - * If connection type is AUTO - */ - if (mii_status & VELOCITY_AUTONEG_ENABLE) { - printf("Velocity is AUTO mode\n"); - /* clear force MAC mode bit */ - BYTE_REG_BITS_OFF(CHIPGCR_FCMODE, ®s->CHIPGCR); - /* set duplex mode of MAC according to duplex mode of MII */ - MII_REG_BITS_ON(ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10, - MII_REG_ANAR, vptr->mac_regs); - MII_REG_BITS_ON(G1000CR_1000FD | G1000CR_1000, - MII_REG_G1000CR, vptr->mac_regs); - MII_REG_BITS_ON(BMCR_SPEED1G, MII_REG_BMCR, - vptr->mac_regs); - - /* enable AUTO-NEGO mode */ - mii_set_auto_on(vptr); - } else { - u16 ANAR; - u8 CHIPGCR; - - /* - * 1. if it's 3119, disable frame bursting in halfduplex mode - * and enable it in fullduplex mode - * 2. set correct MII/GMII and half/full duplex mode in CHIPGCR - * 3. only enable CD heart beat counter in 10HD mode - */ - - /* set force MAC mode bit */ - BYTE_REG_BITS_ON(CHIPGCR_FCMODE, ®s->CHIPGCR); - - CHIPGCR = readb(®s->CHIPGCR); - CHIPGCR &= ~CHIPGCR_FCGMII; - - if (mii_status & VELOCITY_DUPLEX_FULL) { - CHIPGCR |= CHIPGCR_FCFDX; - writeb(CHIPGCR, ®s->CHIPGCR); - printf - ("DEBUG: set Velocity to forced full mode\n"); - if (vptr->rev_id < REV_ID_VT3216_A0) - BYTE_REG_BITS_OFF(TCR_TB2BDIS, ®s->TCR); - } else { - CHIPGCR &= ~CHIPGCR_FCFDX; - printf - ("DEBUG: set Velocity to forced half mode\n"); - writeb(CHIPGCR, ®s->CHIPGCR); - if (vptr->rev_id < REV_ID_VT3216_A0) - BYTE_REG_BITS_ON(TCR_TB2BDIS, ®s->TCR); - } - - MII_REG_BITS_OFF(G1000CR_1000FD | G1000CR_1000, - MII_REG_G1000CR, vptr->mac_regs); - - if (!(mii_status & VELOCITY_DUPLEX_FULL) - && (mii_status & VELOCITY_SPEED_10)) { - BYTE_REG_BITS_OFF(TESTCFG_HBDIS, ®s->TESTCFG); - } else { - BYTE_REG_BITS_ON(TESTCFG_HBDIS, ®s->TESTCFG); - } - /* MII_REG_BITS_OFF(BMCR_SPEED1G, MII_REG_BMCR, vptr->mac_regs); */ - velocity_mii_read(vptr->mac_regs, MII_REG_ANAR, &ANAR); - ANAR &= (~(ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10)); - if (mii_status & VELOCITY_SPEED_100) { - if (mii_status & VELOCITY_DUPLEX_FULL) - ANAR |= ANAR_TXFD; - else - ANAR |= ANAR_TX; - } else { - if (mii_status & VELOCITY_DUPLEX_FULL) - ANAR |= ANAR_10FD; - else - ANAR |= ANAR_10; - } - velocity_mii_write(vptr->mac_regs, MII_REG_ANAR, ANAR); - /* enable AUTO-NEGO mode */ - mii_set_auto_on(vptr); - /* MII_REG_BITS_ON(BMCR_AUTO, MII_REG_BMCR, vptr->mac_regs); */ - } - /* vptr->mii_status=mii_check_media_mode(vptr->mac_regs); */ - /* vptr->mii_status=check_connection_type(vptr->mac_regs); */ - return VELOCITY_LINK_CHANGE; -} - -/** - * mii_check_media_mode - check media state - * @regs: velocity registers - * - * Check the current MII status and determine the link status - * accordingly - */ - -static u32 mii_check_media_mode(struct mac_regs *regs) -{ - u32 status = 0; - u16 ANAR; - - if (!MII_REG_BITS_IS_ON(BMSR_LNK, MII_REG_BMSR, regs)) - status |= VELOCITY_LINK_FAIL; - - if (MII_REG_BITS_IS_ON(G1000CR_1000FD, MII_REG_G1000CR, regs)) - status |= VELOCITY_SPEED_1000 | VELOCITY_DUPLEX_FULL; - else if (MII_REG_BITS_IS_ON(G1000CR_1000, MII_REG_G1000CR, regs)) - status |= (VELOCITY_SPEED_1000); - else { - velocity_mii_read(regs, MII_REG_ANAR, &ANAR); - if (ANAR & ANAR_TXFD) - status |= - (VELOCITY_SPEED_100 | VELOCITY_DUPLEX_FULL); - else if (ANAR & ANAR_TX) - status |= VELOCITY_SPEED_100; - else if (ANAR & ANAR_10FD) - status |= - (VELOCITY_SPEED_10 | VELOCITY_DUPLEX_FULL); - else - status |= (VELOCITY_SPEED_10); - } - - if (MII_REG_BITS_IS_ON(BMCR_AUTO, MII_REG_BMCR, regs)) { - velocity_mii_read(regs, MII_REG_ANAR, &ANAR); - if ((ANAR & (ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10)) - == (ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10)) { - if (MII_REG_BITS_IS_ON - (G1000CR_1000 | G1000CR_1000FD, - MII_REG_G1000CR, regs)) - status |= VELOCITY_AUTONEG_ENABLE; - } - } - - return status; -} - -static u32 check_connection_type(struct mac_regs *regs) -{ - u32 status = 0; - u8 PHYSR0; - u16 ANAR; - PHYSR0 = readb(®s->PHYSR0); - - /* - if (!(PHYSR0 & PHYSR0_LINKGD)) - status|=VELOCITY_LINK_FAIL; - */ - - if (PHYSR0 & PHYSR0_FDPX) - status |= VELOCITY_DUPLEX_FULL; - - if (PHYSR0 & PHYSR0_SPDG) - status |= VELOCITY_SPEED_1000; - if (PHYSR0 & PHYSR0_SPD10) - status |= VELOCITY_SPEED_10; - else - status |= VELOCITY_SPEED_100; - - if (MII_REG_BITS_IS_ON(BMCR_AUTO, MII_REG_BMCR, regs)) { - velocity_mii_read(regs, MII_REG_ANAR, &ANAR); - if ((ANAR & (ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10)) - == (ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10)) { - if (MII_REG_BITS_IS_ON - (G1000CR_1000 | G1000CR_1000FD, - MII_REG_G1000CR, regs)) - status |= VELOCITY_AUTONEG_ENABLE; - } - } - - return status; -} - -/** - * enable_flow_control_ability - flow control - * @vptr: veloity to configure - * - * Set up flow control according to the flow control options - * determined by the eeprom/configuration. - */ - -static void enable_flow_control_ability(struct velocity_info *vptr) -{ - - struct mac_regs *regs = vptr->mac_regs; - - switch (vptr->options.flow_cntl) { - - case FLOW_CNTL_DEFAULT: - if (BYTE_REG_BITS_IS_ON(PHYSR0_RXFLC, ®s->PHYSR0)) - writel(CR0_FDXRFCEN, ®s->CR0Set); - else - writel(CR0_FDXRFCEN, ®s->CR0Clr); - - if (BYTE_REG_BITS_IS_ON(PHYSR0_TXFLC, ®s->PHYSR0)) - writel(CR0_FDXTFCEN, ®s->CR0Set); - else - writel(CR0_FDXTFCEN, ®s->CR0Clr); - break; - - case FLOW_CNTL_TX: - writel(CR0_FDXTFCEN, ®s->CR0Set); - writel(CR0_FDXRFCEN, ®s->CR0Clr); - break; - - case FLOW_CNTL_RX: - writel(CR0_FDXRFCEN, ®s->CR0Set); - writel(CR0_FDXTFCEN, ®s->CR0Clr); - break; - - case FLOW_CNTL_TX_RX: - writel(CR0_FDXTFCEN, ®s->CR0Set); - writel(CR0_FDXRFCEN, ®s->CR0Set); - break; - - case FLOW_CNTL_DISABLE: - writel(CR0_FDXRFCEN, ®s->CR0Clr); - writel(CR0_FDXTFCEN, ®s->CR0Clr); - break; - - default: - break; - } - -} - -/* FIXME: Move to pci.c */ -/** - * pci_set_power_state - Set the power state of a PCI device - * @dev: PCI device to be suspended - * @state: Power state we're entering - * - * Transition a device to a new power state, using the Power Management - * Capabilities in the device's config space. - * - * RETURN VALUE: - * -EINVAL if trying to enter a lower state than we're already in. - * 0 if we're already in the requested state. - * -EIO if device does not support PCI PM. - * 0 if we can successfully change the power state. - */ - -int pci_set_power_state(struct pci_device *dev, int state) -{ - int pm; - u16 pmcsr; - int current_state = 0; - - /* bound the state we're entering */ - if (state > 3) - state = 3; - - /* Validate current state: - * Can enter D0 from any state, but if we can only go deeper - * to sleep if we're already in a low power state - */ - if (state > 0 && current_state > state) - return -1; - else if (current_state == state) - return 0; /* we're already there */ - - /* find PCI PM capability in list */ - pm = pci_find_capability(dev, PCI_CAP_ID_PM); - - /* abort if the device doesn't support PM capabilities */ - if (!pm) - return -2; - - /* check if this device supports the desired state */ - if (state == 1 || state == 2) { - u16 pmc; - pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc); - if (state == 1 && !(pmc & PCI_PM_CAP_D1)) - return -2; - else if (state == 2 && !(pmc & PCI_PM_CAP_D2)) - return -2; - } - - /* If we're in D3, force entire word to 0. - * This doesn't affect PME_Status, disables PME_En, and - * sets PowerState to 0. - */ - if (current_state >= 3) - pmcsr = 0; - else { - pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr); - pmcsr &= ~PCI_PM_CTRL_STATE_MASK; - pmcsr |= state; - } - - /* enter specified state */ - pci_write_config_word(dev, pm + PCI_PM_CTRL, pmcsr); - - /* Mandatory power management transition delays */ - /* see PCI PM 1.1 5.6.1 table 18 */ - if (state == 3 || current_state == 3) - mdelay(10); - else if (state == 2 || current_state == 2) - udelay(200); - current_state = state; - - return 0; -} - -static struct pci_device_id velocity_nics[] = { - PCI_ROM(0x1106, 0x3119, "via-velocity", "VIA Networking Velocity Family Gigabit Ethernet Adapter", 0), -}; - -PCI_DRIVER ( velocity_driver, velocity_nics, PCI_NO_CLASS ); - -DRIVER ( "VIA-VELOCITY/PCI", nic_driver, pci_driver, velocity_driver, - velocity_probe, velocity_disable ); diff --git a/src/drivers/net/via-velocity.h b/src/drivers/net/via-velocity.h deleted file mode 100644 index 753fe445..00000000 --- a/src/drivers/net/via-velocity.h +++ /dev/null @@ -1,1932 +0,0 @@ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * This software may be redistributed and/or modified 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. - * - * File: via-velocity.h - * - * Purpose: Header file to define driver's private structures. - * - * Author: Chuang Liang-Shing, AJ Jiang - * - * Date: Jan 24, 2003 - * - * Changes for Etherboot Port: - * Copyright (c) 2006 by Timothy Legge - */ - -FILE_LICENCE ( GPL2_OR_LATER ); - -#ifndef VELOCITY_H -#define VELOCITY_H - -#define VELOCITY_TX_CSUM_SUPPORT - -#define VELOCITY_NAME "via-velocity" -#define VELOCITY_FULL_DRV_NAM "VIA Networking Velocity Family Gigabit Ethernet Adapter Driver" -#define VELOCITY_VERSION "1.13" - -#define PKT_BUF_SZ 1564 - -#define MAX_UNITS 8 -#define OPTION_DEFAULT { [0 ... MAX_UNITS-1] = -1} - -#define REV_ID_VT6110 (0) - -#define BYTE_REG_BITS_ON(x,p) do { writeb(readb((p))|(x),(p));} while (0) -#define WORD_REG_BITS_ON(x,p) do { writew(readw((p))|(x),(p));} while (0) -#define DWORD_REG_BITS_ON(x,p) do { writel(readl((p))|(x),(p));} while (0) - -#define BYTE_REG_BITS_IS_ON(x,p) (readb((p)) & (x)) -#define WORD_REG_BITS_IS_ON(x,p) (readw((p)) & (x)) -#define DWORD_REG_BITS_IS_ON(x,p) (readl((p)) & (x)) - -#define BYTE_REG_BITS_OFF(x,p) do { writeb(readb((p)) & (~(x)),(p));} while (0) -#define WORD_REG_BITS_OFF(x,p) do { writew(readw((p)) & (~(x)),(p));} while (0) -#define DWORD_REG_BITS_OFF(x,p) do { writel(readl((p)) & (~(x)),(p));} while (0) - -#define BYTE_REG_BITS_SET(x,m,p) do { writeb( (readb((p)) & (~(m))) |(x),(p));} while (0) -#define WORD_REG_BITS_SET(x,m,p) do { writew( (readw((p)) & (~(m))) |(x),(p));} while (0) -#define DWORD_REG_BITS_SET(x,m,p) do { writel( (readl((p)) & (~(m)))|(x),(p));} while (0) - -#define VAR_USED(p) do {(p)=(p);} while (0) - -/* - * Purpose: Structures for MAX RX/TX descriptors. - */ - - -#define B_OWNED_BY_CHIP 1 -#define B_OWNED_BY_HOST 0 - -/* - * Bits in the RSR0 register - */ - -#define RSR_DETAG 0x0080 -#define RSR_SNTAG 0x0040 -#define RSR_RXER 0x0020 -#define RSR_RL 0x0010 -#define RSR_CE 0x0008 -#define RSR_FAE 0x0004 -#define RSR_CRC 0x0002 -#define RSR_VIDM 0x0001 - -/* - * Bits in the RSR1 register - */ - -#define RSR_RXOK 0x8000 // rx OK -#define RSR_PFT 0x4000 // Perfect filtering address match -#define RSR_MAR 0x2000 // MAC accept multicast address packet -#define RSR_BAR 0x1000 // MAC accept broadcast address packet -#define RSR_PHY 0x0800 // MAC accept physical address packet -#define RSR_VTAG 0x0400 // 802.1p/1q tagging packet indicator -#define RSR_STP 0x0200 // start of packet -#define RSR_EDP 0x0100 // end of packet - -/* - * Bits in the RSR1 register - */ - -#define RSR1_RXOK 0x80 // rx OK -#define RSR1_PFT 0x40 // Perfect filtering address match -#define RSR1_MAR 0x20 // MAC accept multicast address packet -#define RSR1_BAR 0x10 // MAC accept broadcast address packet -#define RSR1_PHY 0x08 // MAC accept physical address packet -#define RSR1_VTAG 0x04 // 802.1p/1q tagging packet indicator -#define RSR1_STP 0x02 // start of packet -#define RSR1_EDP 0x01 // end of packet - -/* - * Bits in the CSM register - */ - -#define CSM_IPOK 0x40 //IP Checkusm validatiaon ok -#define CSM_TUPOK 0x20 //TCP/UDP Checkusm validatiaon ok -#define CSM_FRAG 0x10 //Fragment IP datagram -#define CSM_IPKT 0x04 //Received an IP packet -#define CSM_TCPKT 0x02 //Received a TCP packet -#define CSM_UDPKT 0x01 //Received a UDP packet - -/* - * Bits in the TSR0 register - */ - -#define TSR0_ABT 0x0080 // Tx abort because of excessive collision -#define TSR0_OWT 0x0040 // Jumbo frame Tx abort -#define TSR0_OWC 0x0020 // Out of window collision -#define TSR0_COLS 0x0010 // experience collision in this transmit event -#define TSR0_NCR3 0x0008 // collision retry counter[3] -#define TSR0_NCR2 0x0004 // collision retry counter[2] -#define TSR0_NCR1 0x0002 // collision retry counter[1] -#define TSR0_NCR0 0x0001 // collision retry counter[0] -#define TSR0_TERR 0x8000 // -#define TSR0_FDX 0x4000 // current transaction is serviced by full duplex mode -#define TSR0_GMII 0x2000 // current transaction is serviced by GMII mode -#define TSR0_LNKFL 0x1000 // packet serviced during link down -#define TSR0_SHDN 0x0400 // shutdown case -#define TSR0_CRS 0x0200 // carrier sense lost -#define TSR0_CDH 0x0100 // AQE test fail (CD heartbeat) - -/* - * Bits in the TSR1 register - */ - -#define TSR1_TERR 0x80 // -#define TSR1_FDX 0x40 // current transaction is serviced by full duplex mode -#define TSR1_GMII 0x20 // current transaction is serviced by GMII mode -#define TSR1_LNKFL 0x10 // packet serviced during link down -#define TSR1_SHDN 0x04 // shutdown case -#define TSR1_CRS 0x02 // carrier sense lost -#define TSR1_CDH 0x01 // AQE test fail (CD heartbeat) - -// -// Bits in the TCR0 register -// -#define TCR0_TIC 0x80 // assert interrupt immediately while descriptor has been send complete -#define TCR0_PIC 0x40 // priority interrupt request, INA# is issued over adaptive interrupt scheme -#define TCR0_VETAG 0x20 // enable VLAN tag -#define TCR0_IPCK 0x10 // request IP checksum calculation. -#define TCR0_UDPCK 0x08 // request UDP checksum calculation. -#define TCR0_TCPCK 0x04 // request TCP checksum calculation. -#define TCR0_JMBO 0x02 // indicate a jumbo packet in GMAC side -#define TCR0_CRC 0x01 // disable CRC generation - -#define TCPLS_NORMAL 3 -#define TCPLS_START 2 -#define TCPLS_END 1 -#define TCPLS_MED 0 - - -// max transmit or receive buffer size -#define CB_RX_BUF_SIZE 2048UL // max buffer size - // NOTE: must be multiple of 4 - -#define CB_MAX_RD_NUM 512 // MAX # of RD -#define CB_MAX_TD_NUM 256 // MAX # of TD - -#define CB_INIT_RD_NUM_3119 128 // init # of RD, for setup VT3119 -#define CB_INIT_TD_NUM_3119 64 // init # of TD, for setup VT3119 - -#define CB_INIT_RD_NUM 128 // init # of RD, for setup default -#define CB_INIT_TD_NUM 64 // init # of TD, for setup default - -// for 3119 -#define CB_TD_RING_NUM 4 // # of TD rings. -#define CB_MAX_SEG_PER_PKT 7 // max data seg per packet (Tx) - - -/* - * If collisions excess 15 times , tx will abort, and - * if tx fifo underflow, tx will fail - * we should try to resend it - */ - -#define CB_MAX_TX_ABORT_RETRY 3 - -/* - * Receive descriptor - */ - -struct rdesc0 { - u16 RSR; /* Receive status */ - u16 len:14; /* Received packet length */ - u16 reserved:1; - u16 owner:1; /* Who owns this buffer ? */ -}; - -struct rdesc1 { - u16 PQTAG; - u8 CSM; - u8 IPKT; -}; - -struct rx_desc { - struct rdesc0 rdesc0; - struct rdesc1 rdesc1; - u32 pa_low; /* Low 32 bit PCI address */ - u16 pa_high; /* Next 16 bit PCI address (48 total) */ - u16 len:15; /* Frame size */ - u16 inten:1; /* Enable interrupt */ -} __attribute__ ((__packed__)); - -/* - * Transmit descriptor - */ - -struct tdesc0 { - u16 TSR; /* Transmit status register */ - u16 pktsize:14; /* Size of frame */ - u16 reserved:1; - u16 owner:1; /* Who owns the buffer */ -}; - -struct pqinf { /* Priority queue info */ - u16 VID:12; - u16 CFI:1; - u16 priority:3; -} __attribute__ ((__packed__)); - -struct tdesc1 { - struct pqinf pqinf; - u8 TCR; - u8 TCPLS:2; - u8 reserved:2; - u8 CMDZ:4; -} __attribute__ ((__packed__)); - -struct td_buf { - u32 pa_low; - u16 pa_high; - u16 bufsize:14; - u16 reserved:1; - u16 queue:1; -} __attribute__ ((__packed__)); - -struct tx_desc { - struct tdesc0 tdesc0; - struct tdesc1 tdesc1; - struct td_buf td_buf[7]; -}; - -#ifdef LINUX -struct velocity_rd_info { - struct sk_buff *skb; - dma_addr_t skb_dma; -}; - - -/** - * alloc_rd_info - allocate an rd info block - * - * Alocate and initialize a receive info structure used for keeping - * track of kernel side information related to each receive - * descriptor we are using - */ - -static inline struct velocity_rd_info *alloc_rd_info(void) -{ - struct velocity_rd_info *ptr; - if ((ptr = - kmalloc(sizeof(struct velocity_rd_info), GFP_ATOMIC)) == NULL) - return NULL; - else { - memset(ptr, 0, sizeof(struct velocity_rd_info)); - return ptr; - } -} - -/* - * Used to track transmit side buffers. - */ - -struct velocity_td_info { - struct sk_buff *skb; - u8 *buf; - int nskb_dma; - dma_addr_t skb_dma[7]; - dma_addr_t buf_dma; -}; - -#endif -enum { - OWNED_BY_HOST = 0, - OWNED_BY_NIC = 1 -} velocity_owner; - - -/* - * MAC registers and macros. - */ - - -#define MCAM_SIZE 64 -#define VCAM_SIZE 64 -#define TX_QUEUE_NO 4 - -#define MAX_HW_MIB_COUNTER 32 -#define VELOCITY_MIN_MTU (1514-14) -#define VELOCITY_MAX_MTU (9000) - -/* - * Registers in the MAC - */ - -#define MAC_REG_PAR 0x00 // physical address -#define MAC_REG_RCR 0x06 -#define MAC_REG_TCR 0x07 -#define MAC_REG_CR0_SET 0x08 -#define MAC_REG_CR1_SET 0x09 -#define MAC_REG_CR2_SET 0x0A -#define MAC_REG_CR3_SET 0x0B -#define MAC_REG_CR0_CLR 0x0C -#define MAC_REG_CR1_CLR 0x0D -#define MAC_REG_CR2_CLR 0x0E -#define MAC_REG_CR3_CLR 0x0F -#define MAC_REG_MAR 0x10 -#define MAC_REG_CAM 0x10 -#define MAC_REG_DEC_BASE_HI 0x18 -#define MAC_REG_DBF_BASE_HI 0x1C -#define MAC_REG_ISR_CTL 0x20 -#define MAC_REG_ISR_HOTMR 0x20 -#define MAC_REG_ISR_TSUPTHR 0x20 -#define MAC_REG_ISR_RSUPTHR 0x20 -#define MAC_REG_ISR_CTL1 0x21 -#define MAC_REG_TXE_SR 0x22 -#define MAC_REG_RXE_SR 0x23 -#define MAC_REG_ISR 0x24 -#define MAC_REG_ISR0 0x24 -#define MAC_REG_ISR1 0x25 -#define MAC_REG_ISR2 0x26 -#define MAC_REG_ISR3 0x27 -#define MAC_REG_IMR 0x28 -#define MAC_REG_IMR0 0x28 -#define MAC_REG_IMR1 0x29 -#define MAC_REG_IMR2 0x2A -#define MAC_REG_IMR3 0x2B -#define MAC_REG_TDCSR_SET 0x30 -#define MAC_REG_RDCSR_SET 0x32 -#define MAC_REG_TDCSR_CLR 0x34 -#define MAC_REG_RDCSR_CLR 0x36 -#define MAC_REG_RDBASE_LO 0x38 -#define MAC_REG_RDINDX 0x3C -#define MAC_REG_TDBASE_LO 0x40 -#define MAC_REG_RDCSIZE 0x50 -#define MAC_REG_TDCSIZE 0x52 -#define MAC_REG_TDINDX 0x54 -#define MAC_REG_TDIDX0 0x54 -#define MAC_REG_TDIDX1 0x56 -#define MAC_REG_TDIDX2 0x58 -#define MAC_REG_TDIDX3 0x5A -#define MAC_REG_PAUSE_TIMER 0x5C -#define MAC_REG_RBRDU 0x5E -#define MAC_REG_FIFO_TEST0 0x60 -#define MAC_REG_FIFO_TEST1 0x64 -#define MAC_REG_CAMADDR 0x68 -#define MAC_REG_CAMCR 0x69 -#define MAC_REG_GFTEST 0x6A -#define MAC_REG_FTSTCMD 0x6B -#define MAC_REG_MIICFG 0x6C -#define MAC_REG_MIISR 0x6D -#define MAC_REG_PHYSR0 0x6E -#define MAC_REG_PHYSR1 0x6F -#define MAC_REG_MIICR 0x70 -#define MAC_REG_MIIADR 0x71 -#define MAC_REG_MIIDATA 0x72 -#define MAC_REG_SOFT_TIMER0 0x74 -#define MAC_REG_SOFT_TIMER1 0x76 -#define MAC_REG_CFGA 0x78 -#define MAC_REG_CFGB 0x79 -#define MAC_REG_CFGC 0x7A -#define MAC_REG_CFGD 0x7B -#define MAC_REG_DCFG0 0x7C -#define MAC_REG_DCFG1 0x7D -#define MAC_REG_MCFG0 0x7E -#define MAC_REG_MCFG1 0x7F - -#define MAC_REG_TBIST 0x80 -#define MAC_REG_RBIST 0x81 -#define MAC_REG_PMCC 0x82 -#define MAC_REG_STICKHW 0x83 -#define MAC_REG_MIBCR 0x84 -#define MAC_REG_EERSV 0x85 -#define MAC_REG_REVID 0x86 -#define MAC_REG_MIBREAD 0x88 -#define MAC_REG_BPMA 0x8C -#define MAC_REG_EEWR_DATA 0x8C -#define MAC_REG_BPMD_WR 0x8F -#define MAC_REG_BPCMD 0x90 -#define MAC_REG_BPMD_RD 0x91 -#define MAC_REG_EECHKSUM 0x92 -#define MAC_REG_EECSR 0x93 -#define MAC_REG_EERD_DATA 0x94 -#define MAC_REG_EADDR 0x96 -#define MAC_REG_EMBCMD 0x97 -#define MAC_REG_JMPSR0 0x98 -#define MAC_REG_JMPSR1 0x99 -#define MAC_REG_JMPSR2 0x9A -#define MAC_REG_JMPSR3 0x9B -#define MAC_REG_CHIPGSR 0x9C -#define MAC_REG_TESTCFG 0x9D -#define MAC_REG_DEBUG 0x9E -#define MAC_REG_CHIPGCR 0x9F -#define MAC_REG_WOLCR0_SET 0xA0 -#define MAC_REG_WOLCR1_SET 0xA1 -#define MAC_REG_PWCFG_SET 0xA2 -#define MAC_REG_WOLCFG_SET 0xA3 -#define MAC_REG_WOLCR0_CLR 0xA4 -#define MAC_REG_WOLCR1_CLR 0xA5 -#define MAC_REG_PWCFG_CLR 0xA6 -#define MAC_REG_WOLCFG_CLR 0xA7 -#define MAC_REG_WOLSR0_SET 0xA8 -#define MAC_REG_WOLSR1_SET 0xA9 -#define MAC_REG_WOLSR0_CLR 0xAC -#define MAC_REG_WOLSR1_CLR 0xAD -#define MAC_REG_PATRN_CRC0 0xB0 -#define MAC_REG_PATRN_CRC1 0xB2 -#define MAC_REG_PATRN_CRC2 0xB4 -#define MAC_REG_PATRN_CRC3 0xB6 -#define MAC_REG_PATRN_CRC4 0xB8 -#define MAC_REG_PATRN_CRC5 0xBA -#define MAC_REG_PATRN_CRC6 0xBC -#define MAC_REG_PATRN_CRC7 0xBE -#define MAC_REG_BYTEMSK0_0 0xC0 -#define MAC_REG_BYTEMSK0_1 0xC4 -#define MAC_REG_BYTEMSK0_2 0xC8 -#define MAC_REG_BYTEMSK0_3 0xCC -#define MAC_REG_BYTEMSK1_0 0xD0 -#define MAC_REG_BYTEMSK1_1 0xD4 -#define MAC_REG_BYTEMSK1_2 0xD8 -#define MAC_REG_BYTEMSK1_3 0xDC -#define MAC_REG_BYTEMSK2_0 0xE0 -#define MAC_REG_BYTEMSK2_1 0xE4 -#define MAC_REG_BYTEMSK2_2 0xE8 -#define MAC_REG_BYTEMSK2_3 0xEC -#define MAC_REG_BYTEMSK3_0 0xF0 -#define MAC_REG_BYTEMSK3_1 0xF4 -#define MAC_REG_BYTEMSK3_2 0xF8 -#define MAC_REG_BYTEMSK3_3 0xFC - -/* - * Bits in the RCR register - */ - -#define RCR_AS 0x80 -#define RCR_AP 0x40 -#define RCR_AL 0x20 -#define RCR_PROM 0x10 -#define RCR_AB 0x08 -#define RCR_AM 0x04 -#define RCR_AR 0x02 -#define RCR_SEP 0x01 - -/* - * Bits in the TCR register - */ - -#define TCR_TB2BDIS 0x80 -#define TCR_COLTMC1 0x08 -#define TCR_COLTMC0 0x04 -#define TCR_LB1 0x02 /* loopback[1] */ -#define TCR_LB0 0x01 /* loopback[0] */ - -/* - * Bits in the CR0 register - */ - -#define CR0_TXON 0x00000008UL -#define CR0_RXON 0x00000004UL -#define CR0_STOP 0x00000002UL /* stop MAC, default = 1 */ -#define CR0_STRT 0x00000001UL /* start MAC */ -#define CR0_SFRST 0x00008000UL /* software reset */ -#define CR0_TM1EN 0x00004000UL -#define CR0_TM0EN 0x00002000UL -#define CR0_DPOLL 0x00000800UL /* disable rx/tx auto polling */ -#define CR0_DISAU 0x00000100UL -#define CR0_XONEN 0x00800000UL -#define CR0_FDXTFCEN 0x00400000UL /* full-duplex TX flow control enable */ -#define CR0_FDXRFCEN 0x00200000UL /* full-duplex RX flow control enable */ -#define CR0_HDXFCEN 0x00100000UL /* half-duplex flow control enable */ -#define CR0_XHITH1 0x00080000UL /* TX XON high threshold 1 */ -#define CR0_XHITH0 0x00040000UL /* TX XON high threshold 0 */ -#define CR0_XLTH1 0x00020000UL /* TX pause frame low threshold 1 */ -#define CR0_XLTH0 0x00010000UL /* TX pause frame low threshold 0 */ -#define CR0_GSPRST 0x80000000UL -#define CR0_FORSRST 0x40000000UL -#define CR0_FPHYRST 0x20000000UL -#define CR0_DIAG 0x10000000UL -#define CR0_INTPCTL 0x04000000UL -#define CR0_GINTMSK1 0x02000000UL -#define CR0_GINTMSK0 0x01000000UL - -/* - * Bits in the CR1 register - */ - -#define CR1_SFRST 0x80 /* software reset */ -#define CR1_TM1EN 0x40 -#define CR1_TM0EN 0x20 -#define CR1_DPOLL 0x08 /* disable rx/tx auto polling */ -#define CR1_DISAU 0x01 - -/* - * Bits in the CR2 register - */ - -#define CR2_XONEN 0x80 -#define CR2_FDXTFCEN 0x40 /* full-duplex TX flow control enable */ -#define CR2_FDXRFCEN 0x20 /* full-duplex RX flow control enable */ -#define CR2_HDXFCEN 0x10 /* half-duplex flow control enable */ -#define CR2_XHITH1 0x08 /* TX XON high threshold 1 */ -#define CR2_XHITH0 0x04 /* TX XON high threshold 0 */ -#define CR2_XLTH1 0x02 /* TX pause frame low threshold 1 */ -#define CR2_XLTH0 0x01 /* TX pause frame low threshold 0 */ - -/* - * Bits in the CR3 register - */ - -#define CR3_GSPRST 0x80 -#define CR3_FORSRST 0x40 -#define CR3_FPHYRST 0x20 -#define CR3_DIAG 0x10 -#define CR3_INTPCTL 0x04 -#define CR3_GINTMSK1 0x02 -#define CR3_GINTMSK0 0x01 - -#define ISRCTL_UDPINT 0x8000 -#define ISRCTL_TSUPDIS 0x4000 -#define ISRCTL_RSUPDIS 0x2000 -#define ISRCTL_PMSK1 0x1000 -#define ISRCTL_PMSK0 0x0800 -#define ISRCTL_INTPD 0x0400 -#define ISRCTL_HCRLD 0x0200 -#define ISRCTL_SCRLD 0x0100 - -/* - * Bits in the ISR_CTL1 register - */ - -#define ISRCTL1_UDPINT 0x80 -#define ISRCTL1_TSUPDIS 0x40 -#define ISRCTL1_RSUPDIS 0x20 -#define ISRCTL1_PMSK1 0x10 -#define ISRCTL1_PMSK0 0x08 -#define ISRCTL1_INTPD 0x04 -#define ISRCTL1_HCRLD 0x02 -#define ISRCTL1_SCRLD 0x01 - -/* - * Bits in the TXE_SR register - */ - -#define TXESR_TFDBS 0x08 -#define TXESR_TDWBS 0x04 -#define TXESR_TDRBS 0x02 -#define TXESR_TDSTR 0x01 - -/* - * Bits in the RXE_SR register - */ - -#define RXESR_RFDBS 0x08 -#define RXESR_RDWBS 0x04 -#define RXESR_RDRBS 0x02 -#define RXESR_RDSTR 0x01 - -/* - * Bits in the ISR register - */ - -#define ISR_ISR3 0x80000000UL -#define ISR_ISR2 0x40000000UL -#define ISR_ISR1 0x20000000UL -#define ISR_ISR0 0x10000000UL -#define ISR_TXSTLI 0x02000000UL -#define ISR_RXSTLI 0x01000000UL -#define ISR_HFLD 0x00800000UL -#define ISR_UDPI 0x00400000UL -#define ISR_MIBFI 0x00200000UL -#define ISR_SHDNI 0x00100000UL -#define ISR_PHYI 0x00080000UL -#define ISR_PWEI 0x00040000UL -#define ISR_TMR1I 0x00020000UL -#define ISR_TMR0I 0x00010000UL -#define ISR_SRCI 0x00008000UL -#define ISR_LSTPEI 0x00004000UL -#define ISR_LSTEI 0x00002000UL -#define ISR_OVFI 0x00001000UL -#define ISR_FLONI 0x00000800UL -#define ISR_RACEI 0x00000400UL -#define ISR_TXWB1I 0x00000200UL -#define ISR_TXWB0I 0x00000100UL -#define ISR_PTX3I 0x00000080UL -#define ISR_PTX2I 0x00000040UL -#define ISR_PTX1I 0x00000020UL -#define ISR_PTX0I 0x00000010UL -#define ISR_PTXI 0x00000008UL -#define ISR_PRXI 0x00000004UL -#define ISR_PPTXI 0x00000002UL -#define ISR_PPRXI 0x00000001UL - -/* - * Bits in the IMR register - */ - -#define IMR_TXSTLM 0x02000000UL -#define IMR_UDPIM 0x00400000UL -#define IMR_MIBFIM 0x00200000UL -#define IMR_SHDNIM 0x00100000UL -#define IMR_PHYIM 0x00080000UL -#define IMR_PWEIM 0x00040000UL -#define IMR_TMR1IM 0x00020000UL -#define IMR_TMR0IM 0x00010000UL - -#define IMR_SRCIM 0x00008000UL -#define IMR_LSTPEIM 0x00004000UL -#define IMR_LSTEIM 0x00002000UL -#define IMR_OVFIM 0x00001000UL -#define IMR_FLONIM 0x00000800UL -#define IMR_RACEIM 0x00000400UL -#define IMR_TXWB1IM 0x00000200UL -#define IMR_TXWB0IM 0x00000100UL - -#define IMR_PTX3IM 0x00000080UL -#define IMR_PTX2IM 0x00000040UL -#define IMR_PTX1IM 0x00000020UL -#define IMR_PTX0IM 0x00000010UL -#define IMR_PTXIM 0x00000008UL -#define IMR_PRXIM 0x00000004UL -#define IMR_PPTXIM 0x00000002UL -#define IMR_PPRXIM 0x00000001UL - -/* 0x0013FB0FUL = initial value of IMR */ - -#define INT_MASK_DEF ( IMR_PPTXIM|IMR_PPRXIM| IMR_PTXIM|IMR_PRXIM | \ - IMR_PWEIM|IMR_TXWB0IM|IMR_TXWB1IM|IMR_FLONIM| \ - IMR_OVFIM|IMR_LSTEIM|IMR_LSTPEIM|IMR_SRCIM|IMR_MIBFIM|\ - IMR_SHDNIM |IMR_TMR1IM|IMR_TMR0IM|IMR_TXSTLM ) - -/* - * Bits in the TDCSR0/1, RDCSR0 register - */ - -#define TRDCSR_DEAD 0x0008 -#define TRDCSR_WAK 0x0004 -#define TRDCSR_ACT 0x0002 -#define TRDCSR_RUN 0x0001 - -/* - * Bits in the CAMADDR register - */ - -#define CAMADDR_CAMEN 0x80 -#define CAMADDR_VCAMSL 0x40 - -/* - * Bits in the CAMCR register - */ - -#define CAMCR_PS1 0x80 -#define CAMCR_PS0 0x40 -#define CAMCR_AITRPKT 0x20 -#define CAMCR_AITR16 0x10 -#define CAMCR_CAMRD 0x08 -#define CAMCR_CAMWR 0x04 -#define CAMCR_PS_CAM_MASK 0x40 -#define CAMCR_PS_CAM_DATA 0x80 -#define CAMCR_PS_MAR 0x00 - -/* - * Bits in the MIICFG register - */ - -#define MIICFG_MPO1 0x80 -#define MIICFG_MPO0 0x40 -#define MIICFG_MFDC 0x20 - -/* - * Bits in the MIISR register - */ - -#define MIISR_MIDLE 0x80 - -/* - * Bits in the PHYSR0 register - */ - -#define PHYSR0_PHYRST 0x80 -#define PHYSR0_LINKGD 0x40 -#define PHYSR0_FDPX 0x10 -#define PHYSR0_SPDG 0x08 -#define PHYSR0_SPD10 0x04 -#define PHYSR0_RXFLC 0x02 -#define PHYSR0_TXFLC 0x01 - -/* - * Bits in the PHYSR1 register - */ - -#define PHYSR1_PHYTBI 0x01 - -/* - * Bits in the MIICR register - */ - -#define MIICR_MAUTO 0x80 -#define MIICR_RCMD 0x40 -#define MIICR_WCMD 0x20 -#define MIICR_MDPM 0x10 -#define MIICR_MOUT 0x08 -#define MIICR_MDO 0x04 -#define MIICR_MDI 0x02 -#define MIICR_MDC 0x01 - -/* - * Bits in the MIIADR register - */ - -#define MIIADR_SWMPL 0x80 - -/* - * Bits in the CFGA register - */ - -#define CFGA_PMHCTG 0x08 -#define CFGA_GPIO1PD 0x04 -#define CFGA_ABSHDN 0x02 -#define CFGA_PACPI 0x01 - -/* - * Bits in the CFGB register - */ - -#define CFGB_GTCKOPT 0x80 -#define CFGB_MIIOPT 0x40 -#define CFGB_CRSEOPT 0x20 -#define CFGB_OFSET 0x10 -#define CFGB_CRANDOM 0x08 -#define CFGB_CAP 0x04 -#define CFGB_MBA 0x02 -#define CFGB_BAKOPT 0x01 - -/* - * Bits in the CFGC register - */ - -#define CFGC_EELOAD 0x80 -#define CFGC_BROPT 0x40 -#define CFGC_DLYEN 0x20 -#define CFGC_DTSEL 0x10 -#define CFGC_BTSEL 0x08 -#define CFGC_BPS2 0x04 /* bootrom select[2] */ -#define CFGC_BPS1 0x02 /* bootrom select[1] */ -#define CFGC_BPS0 0x01 /* bootrom select[0] */ - -/* - * Bits in the CFGD register - */ - -#define CFGD_IODIS 0x80 -#define CFGD_MSLVDACEN 0x40 -#define CFGD_CFGDACEN 0x20 -#define CFGD_PCI64EN 0x10 -#define CFGD_HTMRL4 0x08 - -/* - * Bits in the DCFG1 register - */ - -#define DCFG_XMWI 0x8000 -#define DCFG_XMRM 0x4000 -#define DCFG_XMRL 0x2000 -#define DCFG_PERDIS 0x1000 -#define DCFG_MRWAIT 0x0400 -#define DCFG_MWWAIT 0x0200 -#define DCFG_LATMEN 0x0100 - -/* - * Bits in the MCFG0 register - */ - -#define MCFG_RXARB 0x0080 -#define MCFG_RFT1 0x0020 -#define MCFG_RFT0 0x0010 -#define MCFG_LOWTHOPT 0x0008 -#define MCFG_PQEN 0x0004 -#define MCFG_RTGOPT 0x0002 -#define MCFG_VIDFR 0x0001 - -/* - * Bits in the MCFG1 register - */ - -#define MCFG_TXARB 0x8000 -#define MCFG_TXQBK1 0x0800 -#define MCFG_TXQBK0 0x0400 -#define MCFG_TXQNOBK 0x0200 -#define MCFG_SNAPOPT 0x0100 - -/* - * Bits in the PMCC register - */ - -#define PMCC_DSI 0x80 -#define PMCC_D2_DIS 0x40 -#define PMCC_D1_DIS 0x20 -#define PMCC_D3C_EN 0x10 -#define PMCC_D3H_EN 0x08 -#define PMCC_D2_EN 0x04 -#define PMCC_D1_EN 0x02 -#define PMCC_D0_EN 0x01 - -/* - * Bits in STICKHW - */ - -#define STICKHW_SWPTAG 0x10 -#define STICKHW_WOLSR 0x08 -#define STICKHW_WOLEN 0x04 -#define STICKHW_DS1 0x02 /* R/W by software/cfg cycle */ -#define STICKHW_DS0 0x01 /* suspend well DS write port */ - -/* - * Bits in the MIBCR register - */ - -#define MIBCR_MIBISTOK 0x80 -#define MIBCR_MIBISTGO 0x40 -#define MIBCR_MIBINC 0x20 -#define MIBCR_MIBHI 0x10 -#define MIBCR_MIBFRZ 0x08 -#define MIBCR_MIBFLSH 0x04 -#define MIBCR_MPTRINI 0x02 -#define MIBCR_MIBCLR 0x01 - -/* - * Bits in the EERSV register - */ - -#define EERSV_BOOT_RPL ((u8) 0x01) /* Boot method selection for VT6110 */ - -#define EERSV_BOOT_MASK ((u8) 0x06) -#define EERSV_BOOT_INT19 ((u8) 0x00) -#define EERSV_BOOT_INT18 ((u8) 0x02) -#define EERSV_BOOT_LOCAL ((u8) 0x04) -#define EERSV_BOOT_BEV ((u8) 0x06) - - -/* - * Bits in BPCMD - */ - -#define BPCMD_BPDNE 0x80 -#define BPCMD_EBPWR 0x02 -#define BPCMD_EBPRD 0x01 - -/* - * Bits in the EECSR register - */ - -#define EECSR_EMBP 0x40 /* eeprom embedded programming */ -#define EECSR_RELOAD 0x20 /* eeprom content reload */ -#define EECSR_DPM 0x10 /* eeprom direct programming */ -#define EECSR_ECS 0x08 /* eeprom CS pin */ -#define EECSR_ECK 0x04 /* eeprom CK pin */ -#define EECSR_EDI 0x02 /* eeprom DI pin */ -#define EECSR_EDO 0x01 /* eeprom DO pin */ - -/* - * Bits in the EMBCMD register - */ - -#define EMBCMD_EDONE 0x80 -#define EMBCMD_EWDIS 0x08 -#define EMBCMD_EWEN 0x04 -#define EMBCMD_EWR 0x02 -#define EMBCMD_ERD 0x01 - -/* - * Bits in TESTCFG register - */ - -#define TESTCFG_HBDIS 0x80 - -/* - * Bits in CHIPGCR register - */ - -#define CHIPGCR_FCGMII 0x80 -#define CHIPGCR_FCFDX 0x40 -#define CHIPGCR_FCRESV 0x20 -#define CHIPGCR_FCMODE 0x10 -#define CHIPGCR_LPSOPT 0x08 -#define CHIPGCR_TM1US 0x04 -#define CHIPGCR_TM0US 0x02 -#define CHIPGCR_PHYINTEN 0x01 - -/* - * Bits in WOLCR0 - */ - -#define WOLCR_MSWOLEN7 0x0080 /* enable pattern match filtering */ -#define WOLCR_MSWOLEN6 0x0040 -#define WOLCR_MSWOLEN5 0x0020 -#define WOLCR_MSWOLEN4 0x0010 -#define WOLCR_MSWOLEN3 0x0008 -#define WOLCR_MSWOLEN2 0x0004 -#define WOLCR_MSWOLEN1 0x0002 -#define WOLCR_MSWOLEN0 0x0001 -#define WOLCR_ARP_EN 0x0001 - -/* - * Bits in WOLCR1 - */ - -#define WOLCR_LINKOFF_EN 0x0800 /* link off detected enable */ -#define WOLCR_LINKON_EN 0x0400 /* link on detected enable */ -#define WOLCR_MAGIC_EN 0x0200 /* magic packet filter enable */ -#define WOLCR_UNICAST_EN 0x0100 /* unicast filter enable */ - - -/* - * Bits in PWCFG - */ - -#define PWCFG_PHYPWOPT 0x80 /* internal MII I/F timing */ -#define PWCFG_PCISTICK 0x40 /* PCI sticky R/W enable */ -#define PWCFG_WOLTYPE 0x20 /* pulse(1) or button (0) */ -#define PWCFG_LEGCY_WOL 0x10 -#define PWCFG_PMCSR_PME_SR 0x08 -#define PWCFG_PMCSR_PME_EN 0x04 /* control by PCISTICK */ -#define PWCFG_LEGACY_WOLSR 0x02 /* Legacy WOL_SR shadow */ -#define PWCFG_LEGACY_WOLEN 0x01 /* Legacy WOL_EN shadow */ - -/* - * Bits in WOLCFG - */ - -#define WOLCFG_PMEOVR 0x80 /* for legacy use, force PMEEN always */ -#define WOLCFG_SAM 0x20 /* accept multicast case reset, default=0 */ -#define WOLCFG_SAB 0x10 /* accept broadcast case reset, default=0 */ -#define WOLCFG_SMIIACC 0x08 /* ?? */ -#define WOLCFG_SGENWH 0x02 -#define WOLCFG_PHYINTEN 0x01 /* 0:PHYINT trigger enable, 1:use internal MII - to report status change */ -/* - * Bits in WOLSR1 - */ - -#define WOLSR_LINKOFF_INT 0x0800 -#define WOLSR_LINKON_INT 0x0400 -#define WOLSR_MAGIC_INT 0x0200 -#define WOLSR_UNICAST_INT 0x0100 - -/* - * Ethernet address filter type - */ - -#define PKT_TYPE_NONE 0x0000 /* Turn off receiver */ -#define PKT_TYPE_DIRECTED 0x0001 /* obselete, directed address is always accepted */ -#define PKT_TYPE_MULTICAST 0x0002 -#define PKT_TYPE_ALL_MULTICAST 0x0004 -#define PKT_TYPE_BROADCAST 0x0008 -#define PKT_TYPE_PROMISCUOUS 0x0020 -#define PKT_TYPE_LONG 0x2000 /* NOTE.... the definition of LONG is >2048 bytes in our chip */ -#define PKT_TYPE_RUNT 0x4000 -#define PKT_TYPE_ERROR 0x8000 /* Accept error packets, e.g. CRC error */ - -/* - * Loopback mode - */ - -#define MAC_LB_NONE 0x00 -#define MAC_LB_INTERNAL 0x01 -#define MAC_LB_EXTERNAL 0x02 - -/* - * Enabled mask value of irq - */ - -#if defined(_SIM) -#define IMR_MASK_VALUE 0x0033FF0FUL /* initial value of IMR - set IMR0 to 0x0F according to spec */ - -#else -#define IMR_MASK_VALUE 0x0013FB0FUL /* initial value of IMR - ignore MIBFI,RACEI to - reduce intr. frequency - NOTE.... do not enable NoBuf int mask at driver driver - when (1) NoBuf -> RxThreshold = SF - (2) OK -> RxThreshold = original value - */ -#endif - -/* - * Revision id - */ - -#define REV_ID_VT3119_A0 0x00 -#define REV_ID_VT3119_A1 0x01 -#define REV_ID_VT3216_A0 0x10 - -/* - * Max time out delay time - */ - -#define W_MAX_TIMEOUT 0x0FFFU - - -/* - * MAC registers as a structure. Cannot be directly accessed this - * way but generates offsets for readl/writel() calls - */ - -struct mac_regs { - volatile u8 PAR[6]; /* 0x00 */ - volatile u8 RCR; - volatile u8 TCR; - - volatile u32 CR0Set; /* 0x08 */ - volatile u32 CR0Clr; /* 0x0C */ - - volatile u8 MARCAM[8]; /* 0x10 */ - - volatile u32 DecBaseHi; /* 0x18 */ - volatile u16 DbfBaseHi; /* 0x1C */ - volatile u16 reserved_1E; - - volatile u16 ISRCTL; /* 0x20 */ - volatile u8 TXESR; - volatile u8 RXESR; - - volatile u32 ISR; /* 0x24 */ - volatile u32 IMR; - - volatile u32 TDStatusPort; /* 0x2C */ - - volatile u16 TDCSRSet; /* 0x30 */ - volatile u8 RDCSRSet; - volatile u8 reserved_33; - volatile u16 TDCSRClr; - volatile u8 RDCSRClr; - volatile u8 reserved_37; - - volatile u32 RDBaseLo; /* 0x38 */ - volatile u16 RDIdx; /* 0x3C */ - volatile u16 reserved_3E; - - volatile u32 TDBaseLo[4]; /* 0x40 */ - - volatile u16 RDCSize; /* 0x50 */ - volatile u16 TDCSize; /* 0x52 */ - volatile u16 TDIdx[4]; /* 0x54 */ - volatile u16 tx_pause_timer; /* 0x5C */ - volatile u16 RBRDU; /* 0x5E */ - - volatile u32 FIFOTest0; /* 0x60 */ - volatile u32 FIFOTest1; /* 0x64 */ - - volatile u8 CAMADDR; /* 0x68 */ - volatile u8 CAMCR; /* 0x69 */ - volatile u8 GFTEST; /* 0x6A */ - volatile u8 FTSTCMD; /* 0x6B */ - - volatile u8 MIICFG; /* 0x6C */ - volatile u8 MIISR; - volatile u8 PHYSR0; - volatile u8 PHYSR1; - volatile u8 MIICR; - volatile u8 MIIADR; - volatile u16 MIIDATA; - - volatile u16 SoftTimer0; /* 0x74 */ - volatile u16 SoftTimer1; - - volatile u8 CFGA; /* 0x78 */ - volatile u8 CFGB; - volatile u8 CFGC; - volatile u8 CFGD; - - volatile u16 DCFG; /* 0x7C */ - volatile u16 MCFG; - - volatile u8 TBIST; /* 0x80 */ - volatile u8 RBIST; - volatile u8 PMCPORT; - volatile u8 STICKHW; - - volatile u8 MIBCR; /* 0x84 */ - volatile u8 reserved_85; - volatile u8 rev_id; - volatile u8 PORSTS; - - volatile u32 MIBData; /* 0x88 */ - - volatile u16 EEWrData; - - volatile u8 reserved_8E; - volatile u8 BPMDWr; - volatile u8 BPCMD; - volatile u8 BPMDRd; - - volatile u8 EECHKSUM; /* 0x92 */ - volatile u8 EECSR; - - volatile u16 EERdData; /* 0x94 */ - volatile u8 EADDR; - volatile u8 EMBCMD; - - - volatile u8 JMPSR0; /* 0x98 */ - volatile u8 JMPSR1; - volatile u8 JMPSR2; - volatile u8 JMPSR3; - volatile u8 CHIPGSR; /* 0x9C */ - volatile u8 TESTCFG; - volatile u8 DEBUG; - volatile u8 CHIPGCR; - - volatile u16 WOLCRSet; /* 0xA0 */ - volatile u8 PWCFGSet; - volatile u8 WOLCFGSet; - - volatile u16 WOLCRClr; /* 0xA4 */ - volatile u8 PWCFGCLR; - volatile u8 WOLCFGClr; - - volatile u16 WOLSRSet; /* 0xA8 */ - volatile u16 reserved_AA; - - volatile u16 WOLSRClr; /* 0xAC */ - volatile u16 reserved_AE; - - volatile u16 PatternCRC[8]; /* 0xB0 */ - volatile u32 ByteMask[4][4]; /* 0xC0 */ -} __attribute__ ((__packed__)); - - -enum hw_mib { - HW_MIB_ifRxAllPkts = 0, - HW_MIB_ifRxOkPkts, - HW_MIB_ifTxOkPkts, - HW_MIB_ifRxErrorPkts, - HW_MIB_ifRxRuntOkPkt, - HW_MIB_ifRxRuntErrPkt, - HW_MIB_ifRx64Pkts, - HW_MIB_ifTx64Pkts, - HW_MIB_ifRx65To127Pkts, - HW_MIB_ifTx65To127Pkts, - HW_MIB_ifRx128To255Pkts, - HW_MIB_ifTx128To255Pkts, - HW_MIB_ifRx256To511Pkts, - HW_MIB_ifTx256To511Pkts, - HW_MIB_ifRx512To1023Pkts, - HW_MIB_ifTx512To1023Pkts, - HW_MIB_ifRx1024To1518Pkts, - HW_MIB_ifTx1024To1518Pkts, - HW_MIB_ifTxEtherCollisions, - HW_MIB_ifRxPktCRCE, - HW_MIB_ifRxJumboPkts, - HW_MIB_ifTxJumboPkts, - HW_MIB_ifRxMacControlFrames, - HW_MIB_ifTxMacControlFrames, - HW_MIB_ifRxPktFAE, - HW_MIB_ifRxLongOkPkt, - HW_MIB_ifRxLongPktErrPkt, - HW_MIB_ifTXSQEErrors, - HW_MIB_ifRxNobuf, - HW_MIB_ifRxSymbolErrors, - HW_MIB_ifInRangeLengthErrors, - HW_MIB_ifLateCollisions, - HW_MIB_SIZE -}; - -enum chip_type { - CHIP_TYPE_VT6110 = 1, -}; - -struct velocity_info_tbl { - enum chip_type chip_id; - char *name; - int io_size; - int txqueue; - u32 flags; -}; - -static struct velocity_info_tbl *info; - -#define mac_hw_mibs_init(regs) {\ - BYTE_REG_BITS_ON(MIBCR_MIBFRZ,&((regs)->MIBCR));\ - BYTE_REG_BITS_ON(MIBCR_MIBCLR,&((regs)->MIBCR));\ - do {}\ - while (BYTE_REG_BITS_IS_ON(MIBCR_MIBCLR,&((regs)->MIBCR)));\ - BYTE_REG_BITS_OFF(MIBCR_MIBFRZ,&((regs)->MIBCR));\ -} - -#define mac_read_isr(regs) readl(&((regs)->ISR)) -#define mac_write_isr(regs, x) writel((x),&((regs)->ISR)) -#define mac_clear_isr(regs) writel(0xffffffffL,&((regs)->ISR)) - -#define mac_write_int_mask(mask, regs) writel((mask),&((regs)->IMR)); -#define mac_disable_int(regs) writel(CR0_GINTMSK1,&((regs)->CR0Clr)) -#define mac_enable_int(regs) writel(CR0_GINTMSK1,&((regs)->CR0Set)) - -#define mac_hw_mibs_read(regs, MIBs) {\ - int i;\ - BYTE_REG_BITS_ON(MIBCR_MPTRINI,&((regs)->MIBCR));\ - for (i=0;iMIBData));\ - }\ -} - -#define mac_set_dma_length(regs, n) {\ - BYTE_REG_BITS_SET((n),0x07,&((regs)->DCFG));\ -} - -#define mac_set_rx_thresh(regs, n) {\ - BYTE_REG_BITS_SET((n),(MCFG_RFT0|MCFG_RFT1),&((regs)->MCFG));\ -} - -#define mac_rx_queue_run(regs) {\ - writeb(TRDCSR_RUN, &((regs)->RDCSRSet));\ -} - -#define mac_rx_queue_wake(regs) {\ - writeb(TRDCSR_WAK, &((regs)->RDCSRSet));\ -} - -#define mac_tx_queue_run(regs, n) {\ - writew(TRDCSR_RUN<<((n)*4),&((regs)->TDCSRSet));\ -} - -#define mac_tx_queue_wake(regs, n) {\ - writew(TRDCSR_WAK<<(n*4),&((regs)->TDCSRSet));\ -} - -#define mac_eeprom_reload(regs) {\ - int i=0;\ - BYTE_REG_BITS_ON(EECSR_RELOAD,&((regs)->EECSR));\ - do {\ - udelay(10);\ - if (i++>0x1000) {\ - break;\ - }\ - }while (BYTE_REG_BITS_IS_ON(EECSR_RELOAD,&((regs)->EECSR)));\ -} - -enum velocity_cam_type { - VELOCITY_VLAN_ID_CAM = 0, - VELOCITY_MULTICAST_CAM -}; - -/** - * mac_get_cam_mask - Read a CAM mask - * @regs: register block for this velocity - * @mask: buffer to store mask - * @cam_type: CAM to fetch - * - * Fetch the mask bits of the selected CAM and store them into the - * provided mask buffer. - */ - -static inline void mac_get_cam_mask(struct mac_regs *regs, u8 * mask, - enum velocity_cam_type cam_type) -{ - int i; - /* Select CAM mask */ - BYTE_REG_BITS_SET(CAMCR_PS_CAM_MASK, CAMCR_PS1 | CAMCR_PS0, - ®s->CAMCR); - - if (cam_type == VELOCITY_VLAN_ID_CAM) - writeb(CAMADDR_VCAMSL, ®s->CAMADDR); - else - writeb(0, ®s->CAMADDR); - - /* read mask */ - for (i = 0; i < 8; i++) - *mask++ = readb(&(regs->MARCAM[i])); - - /* disable CAMEN */ - writeb(0, ®s->CAMADDR); - - /* Select mar */ - BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, - ®s->CAMCR); - -} - -/** - * mac_set_cam_mask - Set a CAM mask - * @regs: register block for this velocity - * @mask: CAM mask to load - * @cam_type: CAM to store - * - * Store a new mask into a CAM - */ - -static inline void mac_set_cam_mask(struct mac_regs *regs, u8 * mask, - enum velocity_cam_type cam_type) -{ - int i; - /* Select CAM mask */ - BYTE_REG_BITS_SET(CAMCR_PS_CAM_MASK, CAMCR_PS1 | CAMCR_PS0, - ®s->CAMCR); - - if (cam_type == VELOCITY_VLAN_ID_CAM) - writeb(CAMADDR_CAMEN | CAMADDR_VCAMSL, ®s->CAMADDR); - else - writeb(CAMADDR_CAMEN, ®s->CAMADDR); - - for (i = 0; i < 8; i++) { - writeb(*mask++, &(regs->MARCAM[i])); - } - /* disable CAMEN */ - writeb(0, ®s->CAMADDR); - - /* Select mar */ - BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, - ®s->CAMCR); -} - -/** - * mac_set_cam - set CAM data - * @regs: register block of this velocity - * @idx: Cam index - * @addr: 2 or 6 bytes of CAM data - * @cam_type: CAM to load - * - * Load an address or vlan tag into a CAM - */ - -static inline void mac_set_cam(struct mac_regs *regs, int idx, u8 * addr, - enum velocity_cam_type cam_type) -{ - int i; - - /* Select CAM mask */ - BYTE_REG_BITS_SET(CAMCR_PS_CAM_DATA, CAMCR_PS1 | CAMCR_PS0, - ®s->CAMCR); - - idx &= (64 - 1); - - if (cam_type == VELOCITY_VLAN_ID_CAM) - writeb(CAMADDR_CAMEN | CAMADDR_VCAMSL | idx, - ®s->CAMADDR); - else - writeb(CAMADDR_CAMEN | idx, ®s->CAMADDR); - - if (cam_type == VELOCITY_VLAN_ID_CAM) - writew(*((u16 *) addr), ®s->MARCAM[0]); - else { - for (i = 0; i < 6; i++) { - writeb(*addr++, &(regs->MARCAM[i])); - } - } - BYTE_REG_BITS_ON(CAMCR_CAMWR, ®s->CAMCR); - - udelay(10); - - writeb(0, ®s->CAMADDR); - - /* Select mar */ - BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, - ®s->CAMCR); -} - -/** - * mac_get_cam - fetch CAM data - * @regs: register block of this velocity - * @idx: Cam index - * @addr: buffer to hold up to 6 bytes of CAM data - * @cam_type: CAM to load - * - * Load an address or vlan tag from a CAM into the buffer provided by - * the caller. VLAN tags are 2 bytes the address cam entries are 6. - */ - -static inline void mac_get_cam(struct mac_regs *regs, int idx, u8 * addr, - enum velocity_cam_type cam_type) -{ - int i; - - /* Select CAM mask */ - BYTE_REG_BITS_SET(CAMCR_PS_CAM_DATA, CAMCR_PS1 | CAMCR_PS0, - ®s->CAMCR); - - idx &= (64 - 1); - - if (cam_type == VELOCITY_VLAN_ID_CAM) - writeb(CAMADDR_CAMEN | CAMADDR_VCAMSL | idx, - ®s->CAMADDR); - else - writeb(CAMADDR_CAMEN | idx, ®s->CAMADDR); - - BYTE_REG_BITS_ON(CAMCR_CAMRD, ®s->CAMCR); - - udelay(10); - - if (cam_type == VELOCITY_VLAN_ID_CAM) - *((u16 *) addr) = readw(&(regs->MARCAM[0])); - else - for (i = 0; i < 6; i++, addr++) - *((u8 *) addr) = readb(&(regs->MARCAM[i])); - - writeb(0, ®s->CAMADDR); - - /* Select mar */ - BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, - ®s->CAMCR); -} - -/** - * mac_wol_reset - reset WOL after exiting low power - * @regs: register block of this velocity - * - * Called after we drop out of wake on lan mode in order to - * reset the Wake on lan features. This function doesn't restore - * the rest of the logic from the result of sleep/wakeup - */ - -inline static void mac_wol_reset(struct mac_regs *regs) -{ - - /* Turn off SWPTAG right after leaving power mode */ - BYTE_REG_BITS_OFF(STICKHW_SWPTAG, ®s->STICKHW); - /* clear sticky bits */ - BYTE_REG_BITS_OFF((STICKHW_DS1 | STICKHW_DS0), ®s->STICKHW); - - BYTE_REG_BITS_OFF(CHIPGCR_FCGMII, ®s->CHIPGCR); - BYTE_REG_BITS_OFF(CHIPGCR_FCMODE, ®s->CHIPGCR); - /* disable force PME-enable */ - writeb(WOLCFG_PMEOVR, ®s->WOLCFGClr); - /* disable power-event config bit */ - writew(0xFFFF, ®s->WOLCRClr); - /* clear power status */ - writew(0xFFFF, ®s->WOLSRClr); -} - - -/* - * Header for WOL definitions. Used to compute hashes - */ - -typedef u8 MCAM_ADDR[ETH_ALEN]; - -struct arp_packet { - u8 dest_mac[ETH_ALEN]; - u8 src_mac[ETH_ALEN]; - u16 type; - u16 ar_hrd; - u16 ar_pro; - u8 ar_hln; - u8 ar_pln; - u16 ar_op; - u8 ar_sha[ETH_ALEN]; - u8 ar_sip[4]; - u8 ar_tha[ETH_ALEN]; - u8 ar_tip[4]; -} __attribute__ ((__packed__)); - -struct _magic_packet { - u8 dest_mac[6]; - u8 src_mac[6]; - u16 type; - u8 MAC[16][6]; - u8 password[6]; -} __attribute__ ((__packed__)); - -/* - * Store for chip context when saving and restoring status. Not - * all fields are saved/restored currently. - */ - -struct velocity_context { - u8 mac_reg[256]; - MCAM_ADDR cam_addr[MCAM_SIZE]; - u16 vcam[VCAM_SIZE]; - u32 cammask[2]; - u32 patcrc[2]; - u32 pattern[8]; -}; - - -/* - * MII registers. - */ - - -/* - * Registers in the MII (offset unit is WORD) - */ - -#define MII_REG_BMCR 0x00 // physical address -#define MII_REG_BMSR 0x01 // -#define MII_REG_PHYID1 0x02 // OUI -#define MII_REG_PHYID2 0x03 // OUI + Module ID + REV ID -#define MII_REG_ANAR 0x04 // -#define MII_REG_ANLPAR 0x05 // -#define MII_REG_G1000CR 0x09 // -#define MII_REG_G1000SR 0x0A // -#define MII_REG_MODCFG 0x10 // -#define MII_REG_TCSR 0x16 // -#define MII_REG_PLED 0x1B // -// NS, MYSON only -#define MII_REG_PCR 0x17 // -// ESI only -#define MII_REG_PCSR 0x17 // -#define MII_REG_AUXCR 0x1C // - -// Marvell 88E1000/88E1000S -#define MII_REG_PSCR 0x10 // PHY specific control register - -// -// Bits in the BMCR register -// -#define BMCR_RESET 0x8000 // -#define BMCR_LBK 0x4000 // -#define BMCR_SPEED100 0x2000 // -#define BMCR_AUTO 0x1000 // -#define BMCR_PD 0x0800 // -#define BMCR_ISO 0x0400 // -#define BMCR_REAUTO 0x0200 // -#define BMCR_FDX 0x0100 // -#define BMCR_SPEED1G 0x0040 // -// -// Bits in the BMSR register -// -#define BMSR_AUTOCM 0x0020 // -#define BMSR_LNK 0x0004 // - -// -// Bits in the ANAR register -// -#define ANAR_ASMDIR 0x0800 // Asymmetric PAUSE support -#define ANAR_PAUSE 0x0400 // Symmetric PAUSE Support -#define ANAR_T4 0x0200 // -#define ANAR_TXFD 0x0100 // -#define ANAR_TX 0x0080 // -#define ANAR_10FD 0x0040 // -#define ANAR_10 0x0020 // -// -// Bits in the ANLPAR register -// -#define ANLPAR_ASMDIR 0x0800 // Asymmetric PAUSE support -#define ANLPAR_PAUSE 0x0400 // Symmetric PAUSE Support -#define ANLPAR_T4 0x0200 // -#define ANLPAR_TXFD 0x0100 // -#define ANLPAR_TX 0x0080 // -#define ANLPAR_10FD 0x0040 // -#define ANLPAR_10 0x0020 // - -// -// Bits in the G1000CR register -// -#define G1000CR_1000FD 0x0200 // PHY is 1000-T Full-duplex capable -#define G1000CR_1000 0x0100 // PHY is 1000-T Half-duplex capable - -// -// Bits in the G1000SR register -// -#define G1000SR_1000FD 0x0800 // LP PHY is 1000-T Full-duplex capable -#define G1000SR_1000 0x0400 // LP PHY is 1000-T Half-duplex capable - -#define TCSR_ECHODIS 0x2000 // -#define AUXCR_MDPPS 0x0004 // - -// Bits in the PLED register -#define PLED_LALBE 0x0004 // - -// Marvell 88E1000/88E1000S Bits in the PHY specific control register (10h) -#define PSCR_ACRSTX 0x0800 // Assert CRS on Transmit - -#define PHYID_CICADA_CS8201 0x000FC410UL -#define PHYID_VT3216_32BIT 0x000FC610UL -#define PHYID_VT3216_64BIT 0x000FC600UL -#define PHYID_MARVELL_1000 0x01410C50UL -#define PHYID_MARVELL_1000S 0x01410C40UL - -#define PHYID_REV_ID_MASK 0x0000000FUL - -#define PHYID_GET_PHY_REV_ID(i) ((i) & PHYID_REV_ID_MASK) -#define PHYID_GET_PHY_ID(i) ((i) & ~PHYID_REV_ID_MASK) - -#define MII_REG_BITS_ON(x,i,p) do {\ - u16 w;\ - velocity_mii_read((p),(i),&(w));\ - (w)|=(x);\ - velocity_mii_write((p),(i),(w));\ -} while (0) - -#define MII_REG_BITS_OFF(x,i,p) do {\ - u16 w;\ - velocity_mii_read((p),(i),&(w));\ - (w)&=(~(x));\ - velocity_mii_write((p),(i),(w));\ -} while (0) - -#define MII_REG_BITS_IS_ON(x,i,p) ({\ - u16 w;\ - velocity_mii_read((p),(i),&(w));\ - ((int) ((w) & (x)));}) - -#define MII_GET_PHY_ID(p) ({\ - u32 id; \ - u16 id2; \ - u16 id1; \ - velocity_mii_read((p),MII_REG_PHYID2, &id2);\ - velocity_mii_read((p),MII_REG_PHYID1, &id1);\ - id = ( ( (u32)id2 ) << 16 ) | id1; \ - (id);}) - -#ifdef LINUX -/* - * Inline debug routine - */ - - -enum velocity_msg_level { - MSG_LEVEL_ERR = 0, //Errors that will cause abnormal operation. - MSG_LEVEL_NOTICE = 1, //Some errors need users to be notified. - MSG_LEVEL_INFO = 2, //Normal message. - MSG_LEVEL_VERBOSE = 3, //Will report all trival errors. - MSG_LEVEL_DEBUG = 4 //Only for debug purpose. -}; - -#ifdef VELOCITY_DEBUG -#define ASSERT(x) { \ - if (!(x)) { \ - printk(KERN_ERR "assertion %s failed: file %s line %d\n", #x,\ - __FUNCTION__, __LINE__);\ - BUG(); \ - }\ -} -#define VELOCITY_DBG(p,args...) printk(p, ##args) -#else -#define ASSERT(x) -#define VELOCITY_DBG(x) -#endif - -#define VELOCITY_PRT(l, p, args...) do {if (l<=msglevel) printf( p ,##args);} while (0) - -#define VELOCITY_PRT_CAMMASK(p,t) {\ - int i;\ - if ((t)==VELOCITY_MULTICAST_CAM) {\ - for (i=0;i<(MCAM_SIZE/8);i++)\ - printk("%02X",(p)->mCAMmask[i]);\ - }\ - else {\ - for (i=0;i<(VCAM_SIZE/8);i++)\ - printk("%02X",(p)->vCAMmask[i]);\ - }\ - printk("\n");\ -} - -#endif - -#define VELOCITY_WOL_MAGIC 0x00000000UL -#define VELOCITY_WOL_PHY 0x00000001UL -#define VELOCITY_WOL_ARP 0x00000002UL -#define VELOCITY_WOL_UCAST 0x00000004UL -#define VELOCITY_WOL_BCAST 0x00000010UL -#define VELOCITY_WOL_MCAST 0x00000020UL -#define VELOCITY_WOL_MAGIC_SEC 0x00000040UL - -/* - * Flags for options - */ - -#define VELOCITY_FLAGS_TAGGING 0x00000001UL -#define VELOCITY_FLAGS_TX_CSUM 0x00000002UL -#define VELOCITY_FLAGS_RX_CSUM 0x00000004UL -#define VELOCITY_FLAGS_IP_ALIGN 0x00000008UL -#define VELOCITY_FLAGS_VAL_PKT_LEN 0x00000010UL - -#define VELOCITY_FLAGS_FLOW_CTRL 0x01000000UL - -/* - * Flags for driver status - */ - -#define VELOCITY_FLAGS_OPENED 0x00010000UL -#define VELOCITY_FLAGS_VMNS_CONNECTED 0x00020000UL -#define VELOCITY_FLAGS_VMNS_COMMITTED 0x00040000UL -#define VELOCITY_FLAGS_WOL_ENABLED 0x00080000UL - -/* - * Flags for MII status - */ - -#define VELOCITY_LINK_FAIL 0x00000001UL -#define VELOCITY_SPEED_10 0x00000002UL -#define VELOCITY_SPEED_100 0x00000004UL -#define VELOCITY_SPEED_1000 0x00000008UL -#define VELOCITY_DUPLEX_FULL 0x00000010UL -#define VELOCITY_AUTONEG_ENABLE 0x00000020UL -#define VELOCITY_FORCED_BY_EEPROM 0x00000040UL - -/* - * For velocity_set_media_duplex - */ - -#define VELOCITY_LINK_CHANGE 0x00000001UL - -enum speed_opt { - SPD_DPX_AUTO = 0, - SPD_DPX_100_HALF = 1, - SPD_DPX_100_FULL = 2, - SPD_DPX_10_HALF = 3, - SPD_DPX_10_FULL = 4 -}; - -enum velocity_init_type { - VELOCITY_INIT_COLD = 0, - VELOCITY_INIT_RESET, - VELOCITY_INIT_WOL -}; - -enum velocity_flow_cntl_type { - FLOW_CNTL_DEFAULT = 1, - FLOW_CNTL_TX, - FLOW_CNTL_RX, - FLOW_CNTL_TX_RX, - FLOW_CNTL_DISABLE, -}; - -struct velocity_opt { - int numrx; /* Number of RX descriptors */ - int numtx; /* Number of TX descriptors */ - enum speed_opt spd_dpx; /* Media link mode */ - int vid; /* vlan id */ - int DMA_length; /* DMA length */ - int rx_thresh; /* RX_THRESH */ - int flow_cntl; - int wol_opts; /* Wake on lan options */ - int td_int_count; - int int_works; - int rx_bandwidth_hi; - int rx_bandwidth_lo; - int rx_bandwidth_en; - u32 flags; -}; - -#define RX_DESC_MIN 4 -#define RX_DESC_MAX 255 -#define RX_DESC_DEF RX_DESC_MIN - -#define TX_DESC_MIN 1 -#define TX_DESC_MAX 256 -#define TX_DESC_DEF TX_DESC_MIN - -static struct velocity_info { -// struct list_head list; - - struct pci_device *pdev; -// struct net_device *dev; -// struct net_device_stats stats; - -#ifdef CONFIG_PM - u32 pci_state[16]; -#endif - -// dma_addr_t rd_pool_dma; -// dma_addr_t td_pool_dma[TX_QUEUE_NO]; - -// dma_addr_t tx_bufs_dma; - u8 *tx_bufs; - - u8 ip_addr[4]; - enum chip_type chip_id; - - struct mac_regs *mac_regs; - unsigned long memaddr; - unsigned long ioaddr; - u32 io_size; - - u8 rev_id; - -#define AVAIL_TD(p,q) ((p)->options.numtx-((p)->td_used[(q)])) - - int num_txq; - - volatile int td_used[TX_QUEUE_NO]; - int td_curr; - int td_tail[TX_QUEUE_NO]; - unsigned char *TxDescArrays; /* Index of Tx Descriptor buffer */ - unsigned char *RxDescArrays; /* Index of Rx Descriptor buffer */ - unsigned char *tx_buffs; - unsigned char *rx_buffs; - - unsigned char *txb; - unsigned char *rxb; - struct tx_desc *td_rings; - struct velocity_td_info *td_infos[TX_QUEUE_NO]; - - int rd_curr; - int rd_dirty; - u32 rd_filled; - struct rx_desc *rd_ring; - struct velocity_rd_info *rd_info; /* It's an array */ - -#define GET_RD_BY_IDX(vptr, idx) (vptr->rd_ring[idx]) - u32 mib_counter[MAX_HW_MIB_COUNTER]; - struct velocity_opt options; - - u32 int_mask; - - u32 flags; - - int rx_buf_sz; - u32 mii_status; - u32 phy_id; - int multicast_limit; - - u8 vCAMmask[(VCAM_SIZE / 8)]; - u8 mCAMmask[(MCAM_SIZE / 8)]; - -// spinlock_t lock; - - int wol_opts; - u8 wol_passwd[6]; - - struct velocity_context context; - - u32 ticks; - u32 rx_bytes; - -} vptx; - -static struct velocity_info *vptr; - -#ifdef LINUX -/** - * velocity_get_ip - find an IP address for the device - * @vptr: Velocity to query - * - * Dig out an IP address for this interface so that we can - * configure wakeup with WOL for ARP. If there are multiple IP - * addresses on this chain then we use the first - multi-IP WOL is not - * supported. - * - * CHECK ME: locking - */ - -inline static int velocity_get_ip(struct velocity_info *vptr) -{ - struct in_device *in_dev = (struct in_device *) vptr->dev->ip_ptr; - struct in_ifaddr *ifa; - - if (in_dev != NULL) { - ifa = (struct in_ifaddr *) in_dev->ifa_list; - if (ifa != NULL) { - memcpy(vptr->ip_addr, &ifa->ifa_address, 4); - return 0; - } - } - return -ENOENT; -} - -/** - * velocity_update_hw_mibs - fetch MIB counters from chip - * @vptr: velocity to update - * - * The velocity hardware keeps certain counters in the hardware - * side. We need to read these when the user asks for statistics - * or when they overflow (causing an interrupt). The read of the - * statistic clears it, so we keep running master counters in user - * space. - */ - -static inline void velocity_update_hw_mibs(struct velocity_info *vptr) -{ - u32 tmp; - int i; - BYTE_REG_BITS_ON(MIBCR_MIBFLSH, &(vptr->mac_regs->MIBCR)); - - while (BYTE_REG_BITS_IS_ON - (MIBCR_MIBFLSH, &(vptr->mac_regs->MIBCR))); - - BYTE_REG_BITS_ON(MIBCR_MPTRINI, &(vptr->mac_regs->MIBCR)); - for (i = 0; i < HW_MIB_SIZE; i++) { - tmp = readl(&(vptr->mac_regs->MIBData)) & 0x00FFFFFFUL; - vptr->mib_counter[i] += tmp; - } -} -#endif -/** - * init_flow_control_register - set up flow control - * @vptr: velocity to configure - * - * Configure the flow control registers for this velocity device. - */ - -static inline void init_flow_control_register(struct velocity_info *vptr) -{ - struct mac_regs *regs = vptr->mac_regs; - - /* Set {XHITH1, XHITH0, XLTH1, XLTH0} in FlowCR1 to {1, 0, 1, 1} - depend on RD=64, and Turn on XNOEN in FlowCR1 */ - writel((CR0_XONEN | CR0_XHITH1 | CR0_XLTH1 | CR0_XLTH0), - ®s->CR0Set); - writel((CR0_FDXTFCEN | CR0_FDXRFCEN | CR0_HDXFCEN | CR0_XHITH0), - ®s->CR0Clr); - - /* Set TxPauseTimer to 0xFFFF */ - writew(0xFFFF, ®s->tx_pause_timer); - - /* Initialize RBRDU to Rx buffer count. */ - writew(vptr->options.numrx, ®s->RBRDU); -} - - -#endif diff --git a/src/hci/commands/config_cmd.c b/src/hci/commands/config_cmd.c index f1fb567c..b81c866f 100644 --- a/src/hci/commands/config_cmd.c +++ b/src/hci/commands/config_cmd.c @@ -44,28 +44,6 @@ static struct option_descriptor config_opts[] = {}; static struct command_descriptor config_cmd = COMMAND_DESC ( struct config_options, config_opts, 0, 1, "[]" ); -/** - * Parse settings scope name - * - * @v text Text - * @ret value Integer value - * @ret rc Return status code - */ -static int parse_settings ( const 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; -} - /** * "config" command * diff --git a/src/hci/commands/fcmgmt_cmd.c b/src/hci/commands/fcmgmt_cmd.c index b7e38040..99f76113 100644 --- a/src/hci/commands/fcmgmt_cmd.c +++ b/src/hci/commands/fcmgmt_cmd.c @@ -43,7 +43,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); * @ret port Fibre Channel port * @ret rc Return status code */ -static int parse_fc_port ( const char *text, struct fc_port **port ) { +static int parse_fc_port ( char *text, struct fc_port **port ) { /* Sanity check */ assert ( text != NULL ); @@ -65,7 +65,7 @@ static int parse_fc_port ( const char *text, struct fc_port **port ) { * @ret port_id Fibre Channel port ID * @ret rc Return status code */ -static int parse_fc_port_id ( const char *text, struct fc_port_id *port_id ) { +static int parse_fc_port_id ( char *text, struct fc_port_id *port_id ) { int rc; /* Sanity check */ @@ -87,8 +87,7 @@ static int parse_fc_port_id ( const char *text, struct fc_port_id *port_id ) { * @ret handler Fibre Channel ELS handler * @ret rc Return status code */ -static int parse_fc_els_handler ( const char *text, - struct fc_els_handler **handler ) { +static int parse_fc_els_handler ( char *text, struct fc_els_handler **handler ){ for_each_table_entry ( (*handler), FC_ELS_HANDLERS ) { if ( strcasecmp ( (*handler)->name, text ) == 0 ) diff --git a/src/hci/commands/ifmgmt_cmd.c b/src/hci/commands/ifmgmt_cmd.c index 3f3f6b51..350f14d0 100644 --- a/src/hci/commands/ifmgmt_cmd.c +++ b/src/hci/commands/ifmgmt_cmd.c @@ -53,6 +53,7 @@ int ifcommon_exec ( int argc, char **argv, int stop_on_first_success ) { struct ifcommon_options opts; struct net_device *netdev; + int i; int rc; /* Parse options */ @@ -61,11 +62,9 @@ int ifcommon_exec ( int argc, char **argv, if ( optind != argc ) { /* Treat arguments as a list of interfaces to try */ - while ( optind != argc ) { - if ( ( rc = parse_netdev ( argv[optind++], - &netdev ) ) != 0 ) { + for ( i = optind ; i < argc ; i++ ) { + if ( ( rc = parse_netdev ( argv[i], &netdev ) ) != 0 ) continue; - } if ( ( ( rc = payload ( netdev ) ) == 0 ) && stop_on_first_success ) { return 0; diff --git a/src/hci/commands/image_cmd.c b/src/hci/commands/image_cmd.c index 6f51a6ba..17e22dcb 100644 --- a/src/hci/commands/image_cmd.c +++ b/src/hci/commands/image_cmd.c @@ -39,7 +39,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); /** "img{single}" options */ struct imgsingle_options { /** Image name */ - const char *name; + char *name; /** Replace image */ int replace; /** Free image after execution */ diff --git a/src/hci/commands/image_trust_cmd.c b/src/hci/commands/image_trust_cmd.c index e7a2bf12..ad238bff 100644 --- a/src/hci/commands/image_trust_cmd.c +++ b/src/hci/commands/image_trust_cmd.c @@ -84,7 +84,7 @@ static int imgtrust_exec ( int argc, char **argv ) { /** "imgverify" options */ struct imgverify_options { /** Required signer common name */ - const char *signer; + char *signer; /** Keep signature after verification */ int keep; }; diff --git a/src/hci/commands/menu_cmd.c b/src/hci/commands/menu_cmd.c index 10966db2..0ad53dbd 100644 --- a/src/hci/commands/menu_cmd.c +++ b/src/hci/commands/menu_cmd.c @@ -41,7 +41,7 @@ FEATURE ( FEATURE_MISC, "Menu", DHCP_EB_FEATURE_MENU, 1 ); /** "menu" options */ struct menu_options { /** Name */ - const char *name; + char *name; /** Delete */ int delete; }; @@ -107,7 +107,7 @@ static int menu_exec ( int argc, char **argv ) { /** "item" options */ struct item_options { /** Menu name */ - const char *menu; + char *menu; /** Shortcut key */ unsigned int key; /** Use as default */ @@ -192,11 +192,11 @@ static int item_exec ( int argc, char **argv ) { /** "choose" options */ struct choose_options { /** Menu name */ - const char *menu; + char *menu; /** Timeout */ unsigned int timeout; /** Default selection */ - const char *select; + char *select; /** Keep menu */ int keep; }; @@ -228,9 +228,9 @@ static struct command_descriptor choose_cmd = */ static int choose_exec ( int argc, char **argv ) { struct choose_options opts; + struct named_setting setting; struct menu *menu; struct menu_item *item; - const char *setting; int rc; /* Parse options */ @@ -238,7 +238,9 @@ static int choose_exec ( int argc, char **argv ) { goto err_parse_options; /* Parse setting name */ - setting = argv[optind]; + if ( ( rc = parse_autovivified_setting ( argv[optind], + &setting ) ) != 0 ) + goto err_parse_setting; /* Identify menu */ if ( ( rc = parse_menu ( opts.menu, &menu ) ) != 0 ) @@ -248,11 +250,15 @@ static int choose_exec ( int argc, char **argv ) { if ( ( rc = show_menu ( menu, opts.timeout, opts.select, &item ) ) != 0) goto err_show_menu; + /* Apply default type if necessary */ + if ( ! setting.setting.type ) + setting.setting.type = &setting_type_string; + /* Store setting */ - if ( ( rc = storef_named_setting ( setting, &setting_type_string, - item->label ) ) != 0 ) { + if ( ( rc = storef_setting ( setting.settings, &setting.setting, + item->label ) ) != 0 ) { printf ( "Could not store \"%s\": %s\n", - setting, strerror ( rc ) ); + setting.setting.name, strerror ( rc ) ); goto err_store; } @@ -265,6 +271,7 @@ static int choose_exec ( int argc, char **argv ) { if ( ! opts.keep ) destroy_menu ( menu ); err_parse_menu: + err_parse_setting: err_parse_options: return rc; } diff --git a/src/hci/commands/nvo_cmd.c b/src/hci/commands/nvo_cmd.c index f255bbf5..3fd684d0 100644 --- a/src/hci/commands/nvo_cmd.c +++ b/src/hci/commands/nvo_cmd.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -55,31 +56,44 @@ static struct command_descriptor show_cmd = */ static int show_exec ( int argc, char **argv ) { struct show_options opts; - const char *name; + struct named_setting setting; + struct settings *origin; char name_buf[32]; - char value_buf[256]; + char *value; int rc; /* Parse options */ if ( ( rc = parse_options ( argc, argv, &show_cmd, &opts ) ) != 0 ) - return rc; + goto err_parse_options; /* Parse setting name */ - name = argv[optind]; + if ( ( rc = parse_existing_setting ( argv[optind], &setting ) ) != 0 ) + goto err_parse_setting; - /* Fetch setting */ - if ( ( rc = fetchf_named_setting ( name, name_buf, sizeof ( name_buf ), - value_buf, - sizeof ( value_buf ) ) ) < 0 ) { + /* Fetch formatted setting value */ + if ( ( rc = fetchf_setting_copy ( setting.settings, &setting.setting, + &value ) ) < 0 ) { printf ( "Could not find \"%s\": %s\n", - name, strerror ( rc ) ); - return rc; + setting.setting.name, strerror ( rc ) ); + goto err_fetchf; } - /* Print setting value */ - printf ( "%s = %s\n", name_buf, value_buf ); + /* Fetch origin and format fully-qualified name */ + origin = fetch_setting_origin ( setting.settings, &setting.setting ); + assert ( origin != NULL ); + setting_name ( origin, &setting.setting, name_buf, sizeof ( name_buf )); - return 0; + /* Print setting value */ + printf ( "%s = %s\n", name_buf, value ); + + /* Success */ + rc = 0; + + free ( value ); + err_fetchf: + err_parse_setting: + err_parse_options: + return rc; } /** "set", "clear", and "read" options */ @@ -109,10 +123,10 @@ static struct command_descriptor clear_read_cmd = */ static int set_core_exec ( int argc, char **argv, struct command_descriptor *cmd, - int ( * get_value ) ( const char *name, + int ( * get_value ) ( struct named_setting *setting, char **args, char **value ) ) { struct set_core_options opts; - const char *name; + struct named_setting setting; char *value; int rc; @@ -121,26 +135,30 @@ static int set_core_exec ( int argc, char **argv, goto err_parse_options; /* Parse setting name */ - name = argv[optind]; + if ( ( rc = parse_autovivified_setting ( argv[optind], + &setting ) ) != 0 ) + goto err_parse_setting; /* Parse setting value */ - if ( ( rc = get_value ( name, &argv[ optind + 1 ], &value ) ) != 0 ) + if ( ( rc = get_value ( &setting, &argv[ optind + 1 ], &value ) ) != 0 ) goto err_get_value; - /* Determine total length of command line */ - if ( ( rc = storef_named_setting ( name, &setting_type_string, - value ) ) != 0 ) { - printf ( "Could not %s \"%s\": %s\n", - argv[0], name, strerror ( rc ) ); + /* Apply default type if necessary */ + if ( ! setting.setting.type ) + setting.setting.type = &setting_type_string; + + /* Store setting */ + if ( ( rc = storef_setting ( setting.settings, &setting.setting, + value ) ) != 0 ) { + printf ( "Could not store \"%s\": %s\n", + setting.setting.name, strerror ( rc ) ); goto err_store; } - free ( value ); - return 0; - err_store: free ( value ); err_get_value: + err_parse_setting: err_parse_options: return rc; } @@ -148,12 +166,13 @@ static int set_core_exec ( int argc, char **argv, /** * Get setting value for "set" command * - * @v name Setting name + * @v setting Named setting * @v args Remaining arguments * @ret value Setting value * @ret rc Return status code */ -static int set_value ( const char *name __unused, char **args, char **value ) { +static int set_value ( struct named_setting *setting __unused, + char **args, char **value ) { *value = concat_args ( args ); if ( ! *value ) @@ -176,13 +195,13 @@ static int set_exec ( int argc, char **argv ) { /** * Get setting value for "clear" command * - * @v name Setting name + * @v setting Named setting * @v args Remaining arguments * @ret value Setting value * @ret rc Return status code */ -static int clear_value ( const char *name __unused, char **args __unused, - char **value ) { +static int clear_value ( struct named_setting *setting __unused, + char **args __unused, char **value ) { *value = NULL; return 0; @@ -202,29 +221,27 @@ static int clear_exec ( int argc, char **argv ) { /** * Get setting value for "read" command * - * @v name Setting name + * @v setting Named setting * @v args Remaining arguments * @ret value Setting value * @ret rc Return status code */ -static int read_value ( const char *name, char **args __unused, char **value ) { +static int read_value ( struct named_setting *setting, char **args __unused, + char **value ) { char *existing; int rc; - /* Read existing value */ - if ( ( rc = fetchf_named_setting_copy ( name, &existing ) ) < 0 ) - goto err_existing; + /* Read existing value, treating errors as equivalent to an + * empty initial setting. + */ + fetchf_setting_copy ( setting->settings, &setting->setting, &existing ); /* Read new value */ if ( ( rc = readline_history ( NULL, existing, NULL, value ) ) != 0 ) - goto err_new; + goto err_readline; - /* Success */ - rc = 0; - - err_new: + err_readline: free ( existing ); - err_existing: return rc; } @@ -239,6 +256,73 @@ static int read_exec ( int argc, char **argv ) { return set_core_exec ( argc, argv, &clear_read_cmd, read_value ); } +/** "inc" options */ +struct inc_options {}; + +/** "inc" option list */ +static struct option_descriptor inc_opts[] = {}; + +/** "inc" command descriptor */ +static struct command_descriptor inc_cmd = + COMMAND_DESC ( struct inc_options, inc_opts, 1, 2, + " []" ); + +/** + * "inc" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int inc_exec ( int argc, char **argv ) { + struct inc_options opts; + struct named_setting setting; + unsigned int increment = 1; + unsigned long value; + int rc; + + /* Parse options */ + if ( ( rc = parse_options ( argc, argv, &inc_cmd, &opts ) ) != 0 ) + goto err_parse_options; + + /* Parse setting name */ + if ( ( rc = parse_existing_setting ( argv[optind], &setting ) ) != 0 ) + goto err_parse_setting; + + /* Parse increment (if present) */ + if ( ( ( optind + 1 ) < argc ) && + ( ( rc = parse_integer ( argv[ optind + 1 ], &increment ) ) != 0)) + goto err_parse_increment; + + /* Fetch existing setting value, if any, allowing for the fact + * that numeric settings are big-endian and variable-length. + */ + if ( ( rc = fetchn_setting ( setting.settings, &setting.setting, + &value ) ) != 0 ) { + /* Treat as a non-existent :int32 setting with a zero value */ + value = 0; + if ( ! setting.setting.type ) + setting.setting.type = &setting_type_int32; + } + + /* Increment value */ + value += increment; + + /* Store updated setting value */ + if ( ( rc = storen_setting ( setting.settings, &setting.setting, + value ) ) != 0 ) { + printf ( "Could not store \"%s\": %s\n", + setting.setting.name, strerror ( rc ) ); + goto err_store; + } + + err_store: + err_parse_increment: + err_parse_setting: + err_parse_options: + return rc; +} + /** Non-volatile option commands */ struct command nvo_commands[] __command = { { @@ -257,4 +341,8 @@ struct command nvo_commands[] __command = { .name = "read", .exec = read_exec, }, + { + .name = "inc", + .exec = inc_exec, + }, }; diff --git a/src/hci/commands/pci_cmd.c b/src/hci/commands/pci_cmd.c new file mode 100644 index 00000000..f2d69569 --- /dev/null +++ b/src/hci/commands/pci_cmd.c @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2013 Michael Brown . + * + * 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. + */ + +#include +#include +#include +#include +#include + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** @file + * + * PCI commands + * + */ + +/** "pciscan" options */ +struct pciscan_options {}; + +/** "pciscan" option list */ +static struct option_descriptor pciscan_opts[] = {}; + +/** "pciscan" command descriptor */ +static struct command_descriptor pciscan_cmd = + COMMAND_DESC ( struct pciscan_options, pciscan_opts, 1, 1, + "" ); + +/** + * "pciscan" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int pciscan_exec ( int argc, char **argv ) { + struct pciscan_options opts; + struct named_setting setting; + struct pci_device pci; + unsigned long prev; + int next; + int len; + int rc; + + /* Parse options */ + if ( ( rc = parse_options ( argc, argv, &pciscan_cmd, &opts ) ) != 0 ) + goto err_parse_options; + + /* Parse setting name */ + if ( ( rc = parse_autovivified_setting ( argv[optind], + &setting ) ) != 0 ) + goto err_parse_setting; + + /* Determine starting bus:dev.fn address */ + if ( ( len = fetch_uint_setting ( setting.settings, &setting.setting, + &prev ) ) < 0 ) { + /* Setting not yet defined: start searching from 00:00.0 */ + prev = 0; + } else { + /* Setting is defined: start searching from next location */ + prev++; + } + + /* Find next existent PCI device */ + if ( ( next = pci_find_next ( &pci, prev ) ) < 0 ) { + rc = next; + goto err_find_next; + } + + /* Apply default type if necessary. Use ":uint16" rather than + * ":busdevfn" to allow for easy inclusion within a + * "${pci/${location}.x.y}" constructed setting. + */ + if ( ! setting.setting.type ) + setting.setting.type = &setting_type_uint16; + + /* Store setting */ + if ( ( rc = storen_setting ( setting.settings, &setting.setting, + next ) ) != 0 ) { + printf ( "Could not store \"%s\": %s\n", + setting.setting.name, strerror ( rc ) ); + goto err_store; + } + + err_store: + err_find_next: + err_parse_setting: + err_parse_options: + return rc; +} + +/** PCI commands */ +struct command pci_commands[] __command = { + { + .name = "pciscan", + .exec = pciscan_exec, + }, +}; diff --git a/src/hci/commands/poweroff_cmd.c b/src/hci/commands/poweroff_cmd.c new file mode 100644 index 00000000..159fe618 --- /dev/null +++ b/src/hci/commands/poweroff_cmd.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2013 Marin Hannache . + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** @file + * + * Power off command + * + */ + +/** "poweroff" options */ +struct poweroff_options {}; + +/** "poweroff" option list */ +static struct option_descriptor poweroff_opts[] = {}; + +/** "poweroff" command descriptor */ +static struct command_descriptor poweroff_cmd = + COMMAND_DESC ( struct poweroff_options, poweroff_opts, 0, 0, "" ); + +/** + * The "poweroff" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int poweroff_exec ( int argc, char **argv ) { + struct poweroff_options opts; + int rc; + + /* Parse options */ + if ( ( rc = parse_options ( argc, argv, &poweroff_cmd, &opts ) ) != 0 ) + return rc; + + /* Power off system */ + rc = poweroff(); + if ( rc != 0 ) + printf ( "Could not power off: %s\n", strerror ( rc ) ); + + return rc; +} + +/** "poweroff" command */ +struct command poweroff_command __command = { + .name = "poweroff", + .exec = poweroff_exec, +}; diff --git a/src/hci/tui/settings_ui.c b/src/hci/tui/settings_ui.c index 403d1245..f7efbbb4 100644 --- a/src/hci/tui/settings_ui.c +++ b/src/hci/tui/settings_ui.c @@ -441,7 +441,7 @@ static void reveal_setting_row ( struct setting_widget *widget, static void init_widget ( struct setting_widget *widget, struct settings *settings ) { - widget->settings = settings; + widget->settings = settings_target ( settings ); widget->num_rows = select_setting_row ( widget, 0 ); widget->first_visible = SETTINGS_LIST_ROWS; draw_title_row ( widget ); @@ -508,13 +508,25 @@ static int main_loop ( struct settings *settings ) { key = getkey ( 0 ); move = 0; switch ( key ) { - case KEY_DOWN: - if ( widget.current < ( widget.num_rows - 1 ) ) - move = +1; - break; case KEY_UP: - if ( widget.current > 0 ) - move = -1; + move = -1; + break; + case KEY_DOWN: + move = +1; + break; + case KEY_PPAGE: + move = ( widget.first_visible - + widget.current - 1 ); + break; + case KEY_NPAGE: + move = ( widget.first_visible - widget.current + + SETTINGS_LIST_ROWS ); + break; + case KEY_HOME: + move = -widget.num_rows; + break; + case KEY_END: + move = +widget.num_rows; break; case CTRL_D: if ( ! widget.row.setting ) @@ -545,10 +557,16 @@ static int main_loop ( struct settings *settings ) { } if ( move ) { next = ( widget.current + move ); - draw_setting_row ( &widget ); - redraw = 1; - reveal_setting_row ( &widget, next ); - select_setting_row ( &widget, next ); + if ( ( int ) next < 0 ) + next = 0; + if ( next >= widget.num_rows ) + next = ( widget.num_rows - 1 ); + if ( next != widget.current ) { + draw_setting_row ( &widget ); + redraw = 1; + reveal_setting_row ( &widget, next ); + select_setting_row ( &widget, next ); + } } } } diff --git a/src/image/script.c b/src/image/script.c index e83180ab..881d51f3 100644 --- a/src/image/script.c +++ b/src/image/script.c @@ -55,44 +55,96 @@ static size_t script_offset; * @ret rc Return status code */ static int process_script ( struct image *image, - int ( * process_line ) ( const char *line ), + int ( * process_line ) ( struct image *image, + size_t offset, + const char *label, + const char *command ), int ( * terminate ) ( int rc ) ) { + size_t len = 0; + char *line = NULL; + size_t line_offset; + char *label; + char *command; off_t eol; - size_t len; - char *line; + size_t frag_len; + char *tmp; int rc; + /* Initialise script and line offsets */ script_offset = 0; + line_offset = 0; do { - + /* Find length of next line, excluding any terminating '\n' */ eol = memchr_user ( image->data, script_offset, '\n', ( image->len - script_offset ) ); if ( eol < 0 ) eol = image->len; - len = ( eol - script_offset ); + frag_len = ( eol - script_offset ); /* Allocate buffer for line */ - line = zalloc ( len + 1 /* NUL */ ); - if ( ! line ) - return -ENOMEM; + tmp = realloc ( line, ( len + frag_len + 1 /* NUL */ ) ); + if ( ! tmp ) { + rc = -ENOMEM; + goto err_alloc; + } + line = tmp; /* Copy line */ - copy_from_user ( line, image->data, script_offset, len ); - DBG ( "$ %s\n", line ); + copy_from_user ( ( line + len ), image->data, script_offset, + frag_len ); + len += frag_len; - /* Move to next line */ - script_offset += ( len + 1 ); + /* Move to next line in script */ + script_offset += ( frag_len + 1 ); - /* Process and free line */ - rc = process_line ( line ); - free ( line ); + /* Strip trailing CR, if present */ + if ( len && ( line[ len - 1 ] == '\r' ) ) + len--; + + /* Handle backslash continuations */ + if ( len && ( line[ len - 1 ] == '\\' ) ) { + len--; + rc = -EINVAL; + continue; + } + + /* Terminate line */ + line[len] = '\0'; + + /* Split line into (optional) label and command */ + command = line; + while ( isspace ( *command ) ) + command++; + if ( *command == ':' ) { + label = ++command; + while ( *command && ! isspace ( *command ) ) + command++; + if ( *command ) + *(command++) = '\0'; + } else { + label = NULL; + } + + /* Process line */ + rc = process_line ( image, line_offset, label, command ); if ( terminate ( rc ) ) - return rc; + goto err_process; + + /* Free line */ + free ( line ); + line = NULL; + len = 0; + + /* Update line offset */ + line_offset = script_offset; } while ( script_offset < image->len ); + err_process: + err_alloc: + free ( line ); return rc; } @@ -111,18 +163,21 @@ static int terminate_on_exit_or_failure ( int rc ) { /** * Execute script line * - * @v line Line of script + * @v image Script + * @v offset Offset within script + * @v label Label, or NULL + * @v command Command * @ret rc Return status code */ -static int script_exec_line ( const char *line ) { +static int script_exec_line ( struct image *image, size_t offset, + const char *label __unused, + const char *command ) { int rc; - /* Skip label lines */ - if ( line[0] == ':' ) - return 0; + DBGC ( image, "[%04zx] $ %s\n", offset, command ); /* Execute command */ - if ( ( rc = system ( line ) ) != 0 ) + if ( ( rc = system ( command ) ) != 0 ) return rc; return 0; @@ -176,7 +231,7 @@ static int script_probe ( struct image *image ) { /* Sanity check */ if ( image->len < sizeof ( test ) ) { - DBG ( "Too short to be a script\n" ); + DBGC ( image, "Too short to be a script\n" ); return -ENOEXEC; } @@ -185,7 +240,7 @@ static int script_probe ( struct image *image ) { if ( ! ( ( ( memcmp ( test, ipxe_magic, sizeof ( test ) - 1 ) == 0 ) || ( memcmp ( test, gpxe_magic, sizeof ( test ) - 1 ) == 0 )) && isspace ( test[ sizeof ( test ) - 1 ] ) ) ) { - DBG ( "Invalid magic signature\n" ); + DBGC ( image, "Invalid magic signature\n" ); return -ENOEXEC; } @@ -219,20 +274,26 @@ static const char *goto_label; /** * Check for presence of label * - * @v line Script line + * @v image Script + * @v offset Offset within script + * @v label Label + * @v command Command * @ret rc Return status code */ -static int goto_find_label ( const char *line ) { - size_t len = strlen ( goto_label ); +static int goto_find_label ( struct image *image, size_t offset, + const char *label, const char *command __unused ) { - if ( line[0] != ':' ) + /* Check label exists */ + if ( ! label ) return -ENOENT; - if ( strncmp ( goto_label, &line[1], len ) != 0 ) + /* Check label matches */ + if ( strcmp ( goto_label, label ) != 0 ) return -ENOENT; - if ( line[ 1 + len ] && ! isspace ( line[ 1 + len ] ) ) - return -ENOENT; + /* Update script offset */ + script_offset = offset; + DBGC ( image, "[%04zx] Gone to :%s\n", offset, label ); return 0; } @@ -278,6 +339,8 @@ static int goto_exec ( int argc, char **argv ) { if ( ( rc = process_script ( current_image, goto_find_label, terminate_on_label_found ) ) != 0 ) { script_offset = saved_offset; + DBGC ( current_image, "[%04zx] No such label :%s\n", + script_offset, goto_label ); return rc; } diff --git a/src/include/big_bswap.h b/src/include/big_bswap.h index 3775fac1..6c375a57 100644 --- a/src/include/big_bswap.h +++ b/src/include/big_bswap.h @@ -1,6 +1,8 @@ #ifndef ETHERBOOT_BIG_BSWAP_H #define ETHERBOOT_BIG_BSWAP_H +#define htonll(x) (x) +#define ntohll(x) (x) #define ntohl(x) (x) #define htonl(x) (x) #define ntohs(x) (x) diff --git a/src/include/compiler.h b/src/include/compiler.h index e5559245..132793b5 100644 --- a/src/include/compiler.h +++ b/src/include/compiler.h @@ -60,12 +60,15 @@ /** Provide a symbol within this object file */ #ifdef ASSEMBLY #define PROVIDE_SYMBOL( _sym ) \ + .section ".provided", "a", @nobits ; \ + .hidden _sym ; \ .globl _sym ; \ - .comm _sym, 0 + _sym: ; \ + .previous #else /* ASSEMBLY */ #define PROVIDE_SYMBOL( _sym ) \ - extern char _sym[]; \ - char _sym[0] + char _sym[0] \ + __attribute__ (( section ( ".provided" ) )) #endif /* ASSEMBLY */ /** Require a symbol within this object file @@ -652,7 +655,7 @@ int __debug_disable; * be in the public domain. */ #define FILE_LICENCE_PUBLIC_DOMAIN \ - PROVIDE_SYMBOL ( __licence_public_domain ) + PROVIDE_SYMBOL ( PREFIX_OBJECT ( __licence__public_domain__ ) ) /** Declare a file as being under version 2 (or later) of the GNU GPL * @@ -661,7 +664,7 @@ int __debug_disable; * (at your option) any later version". */ #define FILE_LICENCE_GPL2_OR_LATER \ - PROVIDE_SYMBOL ( __licence_gpl2_or_later ) + PROVIDE_SYMBOL ( PREFIX_OBJECT ( __licence__gpl2_or_later__ ) ) /** Declare a file as being under version 2 of the GNU GPL * @@ -670,7 +673,7 @@ int __debug_disable; * "or, at your option, any later version" clause. */ #define FILE_LICENCE_GPL2_ONLY \ - PROVIDE_SYMBOL ( __licence_gpl2_only ) + PROVIDE_SYMBOL ( PREFIX_OBJECT ( __licence__gpl2_only__ ) ) /** Declare a file as being under any version of the GNU GPL * @@ -682,7 +685,7 @@ int __debug_disable; * version ever published by the Free Software Foundation". */ #define FILE_LICENCE_GPL_ANY \ - PROVIDE_SYMBOL ( __licence_gpl_any ) + PROVIDE_SYMBOL ( PREFIX_OBJECT ( __licence__gpl_any__ ) ) /** Declare a file as being under the three-clause BSD licence * @@ -707,7 +710,7 @@ int __debug_disable; * functionally equivalent to the standard three-clause BSD licence. */ #define FILE_LICENCE_BSD3 \ - PROVIDE_SYMBOL ( __licence_bsd3 ) + PROVIDE_SYMBOL ( PREFIX_OBJECT ( __licence__bsd3__ ) ) /** Declare a file as being under the two-clause BSD licence * @@ -728,7 +731,7 @@ int __debug_disable; * functionally equivalent to the standard two-clause BSD licence. */ #define FILE_LICENCE_BSD2 \ - PROVIDE_SYMBOL ( __licence_bsd2 ) + PROVIDE_SYMBOL ( PREFIX_OBJECT ( __licence__bsd2__ ) ) /** Declare a file as being under the one-clause MIT-style licence * @@ -738,7 +741,7 @@ int __debug_disable; * permission notice appear in all copies. */ #define FILE_LICENCE_MIT \ - PROVIDE_SYMBOL ( __licence_mit ) + PROVIDE_SYMBOL ( PREFIX_OBJECT ( __licence__mit__ ) ) /** Declare a particular licence as applying to a file */ #define FILE_LICENCE( _licence ) FILE_LICENCE_ ## _licence diff --git a/src/include/ipxe/base16.h b/src/include/ipxe/base16.h index f0c9842f..60e3f231 100644 --- a/src/include/ipxe/base16.h +++ b/src/include/ipxe/base16.h @@ -33,6 +33,8 @@ static inline size_t base16_decoded_max_len ( const char *encoded ) { } extern void base16_encode ( const uint8_t *raw, size_t len, char *encoded ); +extern int hex_decode ( const char *string, char separator, void *data, + size_t len ); extern int base16_decode ( const char *encoded, uint8_t *raw ); #endif /* _IPXE_BASE16_H */ diff --git a/src/include/ipxe/dhcp.h b/src/include/ipxe/dhcp.h index b97dfe32..6c028466 100644 --- a/src/include/ipxe/dhcp.h +++ b/src/include/ipxe/dhcp.h @@ -450,6 +450,18 @@ struct dhcp_netdev_desc { */ #define DHCP_EB_REVERSE_PASSWORD DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xc1 ) +/** User ID + * + * This will be used as the user id for AUTH_SYS based authentication in NFS. + */ +#define DHCP_EB_UID DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xc2 ) + +/** Group ID + * + * This will be used as the group id for AUTH_SYS based authentication in NFS. + */ +#define DHCP_EB_GID DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xc3 ) + /** iPXE version number */ #define DHCP_EB_VERSION DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xeb ) diff --git a/src/include/ipxe/efi/ProcessorBind.h b/src/include/ipxe/efi/ProcessorBind.h index 535cd4ce..1294459f 100644 --- a/src/include/ipxe/efi/ProcessorBind.h +++ b/src/include/ipxe/efi/ProcessorBind.h @@ -1,6 +1,8 @@ #ifndef _IPXE_EFI_PROCESSOR_BIND_H #define _IPXE_EFI_PROCESSOR_BIND_H +FILE_LICENCE ( GPL2_OR_LATER ); + /* * EFI header files rely on having the CPU architecture directory * present in the search path in order to pick up ProcessorBind.h. We diff --git a/src/include/ipxe/efi/efi.h b/src/include/ipxe/efi/efi.h index 0a21c6e7..a98b5588 100644 --- a/src/include/ipxe/efi/efi.h +++ b/src/include/ipxe/efi/efi.h @@ -21,6 +21,8 @@ * trailing whitespace. */ +FILE_LICENCE ( GPL2_OR_LATER ); + /* EFI headers rudely redefine NULL */ #undef NULL diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 67edcc93..4053923b 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -63,6 +63,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_test ( ERRFILE_CORE | 0x00170000 ) #define ERRFILE_xferbuf ( ERRFILE_CORE | 0x00180000 ) #define ERRFILE_pending ( ERRFILE_CORE | 0x00190000 ) +#define ERRFILE_null_reboot ( ERRFILE_CORE | 0x001a0000 ) #define ERRFILE_eisa ( ERRFILE_DRIVER | 0x00000000 ) #define ERRFILE_isa ( ERRFILE_DRIVER | 0x00010000 ) @@ -113,8 +114,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_sundance ( ERRFILE_DRIVER | 0x00410000 ) #define ERRFILE_tlan ( ERRFILE_DRIVER | 0x00420000 ) #define ERRFILE_tulip ( ERRFILE_DRIVER | 0x00430000 ) -#define ERRFILE_via_rhine ( ERRFILE_DRIVER | 0x00440000 ) -#define ERRFILE_via_velocity ( ERRFILE_DRIVER | 0x00450000 ) +#define ERRFILE_rhine ( ERRFILE_DRIVER | 0x00440000 ) +#define ERRFILE_velocity ( ERRFILE_DRIVER | 0x00450000 ) #define ERRFILE_w89c840 ( ERRFILE_DRIVER | 0x00460000 ) #define ERRFILE_ipoib ( ERRFILE_DRIVER | 0x00470000 ) #define ERRFILE_e1000_main ( ERRFILE_DRIVER | 0x00480000 ) @@ -207,6 +208,12 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_fcoe ( ERRFILE_NET | 0x002e0000 ) #define ERRFILE_fcns ( ERRFILE_NET | 0x002f0000 ) #define ERRFILE_vlan ( ERRFILE_NET | 0x00300000 ) +#define ERRFILE_oncrpc ( ERRFILE_NET | 0x00310000 ) +#define ERRFILE_portmap ( ERRFILE_NET | 0x00320000 ) +#define ERRFILE_nfs ( ERRFILE_NET | 0x00330000 ) +#define ERRFILE_nfs_open ( ERRFILE_NET | 0x00340000 ) +#define ERRFILE_mount ( ERRFILE_NET | 0x00350000 ) +#define ERRFILE_oncrpc_iob ( ERRFILE_NET | 0x00360000 ) #define ERRFILE_image ( ERRFILE_IMAGE | 0x00000000 ) #define ERRFILE_elf ( ERRFILE_IMAGE | 0x00010000 ) @@ -275,6 +282,10 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_efi_init ( ERRFILE_OTHER | 0x00390000 ) #define ERRFILE_efi_timer ( ERRFILE_OTHER | 0x003a0000 ) #define ERRFILE_efi_umalloc ( ERRFILE_OTHER | 0x003b0000 ) +#define ERRFILE_linux_pci ( ERRFILE_OTHER | 0x003c0000 ) +#define ERRFILE_pci_settings ( ERRFILE_OTHER | 0x003d0000 ) +#define ERRFILE_efi_reboot ( ERRFILE_OTHER | 0x003e0000 ) +#define ERRFILE_memmap_settings ( ERRFILE_OTHER | 0x003f0000 ) /** @} */ diff --git a/src/include/ipxe/features.h b/src/include/ipxe/features.h index 0c92f5be..d8b8b218 100644 --- a/src/include/ipxe/features.h +++ b/src/include/ipxe/features.h @@ -54,6 +54,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define DHCP_EB_FEATURE_VLAN 0x26 /**< VLAN support */ #define DHCP_EB_FEATURE_MENU 0x27 /**< Menu support */ #define DHCP_EB_FEATURE_SDI 0x28 /**< SDI image support */ +#define DHCP_EB_FEATURE_NFS 0x29 /**< NFS protocol */ /** @} */ diff --git a/src/include/ipxe/in.h b/src/include/ipxe/in.h index 20f1ce26..eee9159f 100644 --- a/src/include/ipxe/in.h +++ b/src/include/ipxe/in.h @@ -59,19 +59,22 @@ struct sockaddr_in { * Always set to @c AF_INET for IPv4 addresses */ sa_family_t sin_family; + /** Flags (part of struct @c sockaddr_tcpip) */ + uint16_t sin_flags; /** TCP/IP port (part of struct @c sockaddr_tcpip) */ uint16_t sin_port; /** IPv4 address */ struct in_addr sin_addr; /** Padding * - * This ensures that a struct @c sockaddr_tcpip is large - * enough to hold a socket address for any TCP/IP address - * family. + * This ensures that a struct @c sockaddr_in is large enough + * to hold a socket address for any TCP/IP address family. */ - char pad[ sizeof ( struct sockaddr ) - sizeof ( sa_family_t ) - - sizeof ( uint16_t ) - - sizeof ( struct in_addr ) ]; + char pad[ sizeof ( struct sockaddr ) - + ( sizeof ( sa_family_t ) /* sin_family */ + + sizeof ( uint16_t ) /* sin_flags */ + + sizeof ( uint16_t ) /* sin_port */ + + sizeof ( struct in_addr ) /* sin_addr */ ) ]; } __attribute__ (( may_alias )); /** @@ -82,12 +85,27 @@ struct sockaddr_in6 { * * Always set to @c AF_INET6 for IPv6 addresses */ - sa_family_t sin_family; + sa_family_t sin6_family; + /** Flags (part of struct @c sockaddr_tcpip) */ + uint16_t sin6_flags; /** TCP/IP port (part of struct @c sockaddr_tcpip) */ - uint16_t sin_port; + uint16_t sin6_port; uint32_t sin6_flowinfo; /* Flow number */ struct in6_addr sin6_addr; /* 128-bit destination address */ uint32_t sin6_scope_id; /* Scope ID */ + /** Padding + * + * This ensures that a struct @c sockaddr_in6 is large + * enough to hold a socket address for any TCP/IP address + * family. + */ + char pad[ sizeof ( struct sockaddr ) - + ( sizeof ( sa_family_t ) /* sin6_family */ + + sizeof ( uint16_t ) /* sin6_flags */ + + sizeof ( uint16_t ) /* sin6_port */ + + sizeof ( uint32_t ) /* sin6_flowinfo */ + + sizeof ( struct in6_addr ) /* sin6_addr */ + + sizeof ( uint32_t ) /* sin6_scope_id */ ) ]; } __attribute__ (( may_alias )); extern int inet_aton ( const char *cp, struct in_addr *inp ); diff --git a/src/include/ipxe/linux.h b/src/include/ipxe/linux.h index dac508ea..a01ace3d 100644 --- a/src/include/ipxe/linux.h +++ b/src/include/ipxe/linux.h @@ -30,6 +30,14 @@ FILE_LICENCE(GPL2_OR_LATER); #include #include +/** + * Convert a Linux error number to an iPXE status code + * + * @v errno Linux error number + * @ret rc iPXE status code (before negation) + */ +#define ELINUX( errno ) EPLATFORM ( EINFO_EPLATFORM, errno ) + /** A linux device */ struct linux_device { /** Generic device */ diff --git a/src/include/ipxe/linux/linux_pci.h b/src/include/ipxe/linux/linux_pci.h new file mode 100644 index 00000000..43916673 --- /dev/null +++ b/src/include/ipxe/linux/linux_pci.h @@ -0,0 +1,130 @@ +#ifndef _IPXE_LINUX_PCI_H +#define _IPXE_LINUX_PCI_H + +/** @file + * + * iPXE PCI API for Linux + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#ifdef PCIAPI_LINUX +#define PCIAPI_PREFIX_linux +#else +#define PCIAPI_PREFIX_linux __linux_ +#endif + +struct pci_device; + +extern int linux_pci_read ( struct pci_device *pci, unsigned long where, + unsigned long *value, size_t len ); +extern int linux_pci_write ( struct pci_device *pci, unsigned long where, + unsigned long value, size_t len ); + +/** + * Read byte from PCI configuration space + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value read + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( linux, pci_read_config_byte ) ( struct pci_device *pci, + unsigned int where, + uint8_t *value ) { + int rc; + unsigned long tmp; + + rc = linux_pci_read ( pci, where, &tmp, sizeof ( *value ) ); + *value = tmp; + return rc; +} + +/** + * Read word from PCI configuration space + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value read + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( linux, pci_read_config_word ) ( struct pci_device *pci, + unsigned int where, + uint16_t *value ) { + int rc; + unsigned long tmp; + + rc = linux_pci_read ( pci, where, &tmp, sizeof ( *value ) ); + *value = tmp; + return rc; +} + +/** + * Read dword from PCI configuration space + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value read + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( linux, pci_read_config_dword ) ( struct pci_device *pci, + unsigned int where, + uint32_t *value ) { + int rc; + unsigned long tmp; + + rc = linux_pci_read ( pci, where, &tmp, sizeof ( *value ) ); + *value = tmp; + return rc; +} + +/** + * Write byte to PCI configuration space + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value to be written + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( linux, pci_write_config_byte ) ( struct pci_device *pci, + unsigned int where, + uint8_t value ) { + return linux_pci_write ( pci, where, value, sizeof ( value ) ); +} + +/** + * Write word to PCI configuration space + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value to be written + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( linux, pci_write_config_word ) ( struct pci_device *pci, + unsigned int where, + uint16_t value ) { + return linux_pci_write ( pci, where, value, sizeof ( value ) ); +} + +/** + * Write dword to PCI configuration space + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value to be written + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( linux, pci_write_config_dword ) ( struct pci_device *pci, + unsigned int where, + uint32_t value ) { + return linux_pci_write ( pci, where, value, sizeof ( value ) ); +} + +#endif /* _IPXE_LINUX_PCI_H */ diff --git a/src/include/ipxe/mount.h b/src/include/ipxe/mount.h new file mode 100644 index 00000000..ca958117 --- /dev/null +++ b/src/include/ipxe/mount.h @@ -0,0 +1,76 @@ +#ifndef _IPXE_MOUNT_H +#define _IPXE_MOUNT_H + +#include + +/** @file + * + * NFS MOUNT protocol. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** NFS MOUNT protocol number */ +#define ONCRPC_MOUNT 100005 +/** NFS MOUNT protocol version */ +#define MOUNT_VERS 3 + + +/** No error */ +#define MNT3_OK 0 +/** Not owner */ +#define MNT3ERR_PERM 1 +/** No such file or directory */ +#define MNT3ERR_NOENT 2 +/** I/O error */ +#define MNT3ERR_IO 5 +/** Permission denied */ +#define MNT3ERR_ACCES 13 +/** Not a directory */ +#define MNT3ERR_NOTDIR 20 +/** Invalid argument */ +#define MNT3ERR_INVAL 22 +/** Filename too long */ +#define MNT3ERR_NAMETOOLONG 63 +/** Operation not supported */ +#define MNT3ERR_NOTSUPP 10004 +/** A failure on the server */ +#define MNT3ERR_SERVERFAULT 10006 + + +/** + * A MOUNT MNT reply + * + */ +struct mount_mnt_reply { + /** Reply status */ + uint32_t status; + /** Root file handle */ + struct nfs_fh fh; +}; + +/** + * Prepare an ONC RPC session to be used as a MOUNT session + * + * @v session ONC RPC session + * @v credential ONC RPC credential + * + * The credential parameter must not be NULL, use 'oncrpc_auth_none' if you + * don't want a particular scheme to be used. + */ +static inline void mount_init_session ( struct oncrpc_session *session, + struct oncrpc_cred *credential ) { + oncrpc_init_session ( session, credential, &oncrpc_auth_none, + ONCRPC_MOUNT, MOUNT_VERS ); +} + +int mount_mnt ( struct interface *intf, struct oncrpc_session *session, + const char *mountpoint ); +int mount_umnt ( struct interface *intf, struct oncrpc_session *session, + const char *mountpoint ); + +int mount_get_mnt_reply ( struct mount_mnt_reply *mnt_reply, + struct oncrpc_reply *reply ); + +#endif /* _IPXE_MOUNT_H */ diff --git a/src/include/ipxe/nfs.h b/src/include/ipxe/nfs.h new file mode 100644 index 00000000..498ed5a2 --- /dev/null +++ b/src/include/ipxe/nfs.h @@ -0,0 +1,157 @@ +#ifndef _IPXE_NFS_H +#define _IPXE_NFS_H + +#include +#include + +/** @file + * + * Network File System protocol. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** NFS protocol number */ +#define ONCRPC_NFS 100003 + +/** NFS protocol version */ +#define NFS_VERS 3 + +/** No error*/ +#define NFS3_OK 0 +/** Not owner */ +#define NFS3ERR_PERM 1 +/** No such file or directory */ +#define NFS3ERR_NOENT 2 +/** I/O error */ +#define NFS3ERR_IO 5 +/** No such device or address */ +#define NFS3ERR_NXIO 6 +/** Permission denied */ +#define NFS3ERR_ACCES 13 +/** The file specified already exists */ +#define NFS3ERR_EXIST 17 +/** Attempt to do a cross-device hard link */ +#define NFS3ERR_XDEV 18 +/** No such device */ +#define NFS3ERR_NODEV 19 +/** Not a directory */ +#define NFS3ERR_NOTDIR 20 + /**Is a directory */ +#define NFS3ERR_ISDIR 21 +/** Invalid argument */ +#define NFS3ERR_INVAL 22 +/** Filename too long */ +#define NFS3ERR_NAMETOOLONG 63 +/** Invalid file handle */ +#define NFS3ERR_STALE 70 +/** Too many levels of remote in path */ +#define NFS3ERR_REMOTE 71 +/** Illegal NFS file handle */ +#define NFS3ERR_BADHANDLE 10001 +/** READDIR or READDIRPLUS cookie is stale */ +#define NFS3ERR_BAD_COOKIE 10003 +/** Operation not supported */ +#define NFS3ERR_NOTSUPP 10004 +/** Buffer or request is too small */ +#define NFS3ERR_TOOSMALL 10005 +/** An error occurred on the server which does not map to any of the legal NFS + * version 3 protocol error values */ +#define NFS3ERR_SERVERFAULT 10006 +/** The server initiated the request, but was not able to complete it in a + * timely fashion */ +#define NFS3ERR_JUKEBOX 10008 + +enum nfs_attr_type { + NFS_ATTR_SYMLINK = 5, +}; + +/** + * A NFS file handle + * + */ +struct nfs_fh { + uint8_t fh[64]; + size_t size; +}; + +/** + * A NFS LOOKUP reply + * + */ +struct nfs_lookup_reply { + /** Reply status */ + uint32_t status; + /** Entity type */ + enum nfs_attr_type ent_type; + /** File handle */ + struct nfs_fh fh; +}; + +/** + * A NFS READLINK reply + * + */ +struct nfs_readlink_reply { + /** Reply status */ + uint32_t status; + /** File path length */ + uint32_t path_len; + /** File path */ + char *path; +}; + + +/** + * A NFS READ reply + * + */ +struct nfs_read_reply { + /** Reply status */ + uint32_t status; + /** File size */ + uint64_t filesize; + /** Bytes read */ + uint32_t count; + /** End-of-File indicator */ + uint32_t eof; + /** Data length */ + uint32_t data_len; + /** Data read */ + void *data; +}; + +size_t nfs_iob_get_fh ( struct io_buffer *io_buf, struct nfs_fh *fh ); +size_t nfs_iob_add_fh ( struct io_buffer *io_buf, const struct nfs_fh *fh ); + +/** + * Prepare an ONC RPC session to be used as a NFS session + * + * @v session ONC RPC session + * @v credential ONC RPC credential + * + * The credential parameter must not be NULL, use 'oncrpc_auth_none' if you + * don't want a particular scheme to be used. + */ +static inline void nfs_init_session ( struct oncrpc_session *session, + struct oncrpc_cred *credential ) { + oncrpc_init_session ( session, credential, &oncrpc_auth_none, + ONCRPC_NFS, NFS_VERS ); +} + +int nfs_lookup ( struct interface *intf, struct oncrpc_session *session, + const struct nfs_fh *fh, const char *filename ); +int nfs_readlink ( struct interface *intf, struct oncrpc_session *session, + const struct nfs_fh *fh ); +int nfs_read ( struct interface *intf, struct oncrpc_session *session, + const struct nfs_fh *fh, uint64_t offset, uint32_t count ); + +int nfs_get_lookup_reply ( struct nfs_lookup_reply *lookup_reply, + struct oncrpc_reply *reply ); +int nfs_get_readlink_reply ( struct nfs_readlink_reply *readlink_reply, + struct oncrpc_reply *reply ); +int nfs_get_read_reply ( struct nfs_read_reply *read_reply, + struct oncrpc_reply *reply ); + +#endif /* _IPXE_NFS_H */ diff --git a/src/include/ipxe/nfs_open.h b/src/include/ipxe/nfs_open.h new file mode 100644 index 00000000..caba977f --- /dev/null +++ b/src/include/ipxe/nfs_open.h @@ -0,0 +1,12 @@ +#ifndef _IPXE_NFS_OPEN_H +#define _IPXE_NFS_OPEN_H + +/** @file + * + * Network File System protocol. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#endif /* _IPXE_NFS_OPEN_H */ diff --git a/src/include/ipxe/oncrpc.h b/src/include/ipxe/oncrpc.h new file mode 100644 index 00000000..76c1260f --- /dev/null +++ b/src/include/ipxe/oncrpc.h @@ -0,0 +1,128 @@ +#ifndef _IPXE_ONCRPC_H +#define _IPXE_ONCRPC_H + +#include +#include +#include + +/** @file + * + * SUN ONC RPC protocol. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** ONC RCP Version */ +#define ONCRPC_VERS 2 + +/** ONC RPC Null Authentication */ +#define ONCRPC_AUTH_NONE 0 + +/** ONC RPC System Authentication (also called UNIX Authentication) */ +#define ONCRPC_AUTH_SYS 1 + +/** Size of an ONC RPC header */ +#define ONCRPC_HEADER_SIZE ( 11 * sizeof ( uint32_t ) ) + +#define ONCRPC_FIELD( type, value ) { oncrpc_ ## type, { .type = value } } +#define ONCRPC_SUBFIELD( type, args... ) \ + { oncrpc_ ## type, { .type = { args } } } + +#define ONCRPC_FIELD_END { oncrpc_none, { } } + +/** Enusure that size is a multiple of four */ +#define oncrpc_align( size ) ( ( (size) + 3 ) & ~3 ) + +/** + * Calculate the length of a string, including padding bytes. + * + * @v str String + * @ret size Length of the padded string + */ +#define oncrpc_strlen( str ) ( oncrpc_align ( strlen ( str ) ) + \ + sizeof ( uint32_t ) ) + +struct oncrpc_cred { + uint32_t flavor; + uint32_t length; +}; + +struct oncrpc_cred_sys { + struct oncrpc_cred credential; + uint32_t stamp; + char *hostname; + uint32_t uid; + uint32_t gid; + uint32_t aux_gid_len; + uint32_t aux_gid[16]; +}; + +struct oncrpc_reply +{ + struct oncrpc_cred *verifier; + uint32_t rpc_id; + uint32_t reply_state; + uint32_t accept_state; + uint32_t frame_size; + struct io_buffer *data; +}; + +struct oncrpc_session { + struct oncrpc_reply pending_reply; + struct oncrpc_cred *credential; + struct oncrpc_cred *verifier; + uint32_t rpc_id; + uint32_t prog_name; + uint32_t prog_vers; +}; + +enum oncrpc_field_type { + oncrpc_none = 0, + oncrpc_int32, + oncrpc_int64, + oncrpc_str, + oncrpc_array, + oncrpc_intarray, + oncrpc_cred, +}; + +union oncrpc_field_value { + struct { + size_t length; + const void *ptr; + } array; + + struct { + size_t length; + const uint32_t *ptr; + } intarray; + + int64_t int64; + int32_t int32; + const char *str; + const struct oncrpc_cred *cred; +}; + +struct oncrpc_field { + enum oncrpc_field_type type; + union oncrpc_field_value value; +}; + +extern struct oncrpc_cred oncrpc_auth_none; + +int oncrpc_init_cred_sys ( struct oncrpc_cred_sys *auth_sys ); +void oncrpc_init_session ( struct oncrpc_session *session, + struct oncrpc_cred *credential, + struct oncrpc_cred *verifier, uint32_t prog_name, + uint32_t prog_vers ); + +int oncrpc_call ( struct interface *intf, struct oncrpc_session *session, + uint32_t proc_name, const struct oncrpc_field fields[] ); + +size_t oncrpc_compute_size ( const struct oncrpc_field fields[] ); + +int oncrpc_get_reply ( struct oncrpc_session *session, + struct oncrpc_reply *reply, struct io_buffer *io_buf ); + +#endif /* _IPXE_ONCRPC_H */ diff --git a/src/include/ipxe/oncrpc_iob.h b/src/include/ipxe/oncrpc_iob.h new file mode 100644 index 00000000..4858d96b --- /dev/null +++ b/src/include/ipxe/oncrpc_iob.h @@ -0,0 +1,102 @@ +#ifndef _IPXE_ONCRPC_IOB_H +#define _IPXE_ONCRPC_IOB_H + +#include +#include +#include +#include +#include + +/** @file + * + * SUN ONC RPC protocol. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** + * Add a string to the end of an I/O buffer + * + * @v io_buf I/O buffer + * @v val String + * @ret size Size of the data written + */ +#define oncrpc_iob_add_string( buf, str ) \ +( { \ + const char * _str = (str); \ + oncrpc_iob_add_array ( (buf), strlen ( _str ), _str ); \ +} ) + +/** + * Get a 32 bits integer from the beginning of an I/O buffer + * + * @v buf I/O buffer + * @ret int Integer + */ + +#define oncrpc_iob_get_int( buf ) \ +( { \ + uint32_t *_val; \ + _val = (buf)->data; \ + iob_pull ( (buf), sizeof ( uint32_t ) ); \ + ntohl ( *_val ); \ +} ) + +/** + * Get a 64 bits integer from the beginning of an I/O buffer + * + * @v buf I/O buffer + * @ret int Integer + */ +#define oncrpc_iob_get_int64( buf ) \ +( { \ + uint64_t *_val; \ + _val = (buf)->data; \ + iob_pull ( (buf), sizeof ( uint64_t ) ); \ + ntohll ( *_val ); \ +} ) + + +size_t oncrpc_iob_add_fields ( struct io_buffer *io_buf, + const struct oncrpc_field fields[] ); + +size_t oncrpc_iob_add_array ( struct io_buffer *io_buf, size_t length, + const void *data ); + +size_t oncrpc_iob_add_intarray ( struct io_buffer *io_buf, size_t length, + const uint32_t *array ); + +size_t oncrpc_iob_add_cred ( struct io_buffer *io_buf, + const struct oncrpc_cred *cred ); + +size_t oncrpc_iob_get_cred ( struct io_buffer *io_buf, + struct oncrpc_cred *cred ); + +/** + * Add a 32 bits integer to the end of an I/O buffer + * + * @v io_buf I/O buffer + * @v val Integer + * @ret size Size of the data written + */ +static inline size_t oncrpc_iob_add_int ( struct io_buffer *io_buf, + uint32_t val ) { + * ( uint32_t * ) iob_put ( io_buf, sizeof ( val ) ) = htonl ( val ); + return ( sizeof ( val) ); +} + +/** + * Add a 64 bits integer to the end of an I/O buffer + * + * @v io_buf I/O buffer + * @v val Integer + * @ret size Size of the data written + */ +static inline size_t oncrpc_iob_add_int64 ( struct io_buffer *io_buf, + uint64_t val ) { + * ( uint64_t * ) iob_put ( io_buf, sizeof ( val ) ) = htonll ( val ); + return ( sizeof ( val) ); +} + +#endif /* _IPXE_ONCRPC_IOB_H */ diff --git a/src/include/ipxe/parseopt.h b/src/include/ipxe/parseopt.h index b492a51e..2e38d231 100644 --- a/src/include/ipxe/parseopt.h +++ b/src/include/ipxe/parseopt.h @@ -11,6 +11,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include +#include struct net_device; struct menu; @@ -31,7 +32,7 @@ struct option_descriptor { * @v value Option value to fill in * @ret rc Return status code */ - int ( * parse ) ( const char *text, void *value ); + int ( * parse ) ( char *text, void *value ); }; /** @@ -43,9 +44,9 @@ struct option_descriptor { * @ret _parse Generic option parser */ #define OPTION_PARSER( _struct, _field, _parse ) \ - ( ( int ( * ) ( const char *text, void *value ) ) \ + ( ( int ( * ) ( char *text, void *value ) ) \ ( ( ( ( typeof ( _parse ) * ) NULL ) == \ - ( ( int ( * ) ( const char *text, \ + ( ( int ( * ) ( char *text, \ typeof ( ( ( _struct * ) NULL )->_field ) * ) ) \ NULL ) ) ? _parse : _parse ) ) @@ -114,12 +115,26 @@ struct command_descriptor { .usage = _usage, \ } -extern int parse_string ( const char *text, const char **value ); -extern int parse_integer ( const char *text, unsigned int *value ); -extern int parse_netdev ( const char *text, struct net_device **netdev ); -extern int parse_menu ( const char *text, struct menu **menu ); -extern int parse_flag ( const char *text __unused, int *flag ); -extern int parse_key ( const char *text, unsigned int *key ); +/** A parsed named setting */ +struct named_setting { + /** Settings block */ + struct settings *settings; + /** Setting */ + struct setting setting; +}; + +extern int parse_string ( char *text, char **value ); +extern int parse_integer ( char *text, unsigned int *value ); +extern int parse_netdev ( char *text, struct net_device **netdev ); +extern int parse_menu ( char *text, struct menu **menu ); +extern int parse_flag ( char *text __unused, int *flag ); +extern int parse_key ( char *text, unsigned int *key ); +extern int parse_settings ( char *text, struct settings **settings ); +extern int parse_setting ( char *text, struct named_setting *setting, + get_child_settings_t get_child ); +extern int parse_existing_setting ( char *text, struct named_setting *setting ); +extern int parse_autovivified_setting ( char *text, + struct named_setting *setting ); extern void print_usage ( struct command_descriptor *cmd, char **argv ); extern int reparse_options ( int argc, char **argv, struct command_descriptor *cmd, void *opts ); diff --git a/src/include/ipxe/pci.h b/src/include/ipxe/pci.h index a6ed484f..692771eb 100644 --- a/src/include/ipxe/pci.h +++ b/src/include/ipxe/pci.h @@ -351,6 +351,7 @@ struct pci_driver { #define PCI_FUNC( busdevfn ) ( ( (busdevfn) >> 0 ) & 0x07 ) #define PCI_BUSDEVFN( bus, slot, func ) \ ( ( (bus) << 8 ) | ( (slot) << 3 ) | ( (func) << 0 ) ) +#define PCI_FIRST_FUNC( busdevfn ) ( (busdevfn) & ~0x07 ) #define PCI_BASE_CLASS( class ) ( (class) >> 16 ) #define PCI_SUB_CLASS( class ) ( ( (class) >> 8 ) & 0xff ) @@ -385,6 +386,7 @@ extern void adjust_pci_device ( struct pci_device *pci ); extern unsigned long pci_bar_start ( struct pci_device *pci, unsigned int reg ); extern int pci_read_config ( struct pci_device *pci ); +extern int pci_find_next ( struct pci_device *pci, unsigned int busdevfn ); extern int pci_find_driver ( struct pci_device *pci ); extern int pci_probe ( struct pci_device *pci ); extern void pci_remove ( struct pci_device *pci ); diff --git a/src/include/ipxe/pci_io.h b/src/include/ipxe/pci_io.h index 7368cf48..781b77fe 100644 --- a/src/include/ipxe/pci_io.h +++ b/src/include/ipxe/pci_io.h @@ -44,6 +44,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); /* Include all architecture-independent I/O API headers */ #include +#include /* Include all architecture-dependent I/O API headers */ #include diff --git a/src/include/ipxe/portmap.h b/src/include/ipxe/portmap.h new file mode 100644 index 00000000..9b735bbc --- /dev/null +++ b/src/include/ipxe/portmap.h @@ -0,0 +1,63 @@ +#ifndef _IPXE_PORTMAP_H +#define _IPXE_PORTMAP_H + +#include +#include + +/** @file + * + * SUN ONC RPC protocol. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** PORTMAP default port */ +#define PORTMAP_PORT 111 + +/** PORTMAP protocol number */ +#define ONCRPC_PORTMAP 100000 + +/** PORTMAP version */ +#define PORTMAP_VERS 2 + + +/** TCP protocol number */ +#define PORTMAP_PROTO_TCP 6 +/** UDB protocol number */ +#define PORTMAP_PROTO_UDP 17 + + +/** + * A PORTMAP GETPORT reply + * + */ +struct portmap_getport_reply { + /** Port returned */ + uint32_t port; +}; + + +/** + * Prepare an ONC RPC session to be used as a PORTMAP session + * + * @v session ONC RPC session + * @v credential ONC RPC credential + * + * The credential parameter must not be NULL, use 'oncrpc_auth_none' if you + * don't want a particular scheme to be used. + */ +static inline void portmap_init_session ( struct oncrpc_session *session, + struct oncrpc_cred *credential) { + oncrpc_init_session ( session, credential, &oncrpc_auth_none, + ONCRPC_PORTMAP, PORTMAP_VERS ); +} + + +int portmap_getport ( struct interface *intf, struct oncrpc_session *session, + uint32_t prog, uint32_t vers, uint32_t proto ); +int portmap_get_getport_reply ( struct portmap_getport_reply *getport_reply, + struct oncrpc_reply *reply ); + + +#endif /* _IPXE_PORTMAP_H */ diff --git a/src/include/ipxe/reboot.h b/src/include/ipxe/reboot.h index 5d882d3d..97e0d5fb 100644 --- a/src/include/ipxe/reboot.h +++ b/src/include/ipxe/reboot.h @@ -55,4 +55,14 @@ FILE_LICENCE ( GPL2_OR_LATER ); */ void reboot ( int warm ); +/** + * Power off system + * + * @ret rc Return status code + * + * This function may fail, since not all systems support being powered + * off by software. + */ +int poweroff ( void ); + #endif /* _IPXE_REBOOT_H */ diff --git a/src/include/ipxe/settings.h b/src/include/ipxe/settings.h index 9b7404ac..d1666e1d 100644 --- a/src/include/ipxe/settings.h +++ b/src/include/ipxe/settings.h @@ -77,6 +77,12 @@ struct setting { /** Settings block operations */ struct settings_operations { + /** Redirect to underlying settings block (if applicable) + * + * @v settings Settings block + * @ret settings Underlying settings block + */ + struct settings * ( * redirect ) ( struct settings *settings ); /** Check applicability of setting * * @v settings Settings block @@ -179,24 +185,47 @@ struct setting_type { * This is the name exposed to the user (e.g. "string"). */ const char *name; - /** Parse formatted setting value + /** 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 ( * parse ) ( const char *value, void *buf, size_t len ); - /** Format setting value + int ( * parse ) ( struct setting_type *type, const char *value, + void *buf, size_t len ); + /** 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 ( * format ) ( const void *raw, size_t raw_len, char *buf, - size_t len ); + int ( * format ) ( struct setting_type *type, const void *raw, + size_t raw_len, char *buf, size_t len ); + /** 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 ( * denumerate ) ( struct setting_type *type, unsigned long value, + void *buf, size_t len ); + /** 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 + */ + int ( * numerate ) ( struct setting_type *type, const void *raw, + size_t raw_len, unsigned long *value ); }; /** Configuration setting type table */ @@ -235,6 +264,9 @@ struct generic_settings { struct list_head list; }; +/** A child settings block locator function */ +typedef struct settings * ( *get_child_settings_t ) ( struct settings *settings, + const char *name ); extern struct settings_operations generic_settings_operations; extern int generic_settings_store ( struct settings *settings, struct setting *setting, @@ -248,6 +280,7 @@ extern int register_settings ( struct settings *settings, struct settings *parent, const char *name ); extern void unregister_settings ( struct settings *settings ); +extern struct settings * settings_target ( struct settings *settings ); extern int setting_applies ( struct settings *settings, struct setting *setting ); extern int store_setting ( struct settings *settings, struct setting *setting, @@ -288,27 +321,35 @@ extern int setting_cmp ( struct setting *a, struct setting *b ); extern struct settings * find_child_settings ( struct settings *parent, const char *name ); +extern struct settings * autovivify_child_settings ( struct settings *parent, + const char *name ); extern const char * settings_name ( struct settings *settings ); extern struct settings * find_settings ( const char *name ); extern struct setting * find_setting ( const char *name ); - +extern int parse_setting_name ( char *name, get_child_settings_t get_child, + struct settings **settings, + struct setting *setting ); extern int setting_name ( struct settings *settings, struct setting *setting, char *buf, size_t len ); +extern int setting_format ( struct setting_type *type, const void *raw, + size_t raw_len, char *buf, size_t len ); +extern int setting_parse ( struct setting_type *type, const char *value, + void *buf, size_t len ); +extern int setting_numerate ( struct setting_type *type, const void *raw, + size_t raw_len, unsigned long *value ); +extern int setting_denumerate ( struct setting_type *type, unsigned long value, + void *buf, size_t len ); extern int fetchf_setting ( struct settings *settings, struct setting *setting, char *buf, size_t len ); +extern int fetchf_setting_copy ( struct settings *settings, + struct setting *setting, char **value ); extern int storef_setting ( struct settings *settings, struct setting *setting, const char *value ); -extern int store_named_setting ( const char *name, - struct setting_type *default_type, - const void *data, size_t len ); -extern int storef_named_setting ( const char *name, - struct setting_type *default_type, - const char *value ); -extern int fetchf_named_setting ( const char *name, char *name_buf, - size_t name_len, char *value_buf, - size_t value_len ); -extern int fetchf_named_setting_copy ( const char *name, char **data ); +extern int fetchn_setting ( struct settings *settings, struct setting *setting, + unsigned long *value ); +extern int storen_setting ( struct settings *settings, struct setting *setting, + unsigned long value ); extern char * expand_settings ( const char *string ); extern struct setting_type setting_type_string __setting_type; @@ -322,7 +363,9 @@ extern struct setting_type setting_type_uint16 __setting_type; extern struct setting_type setting_type_uint32 __setting_type; extern struct setting_type setting_type_hex __setting_type; extern struct setting_type setting_type_hexhyp __setting_type; +extern struct setting_type setting_type_hexraw __setting_type; extern struct setting_type setting_type_uuid __setting_type; +extern struct setting_type setting_type_busdevfn __setting_type; extern struct setting ip_setting __setting ( SETTING_IPv4 ); extern struct setting netmask_setting __setting ( SETTING_IPv4 ); @@ -384,16 +427,6 @@ static inline int delete_setting ( struct settings *settings, return store_setting ( settings, setting, NULL, 0 ); } -/** - * Delete named setting - * - * @v name Name of setting - * @ret rc Return status code - */ -static inline int delete_named_setting ( const char *name ) { - return store_named_setting ( name, NULL, NULL, 0 ); -} - /** * Check existence of setting * diff --git a/src/include/ipxe/tcpip.h b/src/include/ipxe/tcpip.h index 34d7f88f..0cc688a9 100644 --- a/src/include/ipxe/tcpip.h +++ b/src/include/ipxe/tcpip.h @@ -24,6 +24,16 @@ struct net_device; */ #define TCPIP_EMPTY_CSUM 0xffff +/** TCP/IP address flags */ +enum tcpip_st_flags { + /** Bind to a privileged port (less than 1024) + * + * This value is chosen as 1024 to optimise the calculations + * in tcpip_bind(). + */ + TCPIP_BIND_PRIVILEGED = 0x0400, +}; + /** * TCP/IP socket address * @@ -33,6 +43,8 @@ struct net_device; struct sockaddr_tcpip { /** Socket address family (part of struct @c sockaddr) */ sa_family_t st_family; + /** Flags */ + uint16_t st_flags; /** TCP/IP port */ uint16_t st_port; /** Padding @@ -42,7 +54,9 @@ struct sockaddr_tcpip { * family. */ char pad[ sizeof ( struct sockaddr ) - - ( sizeof ( sa_family_t ) + sizeof ( uint16_t ) ) ]; + ( sizeof ( sa_family_t ) /* st_family */ + + sizeof ( uint16_t ) /* st_flags */ + + sizeof ( uint16_t ) /* st_port */ ) ]; } __attribute__ (( may_alias )); /** @@ -125,6 +139,8 @@ extern int tcpip_tx ( struct io_buffer *iobuf, struct tcpip_protocol *tcpip, extern uint16_t generic_tcpip_continue_chksum ( uint16_t partial, const void *data, size_t len ); extern uint16_t tcpip_chksum ( const void *data, size_t len ); +extern int tcpip_bind ( struct sockaddr_tcpip *st_local, + int ( * available ) ( int port ) ); /* Use generic_tcpip_continue_chksum() if no architecture-specific * version is available diff --git a/src/include/ipxe/uri.h b/src/include/ipxe/uri.h index b7b8b441..9a134690 100644 --- a/src/include/ipxe/uri.h +++ b/src/include/ipxe/uri.h @@ -109,17 +109,27 @@ enum { * Note that this is a separate concept from a URI with an absolute * path. */ -static inline int uri_is_absolute ( struct uri *uri ) { +static inline int uri_is_absolute ( const struct uri *uri ) { return ( uri->scheme != NULL ); } +/** + * URI has an opaque part + * + * @v uri URI + * @ret has_opaque URI has an opaque part + */ +static inline int uri_has_opaque ( const struct uri *uri ) { + return ( uri->opaque && ( uri->opaque[0] != '\0' ) ); + +} /** * URI has a path * * @v uri URI * @ret has_path URI has a path */ -static inline int uri_has_path ( struct uri *uri ) { +static inline int uri_has_path ( const struct uri *uri ) { return ( uri->path && ( uri->path[0] != '\0' ) ); } @@ -133,7 +143,7 @@ static inline int uri_has_path ( struct uri *uri ) { * concept from an absolute URI. Note also that a URI may not have a * path at all. */ -static inline int uri_has_absolute_path ( struct uri *uri ) { +static inline int uri_has_absolute_path ( const struct uri *uri ) { return ( uri->path && ( uri->path[0] == '/' ) ); } @@ -147,7 +157,7 @@ static inline int uri_has_absolute_path ( struct uri *uri ) { * this is a separate concept from a relative URI. Note also that a * URI may not have a path at all. */ -static inline int uri_has_relative_path ( struct uri *uri ) { +static inline int uri_has_relative_path ( const struct uri *uri ) { return ( uri->path && ( uri->path[0] != '/' ) ); } diff --git a/src/include/linux_api.h b/src/include/linux_api.h index 94dc991f..28a3cda3 100644 --- a/src/include/linux_api.h +++ b/src/include/linux_api.h @@ -47,11 +47,13 @@ typedef __kernel_loff_t loff_t; typedef unsigned long nfds_t; typedef uint32_t useconds_t; #define MAP_FAILED ( ( void * ) -1 ) +#define SEEK_SET 0 extern long linux_syscall ( int number, ... ); extern int linux_open ( const char *pathname, int flags ); extern int linux_close ( int fd ); +extern off_t linux_lseek ( int fd, off_t offset, int whence ); extern __kernel_ssize_t linux_read ( int fd, void *buf, __kernel_size_t count ); extern __kernel_ssize_t linux_write ( int fd, const void *buf, __kernel_size_t count ); diff --git a/src/include/little_bswap.h b/src/include/little_bswap.h index a5dc9c87..92dd26ba 100644 --- a/src/include/little_bswap.h +++ b/src/include/little_bswap.h @@ -3,6 +3,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); +#define htonll(x) __bswap_64(x) +#define ntohll(x) __bswap_64(x) #define ntohl(x) __bswap_32(x) #define htonl(x) __bswap_32(x) #define ntohs(x) __bswap_16(x) diff --git a/src/include/stdlib.h b/src/include/stdlib.h index 3d30858f..bca85a23 100644 --- a/src/include/stdlib.h +++ b/src/include/stdlib.h @@ -34,19 +34,7 @@ static inline int strtoul_base ( const char **pp, int base ) return base; } -static inline 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; -} - +extern unsigned int strtoul_charval ( unsigned int charval ); extern unsigned long strtoul ( const char *p, char **endp, int base ); extern unsigned long long strtoull ( const char *p, char **endp, int base ); diff --git a/src/include/sys/time.h b/src/include/sys/time.h index 6c9b7421..2647d358 100644 --- a/src/include/sys/time.h +++ b/src/include/sys/time.h @@ -6,6 +6,8 @@ * Date and time */ +FILE_LICENCE ( GPL2_OR_LATER ); + #include /** Seconds since the Epoch diff --git a/src/include/time.h b/src/include/time.h index bc73af4c..452a544b 100644 --- a/src/include/time.h +++ b/src/include/time.h @@ -6,6 +6,8 @@ * Date and time */ +FILE_LICENCE ( GPL2_OR_LATER ); + #include #include diff --git a/src/interface/efi/efi_reboot.c b/src/interface/efi/efi_reboot.c index bfee36aa..96638c48 100644 --- a/src/interface/efi/efi_reboot.c +++ b/src/interface/efi/efi_reboot.c @@ -26,6 +26,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ +#include #include #include @@ -41,4 +42,20 @@ static void efi_reboot ( int warm ) { rs->ResetSystem ( ( warm ? EfiResetWarm : EfiResetCold ), 0, 0, NULL ); } +/** + * Power off system + * + * @ret rc Return status code + */ +static int efi_poweroff ( void ) { + EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices; + + /* Use runtime services to power off system */ + rs->ResetSystem ( EfiResetShutdown, 0, 0, NULL ); + + /* Should never happen */ + return -ECANCELED; +} + PROVIDE_REBOOT ( efi, reboot, efi_reboot ); +PROVIDE_REBOOT ( efi, poweroff, efi_poweroff ); diff --git a/src/interface/linux/linux_pci.c b/src/interface/linux/linux_pci.c new file mode 100644 index 00000000..cbd825c1 --- /dev/null +++ b/src/interface/linux/linux_pci.c @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2013 Michael Brown . + * + * 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 +#include +#include +#include +#include +#include + +/** @file + * + * iPXE PCI API for Linux + * + */ + +/** + * Open PCI configuration space + * + * @v pci PCI device + * @v flags Access mode flags + * @v where Address within configuration space + * @ret fd File handle, or negative error + */ +static int linux_pci_open ( struct pci_device *pci, int flags, + unsigned long where ) { + char filename[ 22 /* "/proc/bus/pci/xx/xx.x" + NUL */ ]; + int fd; + int rc; + + /* Construct filename */ + snprintf ( filename, sizeof ( filename ), "/proc/bus/pci/%02x/%02x.%x", + PCI_BUS ( pci->busdevfn ), PCI_SLOT ( pci->busdevfn ), + PCI_FUNC ( pci->busdevfn ) ); + + /* Open file */ + fd = linux_open ( filename, flags ); + if ( fd < 0 ) { + DBGC ( pci, "PCI could not open %s: %s\n", filename, + linux_strerror ( linux_errno ) ); + rc = -ELINUX ( linux_errno ); + goto err_open; + } + + /* Seek to location */ + if ( linux_lseek ( fd, where, SEEK_SET ) < 0 ) { + DBGC ( pci, "PCI could not seek to %s offset %#02lx: %s\n", + filename, where, linux_strerror ( linux_errno ) ); + rc = -ELINUX ( linux_errno ); + goto err_seek; + } + + return fd; + + err_seek: + linux_close ( fd ); + err_open: + return rc; +} + +/** + * Read from PCI configuration space + * + * @v pci PCI device + * @v where Address within configuration space + * @v value Data buffer + * @v len Length to read + * @ret rc Return status code + */ +int linux_pci_read ( struct pci_device *pci, unsigned long where, + unsigned long *value, size_t len ) { + uint32_t tmp = 0; + int fd; + int check_len; + int rc; + + /* Return "missing device" in case of error */ + *value = -1UL; + + /* Open configuration space */ + fd = linux_pci_open ( pci, O_RDONLY, where ); + if ( fd < 0 ) { + rc = fd; + goto err_open; + } + + /* Read value */ + check_len = linux_read ( fd, &tmp, len ); + if ( check_len < 0 ) { + DBGC ( pci, "PCI could not read from " PCI_FMT " %#02lx+%#zx: " + "%s\n", PCI_ARGS ( pci ), where, len, + linux_strerror ( linux_errno ) ); + rc = -ELINUX ( linux_errno ); + goto err_read; + } + if ( ( size_t ) check_len != len ) { + DBGC ( pci, "PCI read only %#x bytes from " PCI_FMT + " %#02lx+%#zx\n", check_len, PCI_ARGS ( pci ), + where, len ); + rc = -EIO; + goto err_read; + } + + /* Return value */ + *value = le32_to_cpu ( tmp ); + + /* Success */ + rc = 0; + + err_read: + linux_close ( fd ); + err_open: + return rc; +} + +/** + * Write to PCI configuration space + * + * @v pci PCI device + * @v where Address within configuration space + * @v value Value to write + * @v len Length of value + * @ret rc Return status code + */ +int linux_pci_write ( struct pci_device *pci, unsigned long where, + unsigned long value, size_t len ) { + uint32_t tmp; + int fd; + int check_len; + int rc; + + /* Open configuration space */ + fd = linux_pci_open ( pci, O_WRONLY, where ); + if ( fd < 0 ) { + rc = fd; + goto err_open; + } + + /* Prepare value for writing */ + tmp = cpu_to_le32 ( value ); + assert ( len <= sizeof ( tmp ) ); + + /* Write value */ + check_len = linux_write ( fd, &tmp, len ); + if ( check_len < 0 ) { + DBGC ( pci, "PCI could not write to " PCI_FMT " %#02lx+%#zx: " + "%s\n", PCI_ARGS ( pci ), where, len, + linux_strerror ( linux_errno ) ); + rc = -ELINUX ( linux_errno ); + goto err_write; + } + if ( ( size_t ) check_len != len ) { + DBGC ( pci, "PCI wrote only %#x bytes to " PCI_FMT + " %#02lx+%#zx\n", check_len, PCI_ARGS ( pci ), + where, len ); + rc = -EIO; + goto err_write; + } + + /* Success */ + rc = 0; + + err_write: + linux_close ( fd ); + err_open: + return rc; +} diff --git a/src/interface/linux/linux_time.c b/src/interface/linux/linux_time.c index 6d722aad..e3cbafec 100644 --- a/src/interface/linux/linux_time.c +++ b/src/interface/linux/linux_time.c @@ -26,6 +26,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); */ #include +#include #include #include #include diff --git a/src/interface/linux/linux_timer.c b/src/interface/linux/linux_timer.c index 6b4643a6..bf55cd18 100644 --- a/src/interface/linux/linux_timer.c +++ b/src/interface/linux/linux_timer.c @@ -18,6 +18,7 @@ FILE_LICENCE(GPL2_OR_LATER); +#include #include #include diff --git a/src/interface/smbios/smbios_settings.c b/src/interface/smbios/smbios_settings.c index d2975df4..ecd3f1d9 100644 --- a/src/interface/smbios/smbios_settings.c +++ b/src/interface/smbios/smbios_settings.c @@ -197,8 +197,8 @@ struct setting uuid_setting __setting ( SETTING_HOST ) = { .scope = &smbios_settings_scope, }; -/** Other SMBIOS named settings */ -struct setting smbios_named_settings[] __setting ( SETTING_HOST_EXTRA ) = { +/** Other SMBIOS predefined settings */ +struct setting smbios_predefined_settings[] __setting ( SETTING_HOST_EXTRA ) = { { .name = "manufacturer", .description = "Manufacturer", diff --git a/src/net/icmpv6.c b/src/net/icmpv6.c index 1a5aad3b..262ffc3f 100644 --- a/src/net/icmpv6.c +++ b/src/net/icmpv6.c @@ -47,7 +47,7 @@ int icmp6_send_solicit ( struct net_device *netdev, struct in6_addr *src __unuse nsolicit->csum = tcpip_chksum ( nsolicit, sizeof ( *nsolicit ) ); /* Solicited multicast address */ - st_dest.sin6.sin_family = AF_INET6; + st_dest.sin6.sin6_family = AF_INET6; st_dest.sin6.sin6_addr.in6_u.u6_addr8[0] = 0xff; st_dest.sin6.sin6_addr.in6_u.u6_addr8[2] = 0x02; st_dest.sin6.sin6_addr.in6_u.u6_addr16[1] = 0x0000; diff --git a/src/net/ipv6.c b/src/net/ipv6.c index 57bf94d8..d76e59cb 100644 --- a/src/net/ipv6.c +++ b/src/net/ipv6.c @@ -332,10 +332,10 @@ static int ipv6_rx ( struct io_buffer *iobuf, /* Construct socket address */ memset ( &src, 0, sizeof ( src ) ); - src.sin6.sin_family = AF_INET6; + src.sin6.sin6_family = AF_INET6; src.sin6.sin6_addr = ip6hdr->src; memset ( &dest, 0, sizeof ( dest ) ); - dest.sin6.sin_family = AF_INET6; + dest.sin6.sin6_family = AF_INET6; dest.sin6.sin6_addr = ip6hdr->dest; /* Strip header */ diff --git a/src/net/netdev_settings.c b/src/net/netdev_settings.c index 6d875719..1c5cec1a 100644 --- a/src/net/netdev_settings.c +++ b/src/net/netdev_settings.c @@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include /** @file * @@ -34,7 +35,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ -/** Network device named settings */ +/** Network device predefined settings */ struct setting mac_setting __setting ( SETTING_NETDEV ) = { .name = "mac", .description = "MAC address", @@ -45,6 +46,16 @@ struct setting machyp_setting __setting ( SETTING_NETDEV ) = { .description = "MAC address", .type = &setting_type_hexhyp, }; +struct setting bustype_setting __setting ( SETTING_NETDEV ) = { + .name = "bustype", + .description = "Bus type", + .type = &setting_type_string, +}; +struct setting busloc_setting __setting ( SETTING_NETDEV ) = { + .name = "busloc", + .description = "Bus location", + .type = &setting_type_uint32, +}; struct setting busid_setting __setting ( SETTING_NETDEV ) = { .name = "busid", .description = "Bus ID", @@ -98,6 +109,54 @@ static int netdev_fetch_mac ( struct net_device *netdev, void *data, return netdev->ll_protocol->ll_addr_len; } +/** + * Fetch bus type setting + * + * @v netdev Network device + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int netdev_fetch_bustype ( struct net_device *netdev, void *data, + size_t len ) { + static const char *bustypes[] = { + [BUS_TYPE_PCI] = "PCI", + [BUS_TYPE_ISAPNP] = "ISAPNP", + [BUS_TYPE_EISA] = "EISA", + [BUS_TYPE_MCA] = "MCA", + [BUS_TYPE_ISA] = "ISA", + }; + struct device_description *desc = &netdev->dev->desc; + const char *bustype; + + assert ( desc->bus_type < ( sizeof ( bustypes ) / + sizeof ( bustypes[0] ) ) ); + bustype = bustypes[desc->bus_type]; + assert ( bustypes != NULL ); + strncpy ( data, bustype, len ); + return strlen ( bustype ); +} + +/** + * Fetch bus location setting + * + * @v netdev Network device + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int netdev_fetch_busloc ( struct net_device *netdev, void *data, + size_t len ) { + struct device_description *desc = &netdev->dev->desc; + uint32_t busloc; + + busloc = cpu_to_be32 ( desc->location ); + if ( len > sizeof ( busloc ) ) + len = sizeof ( busloc ); + memcpy ( data, &busloc, len ); + return sizeof ( busloc ); +} + /** * Fetch bus ID setting * @@ -163,6 +222,8 @@ struct netdev_setting_operation { static struct netdev_setting_operation netdev_setting_operations[] = { { &mac_setting, netdev_store_mac, netdev_fetch_mac }, { &machyp_setting, netdev_store_mac, netdev_fetch_mac }, + { &bustype_setting, NULL, netdev_fetch_bustype }, + { &busloc_setting, NULL, netdev_fetch_busloc }, { &busid_setting, NULL, netdev_fetch_busid }, { &chip_setting, NULL, netdev_fetch_chip }, }; @@ -241,3 +302,51 @@ struct settings_operations netdev_settings_operations = { .fetch = netdev_fetch, .clear = netdev_clear, }; + +/** + * Redirect "netX" settings block + * + * @v settings Settings block + * @ret settings Underlying settings block + */ +static struct settings * netdev_redirect ( struct settings *settings ) { + struct net_device *netdev; + + /* Redirect to most recently opened network device */ + netdev = last_opened_netdev(); + if ( netdev ) { + return netdev_settings ( netdev ); + } else { + return settings; + } +} + +/** "netX" settings operations */ +static struct settings_operations netdev_redirect_settings_operations = { + .redirect = netdev_redirect, +}; + +/** "netX" settings */ +static struct settings netdev_redirect_settings = { + .refcnt = NULL, + .siblings = LIST_HEAD_INIT ( netdev_redirect_settings.siblings ), + .children = LIST_HEAD_INIT ( netdev_redirect_settings.children ), + .op = &netdev_redirect_settings_operations, +}; + +/** Initialise "netX" settings */ +static void netdev_redirect_settings_init ( void ) { + int rc; + + if ( ( rc = register_settings ( &netdev_redirect_settings, NULL, + "netX" ) ) != 0 ) { + DBG ( "Could not register netX settings: %s\n", + strerror ( rc ) ); + return; + } +} + +/** "netX" settings initialiser */ +struct init_fn netdev_redirect_settings_init_fn __init_fn ( INIT_LATE ) = { + .initialise = netdev_redirect_settings_init, +}; diff --git a/src/net/netdevice.c b/src/net/netdevice.c index 1191ebc1..5af9c6dc 100644 --- a/src/net/netdevice.c +++ b/src/net/netdevice.c @@ -670,6 +670,11 @@ void netdev_irq ( struct net_device *netdev, int enable ) { struct net_device * find_netdev ( const char *name ) { struct net_device *netdev; + /* Allow "netX" shortcut */ + if ( strcmp ( name, "netX" ) == 0 ) + return last_opened_netdev(); + + /* Identify network device by name */ list_for_each_entry ( netdev, &net_devices, list ) { if ( strcmp ( netdev->name, name ) == 0 ) return netdev; diff --git a/src/net/oncrpc/mount.c b/src/net/oncrpc/mount.c new file mode 100644 index 00000000..8838a147 --- /dev/null +++ b/src/net/oncrpc/mount.c @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2013 Marin Hannache . + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * NFS MOUNT protocol + * + */ + +/** MNT procedure number */ +#define MOUNT_MNT 1 +/** UMNT procedure number */ +#define MOUNT_UMNT 3 + +/** + * Send a MNT request + * + * @v intf Interface to send the request on + * @v session ONC RPC session + * @v mountpoinrt The path of the directory to mount. + * @ret rc Return status code + */ +int mount_mnt ( struct interface *intf, struct oncrpc_session *session, + const char *mountpoint ) { + struct oncrpc_field fields[] = { + ONCRPC_FIELD ( str, mountpoint ), + ONCRPC_FIELD_END, + }; + + return oncrpc_call ( intf, session, MOUNT_MNT, fields ); +} + +/** + * Send a UMNT request + * + * @v intf Interface to send the request on + * @v session ONC RPC session + * @v mountpoinrt The path of the directory to unmount. + * @ret rc Return status code + */ +int mount_umnt ( struct interface *intf, struct oncrpc_session *session, + const char *mountpoint ) { + struct oncrpc_field fields[] = { + ONCRPC_FIELD ( str, mountpoint ), + ONCRPC_FIELD_END, + }; + + return oncrpc_call ( intf, session, MOUNT_UMNT, fields ); +} + +/** + * Parse an MNT reply + * + * @v mnt_reply A structure where the data will be saved + * @v reply The ONC RPC reply to get data from + * @ret rc Return status code + */ +int mount_get_mnt_reply ( struct mount_mnt_reply *mnt_reply, + struct oncrpc_reply *reply ) { + if ( ! mnt_reply || ! reply ) + return -EINVAL; + + mnt_reply->status = oncrpc_iob_get_int ( reply->data ); + + switch ( mnt_reply->status ) + { + case MNT3_OK: + break; + case MNT3ERR_NOENT: + return -ENOENT; + case MNT3ERR_IO: + return -EIO; + case MNT3ERR_ACCES: + return -EACCES; + case MNT3ERR_NOTDIR: + return -ENOTDIR; + case MNT3ERR_NAMETOOLONG: + return -ENAMETOOLONG; + default: + return -EPROTO; + } + + nfs_iob_get_fh ( reply->data, &mnt_reply->fh ); + + return 0; +} diff --git a/src/net/oncrpc/nfs.c b/src/net/oncrpc/nfs.c new file mode 100644 index 00000000..b6118f91 --- /dev/null +++ b/src/net/oncrpc/nfs.c @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2013 Marin Hannache . + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Network File System protocol + * + */ + +/** NFS LOOKUP procedure */ +#define NFS_LOOKUP 3 +/** NFS READLINK procedure */ +#define NFS_READLINK 5 +/** NFS READ procedure */ +#define NFS_READ 6 + +/** + * Extract a file handle from the beginning of an I/O buffer + * + * @v io_buf I/O buffer + * @v fh File handle + * @ret size Size of the data read + */ +size_t nfs_iob_get_fh ( struct io_buffer *io_buf, struct nfs_fh *fh ) { + fh->size = oncrpc_iob_get_int ( io_buf ); + + if ( fh->size > 64 ) + return sizeof ( uint32_t ); + + memcpy (fh->fh, io_buf->data, fh->size ); + iob_pull ( io_buf, fh->size ); + + return fh->size + sizeof ( uint32_t ); +} + +/** + * Add a file handle to the end of an I/O buffer + * + * @v io_buf I/O buffer + * @v fh File handle + * @ret size Size of the data written + */ +size_t nfs_iob_add_fh ( struct io_buffer *io_buf, const struct nfs_fh *fh ) { + size_t s; + + s = oncrpc_iob_add_int ( io_buf, fh->size ); + memcpy ( iob_put ( io_buf, fh->size ), &fh->fh, fh->size ); + + return s + fh->size; +} + +/** + * Send a LOOKUP request + * + * @v intf Interface to send the request on + * @v session ONC RPC session + * @v fh The file handle of the the directory + * @v filename The file name + * @ret rc Return status code + */ +int nfs_lookup ( struct interface *intf, struct oncrpc_session *session, + const struct nfs_fh *fh, const char *filename ) { + struct oncrpc_field fields[] = { + ONCRPC_SUBFIELD ( array, fh->size, &fh->fh ), + ONCRPC_FIELD ( str, filename ), + ONCRPC_FIELD_END, + }; + + return oncrpc_call ( intf, session, NFS_LOOKUP, fields ); +} + +/** + * Send a READLINK request + * + * @v intf Interface to send the request on + * @v session ONC RPC session + * @v fh The symlink file handle + * @ret rc Return status code + */ +int nfs_readlink ( struct interface *intf, struct oncrpc_session *session, + const struct nfs_fh *fh ) { + struct oncrpc_field fields[] = { + ONCRPC_SUBFIELD ( array, fh->size, &fh->fh ), + ONCRPC_FIELD_END, + }; + + return oncrpc_call ( intf, session, NFS_READLINK, fields ); +} + +/** + * Send a READ request + * + * @v intf Interface to send the request on + * @v session ONC RPC session + * @v fh The file handle + * @v offset Offset + * @v count Byte count + * @ret rc Return status code + */ +int nfs_read ( struct interface *intf, struct oncrpc_session *session, + const struct nfs_fh *fh, uint64_t offset, uint32_t count ) { + struct oncrpc_field fields[] = { + ONCRPC_SUBFIELD ( array, fh->size, &fh->fh ), + ONCRPC_FIELD ( int64, offset ), + ONCRPC_FIELD ( int32, count ), + ONCRPC_FIELD_END, + }; + + return oncrpc_call ( intf, session, NFS_READ, fields ); +} + +/** + * Parse a LOOKUP reply + * + * @v lookup_reply A structure where the data will be saved + * @v reply The ONC RPC reply to get data from + * @ret rc Return status code + */ +int nfs_get_lookup_reply ( struct nfs_lookup_reply *lookup_reply, + struct oncrpc_reply *reply ) { + if ( ! lookup_reply || ! reply ) + return -EINVAL; + + lookup_reply->status = oncrpc_iob_get_int ( reply->data ); + switch ( lookup_reply->status ) + { + case NFS3_OK: + break; + case NFS3ERR_PERM: + return -EPERM; + case NFS3ERR_NOENT: + return -ENOENT; + case NFS3ERR_IO: + return -EIO; + case NFS3ERR_ACCES: + return -EACCES; + case NFS3ERR_NOTDIR: + return -ENOTDIR; + case NFS3ERR_NAMETOOLONG: + return -ENAMETOOLONG; + case NFS3ERR_STALE: + return -ESTALE; + case NFS3ERR_BADHANDLE: + case NFS3ERR_SERVERFAULT: + default: + return -EPROTO; + } + + nfs_iob_get_fh ( reply->data, &lookup_reply->fh ); + + if ( oncrpc_iob_get_int ( reply->data ) == 1 ) + lookup_reply->ent_type = oncrpc_iob_get_int ( reply->data ); + + return 0; +} +/** + * Parse a READLINK reply + * + * @v readlink_reply A structure where the data will be saved + * @v reply The ONC RPC reply to get data from + * @ret rc Return status code + */ +int nfs_get_readlink_reply ( struct nfs_readlink_reply *readlink_reply, + struct oncrpc_reply *reply ) { + if ( ! readlink_reply || ! reply ) + return -EINVAL; + + readlink_reply->status = oncrpc_iob_get_int ( reply->data ); + switch ( readlink_reply->status ) + { + case NFS3_OK: + break; + case NFS3ERR_IO: + return -EIO; + case NFS3ERR_ACCES: + return -EACCES; + case NFS3ERR_INVAL: + return -EINVAL; + case NFS3ERR_NOTSUPP: + return -ENOTSUP; + case NFS3ERR_STALE: + return -ESTALE; + case NFS3ERR_BADHANDLE: + case NFS3ERR_SERVERFAULT: + default: + return -EPROTO; + } + + if ( oncrpc_iob_get_int ( reply->data ) == 1 ) + iob_pull ( reply->data, 5 * sizeof ( uint32_t ) + + 8 * sizeof ( uint64_t ) ); + + readlink_reply->path_len = oncrpc_iob_get_int ( reply->data ); + readlink_reply->path = reply->data->data; + + return 0; +} + +/** + * Parse a READ reply + * + * @v read_reply A structure where the data will be saved + * @v reply The ONC RPC reply to get data from + * @ret rc Return status code + */ +int nfs_get_read_reply ( struct nfs_read_reply *read_reply, + struct oncrpc_reply *reply ) { + if ( ! read_reply || ! reply ) + return -EINVAL; + + read_reply->status = oncrpc_iob_get_int ( reply->data ); + switch ( read_reply->status ) + { + case NFS3_OK: + break; + case NFS3ERR_PERM: + return -EPERM; + case NFS3ERR_NOENT: + return -ENOENT; + case NFS3ERR_IO: + return -EIO; + case NFS3ERR_NXIO: + return -ENXIO; + case NFS3ERR_ACCES: + return -EACCES; + case NFS3ERR_INVAL: + return -EINVAL; + case NFS3ERR_STALE: + return -ESTALE; + case NFS3ERR_BADHANDLE: + case NFS3ERR_SERVERFAULT: + default: + return -EPROTO; + } + + if ( oncrpc_iob_get_int ( reply->data ) == 1 ) + { + iob_pull ( reply->data, 5 * sizeof ( uint32_t ) ); + read_reply->filesize = oncrpc_iob_get_int64 ( reply->data ); + iob_pull ( reply->data, 7 * sizeof ( uint64_t ) ); + } + + read_reply->count = oncrpc_iob_get_int ( reply->data ); + read_reply->eof = oncrpc_iob_get_int ( reply->data ); + read_reply->data_len = oncrpc_iob_get_int ( reply->data ); + read_reply->data = reply->data->data; + + if ( read_reply->count != read_reply->data_len ) + return -EPROTO; + + return 0; +} + diff --git a/src/net/oncrpc/nfs_open.c b/src/net/oncrpc/nfs_open.c new file mode 100644 index 00000000..349957ff --- /dev/null +++ b/src/net/oncrpc/nfs_open.c @@ -0,0 +1,711 @@ +/* + * Copyright (C) 2013 Marin Hannache . + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Network File System protocol + * + */ + +FEATURE ( FEATURE_PROTOCOL, "NFS", DHCP_EB_FEATURE_NFS, 1 ); + +#define NFS_RSIZE 100000 + +enum nfs_pm_state { + NFS_PORTMAP_NONE = 0, + NFS_PORTMAP_MOUNTPORT, + NFS_PORTMAP_NFSPORT, + MFS_PORTMAP_CLOSED, +}; + +enum nfs_mount_state { + NFS_MOUNT_NONE = 0, + NFS_MOUNT_MNT, + NFS_MOUNT_UMNT, + NFS_MOUNT_CLOSED, +}; + +enum nfs_state { + NFS_NONE = 0, + NFS_LOOKUP, + NFS_LOOKUP_SENT, + NFS_READLINK, + NFS_READLINK_SENT, + NFS_READ, + NFS_READ_SENT, + NFS_CLOSED, +}; + +/** + * A NFS request + * + */ +struct nfs_request { + /** Reference counter */ + struct refcnt refcnt; + /** Data transfer interface */ + struct interface xfer; + + struct interface pm_intf; + struct interface mount_intf; + struct interface nfs_intf; + + enum nfs_pm_state pm_state; + enum nfs_mount_state mount_state; + enum nfs_state nfs_state; + + struct oncrpc_session pm_session; + struct oncrpc_session mount_session; + struct oncrpc_session nfs_session; + + struct oncrpc_cred_sys auth_sys; + + char * hostname; + char * path; + char * mountpoint; + char * filename; + size_t filename_offset; + + struct nfs_fh readlink_fh; + struct nfs_fh current_fh; + uint64_t file_offset; + + size_t remaining; + int eof; +}; + +static void nfs_step ( struct nfs_request *nfs ); + +/** + * Free NFS request + * + * @v refcnt Reference counter + */ +static void nfs_free ( struct refcnt *refcnt ) { + struct nfs_request *nfs; + + nfs = container_of ( refcnt, struct nfs_request, refcnt ); + DBGC ( nfs, "NFS_OPEN %p freed\n", nfs ); + + free ( nfs->hostname ); + free ( nfs->path ); + free ( nfs->auth_sys.hostname ); + free ( nfs ); +} + +/** + * Mark NFS operation as complete + * + * @v nfs NFS request + * @v rc Return status code + */ +static void nfs_done ( struct nfs_request *nfs, int rc ) { + if ( rc == 0 && nfs->nfs_state != NFS_CLOSED ) + rc = -ECONNRESET; + + DBGC ( nfs, "NFS_OPEN %p completed (%s)\n", nfs, strerror ( rc ) ); + + intf_shutdown ( &nfs->xfer, rc ); + intf_shutdown ( &nfs->pm_intf, rc ); + intf_shutdown ( &nfs->mount_intf, rc ); + intf_shutdown ( &nfs->nfs_intf, rc ); +} + +static int nfs_connect ( struct interface *intf, uint16_t port, + const char *hostname ) { + struct sockaddr_tcpip peer; + struct sockaddr_tcpip local; + + if ( ! intf || ! hostname || ! port ) + return -EINVAL; + + memset ( &peer, 0, sizeof ( peer ) ); + memset ( &local, 0, sizeof ( local ) ); + peer.st_port = htons ( port ); + + /* Use a local port < 1024 to avoid using the 'insecure' option in + * /etc/exports file. */ + local.st_flags = TCPIP_BIND_PRIVILEGED; + + return xfer_open_named_socket ( intf, SOCK_STREAM, + ( struct sockaddr * ) &peer, hostname, + ( struct sockaddr * ) &local ); +} + +static void nfs_pm_step ( struct nfs_request *nfs ) { + int rc; + + if ( ! xfer_window ( &nfs->pm_intf ) ) + return; + + if ( nfs->pm_state == NFS_PORTMAP_NONE ) { + DBGC ( nfs, "NFS_OPEN %p GETPORT call (mount)\n", nfs ); + + rc = portmap_getport ( &nfs->pm_intf, &nfs->pm_session, + ONCRPC_MOUNT, MOUNT_VERS, + PORTMAP_PROTO_TCP ); + if ( rc != 0 ) + goto err; + + nfs->pm_state++; + return; + } + + if ( nfs->pm_state == NFS_PORTMAP_NFSPORT ) { + DBGC ( nfs, "NFS_OPEN %p GETPORT call (nfs)\n", nfs ); + + rc = portmap_getport ( &nfs->pm_intf, &nfs->pm_session, + ONCRPC_NFS, NFS_VERS, + PORTMAP_PROTO_TCP ); + if ( rc != 0 ) + goto err; + + return; + } + + return; +err: + nfs_done ( nfs, rc ); +} + +static int nfs_pm_deliver ( struct nfs_request *nfs, + struct io_buffer *io_buf, + struct xfer_metadata *meta __unused ) { + int rc; + struct oncrpc_reply reply; + struct portmap_getport_reply getport_reply; + + oncrpc_get_reply ( &nfs->pm_session, &reply, io_buf ); + if ( reply.accept_state != 0 ) + { + rc = -EPROTO; + goto err; + } + + if ( nfs->pm_state == NFS_PORTMAP_MOUNTPORT ) { + DBGC ( nfs, "NFS_OPEN %p got GETPORT reply (mount)\n", nfs ); + + rc = portmap_get_getport_reply ( &getport_reply, &reply ); + if ( rc != 0 ) + goto err; + + rc = nfs_connect ( &nfs->mount_intf, getport_reply.port, + nfs->hostname ); + if ( rc != 0 ) + goto err; + + nfs->pm_state++; + nfs_pm_step ( nfs ); + + goto done; + } + + if ( nfs->pm_state == NFS_PORTMAP_NFSPORT ) { + DBGC ( nfs, "NFS_OPEN %p got GETPORT reply (nfs)\n", nfs ); + + rc = portmap_get_getport_reply ( &getport_reply, &reply ); + if ( rc != 0 ) + goto err; + + rc = nfs_connect ( &nfs->nfs_intf, getport_reply.port, + nfs->hostname ); + if ( rc != 0 ) + goto err; + + intf_shutdown ( &nfs->pm_intf, 0 ); + nfs->pm_state++; + + goto done; + } + + rc = -EPROTO; +err: + nfs_done ( nfs, rc ); +done: + free_iob ( io_buf ); + return 0; +} + +static void nfs_mount_step ( struct nfs_request *nfs ) { + int rc; + + if ( ! xfer_window ( &nfs->mount_intf ) ) + return; + + if ( nfs->mount_state == NFS_MOUNT_NONE ) { + DBGC ( nfs, "NFS_OPEN %p MNT call (%s)\n", nfs, + nfs->mountpoint ); + + rc = mount_mnt ( &nfs->mount_intf, &nfs->mount_session, + nfs->mountpoint ); + if ( rc != 0 ) + goto err; + + nfs->mount_state++; + return; + } + + if ( nfs->mount_state == NFS_MOUNT_UMNT ) { + DBGC ( nfs, "NFS_OPEN %p UMNT call\n", nfs ); + + rc = mount_umnt ( &nfs->mount_intf, &nfs->mount_session, + nfs->mountpoint ); + if ( rc != 0 ) + goto err; + } + + return; +err: + nfs_done ( nfs, rc ); +} + +static int nfs_mount_deliver ( struct nfs_request *nfs, + struct io_buffer *io_buf, + struct xfer_metadata *meta __unused ) { + int rc; + char *sep; + struct oncrpc_reply reply; + struct mount_mnt_reply mnt_reply; + + oncrpc_get_reply ( &nfs->mount_session, &reply, io_buf ); + if ( reply.accept_state != 0 ) + { + rc = -EPROTO; + goto err; + } + + if ( nfs->mount_state == NFS_MOUNT_MNT ) { + DBGC ( nfs, "NFS_OPEN %p got MNT reply\n", nfs ); + rc = mount_get_mnt_reply ( &mnt_reply, &reply ); + if ( rc != 0 ) { + if ( mnt_reply.status != MNT3ERR_NOTDIR ) + goto err; + + if ( strcmp ( nfs->mountpoint, "/" ) == 0 ) + goto err; + + sep = strrchr ( nfs->mountpoint, '/' ); + nfs->filename[-1] = '/'; + nfs->filename = sep + 1; + *sep = '\0'; + + DBGC ( nfs, "NFS_OPEN %p ENOTDIR received retrying" \ + "with %s\n", nfs, nfs->mountpoint ); + goto done; + } + + nfs->current_fh = mnt_reply.fh; + nfs->nfs_state = NFS_LOOKUP; + nfs_step ( nfs ); + + goto done; + } + + if ( nfs->mount_state == NFS_MOUNT_UMNT ) { + DBGC ( nfs, "NFS_OPEN %p got UMNT reply\n", nfs ); + nfs_done ( nfs, 0 ); + + goto done; + } + + rc = -EPROTO; +err: + nfs_done ( nfs, rc ); +done: + free_iob ( io_buf ); + return 0; +} + +static void nfs_step ( struct nfs_request *nfs ) { + int rc; + char *path_component = NULL; + + if ( ! xfer_window ( &nfs->nfs_intf ) ) + return; + + if ( nfs->nfs_state == NFS_LOOKUP ) { + while ( path_component == NULL || path_component[0] == '\0') { + path_component = nfs->filename; + while ( *nfs->filename != '\0' ) { + nfs->filename_offset++; + if ( *nfs->filename++ == '/' ) { + *(nfs->filename - 1) = '\0'; + break; + } + } + } + + DBGC ( nfs, "NFS_OPEN %p LOOKUP call (%s)\n", nfs, + path_component ); + + rc = nfs_lookup ( &nfs->nfs_intf, &nfs->nfs_session, + &nfs->current_fh, path_component ); + if ( rc != 0 ) + goto err; + + nfs->nfs_state++; + return; + } + + + if ( nfs->nfs_state == NFS_READLINK ) { + DBGC ( nfs, "NFS_OPEN %p READLINK call\n", nfs ); + + rc = nfs_readlink ( &nfs->nfs_intf, &nfs->nfs_session, + &nfs->readlink_fh ); + if ( rc != 0 ) + goto err; + + nfs->nfs_state++; + return; + } + + if ( nfs->nfs_state == NFS_READ ) { + DBGC ( nfs, "NFS_OPEN %p READ call\n", nfs ); + + rc = nfs_read ( &nfs->nfs_intf, &nfs->nfs_session, + &nfs->current_fh, nfs->file_offset, + NFS_RSIZE ); + if ( rc != 0 ) + goto err; + + nfs->nfs_state++; + return; + } + + return; +err: + nfs_done ( nfs, rc ); +} + +static int nfs_deliver ( struct nfs_request *nfs, + struct io_buffer *io_buf, + struct xfer_metadata *meta __unused ) { + int rc; + struct oncrpc_reply reply; + + if ( nfs->remaining == 0 ) { + oncrpc_get_reply ( &nfs->nfs_session, &reply, io_buf ); + if ( reply.accept_state != 0 ) { + rc = -EPROTO; + goto err; + } + } + + if ( nfs->nfs_state == NFS_LOOKUP_SENT ) { + struct nfs_lookup_reply lookup_reply; + + DBGC ( nfs, "NFS_OPEN %p got LOOKUP reply\n", nfs ); + + rc = nfs_get_lookup_reply ( &lookup_reply, &reply ); + if ( rc != 0 ) + goto err; + + if ( lookup_reply.ent_type == NFS_ATTR_SYMLINK ) { + nfs->readlink_fh = lookup_reply.fh; + nfs->nfs_state = NFS_READLINK; + } else { + nfs->current_fh = lookup_reply.fh; + + if ( nfs->filename[0] == '\0' ) + nfs->nfs_state = NFS_READ; + else + nfs->nfs_state--; + } + + nfs_step ( nfs ); + goto done; + } + + if ( nfs->nfs_state == NFS_READLINK_SENT ) { + char *new_filename; + struct nfs_readlink_reply readlink_reply; + + DBGC ( nfs, "NFS_OPEN %p got READLINK reply\n", nfs ); + + rc = nfs_get_readlink_reply ( &readlink_reply, &reply ); + if ( rc != 0 ) + goto err; + + if ( readlink_reply.path_len == 0 ) + return -EINVAL; + + if ( readlink_reply.path[0] == '/' ) { + if ( strncmp ( readlink_reply.path, nfs->mountpoint, + strlen ( nfs->mountpoint ) ) != 0 ) + return -EINVAL; + + /* The mountpoint part of the path is ended by a '/' */ + if ( strlen ( nfs->mountpoint ) != + readlink_reply.path_len ) { + readlink_reply.path += 1; + readlink_reply.path_len -= 1; + } + + /* We are considering the last part of the absolute + * path as a relative path from the mountpoint. + */ + readlink_reply.path += strlen ( nfs->mountpoint ); + readlink_reply.path_len -= strlen ( nfs->mountpoint ); + } + + new_filename = malloc ( readlink_reply.path_len + + strlen ( nfs->filename ) + 2 ); + if ( ! new_filename ) { + rc = -ENOMEM; + goto err; + } + + memcpy ( new_filename, readlink_reply.path, + readlink_reply.path_len ); + strcpy ( new_filename + readlink_reply.path_len + 1, + nfs->filename ); + new_filename[readlink_reply.path_len] = '/'; + + free ( nfs->filename - nfs->filename_offset ); + nfs->filename = new_filename; + nfs->filename_offset = 0; + + DBGC ( nfs, "NFS_OPEN %p new filename: %s\n", nfs, + nfs->filename ); + + nfs->nfs_state = NFS_LOOKUP; + nfs_step ( nfs ); + goto done; + } + + if ( nfs->nfs_state == NFS_READ_SENT ) { + if ( nfs->remaining == 0 ) { + DBGC ( nfs, "NFS_OPEN %p got READ reply\n", nfs ); + + struct nfs_read_reply read_reply; + + rc = nfs_get_read_reply ( &read_reply, &reply ); + if ( rc != 0 ) + goto err; + + if ( nfs->file_offset == 0 ) { + DBGC2 ( nfs, "NFS_OPEN %p size: %llu bytes\n", + nfs, read_reply.filesize ); + + xfer_seek ( &nfs->xfer, read_reply.filesize ); + xfer_seek ( &nfs->xfer, 0 ); + } + + nfs->file_offset += read_reply.count; + nfs->remaining = read_reply.count; + nfs->eof = read_reply.eof; + } + + size_t len = iob_len ( io_buf ); + if ( len > nfs->remaining ) + iob_unput ( io_buf, len - nfs->remaining ); + + nfs->remaining -= iob_len ( io_buf ); + + DBGC ( nfs, "NFS_OPEN %p got %zd bytes\n", nfs, + iob_len ( io_buf ) ); + + rc = xfer_deliver_iob ( &nfs->xfer, iob_disown ( io_buf ) ); + if ( rc != 0 ) + goto err; + + if ( nfs->remaining == 0 ) { + if ( ! nfs->eof ) { + nfs->nfs_state--; + nfs_step ( nfs ); + } else { + intf_shutdown ( &nfs->nfs_intf, 0 ); + nfs->nfs_state++; + nfs->mount_state++; + nfs_mount_step ( nfs ); + } + } + + return 0; + } + + rc = -EPROTO; +err: + nfs_done ( nfs, rc ); +done: + free_iob ( io_buf ); + return 0; +} + +/***************************************************************************** + * Interfaces + * + */ + +static struct interface_operation nfs_xfer_operations[] = { + INTF_OP ( intf_close, struct nfs_request *, nfs_done ), +}; + +/** NFS data transfer interface descriptor */ +static struct interface_descriptor nfs_xfer_desc = + INTF_DESC ( struct nfs_request, xfer, nfs_xfer_operations ); + +static struct interface_operation nfs_pm_operations[] = { + INTF_OP ( intf_close, struct nfs_request *, nfs_done ), + INTF_OP ( xfer_deliver, struct nfs_request *, nfs_pm_deliver ), + INTF_OP ( xfer_window_changed, struct nfs_request *, nfs_pm_step ), +}; + +static struct interface_descriptor nfs_pm_desc = + INTF_DESC ( struct nfs_request, pm_intf, nfs_pm_operations ); + +static struct interface_operation nfs_mount_operations[] = { + INTF_OP ( intf_close, struct nfs_request *, nfs_done ), + INTF_OP ( xfer_deliver, struct nfs_request *, nfs_mount_deliver ), + INTF_OP ( xfer_window_changed, struct nfs_request *, nfs_mount_step ), +}; + +static struct interface_descriptor nfs_mount_desc = + INTF_DESC ( struct nfs_request, mount_intf, nfs_mount_operations ); + +static struct interface_operation nfs_operations[] = { + INTF_OP ( intf_close, struct nfs_request *, nfs_done ), + INTF_OP ( xfer_deliver, struct nfs_request *, nfs_deliver ), + INTF_OP ( xfer_window_changed, struct nfs_request *, nfs_step ), +}; + +static struct interface_descriptor nfs_desc = + INTF_DESC_PASSTHRU ( struct nfs_request, nfs_intf, nfs_operations, + xfer ); + +/***************************************************************************** + * + * URI opener + * + */ + +static int nfs_parse_uri ( struct nfs_request *nfs, const struct uri *uri ) { + int rc; + + if ( ! uri || ! uri->host || ! uri->path ) + return -EINVAL; + + if ( ! ( nfs->path = strdup ( uri->path ) ) ) + return -ENOMEM; + + if ( ! ( nfs->hostname = strdup ( uri->host ) ) ) { + rc = -ENOMEM; + goto err_hostname; + } + + nfs->filename = basename ( nfs->path ); + nfs->mountpoint = dirname ( nfs->path ); + + if ( nfs->filename[0] == '\0' ) + goto err_filename; + + DBGC ( nfs, "NFS_OPEN %p URI parsed: (mountpoint=%s, filename=%s)\n", + nfs, nfs->mountpoint, nfs->filename ); + + return 0; + +err_filename: + rc = -EINVAL; + free ( nfs->hostname ); +err_hostname: + free ( nfs->path ); + return rc; +} + +/** + * Initiate a NFS connection + * + * @v xfer Data transfer interface + * @v uri Uniform Resource Identifier + * @ret rc Return status code + */ +static int nfs_open ( struct interface *xfer, struct uri *uri ) { + int rc; + struct nfs_request *nfs; + + nfs = zalloc ( sizeof ( *nfs ) ); + if ( ! nfs ) + return -ENOMEM; + + rc = nfs_parse_uri( nfs, uri ); + if ( rc != 0 ) + goto err_uri; + + rc = oncrpc_init_cred_sys ( &nfs->auth_sys ); + if ( rc != 0 ) + goto err_cred; + + ref_init ( &nfs->refcnt, nfs_free ); + intf_init ( &nfs->xfer, &nfs_xfer_desc, &nfs->refcnt ); + intf_init ( &nfs->pm_intf, &nfs_pm_desc, &nfs->refcnt ); + intf_init ( &nfs->mount_intf, &nfs_mount_desc, &nfs->refcnt ); + intf_init ( &nfs->nfs_intf, &nfs_desc, &nfs->refcnt ); + + portmap_init_session ( &nfs->pm_session, &nfs->auth_sys.credential ); + mount_init_session ( &nfs->mount_session, &nfs->auth_sys.credential ); + nfs_init_session ( &nfs->nfs_session, &nfs->auth_sys.credential ); + + rc = nfs_connect ( &nfs->pm_intf, PORTMAP_PORT, nfs->hostname ); + if ( rc != 0 ) + goto err_connect; + + /* Attach to parent interface, mortalise self, and return */ + intf_plug_plug ( &nfs->xfer, xfer ); + ref_put ( &nfs->refcnt ); + + return 0; + +err_connect: + free ( nfs->auth_sys.hostname ); +err_cred: +err_uri: + free ( nfs ); + return rc; +} + +/** NFS URI opener */ +struct uri_opener nfs_uri_opener __uri_opener = { + .scheme = "nfs", + .open = nfs_open, +}; diff --git a/src/net/oncrpc/oncrpc_iob.c b/src/net/oncrpc/oncrpc_iob.c new file mode 100644 index 00000000..be51805e --- /dev/null +++ b/src/net/oncrpc/oncrpc_iob.c @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2013 Marin Hannache . + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * SUN ONC RPC protocol + * + */ + +size_t oncrpc_iob_add_fields ( struct io_buffer *io_buf, + const struct oncrpc_field fields[] ) { + size_t i; + size_t s = 0; + + struct oncrpc_field f; + + if ( ! io_buf ) + return 0; + + for ( i = 0; fields[i].type != oncrpc_none; i++ ) { + f = fields[i]; + switch ( f.type ) { + case oncrpc_int32: + s += oncrpc_iob_add_int ( io_buf, f.value.int32 ); + break; + + case oncrpc_int64: + s += oncrpc_iob_add_int64 ( io_buf, f.value.int64 ); + break; + + case oncrpc_str: + s += oncrpc_iob_add_string ( io_buf, f.value.str ); + break; + + case oncrpc_array: + s += oncrpc_iob_add_array ( io_buf, + f.value.array.length, + f.value.array.ptr ); + break; + + case oncrpc_intarray: + s += oncrpc_iob_add_intarray ( io_buf, + f.value.intarray.length, + f.value.intarray.ptr ); + break; + + case oncrpc_cred: + s += oncrpc_iob_add_cred ( io_buf, f.value.cred); + break; + + default: + return s; + } + } + + return s; +} + +/** + * Add an array of bytes to the end of an I/O buffer + * + * @v io_buf I/O buffer + * @v val String + * @ret size Size of the data written + * + * In the ONC RPC protocol, every data is four byte paded, we add padding when + * necessary by using oncrpc_align() + */ +size_t oncrpc_iob_add_array ( struct io_buffer *io_buf, size_t length, + const void *data ) { + size_t padding = oncrpc_align ( length ) - length; + + oncrpc_iob_add_int ( io_buf, length ); + memcpy ( iob_put ( io_buf, length ), data, length ); + memset ( iob_put ( io_buf, padding ), 0, padding ); + + return length + padding + sizeof ( uint32_t ); +} + +/** + * Add an int array to the end of an I/O buffer + * + * @v io_buf I/O buffer + * @v length Length od the array + * @v val Int array + * @ret size Size of the data written + */ +size_t oncrpc_iob_add_intarray ( struct io_buffer *io_buf, size_t length, + const uint32_t *array ) { + size_t i; + + oncrpc_iob_add_int ( io_buf, length ); + + for ( i = 0; i < length; ++i ) + oncrpc_iob_add_int ( io_buf, array[i] ); + + return ( ( length + 1 ) * sizeof ( uint32_t ) ); +} + +/** + * Add credential information to the end of an I/O buffer + * + * @v io_buf I/O buffer + * @v cred Credential information + * @ret size Size of the data written + */ +size_t oncrpc_iob_add_cred ( struct io_buffer *io_buf, + const struct oncrpc_cred *cred ) { + struct oncrpc_cred_sys *syscred; + size_t s; + + struct oncrpc_field credfields[] = { + ONCRPC_FIELD ( int32, cred->flavor ), + ONCRPC_FIELD ( int32, cred->length ), + ONCRPC_FIELD_END, + }; + + if ( ! io_buf || ! cred ) + return 0; + + s = oncrpc_iob_add_fields ( io_buf, credfields); + + switch ( cred->flavor ) { + case ONCRPC_AUTH_NONE: + break; + + case ONCRPC_AUTH_SYS: + syscred = container_of ( cred, struct oncrpc_cred_sys, + credential ); + + struct oncrpc_field syscredfields[] = { + ONCRPC_FIELD ( int32, syscred->stamp ), + ONCRPC_FIELD ( str, syscred->hostname ), + ONCRPC_FIELD ( int32, syscred->uid ), + ONCRPC_FIELD ( int32, syscred->gid ), + ONCRPC_SUBFIELD ( intarray, syscred->aux_gid_len, + syscred->aux_gid ), + ONCRPC_FIELD_END, + }; + + s += oncrpc_iob_add_fields ( io_buf, syscredfields ); + break; + } + + return s; +} + +/** + * Get credential information from the beginning of an I/O buffer + * + * @v io_buf I/O buffer + * @v cred Struct where the information will be saved + * @ret size Size of the data read + */ +size_t oncrpc_iob_get_cred ( struct io_buffer *io_buf, + struct oncrpc_cred *cred ) { + if ( cred == NULL ) + return * ( uint32_t * ) io_buf->data; + + cred->flavor = oncrpc_iob_get_int ( io_buf ); + cred->length = oncrpc_iob_get_int ( io_buf ); + + iob_pull ( io_buf, cred->length ); + + return ( 2 * sizeof ( uint32_t ) + cred->length ); +} diff --git a/src/net/oncrpc/portmap.c b/src/net/oncrpc/portmap.c new file mode 100644 index 00000000..df62221d --- /dev/null +++ b/src/net/oncrpc/portmap.c @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2013 Marin Hannache . + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * PORTMAPPER protocol. + * + */ + +/** PORTMAP GETPORT procedure. */ +#define PORTMAP_GETPORT 3 + +/** + * Send a GETPORT request + * + * @v intf Interface to send the request on + * @v session ONC RPC session + * @v prog ONC RPC program number + * @v vers ONC RPC rogram version number + * @v proto Protocol (TCP or UDP) + * @ret rc Return status code + */ +int portmap_getport ( struct interface *intf, struct oncrpc_session *session, + uint32_t prog, uint32_t vers, uint32_t proto ) { + struct oncrpc_field fields[] = { + ONCRPC_FIELD ( int32, prog ), + ONCRPC_FIELD ( int32, vers ), + ONCRPC_FIELD ( int32, proto ), + ONCRPC_FIELD ( int32, 0 ), /* The port field is only meaningful + in GETPORT reply */ + ONCRPC_FIELD_END, + }; + + return oncrpc_call ( intf, session, PORTMAP_GETPORT, fields ); +} + +/** + * Parse a GETPORT reply + * + * @v getport_reply A structure where the data will be saved + * @v reply The ONC RPC reply to get data from + * @ret rc Return status code + */ +int portmap_get_getport_reply ( struct portmap_getport_reply *getport_reply, + struct oncrpc_reply *reply ) { + if ( ! getport_reply || ! reply ) + return -EINVAL; + + getport_reply->port = oncrpc_iob_get_int ( reply->data ); + if ( getport_reply == 0 || getport_reply->port >= 65536 ) + return -EINVAL; + + return 0; +} diff --git a/src/net/tcp.c b/src/net/tcp.c index 02bc3a90..0e18c831 100644 --- a/src/net/tcp.c +++ b/src/net/tcp.c @@ -157,6 +157,7 @@ static LIST_HEAD ( tcp_conns ); static struct interface_descriptor tcp_xfer_desc; static void tcp_expired ( struct retry_timer *timer, int over ); static void tcp_wait_expired ( struct retry_timer *timer, int over ); +static struct tcp_connection * tcp_demux ( unsigned int local_port ); static int tcp_rx_ack ( struct tcp_connection *tcp, uint32_t ack, uint32_t win ); @@ -226,46 +227,14 @@ tcp_dump_flags ( struct tcp_connection *tcp, unsigned int flags ) { */ /** - * Bind TCP connection to local port + * Check if local TCP port is available * - * @v tcp TCP connection * @v port Local port number - * @ret rc Return status code - * - * If the port is 0, the connection is assigned an available port - * between 1024 and 65535. + * @ret port Local port number, or negative error */ -static int tcp_bind ( struct tcp_connection *tcp, unsigned int port ) { - struct tcp_connection *existing; - uint16_t try_port; - unsigned int i; +static int tcp_port_available ( int port ) { - /* If no port is specified, find an available port */ - if ( ! port ) { - try_port = random(); - for ( i = 0 ; i < 65536 ; i++ ) { - try_port++; - if ( try_port < 1024 ) - continue; - if ( tcp_bind ( tcp, try_port ) == 0 ) - return 0; - } - DBGC ( tcp, "TCP %p could not bind: no free ports\n", tcp ); - return -EADDRINUSE; - } - - /* Attempt bind to local port */ - list_for_each_entry ( existing, &tcp_conns, list ) { - if ( existing->local_port == port ) { - DBGC ( tcp, "TCP %p could not bind: port %d in use\n", - tcp, port ); - return -EADDRINUSE; - } - } - tcp->local_port = port; - - DBGC ( tcp, "TCP %p bound to port %d\n", tcp, port ); - return 0; + return ( tcp_demux ( port ) ? -EADDRINUSE : port ); } /** @@ -281,7 +250,7 @@ static int tcp_open ( struct interface *xfer, struct sockaddr *peer, struct sockaddr_tcpip *st_peer = ( struct sockaddr_tcpip * ) peer; struct sockaddr_tcpip *st_local = ( struct sockaddr_tcpip * ) local; struct tcp_connection *tcp; - unsigned int bind_port; + int port; int rc; /* Allocate and initialise structure */ @@ -303,9 +272,15 @@ static int tcp_open ( struct interface *xfer, struct sockaddr *peer, memcpy ( &tcp->peer, st_peer, sizeof ( tcp->peer ) ); /* Bind to local port */ - bind_port = ( st_local ? ntohs ( st_local->st_port ) : 0 ); - if ( ( rc = tcp_bind ( tcp, bind_port ) ) != 0 ) + port = tcpip_bind ( st_local, tcp_port_available ); + if ( port < 0 ) { + rc = port; + DBGC ( tcp, "TCP %p could not bind: %s\n", + tcp, strerror ( rc ) ); goto err; + } + tcp->local_port = port; + DBGC ( tcp, "TCP %p bound to port %d\n", tcp, tcp->local_port ); /* Start timer to initiate SYN */ start_timer_nodelay ( &tcp->timer ); @@ -1216,10 +1191,8 @@ static int tcp_rx ( struct io_buffer *iobuf, tcp_dump_flags ( tcp, tcphdr->flags ); DBGC2 ( tcp, "\n" ); - /* If no connection was found, send RST */ + /* If no connection was found, silently drop packet */ if ( ! tcp ) { - /* Transmitting RST costs precious neighbor table space, and this is no worse than most boot roms */ - /* tcp_xmit_reset ( tcp, st_src, tcphdr ); */ rc = -ENOTCONN; goto discard; } diff --git a/src/net/tcp/oncrpc.c b/src/net/tcp/oncrpc.c new file mode 100644 index 00000000..819d3179 --- /dev/null +++ b/src/net/tcp/oncrpc.c @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2013 Marin Hannache . + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * SUN ONC RPC protocol + * + */ + +/** Set most significant bit to 1. */ +#define SET_LAST_FRAME( x ) ( (x) | 1 << 31 ) +#define GET_FRAME_SIZE( x ) ( (x) & ~( 1 << 31 ) ) + +#define ONCRPC_CALL 0 +#define ONCRPC_REPLY 1 + +/** AUTH NONE authentication flavor */ +struct oncrpc_cred oncrpc_auth_none = { + .flavor = ONCRPC_AUTH_NONE, + .length = 0 +}; + +struct setting uid_setting __setting ( SETTING_AUTH ) = { + .name = "uid", + .description = "User ID", + .tag = DHCP_EB_UID, + .type = &setting_type_uint32 +}; + +struct setting gid_setting __setting ( SETTING_AUTH ) = { + .name = "gid", + .description = "Group ID", + .tag = DHCP_EB_GID, + .type = &setting_type_uint32 +}; + +/** + * Initialize an ONC RPC AUTH SYS credential structure + * + * @v auth_sys The structure to initialize + * + * The hostname field is filled with the value of the hostname setting, if the + * hostname setting is empty, PRODUCT_SHORT_NAME (usually "iPXE") is used + * instead. + */ +int oncrpc_init_cred_sys ( struct oncrpc_cred_sys *auth_sys ) { + if ( ! auth_sys ) + return -EINVAL; + + fetch_string_setting_copy ( NULL, &hostname_setting, + &auth_sys->hostname ); + if ( ! auth_sys->hostname ) + if ( ! ( auth_sys->hostname = strdup ( PRODUCT_SHORT_NAME ) ) ) + return -ENOMEM; + + auth_sys->uid = fetch_uintz_setting ( NULL, &uid_setting ); + auth_sys->gid = fetch_uintz_setting ( NULL, &uid_setting ); + auth_sys->aux_gid_len = 0; + auth_sys->stamp = 0; + + auth_sys->credential.flavor = ONCRPC_AUTH_SYS; + auth_sys->credential.length = 16 + + oncrpc_strlen ( auth_sys->hostname ); + + return 0; +} + +/** + * Prepare an ONC RPC session structure to be used by the ONC RPC layer + * + * @v session ONC RPC session + * @v credential Credential structure pointer + * @v verifier Verifier structure pointer + * @v prog_name ONC RPC program number + * @v prog_vers ONC RPC program version number + */ +void oncrpc_init_session ( struct oncrpc_session *session, + struct oncrpc_cred *credential, + struct oncrpc_cred *verifier, uint32_t prog_name, + uint32_t prog_vers ) { + if ( ! session ) + return; + + session->rpc_id = rand(); + session->credential = credential; + session->verifier = verifier; + session->prog_name = prog_name; + session->prog_vers = prog_vers; +} + +int oncrpc_call ( struct interface *intf, struct oncrpc_session *session, + uint32_t proc_name, const struct oncrpc_field fields[] ) { + int rc; + size_t frame_size; + struct io_buffer *io_buf; + + if ( ! session ) + return -EINVAL; + + struct oncrpc_field header[] = { + ONCRPC_FIELD ( int32, 0 ), + ONCRPC_FIELD ( int32, ++session->rpc_id ), + ONCRPC_FIELD ( int32, ONCRPC_CALL ), + ONCRPC_FIELD ( int32, ONCRPC_VERS ), + ONCRPC_FIELD ( int32, session->prog_name ), + ONCRPC_FIELD ( int32, session->prog_vers ), + ONCRPC_FIELD ( int32, proc_name ), + ONCRPC_FIELD ( cred, session->credential ), + ONCRPC_FIELD ( cred, session->verifier ), + ONCRPC_FIELD_END, + }; + + frame_size = oncrpc_compute_size ( header ); + frame_size += oncrpc_compute_size ( fields ); + + io_buf = alloc_iob ( frame_size ); + if ( ! io_buf ) + return -ENOBUFS; + + header[0].value.int32 = SET_LAST_FRAME ( frame_size - + sizeof ( uint32_t ) ); + + oncrpc_iob_add_fields ( io_buf, header ); + oncrpc_iob_add_fields ( io_buf, fields ); + + rc = xfer_deliver_iob ( intf, io_buf ); + if ( rc != 0 ) + free_iob ( io_buf ); + + return rc; +} + +size_t oncrpc_compute_size ( const struct oncrpc_field fields[] ) { + + size_t i; + size_t size = 0; + + for ( i = 0; fields[i].type != oncrpc_none; i++ ) { + switch ( fields[i].type ) { + case oncrpc_int32: + size += sizeof ( uint32_t ); + break; + + case oncrpc_int64: + size += sizeof ( uint64_t ); + break; + + case oncrpc_str: + size += oncrpc_strlen ( fields[i].value.str ); + break; + + case oncrpc_array: + size += oncrpc_align ( fields[i].value.array.length ); + size += sizeof ( uint32_t ); + break; + + case oncrpc_intarray: + size += sizeof ( uint32_t ) * + fields[i].value.intarray.length; + size += sizeof ( uint32_t ); + break; + + case oncrpc_cred: + size += fields[i].value.cred->length; + size += 2 * sizeof ( uint32_t ); + break; + + default: + return size; + } + } + + return size; +} + +/** + * Parse an I/O buffer to extract a ONC RPC REPLY + * @v session ONC RPC session + * @v reply Reply structure where data will be saved + * @v io_buf I/O buffer + */ +int oncrpc_get_reply ( struct oncrpc_session *session __unused, + struct oncrpc_reply *reply, struct io_buffer *io_buf ) { + if ( ! reply || ! io_buf ) + return -EINVAL; + + reply->frame_size = GET_FRAME_SIZE ( oncrpc_iob_get_int ( io_buf ) ); + reply->rpc_id = oncrpc_iob_get_int ( io_buf ); + + /* iPXE has no support for handling ONC RPC call */ + if ( oncrpc_iob_get_int ( io_buf ) != ONCRPC_REPLY ) + return -ENOSYS; + + reply->reply_state = oncrpc_iob_get_int ( io_buf ); + + if ( reply->reply_state == 0 ) + { + /* verifier.flavor */ + oncrpc_iob_get_int ( io_buf ); + /* verifier.length */ + iob_pull ( io_buf, oncrpc_iob_get_int ( io_buf )); + + /* We don't use the verifier in iPXE, let it be an empty + verifier. */ + reply->verifier = &oncrpc_auth_none; + } + + reply->accept_state = oncrpc_iob_get_int ( io_buf ); + reply->data = io_buf; + + return 0; +} diff --git a/src/net/tcpip.c b/src/net/tcpip.c index 8e187f7e..721a4e48 100644 --- a/src/net/tcpip.c +++ b/src/net/tcpip.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -133,3 +134,46 @@ uint16_t generic_tcpip_continue_chksum ( uint16_t partial, uint16_t tcpip_chksum ( const void *data, size_t len ) { return tcpip_continue_chksum ( TCPIP_EMPTY_CSUM, data, len ); } + +/** + * Bind to local TCP/IP port + * + * @v st_local Local TCP/IP socket address, or NULL + * @v available Function to check port availability + * @ret port Local port number, or negative error + */ +int tcpip_bind ( struct sockaddr_tcpip *st_local, + int ( * available ) ( int port ) ) { + uint16_t flags = 0; + uint16_t try_port = 0; + uint16_t min_port; + uint16_t max_port; + unsigned int offset; + unsigned int i; + + /* Extract parameters from local socket address */ + if ( st_local ) { + flags = st_local->st_flags; + try_port = ntohs ( st_local->st_port ); + } + + /* If an explicit port is specified, check its availability */ + if ( try_port ) + return available ( try_port ); + + /* Otherwise, find an available port in the range [1,1023] or + * [1025,65535] as appropriate. + */ + min_port = ( ( ( ! flags ) & TCPIP_BIND_PRIVILEGED ) + 1 ); + max_port = ( ( flags & TCPIP_BIND_PRIVILEGED ) - 1 ); + offset = random(); + for ( i = 0 ; i <= max_port ; i++ ) { + try_port = ( ( i + offset ) & max_port ); + if ( try_port < min_port ) + continue; + if ( available ( try_port ) < 0 ) + continue; + return try_port; + } + return -EADDRINUSE; +} diff --git a/src/net/udp.c b/src/net/udp.c index 20badb7f..edc7488a 100644 --- a/src/net/udp.c +++ b/src/net/udp.c @@ -48,45 +48,19 @@ static struct interface_descriptor udp_xfer_desc; struct tcpip_protocol udp_protocol __tcpip_protocol; /** - * Bind UDP connection to local port + * Check if local UDP port is available * - * @v udp UDP connection - * @ret rc Return status code - * - * Opens the UDP connection and binds to the specified local port. If - * no local port is specified, the first available port will be used. + * @v port Local port number + * @ret port Local port number, or negative error */ -static int udp_bind ( struct udp_connection *udp ) { - struct udp_connection *existing; - static uint16_t try_port = 1023; +static int udp_port_available ( int port ) { + struct udp_connection *udp; - /* If no port specified, find the first available port */ - if ( ! udp->local.st_port ) { - while ( try_port ) { - try_port++; - if ( try_port < 1024 ) - continue; - udp->local.st_port = htons ( try_port ); - if ( udp_bind ( udp ) == 0 ) - return 0; - } - return -EADDRINUSE; - } - - /* Attempt bind to local port */ - list_for_each_entry ( existing, &udp_conns, list ) { - if ( existing->local.st_port == udp->local.st_port ) { - DBGC ( udp, "UDP %p could not bind: port %d in use\n", - udp, ntohs ( udp->local.st_port ) ); + list_for_each_entry ( udp, &udp_conns, list ) { + if ( udp->local.st_port == htons ( port ) ) return -EADDRINUSE; - } } - - /* Add to UDP connection list */ - DBGC ( udp, "UDP %p bound to port %d\n", - udp, ntohs ( udp->local.st_port ) ); - - return 0; + return port; } /** @@ -104,6 +78,7 @@ static int udp_open_common ( struct interface *xfer, struct sockaddr_tcpip *st_peer = ( struct sockaddr_tcpip * ) peer; struct sockaddr_tcpip *st_local = ( struct sockaddr_tcpip * ) local; struct udp_connection *udp; + int port; int rc; /* Allocate and initialise structure */ @@ -120,8 +95,16 @@ static int udp_open_common ( struct interface *xfer, /* Bind to local port */ if ( ! promisc ) { - if ( ( rc = udp_bind ( udp ) ) != 0 ) + port = tcpip_bind ( st_local, udp_port_available ); + if ( port < 0 ) { + rc = port; + DBGC ( udp, "UDP %p could not bind: %s\n", + udp, strerror ( rc ) ); goto err; + } + udp->local.st_port = htons ( port ); + DBGC ( udp, "UDP %p bound to port %d\n", + udp, ntohs ( udp->local.st_port ) ); } /* Attach parent interface, transfer reference to connection @@ -220,9 +203,9 @@ static int udp_tx ( struct udp_connection *udp, struct io_buffer *iobuf, udphdr->chksum = tcpip_chksum ( udphdr, len ); /* Dump debugging information */ - DBGC ( udp, "UDP %p TX %d->%d len %d\n", udp, - ntohs ( udphdr->src ), ntohs ( udphdr->dest ), - ntohs ( udphdr->len ) ); + DBGC2 ( udp, "UDP %p TX %d->%d len %d\n", udp, + ntohs ( udphdr->src ), ntohs ( udphdr->dest ), + ntohs ( udphdr->len ) ); /* Send it to the next layer for processing */ if ( ( rc = tcpip_tx ( iobuf, &udp_protocol, src, dest, netdev, @@ -317,8 +300,8 @@ static int udp_rx ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src, iob_pull ( iobuf, sizeof ( *udphdr ) ); /* Dump debugging information */ - DBGC ( udp, "UDP %p RX %d<-%d len %zd\n", udp, - ntohs ( udphdr->dest ), ntohs ( udphdr->src ), ulen ); + DBGC2 ( udp, "UDP %p RX %d<-%d len %zd\n", udp, + ntohs ( udphdr->dest ), ntohs ( udphdr->src ), ulen ); /* Ignore if no matching connection found */ if ( ! udp ) { diff --git a/src/tests/base16_test.c b/src/tests/base16_test.c new file mode 100644 index 00000000..9b047b74 --- /dev/null +++ b/src/tests/base16_test.c @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2012 Michael Brown . + * + * 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 + * + * Base16 tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include +#include + +/** A Base16 test */ +struct base16_test { + /** Raw data */ + const void *data; + /** Length of raw data */ + size_t len; + /** Base16-encoded data */ + const char *encoded; +}; + +/** Define inline data */ +#define DATA(...) { __VA_ARGS__ } + +/** Define a base16 test */ +#define BASE16( name, DATA, ENCODED ) \ + static const uint8_t name ## _data[] = DATA; \ + static struct base16_test name = { \ + .data = name ## _data, \ + .len = sizeof ( name ## _data ), \ + .encoded = ENCODED, \ + } + +/** Empty data test */ +BASE16 ( empty_test, DATA(), "" ); + +/** "Hello world" test */ +BASE16 ( hw_test, + DATA ( 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd' ), + "48656c6c6f20776f726c64" ); + +/** Random data test */ +BASE16 ( random_test, + DATA ( 0x8b, 0x1a, 0xa2, 0x6c, 0xa9, 0x38, 0x43, 0xb8, 0x81, 0xf8, + 0x30, 0x44, 0xb2, 0x32, 0x6e, 0x82, 0xfe, 0x0f, 0x84, 0x91 ), + "8b1aa26ca93843b881f83044b2326e82fe0f8491" ); + +/** + * Report a base16 encoding test result + * + * @v test Base16 test + */ +#define base16_encode_ok( test ) do { \ + size_t len = base16_encoded_len ( (test)->len ); \ + char buf[ len + 1 /* NUL */ ]; \ + ok ( len == strlen ( (test)->encoded ) ); \ + base16_encode ( (test)->data, (test)->len, buf ); \ + ok ( strcmp ( (test)->encoded, buf ) == 0 ); \ + } while ( 0 ) + +/** + * Report a base16 decoding test result + * + * @v test Base16 test + */ +#define base16_decode_ok( test ) do { \ + size_t max_len = base16_decoded_max_len ( (test)->encoded ); \ + uint8_t buf[max_len]; \ + int len; \ + len = base16_decode ( (test)->encoded, buf ); \ + ok ( len >= 0 ); \ + ok ( ( size_t ) len <= max_len ); \ + ok ( ( size_t ) len == (test)->len ); \ + ok ( memcmp ( (test)->data, buf, len ) == 0 ); \ + } while ( 0 ) + +/** + * Perform Base16 self-tests + * + */ +static void base16_test_exec ( void ) { + + base16_encode_ok ( &empty_test ); + base16_decode_ok ( &empty_test ); + + base16_encode_ok ( &hw_test ); + base16_decode_ok ( &hw_test ); + + base16_encode_ok ( &random_test ); + base16_decode_ok ( &random_test ); +} + +/** Base16 self-test */ +struct self_test base16_test __self_test = { + .name = "base16", + .exec = base16_test_exec, +}; diff --git a/src/tests/settings_test.c b/src/tests/settings_test.c index 028f8163..d1d923a4 100644 --- a/src/tests/settings_test.c +++ b/src/tests/settings_test.c @@ -51,9 +51,15 @@ FILE_LICENCE ( GPL2_OR_LATER ); ok ( storef_setting ( settings, setting, formatted ) == 0 ); \ len = fetch_setting ( settings, setting, actual, \ sizeof ( actual ) ); \ - DBGC ( settings, "Stored %s \"%s\", got:\n", \ - (setting)->type->name, formatted ); \ - DBGC_HDA ( settings, 0, actual, len ); \ + if ( len >= 0 ) { \ + DBGC ( settings, "Stored %s \"%s\", got:\n", \ + (setting)->type->name, formatted ); \ + DBGC_HDA ( settings, 0, actual, len ); \ + } else { \ + DBGC ( settings, "Stored %s \"%s\", got error %s\n", \ + (setting)->type->name, formatted, \ + strerror ( len ) ); \ + } \ ok ( len == ( int ) sizeof ( actual ) ); \ ok ( memcmp ( actual, expected, sizeof ( actual ) ) == 0 ); \ } while ( 0 ) @@ -76,12 +82,63 @@ FILE_LICENCE ( GPL2_OR_LATER ); len = fetchf_setting ( settings, setting, actual, \ sizeof ( actual ) ); \ DBGC ( settings, "Fetched %s \"%s\" from:\n", \ - (setting)->type->name, formatted ); \ + (setting)->type->name, actual ); \ DBGC_HDA ( settings, 0, raw, sizeof ( raw ) ); \ ok ( len == ( int ) ( sizeof ( actual ) - 1 ) ); \ ok ( strcmp ( actual, formatted ) == 0 ); \ } while ( 0 ) +/** + * Report a numeric-store test result + * + * @v settings Settings block + * @v setting Setting + * @v numeric Numeric value + * @v raw_array Expected raw value + */ +#define storen_ok( settings, setting, numeric, raw_array ) do { \ + const uint8_t expected[] = raw_array; \ + uint8_t actual[ sizeof ( expected ) ]; \ + int len; \ + \ + ok ( storen_setting ( settings, setting, numeric ) == 0 ); \ + len = fetch_setting ( settings, setting, actual, \ + sizeof ( actual ) ); \ + if ( len >= 0 ) { \ + DBGC ( settings, "Stored %s %#lx, got:\n", \ + (setting)->type->name, \ + ( unsigned long ) numeric ); \ + DBGC_HDA ( settings, 0, actual, len ); \ + } else { \ + DBGC ( settings, "Stored %s %#lx, got error %s\n", \ + (setting)->type->name, \ + ( unsigned long ) numeric, strerror ( len ) ); \ + } \ + ok ( len == ( int ) sizeof ( actual ) ); \ + ok ( memcmp ( actual, expected, sizeof ( actual ) ) == 0 ); \ + } while ( 0 ) + +/** + * Report a numeric-fetch test result + * + * @v settings Settings block + * @v setting Setting + * @v raw_array Raw array + * @v numeric Expected numeric value + */ +#define fetchn_ok( settings, setting, raw_array, numeric ) do { \ + const uint8_t raw[] = raw_array; \ + unsigned long actual; \ + \ + ok ( store_setting ( settings, setting, raw, \ + sizeof ( raw ) ) == 0 ); \ + ok ( fetchn_setting ( settings, setting, &actual ) == 0 ); \ + DBGC ( settings, "Fetched %s %#lx from:\n", \ + (setting)->type->name, actual ); \ + DBGC_HDA ( settings, 0, raw, sizeof ( raw ) ); \ + ok ( actual == ( unsigned long ) numeric ); \ + } while ( 0 ) + /** Test generic settings block */ struct generic_settings test_generic_settings = { .settings = { @@ -164,12 +221,24 @@ static struct setting test_hexhyp_setting = { .type = &setting_type_hexhyp, }; +/** Test raw hex string setting type */ +static struct setting test_hexraw_setting = { + .name = "test_hexraw", + .type = &setting_type_hexraw, +}; + /** Test UUID setting type */ static struct setting test_uuid_setting = { .name = "test_uuid", .type = &setting_type_uuid, }; +/** Test PCI bus:dev.fn setting type */ +static struct setting test_busdevfn_setting = { + .name = "test_busdevfn", + .type = &setting_type_busdevfn, +}; + /** * Perform settings self-tests * @@ -198,7 +267,7 @@ static void settings_test_exec ( void ) { fetchf_ok ( &test_settings, &test_ipv4_setting, RAW ( 212, 13, 204, 60 ), "212.13.204.60" ); - /* Integer setting types */ + /* Integer setting types (as formatted strings) */ storef_ok ( &test_settings, &test_int8_setting, "54", RAW ( 54 ) ); storef_ok ( &test_settings, &test_int8_setting, @@ -238,11 +307,49 @@ static void settings_test_exec ( void ) { fetchf_ok ( &test_settings, &test_uint32_setting, RAW ( 0xf2, 0x37, 0xb2, 0x18 ), "0xf237b218" ); + /* Integer setting types (as numeric values) */ + storen_ok ( &test_settings, &test_int8_setting, + 72, RAW ( 72 ) ); + storen_ok ( &test_settings, &test_int8_setting, + 0xabcd, RAW ( 0xcd ) ); + fetchn_ok ( &test_settings, &test_int8_setting, + RAW ( 0xfe ), -2 ); + storen_ok ( &test_settings, &test_uint8_setting, + 84, RAW ( 84 ) ); + fetchn_ok ( &test_settings, &test_uint8_setting, + RAW ( 0xfe ), 0xfe ); + storen_ok ( &test_settings, &test_int16_setting, + 0x87bd, RAW ( 0x87, 0xbd ) ); + fetchn_ok ( &test_settings, &test_int16_setting, + RAW ( 0x3d, 0x14 ), 0x3d14 ); + fetchn_ok ( &test_settings, &test_int16_setting, + RAW ( 0x80 ), -128 ); + storen_ok ( &test_settings, &test_uint16_setting, + 1, RAW ( 0x00, 0x01 ) ); + fetchn_ok ( &test_settings, &test_uint16_setting, + RAW ( 0xbd, 0x87 ), 0xbd87 ); + fetchn_ok ( &test_settings, &test_uint16_setting, + RAW ( 0x80 ), 0x0080 ); + storen_ok ( &test_settings, &test_int32_setting, + 0x0812bfd2, RAW ( 0x08, 0x12, 0xbf, 0xd2 ) ); + fetchn_ok ( &test_settings, &test_int32_setting, + RAW ( 0x43, 0x87, 0x91, 0xb4 ), 0x438791b4 ); + fetchn_ok ( &test_settings, &test_int32_setting, + RAW ( 0xff, 0xff, 0xfe ), -2 ); + storen_ok ( &test_settings, &test_uint32_setting, + 0xb5927ab8, RAW ( 0xb5, 0x92, 0x7a, 0xb8 ) ); + fetchn_ok ( &test_settings, &test_uint32_setting, + RAW ( 0x98, 0xab, 0x41, 0x81 ), 0x98ab4181 ); + fetchn_ok ( &test_settings, &test_uint32_setting, + RAW ( 0xff, 0xff, 0xfe ), 0x00fffffe ); + fetchn_ok ( &test_settings, &test_uint32_setting, + RAW ( 0, 0, 0, 0x12, 0x34, 0x56, 0x78 ), 0x12345678 ); + fetchn_ok ( &test_settings, &test_int32_setting, + RAW ( 0, 0, 0, 0x12, 0x34, 0x56, 0x78 ), 0x12345678 ); + fetchn_ok ( &test_settings, &test_int32_setting, + RAW ( 0xff, 0xff, 0x87, 0x65, 0x43, 0x21 ), -0x789abcdf ); + /* "hex" setting type */ - storef_ok ( &test_settings, &test_hex_setting, - ":", RAW ( 0x00, 0x00 ) ); - storef_ok ( &test_settings, &test_hex_setting, - "1:2:", RAW ( 0x01, 0x02, 0x00 ) ); storef_ok ( &test_settings, &test_hex_setting, "08:12:f5:22:90:1b:4b:47:a8:30:cb:4d:67:4c:d6:76", RAW ( 0x08, 0x12, 0xf5, 0x22, 0x90, 0x1b, 0x4b, 0x47, 0xa8, @@ -260,12 +367,24 @@ static void settings_test_exec ( void ) { 0x09, 0x6c, 0x66, 0x13, 0xc1, 0xa8, 0xec, 0x27 ), "9f-e5-6d-fb-24-3a-4c-bb-a9-09-6c-66-13-c1-a8-ec-27" ); + /* "hexraw" setting type */ + storef_ok ( &test_settings, &test_hexraw_setting, + "012345abcdef", RAW ( 0x01, 0x23, 0x45, 0xab, 0xcd, 0xef )); + fetchf_ok ( &test_settings, &test_hexraw_setting, + RAW ( 0x9e, 0x4b, 0x6e, 0xef, 0x36, 0xb6, 0x46, 0xfe, 0x8f, + 0x17, 0x06, 0x39, 0x6b, 0xf4, 0x48, 0x4e ), + "9e4b6eef36b646fe8f1706396bf4484e" ); + /* "uuid" setting type (no store capability) */ fetchf_ok ( &test_settings, &test_uuid_setting, RAW ( 0x1a, 0x6a, 0x74, 0x9d, 0x0e, 0xda, 0x46, 0x1a,0xa8, 0x7a, 0x7c, 0xfe, 0x4f, 0xca, 0x4a, 0x57 ), "1a6a749d-0eda-461a-a87a-7cfe4fca4a57" ); + /* "busdevfn" setting type (no store capability) */ + fetchf_ok ( &test_settings, &test_busdevfn_setting, + RAW ( 0x03, 0x45 ), "03:08.5" ); + /* Clear and unregister test settings block */ clear_settings ( &test_settings ); unregister_settings ( &test_settings ); diff --git a/src/tests/tests.c b/src/tests/tests.c index af969ec8..f965e6e3 100644 --- a/src/tests/tests.c +++ b/src/tests/tests.c @@ -28,9 +28,11 @@ FILE_LICENCE ( GPL2_OR_LATER ); /* Drag in all applicable self-tests */ REQUIRE_OBJECT ( memcpy_test ); REQUIRE_OBJECT ( string_test ); +REQUIRE_OBJECT ( vsprintf_test ); REQUIRE_OBJECT ( list_test ); REQUIRE_OBJECT ( byteswap_test ); REQUIRE_OBJECT ( base64_test ); +REQUIRE_OBJECT ( base16_test ); REQUIRE_OBJECT ( settings_test ); REQUIRE_OBJECT ( time_test ); REQUIRE_OBJECT ( tcpip_test ); diff --git a/src/tests/vsprintf_test.c b/src/tests/vsprintf_test.c new file mode 100644 index 00000000..11512ec8 --- /dev/null +++ b/src/tests/vsprintf_test.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2012 Michael Brown . + * + * 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 + * + * vsprintf() self-tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include + +/** + * Report an snprintf() test result + * + */ +#define snprintf_ok( len, result, format, ... ) do { \ + char actual[ (len) ]; \ + const char expected[] = result; \ + size_t actual_len; \ + \ + actual_len = snprintf ( actual, sizeof ( actual ), \ + format, ##__VA_ARGS__ ); \ + ok ( actual_len >= strlen ( result ) ); \ + ok ( strcmp ( actual, expected ) == 0 ); \ + if ( strcmp ( actual, expected ) != 0 ) { \ + DBG ( "SNPRINTF expected \"%s\", got \"%s\"\n", \ + expected, actual ); \ + } \ + } while ( 0 ) + +/** + * Perform vsprintf() self-tests + * + */ +static void vsprintf_test_exec ( void ) { + + /* Constant string */ + snprintf_ok ( 16, "Testing", "Testing" ); + + /* Constant string, truncated to fit */ + snprintf_ok ( 5, "Test", "Testing" ); + + /* Basic format specifiers */ + snprintf_ok ( 16, "%", "%%" ); + snprintf_ok ( 16, "ABC", "%c%c%c", 'A', 'B', 'C' ); + snprintf_ok ( 16, "abc", "%lc%lc%lc", L'a', L'b', L'c' ); + snprintf_ok ( 16, "Hello world", "%s %s", "Hello", "world" ); + snprintf_ok ( 16, "Goodbye world", "%ls %s", L"Goodbye", "world" ); + snprintf_ok ( 16, "0x1234abcd", "%p", ( ( void * ) 0x1234abcd ) ); + snprintf_ok ( 16, "0xa723", "%#x", 0xa723 ); + snprintf_ok ( 16, "a723", "%x", 0xa723 ); + snprintf_ok ( 16, "0x0000a723", "%#08x", 0xa723 ); + snprintf_ok ( 16, "00A723", "%06X", 0xa723 ); + snprintf_ok ( 16, "9876abcd", "%lx", 0x9876abcdUL ); + snprintf_ok ( 16, "1234 5678", "%04llx %04llx", 0x1234ULL, 0x5678ULL ); + snprintf_ok ( 16, "123", "%d", 123 ); + snprintf_ok ( 16, "456", "%i", 456 ); + snprintf_ok ( 16, " 99", "%3d", 99 ); + snprintf_ok ( 16, "099", "%03d", 99 ); + snprintf_ok ( 16, "-72", "%d", -72 ); + snprintf_ok ( 16, " -72", "%4d", -72 ); + snprintf_ok ( 16, "-072", "%04d", -72 ); + snprintf_ok ( 16, "4", "%zd", sizeof ( uint32_t ) ); + snprintf_ok ( 16, "123456789", "%d", 123456789 ); + + /* Realistic combinations */ + snprintf_ok ( 64, "DBG 0x1234 thingy at 0x0003f0c0+0x5c\n", + "DBG %p %s at %#08lx+%#zx\n", ( ( void * ) 0x1234 ), + "thingy", 0x3f0c0UL, ( ( size_t ) 0x5c ) ); + snprintf_ok ( 64, "PCI 00:1f.3", "PCI %02x:%02x.%x", 0x00, 0x1f, 0x03 ); + snprintf_ok ( 64, "Region [1000000,3f000000)", "Region [%llx,%llx)", + 0x1000000ULL, 0x3f000000ULL ); +} + +/** vsprintf() self-test */ +struct self_test vsprintf_test __self_test = { + .name = "vsprintf", + .exec = vsprintf_test_exec, +}; diff --git a/src/usr/lotest.c b/src/usr/lotest.c index c4b0b441..905290a5 100644 --- a/src/usr/lotest.c +++ b/src/usr/lotest.c @@ -175,7 +175,8 @@ static int loopback_wait ( struct net_device *receiver, void *data, */ int loopback_test ( struct net_device *sender, struct net_device *receiver, size_t mtu ) { - uint8_t buf[mtu]; + uint8_t *buf; + uint32_t *seq; struct io_buffer *iobuf; unsigned int i; unsigned int successes; @@ -193,6 +194,14 @@ int loopback_test ( struct net_device *sender, struct net_device *receiver, if ( ( rc = iflinkwait ( receiver, LINK_WAIT_MS ) ) != 0 ) return rc; + /* Allocate data buffer */ + if ( mtu < sizeof ( *seq ) ) + mtu = sizeof ( *seq ); + buf = malloc ( mtu ); + if ( ! buf ) + return -ENOMEM; + seq = ( ( void * ) buf ); + /* Print initial statistics */ printf ( "Performing loopback test from %s to %s with %zd byte MTU\n", sender->name, receiver->name, mtu ); @@ -211,17 +220,17 @@ int loopback_test ( struct net_device *sender, struct net_device *receiver, printf ( "\r%d", successes ); /* Generate random packet */ - for ( i = 0 ; i < sizeof ( buf ) ; i++ ) + *seq = htonl ( successes ); + for ( i = sizeof ( *seq ) ; i < mtu ; i++ ) buf[i] = random(); - iobuf = alloc_iob ( MAX_LL_HEADER_LEN + sizeof ( buf ) ); + iobuf = alloc_iob ( MAX_LL_HEADER_LEN + mtu ); if ( ! iobuf ) { printf ( "\nFailed to allocate I/O buffer" ); rc = -ENOMEM; break; } iob_reserve ( iobuf, MAX_LL_HEADER_LEN ); - memcpy ( iob_put ( iobuf, sizeof ( buf ) ), - buf, sizeof ( buf ) ); + memcpy ( iob_put ( iobuf, mtu ), buf, mtu ); /* Transmit packet */ if ( ( rc = net_tx ( iob_disown ( iobuf ), sender, @@ -233,10 +242,8 @@ int loopback_test ( struct net_device *sender, struct net_device *receiver, } /* Wait for received packet */ - if ( ( rc = loopback_wait ( receiver, buf, - sizeof ( buf ) ) ) != 0 ) { + if ( ( rc = loopback_wait ( receiver, buf, mtu ) ) != 0 ) break; - } } printf ( "\n"); @@ -246,5 +253,8 @@ int loopback_test ( struct net_device *sender, struct net_device *receiver, ifstat ( sender ); ifstat ( receiver ); + /* Free buffer */ + free ( buf ); + return 0; } diff --git a/src/usr/nslookup.c b/src/usr/nslookup.c index c931ec5a..cb6d8d2b 100644 --- a/src/usr/nslookup.c +++ b/src/usr/nslookup.c @@ -46,7 +46,7 @@ struct nslookup { struct interface resolver; /** Setting name */ - const char *setting_name; + char *setting_name; }; /** @@ -71,7 +71,9 @@ static void nslookup_close ( struct nslookup *nslookup, int rc ) { static void nslookup_resolv_done ( struct nslookup *nslookup, struct sockaddr *sa ) { struct sockaddr_in *sin; - struct setting_type *type; + struct setting_type *default_type; + struct settings *settings; + struct setting setting; void *data; size_t len; int rc; @@ -82,16 +84,25 @@ static void nslookup_resolv_done ( struct nslookup *nslookup, sin = ( ( struct sockaddr_in * ) sa ); data = &sin->sin_addr; len = sizeof ( sin->sin_addr ); - type = &setting_type_ipv4; + default_type = &setting_type_ipv4; break; default: rc = -ENOTSUP; goto err; } - /* Save in specified setting */ - if ( ( rc = store_named_setting ( nslookup->setting_name, type, - data, len ) ) != 0 ) + /* Parse specified setting name */ + if ( ( rc = parse_setting_name ( nslookup->setting_name, + autovivify_child_settings, &settings, + &setting ) ) != 0 ) + goto err; + + /* Apply default type if necessary */ + if ( ! setting.type ) + setting.type = default_type; + + /* Store in specified setting */ + if ( ( rc = store_setting ( settings, &setting, data, len ) ) != 0 ) goto err; err: