diff options
Diffstat (limited to 'src')
63 files changed, 2803 insertions, 345 deletions
diff --git a/src/Makefile.housekeeping b/src/Makefile.housekeeping index e017c656b..0deb15c10 100644 --- a/src/Makefile.housekeeping +++ b/src/Makefile.housekeeping @@ -1167,7 +1167,8 @@ $(BLIB) : $(BLIB_OBJS) $(BLIB_LIST) $(MAKEDEPS) $(Q)$(RM) $(BLIB) $(QM)$(ECHO) " [AR] $@" $(Q)$(AR) rD $@ $(sort $(BLIB_OBJS)) - $(Q)$(OBJCOPY) --prefix-symbols=$(SYMBOL_PREFIX) $@ + $(Q)$(OBJCOPY) --enable-deterministic-archives \ + --prefix-symbols=$(SYMBOL_PREFIX) $@ $(Q)$(RANLIB) -D $@ blib : $(BLIB) diff --git a/src/arch/x86/core/x86_bigint.c b/src/arch/x86/core/x86_bigint.c index 6413b2fa8..9a25bdad5 100644 --- a/src/arch/x86/core/x86_bigint.c +++ b/src/arch/x86/core/x86_bigint.c @@ -75,17 +75,18 @@ void bigint_multiply_raw ( const uint32_t *multiplicand0, * * a < 2^{n}, b < 2^{n} => ab < 2^{2n} */ - __asm__ __volatile__ ( "mull %4\n\t" - "addl %%eax, (%5,%2,4)\n\t" - "adcl %%edx, 4(%5,%2,4)\n\t" + __asm__ __volatile__ ( "mull %5\n\t" + "addl %%eax, (%6,%2,4)\n\t" + "adcl %%edx, 4(%6,%2,4)\n\t" "\n1:\n\t" - "adcl $0, 8(%5,%2,4)\n\t" + "adcl $0, 8(%6,%2,4)\n\t" "inc %2\n\t" /* Does not affect CF */ "jc 1b\n\t" : "=&a" ( discard_a ), "=&d" ( discard_d ), - "=&r" ( index ) + "=&r" ( index ), + "+m" ( *result ) : "0" ( multiplicand_element ), "g" ( multiplier_element ), "r" ( result_elements ), diff --git a/src/arch/x86/drivers/net/undinet.c b/src/arch/x86/drivers/net/undinet.c index 9b7d6d849..43cb18bfe 100644 --- a/src/arch/x86/drivers/net/undinet.c +++ b/src/arch/x86/drivers/net/undinet.c @@ -104,6 +104,13 @@ static union u_PXENV_ANY __bss16 ( undinet_params ); SEGOFF16_t __bss16 ( undinet_entry_point ); #define undinet_entry_point __use_data16 ( undinet_entry_point ) +/* Read TSC in real mode only when profiling */ +#if PROFILING +#define RDTSC_IF_PROFILING "rdtsc\n\t" +#else +#define RDTSC_IF_PROFILING "" +#endif + /** IRQ profiler */ static struct profiler undinet_irq_profiler __profiler = { .name = "undinet.irq" }; @@ -288,14 +295,14 @@ static int undinet_call ( struct undi_nic *undinic, unsigned int function, */ profile_start ( &profiler->total ); __asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */ - "rdtsc\n\t" + RDTSC_IF_PROFILING "pushl %%eax\n\t" "pushw %%es\n\t" "pushw %%di\n\t" "pushw %%bx\n\t" "lcall *undinet_entry_point\n\t" "movw %%ax, %%bx\n\t" - "rdtsc\n\t" + RDTSC_IF_PROFILING "addw $6, %%sp\n\t" "popl %%edx\n\t" "popl %%ebp\n\t" /* gcc bug */ ) diff --git a/src/arch/x86/image/bzimage.c b/src/arch/x86/image/bzimage.c index 51498bf95..a782127a1 100644 --- a/src/arch/x86/image/bzimage.c +++ b/src/arch/x86/image/bzimage.c @@ -327,32 +327,6 @@ static void bzimage_set_cmdline ( struct image *image, } /** - * Parse standalone image command line for cpio parameters - * - * @v image bzImage file - * @v cpio CPIO header - * @v cmdline Command line - */ -static void bzimage_parse_cpio_cmdline ( struct image *image, - struct cpio_header *cpio, - const char *cmdline ) { - char *arg; - char *end; - unsigned int mode; - - /* Look for "mode=" */ - if ( ( arg = strstr ( cmdline, "mode=" ) ) ) { - arg += 5; - mode = strtoul ( arg, &end, 8 /* Octal for file mode */ ); - if ( *end && ( *end != ' ' ) ) { - DBGC ( image, "bzImage %p strange \"mode=\"" - "terminator '%c'\n", image, *end ); - } - cpio_set_field ( cpio->c_mode, ( 0100000 | mode ) ); - } -} - -/** * Align initrd length * * @v len Length @@ -374,11 +348,9 @@ static inline size_t bzimage_align ( size_t len ) { static size_t bzimage_load_initrd ( struct image *image, struct image *initrd, userptr_t address ) { - char *filename = initrd->cmdline; - char *cmdline; + const char *filename = cpio_name ( initrd ); struct cpio_header cpio; size_t offset; - size_t name_len; size_t pad_len; /* Do not include kernel image itself as an initrd */ @@ -386,25 +358,7 @@ static size_t bzimage_load_initrd ( struct image *image, return 0; /* Create cpio header for non-prebuilt images */ - if ( filename && filename[0] ) { - cmdline = strchr ( filename, ' ' ); - name_len = ( ( cmdline ? ( ( size_t ) ( cmdline - filename ) ) - : strlen ( filename ) ) + 1 /* NUL */ ); - memset ( &cpio, '0', sizeof ( cpio ) ); - memcpy ( cpio.c_magic, CPIO_MAGIC, sizeof ( cpio.c_magic ) ); - cpio_set_field ( cpio.c_mode, 0100644 ); - cpio_set_field ( cpio.c_nlink, 1 ); - cpio_set_field ( cpio.c_filesize, initrd->len ); - cpio_set_field ( cpio.c_namesize, name_len ); - if ( cmdline ) { - bzimage_parse_cpio_cmdline ( image, &cpio, - ( cmdline + 1 /* ' ' */ )); - } - offset = ( ( sizeof ( cpio ) + name_len + 0x03 ) & ~0x03 ); - } else { - offset = 0; - name_len = 0; - } + offset = cpio_header ( initrd, &cpio ); /* Copy in initrd image body (and cpio header if applicable) */ if ( address ) { @@ -413,7 +367,7 @@ static size_t bzimage_load_initrd ( struct image *image, memset_user ( address, 0, 0, offset ); copy_to_user ( address, 0, &cpio, sizeof ( cpio ) ); copy_to_user ( address, sizeof ( cpio ), filename, - ( name_len - 1 /* NUL (or space) */ ) ); + cpio_name_len ( initrd ) ); } DBGC ( image, "bzImage %p initrd %p [%#08lx,%#08lx,%#08lx)" "%s%s\n", image, initrd, user_to_phys ( address, 0 ), diff --git a/src/arch/x86/image/initrd.c b/src/arch/x86/image/initrd.c index 49b959a91..d7b1f5773 100644 --- a/src/arch/x86/image/initrd.c +++ b/src/arch/x86/image/initrd.c @@ -29,6 +29,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/uaccess.h> #include <ipxe/init.h> #include <ipxe/memblock.h> +#include <ipxe/cpio.h> /** @file * diff --git a/src/arch/x86/include/bits/bigint.h b/src/arch/x86/include/bits/bigint.h index 4f1bc87ff..7443d6fdc 100644 --- a/src/arch/x86/include/bits/bigint.h +++ b/src/arch/x86/include/bits/bigint.h @@ -25,19 +25,22 @@ typedef uint32_t bigint_element_t; static inline __attribute__ (( always_inline )) void bigint_init_raw ( uint32_t *value0, unsigned int size, const void *data, size_t len ) { - long pad_len = ( sizeof ( bigint_t ( size ) ) - len ); + bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( void * ) value0 ); + long pad_len = ( sizeof ( *value ) - len ); void *discard_D; long discard_c; /* Copy raw data in reverse order, padding with zeros */ __asm__ __volatile__ ( "\n1:\n\t" - "movb -1(%2,%1), %%al\n\t" + "movb -1(%3,%1), %%al\n\t" "stosb\n\t" "loop 1b\n\t" "xorl %%eax, %%eax\n\t" - "mov %3, %1\n\t" + "mov %4, %1\n\t" "rep stosb\n\t" - : "=&D" ( discard_D ), "=&c" ( discard_c ) + : "=&D" ( discard_D ), "=&c" ( discard_c ), + "+m" ( *value ) : "r" ( data ), "g" ( pad_len ), "0" ( value0 ), "1" ( len ) : "eax" ); @@ -53,6 +56,8 @@ bigint_init_raw ( uint32_t *value0, unsigned int size, static inline __attribute__ (( always_inline )) void bigint_add_raw ( const uint32_t *addend0, uint32_t *value0, unsigned int size ) { + bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( void * ) value0 ); long index; void *discard_S; long discard_c; @@ -60,11 +65,11 @@ bigint_add_raw ( const uint32_t *addend0, uint32_t *value0, __asm__ __volatile__ ( "xor %0, %0\n\t" /* Zero %0 and clear CF */ "\n1:\n\t" "lodsl\n\t" - "adcl %%eax, (%3,%0,4)\n\t" + "adcl %%eax, (%4,%0,4)\n\t" "inc %0\n\t" /* Does not affect CF */ "loop 1b\n\t" : "=&r" ( index ), "=&S" ( discard_S ), - "=&c" ( discard_c ) + "=&c" ( discard_c ), "+m" ( *value ) : "r" ( value0 ), "1" ( addend0 ), "2" ( size ) : "eax" ); } @@ -79,6 +84,8 @@ bigint_add_raw ( const uint32_t *addend0, uint32_t *value0, static inline __attribute__ (( always_inline )) void bigint_subtract_raw ( const uint32_t *subtrahend0, uint32_t *value0, unsigned int size ) { + bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( void * ) value0 ); long index; void *discard_S; long discard_c; @@ -86,11 +93,11 @@ bigint_subtract_raw ( const uint32_t *subtrahend0, uint32_t *value0, __asm__ __volatile__ ( "xor %0, %0\n\t" /* Zero %0 and clear CF */ "\n1:\n\t" "lodsl\n\t" - "sbbl %%eax, (%3,%0,4)\n\t" + "sbbl %%eax, (%4,%0,4)\n\t" "inc %0\n\t" /* Does not affect CF */ "loop 1b\n\t" : "=&r" ( index ), "=&S" ( discard_S ), - "=&c" ( discard_c ) + "=&c" ( discard_c ), "+m" ( *value ) : "r" ( value0 ), "1" ( subtrahend0 ), "2" ( size ) : "eax" ); @@ -104,15 +111,18 @@ bigint_subtract_raw ( const uint32_t *subtrahend0, uint32_t *value0, */ static inline __attribute__ (( always_inline )) void bigint_rol_raw ( uint32_t *value0, unsigned int size ) { + bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( void * ) value0 ); long index; long discard_c; __asm__ __volatile__ ( "xor %0, %0\n\t" /* Zero %0 and clear CF */ "\n1:\n\t" - "rcll $1, (%2,%0,4)\n\t" + "rcll $1, (%3,%0,4)\n\t" "inc %0\n\t" /* Does not affect CF */ "loop 1b\n\t" - : "=&r" ( index ), "=&c" ( discard_c ) + : "=&r" ( index ), "=&c" ( discard_c ), + "+m" ( *value ) : "r" ( value0 ), "1" ( size ) ); } @@ -124,13 +134,15 @@ bigint_rol_raw ( uint32_t *value0, unsigned int size ) { */ static inline __attribute__ (( always_inline )) void bigint_ror_raw ( uint32_t *value0, unsigned int size ) { + bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( void * ) value0 ); long discard_c; __asm__ __volatile__ ( "clc\n\t" "\n1:\n\t" - "rcrl $1, -4(%1,%0,4)\n\t" + "rcrl $1, -4(%2,%0,4)\n\t" "loop 1b\n\t" - : "=&c" ( discard_c ) + : "=&c" ( discard_c ), "+m" ( *value ) : "r" ( value0 ), "0" ( size ) ); } @@ -239,6 +251,8 @@ bigint_max_set_bit_raw ( const uint32_t *value0, unsigned int size ) { static inline __attribute__ (( always_inline )) void bigint_grow_raw ( const uint32_t *source0, unsigned int source_size, uint32_t *dest0, unsigned int dest_size ) { + bigint_t ( dest_size ) __attribute__ (( may_alias )) *dest = + ( ( void * ) dest0 ); long pad_size = ( dest_size - source_size ); void *discard_D; void *discard_S; @@ -246,10 +260,10 @@ bigint_grow_raw ( const uint32_t *source0, unsigned int source_size, __asm__ __volatile__ ( "rep movsl\n\t" "xorl %%eax, %%eax\n\t" - "mov %3, %2\n\t" + "mov %4, %2\n\t" "rep stosl\n\t" : "=&D" ( discard_D ), "=&S" ( discard_S ), - "=&c" ( discard_c ) + "=&c" ( discard_c ), "+m" ( *dest ) : "g" ( pad_size ), "0" ( dest0 ), "1" ( source0 ), "2" ( source_size ) : "eax" ); @@ -266,13 +280,15 @@ bigint_grow_raw ( const uint32_t *source0, unsigned int source_size, static inline __attribute__ (( always_inline )) void bigint_shrink_raw ( const uint32_t *source0, unsigned int source_size __unused, uint32_t *dest0, unsigned int dest_size ) { + bigint_t ( dest_size ) __attribute__ (( may_alias )) *dest = + ( ( void * ) dest0 ); void *discard_D; void *discard_S; long discard_c; __asm__ __volatile__ ( "rep movsl\n\t" : "=&D" ( discard_D ), "=&S" ( discard_S ), - "=&c" ( discard_c ) + "=&c" ( discard_c ), "+m" ( *dest ) : "0" ( dest0 ), "1" ( source0 ), "2" ( dest_size ) : "eax" ); @@ -289,15 +305,19 @@ bigint_shrink_raw ( const uint32_t *source0, unsigned int source_size __unused, static inline __attribute__ (( always_inline )) void bigint_done_raw ( const uint32_t *value0, unsigned int size __unused, void *out, size_t len ) { + struct { + uint8_t bytes[len]; + } __attribute__ (( may_alias )) *out_bytes = out; void *discard_D; long discard_c; /* Copy raw data in reverse order */ __asm__ __volatile__ ( "\n1:\n\t" - "movb -1(%2,%1), %%al\n\t" + "movb -1(%3,%1), %%al\n\t" "stosb\n\t" "loop 1b\n\t" - : "=&D" ( discard_D ), "=&c" ( discard_c ) + : "=&D" ( discard_D ), "=&c" ( discard_c ), + "+m" ( *out_bytes ) : "r" ( value0 ), "0" ( out ), "1" ( len ) : "eax" ); } diff --git a/src/arch/x86/include/initrd.h b/src/arch/x86/include/initrd.h index ddb3e5a45..2fb9d3d3a 100644 --- a/src/arch/x86/include/initrd.h +++ b/src/arch/x86/include/initrd.h @@ -11,13 +11,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/uaccess.h> -/** Minimum alignment for initrds - * - * Some versions of Linux complain about initrds that are not - * page-aligned. - */ -#define INITRD_ALIGN 4096 - /** Minimum free space required to reshuffle initrds * * Chosen to avoid absurdly long reshuffling times diff --git a/src/arch/x86/include/ipxe/cpuid.h b/src/arch/x86/include/ipxe/cpuid.h index b5403bd9d..3983dfb89 100644 --- a/src/arch/x86/include/ipxe/cpuid.h +++ b/src/arch/x86/include/ipxe/cpuid.h @@ -42,6 +42,9 @@ struct x86_features { /** Hypervisor is present */ #define CPUID_FEATURES_INTEL_ECX_HYPERVISOR 0x80000000UL +/** TSC is present */ +#define CPUID_FEATURES_INTEL_EDX_TSC 0x00000010UL + /** FXSAVE and FXRSTOR are supported */ #define CPUID_FEATURES_INTEL_EDX_FXSR 0x01000000UL diff --git a/src/arch/x86/interface/pcbios/rtc_entropy.c b/src/arch/x86/interface/pcbios/rtc_entropy.c index e9e6baa59..e0c175685 100644 --- a/src/arch/x86/interface/pcbios/rtc_entropy.c +++ b/src/arch/x86/interface/pcbios/rtc_entropy.c @@ -36,6 +36,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <biosint.h> #include <pic8259.h> #include <rtc.h> +#include <ipxe/cpuid.h> #include <ipxe/entropy.h> /** Maximum time to wait for an RTC interrupt, in milliseconds */ @@ -174,8 +175,17 @@ static int rtc_entropy_check ( void ) { * @ret rc Return status code */ static int rtc_entropy_enable ( void ) { + struct x86_features features; int rc; + /* Check that TSC is supported */ + x86_features ( &features ); + if ( ! ( features.intel.edx & CPUID_FEATURES_INTEL_EDX_TSC ) ) { + DBGC ( &rtc_flag, "RTC has no TSC\n" ); + rc = -ENOTSUP; + goto err_no_tsc; + } + /* Hook ISR and enable RTC interrupts */ rtc_hook_isr(); enable_irq ( RTC_IRQ ); @@ -191,6 +201,7 @@ static int rtc_entropy_enable ( void ) { rtc_disable_int(); disable_irq ( RTC_IRQ ); rtc_unhook_isr(); + err_no_tsc: return rc; } diff --git a/src/arch/x86/prefix/unlzma.S b/src/arch/x86/prefix/unlzma.S index 956eeb24c..979f699ee 100644 --- a/src/arch/x86/prefix/unlzma.S +++ b/src/arch/x86/prefix/unlzma.S @@ -44,7 +44,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ .text - .arch i586 + .arch i486 .section ".prefix.lib", "ax", @progbits #ifdef CODE16 diff --git a/src/config/cloud/aws.ipxe b/src/config/cloud/aws.ipxe index 2c96e3888..6c007398e 100644 --- a/src/config/cloud/aws.ipxe +++ b/src/config/cloud/aws.ipxe @@ -3,6 +3,22 @@ echo Amazon EC2 - iPXE boot via user-data echo CPU: ${cpuvendor} ${cpumodel} ifstat || -dhcp || + +set attempt:int8 1 +:dhcp_retry +echo DHCP attempt ${attempt} +dhcp --timeout 5000 && goto dhcp_ok || +ifstat || +inc attempt +iseq ${attempt} 10 || goto dhcp_retry + +:dhcp_fail +echo DHCP failed - rebooting +reboot || +exit + +:dhcp_ok route || -chain -ar http://169.254.169.254/latest/user-data +chain -ar http://169.254.169.254/latest/user-data || +ifstat || +exit diff --git a/src/config/cloud/gce.ipxe b/src/config/cloud/gce.ipxe index 88e12b56b..65e2e57dc 100644 --- a/src/config/cloud/gce.ipxe +++ b/src/config/cloud/gce.ipxe @@ -5,4 +5,5 @@ echo CPU: ${cpuvendor} ${cpumodel} ifstat || dhcp || route || -chain -ar http://metadata.google.internal/computeMetadata/v1/instance/attributes/ipxeboot +chain -ar http://metadata.google.internal/computeMetadata/v1/instance/attributes/ipxeboot || +ifstat || diff --git a/src/config/cloud/settings.h b/src/config/cloud/settings.h index 34deeb070..c9d6bdc93 100644 --- a/src/config/cloud/settings.h +++ b/src/config/cloud/settings.h @@ -1,4 +1,6 @@ /* It can often be useful to know the CPU on which a cloud instance is * running (e.g. to isolate problems with Azure AMD instances). */ +#if defined ( __i386__ ) || defined ( __x86_64__ ) #define CPUID_SETTINGS +#endif diff --git a/src/config/config.c b/src/config/config.c index 5e7a3ecfd..a81866132 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -182,6 +182,12 @@ REQUIRE_OBJECT ( efi_image ); #ifdef IMAGE_SDI REQUIRE_OBJECT ( sdi ); #endif +#ifdef IMAGE_ZLIB +REQUIRE_OBJECT ( zlib ); +#endif +#ifdef IMAGE_GZIP +REQUIRE_OBJECT ( gzip ); +#endif /* * Drag in all requested commands diff --git a/src/config/config_archive.c b/src/config/config_archive.c new file mode 100644 index 000000000..746fc7e44 --- /dev/null +++ b/src/config/config_archive.c @@ -0,0 +1,36 @@ +/* + * 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. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <config/general.h> + +/** @file + * + * Archive image configuration + * + */ + +PROVIDE_REQUIRING_SYMBOL(); + +#ifdef IMAGE_ARCHIVE_CMD +REQUIRE_OBJECT ( image_archive_cmd ); +#endif diff --git a/src/config/general.h b/src/config/general.h index 9b21f1271..2d15f500a 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -117,6 +117,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define IMAGE_PNG /* PNG image support */ #define IMAGE_DER /* DER image support */ #define IMAGE_PEM /* PEM image support */ +//#define IMAGE_ZLIB /* ZLIB image support */ +//#define IMAGE_GZIP /* GZIP image support */ /* * Command-line commands to include @@ -156,6 +158,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); //#define NTP_CMD /* NTP commands */ //#define CERT_CMD /* Certificate management commands */ //#define IMAGE_MEM_CMD /* Read memory command */ +#define IMAGE_ARCHIVE_CMD /* Archive image management commands */ /* * ROM-specific options diff --git a/src/core/archive.c b/src/core/archive.c new file mode 100644 index 000000000..bb62c7e47 --- /dev/null +++ b/src/core/archive.c @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2021 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <string.h> +#include <errno.h> +#include <ipxe/image.h> + +/** @file + * + * Archive images + * + */ + +/** + * Extract archive image + * + * @v image Image + * @v name Extracted image name + * @v extracted Extracted image to fill in + * @ret rc Return status code + */ +int image_extract ( struct image *image, const char *name, + struct image **extracted ) { + char *dot; + int rc; + + /* Check that this image can be used to extract an archive image */ + if ( ! ( image->type && image->type->extract ) ) { + rc = -ENOTSUP; + goto err_unsupported; + } + + /* Allocate new image */ + *extracted = alloc_image ( image->uri ); + if ( ! *extracted ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Set image name */ + if ( ( rc = image_set_name ( *extracted, + ( name ? name : image->name ) ) ) != 0 ) { + goto err_set_name; + } + + /* Strip any archive or compression suffix from implicit name */ + if ( ( ! name ) && ( (*extracted)->name ) && + ( ( dot = strrchr ( (*extracted)->name, '.' ) ) != NULL ) ) { + *dot = '\0'; + } + + /* Try extracting archive image */ + if ( ( rc = image->type->extract ( image, *extracted ) ) != 0 ) { + DBGC ( image, "IMAGE %s could not extract image: %s\n", + image->name, strerror ( rc ) ); + goto err_extract; + } + + /* Register image */ + if ( ( rc = register_image ( *extracted ) ) != 0 ) + goto err_register; + + /* Propagate trust flag */ + if ( image->flags & IMAGE_TRUSTED ) + image_trust ( *extracted ); + + /* Drop local reference to image */ + image_put ( *extracted ); + + return 0; + + unregister_image ( *extracted ); + err_register: + err_extract: + err_set_name: + image_put ( *extracted ); + err_alloc: + err_unsupported: + return rc; +} + +/** + * Extract and execute image + * + * @v image Image + * @ret rc Return status code + */ +int image_extract_exec ( struct image *image ) { + struct image *extracted; + int rc; + + /* Extract image */ + if ( ( rc = image_extract ( image, NULL, &extracted ) ) != 0 ) + goto err_extract; + + /* Set image command line */ + if ( ( rc = image_set_cmdline ( extracted, image->cmdline ) ) != 0 ) + goto err_set_cmdline; + + /* Set auto-unregister flag */ + extracted->flags |= IMAGE_AUTO_UNREGISTER; + + /* Tail-recurse into extracted image */ + return image_exec ( extracted ); + + err_set_cmdline: + unregister_image ( extracted ); + err_extract: + return rc; +} + +/* Drag in objects via image_extract() */ +REQUIRING_SYMBOL ( image_extract ); + +/* Drag in archive image formats */ +REQUIRE_OBJECT ( config_archive ); diff --git a/src/core/base64.c b/src/core/base64.c index e452f7d41..ec11be261 100644 --- a/src/core/base64.c +++ b/src/core/base64.c @@ -36,7 +36,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ -static const char base64[64] = +static const char base64[ 64 + 1 /* NUL */ ] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /** diff --git a/src/core/console.c b/src/core/console.c index 7fd00036f..2b90809bf 100644 --- a/src/core/console.c +++ b/src/core/console.c @@ -20,11 +20,12 @@ unsigned int console_height = CONSOLE_DEFAULT_HEIGHT; * Write a single character to each console device * * @v character Character to be written + * @ret character Character written * * The character is written out to all enabled console devices, using * each device's console_driver::putchar() method. */ -void putchar ( int character ) { +int putchar ( int character ) { struct console_driver *console; /* Automatic LF -> CR,LF translation */ @@ -37,6 +38,8 @@ void putchar ( int character ) { console->putchar ) console->putchar ( character ); } + + return character; } /** diff --git a/src/core/cpio.c b/src/core/cpio.c index 080c72daf..27aee7581 100644 --- a/src/core/cpio.c +++ b/src/core/cpio.c @@ -30,6 +30,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ #include <stdio.h> +#include <stdlib.h> #include <string.h> #include <ipxe/cpio.h> @@ -45,3 +46,87 @@ void cpio_set_field ( char *field, unsigned long value ) { snprintf ( buf, sizeof ( buf ), "%08lx", value ); memcpy ( field, buf, 8 ); } + +/** + * Get CPIO image filename + * + * @v image Image + * @ret len CPIO filename length (0 for no filename) + */ +size_t cpio_name_len ( struct image *image ) { + const char *name = cpio_name ( image ); + char *sep; + size_t len; + + /* Check for existence of CPIO filename */ + if ( ! name ) + return 0; + + /* Locate separator (if any) */ + sep = strchr ( name, ' ' ); + len = ( sep ? ( ( size_t ) ( sep - name ) ) : strlen ( name ) ); + + return len; +} + +/** + * Parse CPIO image parameters + * + * @v image Image + * @v cpio CPIO header to fill in + */ +static void cpio_parse_cmdline ( struct image *image, + struct cpio_header *cpio ) { + const char *cmdline; + char *arg; + char *end; + unsigned int mode; + + /* Skip image filename */ + cmdline = ( cpio_name ( image ) + cpio_name_len ( image ) ); + + /* Look for "mode=" */ + if ( ( arg = strstr ( cmdline, "mode=" ) ) ) { + arg += 5; + mode = strtoul ( arg, &end, 8 /* Octal for file mode */ ); + if ( *end && ( *end != ' ' ) ) { + DBGC ( image, "CPIO %p strange \"mode=\" " + "terminator '%c'\n", image, *end ); + } + cpio_set_field ( cpio->c_mode, ( 0100000 | mode ) ); + } +} + +/** + * Construct CPIO header for image, if applicable + * + * @v image Image + * @v cpio CPIO header to fill in + * @ret len Length of magic CPIO header (including filename) + */ +size_t cpio_header ( struct image *image, struct cpio_header *cpio ) { + size_t name_len; + size_t len; + + /* Get filename length */ + name_len = cpio_name_len ( image ); + + /* Images with no filename are assumed to already be CPIO archives */ + if ( ! name_len ) + return 0; + + /* Construct CPIO header */ + memset ( cpio, '0', sizeof ( *cpio ) ); + memcpy ( cpio->c_magic, CPIO_MAGIC, sizeof ( cpio->c_magic ) ); + cpio_set_field ( cpio->c_mode, 0100644 ); + cpio_set_field ( cpio->c_nlink, 1 ); + cpio_set_field ( cpio->c_filesize, image->len ); + cpio_set_field ( cpio->c_namesize, ( name_len + 1 /* NUL */ ) ); + cpio_parse_cmdline ( image, cpio ); + + /* Calculate total length */ + len = ( ( sizeof ( *cpio ) + name_len + 1 /* NUL */ + CPIO_ALIGN - 1 ) + & ~( CPIO_ALIGN - 1 ) ); + + return len; +} diff --git a/src/core/image.c b/src/core/image.c index 9fe77c54c..ce8cf868b 100644 --- a/src/core/image.c +++ b/src/core/image.c @@ -176,14 +176,13 @@ int image_set_cmdline ( struct image *image, const char *cmdline ) { } /** - * Set image data + * Set image length * * @v image Image - * @v data Image data * @v len Length of image data * @ret rc Return status code */ -int image_set_data ( struct image *image, userptr_t data, size_t len ) { +int image_set_len ( struct image *image, size_t len ) { userptr_t new; /* (Re)allocate image data */ @@ -191,10 +190,28 @@ int image_set_data ( struct image *image, userptr_t data, size_t len ) { if ( ! new ) return -ENOMEM; image->data = new; + image->len = len; + + return 0; +} + +/** + * Set image data + * + * @v image Image + * @v data Image data + * @v len Length of image data + * @ret rc Return status code + */ +int image_set_data ( struct image *image, userptr_t data, size_t len ) { + int rc; + + /* Set image length */ + if ( ( rc = image_set_len ( image, len ) ) != 0 ) + return rc; /* Copy in new image data */ memcpy_user ( image->data, 0, data, 0, len ); - image->len = len; return 0; } diff --git a/src/core/open.c b/src/core/open.c index c27d8a021..f9198c9d9 100644 --- a/src/core/open.c +++ b/src/core/open.c @@ -25,6 +25,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdarg.h> #include <string.h> +#include <strings.h> #include <errno.h> #include <ipxe/xfer.h> #include <ipxe/uri.h> @@ -47,7 +48,7 @@ struct uri_opener * xfer_uri_opener ( const char *scheme ) { struct uri_opener *opener; for_each_table_entry ( opener, URI_OPENERS ) { - if ( strcmp ( scheme, opener->scheme ) == 0 ) + if ( strcasecmp ( scheme, opener->scheme ) == 0 ) return opener; } return NULL; diff --git a/src/core/string.c b/src/core/string.c index 188fe0864..9a1b9b72a 100644 --- a/src/core/string.c +++ b/src/core/string.c @@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> #include <string.h> +#include <strings.h> #include <ctype.h> /** @file @@ -205,11 +206,24 @@ int strncmp ( const char *first, const char *second, size_t max ) { * @ret diff Difference */ int strcasecmp ( const char *first, const char *second ) { + + return strncasecmp ( first, second, ~( ( size_t ) 0 ) ); +} + +/** + * Compare case-insensitive strings + * + * @v first First string + * @v second Second string + * @v max Maximum length to compare + * @ret diff Difference + */ +int strncasecmp ( const char *first, const char *second, size_t max ) { const uint8_t *first_bytes = ( ( const uint8_t * ) first ); const uint8_t *second_bytes = ( ( const uint8_t * ) second ); int diff; - for ( ; ; first_bytes++, second_bytes++ ) { + for ( ; max-- ; first_bytes++, second_bytes++ ) { diff = ( toupper ( *first_bytes ) - toupper ( *second_bytes ) ); if ( diff ) @@ -217,6 +231,7 @@ int strcasecmp ( const char *first, const char *second ) { if ( ! *first_bytes ) return 0; } + return 0; } /** diff --git a/src/crypto/x509.c b/src/crypto/x509.c index 17d8c7a43..1f017eb03 100644 --- a/src/crypto/x509.c +++ b/src/crypto/x509.c @@ -25,6 +25,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdlib.h> #include <string.h> +#include <strings.h> #include <errno.h> #include <assert.h> #include <ipxe/list.h> @@ -1464,7 +1465,7 @@ static int x509_check_dnsname ( struct x509_certificate *cert, /* Compare names */ if ( ! ( ( strlen ( name ) == len ) && - ( memcmp ( name, dnsname, len ) == 0 ) ) ) + ( strncasecmp ( name, dnsname, len ) == 0 ) ) ) return -ENOENT; if ( name != fullname ) { diff --git a/src/drivers/net/ath/ath5k/ath5k_eeprom.c b/src/drivers/net/ath/ath5k/ath5k_eeprom.c index 12519bc59..46f33d1e8 100644 --- a/src/drivers/net/ath/ath5k/ath5k_eeprom.c +++ b/src/drivers/net/ath/ath5k/ath5k_eeprom.c @@ -39,6 +39,9 @@ static int ath5k_hw_eeprom_read(struct ath5k_hw *ah, u32 offset, u16 *data) { u32 status, timeout; + /* Avoid returning uninitialised data on error */ + *data = 0xffff; + /* * Initialize EEPROM access */ diff --git a/src/drivers/net/bnxt/bnxt.c b/src/drivers/net/bnxt/bnxt.c index b8663c00a..e3876503f 100644 --- a/src/drivers/net/bnxt/bnxt.c +++ b/src/drivers/net/bnxt/bnxt.c @@ -23,16 +23,74 @@ static void bnxt_adv_cq_index ( struct bnxt *bp, u16 cnt ); static int bnxt_rx_complete ( struct net_device *dev, struct rx_pkt_cmpl *rx ); void bnxt_link_evt ( struct bnxt *bp, struct hwrm_async_event_cmpl *evt ); +static struct pci_device_id bnxt_nics[] = { + PCI_ROM( 0x14e4, 0x16c0, "14e4-16C0", "14e4-16C0", 0 ), + PCI_ROM( 0x14e4, 0x16c1, "14e4-16C1", "14e4-16C1", BNXT_FLAG_PCI_VF ), + PCI_ROM( 0x14e4, 0x16c8, "14e4-16C8", "14e4-16C8", 0 ), + PCI_ROM( 0x14e4, 0x16c9, "14e4-16C9", "14e4-16C9", 0 ), + PCI_ROM( 0x14e4, 0x16ca, "14e4-16CA", "14e4-16CA", 0 ), + PCI_ROM( 0x14e4, 0x16cc, "14e4-16CC", "14e4-16CC", 0 ), + PCI_ROM( 0x14e4, 0x16cd, "14e4-16CD", "14e4-16CD", 0 ), + PCI_ROM( 0x14e4, 0x16ce, "14e4-16CE", "14e4-16CE", 0 ), + PCI_ROM( 0x14e4, 0x16cf, "14e4-16CF", "14e4-16CF", 0 ), + PCI_ROM( 0x14e4, 0x16d0, "14e4-16D0", "14e4-16D0", 0 ), + PCI_ROM( 0x14e4, 0x16d1, "14e4-16D1", "14e4-16D1", 0 ), + PCI_ROM( 0x14e4, 0x16d2, "14e4-16D2", "14e4-16D2", 0 ), + PCI_ROM( 0x14e4, 0x16d4, "14e4-16D4", "14e4-16D4", 0 ), + PCI_ROM( 0x14e4, 0x16d5, "14e4-16D5", "14e4-16D5", 0 ), + PCI_ROM( 0x14e4, 0x16d6, "14e4-16D6", "14e4-16D6", 0 ), + PCI_ROM( 0x14e4, 0x16d7, "14e4-16D7", "14e4-16D7", 0 ), + PCI_ROM( 0x14e4, 0x16d8, "14e4-16D8", "14e4-16D8", 0 ), + PCI_ROM( 0x14e4, 0x16d9, "14e4-16D9", "14e4-16D9", 0 ), + PCI_ROM( 0x14e4, 0x16da, "14e4-16DA", "14e4-16DA", 0 ), + PCI_ROM( 0x14e4, 0x16db, "14e4-16DB", "14e4-16DB", 0 ), + PCI_ROM( 0x14e4, 0x16dc, "14e4-16DC", "14e4-16DC", BNXT_FLAG_PCI_VF ), + PCI_ROM( 0x14e4, 0x16de, "14e4-16DE", "14e4-16DE", 0 ), + PCI_ROM( 0x14e4, 0x16df, "14e4-16DF", "14e4-16DF", 0 ), + PCI_ROM( 0x14e4, 0x16e0, "14e4-16E0", "14e4-16E0", 0 ), + PCI_ROM( 0x14e4, 0x16e2, "14e4-16E2", "14e4-16E2", 0 ), + PCI_ROM( 0x14e4, 0x16e3, "14e4-16E3", "14e4-16E3", 0 ), + PCI_ROM( 0x14e4, 0x16e4, "14e4-16E4", "14e4-16E4", 0 ), + PCI_ROM( 0x14e4, 0x16e7, "14e4-16E7", "14e4-16E7", 0 ), + PCI_ROM( 0x14e4, 0x16e8, "14e4-16E8", "14e4-16E8", 0 ), + PCI_ROM( 0x14e4, 0x16e9, "14e4-16E9", "14e4-16E9", 0 ), + PCI_ROM( 0x14e4, 0x16ea, "14e4-16EA", "14e4-16EA", 0 ), + PCI_ROM( 0x14e4, 0x16eb, "14e4-16EB", "14e4-16EB", 0 ), + PCI_ROM( 0x14e4, 0x16ec, "14e4-16EC", "14e4-16EC", 0 ), + PCI_ROM( 0x14e4, 0x16ed, "14e4-16ED", "14e4-16ED", 0 ), + PCI_ROM( 0x14e4, 0x16ee, "14e4-16EE", "14e4-16EE", 0 ), + PCI_ROM( 0x14e4, 0x16ef, "14e4-16EF", "14e4-16EF", 0 ), + PCI_ROM( 0x14e4, 0x16f0, "14e4-16F0", "14e4-16F0", 0 ), + PCI_ROM( 0x14e4, 0x16f1, "14e4-16F1", "14e4-16F1", 0 ), + PCI_ROM( 0x14e4, 0x1604, "14e4-1604", "14e4-1604", 0 ), + PCI_ROM( 0x14e4, 0x1605, "14e4-1605", "14e4-1605", 0 ), + PCI_ROM( 0x14e4, 0x1606, "14e4-1606", "14e4-1606", 0 ), + PCI_ROM( 0x14e4, 0x1609, "14e4-1609", "14e4-1609", 0 ), + PCI_ROM( 0x14e4, 0x1614, "14e4-1614", "14e4-1614", 0 ), + PCI_ROM( 0x14e4, 0xd802, "14e4-D802", "14e4-D802", 0 ), + PCI_ROM( 0x14e4, 0xd804, "14e4-D804", "14e4-D804", 0 ), + PCI_ROM( 0x14e4, 0x1750, "14e4-1750", "14e4-1750", 0 ), + PCI_ROM( 0x14e4, 0x1802, "14e4-1802", "14e4-1802", 0 ), + PCI_ROM( 0x14e4, 0x1805, "14e4-1805", "14e4-1805", 0 ), + PCI_ROM( 0x14e4, 0x1751, "14e4-1751", "14e4-1751", 0 ), + PCI_ROM( 0x14e4, 0x1801, "14e4-1801", "14e4-1801", 0 ), + PCI_ROM( 0x14e4, 0x1804, "14e4-1804", "14e4-1804", 0 ), + PCI_ROM( 0x14e4, 0x1752, "14e4-1752", "14e4-1752", 0 ), + PCI_ROM( 0x14e4, 0x1800, "14e4-1800", "14e4-1800", 0 ), + PCI_ROM( 0x14e4, 0x1803, "14e4-1803", "14e4-1803", 0 ), + PCI_ROM( 0x14e4, 0x1806, "14e4-1806", "14e4-1806", BNXT_FLAG_PCI_VF ), + PCI_ROM( 0x14e4, 0x1807, "14e4-1807", "14e4-1807", BNXT_FLAG_PCI_VF ), + PCI_ROM( 0x14e4, 0x1808, "14e4-1808", "14e4-1808", BNXT_FLAG_PCI_VF ), + PCI_ROM( 0x14e4, 0x1809, "14e4-1809", "14e4-1809", BNXT_FLAG_PCI_VF ), +}; + /** * Check if Virtual Function */ u8 bnxt_is_pci_vf ( struct pci_device *pdev ) { - u16 i; - - for ( i = 0; i < ARRAY_SIZE ( bnxt_vf_nics ); i++ ) { - if ( pdev->device == bnxt_vf_nics[i] ) - return 1; + if ( FLAG_TEST ( pdev->id->driver_data, BNXT_FLAG_PCI_VF ) ) { + return 1; } return 0; } diff --git a/src/drivers/net/bnxt/bnxt.h b/src/drivers/net/bnxt/bnxt.h index 4cca07bd3..2cbaec5e5 100644 --- a/src/drivers/net/bnxt/bnxt.h +++ b/src/drivers/net/bnxt/bnxt.h @@ -51,6 +51,7 @@ union dma_addr64_t { #define BNXT_FLAG_MULTI_HOST 0x0008 #define BNXT_FLAG_NPAR_MODE 0x0010 #define BNXT_FLAG_ATOMICS_ENABLE 0x0020 +#define BNXT_FLAG_PCI_VF 0x0040 /******************************************************************************* * Status codes. ******************************************************************************/ @@ -867,140 +868,4 @@ struct bnxt { FUNC_VF_CFG_REQ_ENABLES_ASYNC_EVENT_CR | \ FUNC_VF_CFG_REQ_ENABLES_DFLT_MAC_ADDR) -/* Device ID's */ -#define PCI_VID_BCOM 0x14e4 #define CHIP_NUM_57500 0x1750 - -#define DID_57508 0x1750 -#define DID_57508_MF 0x1802 -#define DID_57508_MF_RDMA 0x1805 -#define DID_57504 0x1751 -#define DID_57504_MF 0x1801 -#define DID_57504_MF_RDMA 0x1804 -#define DID_57502 0x1752 -#define DID_57502_MF 0x1800 -#define DID_57502_MF_RDMA 0x1803 -#define DID_57508_VF 0x1806 -#define DID_57508_VF_RDMA 0x1807 -#define DID_57508_VF_HV 0x1806 -#define DID_57508_VF_RDMA_HV 0x1807 -/* Stratus Device IDs */ -#define DID_57320_1 0x16F0 -#define DID_57320_2 0x16F1 -#define DID_57454_MHB 0x1604 -#define DID_57454_MHB_RDMA 0x1605 -#define DID_57454_VF_RDMA 0x1606 -#define DID_57454_VF 0x1609 -#define DID_57454 0x1614 -#define DID_58802 0xD802 -#define DID_58804 0xD804 - -#define DID_57417_RDMA_MF 0x16C0 -#define DID_57417_VF_RDMA 0x16c1 -#define DID_57301 0x16C8 -#define DID_57302 0x16C9 -#define DID_57304 0x16CA -#define DID_57417_MF 0x16CC -#define DID_58700 0x16CD -#define DID_57311 0x16CE -#define DID_57312 0x16CF -#define DID_57402 0x16D0 -#define DID_57404 0x16D1 -#define DID_57406 0x16D2 -#define DID_57402_MF 0x16D4 -#define DID_57407C 0x16D5 -#define DID_57412 0x16D6 -#define DID_57414 0x16D7 -#define DID_57416C 0x16D8 -#define DID_57417C 0x16D9 -#define DID_57402L 0x16DA -#define DID_57404L 0x16DB -#define DID_57417_VF 0x16dc -#define DID_57412_MF 0x16DE -#define DID_57314 0x16DF -#define DID_57317C 0x16E0 -#define DID_57417F 0x16E2 -#define DID_57416F 0x16E3 -#define DID_57317F 0x16E4 -#define DID_57404_MF 0x16E7 -#define DID_57406_MF 0x16E8 -#define DID_57407F 0x16E9 -#define DID_57407_MF 0x16EA -#define DID_57412_RDMA_MF 0x16EB -#define DID_57414_MF 0x16EC -#define DID_57414_RDMA_MF 0x16ED -#define DID_57416_MF 0x16EE -#define DID_57416_RDMA_MF 0x16EF - -static struct pci_device_id bnxt_nics[] = { - PCI_ROM(PCI_VID_BCOM, DID_57417_RDMA_MF, "14e4-16C0", "14e4-16C0", 0), - PCI_ROM(PCI_VID_BCOM, DID_57417_VF_RDMA, "14e4-16C1", "14e4-16C1", 0), - PCI_ROM(PCI_VID_BCOM, DID_57301, "14e4-16C8", "14e4-16C8", 0), - PCI_ROM(PCI_VID_BCOM, DID_57302, "14e4-16C9", "14e4-16C9", 0), - PCI_ROM(PCI_VID_BCOM, DID_57304, "14e4-16CA", "14e4-16CA", 0), - PCI_ROM(PCI_VID_BCOM, DID_57417_MF, "14e4-16CC", "14e4-16CC", 0), - PCI_ROM(PCI_VID_BCOM, DID_58700, "14e4-16CD", "14e4-16CD", 0), - PCI_ROM(PCI_VID_BCOM, DID_57311, "14e4-16CE", "14e4-16CE", 0), - PCI_ROM(PCI_VID_BCOM, DID_57312, "14e4-16CF", "14e4-16CF", 0), - PCI_ROM(PCI_VID_BCOM, DID_57402, "14e4-16D0", "14e4-16D0", 0), - PCI_ROM(PCI_VID_BCOM, DID_57404, "14e4-16D1", "14e4-16D1", 0), - PCI_ROM(PCI_VID_BCOM, DID_57406, "14e4-16D2", "14e4-16D2", 0), - PCI_ROM(PCI_VID_BCOM, DID_57402_MF, "14e4-16D4", "14e4-16D4", 0), - PCI_ROM(PCI_VID_BCOM, DID_57407C, "14e4-16D5", "14e4-16D5", 0), - PCI_ROM(PCI_VID_BCOM, DID_57412, "14e4-16D6", "14e4-16D6", 0), - PCI_ROM(PCI_VID_BCOM, DID_57414, "14e4-16D7", "14e4-16D7", 0), - PCI_ROM(PCI_VID_BCOM, DID_57416C, "14e4-16D8", "14e4-16D8", 0), - PCI_ROM(PCI_VID_BCOM, DID_57417C, "14e4-16D9", "14e4-16D9", 0), - PCI_ROM(PCI_VID_BCOM, DID_57402L, "14e4-16DA", "14e4-16DA", 0), - PCI_ROM(PCI_VID_BCOM, DID_57404L, "14e4-16DB", "14e4-16DB", 0), - PCI_ROM(PCI_VID_BCOM, DID_57417_VF, "14e4-16DC", "14e4-16DC", 0), - PCI_ROM(PCI_VID_BCOM, DID_57412_MF, "14e4-16DE", "14e4-16DE", 0), - PCI_ROM(PCI_VID_BCOM, DID_57314, "14e4-16DF", "14e4-16DF", 0), - PCI_ROM(PCI_VID_BCOM, DID_57317C, "14e4-16E0", "14e4-16E0", 0), - PCI_ROM(PCI_VID_BCOM, DID_57417F, "14e4-16E2", "14e4-16E2", 0), - PCI_ROM(PCI_VID_BCOM, DID_57416F, "14e4-16E3", "14e4-16E3", 0), - PCI_ROM(PCI_VID_BCOM, DID_57317F, "14e4-16E4", "14e4-16E4", 0), - PCI_ROM(PCI_VID_BCOM, DID_57404_MF, "14e4-16E7", "14e4-16E7", 0), - PCI_ROM(PCI_VID_BCOM, DID_57406_MF, "14e4-16E8", "14e4-16E8", 0), - PCI_ROM(PCI_VID_BCOM, DID_57407F, "14e4-16E9", "14e4-16E9", 0), - PCI_ROM(PCI_VID_BCOM, DID_57407_MF, "14e4-16EA", "14e4-16EA", 0), - PCI_ROM(PCI_VID_BCOM, DID_57412_RDMA_MF, "14e4-16EB", "14e4-16EB", 0), - PCI_ROM(PCI_VID_BCOM, DID_57414_MF, "14e4-16EC", "14e4-16EC", 0), - PCI_ROM(PCI_VID_BCOM, DID_57414_RDMA_MF, "14e4-16ED", "14e4-16ED", 0), - PCI_ROM(PCI_VID_BCOM, DID_57416_MF, "14e4-16EE", "14e4-16EE", 0), - PCI_ROM(PCI_VID_BCOM, DID_57416_RDMA_MF, "14e4-16EF", "14e4-16EF", 0), - - PCI_ROM(PCI_VID_BCOM, DID_57320_1, "14e4-16F0", "14e4-16F0", 0), - PCI_ROM(PCI_VID_BCOM, DID_57320_2, "14e4-16F1", "14e4-16F1", 0), - PCI_ROM(PCI_VID_BCOM, DID_57454_MHB, "14e4-1604", "14e4-1604", 0), - PCI_ROM(PCI_VID_BCOM, DID_57454_MHB_RDMA, "14e4-1605", "14e4-1605", 0), - PCI_ROM(PCI_VID_BCOM, DID_57454_VF_RDMA, "14e4-1606", "14e4-1606", 0), - PCI_ROM(PCI_VID_BCOM, DID_57454_VF, "14e4-1609", "14e4-1609", 0), - PCI_ROM(PCI_VID_BCOM, DID_57454, "14e4-1614", "14e4-1614", 0), - PCI_ROM(PCI_VID_BCOM, DID_58802, "14e4-D802", "14e4-D802", 0), - PCI_ROM(PCI_VID_BCOM, DID_58804, "14e4-D804", "14e4-D804", 0), - - PCI_ROM(PCI_VID_BCOM, DID_57508, "14e4-1750", "14e4-1750", 0), - PCI_ROM(PCI_VID_BCOM, DID_57508_MF, "14e4-1802", "14e4-1802", 0), - PCI_ROM(PCI_VID_BCOM, DID_57508_MF_RDMA, "14e4-1805", "14e4-1805", 0), - PCI_ROM(PCI_VID_BCOM, DID_57504, "14e4-1751", "14e4-1751", 0), - PCI_ROM(PCI_VID_BCOM, DID_57504_MF, "14e4-1801", "14e4-1801", 0), - PCI_ROM(PCI_VID_BCOM, DID_57504_MF_RDMA, "14e4-1804", "14e4-1804", 0), - PCI_ROM(PCI_VID_BCOM, DID_57502, "14e4-1752", "14e4-1752", 0), - PCI_ROM(PCI_VID_BCOM, DID_57502_MF, "14e4-1800", "14e4-1800", 0), - PCI_ROM(PCI_VID_BCOM, DID_57502_MF_RDMA, "14e4-1803", "14e4-1803", 0), - PCI_ROM(PCI_VID_BCOM, DID_57508_VF, "14e4-1806", "14e4-1806", 0), - PCI_ROM(PCI_VID_BCOM, DID_57508_VF_RDMA, "14e4-1807", "14e4-1807", 0), - PCI_ROM(PCI_VID_BCOM, DID_57508_VF_HV, "14e4-1808", "14e4-1808", 0), - PCI_ROM(PCI_VID_BCOM, - DID_57508_VF_RDMA_HV, "14e4-1809", "14e4-1809", 0), -}; - -static u16 bnxt_vf_nics[] = { - DID_57508_VF, - DID_57508_VF_RDMA, - DID_57508_VF_HV, - DID_57508_VF_RDMA_HV, - DID_57417_VF, - DID_57417_VF_RDMA, -}; diff --git a/src/drivers/net/intel.c b/src/drivers/net/intel.c index 83492961f..ea3ebf68d 100644 --- a/src/drivers/net/intel.c +++ b/src/drivers/net/intel.c @@ -1025,6 +1025,12 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x043a, "dh8900cc-f", "DH8900CC Fiber", 0 ), PCI_ROM ( 0x8086, 0x043c, "dh8900cc-b", "DH8900CC Backplane", 0 ), PCI_ROM ( 0x8086, 0x0440, "dh8900cc-s", "DH8900CC SFP", 0 ), + PCI_ROM ( 0x8086, 0x0d4c, "i219lm-11", "I219-LM (11)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x0d4d, "i219v-11", "I219-V (11)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x0d4e, "i219lm-10", "I219-LM (10)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x0d4f, "i219v-10", "I219-V (10)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x0d53, "i219lm-12", "I219-LM (12)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x0d55, "i219v-12", "I219-V (12)", INTEL_I219 ), PCI_ROM ( 0x8086, 0x1000, "82542-f", "82542 (Fiber)", 0 ), PCI_ROM ( 0x8086, 0x1001, "82543gc-f", "82543GC (Fiber)", 0 ), PCI_ROM ( 0x8086, 0x1004, "82543gc", "82543GC (Copper)", 0 ), @@ -1161,6 +1167,12 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x15e1, "i219lm-9", "I219-LM (9)", INTEL_I219 ), PCI_ROM ( 0x8086, 0x15e2, "i219v-9", "I219-V (9)", INTEL_I219 ), PCI_ROM ( 0x8086, 0x15e3, "i219lm-5", "I219-LM (5)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15f4, "i219lm-15", "I219-LM (15)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15f5, "i219v-15", "I219-V (15)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15f9, "i219lm-14", "I219-LM (14)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15fa, "i219v-14", "I219-V (14)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15fb, "i219lm-13", "I219-LM (13)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15fc, "i219v-13", "I219-V (13)", INTEL_I219 ), PCI_ROM ( 0x8086, 0x1f41, "i354", "I354", INTEL_NO_ASDE ), PCI_ROM ( 0x8086, 0x294c, "82566dc-2", "82566DC-2", 0 ), PCI_ROM ( 0x8086, 0x2e6e, "cemedia", "CE Media Processor", 0 ), diff --git a/src/drivers/net/rdc.c b/src/drivers/net/rdc.c new file mode 100644 index 000000000..c3239c002 --- /dev/null +++ b/src/drivers/net/rdc.c @@ -0,0 +1,694 @@ +/* + * Copyright (C) 2021 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <byteswap.h> +#include <ipxe/netdevice.h> +#include <ipxe/ethernet.h> +#include <ipxe/if_ether.h> +#include <ipxe/iobuf.h> +#include <ipxe/malloc.h> +#include <ipxe/pci.h> +#include "rdc.h" + +/** @file + * + * RDC R6040 network driver + * + */ + +/****************************************************************************** + * + * Device reset + * + ****************************************************************************** + */ + +/** + * Reset hardware + * + * @v rdc RDC device + * @ret rc Return status code + */ +static int rdc_reset ( struct rdc_nic *rdc ) { + unsigned int i; + + /* Reset NIC */ + writew ( RDC_MCR1_RST, rdc->regs + RDC_MCR1 ); + + /* Wait for reset to complete */ + for ( i = 0 ; i < RDC_RESET_MAX_WAIT_MS ; i++ ) { + + /* Check for reset completion */ + if ( readw ( rdc->regs + RDC_MCR1 ) & RDC_MCR1_RST ) { + mdelay ( 1 ); + continue; + } + + /* Reset internal state machine */ + writew ( RDC_MACSM_RST, rdc->regs + RDC_MACSM ); + writew ( 0, rdc->regs + RDC_MACSM ); + mdelay ( RDC_MACSM_RESET_DELAY_MS ); + + return 0; + } + + DBGC ( rdc, "RDC %p timed out waiting for reset\n", rdc ); + return -ETIMEDOUT; +} + +/****************************************************************************** + * + * MII interface + * + ****************************************************************************** + */ + +/** + * Read from MII register + * + * @v mdio MII interface + * @v phy PHY address + * @v reg Register address + * @ret value Data read, or negative error + */ +static int rdc_mii_read ( struct mii_interface *mdio, unsigned int phy, + unsigned int reg ) { + struct rdc_nic *rdc = container_of ( mdio, struct rdc_nic, mdio ); + uint16_t mmdio; + unsigned int i; + + /* Initiate read */ + mmdio = ( RDC_MMDIO_MIIRD | RDC_MMDIO_PHYAD ( phy ) | + RDC_MMDIO_REGAD ( reg ) ); + writew ( mmdio, rdc->regs + RDC_MMDIO ); + + /* Wait for read to complete */ + for ( i = 0 ; i < RDC_MII_MAX_WAIT_US ; i++ ) { + + /* Check for read completion */ + if ( readw ( rdc->regs + RDC_MMDIO ) & RDC_MMDIO_MIIRD ) { + udelay ( 1 ); + continue; + } + + /* Return register value */ + return ( readw ( rdc->regs + RDC_MMRD ) ); + } + + DBGC ( rdc, "RDC %p timed out waiting for MII read\n", rdc ); + return -ETIMEDOUT; +} + +/** + * Write to MII register + * + * @v mdio MII interface + * @v phy PHY address + * @v reg Register address + * @v data Data to write + * @ret rc Return status code + */ +static int rdc_mii_write ( struct mii_interface *mdio, unsigned int phy, + unsigned int reg, unsigned int data ) { + struct rdc_nic *rdc = container_of ( mdio, struct rdc_nic, mdio ); + uint16_t mmdio; + unsigned int i; + + /* Initiate write */ + mmdio = ( RDC_MMDIO_MIIWR | RDC_MMDIO_PHYAD ( phy ) | + RDC_MMDIO_REGAD ( reg ) ); + writew ( data, rdc->regs + RDC_MMWD ); + writew ( mmdio, rdc->regs + RDC_MMDIO ); + + /* Wait for write to complete */ + for ( i = 0 ; i < RDC_MII_MAX_WAIT_US ; i++ ) { + + /* Check for write completion */ + if ( readw ( rdc->regs + RDC_MMDIO ) & RDC_MMDIO_MIIWR ) { + udelay ( 1 ); + continue; + } + + return 0; + } + + DBGC ( rdc, "RDC %p timed out waiting for MII write\n", rdc ); + return -ETIMEDOUT; +} + +/** RDC MII operations */ +static struct mii_operations rdc_mii_operations = { + .read = rdc_mii_read, + .write = rdc_mii_write, +}; + +/****************************************************************************** + * + * Link state + * + ****************************************************************************** + */ + +/** + * Initialise PHY + * + * @v rdc RDC device + * @ret rc Return status code + */ +static int rdc_init_phy ( struct rdc_nic *rdc ) { + int rc; + + /* Find PHY address */ + if ( ( rc = mii_find ( &rdc->mii ) ) != 0 ) { + DBGC ( rdc, "RDC %p could not find PHY address: %s\n", + rdc, strerror ( rc ) ); + return rc; + } + + /* Reset PHY */ + if ( ( rc = mii_reset ( &rdc->mii ) ) != 0 ) { + DBGC ( rdc, "RDC %p could not reset PHY: %s\n", + rdc, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Check link state + * + * @v netdev Network device + * @ret rc Return status code + */ +static int rdc_check_link ( struct net_device *netdev ) { + struct rdc_nic *rdc = netdev->priv; + int rc; + + /* Check link state */ + if ( ( rc = mii_check_link ( &rdc->mii, netdev ) ) != 0 ) { + DBGC ( rdc, "RDC %p could not check link: %s\n", + rdc, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Create descriptor ring + * + * @v rdc RDC device + * @v ring Descriptor ring + * @ret rc Return status code + */ +static int rdc_create_ring ( struct rdc_nic *rdc, struct rdc_ring *ring ) { + size_t len = ( ring->count * sizeof ( ring->desc[0] ) ); + struct rdc_descriptor *desc; + struct rdc_descriptor *next; + physaddr_t start; + unsigned int i; + + /* Allocate descriptor ring */ + ring->desc = dma_alloc ( rdc->dma, &ring->map, len, len ); + if ( ! ring->desc ) + return -ENOMEM; + + /* Initialise descriptor ring */ + memset ( ring->desc, 0, len ); + for ( i = 0 ; i < ring->count ; i++ ) { + desc = &ring->desc[i]; + next = &ring->desc[ ( i + 1 ) & ( ring->count - 1 ) ]; + desc->next = cpu_to_le32 ( dma ( &ring->map, next ) ); + } + + /* Program ring address */ + start = dma ( &ring->map, ring->desc ); + writew ( ( start >> 0 ), ( rdc->regs + ring->reg + RDC_MxDSA_LO ) ); + writew ( ( start >> 16 ), ( rdc->regs + ring->reg + RDC_MxDSA_HI ) ); + + DBGC ( rdc, "RDC %p ring %#02x is at [%08lx,%08lx)\n", + rdc, ring->reg, virt_to_phys ( ring->desc ), + ( virt_to_phys ( ring->desc ) + len ) ); + return 0; +} + +/** + * Destroy descriptor ring + * + * @v rdc RDC device + * @v ring Descriptor ring + */ +static void rdc_destroy_ring ( struct rdc_nic *rdc, struct rdc_ring *ring ) { + size_t len = ( ring->count * sizeof ( ring->desc[0] ) ); + + /* Clear ring address */ + writew ( 0, ( rdc->regs + ring->reg + RDC_MxDSA_LO ) ); + writew ( 0, ( rdc->regs + ring->reg + RDC_MxDSA_HI ) ); + + /* Free descriptors */ + dma_free ( &ring->map, ring->desc, len ); + ring->desc = NULL; + + /* Reset ring */ + ring->prod = 0; + ring->cons = 0; +} + +/** + * Refill receive descriptor ring + * + * @v rdc RDC device + */ +static void rdc_refill_rx ( struct rdc_nic *rdc ) { + struct rdc_descriptor *rx; + struct io_buffer *iobuf; + unsigned int rx_idx; + + /* Refill ring */ + while ( ( rdc->rx.prod - rdc->rx.cons ) < RDC_NUM_RX_DESC ) { + + /* Allocate I/O buffer */ + iobuf = alloc_rx_iob ( RDC_RX_MAX_LEN, rdc->dma ); + if ( ! iobuf ) { + /* Wait for next refill */ + break; + } + + /* Get next receive descriptor */ + rx_idx = ( rdc->rx.prod++ % RDC_NUM_RX_DESC ); + rx = &rdc->rx.desc[rx_idx]; + + /* Populate receive descriptor */ + rx->len = cpu_to_le16 ( RDC_RX_MAX_LEN ); + rx->addr = cpu_to_le32 ( iob_dma ( iobuf ) ); + wmb(); + rx->flags = cpu_to_le16 ( RDC_FL_OWNED ); + + /* Record I/O buffer */ + assert ( rdc->rx_iobuf[rx_idx] == NULL ); + rdc->rx_iobuf[rx_idx] = iobuf; + + DBGC2 ( rdc, "RDC %p RX %d is [%lx,%lx)\n", + rdc, rx_idx, virt_to_phys ( iobuf->data ), + ( virt_to_phys ( iobuf->data ) + RDC_RX_MAX_LEN ) ); + } +} + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int rdc_open ( struct net_device *netdev ) { + struct rdc_nic *rdc = netdev->priv; + int rc; + + /* Create transmit descriptor ring */ + if ( ( rc = rdc_create_ring ( rdc, &rdc->tx ) ) != 0 ) + goto err_create_tx; + + /* Create receive descriptor ring */ + if ( ( rc = rdc_create_ring ( rdc, &rdc->rx ) ) != 0 ) + goto err_create_rx; + + /* Program receive buffer length */ + writew ( RDC_RX_MAX_LEN, rdc->regs + RDC_MRBSR ); + + /* Enable transmit and receive */ + writew ( ( RDC_MCR0_FD | RDC_MCR0_TXEN | RDC_MCR0_PROMISC | + RDC_MCR0_RXEN ), + rdc->regs + RDC_MCR0 ); + + /* Enable PHY status polling */ + writew ( ( RDC_MPSCCR_EN | RDC_MPSCCR_PHYAD ( rdc->mii.address ) | + RDC_MPSCCR_SLOW ), + rdc->regs + RDC_MPSCCR ); + + /* Fill receive ring */ + rdc_refill_rx ( rdc ); + + /* Update link state */ + rdc_check_link ( netdev ); + + return 0; + + rdc_destroy_ring ( rdc, &rdc->rx ); + err_create_rx: + rdc_destroy_ring ( rdc, &rdc->tx ); + err_create_tx: + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void rdc_close ( struct net_device *netdev ) { + struct rdc_nic *rdc = netdev->priv; + unsigned int i; + + /* Disable NIC */ + writew ( 0, rdc->regs + RDC_MCR0 ); + + /* Destroy receive descriptor ring */ + rdc_destroy_ring ( rdc, &rdc->rx ); + + /* Discard any unused receive buffers */ + for ( i = 0 ; i < RDC_NUM_RX_DESC ; i++ ) { + if ( rdc->rx_iobuf[i] ) + free_rx_iob ( rdc->rx_iobuf[i] ); + rdc->rx_iobuf[i] = NULL; + } + + /* Destroy transmit descriptor ring */ + rdc_destroy_ring ( rdc, &rdc->tx ); +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int rdc_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { + struct rdc_nic *rdc = netdev->priv; + struct rdc_descriptor *tx; + unsigned int tx_idx; + int rc; + + /* Get next transmit descriptor */ + if ( ( rdc->tx.prod - rdc->tx.cons ) >= RDC_NUM_TX_DESC ) { + DBGC ( rdc, "RDC %p out of transmit descriptors\n", rdc ); + return -ENOBUFS; + } + tx_idx = ( rdc->tx.prod % RDC_NUM_TX_DESC ); + tx = &rdc->tx.desc[tx_idx]; + + /* Pad to minimum length */ + iob_pad ( iobuf, ETH_ZLEN ); + + /* Map I/O buffer */ + if ( ( rc = iob_map_tx ( iobuf, rdc->dma ) ) != 0 ) + return rc; + + /* Update producer index */ + rdc->tx.prod++; + + /* Populate transmit descriptor */ + tx->len = cpu_to_le16 ( iob_len ( iobuf ) ); + tx->addr = cpu_to_le32 ( iob_dma ( iobuf ) ); + wmb(); + tx->flags = cpu_to_le16 ( RDC_FL_OWNED ); + wmb(); + + /* Notify card that there are packets ready to transmit */ + writew ( RDC_MTPR_TM2TX, rdc->regs + RDC_MTPR ); + + return 0; +} + +/** + * Poll for completed packets + * + * @v netdev Network device + */ +static void rdc_poll_tx ( struct net_device *netdev ) { + struct rdc_nic *rdc = netdev->priv; + struct rdc_descriptor *tx; + unsigned int tx_idx; + + /* Check for completed packets */ + while ( rdc->tx.cons != rdc->tx.prod ) { + + /* Get next transmit descriptor */ + tx_idx = ( rdc->tx.cons % RDC_NUM_TX_DESC ); + tx = &rdc->tx.desc[tx_idx]; + + /* Stop if descriptor is still in use */ + if ( tx->flags & cpu_to_le16 ( RDC_FL_OWNED ) ) + return; + DBGC2 ( rdc, "RDC %p TX %d complete\n", rdc, tx_idx ); + + /* Complete transmit descriptor */ + rdc->tx.cons++; + netdev_tx_complete_next ( netdev ); + } +} + +/** + * Poll for received packets + * + * @v netdev Network device + */ +static void rdc_poll_rx ( struct net_device *netdev ) { + struct rdc_nic *rdc = netdev->priv; + struct rdc_descriptor *rx; + struct io_buffer *iobuf; + unsigned int rx_idx; + size_t len; + + /* Check for received packets */ + while ( rdc->rx.cons != rdc->rx.prod ) { + + /* Get next receive descriptor */ + rx_idx = ( rdc->rx.cons % RDC_NUM_RX_DESC ); + rx = &rdc->rx.desc[rx_idx]; + + /* Stop if descriptor is still in use */ + if ( rx->flags & cpu_to_le16 ( RDC_FL_OWNED ) ) + return; + + /* Populate I/O buffer */ + iobuf = rdc->rx_iobuf[rx_idx]; + rdc->rx_iobuf[rx_idx] = NULL; + len = le16_to_cpu ( rx->len ); + iob_put ( iobuf, len ); + iob_unput ( iobuf, 4 /* strip CRC */ ); + + /* Hand off to network stack */ + if ( rx->flags & cpu_to_le16 ( RDC_FL_OK ) ) { + DBGC2 ( rdc, "RDC %p RX %d complete (length %zd)\n", + rdc, rx_idx, len ); + netdev_rx ( netdev, iobuf ); + } else { + DBGC2 ( rdc, "RDC %p RX %d error (length %zd, " + "flags %#04x)\n", rdc, rx_idx, len, + le16_to_cpu ( rx->flags ) ); + netdev_rx_err ( netdev, iobuf, -EIO ); + } + rdc->rx.cons++; + } +} + +/** + * Poll for completed and received packets + * + * @v netdev Network device + */ +static void rdc_poll ( struct net_device *netdev ) { + struct rdc_nic *rdc = netdev->priv; + uint16_t misr; + + /* Check for (and acknowledge) interrupts */ + misr = readw ( rdc->regs + RDC_MISR ); + + /* Poll for TX completions, if applicable */ + if ( misr & RDC_MIRQ_TX ) + rdc_poll_tx ( netdev ); + + /* Poll for RX completions, if applicable */ + if ( misr & RDC_MIRQ_RX ) + rdc_poll_rx ( netdev ); + + /* Check link state, if applicable */ + if ( misr & RDC_MIRQ_LINK ) + rdc_check_link ( netdev ); + + /* Check for unexpected interrupts */ + if ( misr & ~( RDC_MIRQ_LINK | RDC_MIRQ_TX | RDC_MIRQ_RX_EARLY | + RDC_MIRQ_RX_EMPTY | RDC_MIRQ_RX ) ) { + DBGC ( rdc, "RDC %p unexpected MISR %#04x\n", rdc, misr ); + /* Report as a TX error */ + netdev_tx_err ( netdev, NULL, -ENOTSUP ); + } + + /* Refill receive ring */ + rdc_refill_rx ( rdc ); +} + +/** + * Enable or disable interrupts + * + * @v netdev Network device + * @v enable Interrupts should be enabled + */ +static void rdc_irq ( struct net_device *netdev, int enable ) { + struct rdc_nic *rdc = netdev->priv; + uint16_t mier; + + /* Enable/disable interrupts */ + mier = ( enable ? ( RDC_MIRQ_LINK | RDC_MIRQ_TX | RDC_MIRQ_RX ) : 0 ); + writew ( mier, rdc->regs + RDC_MIER ); +} + +/** RDC network device operations */ +static struct net_device_operations rdc_operations = { + .open = rdc_open, + .close = rdc_close, + .transmit = rdc_transmit, + .poll = rdc_poll, + .irq = rdc_irq, +}; + +/****************************************************************************** + * + * PCI interface + * + ****************************************************************************** + */ + +/** + * Probe PCI device + * + * @v pci PCI device + * @ret rc Return status code + */ +static int rdc_probe ( struct pci_device *pci ) { + struct net_device *netdev; + struct rdc_nic *rdc; + union rdc_mac mac; + int rc; + + /* Allocate and initialise net device */ + netdev = alloc_etherdev ( sizeof ( *rdc ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &rdc_operations ); + rdc = netdev->priv; + pci_set_drvdata ( pci, netdev ); + netdev->dev = &pci->dev; + memset ( rdc, 0, sizeof ( *rdc ) ); + rdc->dma = &pci->dma; + mdio_init ( &rdc->mdio, &rdc_mii_operations ); + mii_init ( &rdc->mii, &rdc->mdio, 0 ); + rdc_init_ring ( &rdc->tx, RDC_NUM_TX_DESC, RDC_MTDSA ); + rdc_init_ring ( &rdc->rx, RDC_NUM_RX_DESC, RDC_MRDSA ); + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map registers */ + rdc->regs = pci_ioremap ( pci, pci->membase, RDC_BAR_SIZE ); + if ( ! rdc->regs ) { + rc = -ENODEV; + goto err_ioremap; + } + + /* Fetch MAC address */ + mac.mid[0] = cpu_to_le16 ( readw ( rdc->regs + RDC_MID0 ) ); + mac.mid[1] = cpu_to_le16 ( readw ( rdc->regs + RDC_MID1 ) ); + mac.mid[2] = cpu_to_le16 ( readw ( rdc->regs + RDC_MID2 ) ); + memcpy ( netdev->hw_addr, mac.raw, ETH_ALEN ); + + /* Reset the NIC */ + if ( ( rc = rdc_reset ( rdc ) ) != 0 ) + goto err_reset; + + /* Initialise PHY */ + if ( ( rc = rdc_init_phy ( rdc ) ) != 0 ) + goto err_init_phy; + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register_netdev; + + /* Set initial link state */ + rdc_check_link ( netdev ); + + return 0; + + unregister_netdev ( netdev ); + err_register_netdev: + err_init_phy: + rdc_reset ( rdc ); + err_reset: + iounmap ( rdc->regs ); + err_ioremap: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void rdc_remove ( struct pci_device *pci ) { + struct net_device *netdev = pci_get_drvdata ( pci ); + struct rdc_nic *rdc = netdev->priv; + + /* Unregister network device */ + unregister_netdev ( netdev ); + + /* Reset card */ + rdc_reset ( rdc ); + + /* Free network device */ + iounmap ( rdc->regs ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** RDC PCI device IDs */ +static struct pci_device_id rdc_nics[] = { + PCI_ROM ( 0x17f3, 0x6040, "r6040", "RDC R6040", 0 ), +}; + +/** RDC PCI driver */ +struct pci_driver rdc_driver __pci_driver = { + .ids = rdc_nics, + .id_count = ( sizeof ( rdc_nics ) / sizeof ( rdc_nics[0] ) ), + .probe = rdc_probe, + .remove = rdc_remove, +}; diff --git a/src/drivers/net/rdc.h b/src/drivers/net/rdc.h new file mode 100644 index 000000000..ee1671fdd --- /dev/null +++ b/src/drivers/net/rdc.h @@ -0,0 +1,194 @@ +#ifndef _RDC_H +#define _RDC_H + +/** @file + * + * RDC R6040 network driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> +#include <ipxe/if_ether.h> +#include <ipxe/mii.h> + +/** RDC BAR size */ +#define RDC_BAR_SIZE 256 + +/** An RDC descriptor */ +struct rdc_descriptor { + /** Flags */ + uint16_t flags; + /** Length */ + uint16_t len; + /** Address */ + uint32_t addr; + /** Next descriptor */ + uint32_t next; + /** Reserved */ + uint32_t reserved; +} __attribute__ (( packed )); + +/** Descriptor is owned by NIC */ +#define RDC_FL_OWNED 0x8000 + +/** Packet OK */ +#define RDC_FL_OK 0x4000 + +/** MAC control register 0 */ +#define RDC_MCR0 0x00 +#define RDC_MCR0_FD 0x8000 /**< Full duplex */ +#define RDC_MCR0_TXEN 0x1000 /**< Transmit enable */ +#define RDC_MCR0_PROMISC 0x0020 /**< Promiscuous mode */ +#define RDC_MCR0_RXEN 0x0002 /**< Receive enable */ + +/** MAC control register 1 */ +#define RDC_MCR1 0x04 +#define RDC_MCR1_RST 0x0001 /**< MAC reset */ + +/** Maximum time to wait for reset */ +#define RDC_RESET_MAX_WAIT_MS 10 + +/** MAC transmit poll command register */ +#define RDC_MTPR 0x14 +#define RDC_MTPR_TM2TX 0x0001 /**< Trigger MAC to transmit */ + +/** MAC receive buffer size register */ +#define RDC_MRBSR 0x18 + +/** MAC MDIO control register */ +#define RDC_MMDIO 0x20 +#define RDC_MMDIO_MIIWR 0x4000 /**< MDIO write */ +#define RDC_MMDIO_MIIRD 0x2000 /**< MDIO read */ +#define RDC_MMDIO_PHYAD(x) ( (x) << 8 ) /**< PHY address */ +#define RDC_MMDIO_REGAD(x) ( (x) << 0 ) /**< Register address */ + +/** Maximum time to wait for an MII read or write */ +#define RDC_MII_MAX_WAIT_US 2048 + +/** MAC MDIO read data register */ +#define RDC_MMRD 0x24 + +/** MAC MDIO write data register */ +#define RDC_MMWD 0x28 + +/** MAC transmit descriptor start address */ +#define RDC_MTDSA 0x2c + +/** MAC receive descriptor start address */ +#define RDC_MRDSA 0x34 + +/** MAC descriptor start address low half */ +#define RDC_MxDSA_LO 0x0 + +/** MAC descriptor start address low half */ +#define RDC_MxDSA_HI 0x4 + +/** MAC interrupt status register */ +#define RDC_MISR 0x3c +#define RDC_MIRQ_LINK 0x0200 /**< Link status changed */ +#define RDC_MIRQ_TX 0x0010 /**< Transmit complete */ +#define RDC_MIRQ_RX_EARLY 0x0008 /**< Receive early interrupt */ +#define RDC_MIRQ_RX_EMPTY 0x0002 /**< Receive descriptor unavailable */ +#define RDC_MIRQ_RX 0x0001 /**< Receive complete */ + +/** MAC interrupt enable register */ +#define RDC_MIER 0x40 + +/** MAC address word 0 */ +#define RDC_MID0 0x68 + +/** MAC address word 1 */ +#define RDC_MID1 0x6a + +/** MAC address word 2 */ +#define RDC_MID2 0x6c + +/** MAC PHY status change configuration register */ +#define RDC_MPSCCR 0x88 +#define RDC_MPSCCR_EN 0x8000 /**< PHY status change enable */ +#define RDC_MPSCCR_PHYAD(x) ( (x) << 8 ) /**< PHY address */ +#define RDC_MPSCCR_SLOW 0x0007 /**< Poll slowly */ + +/** MAC state machine register */ +#define RDC_MACSM 0xac +#define RDC_MACSM_RST 0x0002 /**< Reset state machine */ + +/** Time to wait after resetting MAC state machine */ +#define RDC_MACSM_RESET_DELAY_MS 10 + +/** A MAC address */ +union rdc_mac { + /** Raw bytes */ + uint8_t raw[ETH_ALEN]; + /** MIDx registers */ + uint16_t mid[ ETH_ALEN / 2 ]; +}; + +/** A descriptor ring */ +struct rdc_ring { + /** Descriptors */ + struct rdc_descriptor *desc; + /** Descriptor ring DMA mapping */ + struct dma_mapping map; + /** Producer index */ + unsigned int prod; + /** Consumer index */ + unsigned int cons; + + /** Number of descriptors */ + unsigned int count; + /** Start address register 0 */ + unsigned int reg; +}; + +/** + * Initialise descriptor ring + * + * @v ring Descriptor ring + * @v count Number of descriptors + * @v reg Start address register 0 + */ +static inline __attribute__ (( always_inline )) void +rdc_init_ring ( struct rdc_ring *ring, unsigned int count, unsigned int reg ) { + + ring->count = count; + ring->reg = reg; +} + +/** Number of transmit descriptors + * + * This is a policy decision. + */ +#define RDC_NUM_TX_DESC 16 + +/** Number of receive descriptors + * + * This is a policy decision. + */ +#define RDC_NUM_RX_DESC 8 + +/** Receive buffer length */ +#define RDC_RX_MAX_LEN ( ETH_FRAME_LEN + 4 /* VLAN */ + 4 /* CRC */ ) + +/** An RDC network card */ +struct rdc_nic { + /** Registers */ + void *regs; + /** DMA device */ + struct dma_device *dma; + /** MII interface */ + struct mii_interface mdio; + /** MII device */ + struct mii_device mii; + + /** Transmit descriptor ring */ + struct rdc_ring tx; + /** Receive descriptor ring */ + struct rdc_ring rx; + /** Receive I/O buffers */ + struct io_buffer *rx_iobuf[RDC_NUM_RX_DESC]; +}; + +#endif /* _RDC_H */ diff --git a/src/drivers/net/realtek.c b/src/drivers/net/realtek.c index 0af3416d5..a43efb68b 100644 --- a/src/drivers/net/realtek.c +++ b/src/drivers/net/realtek.c @@ -420,6 +420,16 @@ static int realtek_phy_reset ( struct realtek_nic *rtl ) { */ } + /* Some cards (e.g. RTL8211B) have a hardware errata that + * requires the MII_MMD_DATA register to be cleared before the + * link will come up. + */ + if ( ( rc = mii_write ( &rtl->mii, MII_MMD_DATA, 0 ) ) != 0 ) { + /* Ignore failures, since the register may not be + * present on all PHYs. + */ + } + /* Restart autonegotiation */ if ( ( rc = mii_restart ( &rtl->mii ) ) != 0 ) { DBGC ( rtl, "REALTEK %p could not restart MII: %s\n", diff --git a/src/hci/commands/image_archive_cmd.c b/src/hci/commands/image_archive_cmd.c new file mode 100644 index 000000000..a2212aecf --- /dev/null +++ b/src/hci/commands/image_archive_cmd.c @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2021 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <getopt.h> +#include <ipxe/command.h> +#include <ipxe/parseopt.h> +#include <usr/imgmgmt.h> +#include <usr/imgarchive.h> + +/** @file + * + * Archive image commands + * + */ + +/** "imgextract" options */ +struct imgextract_options { + /** Image name */ + char *name; + /** Keep original image */ + int keep; + /** Download timeout */ + unsigned long timeout; +}; + +/** "imgextract" option list */ +static struct option_descriptor imgextract_opts[] = { + OPTION_DESC ( "name", 'n', required_argument, + struct imgextract_options, name, parse_string ), + OPTION_DESC ( "keep", 'k', no_argument, + struct imgextract_options, keep, parse_flag ), + OPTION_DESC ( "timeout", 't', required_argument, + struct imgextract_options, timeout, parse_timeout ), +}; + +/** "imgextract" command descriptor */ +static struct command_descriptor imgextract_cmd = + COMMAND_DESC ( struct imgextract_options, imgextract_opts, 1, 1, NULL ); + +/** + * The "imgextract" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int imgextract_exec ( int argc, char **argv ) { + struct imgextract_options opts; + struct image *image; + int rc; + + /* Parse options */ + if ( ( rc = parse_options ( argc, argv, &imgextract_cmd, + &opts ) ) != 0 ) + goto err_parse; + + /* Acquire image */ + if ( ( rc = imgacquire ( argv[optind], opts.timeout, &image ) ) != 0 ) + goto err_acquire; + + /* Extract archive image */ + if ( ( rc = imgextract ( image, opts.name ) ) != 0 ) + goto err_extract; + + /* Success */ + rc = 0; + + err_extract: + /* Discard original image unless --keep was specified */ + if ( ! opts.keep ) + unregister_image ( image ); + err_acquire: + err_parse: + return rc; +} + +/** Archive image commands */ +struct command image_archive_commands[] __command = { + { + .name = "imgextract", + .exec = imgextract_exec, + }, +}; diff --git a/src/hci/commands/nvo_cmd.c b/src/hci/commands/nvo_cmd.c index ac0d60651..6ad7e7428 100644 --- a/src/hci/commands/nvo_cmd.c +++ b/src/hci/commands/nvo_cmd.c @@ -100,20 +100,40 @@ static int show_exec ( int argc, char **argv ) { } /** "set", "clear", and "read" options */ -struct set_core_options {}; +struct set_core_options { + /** Timeout */ + unsigned long timeout; +}; /** "set", "clear", and "read" option list */ -static struct option_descriptor set_core_opts[] = {}; +static union { + /* "set" takes no options */ + struct option_descriptor set[0]; + /* "clear" takes no options */ + struct option_descriptor clear[0]; + /* "read" takes --timeout option */ + struct option_descriptor read[1]; +} set_core_opts = { + .read = { + OPTION_DESC ( "timeout", 't', required_argument, + struct set_core_options, timeout, parse_timeout ), + }, +}; /** "set" command descriptor */ static struct command_descriptor set_cmd = - COMMAND_DESC ( struct set_core_options, set_core_opts, 1, MAX_ARGUMENTS, - "<setting> <value>" ); + COMMAND_DESC ( struct set_core_options, set_core_opts.set, + 1, MAX_ARGUMENTS, "<setting> <value>" ); + +/** "clear" command descriptor */ +static struct command_descriptor clear_cmd = + COMMAND_DESC ( struct set_core_options, set_core_opts.clear, + 1, 1, "<setting>" ); -/** "clear" and "read" command descriptor */ -static struct command_descriptor clear_read_cmd = - COMMAND_DESC ( struct set_core_options, set_core_opts, 1, 1, - "<setting>" ); +/** "read" command descriptor */ +static struct command_descriptor read_cmd = + COMMAND_DESC ( struct set_core_options, set_core_opts.read, + 1, 1, "<setting>" ); /** * "set", "clear", and "read" command @@ -127,6 +147,7 @@ static struct command_descriptor clear_read_cmd = static int set_core_exec ( int argc, char **argv, struct command_descriptor *cmd, int ( * get_value ) ( struct named_setting *setting, + struct set_core_options *opts, char **args, char **value ) ) { struct set_core_options opts; struct named_setting setting; @@ -143,7 +164,8 @@ static int set_core_exec ( int argc, char **argv, goto err_parse_setting; /* Parse setting value */ - if ( ( rc = get_value ( &setting, &argv[ optind + 1 ], &value ) ) != 0 ) + if ( ( rc = get_value ( &setting, &opts, &argv[ optind + 1 ], + &value ) ) != 0 ) goto err_get_value; /* Apply default type if necessary */ @@ -170,11 +192,13 @@ static int set_core_exec ( int argc, char **argv, * Get setting value for "set" command * * @v setting Named setting + * @v opts Options list * @v args Remaining arguments * @ret value Setting value * @ret rc Return status code */ static int set_value ( struct named_setting *setting __unused, + struct set_core_options *opts __unused, char **args, char **value ) { *value = concat_args ( args ); @@ -200,10 +224,12 @@ static int set_exec ( int argc, char **argv ) { * * @v setting Named setting * @v args Remaining arguments + * @v opts Options list * @ret value Setting value * @ret rc Return status code */ static int clear_value ( struct named_setting *setting __unused, + struct set_core_options *opts __unused, char **args __unused, char **value ) { *value = NULL; @@ -218,7 +244,7 @@ static int clear_value ( struct named_setting *setting __unused, * @ret rc Return status code */ static int clear_exec ( int argc, char **argv ) { - return set_core_exec ( argc, argv, &clear_read_cmd, clear_value ); + return set_core_exec ( argc, argv, &clear_cmd, clear_value ); } /** @@ -226,11 +252,13 @@ static int clear_exec ( int argc, char **argv ) { * * @v setting Named setting * @v args Remaining arguments + * @v opts Options list * @ret value Setting value * @ret rc Return status code */ -static int read_value ( struct named_setting *setting, char **args __unused, - char **value ) { +static int read_value ( struct named_setting *setting, + struct set_core_options *opts, + char **args __unused, char **value ) { char *existing; int rc; @@ -241,7 +269,8 @@ static int read_value ( struct named_setting *setting, char **args __unused, NULL, &setting->setting, &existing ); /* Read new value */ - if ( ( rc = readline_history ( NULL, existing, NULL, value ) ) != 0 ) + if ( ( rc = readline_history ( NULL, existing, NULL, opts->timeout, + value ) ) != 0 ) goto err_readline; err_readline: @@ -257,7 +286,7 @@ static int read_value ( struct named_setting *setting, char **args __unused, * @ret rc Return status code */ static int read_exec ( int argc, char **argv ) { - return set_core_exec ( argc, argv, &clear_read_cmd, read_value ); + return set_core_exec ( argc, argv, &read_cmd, read_value ); } /** "inc" options */ diff --git a/src/hci/readline.c b/src/hci/readline.c index 83a2e0b90..852c4503a 100644 --- a/src/hci/readline.c +++ b/src/hci/readline.c @@ -248,6 +248,7 @@ void history_free ( struct readline_history *history ) { * @v prompt Prompt string * @v prefill Prefill string, or NULL for no prefill * @v history History buffer, or NULL for no history + * @v timeout Timeout period, in ticks (0=indefinite) * @ret line Line read from console (excluding terminating newline) * @ret rc Return status code * @@ -255,7 +256,8 @@ void history_free ( struct readline_history *history ) { * eventually call free() to release the storage. */ int readline_history ( const char *prompt, const char *prefill, - struct readline_history *history, char **line ) { + struct readline_history *history, unsigned long timeout, + char **line ) { char buf[READLINE_MAX]; struct edit_string string; int key; @@ -285,8 +287,17 @@ int readline_history ( const char *prompt, const char *prefill, } while ( 1 ) { + + /* Get keypress */ + key = getkey ( timeout ); + if ( key < 0 ) { + rc = -ETIMEDOUT; + goto done; + } + timeout = 0; + /* Handle keypress */ - key = edit_string ( &string, getkey ( 0 ) ); + key = edit_string ( &string, key ); sync_console ( &string ); move_by = 0; switch ( key ) { @@ -342,6 +353,6 @@ int readline_history ( const char *prompt, const char *prefill, char * readline ( const char *prompt ) { char *line; - readline_history ( prompt, NULL, NULL, &line ); + readline_history ( prompt, NULL, NULL, 0, &line ); return line; } diff --git a/src/hci/shell.c b/src/hci/shell.c index 276eb3527..8ecf73a6f 100644 --- a/src/hci/shell.c +++ b/src/hci/shell.c @@ -91,7 +91,7 @@ int shell ( void ) { /* Read and execute commands */ do { - readline_history ( shell_prompt, NULL, &history, &line ); + readline_history ( shell_prompt, NULL, &history, 0, &line ); if ( line ) { rc = system ( line ); free ( line ); diff --git a/src/image/gzip.c b/src/image/gzip.c new file mode 100644 index 000000000..98376e113 --- /dev/null +++ b/src/image/gzip.c @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2021 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdlib.h> +#include <errno.h> +#include <assert.h> +#include <ipxe/deflate.h> +#include <ipxe/uaccess.h> +#include <ipxe/image.h> +#include <ipxe/zlib.h> +#include <ipxe/gzip.h> + +/** @file + * + * gzip compressed images + * + */ + +/** + * Extract gzip image + * + * @v image Image + * @v extracted Extracted image + * @ret rc Return status code + */ +static int gzip_extract ( struct image *image, struct image *extracted ) { + struct gzip_header header; + struct gzip_extra_header extra; + struct gzip_crc_header crc; + struct gzip_footer footer; + struct deflate_chunk in; + unsigned int strings; + size_t offset; + size_t len; + off_t nul; + int rc; + + /* Sanity check */ + assert ( image->len >= ( sizeof ( header ) + sizeof ( footer ) ) ); + + /* Extract footer */ + len = ( image->len - sizeof ( footer ) ); + copy_from_user ( &footer, image->data, len, sizeof ( footer ) ); + + /* Extract fixed header */ + copy_from_user ( &header, image->data, 0, sizeof ( header ) ); + offset = sizeof ( header ); + assert ( offset <= ( image->len - sizeof ( footer ) ) ); + + /* Skip extra header, if present */ + if ( header.flags & GZIP_FL_EXTRA ) { + copy_from_user ( &extra, image->data, offset, + sizeof ( extra ) ); + offset += sizeof ( extra ); + offset += le16_to_cpu ( extra.len ); + if ( offset > len ) { + DBGC ( image, "GZIP %p overlength extra header\n", + image ); + return -EINVAL; + } + } + assert ( offset <= ( image->len - sizeof ( footer ) ) ); + + /* Skip name and/or comment, if present */ + strings = 0; + if ( header.flags & GZIP_FL_NAME ) + strings++; + if ( header.flags & GZIP_FL_COMMENT ) + strings++; + while ( strings-- ) { + nul = memchr_user ( image->data, offset, 0, ( len - offset ) ); + if ( nul < 0 ) { + DBGC ( image, "GZIP %p overlength name/comment\n", + image ); + return -EINVAL; + } + offset = ( nul + 1 /* NUL */ ); + } + assert ( offset <= ( image->len - sizeof ( footer ) ) ); + + /* Skip CRC, if present */ + if ( header.flags & GZIP_FL_HCRC ) { + offset += sizeof ( crc ); + if ( offset > len ) { + DBGC ( image, "GZIP %p overlength CRC header\n", + image ); + return -EINVAL; + } + } + + /* Initialise input chunk */ + deflate_chunk_init ( &in, userptr_add ( image->data, offset ), 0, len ); + + /* Presize extracted image */ + if ( ( rc = image_set_len ( extracted, + le32_to_cpu ( footer.len ) ) ) != 0 ) { + DBGC ( image, "GZIP %p could not presize: %s\n", + image, strerror ( rc ) ); + return rc; + } + + /* Decompress image (expanding if necessary) */ + if ( ( rc = zlib_deflate ( DEFLATE_RAW, &in, extracted ) ) != 0 ) { + DBGC ( image, "GZIP %p could not decompress: %s\n", + image, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Probe gzip image + * + * @v image gzip image + * @ret rc Return status code + */ +static int gzip_probe ( struct image *image ) { + struct gzip_header header; + struct gzip_footer footer; + + /* Sanity check */ + if ( image->len < ( sizeof ( header ) + sizeof ( footer ) ) ) { + DBGC ( image, "GZIP %p image too short\n", image ); + return -ENOEXEC; + } + + /* Check magic header */ + copy_from_user ( &header.magic, image->data, 0, + sizeof ( header.magic ) ); + if ( header.magic != cpu_to_be16 ( GZIP_MAGIC ) ) { + DBGC ( image, "GZIP %p invalid magic\n", image ); + return -ENOEXEC; + } + + return 0; +} + +/** gzip image type */ +struct image_type gzip_image_type __image_type ( PROBE_NORMAL ) = { + .name = "gzip", + .probe = gzip_probe, + .extract = gzip_extract, + .exec = image_extract_exec, +}; diff --git a/src/image/zlib.c b/src/image/zlib.c new file mode 100644 index 000000000..a42c47e1b --- /dev/null +++ b/src/image/zlib.c @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2021 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdlib.h> +#include <errno.h> +#include <assert.h> +#include <ipxe/deflate.h> +#include <ipxe/uaccess.h> +#include <ipxe/image.h> +#include <ipxe/zlib.h> + +/** @file + * + * zlib compressed images + * + */ + +/** + * Extract compressed data to image + * + * @v format Compression format code + * @v in Compressed input chunk + * @v extracted Extracted image + * @ret rc Return status code + */ +int zlib_deflate ( enum deflate_format format, struct deflate_chunk *in, + struct image *extracted ) { + struct deflate *deflate; + struct deflate_chunk out; + int rc; + + /* Allocate and initialise decompressor */ + deflate = zalloc ( sizeof ( *deflate ) ); + if ( ! deflate ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Decompress data, (re)allocating if necessary */ + while ( 1 ) { + + /* (Re)initialise decompressor */ + deflate_init ( deflate, format ); + + /* (Re)initialise input chunk */ + in->offset = 0; + + /* Initialise output chunk */ + deflate_chunk_init ( &out, extracted->data, 0, extracted->len ); + + /* Decompress data */ + if ( ( rc = deflate_inflate ( deflate, in, &out ) ) != 0 ) { + DBGC ( extracted, "ZLIB %p could not decompress: %s\n", + extracted, strerror ( rc ) ); + goto err_inflate; + } + + /* Check that decompression is valid */ + if ( ! deflate_finished ( deflate ) ) { + DBGC ( extracted, "ZLIB %p decompression incomplete\n", + extracted ); + rc = -EINVAL; + goto err_unfinished; + } + + /* Finish if output image size was correct */ + if ( out.offset == extracted->len ) + break; + + /* Otherwise, resize output image and retry */ + if ( ( rc = image_set_len ( extracted, out.offset ) ) != 0 ) { + DBGC ( extracted, "ZLIB %p could not resize: %s\n", + extracted, strerror ( rc ) ); + goto err_set_size; + } + } + + /* Success */ + rc = 0; + + err_set_size: + err_unfinished: + err_inflate: + free ( deflate ); + err_alloc: + return rc; +} + +/** + * Extract zlib image + * + * @v image Image + * @v extracted Extracted image + * @ret rc Return status code + */ +static int zlib_extract ( struct image *image, struct image *extracted ) { + struct deflate_chunk in; + int rc; + + /* Initialise input chunk */ + deflate_chunk_init ( &in, image->data, 0, image->len ); + + /* Decompress image */ + if ( ( rc = zlib_deflate ( DEFLATE_ZLIB, &in, extracted ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Probe zlib image + * + * @v image zlib image + * @ret rc Return status code + */ +static int zlib_probe ( struct image *image ) { + union zlib_magic magic; + + /* Sanity check */ + if ( image->len < sizeof ( magic ) ) { + DBGC ( image, "ZLIB %p image too short\n", image ); + return -ENOEXEC; + } + + /* Check magic header */ + copy_from_user ( &magic, image->data, 0, sizeof ( magic ) ); + if ( ! zlib_magic_is_valid ( &magic ) ) { + DBGC ( image, "ZLIB %p invalid magic data\n", image ); + return -ENOEXEC; + } + + return 0; +} + +/** zlib image type */ +struct image_type zlib_image_type __image_type ( PROBE_NORMAL ) = { + .name = "zlib", + .probe = zlib_probe, + .extract = zlib_extract, + .exec = image_extract_exec, +}; diff --git a/src/include/endian.h b/src/include/endian.h index 79c3163ee..bdae9de45 100644 --- a/src/include/endian.h +++ b/src/include/endian.h @@ -8,14 +8,18 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * Little-endian systems should define BYTE_ORDER as LITTLE_ENDIAN. * This constant is intended to be used only at compile time. */ +#ifndef __LITTLE_ENDIAN #define __LITTLE_ENDIAN 0x44332211UL +#endif /** Constant representing big-endian byte order * * Big-endian systems should define BYTE_ORDER as BIG_ENDIAN. * This constant is intended to be used only at compile time. */ +#ifndef __BIG_ENDIAN #define __BIG_ENDIAN 0x11223344UL +#endif #include "bits/endian.h" diff --git a/src/include/ipxe/cpio.h b/src/include/ipxe/cpio.h index 0637c531d..9c5e22d5a 100644 --- a/src/include/ipxe/cpio.h +++ b/src/include/ipxe/cpio.h @@ -9,6 +9,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +#include <ipxe/image.h> + /** A CPIO archive header * * All field are hexadecimal ASCII numbers padded with '0' on the @@ -48,6 +50,25 @@ struct cpio_header { /** CPIO magic */ #define CPIO_MAGIC "070701" +/** CPIO header length alignment */ +#define CPIO_ALIGN 4 + +/** Alignment for CPIO archives within an initrd */ +#define INITRD_ALIGN 4096 + +/** + * Get CPIO image name + * + * @v image Image + * @ret name Image name (not NUL terminated) + */ +static inline __attribute__ (( always_inline )) const char * +cpio_name ( struct image *image ) { + return image->cmdline; +} + extern void cpio_set_field ( char *field, unsigned long value ); +extern size_t cpio_name_len ( struct image *image ); +extern size_t cpio_header ( struct image *image, struct cpio_header *cpio ); #endif /* _IPXE_CPIO_H */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index e3bf9f565..cf5757874 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -213,6 +213,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_usbblk ( ERRFILE_DRIVER | 0x00ce0000 ) #define ERRFILE_iphone ( ERRFILE_DRIVER | 0x00cf0000 ) #define ERRFILE_slirp ( ERRFILE_DRIVER | 0x00d00000 ) +#define ERRFILE_rdc ( ERRFILE_DRIVER | 0x00d10000 ) #define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 ) #define ERRFILE_arp ( ERRFILE_NET | 0x00010000 ) @@ -301,6 +302,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_png ( ERRFILE_IMAGE | 0x00070000 ) #define ERRFILE_der ( ERRFILE_IMAGE | 0x00080000 ) #define ERRFILE_pem ( ERRFILE_IMAGE | 0x00090000 ) +#define ERRFILE_archive ( ERRFILE_IMAGE | 0x000a0000 ) +#define ERRFILE_zlib ( ERRFILE_IMAGE | 0x000b0000 ) +#define ERRFILE_gzip ( ERRFILE_IMAGE | 0x000c0000 ) #define ERRFILE_asn1 ( ERRFILE_OTHER | 0x00000000 ) #define ERRFILE_chap ( ERRFILE_OTHER | 0x00010000 ) diff --git a/src/include/ipxe/gzip.h b/src/include/ipxe/gzip.h new file mode 100644 index 000000000..c8cf64147 --- /dev/null +++ b/src/include/ipxe/gzip.h @@ -0,0 +1,71 @@ +#ifndef _IPXE_GZIP_H +#define _IPXE_GZIP_H + +/** @file + * + * gzip compressed images + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> +#include <ipxe/image.h> + +/** gzip header */ +struct gzip_header { + /** Magic ID */ + uint16_t magic; + /** Compression method */ + uint8_t method; + /** Flags */ + uint8_t flags; + /** Modification time */ + uint32_t mtime; + /** Extra flags */ + uint8_t extra; + /** Operating system */ + uint8_t os; +} __attribute__ (( packed )); + +/** Magic ID */ +#define GZIP_MAGIC 0x1f8b + +/** Compression method */ +#define GZIP_METHOD_DEFLATE 0x08 + +/** CRC header is present */ +#define GZIP_FL_HCRC 0x02 + +/** Extra header is present */ +#define GZIP_FL_EXTRA 0x04 + +/** File name is present */ +#define GZIP_FL_NAME 0x08 + +/** File comment is present */ +#define GZIP_FL_COMMENT 0x10 + +/** gzip extra header */ +struct gzip_extra_header { + /** Extra header length (excluding this field) */ + uint16_t len; +} __attribute__ (( packed )); + +/** gzip CRC header */ +struct gzip_crc_header { + /** CRC-16 */ + uint16_t crc; +} __attribute__ (( packed )); + +/** gzip footer */ +struct gzip_footer { + /** CRC-32 */ + uint32_t crc; + /** Uncompressed size (modulo 2^32) */ + uint32_t len; +} __attribute__ (( packed )); + +extern struct image_type gzip_image_type __image_type ( PROBE_NORMAL ); + +#endif /* _IPXE_GZIP_H */ diff --git a/src/include/ipxe/image.h b/src/include/ipxe/image.h index 4fd270081..0a5a26034 100644 --- a/src/include/ipxe/image.h +++ b/src/include/ipxe/image.h @@ -113,6 +113,14 @@ struct image_type { */ int ( * asn1 ) ( struct image *image, size_t offset, struct asn1_cursor **cursor ); + /** + * Extract archive image + * + * @v image Image + * @v extracted Extracted image + * @ret rc Return status code + */ + int ( * extract ) ( struct image *image, struct image *extracted ); }; /** @@ -175,6 +183,7 @@ extern struct image * alloc_image ( struct uri *uri ); extern int image_set_uri ( struct image *image, struct uri *uri ); extern int image_set_name ( struct image *image, const char *name ); extern int image_set_cmdline ( struct image *image, const char *cmdline ); +extern int image_set_len ( struct image *image, size_t len ); extern int image_set_data ( struct image *image, userptr_t data, size_t len ); extern int register_image ( struct image *image ); extern void unregister_image ( struct image *image ); @@ -189,6 +198,9 @@ extern struct image * image_memory ( const char *name, userptr_t data, extern int image_pixbuf ( struct image *image, struct pixel_buffer **pixbuf ); extern int image_asn1 ( struct image *image, size_t offset, struct asn1_cursor **cursor ); +extern int image_extract ( struct image *image, const char *name, + struct image **extracted ); +extern int image_extract_exec ( struct image *image ); /** * Increment reference count on an image diff --git a/src/include/ipxe/rndis.h b/src/include/ipxe/rndis.h index bcb6d8e6a..e8ece1e85 100644 --- a/src/include/ipxe/rndis.h +++ b/src/include/ipxe/rndis.h @@ -84,7 +84,7 @@ struct rndis_initialise_completion { /** Packet alignment factor */ uint32_t align; /** Reserved */ - uint32_t reserved; + uint32_t reserved[2]; } __attribute__ (( packed )); /** RNDIS halt message */ @@ -237,7 +237,7 @@ struct rndis_packet_message { /** Per-packet information record */ struct rndis_packet_field ppi; /** Reserved */ - uint32_t reserved; + uint32_t reserved[2]; } __attribute__ (( packed )); /** RNDIS packet record */ diff --git a/src/include/ipxe/zlib.h b/src/include/ipxe/zlib.h new file mode 100644 index 000000000..29016c38e --- /dev/null +++ b/src/include/ipxe/zlib.h @@ -0,0 +1,43 @@ +#ifndef _IPXE_ZLIB_H +#define _IPXE_ZLIB_H + +/** @file + * + * zlib compressed images + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> +#include <byteswap.h> +#include <ipxe/image.h> +#include <ipxe/deflate.h> + +/** zlib magic header */ +union zlib_magic { + /** Compression method and flags */ + uint8_t cmf; + /** Check value */ + uint16_t check; +} __attribute__ (( packed )); + +/** + * Check that zlib magic header is valid + * + * @v magic Magic header + * @ret is_valid Magic header is valid + */ +static inline int zlib_magic_is_valid ( union zlib_magic *magic ) { + + /* Check magic value as per RFC 6713 */ + return ( ( ( magic->cmf & 0x8f ) == 0x08 ) && + ( ( be16_to_cpu ( magic->check ) % 31 ) == 0 ) ); +} + +extern int zlib_deflate ( enum deflate_format format, struct deflate_chunk *in, + struct image *extracted ); + +extern struct image_type zlib_image_type __image_type ( PROBE_NORMAL ); + +#endif /* _IPXE_ZLIB_H */ diff --git a/src/include/mii.h b/src/include/mii.h index e2afef854..515ba224d 100644 --- a/src/include/mii.h +++ b/src/include/mii.h @@ -23,6 +23,8 @@ FILE_LICENCE ( GPL2_ONLY ); #define MII_EXPANSION 0x06 /* Expansion register */ #define MII_CTRL1000 0x09 /* 1000BASE-T control */ #define MII_STAT1000 0x0a /* 1000BASE-T status */ +#define MII_MMD_CTRL 0x0d /* MMD Access Control Register */ +#define MII_MMD_DATA 0x0e /* MMD Access Data Register */ #define MII_ESTATUS 0x0f /* Extended Status */ #define MII_DCOUNTER 0x12 /* Disconnect counter */ #define MII_FCSCOUNTER 0x13 /* False carrier counter */ diff --git a/src/include/readline/readline.h b/src/include/readline/readline.h index afafbbdf5..3caf28b47 100644 --- a/src/include/readline/readline.h +++ b/src/include/readline/readline.h @@ -51,7 +51,8 @@ struct readline_history { extern void history_free ( struct readline_history *history ); extern int readline_history ( const char *prompt, const char *prefill, - struct readline_history *history, char **line ); + struct readline_history *history, + unsigned long timeout, char **line ); extern char * __malloc readline ( const char *prompt ); #endif /* _READLINE_H */ diff --git a/src/include/stdio.h b/src/include/stdio.h index a618482ce..ac17da83d 100644 --- a/src/include/stdio.h +++ b/src/include/stdio.h @@ -6,7 +6,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdarg.h> -extern void putchar ( int character ); +extern int putchar ( int character ); extern int getchar ( void ); diff --git a/src/include/strings.h b/src/include/strings.h index fab26dc28..d7e9d6971 100644 --- a/src/include/strings.h +++ b/src/include/strings.h @@ -189,5 +189,7 @@ bzero ( void *dest, size_t len ) { } int __pure strcasecmp ( const char *first, const char *second ) __nonnull; +int __pure strncasecmp ( const char *first, const char *second, + size_t max ) __nonnull; #endif /* _STRINGS_H */ diff --git a/src/include/usr/imgarchive.h b/src/include/usr/imgarchive.h new file mode 100644 index 000000000..bf0c18f55 --- /dev/null +++ b/src/include/usr/imgarchive.h @@ -0,0 +1,16 @@ +#ifndef _USR_IMGARCHIVE_H +#define _USR_IMGARCHIVE_H + +/** @file + * + * Archive image management + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <ipxe/image.h> + +extern int imgextract ( struct image *image, const char *name ); + +#endif /* _USR_IMGARCHIVE_H */ diff --git a/src/interface/efi/efi_file.c b/src/interface/efi/efi_file.c index 52de0987c..fc64b369c 100644 --- a/src/interface/efi/efi_file.c +++ b/src/interface/efi/efi_file.c @@ -38,6 +38,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <errno.h> #include <wchar.h> #include <ipxe/image.h> +#include <ipxe/cpio.h> #include <ipxe/efi/efi.h> #include <ipxe/efi/Protocol/SimpleFileSystem.h> #include <ipxe/efi/Protocol/BlockIo.h> @@ -50,17 +51,54 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** EFI media ID */ #define EFI_MEDIA_ID_MAGIC 0x69505845 -/** An image exposed as an EFI file */ +/** An EFI virtual file reader */ +struct efi_file_reader { + /** EFI file */ + struct efi_file *file; + /** Position within virtual file */ + size_t pos; + /** Output data buffer */ + void *data; + /** Length of output data buffer */ + size_t len; +}; + +/** An EFI file */ struct efi_file { + /** Reference count */ + struct refcnt refcnt; /** EFI file protocol */ EFI_FILE_PROTOCOL file; - /** Image */ + /** Image (if any) */ struct image *image; + /** Filename */ + const char *name; /** Current file position */ size_t pos; + /** + * Read from file + * + * @v reader File reader + * @ret len Length read + */ + size_t ( * read ) ( struct efi_file_reader *reader ); }; static struct efi_file efi_file_root; +static struct efi_file efi_file_initrd; + +/** + * Free EFI file + * + * @v refcnt Reference count + */ +static void efi_file_free ( struct refcnt *refcnt ) { + struct efi_file *file = + container_of ( refcnt, struct efi_file, refcnt ); + + image_put ( file->image ); + free ( file ); +} /** * Get EFI file name (for debugging) @@ -70,28 +108,201 @@ static struct efi_file efi_file_root; */ static const char * efi_file_name ( struct efi_file *file ) { - return ( file->image ? file->image->name : "<root>" ); + return ( file == &efi_file_root ? "<root>" : file->name ); } /** * Find EFI file image * - * @v wname Filename + * @v name Filename * @ret image Image, or NULL */ -static struct image * efi_file_find ( const CHAR16 *wname ) { - char name[ wcslen ( wname ) + 1 /* NUL */ ]; +static struct image * efi_file_find ( const char *name ) { struct image *image; /* Find image */ - snprintf ( name, sizeof ( name ), "%ls", wname ); list_for_each_entry ( image, &images, list ) { if ( strcasecmp ( image->name, name ) == 0 ) return image; } return NULL; +} + +/** + * Get length of EFI file + * + * @v file EFI file + * @ret len Length of file + */ +static size_t efi_file_len ( struct efi_file *file ) { + struct efi_file_reader reader; + + /* If this is the root directory, then treat as length zero */ + if ( ! file->read ) + return 0; + + /* Initialise reader */ + reader.file = file; + reader.pos = 0; + reader.data = NULL; + reader.len = 0; + + /* Perform dummy read to determine file length */ + file->read ( &reader ); + + return reader.pos; +} + +/** + * Read chunk of EFI file + * + * @v reader EFI file reader + * @v data Input data, or UNULL to zero-fill + * @v len Length of input data + * @ret len Length of output data + */ +static size_t efi_file_read_chunk ( struct efi_file_reader *reader, + userptr_t data, size_t len ) { + struct efi_file *file = reader->file; + size_t offset; + + /* Calculate offset into input data */ + offset = ( file->pos - reader->pos ); + + /* Consume input data range */ + reader->pos += len; + + /* Calculate output length */ + if ( offset < len ) { + len -= offset; + } else { + len = 0; + } + if ( len > reader->len ) + len = reader->len; + + /* Copy or zero output data */ + if ( data ) { + copy_from_user ( reader->data, data, offset, len ); + } else { + memset ( reader->data, 0, len ); + } + + /* Consume output buffer */ + file->pos += len; + reader->data += len; + reader->len -= len; + + return len; +} + +/** + * Read from image-backed file + * + * @v reader EFI file reader + * @ret len Length read + */ +static size_t efi_file_read_image ( struct efi_file_reader *reader ) { + struct efi_file *file = reader->file; + struct image *image = file->image; + + /* Read from file */ + return efi_file_read_chunk ( reader, image->data, image->len ); +} + +/** + * Read from magic initrd file + * + * @v reader EFI file reader + * @ret len Length read + */ +static size_t efi_file_read_initrd ( struct efi_file_reader *reader ) { + struct efi_file *file = reader->file; + struct cpio_header cpio; + struct image *image; + const char *name; + size_t pad_len; + size_t cpio_len; + size_t name_len; + size_t len; + + /* Read from file */ + len = 0; + for_each_image ( image ) { + + /* Ignore currently executing image */ + if ( image == current_image ) + continue; + + /* Pad to alignment boundary */ + pad_len = ( ( -reader->pos ) & ( INITRD_ALIGN - 1 ) ); + if ( pad_len ) { + DBGC ( file, "EFIFILE %s [%#08zx,%#08zx) pad\n", + efi_file_name ( file ), reader->pos, + ( reader->pos + pad_len ) ); + } + len += efi_file_read_chunk ( reader, UNULL, pad_len ); + + /* Read CPIO header, if applicable */ + cpio_len = cpio_header ( image, &cpio ); + if ( cpio_len ) { + name = cpio_name ( image ); + name_len = cpio_name_len ( image ); + pad_len = ( cpio_len - sizeof ( cpio ) - name_len ); + DBGC ( file, "EFIFILE %s [%#08zx,%#08zx) %s header\n", + efi_file_name ( file ), reader->pos, + ( reader->pos + cpio_len ), image->name ); + len += efi_file_read_chunk ( reader, + virt_to_user ( &cpio ), + sizeof ( cpio ) ); + len += efi_file_read_chunk ( reader, + virt_to_user ( name ), + name_len ); + len += efi_file_read_chunk ( reader, UNULL, pad_len ); + } + + /* Read file data */ + DBGC ( file, "EFIFILE %s [%#08zx,%#08zx) %s\n", + efi_file_name ( file ), reader->pos, + ( reader->pos + image->len ), image->name ); + len += efi_file_read_chunk ( reader, image->data, image->len ); + } + + return len; +} + +/** + * Open fixed file + * + * @v file EFI file + * @v new New EFI file + * @ret efirc EFI status code + */ +static EFI_STATUS efi_file_open_fixed ( struct efi_file *file, + EFI_FILE_PROTOCOL **new ) { + + /* Increment reference count */ + ref_get ( &file->refcnt ); + + /* Return opened file */ + *new = &file->file; + + DBGC ( file, "EFIFILE %s opened\n", efi_file_name ( file ) ); + return 0; +} + +/** + * Associate file with image + * + * @v file EFI file + * @v image Image + */ +static void efi_file_image ( struct efi_file *file, struct image *image ) { + file->image = image; + file->name = image->name; + file->read = efi_file_read_image; } /** @@ -106,50 +317,60 @@ static struct image * efi_file_find ( const CHAR16 *wname ) { */ static EFI_STATUS EFIAPI efi_file_open ( EFI_FILE_PROTOCOL *this, EFI_FILE_PROTOCOL **new, - CHAR16 *wname, UINT64 mode __unused, - UINT64 attributes __unused ) { + CHAR16 *wname, UINT64 mode, UINT64 attributes __unused ) { struct efi_file *file = container_of ( this, struct efi_file, file ); + char buf[ wcslen ( wname ) + 1 /* NUL */ ]; struct efi_file *new_file; struct image *image; + char *name; + + /* Convert name to ASCII */ + snprintf ( buf, sizeof ( buf ), "%ls", wname ); + name = buf; /* Initial '\' indicates opening from the root directory */ - while ( *wname == L'\\' ) { + while ( *name == '\\' ) { file = &efi_file_root; - wname++; + name++; } /* Allow root directory itself to be opened */ - if ( ( wname[0] == L'\0' ) || ( wname[0] == L'.' ) ) { - *new = &efi_file_root.file; - return 0; - } + if ( ( name[0] == '\0' ) || ( name[0] == '.' ) ) + return efi_file_open_fixed ( &efi_file_root, new ); /* Fail unless opening from the root */ - if ( file->image ) { + if ( file != &efi_file_root ) { DBGC ( file, "EFIFILE %s is not a directory\n", efi_file_name ( file ) ); return EFI_NOT_FOUND; } - /* Identify image */ - image = efi_file_find ( wname ); - if ( ! image ) { - DBGC ( file, "EFIFILE \"%ls\" does not exist\n", wname ); - return EFI_NOT_FOUND; - } - /* Fail unless opening read-only */ if ( mode != EFI_FILE_MODE_READ ) { DBGC ( file, "EFIFILE %s cannot be opened in mode %#08llx\n", - image->name, mode ); + name, mode ); return EFI_WRITE_PROTECTED; } + /* Allow magic initrd to be opened */ + if ( strcasecmp ( name, efi_file_initrd.name ) == 0 ) + return efi_file_open_fixed ( &efi_file_initrd, new ); + + /* Identify image */ + image = efi_file_find ( name ); + if ( ! image ) { + DBGC ( file, "EFIFILE %s does not exist\n", name ); + return EFI_NOT_FOUND; + } + /* Allocate and initialise file */ new_file = zalloc ( sizeof ( *new_file ) ); + if ( ! new_file ) + return EFI_OUT_OF_RESOURCES; + ref_init ( &file->refcnt, efi_file_free ); memcpy ( &new_file->file, &efi_file_root.file, sizeof ( new_file->file ) ); - new_file->image = image_get ( image ); + efi_file_image ( new_file, image_get ( image ) ); *new = &new_file->file; DBGC ( new_file, "EFIFILE %s opened\n", efi_file_name ( new_file ) ); @@ -165,14 +386,9 @@ efi_file_open ( EFI_FILE_PROTOCOL *this, EFI_FILE_PROTOCOL **new, static EFI_STATUS EFIAPI efi_file_close ( EFI_FILE_PROTOCOL *this ) { struct efi_file *file = container_of ( this, struct efi_file, file ); - /* Do nothing if this is the root */ - if ( ! file->image ) - return 0; - /* Close file */ DBGC ( file, "EFIFILE %s closed\n", efi_file_name ( file ) ); - image_put ( file->image ); - free ( file ); + ref_put ( &file->refcnt ); return 0; } @@ -229,30 +445,29 @@ static EFI_STATUS efi_file_varlen ( UINT64 *base, size_t base_len, /** * Return file information structure * - * @v image Image, or NULL for the root directory + * @v file EFI file * @v len Length of data buffer * @v data Data buffer * @ret efirc EFI status code */ -static EFI_STATUS efi_file_info ( struct image *image, UINTN *len, +static EFI_STATUS efi_file_info ( struct efi_file *file, UINTN *len, VOID *data ) { EFI_FILE_INFO info; - const char *name; + size_t file_len; + + /* Get file length */ + file_len = efi_file_len ( file ); /* Populate file information */ memset ( &info, 0, sizeof ( info ) ); - if ( image ) { - info.FileSize = image->len; - info.PhysicalSize = image->len; - info.Attribute = EFI_FILE_READ_ONLY; - name = image->name; - } else { - info.Attribute = ( EFI_FILE_READ_ONLY | EFI_FILE_DIRECTORY ); - name = ""; - } - - return efi_file_varlen ( &info.Size, SIZE_OF_EFI_FILE_INFO, name, - len, data ); + info.FileSize = file_len; + info.PhysicalSize = file_len; + info.Attribute = EFI_FILE_READ_ONLY; + if ( file == &efi_file_root ) + info.Attribute |= EFI_FILE_DIRECTORY; + + return efi_file_varlen ( &info.Size, SIZE_OF_EFI_FILE_INFO, + file->name, len, data ); } /** @@ -266,14 +481,16 @@ static EFI_STATUS efi_file_info ( struct image *image, UINTN *len, static EFI_STATUS efi_file_read_dir ( struct efi_file *file, UINTN *len, VOID *data ) { EFI_STATUS efirc; + struct efi_file entry; struct image *image; unsigned int index; - /* Construct directory entry at current position */ + /* Construct directory entries for image-backed files */ index = file->pos; for_each_image ( image ) { if ( index-- == 0 ) { - efirc = efi_file_info ( image, len, data ); + efi_file_image ( &entry, image ); + efirc = efi_file_info ( &entry, len, data ); if ( efirc == 0 ) file->pos++; return efirc; @@ -296,21 +513,25 @@ static EFI_STATUS efi_file_read_dir ( struct efi_file *file, UINTN *len, static EFI_STATUS EFIAPI efi_file_read ( EFI_FILE_PROTOCOL *this, UINTN *len, VOID *data ) { struct efi_file *file = container_of ( this, struct efi_file, file ); - size_t remaining; + struct efi_file_reader reader; + size_t pos = file->pos; /* If this is the root directory, then construct a directory entry */ - if ( ! file->image ) + if ( ! file->read ) return efi_file_read_dir ( file, len, data ); + /* Initialise reader */ + reader.file = file; + reader.pos = 0; + reader.data = data; + reader.len = *len; + /* Read from the file */ - remaining = ( file->image->len - file->pos ); - if ( *len > remaining ) - *len = remaining; DBGC ( file, "EFIFILE %s read [%#08zx,%#08zx)\n", - efi_file_name ( file ), file->pos, - ( ( size_t ) ( file->pos + *len ) ) ); - copy_from_user ( data, file->image->data, file->pos, *len ); - file->pos += *len; + efi_file_name ( file ), pos, file->pos ); + *len = file->read ( &reader ); + assert ( ( pos + *len ) == file->pos ); + return 0; } @@ -342,24 +563,21 @@ static EFI_STATUS EFIAPI efi_file_write ( EFI_FILE_PROTOCOL *this, static EFI_STATUS EFIAPI efi_file_set_position ( EFI_FILE_PROTOCOL *this, UINT64 position ) { struct efi_file *file = container_of ( this, struct efi_file, file ); + size_t len; - /* If this is the root directory, reset to the start */ - if ( ! file->image ) { - DBGC ( file, "EFIFILE root directory rewound\n" ); - file->pos = 0; - return 0; - } + /* Get file length */ + len = efi_file_len ( file ); /* Check for the magic end-of-file value */ if ( position == 0xffffffffffffffffULL ) - position = file->image->len; + position = len; /* Fail if we attempt to seek past the end of the file (since * we do not support writes). */ - if ( position > file->image->len ) { + if ( position > len ) { DBGC ( file, "EFIFILE %s cannot seek to %#08llx of %#08zx\n", - efi_file_name ( file ), position, file->image->len ); + efi_file_name ( file ), position, len ); return EFI_UNSUPPORTED; } @@ -408,7 +626,7 @@ static EFI_STATUS EFIAPI efi_file_get_info ( EFI_FILE_PROTOCOL *this, /* Get file information */ DBGC ( file, "EFIFILE %s get file information\n", efi_file_name ( file ) ); - return efi_file_info ( file->image, len, data ); + return efi_file_info ( file, len, data ); } else if ( memcmp ( type, &efi_file_system_info_id, sizeof ( *type ) ) == 0 ) { @@ -468,6 +686,27 @@ static EFI_STATUS EFIAPI efi_file_flush ( EFI_FILE_PROTOCOL *this ) { /** Root directory */ static struct efi_file efi_file_root = { + .refcnt = REF_INIT ( ref_no_free ), + .file = { + .Revision = EFI_FILE_PROTOCOL_REVISION, + .Open = efi_file_open, + .Close = efi_file_close, + .Delete = efi_file_delete, + .Read = efi_file_read, + .Write = efi_file_write, + .GetPosition = efi_file_get_position, + .SetPosition = efi_file_set_position, + .GetInfo = efi_file_get_info, + .SetInfo = efi_file_set_info, + .Flush = efi_file_flush, + }, + .image = NULL, + .name = "", +}; + +/** Magic initrd file */ +static struct efi_file efi_file_initrd = { + .refcnt = REF_INIT ( ref_no_free ), .file = { .Revision = EFI_FILE_PROTOCOL_REVISION, .Open = efi_file_open, @@ -482,6 +721,8 @@ static struct efi_file efi_file_root = { .Flush = efi_file_flush, }, .image = NULL, + .name = "initrd.magic", + .read = efi_file_read_initrd, }; /** @@ -496,8 +737,7 @@ efi_file_open_volume ( EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *filesystem __unused, EFI_FILE_PROTOCOL **file ) { DBGC ( &efi_file_root, "EFIFILE open volume\n" ); - *file = &efi_file_root.file; - return 0; + return efi_file_open_fixed ( &efi_file_root, file ); } /** EFI simple file system protocol */ diff --git a/src/interface/efi/efi_pci.c b/src/interface/efi/efi_pci.c index 4adee0fd8..fda4aba0e 100644 --- a/src/interface/efi/efi_pci.c +++ b/src/interface/efi/efi_pci.c @@ -337,6 +337,9 @@ void * efipci_ioremap ( struct pci_device *pci, unsigned long bus_addr, offset = le64_to_cpu ( u.res->qword.offset ); start = ( offset + le64_to_cpu ( u.res->qword.min ) ); end = ( start + le64_to_cpu ( u.res->qword.len ) ); + DBGC2 ( pci, "EFIPCI " PCI_FMT " found range [%08llx,%08llx) " + "-> [%08llx,%08llx)\n", PCI_ARGS ( pci ), start, end, + ( start - offset ), ( end - offset ) ); if ( ( bus_addr < start ) || ( ( bus_addr + len ) > end ) ) continue; @@ -523,7 +526,8 @@ static void * efipci_dma_alloc ( struct dma_device *dma, /* Map buffer */ if ( ( rc = efipci_dma_map ( dma, map, virt_to_phys ( addr ), - len, DMA_BI ) ) != 0 ) + ( pages * EFI_PAGE_SIZE ), + DMA_BI ) ) != 0 ) goto err_map; /* Increment allocation count (for debugging) */ diff --git a/src/interface/efi/efi_veto.c b/src/interface/efi/efi_veto.c index 6ff7898e9..b616539d3 100644 --- a/src/interface/efi/efi_veto.c +++ b/src/interface/efi/efi_veto.c @@ -362,7 +362,7 @@ static int efi_veto_driver ( EFI_HANDLE driver ) { } /** - * Veto Dell Ip4ConfigDxe driver + * Veto Ip4ConfigDxe driver on some platforms * * @v binding Driver binding protocol * @v loaded Loaded image protocol @@ -372,19 +372,21 @@ static int efi_veto_driver ( EFI_HANDLE driver ) { * @ret vetoed Driver is to be vetoed */ static int -efi_veto_dell_ip4config ( EFI_DRIVER_BINDING_PROTOCOL *binding __unused, - EFI_LOADED_IMAGE_PROTOCOL *loaded __unused, - EFI_COMPONENT_NAME_PROTOCOL *wtf __unused, - const char *manufacturer, const CHAR16 *name ) { +efi_veto_ip4config ( EFI_DRIVER_BINDING_PROTOCOL *binding __unused, + EFI_LOADED_IMAGE_PROTOCOL *loaded __unused, + EFI_COMPONENT_NAME_PROTOCOL *wtf __unused, + const char *manufacturer, const CHAR16 *name ) { static const CHAR16 ip4cfg[] = L"IP4 CONFIG Network Service Driver"; static const char *dell = "Dell Inc."; + static const char *itautec = "Itautec S.A."; /* Check manufacturer and driver name */ if ( ! manufacturer ) return 0; if ( ! name ) return 0; - if ( strcmp ( manufacturer, dell ) != 0 ) + if ( ( strcmp ( manufacturer, dell ) != 0 ) && + ( strcmp ( manufacturer, itautec ) != 0 ) ) return 0; if ( memcmp ( name, ip4cfg, sizeof ( ip4cfg ) ) != 0 ) return 0; @@ -436,8 +438,8 @@ efi_veto_hp_xhci ( EFI_DRIVER_BINDING_PROTOCOL *binding __unused, /** Driver vetoes */ static struct efi_veto efi_vetoes[] = { { - .name = "Dell Ip4Config", - .veto = efi_veto_dell_ip4config, + .name = "Ip4Config", + .veto = efi_veto_ip4config, }, { .name = "HP Xhci", diff --git a/src/interface/xen/xenstore.c b/src/interface/xen/xenstore.c index a14881fcd..caeb4e934 100644 --- a/src/interface/xen/xenstore.c +++ b/src/interface/xen/xenstore.c @@ -68,14 +68,14 @@ static void xenstore_send ( struct xen_hypervisor *xen, const void *data, XENSTORE_RING_IDX cons; XENSTORE_RING_IDX idx; const char *bytes = data; - size_t offset = 0; + size_t offset; size_t fill; DBGCP ( intf, "XENSTORE raw request:\n" ); DBGCP_HDA ( intf, MASK_XENSTORE_IDX ( prod ), data, len ); /* Write one byte at a time */ - while ( offset < len ) { + for ( offset = 0 ; offset < len ; offset++ ) { /* Wait for space to become available */ while ( 1 ) { @@ -90,7 +90,7 @@ static void xenstore_send ( struct xen_hypervisor *xen, const void *data, /* Write byte */ idx = MASK_XENSTORE_IDX ( prod++ ); - writeb ( bytes[offset++], &intf->req[idx] ); + writeb ( bytes[offset], &intf->req[idx] ); } /* Update producer counter */ @@ -125,13 +125,13 @@ static void xenstore_recv ( struct xen_hypervisor *xen, void *data, XENSTORE_RING_IDX prod; XENSTORE_RING_IDX idx; char *bytes = data; - size_t offset = 0; + size_t offset; size_t fill; DBGCP ( intf, "XENSTORE raw response:\n" ); /* Read one byte at a time */ - while ( offset < len ) { + for ( offset = 0 ; offset < len ; offset++ ) { /* Wait for data to be ready */ while ( 1 ) { @@ -147,7 +147,7 @@ static void xenstore_recv ( struct xen_hypervisor *xen, void *data, /* Read byte */ idx = MASK_XENSTORE_IDX ( cons++ ); if ( data ) - bytes[offset++] = readb ( &intf->rsp[idx] ); + bytes[offset] = readb ( &intf->rsp[idx] ); } if ( data ) DBGCP_HDA ( intf, MASK_XENSTORE_IDX ( cons - len ), data, len ); diff --git a/src/net/peerdisc.c b/src/net/peerdisc.c index 55e3f7fa7..d7e0d2989 100644 --- a/src/net/peerdisc.c +++ b/src/net/peerdisc.c @@ -73,6 +73,9 @@ static LIST_HEAD ( peerdisc_segments ); */ unsigned int peerdisc_timeout_secs = PEERDISC_DEFAULT_TIMEOUT_SECS; +/** Most recently discovered peer (for any block) */ +static char *peerdisc_recent; + /** Hosted cache server */ static char *peerhost; @@ -383,6 +386,7 @@ static int peerdisc_discovered ( struct peerdisc_segment *segment, struct peerdisc_peer *peer; struct peerdisc_client *peerdisc; struct peerdisc_client *tmp; + char *recent; /* Ignore duplicate peers */ list_for_each_entry ( peer, &segment->peers, list ) { @@ -403,6 +407,15 @@ static int peerdisc_discovered ( struct peerdisc_segment *segment, /* Add to end of list of peers */ list_add_tail ( &peer->list, &segment->peers ); + /* Record as most recently discovered peer */ + if ( location != peerdisc_recent ) { + recent = strdup ( location ); + if ( recent ) { + free ( peerdisc_recent ); + peerdisc_recent = recent; + } + } + /* Notify all clients */ list_for_each_entry_safe ( peerdisc, tmp, &segment->clients, list ) peerdisc->op->discovered ( peerdisc ); @@ -484,6 +497,16 @@ static struct peerdisc_segment * peerdisc_create ( const char *id ) { } else { + /* Add most recently discovered peer to list of peers + * + * This is a performance optimisation: we assume that + * the most recently discovered peer for any block has + * a high probability of also having a copy of the + * next block that we attempt to discover. + */ + if ( peerdisc_recent ) + peerdisc_discovered ( segment, peerdisc_recent ); + /* Start discovery timer */ start_timer_nodelay ( &segment->timer ); DBGC2 ( segment, "PEERDISC %p discovering %s\n", diff --git a/src/net/tcp/httpconn.c b/src/net/tcp/httpconn.c index f9221b27e..538c4dcf6 100644 --- a/src/net/tcp/httpconn.c +++ b/src/net/tcp/httpconn.c @@ -32,6 +32,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdlib.h> #include <string.h> +#include <strings.h> #include <errno.h> #include <byteswap.h> #include <ipxe/tcpip.h> @@ -63,7 +64,7 @@ static struct http_scheme * http_scheme ( struct uri *uri ) { /* Identify scheme */ for_each_table_entry ( scheme, HTTP_SCHEMES ) { - if ( strcmp ( uri->scheme, scheme->name ) == 0 ) + if ( strcasecmp ( uri->scheme, scheme->name ) == 0 ) return scheme; } diff --git a/src/tests/gzip_test.c b/src/tests/gzip_test.c new file mode 100644 index 000000000..fa76edc53 --- /dev/null +++ b/src/tests/gzip_test.c @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2021 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * gzip image tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include <stdint.h> +#include <ipxe/image.h> +#include <ipxe/gzip.h> +#include <ipxe/test.h> + +/** A gzip test */ +struct gzip_test { + /** Compressed filename */ + const char *compressed_name; + /** Compressed data */ + const void *compressed; + /** Length of compressed data */ + size_t compressed_len; + /** Expected uncompressed name */ + const char *expected_name; + /** Expected uncompressed data */ + const void *expected; + /** Length of expected uncompressed data */ + size_t expected_len; +}; + +/** Define inline data */ +#define DATA(...) { __VA_ARGS__ } + +/** Define a gzip test */ +#define GZIP( name, COMPRESSED, EXPECTED ) \ + static const uint8_t name ## _compressed[] = COMPRESSED; \ + static const uint8_t name ## _expected[] = EXPECTED; \ + static struct gzip_test name = { \ + .compressed_name = #name ".gz", \ + .compressed = name ## _compressed, \ + .compressed_len = sizeof ( name ## _compressed ), \ + .expected_name = #name, \ + .expected = name ## _expected, \ + .expected_len = sizeof ( name ## _expected ), \ + }; + +/** "Hello world" */ +GZIP ( hello_world, + DATA ( 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0xf3, 0x48, 0xcd, 0xc9, 0xc9, 0x57, 0x28, 0xcf, 0x2f, 0xca, + 0x49, 0x01, 0x00, 0x52, 0x9e, 0xd6, 0x8b, 0x0b, 0x00, 0x00, + 0x00 ), + DATA ( 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, + 0x64 ) ); + +/** "Hello filename" */ +GZIP ( hello_filename, + DATA ( 0x1f, 0x8b, 0x08, 0x08, 0xeb, 0x5b, 0x96, 0x60, 0x00, 0x03, + 0x68, 0x77, 0x2e, 0x74, 0x78, 0x74, 0x00, 0xf3, 0x48, 0xcd, + 0xc9, 0xc9, 0x57, 0x48, 0xcb, 0xcc, 0x49, 0xcd, 0x4b, 0xcc, + 0x4d, 0x05, 0x00, 0x69, 0x37, 0x25, 0x3c, 0x0e, 0x00, 0x00, + 0x00 ), + DATA ( 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x66, 0x69, 0x6c, 0x65, + 0x6e, 0x61, 0x6d, 0x65 ) ); + +/** "Hello assorted headers" */ +GZIP ( hello_headers, + DATA ( 0x1f, 0x8b, 0x08, 0x1c, 0x11, 0x5c, 0x96, 0x60, 0x00, 0x03, + 0x05, 0x00, 0x41, 0x70, 0x01, 0x00, 0x0d, 0x68, 0x77, 0x2e, + 0x74, 0x78, 0x74, 0x00, 0x2f, 0x2f, 0x77, 0x68, 0x79, 0x3f, + 0x00, 0xf3, 0x48, 0xcd, 0xc9, 0xc9, 0x57, 0x48, 0x2c, 0x2e, + 0xce, 0x2f, 0x2a, 0x49, 0x4d, 0x51, 0xc8, 0x48, 0x4d, 0x4c, + 0x49, 0x2d, 0x2a, 0x06, 0x00, 0x59, 0xa4, 0x19, 0x61, 0x16, + 0x00, 0x00, 0x00 ), + DATA ( 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x61, 0x73, 0x73, 0x6f, + 0x72, 0x74, 0x65, 0x64, 0x20, 0x68, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x73 ) ); + +/** + * Report gzip test result + * + * @v test gzip test + * @v file Test code file + * @v line Test code line + */ +static void gzip_okx ( struct gzip_test *test, const char *file, + unsigned int line ) { + struct image *image; + struct image *extracted; + + /* Construct compressed image */ + image = image_memory ( test->compressed_name, + virt_to_user ( test->compressed ), + test->compressed_len ); + okx ( image != NULL, file, line ); + okx ( image->len == test->compressed_len, file, line ); + + /* Check type detection */ + okx ( image->type == &gzip_image_type, file, line ); + + /* Extract archive image */ + okx ( image_extract ( image, NULL, &extracted ) == 0, file, line ); + + /* Verify extracted image content */ + okx ( extracted->len == test->expected_len, file, line ); + okx ( memcmp_user ( extracted->data, 0, + virt_to_user ( test->expected ), 0, + test->expected_len ) == 0, file, line ); + + /* Verify extracted image name */ + okx ( strcmp ( extracted->name, test->expected_name ) == 0, + file, line ); + + /* Unregister images */ + unregister_image ( extracted ); + unregister_image ( image ); +} +#define gzip_ok( test ) gzip_okx ( test, __FILE__, __LINE__ ) + +/** + * Perform gzip self-test + * + */ +static void gzip_test_exec ( void ) { + + gzip_ok ( &hello_world ); + gzip_ok ( &hello_filename ); + gzip_ok ( &hello_headers ); +} + +/** gzip self-test */ +struct self_test gzip_test __self_test = { + .name = "gzip", + .exec = gzip_test_exec, +}; diff --git a/src/tests/string_test.c b/src/tests/string_test.c index 88a730aec..3afb8deb2 100644 --- a/src/tests/string_test.c +++ b/src/tests/string_test.c @@ -105,6 +105,14 @@ static void string_test_exec ( void ) { ok ( strcasecmp ( "Uncle", "Uncle Jack" ) != 0 ); ok ( strcasecmp ( "not", "equal" ) != 0 ); + /* Test strncasecmp() */ + ok ( strncasecmp ( "", "", 0 ) == 0 ); + ok ( strncasecmp ( "", "", 73 ) == 0 ); + ok ( strncasecmp ( "Uncle Jack", "Uncle jack", 47 ) == 0 ); + ok ( strncasecmp ( "Uncle Jack", "Uncle jake", 47 ) != 0 ); + ok ( strncasecmp ( "Uncle Jack", "Uncle jake", 9 ) != 0 ); + ok ( strncasecmp ( "Uncle Jack", "Uncle jake", 8 ) == 0 ); + /* Test memcmp() */ ok ( memcmp ( "", "", 0 ) == 0 ); ok ( memcmp ( "Foo", "Foo", 3 ) == 0 ); diff --git a/src/tests/tests.c b/src/tests/tests.c index 2e812d6ff..1cc4c81e8 100644 --- a/src/tests/tests.c +++ b/src/tests/tests.c @@ -73,3 +73,5 @@ REQUIRE_OBJECT ( bitops_test ); REQUIRE_OBJECT ( der_test ); REQUIRE_OBJECT ( pem_test ); REQUIRE_OBJECT ( ntlm_test ); +REQUIRE_OBJECT ( zlib_test ); +REQUIRE_OBJECT ( gzip_test ); diff --git a/src/tests/x509_test.c b/src/tests/x509_test.c index 256c3e85e..b6cba575c 100644 --- a/src/tests/x509_test.c +++ b/src/tests/x509_test.c @@ -1037,6 +1037,7 @@ static void x509_test_exec ( void ) { /* Check certificate names */ x509_check_name_ok ( &server_crt, "boot.test.ipxe.org" ); x509_check_name_ok ( &server_crt, "demo.test.ipxe.org" ); + x509_check_name_ok ( &server_crt, "demo.test.iPXE.org" ); x509_check_name_fail_ok ( &server_crt, "incorrect.test.ipxe.org" ); x509_check_name_ok ( &server_crt, "anything.alt.test.ipxe.org" ); x509_check_name_ok ( &server_crt, "wildcard.alt.test.ipxe.org" ); diff --git a/src/tests/zlib_test.c b/src/tests/zlib_test.c new file mode 100644 index 000000000..df52d09ac --- /dev/null +++ b/src/tests/zlib_test.c @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2021 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * zlib image tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include <stdint.h> +#include <ipxe/image.h> +#include <ipxe/zlib.h> +#include <ipxe/test.h> + +/** A zlib test */ +struct zlib_test { + /** Compressed filename */ + const char *compressed_name; + /** Compressed data */ + const void *compressed; + /** Length of compressed data */ + size_t compressed_len; + /** Expected uncompressed name */ + const char *expected_name; + /** Expected uncompressed data */ + const void *expected; + /** Length of expected uncompressed data */ + size_t expected_len; +}; + +/** Define inline data */ +#define DATA(...) { __VA_ARGS__ } + +/** Define a zlib test */ +#define ZLIB( name, COMPRESSED, EXPECTED ) \ + static const uint8_t name ## _compressed[] = COMPRESSED; \ + static const uint8_t name ## _expected[] = EXPECTED; \ + static struct zlib_test name = { \ + .compressed_name = #name ".z", \ + .compressed = name ## _compressed, \ + .compressed_len = sizeof ( name ## _compressed ), \ + .expected_name = #name, \ + .expected = name ## _expected, \ + .expected_len = sizeof ( name ## _expected ), \ + }; + +/** "Hello world" */ +ZLIB ( hello_world, + DATA ( 0x78, 0x9c, 0xf3, 0x48, 0xcd, 0xc9, 0xc9, 0x57, 0x28, 0xcf, + 0x2f, 0xca, 0x49, 0x01, 0x00, 0x18, 0xab, 0x04, 0x3d ), + DATA ( 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, + 0x64 ) ); + +/** + * Report zlib test result + * + * @v test zlib test + * @v file Test code file + * @v line Test code line + */ +static void zlib_okx ( struct zlib_test *test, const char *file, + unsigned int line ) { + struct image *image; + struct image *extracted; + + /* Construct compressed image */ + image = image_memory ( test->compressed_name, + virt_to_user ( test->compressed ), + test->compressed_len ); + okx ( image != NULL, file, line ); + okx ( image->len == test->compressed_len, file, line ); + + /* Check type detection */ + okx ( image->type == &zlib_image_type, file, line ); + + /* Extract archive image */ + okx ( image_extract ( image, NULL, &extracted ) == 0, file, line ); + + /* Verify extracted image content */ + okx ( extracted->len == test->expected_len, file, line ); + okx ( memcmp_user ( extracted->data, 0, + virt_to_user ( test->expected ), 0, + test->expected_len ) == 0, file, line ); + + /* Verify extracted image name */ + okx ( strcmp ( extracted->name, test->expected_name ) == 0, + file, line ); + + /* Unregister images */ + unregister_image ( extracted ); + unregister_image ( image ); +} +#define zlib_ok( test ) zlib_okx ( test, __FILE__, __LINE__ ) + +/** + * Perform zlib self-test + * + */ +static void zlib_test_exec ( void ) { + + zlib_ok ( &hello_world ); +} + +/** zlib self-test */ +struct self_test zlib_test __self_test = { + .name = "zlib", + .exec = zlib_test_exec, +}; diff --git a/src/usr/imgarchive.c b/src/usr/imgarchive.c new file mode 100644 index 000000000..6849dd510 --- /dev/null +++ b/src/usr/imgarchive.c @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2021 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdio.h> +#include <ipxe/image.h> +#include <usr/imgarchive.h> + +/** @file + * + * Archive image management + * + */ + +/** + * Extract archive image + * + * @v image Image + * @v name Extracted image name (or NULL to use default) + * @ret rc Return status code + */ +int imgextract ( struct image *image, const char *name ) { + struct image *extracted; + int rc; + + /* Extract archive image */ + if ( ( rc = image_extract ( image, name, &extracted ) ) != 0 ) { + printf ( "Could not extract image: %s\n", strerror ( rc ) ); + return rc; + } + + return 0; +} diff --git a/src/util/elf2efi.c b/src/util/elf2efi.c index 5542b99f8..8af53aeb4 100644 --- a/src/util/elf2efi.c +++ b/src/util/elf2efi.c @@ -125,7 +125,21 @@ #define R_ARM_V4BX 40 #endif -#define EFI_FILE_ALIGN 0x20 +/** + * Alignment of raw data of sections in the image file + * + * Some versions of signtool.exe will spuriously complain if this + * value is less than 512. + */ +#define EFI_FILE_ALIGN 0x200 + +/** + * Alignment of sections when loaded into memory + * + * This must equal the architecture page size, in order to allow for + * the possibility of the firmware using page-level protection to + * enforce section attributes at runtime. + */ #define EFI_IMAGE_ALIGN 0x1000 struct elf_file { diff --git a/src/util/genfsimg b/src/util/genfsimg index c3123f3ed..1635a11ac 100755 --- a/src/util/genfsimg +++ b/src/util/genfsimg @@ -230,7 +230,7 @@ done # since isohybrid will refuse to work without them. # if [ -n "${ISOIMG}" ] ; then - ISOARGS="-J -R" + ISOARGS="-J -R -l" copy_syslinux_file "isolinux.bin" "${ISODIR}" copy_syslinux_file "ldlinux.c32" "${ISODIR}" 2>/dev/null || true ISOARGS="${ISOARGS} -no-emul-boot -eltorito-boot isolinux.bin" @@ -240,6 +240,16 @@ if [ -n "${ISOIMG}" ] ; then else FATIMG= fi + if [ -n "${SOURCE_DATE_EPOCH:-}" ] ; then + DATE_FMT="+%Y%m%d%H%M%S00" + BUILD_DATE=$(date -u -d "@${SOURCE_DATE_EPOCH}" "${DATE_FMT}" \ + 2>/dev/null || \ + date -u -r "${SOURCE_DATE_EPOCH}" "${DATE_FMT}" \ + 2>/dev/null || \ + date -u "${DATE_FMT}") + ISOARGS="${ISOARGS} --set_all_file_dates ${BUILD_DATE}" + ISOARGS="${ISOARGS} --modification-date=${BUILD_DATE}" + fi fi # Create FAT filesystem image, if applicable @@ -297,7 +307,11 @@ if [ -n "${ISOIMG}" ] ; then -publisher "ipxe.org" -sysid "iPXE" -o "${ISOIMG}" \ ${ISOARGS} "${ISODIR}" if isohybrid --version >/dev/null 2>&1 ; then - isohybrid "${ISOIMG}" + ISOHYBRIDARGS= + if [ -n "${SOURCE_DATE_EPOCH:-}" ] ; then + ISOHYBRIDARGS="${ISOHYBRIDARGS} --id ${SOURCE_DATE_EPOCH}" + fi + isohybrid ${ISOHYBRIDARGS} "${ISOIMG}" fi fi |
