diff --git a/contrib/vm/bochsrc.txt b/contrib/vm/bochsrc.txt index 9bacd741..32842a3b 100644 --- a/contrib/vm/bochsrc.txt +++ b/contrib/vm/bochsrc.txt @@ -300,6 +300,7 @@ ata3: enabled=0, ioaddr1=0x168, ioaddr2=0x360, irq=9 #======================================================================= #boot: floppy #boot: disk +boot: network, floppy #======================================================================= # CLOCK: diff --git a/src/arch/i386/drivers/net/undionly.c b/src/arch/i386/drivers/net/undionly.c index ab9c61fb..c38b574d 100644 --- a/src/arch/i386/drivers/net/undionly.c +++ b/src/arch/i386/drivers/net/undionly.c @@ -114,13 +114,13 @@ struct root_device undi_root_device __root_device = { /** * Prepare for exit * - * @v flags Shutdown flags + * @v booting System is shutting down for OS boot */ -static void undionly_shutdown ( int flags ) { +static void undionly_shutdown ( int booting ) { /* If we are shutting down to boot an OS, clear the "keep PXE * stack" flag. */ - if ( flags & SHUTDOWN_BOOT ) + if ( booting ) preloaded_undi.flags &= ~UNDI_FL_KEEP_ALL; } diff --git a/src/arch/i386/image/bzimage.c b/src/arch/i386/image/bzimage.c index 900e34e1..45a1e862 100644 --- a/src/arch/i386/image/bzimage.c +++ b/src/arch/i386/image/bzimage.c @@ -477,7 +477,7 @@ static int bzimage_exec ( struct image *image ) { bzimage_update_header ( image, &bzimg, bzimg.rm_kernel ); /* Prepare for exiting */ - shutdown ( SHUTDOWN_BOOT ); + shutdown_boot(); DBGC ( image, "bzImage %p jumping to RM kernel at %04x:0000 " "(stack %04x:%04zx)\n", image, ( bzimg.rm_kernel_seg + 0x20 ), diff --git a/src/arch/i386/image/elfboot.c b/src/arch/i386/image/elfboot.c index 7cb51bf9..331d3764 100644 --- a/src/arch/i386/image/elfboot.c +++ b/src/arch/i386/image/elfboot.c @@ -48,7 +48,7 @@ static int elfboot_exec ( struct image *image ) { /* An ELF image has no callback interface, so we need to shut * down before invoking it. */ - shutdown ( SHUTDOWN_BOOT ); + shutdown_boot(); /* Jump to OS with flat physical addressing */ DBGC ( image, "ELF %p starting execution at %lx\n", image, entry ); diff --git a/src/arch/i386/image/multiboot.c b/src/arch/i386/image/multiboot.c index 041f0f2a..3ed4d840 100644 --- a/src/arch/i386/image/multiboot.c +++ b/src/arch/i386/image/multiboot.c @@ -278,7 +278,7 @@ static int multiboot_exec ( struct image *image ) { /* Multiboot images may not return and have no callback * interface, so shut everything down prior to booting the OS. */ - shutdown ( SHUTDOWN_BOOT ); + shutdown_boot(); /* Build memory map after unhiding bootloader memory regions as part of * shutting everything down. diff --git a/src/arch/i386/image/nbi.c b/src/arch/i386/image/nbi.c index 67f0d511..804b2303 100644 --- a/src/arch/i386/image/nbi.c +++ b/src/arch/i386/image/nbi.c @@ -406,7 +406,7 @@ static int nbi_exec ( struct image *image ) { /* Shut down now if NBI image will not return */ may_return = NBI_PROGRAM_RETURNS ( imgheader.flags ); if ( ! may_return ) - shutdown ( SHUTDOWN_BOOT ); + shutdown_boot(); /* Execute NBI image */ if ( NBI_LINEAR_EXEC_ADDR ( imgheader.flags ) ) { diff --git a/src/arch/i386/interface/pcbios/int13.c b/src/arch/i386/interface/pcbios/int13.c index 1d973e77..a27dbad7 100644 --- a/src/arch/i386/interface/pcbios/int13.c +++ b/src/arch/i386/interface/pcbios/int13.c @@ -1209,8 +1209,10 @@ static int int13_hook ( struct uri *uri, unsigned int drive ) { int13->cylinders, int13->heads, int13->sectors_per_track ); /* Hook INT 13 vector if not already hooked */ - if ( list_empty ( &int13s ) ) + if ( list_empty ( &int13s ) ) { int13_hook_vector(); + devices_get(); + } /* Add to list of emulated drives */ list_add ( &int13->list, &int13s ); @@ -1277,8 +1279,10 @@ static void int13_unhook ( unsigned int drive ) { DBGC ( int13, "INT13 drive %02x unregistered\n", int13->drive ); /* Unhook INT 13 vector if no more drives */ - if ( list_empty ( &int13s ) ) + if ( list_empty ( &int13s ) ) { + devices_put(); int13_unhook_vector(); + } /* Drop list's reference to drive */ ref_put ( &int13->refcnt ); diff --git a/src/arch/i386/interface/pxe/pxe_call.c b/src/arch/i386/interface/pxe/pxe_call.c index f6324bbf..f3208000 100644 --- a/src/arch/i386/interface/pxe/pxe_call.c +++ b/src/arch/i386/interface/pxe/pxe_call.c @@ -448,6 +448,7 @@ void pxe_activate ( struct net_device *netdev ) { if ( ! int_1a_hooked ) { hook_bios_interrupt ( 0x1a, ( unsigned int ) pxe_int_1a, &pxe_int_1a_vector ); + devices_get(); int_1a_hooked = 1; } @@ -475,6 +476,7 @@ int pxe_deactivate ( void ) { strerror ( rc ) ); return rc; } + devices_put(); int_1a_hooked = 0; } diff --git a/src/arch/i386/interface/pxe/pxe_preboot.c b/src/arch/i386/interface/pxe/pxe_preboot.c index 7698df52..9e4853b0 100644 --- a/src/arch/i386/interface/pxe/pxe_preboot.c +++ b/src/arch/i386/interface/pxe/pxe_preboot.c @@ -290,7 +290,7 @@ PXENV_EXIT_t pxenv_stop_undi ( struct s_PXENV_STOP_UNDI *stop_undi ) { pxe_deactivate(); /* Prepare for unload */ - shutdown ( SHUTDOWN_BOOT ); + shutdown_boot(); /* Check to see if we still have any hooked interrupts */ if ( hooked_bios_interrupts != 0 ) { diff --git a/src/arch/i386/interface/pxe/pxe_undi.c b/src/arch/i386/interface/pxe/pxe_undi.c index eb54417c..afbc1799 100644 --- a/src/arch/i386/interface/pxe/pxe_undi.c +++ b/src/arch/i386/interface/pxe/pxe_undi.c @@ -615,7 +615,9 @@ PXENV_EXIT_t pxenv_undi_get_iface_info ( struct s_PXENV_UNDI_GET_IFACE_INFO undi_get_iface_info->ServiceFlags = ( SUPPORTED_BROADCAST | SUPPORTED_MULTICAST | SUPPORTED_SET_STATION_ADDRESS | SUPPORTED_RESET | - SUPPORTED_OPEN_CLOSE | SUPPORTED_IRQ ); + SUPPORTED_OPEN_CLOSE ); + if ( netdev_irq_supported ( pxe_netdev ) ) + undi_get_iface_info->ServiceFlags |= SUPPORTED_IRQ; memset ( undi_get_iface_info->Reserved, 0, sizeof(undi_get_iface_info->Reserved) ); diff --git a/src/arch/i386/interface/syslinux/comboot_call.c b/src/arch/i386/interface/syslinux/comboot_call.c index 0c52b28a..95083270 100644 --- a/src/arch/i386/interface/syslinux/comboot_call.c +++ b/src/arch/i386/interface/syslinux/comboot_call.c @@ -531,7 +531,7 @@ static __asmcall void int22 ( struct i386_all_regs *ix86 ) { break; /* Perform final cleanup */ - shutdown ( SHUTDOWN_BOOT ); + shutdown_boot(); /* Perform sequence of copies */ shuffle ( ix86->segs.es, ix86->regs.di, ix86->regs.cx ); @@ -608,7 +608,7 @@ static __asmcall void int22 ( struct i386_all_regs *ix86 ) { break; /* Perform final cleanup */ - shutdown ( SHUTDOWN_BOOT ); + shutdown_boot(); /* Perform sequence of copies */ shuffle ( ix86->segs.es, ix86->regs.di, ix86->regs.cx ); diff --git a/src/arch/i386/prefix/libprefix.S b/src/arch/i386/prefix/libprefix.S index 340e74be..f95a7a1f 100644 --- a/src/arch/i386/prefix/libprefix.S +++ b/src/arch/i386/prefix/libprefix.S @@ -24,6 +24,9 @@ FILE_LICENCE ( GPL2_OR_LATER ) /* Image compression enabled */ #define COMPRESS 1 +/* Protected mode flag */ +#define CR0_PE 1 + /***************************************************************************** * Utility function: print character (with LF -> LF,CR translation) * @@ -229,16 +232,186 @@ print_kill_line: * None **************************************************************************** */ -#if ! COMPRESS .section ".prefix.lib", "awx", @progbits .code16 copy_bytes: - pushl %ecx + pushl %ecx rep addr32 movsb - popl %ecx + popl %ecx ret - .size copy_bytes, . - copy_bytes -#endif /* COMPRESS */ + .size copy_bytes, . - copy_bytes + +/**************************************************************************** + * zero_bytes + * + * Zero bytes + * + * Parameters: + * %ds:esi : source address + * %es:edi : destination address + * %ecx : length + * Returns: + * %ds:esi : next source address + * %es:edi : next destination address + * Corrupts: + * None + **************************************************************************** + */ + .section ".prefix.lib", "awx", @progbits + .code16 +zero_bytes: + pushl %ecx + pushw %ax + xorw %ax, %ax + rep addr32 stosb + popw %ax + popl %ecx + ret + .size zero_bytes, . - zero_bytes + +/**************************************************************************** + * process_bytes + * + * Call memcpy()-like function + * + * Parameters: + * %esi : source physical address + * %edi : destination physical address + * %ecx : length + * %bx : memcpy()-like function to call, passing parameters: + * %ds:esi : source address + * %es:edi : destination address + * %ecx : length + * and returning: + * %ds:esi : next source address + * %es:edi : next destination address + * Returns: + * %esi : next source physical address + * %edi : next destination physical address + * Corrupts: + * None + **************************************************************************** + */ + .section ".prefix.lib", "awx", @progbits + .code16 +process_bytes: + +#ifndef KEEP_IT_REAL + + /* Preserve registers */ + pushfw + pushl %eax + pushl %ebp + + /* Construct GDT on stack (since .prefix may not be writable) */ + .equ PM_DS, 0x18 /* Flat data segment */ + pushl $0x008f9300 + pushl $0x0000ffff + .equ PM_SS, 0x10 /* Stack segment based at %ss:0000 */ + pushl $0x008f0930 + pushw %ss + pushw $0xffff + .equ PM_CS, 0x08 /* Code segment based at %cs:0000 */ + pushl $0x008f09b0 + pushw %cs + pushw $0xffff + pushl $0 /* Base and length */ + pushw %ss + pushw $0x1f + movzwl %sp, %ebp + shll $4, 0x02(%bp) + addl %ebp, 0x02(%bp) + shll $4, 0x0a(%bp) + shll $4, 0x12(%bp) + subw $8, %sp + sgdt -8(%bp) + + /* Switch to protected mode */ + pushw %gs + pushw %fs + pushw %es + pushw %ds + pushw %ss + pushw %cs + pushw $2f + cli + data32 lgdt (%bp) + movl %cr0, %eax + orb $CR0_PE, %al + movl %eax, %cr0 + ljmp $PM_CS, $1f +1: movw $PM_SS, %ax + movw %ax, %ss + movw $PM_DS, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + + /* Call memcpy()-like function */ + call *%bx + + /* Return to (flat) real mode */ + movl %cr0, %eax + andb $0!CR0_PE, %al + movl %eax, %cr0 + lret +2: /* lret will ljmp to here */ + popw %ss + popw %ds + popw %es + popw %fs + popw %gs + + /* Restore GDT */ + data32 lgdt -8(%bp) + addw $( 8 /* saved GDT */ + ( PM_DS + 8 ) /* GDT on stack */ ), %sp + + /* Restore registers and return */ + popl %ebp + popl %eax + popfw + ret + +#else /* KEEP_IT_REAL */ + + /* Preserve registers */ + pushl %eax + pushw %ds + pushw %es + + /* Convert %esi and %edi to %ds:esi and %es:edi */ + shrl $4, %esi + movw %si, %ds + xorw %si, %si + shll $4, %esi + shrl $4, %edi + movw %di, %es + xorw %di, %di + shll $4, %edi + + /* Call memcpy()-like function */ + call *%bx + + /* Convert %ds:esi and %es:edi back to physical addresses */ + xorl %eax, %eax + movw %ds, %cx + shll $4, %eax + addl %eax, %esi + xorl %eax, %eax + movw %es, %cx + shll $4, %eax + addl %eax, %edi + + /* Restore registers and return */ + popw %es + popw %ds + popl %eax + ret + +#endif /* KEEP_IT_REAL */ + + .size process_bytes, . - process_bytes /**************************************************************************** * install_block @@ -261,35 +434,22 @@ copy_bytes: .code16 install_block: /* Preserve registers */ - pushw %ds - pushw %es pushl %ecx - - /* Convert %esi and %edi to %ds:esi and %es:edi */ - shrl $4, %esi - movw %si, %ds - xorw %si, %si - shll $4, %esi - shrl $4, %edi - movw %di, %es - xorw %di, %di - shll $4, %edi + pushw %bx + /* Decompress (or copy) source to destination */ #if COMPRESS - /* Decompress source to destination */ - call decompress16 + movw $decompress16, %bx #else - /* Copy source to destination */ - call copy_bytes + movw $copy_bytes, %bx #endif + call process_bytes /* Zero .bss portion */ negl %ecx addl %edx, %ecx - pushw %ax - xorw %ax, %ax - rep addr32 stosb - popw %ax + movw $zero_bytes, %bx + call process_bytes /* Round up %esi and %edi to start of next blocks */ addl $0xf, %esi @@ -297,20 +457,9 @@ install_block: addl $0xf, %edi andl $~0xf, %edi - /* Convert %ds:esi and %es:edi back to physical addresses */ - xorl %ecx, %ecx - movw %ds, %cx - shll $4, %ecx - addl %ecx, %esi - xorl %ecx, %ecx - movw %es, %cx - shll $4, %ecx - addl %ecx, %edi - /* Restore registers and return */ + popw %bx popl %ecx - popw %es - popw %ds ret .size install_block, . - install_block @@ -500,7 +649,10 @@ install_prealloc: popl %esi #ifndef KEEP_IT_REAL - /* Access high memory */ + /* Access high memory by enabling the A20 gate. (We will + * already have 4GB segment limits as a result of calling + * install_block.) + */ pushw %cs pushw $1f pushw %ax @@ -612,11 +764,10 @@ payload_death_message: /* Copy code to new location */ pushl %edi - pushw %ax - xorw %ax, %ax - movw %ax, %es - es rep addr32 movsb - popw %ax + pushw %bx + movw $copy_bytes, %bx + call process_bytes + popw %bx popl %edi /* Initialise librm at new location */ diff --git a/src/arch/i386/prefix/mromprefix.S b/src/arch/i386/prefix/mromprefix.S index 989cea1e..3e177d00 100644 --- a/src/arch/i386/prefix/mromprefix.S +++ b/src/arch/i386/prefix/mromprefix.S @@ -151,7 +151,12 @@ find_mem_bar: /* Copy payload to buffer, or set buffer address to BAR address */ testl %esi, %esi jz 1f - /* We have a buffer; copy payload to it */ + /* We have a buffer; copy payload to it. Since .mrom is + * designed specifically for real hardware, we assume that + * flat real mode is working properly. (In the unlikely event + * that this code is run inside a hypervisor that doesn't + * properly support flat real mode, it will die horribly.) + */ pushl %esi pushw %es movl %esi, %edi diff --git a/src/arch/i386/transitions/libflat.S b/src/arch/i386/transitions/liba20.S similarity index 72% rename from src/arch/i386/transitions/libflat.S rename to src/arch/i386/transitions/liba20.S index 98f6f4df..594d6201 100644 --- a/src/arch/i386/transitions/libflat.S +++ b/src/arch/i386/transitions/liba20.S @@ -21,124 +21,6 @@ FILE_LICENCE ( GPL2_OR_LATER ) .arch i386 -#define CR0_PE 1 - -/**************************************************************************** - * flatten_real_mode - * - * Set up 4GB segment limits - * - * Parameters: - * none - * Returns: - * none - * Corrupts: - * none - **************************************************************************** - */ - /* GDT for protected-mode calls */ - .section ".text16.early.data", "aw", @progbits - .align 16 -flatten_gdt: -flatten_gdt_limit: .word flatten_gdt_length - 1 -flatten_gdt_base: .long 0 - .word 0 /* padding */ -flatten_cs: /* 16-bit protected-mode flat code segment */ - .equ FLAT_CS, flatten_cs - flatten_gdt - .word 0xffff, 0 - .byte 0, 0x9b, 0x8f, 0 -flatten_ss: /* 16-bit protected-mode flat stack segment */ - .equ FLAT_SS, flatten_ss - flatten_gdt - .word 0xffff, 0 - .byte 0, 0x93, 0x8f, 0 -flatten_gdt_end: - .equ flatten_gdt_length, . - flatten_gdt - .size flatten_gdt, . - flatten_gdt - - .section ".text16.early.data", "aw", @progbits - .align 16 -flatten_saved_gdt: - .long 0, 0 - .size flatten_saved_gdt, . - flatten_saved_gdt - - .section ".text16.early", "awx", @progbits - .code16 -flatten_real_mode: - /* Preserve registers and flags */ - pushfl - pushl %eax - pushw %si - pushw %gs - pushw %fs - pushw %es - pushw %ds - pushw %ss - - /* Set %ds for access to .text16.early.data variables */ - pushw %cs - popw %ds - - /* Preserve original GDT */ - sgdt flatten_saved_gdt - - /* Set up GDT bases */ - xorl %eax, %eax - movw %cs, %ax - shll $4, %eax - addl $flatten_gdt, %eax - movl %eax, flatten_gdt_base - movw %cs, %ax - movw $flatten_cs, %si - call set_seg_base - movw %ss, %ax - movw $flatten_ss, %si - call set_seg_base - - /* Switch temporarily to protected mode and set segment registers */ - pushw %cs - pushw $2f - cli - data32 lgdt flatten_gdt - movl %cr0, %eax - orb $CR0_PE, %al - movl %eax, %cr0 - ljmp $FLAT_CS, $1f -1: movw $FLAT_SS, %ax - movw %ax, %ss - movw %ax, %ds - movw %ax, %es - movw %ax, %fs - movw %ax, %gs - movl %cr0, %eax - andb $0!CR0_PE, %al - movl %eax, %cr0 - lret -2: /* lret will ljmp to here */ - - /* Restore GDT, registers and flags */ - data32 lgdt flatten_saved_gdt - popw %ss - popw %ds - popw %es - popw %fs - popw %gs - popw %si - popl %eax - popfl - ret - .size flatten_real_mode, . - flatten_real_mode - - .section ".text16.early", "awx", @progbits - .code16 -set_seg_base: - rolw $4, %ax - movw %ax, 2(%si) - andw $0xfff0, 2(%si) - movb %al, 4(%si) - andb $0x0f, 4(%si) - ret - .size set_seg_base, . - set_seg_base - /**************************************************************************** * test_a20_short, test_a20_long * @@ -403,7 +285,7 @@ enable_a20_method: /**************************************************************************** * access_highmem (real mode far call) * - * Open up access to high memory in flat real mode with A20 enabled + * Open up access to high memory with A20 enabled * * Parameters: * none @@ -419,7 +301,5 @@ enable_a20_method: access_highmem: /* Enable A20 line */ call enable_a20 - /* Set up 4GB limits */ - call flatten_real_mode lret .size access_highmem, . - access_highmem diff --git a/src/core/device.c b/src/core/device.c index cb2c23b0..dc182e03 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -35,6 +35,9 @@ FILE_LICENCE ( GPL2_OR_LATER ); /** Registered root devices */ static LIST_HEAD ( devices ); +/** Device removal inhibition counter */ +int device_keep_count = 0; + /** * Probe a root device * @@ -87,11 +90,11 @@ static void probe_devices ( void ) { * Remove all devices * */ -static void remove_devices ( int flags ) { +static void remove_devices ( int booting __unused ) { struct root_device *rootdev; struct root_device *tmp; - if ( flags & SHUTDOWN_KEEP_DEVICES ) { + if ( device_keep_count != 0 ) { DBG ( "Refusing to remove devices on shutdown\n" ); return; } diff --git a/src/core/exec.c b/src/core/exec.c index bb3b343d..57b2df58 100644 --- a/src/core/exec.c +++ b/src/core/exec.c @@ -86,80 +86,6 @@ int execv ( const char *command, char * const argv[] ) { return -ENOEXEC; } -/** - * Expand variables within command line - * - * @v command Command line - * @ret expcmd Expanded command line - * - * The expanded command line is allocated with malloc() and the caller - * must eventually free() it. - */ -static char * expand_command ( const char *command ) { - char *expcmd; - char *start; - char *end; - char *head; - char *name; - char *tail; - int setting_len; - int new_len; - char *tmp; - - /* Obtain temporary modifiable copy of command line */ - expcmd = strdup ( command ); - if ( ! expcmd ) - return NULL; - - /* Expand while expansions remain */ - while ( 1 ) { - - head = expcmd; - - /* Locate setting to be expanded */ - start = NULL; - end = NULL; - for ( tmp = expcmd ; *tmp ; tmp++ ) { - if ( ( tmp[0] == '$' ) && ( tmp[1] == '{' ) ) - start = tmp; - if ( start && ( tmp[0] == '}' ) ) { - end = tmp; - break; - } - } - if ( ! end ) - break; - *start = '\0'; - name = ( start + 2 ); - *end = '\0'; - tail = ( end + 1 ); - - /* Determine setting length */ - setting_len = fetchf_named_setting ( name, NULL, 0 ); - if ( setting_len < 0 ) - setting_len = 0; /* Treat error as empty setting */ - - /* Read setting into temporary buffer */ - { - char setting_buf[ setting_len + 1 ]; - - setting_buf[0] = '\0'; - fetchf_named_setting ( name, setting_buf, - sizeof ( setting_buf ) ); - - /* Construct expanded string and discard old string */ - tmp = expcmd; - new_len = asprintf ( &expcmd, "%s%s%s", - head, setting_buf, tail ); - free ( tmp ); - if ( new_len < 0 ) - return NULL; - } - } - - return expcmd; -} - /** * Split command line into tokens * @@ -294,7 +220,7 @@ int system ( const char *command ) { int rc = 0; /* Perform variable expansion */ - expcmd = expand_command ( command ); + expcmd = expand_settings ( command ); if ( ! expcmd ) return -ENOMEM; diff --git a/src/core/main.c b/src/core/main.c index a1128dd0..e2b4e3e2 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -85,7 +85,7 @@ __asmcall int main ( void ) { shell(); } - shutdown ( SHUTDOWN_EXIT | shutdown_exit_flags ); + shutdown_exit(); return 0; } diff --git a/src/core/nvo.c b/src/core/nvo.c index 1a886c0f..f4da407a 100644 --- a/src/core/nvo.c +++ b/src/core/nvo.c @@ -43,12 +43,79 @@ static unsigned int nvo_checksum ( struct nvo_block *nvo ) { uint8_t sum = 0; unsigned int i; - for ( i = 0 ; i < nvo->total_len ; i++ ) { + for ( i = 0 ; i < nvo->len ; i++ ) { sum += *(data++); } return sum; } +/** + * Reallocate non-volatile stored options block + * + * @v nvo Non-volatile options block + * @v len New length + * @ret rc Return status code + */ +static int nvo_realloc ( struct nvo_block *nvo, size_t len ) { + void *new_data; + + /* Reallocate data */ + new_data = realloc ( nvo->data, len ); + if ( ! new_data ) { + DBGC ( nvo, "NVO %p could not allocate %zd bytes\n", + nvo, len ); + return -ENOMEM; + } + nvo->data = new_data; + nvo->len = len; + + /* Update DHCP option block */ + if ( len ) { + nvo->dhcpopts.data = ( nvo->data + 1 /* checksum */ ); + nvo->dhcpopts.alloc_len = ( len - 1 /* checksum */ ); + } else { + nvo->dhcpopts.data = NULL; + nvo->dhcpopts.used_len = 0; + nvo->dhcpopts.alloc_len = 0; + } + + return 0; +} + +/** + * Reallocate non-volatile stored options DHCP option block + * + * @v options DHCP option block + * @v len New length + * @ret rc Return status code + */ +static int nvo_realloc_dhcpopt ( struct dhcp_options *options, size_t len ) { + struct nvo_block *nvo = + container_of ( options, struct nvo_block, dhcpopts ); + int rc; + + /* Refuse to reallocate if we have no way to resize the block */ + if ( ! nvo->resize ) + return dhcpopt_no_realloc ( options, len ); + + /* Allow one byte for the checksum (if any data is present) */ + if ( len ) + len += 1; + + /* Resize underlying non-volatile options block */ + if ( ( rc = nvo->resize ( nvo, len ) ) != 0 ) { + DBGC ( nvo, "NVO %p could not resize to %zd bytes: %s\n", + nvo, len, strerror ( rc ) ); + return rc; + } + + /* Reallocate in-memory options block */ + if ( ( rc = nvo_realloc ( nvo, len ) ) != 0 ) + return rc; + + return 0; +} + /** * Load non-volatile stored options from non-volatile storage device * @@ -56,21 +123,37 @@ static unsigned int nvo_checksum ( struct nvo_block *nvo ) { * @ret rc Return status code */ static int nvo_load ( struct nvo_block *nvo ) { - void *data = nvo->data; - struct nvo_fragment *frag; + uint8_t *options_data = nvo->dhcpopts.data; int rc; - /* Read data a fragment at a time */ - for ( frag = nvo->fragments ; frag->len ; frag++ ) { - if ( ( rc = nvs_read ( nvo->nvs, frag->address, data, - frag->len ) ) != 0 ) { - DBGC ( nvo, "NVO %p could not read %zd bytes at " - "%#04x\n", nvo, frag->len, frag->address ); - return rc; - } - data += frag->len; + /* Skip reading zero-length NVO fields */ + if ( nvo->len == 0 ) { + DBGC ( nvo, "NVO %p is empty; skipping load\n", nvo ); + return 0; } + /* Read data */ + if ( ( rc = nvs_read ( nvo->nvs, nvo->address, nvo->data, + nvo->len ) ) != 0 ) { + DBGC ( nvo, "NVO %p could not read %zd bytes at %#04x: %s\n", + nvo, nvo->len, nvo->address, strerror ( rc ) ); + return rc; + } + + /* If checksum fails, or options data starts with a zero, + * assume the whole block is invalid. This should capture the + * case of random initial contents. + */ + if ( ( nvo_checksum ( nvo ) != 0 ) || ( options_data[0] == 0 ) ) { + DBGC ( nvo, "NVO %p has checksum %02x and initial byte %02x; " + "assuming empty\n", nvo, nvo_checksum ( nvo ), + options_data[0] ); + memset ( nvo->data, 0, nvo->len ); + } + + /* Rescan DHCP option block */ + dhcpopt_update_used_len ( &nvo->dhcpopts ); + DBGC ( nvo, "NVO %p loaded from non-volatile storage\n", nvo ); return 0; } @@ -82,60 +165,25 @@ static int nvo_load ( struct nvo_block *nvo ) { * @ret rc Return status code */ static int nvo_save ( struct nvo_block *nvo ) { - void *data = nvo->data; - uint8_t *checksum = data; - struct nvo_fragment *frag; + uint8_t *checksum = nvo->data; int rc; - /* Recalculate checksum */ - *checksum -= nvo_checksum ( nvo ); + /* Recalculate checksum, if applicable */ + if ( nvo->len > 0 ) + *checksum -= nvo_checksum ( nvo ); - /* Write data a fragment at a time */ - for ( frag = nvo->fragments ; frag->len ; frag++ ) { - if ( ( rc = nvs_write ( nvo->nvs, frag->address, data, - frag->len ) ) != 0 ) { - DBGC ( nvo, "NVO %p could not write %zd bytes at " - "%#04x\n", nvo, frag->len, frag->address ); - return rc; - } - data += frag->len; + /* Write data */ + if ( ( rc = nvs_write ( nvo->nvs, nvo->address, nvo->data, + nvo->len ) ) != 0 ) { + DBGC ( nvo, "NVO %p could not write %zd bytes at %#04x: %s\n", + nvo, nvo->len, nvo->address, strerror ( rc ) ); + return rc; } DBGC ( nvo, "NVO %p saved to non-volatile storage\n", nvo ); return 0; } -/** - * Parse stored options - * - * @v nvo Non-volatile options block - * - * Verifies that the options data is valid, and configures the DHCP - * options block. If the data is not valid, it is replaced with an - * empty options block. - */ -static void nvo_init_dhcpopts ( struct nvo_block *nvo ) { - uint8_t *options_data; - size_t options_len; - - /* Steal one byte for the checksum */ - options_data = ( nvo->data + 1 ); - options_len = ( nvo->total_len - 1 ); - - /* If checksum fails, or options data starts with a zero, - * assume the whole block is invalid. This should capture the - * case of random initial contents. - */ - if ( ( nvo_checksum ( nvo ) != 0 ) || ( options_data[0] == 0 ) ) { - DBGC ( nvo, "NVO %p has checksum %02x and initial byte %02x; " - "assuming empty\n", nvo, nvo_checksum ( nvo ), - options_data[0] ); - memset ( nvo->data, 0, nvo->total_len ); - } - - dhcpopt_init ( &nvo->dhcpopts, options_data, options_len ); -} - /** * Store value of NVO setting * @@ -197,13 +245,20 @@ static struct settings_operations nvo_settings_operations = { * * @v nvo Non-volatile options block * @v nvs Underlying non-volatile storage device - * @v fragments List of option-containing fragments, or NULL + * @v address Address within NVS device + * @v len Length of non-volatile options data + * @v resize Resize method * @v refcnt Containing object reference counter, or NULL */ void nvo_init ( struct nvo_block *nvo, struct nvs_device *nvs, - struct nvo_fragment *fragments, struct refcnt *refcnt ) { + size_t address, size_t len, + int ( * resize ) ( struct nvo_block *nvo, size_t len ), + struct refcnt *refcnt ) { nvo->nvs = nvs; - nvo->fragments = fragments; + nvo->address = address; + nvo->len = len; + nvo->resize = resize; + dhcpopt_init ( &nvo->dhcpopts, NULL, 0, nvo_realloc_dhcpopt ); settings_init ( &nvo->settings, &nvo_settings_operations, refcnt, 0 ); } @@ -215,40 +270,17 @@ void nvo_init ( struct nvo_block *nvo, struct nvs_device *nvs, * @ret rc Return status code */ int register_nvo ( struct nvo_block *nvo, struct settings *parent ) { - struct nvo_fragment *fragment = nvo->fragments; int rc; - /* Calculate total length of all fragments, if applicable */ - if ( fragment ) { - for ( ; fragment->len ; fragment++ ) - nvo->total_len += fragment->len; - } else { - nvo->total_len = nvo->nvs->size; - } - - /* Allocate memory for options (and fragment list, if applicable) */ - nvo->data = zalloc ( nvo->total_len + - ( fragment ? 0 : ( 2 * sizeof ( *fragment ) ) ) ); - if ( ! nvo->data ) { - DBGC ( nvo, "NVO %p could not allocate %zd bytes\n", - nvo, nvo->total_len ); - rc = -ENOMEM; - goto err_malloc; - } - - /* Create fragment list, if applicable */ - if ( ! fragment ) { - fragment = ( nvo->data + nvo->total_len ); - fragment->len = nvo->total_len; - nvo->fragments = fragment; - } + /* Allocate memory for options */ + if ( ( rc = nvo_realloc ( nvo, nvo->len ) ) != 0 ) + goto err_realloc; /* Read data from NVS */ if ( ( rc = nvo_load ( nvo ) ) != 0 ) goto err_load; - /* Verify and register options */ - nvo_init_dhcpopts ( nvo ); + /* Register settings */ if ( ( rc = register_settings ( &nvo->settings, parent, "nvo" ) ) != 0 ) goto err_register; @@ -257,9 +289,8 @@ int register_nvo ( struct nvo_block *nvo, struct settings *parent ) { err_register: err_load: - free ( nvo->data ); - nvo->data = NULL; - err_malloc: + nvo_realloc ( nvo, 0 ); + err_realloc: return rc; } @@ -270,7 +301,6 @@ int register_nvo ( struct nvo_block *nvo, struct settings *parent ) { */ void unregister_nvo ( struct nvo_block *nvo ) { unregister_settings ( &nvo->settings ); - free ( nvo->data ); - nvo->data = NULL; + nvo_realloc ( nvo, 0 ); DBGC ( nvo, "NVO %p unregistered\n", nvo ); } diff --git a/src/core/open.c b/src/core/open.c index f8ee9f3a..b026efcd 100644 --- a/src/core/open.c +++ b/src/core/open.c @@ -32,6 +32,22 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ +/** + * Find opener for URI scheme + * + * @v scheme URI scheme + * @ret opener Opener, or NULL + */ +struct uri_opener * xfer_uri_opener ( const char *scheme ) { + struct uri_opener *opener; + + for_each_table_entry ( opener, URI_OPENERS ) { + if ( strcmp ( scheme, opener->scheme ) == 0 ) + return opener; + } + return NULL; +} + /** * Open URI * @@ -45,29 +61,38 @@ FILE_LICENCE ( GPL2_OR_LATER ); int xfer_open_uri ( struct interface *intf, struct uri *uri ) { struct uri_opener *opener; struct uri *resolved_uri; - int rc = -ENOTSUP; + int rc; /* Resolve URI */ resolved_uri = resolve_uri ( cwuri, uri ); - if ( ! resolved_uri ) - return -ENOMEM; + if ( ! resolved_uri ) { + rc = -ENOMEM; + goto err_resolve_uri; + } /* Find opener which supports this URI scheme */ - for_each_table_entry ( opener, URI_OPENERS ) { - if ( strcmp ( resolved_uri->scheme, opener->scheme ) == 0 ) { - DBGC ( INTF_COL ( intf ), "INTF " INTF_FMT - " opening %s URI\n", INTF_DBG ( intf ), - resolved_uri->scheme ); - rc = opener->open ( intf, resolved_uri ); - goto done; - } + opener = xfer_uri_opener ( resolved_uri->scheme ); + if ( ! opener ) { + DBGC ( INTF_COL ( intf ), "INTF " INTF_FMT " attempted to open " + "unsupported URI scheme \"%s\"\n", + INTF_DBG ( intf ), resolved_uri->scheme ); + rc = -ENOTSUP; + goto err_opener; } - DBGC ( INTF_COL ( intf ), "INTF " INTF_FMT " attempted to open " - "unsupported URI scheme \"%s\"\n", - INTF_DBG ( intf ), resolved_uri->scheme ); - done: + /* Call opener */ + DBGC ( INTF_COL ( intf ), "INTF " INTF_FMT " opening %s URI\n", + INTF_DBG ( intf ), resolved_uri->scheme ); + if ( ( rc = opener->open ( intf, resolved_uri ) ) != 0 ) { + DBGC ( INTF_COL ( intf ), "INTF " INTF_FMT " could not open: " + "%s\n", INTF_DBG ( intf ), strerror ( rc ) ); + goto err_open; + } + + err_open: + err_opener: uri_put ( resolved_uri ); + err_resolve_uri: return rc; } diff --git a/src/core/settings.c b/src/core/settings.c index e2b48263..da662e61 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -1467,6 +1467,87 @@ struct setting_type setting_type_uuid __setting_type = { .fetchf = fetchf_uuid, }; +/****************************************************************************** + * + * Setting expansion + * + ****************************************************************************** + */ + +/** + * Expand variables within string + * + * @v string String + * @ret expstr Expanded string + * + * The expanded string is allocated with malloc() and the caller must + * eventually free() it. + */ +char * expand_settings ( const char *string ) { + char *expstr; + char *start; + char *end; + char *head; + char *name; + char *tail; + int setting_len; + int new_len; + char *tmp; + + /* Obtain temporary modifiable copy of string */ + expstr = strdup ( string ); + if ( ! expstr ) + return NULL; + + /* Expand while expansions remain */ + while ( 1 ) { + + head = expstr; + + /* Locate setting to be expanded */ + start = NULL; + end = NULL; + for ( tmp = expstr ; *tmp ; tmp++ ) { + if ( ( tmp[0] == '$' ) && ( tmp[1] == '{' ) ) + start = tmp; + if ( start && ( tmp[0] == '}' ) ) { + end = tmp; + break; + } + } + if ( ! end ) + break; + *start = '\0'; + name = ( start + 2 ); + *end = '\0'; + tail = ( end + 1 ); + + /* Determine setting length */ + setting_len = fetchf_named_setting ( name, NULL, 0 ); + if ( setting_len < 0 ) + setting_len = 0; /* Treat error as empty setting */ + + /* Read setting into temporary buffer */ + { + char setting_buf[ setting_len + 1 ]; + + setting_buf[0] = '\0'; + fetchf_named_setting ( name, setting_buf, + sizeof ( setting_buf ) ); + + /* Construct expanded string and discard old string */ + tmp = expstr; + new_len = asprintf ( &expstr, "%s%s%s", + head, setting_buf, tail ); + free ( tmp ); + if ( new_len < 0 ) + return NULL; + } + } + + return expstr; +} + /****************************************************************************** * * Settings diff --git a/src/drivers/bus/pcivpd.c b/src/drivers/bus/pcivpd.c index 1f7d832b..15cf9059 100644 --- a/src/drivers/bus/pcivpd.c +++ b/src/drivers/bus/pcivpd.c @@ -19,6 +19,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include +#include #include #include #include @@ -73,6 +74,10 @@ static int pci_vpd_read_dword ( struct pci_vpd *vpd, int address, unsigned int retries; uint16_t flag; + /* Fail if no VPD present */ + if ( ! cap ) + return -ENOTTY; + /* Return cached value, if present */ if ( pci_vpd_cache_is_valid ( vpd ) && ( vpd->cache.address == address ) ) { @@ -127,6 +132,10 @@ static int pci_vpd_write_dword ( struct pci_vpd *vpd, int address, unsigned int retries; uint16_t flag; + /* Fail if no VPD present */ + if ( ! cap ) + return -ENOTTY; + /* Invalidate cache */ pci_vpd_invalidate_cache ( vpd ); @@ -384,3 +393,163 @@ int pci_vpd_find ( struct pci_vpd *vpd, unsigned int field, PCI_ARGS ( vpd->pci ), PCI_VPD_FIELD_ARGS ( field ) ); return -ENOENT; } + +/** + * Resize VPD field + * + * @v vpd PCI VPD + * @v field VPD field descriptor + * @v len New length of field body + * @ret address Address of field body + * @ret rc Return status code + */ +int pci_vpd_resize ( struct pci_vpd *vpd, unsigned int field, size_t len, + unsigned int *address ) { + struct pci_vpd_field rw_field; + struct pci_vpd_field old_field; + struct pci_vpd_field new_field; + unsigned int rw_address; + unsigned int old_address; + unsigned int copy_address; + unsigned int dst_address; + unsigned int dump_address; + size_t rw_len; + size_t old_len; + size_t available_len; + size_t copy_len; + size_t dump_len; + void *copy; + int rc; + + /* Sanity checks */ + assert ( PCI_VPD_TAG ( field ) == PCI_VPD_TAG_RW ); + assert ( PCI_VPD_KEYWORD ( field ) != 0 ); + assert ( field != PCI_VPD_FIELD_RW ); + + /* Locate 'RW' field */ + if ( ( rc = pci_vpd_find ( vpd, PCI_VPD_FIELD_RW, &rw_address, + &rw_len ) ) != 0 ) + goto err_no_rw; + + /* Locate old field, if any */ + if ( ( rc = pci_vpd_find ( vpd, field, &old_address, + &old_len ) ) == 0 ) { + + /* Field already exists */ + if ( old_address > rw_address ) { + DBGC ( vpd, PCI_FMT " VPD field " PCI_VPD_FIELD_FMT + " at [%04x,%04zx) is after field " + PCI_VPD_FIELD_FMT " at [%04x,%04zx)\n", + PCI_ARGS ( vpd->pci ), + PCI_VPD_FIELD_ARGS ( field ), + old_address, ( old_address + old_len ), + PCI_VPD_FIELD_ARGS ( PCI_VPD_FIELD_RW ), + rw_address, ( rw_address + rw_len ) ); + rc = -ENXIO; + goto err_after_rw; + } + dst_address = ( old_address - sizeof ( old_field ) ); + copy_address = ( old_address + old_len ); + copy_len = ( rw_address - sizeof ( rw_field ) - copy_address ); + + /* Calculate available length */ + available_len = ( rw_len + old_len ); + + } else { + + /* Field does not yet exist */ + dst_address = ( rw_address - sizeof ( rw_field ) ); + copy_address = dst_address; + copy_len = 0; + + /* Calculate available length */ + available_len = ( ( rw_len > sizeof ( new_field ) ) ? + ( rw_len - sizeof ( new_field ) ) : 0 ); + } + + /* Dump region before changes */ + dump_address = dst_address; + dump_len = ( rw_address + rw_len - dump_address ); + DBGC ( vpd, PCI_FMT " VPD before resizing field " PCI_VPD_FIELD_FMT + " to %zd bytes:\n", PCI_ARGS ( vpd->pci ), + PCI_VPD_FIELD_ARGS ( field ), len ); + pci_vpd_dump ( vpd, dump_address, dump_len ); + + /* Check available length */ + if ( available_len > PCI_VPD_MAX_LEN ) + available_len = PCI_VPD_MAX_LEN; + if ( len > available_len ) { + DBGC ( vpd, PCI_FMT " VPD no space for field " + PCI_VPD_FIELD_FMT " (need %02zx, have %02zx)\n", + PCI_ARGS ( vpd->pci ), PCI_VPD_FIELD_ARGS ( field ), + len, available_len ); + rc = -ENOSPC; + goto err_no_space; + } + + /* Preserve intermediate fields, if any */ + copy = malloc ( copy_len ); + if ( ! copy ) { + rc = -ENOMEM; + goto err_copy_alloc; + } + if ( ( rc = pci_vpd_read ( vpd, copy_address, copy, copy_len ) ) != 0 ) + goto err_copy_read; + + /* Create new field, if applicable */ + if ( len ) { + new_field.keyword = PCI_VPD_KEYWORD ( field ); + new_field.len = len; + if ( ( rc = pci_vpd_write ( vpd, dst_address, &new_field, + sizeof ( new_field ) ) ) != 0 ) + goto err_new_write; + dst_address += sizeof ( new_field ); + *address = dst_address; + DBGC ( vpd, PCI_FMT " VPD field " PCI_VPD_FIELD_FMT " is now " + "at [%04x,%04x)\n", PCI_ARGS ( vpd->pci ), + PCI_VPD_FIELD_ARGS ( field ), dst_address, + ( dst_address + new_field.len ) ); + dst_address += len; + } else { + DBGC ( vpd, PCI_FMT " VPD field " PCI_VPD_FIELD_FMT + " no longer exists\n", PCI_ARGS ( vpd->pci ), + PCI_VPD_FIELD_ARGS ( field ) ); + } + + /* Restore intermediate fields, if any */ + if ( ( rc = pci_vpd_write ( vpd, dst_address, copy, copy_len ) ) != 0 ) + goto err_copy_write; + dst_address += copy_len; + + /* Create 'RW' field */ + rw_field.keyword = PCI_VPD_KEYWORD ( PCI_VPD_FIELD_RW ); + rw_field.len = ( rw_len + + ( rw_address - sizeof ( rw_field ) ) - dst_address ); + if ( ( rc = pci_vpd_write ( vpd, dst_address, &rw_field, + sizeof ( rw_field ) ) ) != 0 ) + goto err_rw_write; + dst_address += sizeof ( rw_field ); + DBGC ( vpd, PCI_FMT " VPD field " PCI_VPD_FIELD_FMT " is now " + "at [%04x,%04x)\n", PCI_ARGS ( vpd->pci ), + PCI_VPD_FIELD_ARGS ( PCI_VPD_FIELD_RW ), dst_address, + ( dst_address + rw_field.len ) ); + + /* Dump region after changes */ + DBGC ( vpd, PCI_FMT " VPD after resizing field " PCI_VPD_FIELD_FMT + " to %zd bytes:\n", PCI_ARGS ( vpd->pci ), + PCI_VPD_FIELD_ARGS ( field ), len ); + pci_vpd_dump ( vpd, dump_address, dump_len ); + + rc = 0; + + err_rw_write: + err_new_write: + err_copy_write: + err_copy_read: + free ( copy ); + err_copy_alloc: + err_no_space: + err_after_rw: + err_no_rw: + return rc; +} diff --git a/src/drivers/infiniband/hermon.c b/src/drivers/infiniband/hermon.c index 63cf7d8c..6e3955af 100644 --- a/src/drivers/infiniband/hermon.c +++ b/src/drivers/infiniband/hermon.c @@ -2319,17 +2319,6 @@ static void hermon_eth_poll ( struct net_device *netdev ) { ib_poll_eq ( ibdev ); } -/** - * Enable/disable interrupts on Hermon Ethernet device - * - * @v netdev Network device - * @v enable Interrupts should be enabled - */ -static void hermon_eth_irq ( struct net_device *netdev __unused, - int enable __unused ) { - /* No implementation */ -} - /** * Open Hermon Ethernet device * @@ -2469,7 +2458,6 @@ static struct net_device_operations hermon_eth_operations = { .close = hermon_eth_close, .transmit = hermon_eth_transmit, .poll = hermon_eth_poll, - .irq = hermon_eth_irq, }; /** diff --git a/src/drivers/net/efi/snponly.c b/src/drivers/net/efi/snponly.c index 435ed4fb..6fcc54a0 100644 --- a/src/drivers/net/efi/snponly.c +++ b/src/drivers/net/efi/snponly.c @@ -114,13 +114,13 @@ struct root_device snp_root_device __root_device = { /** * Prepare for exit * - * @v flags Shutdown flags + * @v booting System is shutting down for OS boot */ -static void snponly_shutdown ( int flags ) { +static void snponly_shutdown ( int booting ) { /* If we are shutting down to boot an OS, make sure the SNP does not * stay active. */ - if ( flags & SHUTDOWN_BOOT ) + if ( booting ) snponly_dev.removal_state = EfiSimpleNetworkStopped; } diff --git a/src/drivers/net/etherfabric.c b/src/drivers/net/etherfabric.c index 352cdd99..836b85a9 100644 --- a/src/drivers/net/etherfabric.c +++ b/src/drivers/net/etherfabric.c @@ -1492,12 +1492,6 @@ fail1: return rc; } -/** Portion of EEPROM available for non-volatile options */ -static struct nvo_fragment falcon_nvo_fragments[] = { - { 0x100, 0xf0 }, - { 0, 0 } -}; - /******************************************************************************* * * @@ -3277,9 +3271,10 @@ falcon_probe_spi ( struct efab_nic *efab ) } /* If the device has EEPROM attached, then advertise NVO space */ - if ( has_eeprom ) - nvo_init ( &efab->nvo, &efab->spi_eeprom.nvs, falcon_nvo_fragments, - &efab->netdev->refcnt ); + if ( has_eeprom ) { + nvo_init ( &efab->nvo, &efab->spi_eeprom.nvs, 0x100, 0xf0, + NULL, &efab->netdev->refcnt ); + } return 0; } diff --git a/src/drivers/net/ipoib.c b/src/drivers/net/ipoib.c index 09c89bdc..4917b58e 100644 --- a/src/drivers/net/ipoib.c +++ b/src/drivers/net/ipoib.c @@ -537,17 +537,6 @@ static void ipoib_poll ( struct net_device *netdev ) { ib_poll_eq ( ibdev ); } -/** - * Enable/disable interrupts on IPoIB network device - * - * @v netdev Network device - * @v enable Interrupts should be enabled - */ -static void ipoib_irq ( struct net_device *netdev __unused, - int enable __unused ) { - /* No implementation */ -} - /** * Handle IPv4 broadcast multicast group join completion * @@ -730,7 +719,6 @@ static struct net_device_operations ipoib_operations = { .close = ipoib_close, .transmit = ipoib_transmit, .poll = ipoib_poll, - .irq = ipoib_irq, }; /** diff --git a/src/drivers/net/myri10ge.c b/src/drivers/net/myri10ge.c index cea2758f..c7b9dfa2 100644 --- a/src/drivers/net/myri10ge.c +++ b/src/drivers/net/myri10ge.c @@ -183,8 +183,8 @@ struct myri10ge_private */ struct nvs_device nvs; - struct nvo_fragment nvo_fragment[2]; struct nvo_block nvo; + unsigned int nvo_registered; /* Cached PCI capability locations. */ @@ -727,28 +727,21 @@ static int myri10ge_nv_init ( struct myri10ge_private *priv ) priv->nvs.read = myri10ge_nvs_read; priv->nvs.write = myri10ge_nvs_write; - /* Build the NonVolatile storage fragment list. We would like - to use the whole last EEPROM block for this, but we must - reduce the block size lest malloc fail in - src/core/nvo.o. */ - - priv->nvo_fragment[0].address = nvo_fragment_pos; - priv->nvo_fragment[0].len = 0x200; - /* Register the NonVolatile Options storage. */ nvo_init ( &priv->nvo, &priv->nvs, - priv->nvo_fragment, + nvo_fragment_pos, 0x200, + NULL, & myri10ge_netdev (priv) -> refcnt ); rc = register_nvo ( &priv->nvo, netdev_settings ( myri10ge_netdev ( priv ) ) ); if ( rc ) { DBG ("register_nvo failed"); - priv->nvo_fragment[0].len = 0; return rc; } + priv->nvo_registered = 1; DBG2 ( "NVO supported\n" ); return 0; } @@ -758,7 +751,7 @@ myri10ge_nv_fini ( struct myri10ge_private *priv ) { /* Simply return if nonvolatile access is not supported. */ - if ( 0 == priv->nvo_fragment[0].len ) + if ( 0 == priv->nvo_registered ) return; unregister_nvo ( &priv->nvo ); diff --git a/src/drivers/net/natsemi.c b/src/drivers/net/natsemi.c index 19c20edd..da2f0886 100644 --- a/src/drivers/net/natsemi.c +++ b/src/drivers/net/natsemi.c @@ -130,15 +130,6 @@ static struct bit_basher_operations natsemi_basher_ops = { .write = natsemi_spi_write_bit, }; -/* It looks that this portion of EEPROM can be used for - * non-volatile stored options. Data sheet does not talk about this region. - * Currently it is not working. But with some efforts it can. - */ -static struct nvo_fragment natsemi_nvo_fragments[] = { - { 0x0c, 0x68 }, - { 0, 0 } -}; - /* * Set up for EEPROM access * @@ -157,8 +148,13 @@ static void natsemi_init_eeprom ( struct natsemi_private *np ) { */ init_at93c46 ( &np->eeprom, 16 ); np->eeprom.bus = &np->spibit.bus; - np->nvo.nvs = &np->eeprom.nvs; - np->nvo.fragments = natsemi_nvo_fragments; + + /* It looks that this portion of EEPROM can be used for + * non-volatile stored options. Data sheet does not talk about + * this region. Currently it is not working. But with some + * efforts it can. + */ + nvo_init ( &np->nvo, &np->eeprom.nvs, 0x0c, 0x68, NULL, NULL ); } /** diff --git a/src/drivers/net/rtl8139.c b/src/drivers/net/rtl8139.c index 1ce1344e..7cc1de2f 100644 --- a/src/drivers/net/rtl8139.c +++ b/src/drivers/net/rtl8139.c @@ -251,17 +251,6 @@ static struct bit_basher_operations rtl_basher_ops = { .write = rtl_spi_write_bit, }; -/** Portion of EEPROM available for non-volatile stored options - * - * We use offset 0x40 (i.e. address 0x20), length 0x40. This block is - * marked as VPD in the rtl8139 datasheets, so we use it only if we - * detect that the card is not supporting VPD. - */ -static struct nvo_fragment rtl_nvo_fragments[] = { - { 0x20, 0x40 }, - { 0, 0 } -}; - /** * Set up for EEPROM access * @@ -288,13 +277,18 @@ static void rtl_init_eeprom ( struct net_device *netdev ) { } rtl->eeprom.bus = &rtl->spibit.bus; - /* Initialise space for non-volatile options, if available */ + /* Initialise space for non-volatile options, if available + * + * We use offset 0x40 (i.e. address 0x20), length 0x40. This + * block is marked as VPD in the rtl8139 datasheets, so we use + * it only if we detect that the card is not supporting VPD. + */ vpd = ( inw ( rtl->ioaddr + Config1 ) & VPDEnable ); if ( vpd ) { DBGC ( rtl, "rtl8139 %p EEPROM in use for VPD; cannot use " "for options\n", rtl ); } else { - nvo_init ( &rtl->nvo, &rtl->eeprom.nvs, rtl_nvo_fragments, + nvo_init ( &rtl->nvo, &rtl->eeprom.nvs, 0x20, 0x40, NULL, &netdev->refcnt ); } } diff --git a/src/drivers/net/vxge/vxge_config.c b/src/drivers/net/vxge/vxge_config.c index bfcd533b..ba62b508 100644 --- a/src/drivers/net/vxge/vxge_config.c +++ b/src/drivers/net/vxge/vxge_config.c @@ -17,6 +17,7 @@ FILE_LICENCE(GPL2_ONLY); #include #include #include +#include #include #include #include @@ -186,28 +187,47 @@ __vxge_hw_device_vpath_reset_in_prog_check(u64 __iomem *vpath_rst_in_prog) return status; } +/* + * __vxge_hw_device_get_legacy_reg + * This routine gets the legacy register section's memory mapped address + * and sets the swapper. + */ +static struct vxge_hw_legacy_reg __iomem * +__vxge_hw_device_get_legacy_reg(struct pci_device *pdev, void __iomem *bar0) +{ + enum vxge_hw_status status; + struct vxge_hw_legacy_reg __iomem *legacy_reg; + /* + * If the length of Bar0 is 16MB, then assume that we are configured + * in MF8P_VP2 mode and then add 8MB to the legacy_reg offsets + */ + if (pci_bar_size(pdev, PCI_BASE_ADDRESS_0) == 0x1000000) + legacy_reg = (struct vxge_hw_legacy_reg __iomem *) + (bar0 + 0x800000); + else + legacy_reg = (struct vxge_hw_legacy_reg __iomem *)bar0; + + status = __vxge_hw_legacy_swapper_set(legacy_reg); + if (status != VXGE_HW_OK) + return NULL; + + return legacy_reg; +} /* * __vxge_hw_device_toc_get * This routine sets the swapper and reads the toc pointer and returns the * memory mapped address of the toc */ struct vxge_hw_toc_reg __iomem * -__vxge_hw_device_toc_get(void __iomem *bar0) +__vxge_hw_device_toc_get(void __iomem *bar0, + struct vxge_hw_legacy_reg __iomem *legacy_reg) { u64 val64; struct vxge_hw_toc_reg __iomem *toc = NULL; - enum vxge_hw_status status; - - struct vxge_hw_legacy_reg __iomem *legacy_reg = - (struct vxge_hw_legacy_reg __iomem *)bar0; - - status = __vxge_hw_legacy_swapper_set(legacy_reg); - if (status != VXGE_HW_OK) - goto exit; val64 = readq(&legacy_reg->toc_first_pointer); toc = (struct vxge_hw_toc_reg __iomem *)(bar0+val64); -exit: + return toc; } @@ -224,9 +244,15 @@ __vxge_hw_device_reg_addr_get(struct __vxge_hw_device *hldev) u32 i; enum vxge_hw_status status = VXGE_HW_OK; - hldev->legacy_reg = (struct vxge_hw_legacy_reg __iomem *)hldev->bar0; + hldev->legacy_reg = __vxge_hw_device_get_legacy_reg(hldev->pdev, + hldev->bar0); + if (hldev->legacy_reg == NULL) { + status = VXGE_HW_FAIL; + goto exit; + } - hldev->toc_reg = __vxge_hw_device_toc_get(hldev->bar0); + hldev->toc_reg = __vxge_hw_device_toc_get(hldev->bar0, + hldev->legacy_reg); if (hldev->toc_reg == NULL) { status = VXGE_HW_FAIL; goto exit; @@ -234,7 +260,7 @@ __vxge_hw_device_reg_addr_get(struct __vxge_hw_device *hldev) val64 = readq(&hldev->toc_reg->toc_common_pointer); hldev->common_reg = - (struct vxge_hw_common_reg __iomem *)(hldev->bar0 + val64); + (struct vxge_hw_common_reg __iomem *)(hldev->bar0 + val64); val64 = readq(&hldev->toc_reg->toc_mrpcim_pointer); hldev->mrpcim_reg = @@ -355,7 +381,7 @@ void __vxge_hw_device_host_info_get(struct __vxge_hw_device *hldev) * each vpath */ enum vxge_hw_status -vxge_hw_device_hw_info_get(void __iomem *bar0, +vxge_hw_device_hw_info_get(struct pci_device *pdev, void __iomem *bar0, struct vxge_hw_device_hw_info *hw_info) { u32 i; @@ -365,13 +391,20 @@ vxge_hw_device_hw_info_get(void __iomem *bar0, struct vxge_hw_common_reg __iomem *common_reg; struct vxge_hw_vpath_reg __iomem *vpath_reg; struct vxge_hw_vpmgmt_reg __iomem *vpmgmt_reg; + struct vxge_hw_legacy_reg __iomem *legacy_reg; enum vxge_hw_status status; vxge_trace(); memset(hw_info, 0, sizeof(struct vxge_hw_device_hw_info)); - toc = __vxge_hw_device_toc_get(bar0); + legacy_reg = __vxge_hw_device_get_legacy_reg(pdev, bar0); + if (legacy_reg == NULL) { + status = VXGE_HW_ERR_CRITICAL; + goto exit; + } + + toc = __vxge_hw_device_toc_get(bar0, legacy_reg); if (toc == NULL) { status = VXGE_HW_ERR_CRITICAL; goto exit; diff --git a/src/drivers/net/vxge/vxge_config.h b/src/drivers/net/vxge/vxge_config.h index 3313123a..bf25134a 100644 --- a/src/drivers/net/vxge/vxge_config.h +++ b/src/drivers/net/vxge/vxge_config.h @@ -440,7 +440,7 @@ struct vxge_hw_device_hw_info { #define VXGE_HW_VH_NORMAL_FUNCTION 7 u64 function_mode; #define VXGE_HW_FUNCTION_MODE_MIN 0 -#define VXGE_HW_FUNCTION_MODE_MAX 10 +#define VXGE_HW_FUNCTION_MODE_MAX 11 #define VXGE_HW_FUNCTION_MODE_SINGLE_FUNCTION 0 #define VXGE_HW_FUNCTION_MODE_MULTI_FUNCTION 1 @@ -453,6 +453,7 @@ struct vxge_hw_device_hw_info { #define VXGE_HW_FUNCTION_MODE_MULTI_FUNCTION_2 8 #define VXGE_HW_FUNCTION_MODE_MULTI_FUNCTION_4 9 #define VXGE_HW_FUNCTION_MODE_MRIOV_4 10 +#define VXGE_HW_FUNCTION_MODE_MULTI_FUNCTION_DIRECT_IO 11 u32 func_id; u64 vpath_mask; @@ -607,6 +608,7 @@ void vxge_hw_ring_rxd_1b_set(struct vxge_hw_ring_rxd_1 *rxdp, } enum vxge_hw_status vxge_hw_device_hw_info_get( + struct pci_device *pdev, void __iomem *bar0, struct vxge_hw_device_hw_info *hw_info); @@ -727,12 +729,6 @@ __vxge_hw_pio_mem_write64(u64 val64, void __iomem *addr, return status; } -struct vxge_hw_toc_reg __iomem * -__vxge_hw_device_toc_get(void __iomem *bar0); - -enum vxge_hw_status -__vxge_hw_device_reg_addr_get(struct __vxge_hw_device *hldev); - void __vxge_hw_device_host_info_get(struct __vxge_hw_device *hldev); diff --git a/src/drivers/net/vxge/vxge_main.c b/src/drivers/net/vxge/vxge_main.c index 4e07271a..e2abafbf 100644 --- a/src/drivers/net/vxge/vxge_main.c +++ b/src/drivers/net/vxge/vxge_main.c @@ -45,6 +45,7 @@ static char *vxge_func_mode_names[] = { "Multi Function 2 - 2 func, 8 vpath per func", "Multi Function 4 - 4 func, 4 vpath per func", "WLPEX/SharedIO 4 - 17 func, 1 vpath per func (PCIe ARI)", + "Multi Function 8 - For ESX DirectIO - 8 func, 2 vpath per func", }; static inline int is_vxge_card_up(struct vxgedev *vdev) @@ -533,7 +534,7 @@ vxge_probe(struct pci_device *pdev, const struct pci_device_id *id __unused) goto _exit0; } - status = vxge_hw_device_hw_info_get(bar0, &hw_info); + status = vxge_hw_device_hw_info_get(pdev, bar0, &hw_info); if (status != VXGE_HW_OK) { vxge_debug(VXGE_ERR, "%s: Reading of hardware info failed.\n", diff --git a/src/drivers/net/vxge/vxge_version.h b/src/drivers/net/vxge/vxge_version.h index f1a02099..1475b77e 100644 --- a/src/drivers/net/vxge/vxge_version.h +++ b/src/drivers/net/vxge/vxge_version.h @@ -22,9 +22,9 @@ FILE_LICENCE(GPL2_ONLY); * Note: Each field must be a nibble size */ #define VXGE_VERSION_MAJOR 3 -#define VXGE_VERSION_MINOR 1 +#define VXGE_VERSION_MINOR 5 #define VXGE_VERSION_FIX 0 -#define VXGE_VERSION_BUILD 0 +#define VXGE_VERSION_BUILD 1 #define VXGE_FW_VER(major, minor, build) \ (((major) << 16) + ((minor) << 8) + (build)) diff --git a/src/drivers/nvs/nvs.c b/src/drivers/nvs/nvs.c index efa49ac5..a4a06ccf 100644 --- a/src/drivers/nvs/nvs.c +++ b/src/drivers/nvs/nvs.c @@ -30,6 +30,34 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ +/** + * Calculate length up to next block boundary + * + * @v nvs NVS device + * @v address Starting address + * @v max_len Maximum length + * @ret len Length to use, stopping at block boundaries + */ +static size_t nvs_frag_len ( struct nvs_device *nvs, unsigned int address, + size_t max_len ) { + size_t frag_len; + + /* If there are no block boundaries, return the maximum length */ + if ( ! nvs->block_size ) + return max_len; + + /* Calculate space remaining up to next block boundary */ + frag_len = ( ( nvs->block_size - + ( address & ( nvs->block_size - 1 ) ) ) + << nvs->word_len_log2 ); + + /* Limit to maximum length */ + if ( max_len < frag_len ) + return max_len; + + return frag_len; +} + /** * Read from non-volatile storage device * @@ -51,14 +79,8 @@ int nvs_read ( struct nvs_device *nvs, unsigned int address, while ( len ) { - /* Calculate space remaining up to next block boundary */ - frag_len = ( ( nvs->block_size - - ( address & ( nvs->block_size - 1 ) ) ) - << nvs->word_len_log2 ); - - /* Limit to space remaining in buffer */ - if ( frag_len > len ) - frag_len = len; + /* Calculate length to read, stopping at block boundaries */ + frag_len = nvs_frag_len ( nvs, address, len ); /* Read this portion of the buffer from the device */ if ( ( rc = nvs->read ( nvs, address, data, frag_len ) ) != 0 ) @@ -122,14 +144,8 @@ int nvs_write ( struct nvs_device *nvs, unsigned int address, while ( len ) { - /* Calculate space remaining up to next block boundary */ - frag_len = ( ( nvs->block_size - - ( address & ( nvs->block_size - 1 ) ) ) - << nvs->word_len_log2 ); - - /* Limit to space remaining in buffer */ - if ( frag_len > len ) - frag_len = len; + /* Calculate length to write, stopping at block boundaries */ + frag_len = nvs_frag_len ( nvs, address, len ); /* Write this portion of the buffer to the device */ if ( ( rc = nvs->write ( nvs, address, data, frag_len ) ) != 0) diff --git a/src/drivers/nvs/nvsvpd.c b/src/drivers/nvs/nvsvpd.c index 1f61a55d..a22ec825 100644 --- a/src/drivers/nvs/nvsvpd.c +++ b/src/drivers/nvs/nvsvpd.c @@ -19,9 +19,11 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include +#include #include #include #include +#include #include /** @file @@ -31,24 +33,50 @@ FILE_LICENCE ( GPL2_OR_LATER ); */ /** - * Read from VPD + * Read from VPD field * * @v nvs NVS device - * @v address Starting address - * @v buf Data buffer + * @v field VPD field descriptor + * @v data Data buffer * @v len Length of data buffer * @ret rc Return status code */ -static int nvs_vpd_read ( struct nvs_device *nvs, unsigned int address, +static int nvs_vpd_read ( struct nvs_device *nvs, unsigned int field, void *data, size_t len ) { struct nvs_vpd_device *nvsvpd = container_of ( nvs, struct nvs_vpd_device, nvs ); + struct pci_device *pci = nvsvpd->vpd.pci; + unsigned int address; + size_t max_len; int rc; - if ( ( rc = pci_vpd_read ( &nvsvpd->vpd, ( nvsvpd->address + address ), - data, len ) ) != 0 ) { - DBGC ( nvsvpd->vpd.pci, PCI_FMT " NVS could not read " - "[%04x,%04zx): %s\n", PCI_ARGS ( nvsvpd->vpd.pci ), + /* Allow reading non-existent field */ + if ( len == 0 ) + return 0; + + /* Locate VPD field */ + if ( ( rc = pci_vpd_find ( &nvsvpd->vpd, field, &address, + &max_len ) ) != 0 ) { + DBGC ( pci, PCI_FMT " NVS VPD could not locate field " + PCI_VPD_FIELD_FMT ": %s\n", PCI_ARGS ( pci ), + PCI_VPD_FIELD_ARGS ( field ), strerror ( rc ) ); + return rc; + } + + /* Sanity check */ + if ( len > max_len ) { + DBGC ( pci, PCI_FMT " NVS VPD cannot read %#02zx bytes " + "beyond field " PCI_VPD_FIELD_FMT " at [%04x,%04zx)\n", + PCI_ARGS ( pci ), len, PCI_VPD_FIELD_ARGS ( field ), + address, ( address + max_len ) ); + return -ENXIO; + } + + /* Read from VPD field */ + if ( ( rc = pci_vpd_read ( &nvsvpd->vpd, address, data, len ) ) != 0 ) { + DBGC ( pci, PCI_FMT " NVS VPD could not read field " + PCI_VPD_FIELD_FMT " at [%04x,%04zx): %s\n", + PCI_ARGS ( pci ), PCI_VPD_FIELD_ARGS ( field ), address, ( address + len ), strerror ( rc ) ); return rc; } @@ -57,24 +85,47 @@ static int nvs_vpd_read ( struct nvs_device *nvs, unsigned int address, } /** - * Write to VPD + * Write to VPD field * * @v nvs NVS device - * @v address Starting address - * @v buf Data buffer + * @v field VPD field descriptor + * @v data Data buffer * @v len Length of data buffer * @ret rc Return status code */ -static int nvs_vpd_write ( struct nvs_device *nvs, unsigned int address, +static int nvs_vpd_write ( struct nvs_device *nvs, unsigned int field, const void *data, size_t len ) { struct nvs_vpd_device *nvsvpd = container_of ( nvs, struct nvs_vpd_device, nvs ); + struct pci_device *pci = nvsvpd->vpd.pci; + unsigned int address; + size_t max_len; int rc; - if ( ( rc = pci_vpd_write ( &nvsvpd->vpd, ( nvsvpd->address + address ), - data, len ) ) != 0 ) { - DBGC ( nvsvpd->vpd.pci, PCI_FMT " NVS could not write " - "[%04x,%04zx): %s\n", PCI_ARGS ( nvsvpd->vpd.pci ), + /* Locate VPD field */ + if ( ( rc = pci_vpd_find ( &nvsvpd->vpd, field, &address, + &max_len ) ) != 0 ) { + DBGC ( pci, PCI_FMT " NVS VPD could not locate field " + PCI_VPD_FIELD_FMT ": %s\n", PCI_ARGS ( pci ), + PCI_VPD_FIELD_ARGS ( field ), strerror ( rc ) ); + return rc; + } + + /* Sanity check */ + if ( len > max_len ) { + DBGC ( pci, PCI_FMT " NVS VPD cannot write %#02zx bytes " + "beyond field " PCI_VPD_FIELD_FMT " at [%04x,%04zx)\n", + PCI_ARGS ( pci ), len, PCI_VPD_FIELD_ARGS ( field ), + address, ( address + max_len ) ); + return -ENXIO; + } + + /* Write field */ + if ( ( rc = pci_vpd_write ( &nvsvpd->vpd, address, data, + len ) ) != 0 ) { + DBGC ( pci, PCI_FMT " NVS VPD could not write field " + PCI_VPD_FIELD_FMT " at [%04x,%04zx): %s\n", + PCI_ARGS ( pci ), PCI_VPD_FIELD_ARGS ( field ), address, ( address + len ), strerror ( rc ) ); return rc; } @@ -82,6 +133,36 @@ static int nvs_vpd_write ( struct nvs_device *nvs, unsigned int address, return 0; } +/** + * Resize VPD field + * + * @v nvs NVS device + * @v field VPD field descriptor + * @v data Data buffer + * @v len Length of data buffer + * @ret rc Return status code + */ +static int nvs_vpd_resize ( struct nvs_device *nvs, unsigned int field, + size_t len ) { + struct nvs_vpd_device *nvsvpd = + container_of ( nvs, struct nvs_vpd_device, nvs ); + struct pci_device *pci = nvsvpd->vpd.pci; + unsigned int address; + int rc; + + /* Resize field */ + if ( ( rc = pci_vpd_resize ( &nvsvpd->vpd, field, len, + &address ) ) != 0 ) { + DBGC ( pci, PCI_FMT " NVS VPD could not resize field " + PCI_VPD_FIELD_FMT " to %#02zx bytes: %s\n", + PCI_ARGS ( pci ), PCI_VPD_FIELD_ARGS ( field ), + len, strerror ( rc ) ); + return rc; + } + + return 0; +} + /** * Initialise NVS VPD device * @@ -89,9 +170,7 @@ static int nvs_vpd_write ( struct nvs_device *nvs, unsigned int address, * @v pci PCI device * @ret rc Return status code */ -int nvs_vpd_init ( struct nvs_vpd_device *nvsvpd, struct pci_device *pci, - unsigned int field ) { - size_t len; +int nvs_vpd_init ( struct nvs_vpd_device *nvsvpd, struct pci_device *pci ) { int rc; /* Initialise VPD device */ @@ -101,24 +180,54 @@ int nvs_vpd_init ( struct nvs_vpd_device *nvsvpd, struct pci_device *pci, return rc; } - /* Locate VPD field */ - if ( ( rc = pci_vpd_find ( &nvsvpd->vpd, field, &nvsvpd->address, - &len ) ) != 0 ) { - DBGC ( pci, PCI_FMT " NVS could not locate VPD field " - PCI_VPD_FIELD_FMT ": %s\n", PCI_ARGS ( pci ), - PCI_VPD_FIELD_ARGS ( field ), strerror ( rc ) ); - return rc; - } - /* Initialise NVS device */ - nvsvpd->nvs.block_size = 1; - nvsvpd->nvs.size = len; nvsvpd->nvs.read = nvs_vpd_read; nvsvpd->nvs.write = nvs_vpd_write; - DBGC ( pci, PCI_FMT " NVS using VPD field " PCI_VPD_FIELD_FMT " at " - "[%04x,%04x)\n", PCI_ARGS ( pci ), PCI_VPD_FIELD_ARGS ( field ), - nvsvpd->address, ( nvsvpd->address + nvsvpd->nvs.size ) ); + return 0; +} + +/** + * Resize non-volatile option storage within NVS VPD device + * + * @v nvo Non-volatile options block + * @v len New length + * @ret rc Return status code + */ +static int nvs_vpd_nvo_resize ( struct nvo_block *nvo, size_t len ) { + int rc; + + /* Resize VPD field */ + if ( ( rc = nvs_vpd_resize ( nvo->nvs, nvo->address, len ) ) != 0 ) + return rc; return 0; } + +/** + * Initialise non-volatile option storage within NVS VPD device + * + * @v nvsvpd NVS VPD device + * @v field VPD field descriptor + * @v nvo Non-volatile options block + * @v refcnt Containing object reference counter, or NULL + */ +void nvs_vpd_nvo_init ( struct nvs_vpd_device *nvsvpd, unsigned int field, + struct nvo_block *nvo, struct refcnt *refcnt ) { + struct pci_device *pci = nvsvpd->vpd.pci; + unsigned int address; + size_t len; + int rc; + + /* Locate VPD field, if present */ + if ( ( rc = pci_vpd_find ( &nvsvpd->vpd, field, &address, + &len ) ) != 0 ) { + DBGC ( pci, PCI_FMT " NVS VPD field " PCI_VPD_FIELD_FMT + " not present; assuming empty\n", + PCI_ARGS ( pci ), PCI_VPD_FIELD_ARGS ( field ) ); + len = 0; + } + + /* Initialise non-volatile options block */ + nvo_init ( nvo, &nvsvpd->nvs, field, len, nvs_vpd_nvo_resize, refcnt ); +} diff --git a/src/hci/commands/sanboot_cmd.c b/src/hci/commands/sanboot_cmd.c index 1f11cc2d..61bc5463 100644 --- a/src/hci/commands/sanboot_cmd.c +++ b/src/hci/commands/sanboot_cmd.c @@ -18,9 +18,11 @@ #include #include +#include #include #include #include +#include #include FILE_LICENCE ( GPL2_OR_LATER ); @@ -52,23 +54,33 @@ static struct command_descriptor sanboot_cmd = static int sanboot_exec ( int argc, char **argv ) { struct sanboot_options opts; const char *root_path; + struct uri *uri; int rc; /* Parse options */ if ( ( rc = parse_options ( argc, argv, &sanboot_cmd, &opts ) ) != 0 ) - return rc; + goto err_parse_options; /* Parse root path */ root_path = argv[optind]; - - /* Boot from root path */ - if ( ( rc = boot_root_path ( root_path ) ) != 0 ) { - printf ( "Could not boot from %s: %s\n", - root_path, strerror ( rc ) ); - return rc; + uri = parse_uri ( root_path ); + if ( ! uri ) { + rc = -ENOMEM; + goto err_parse_uri; } - return 0; + /* Boot from root path */ + if ( ( rc = uriboot ( NULL, uri ) ) != 0 ) { + printf ( "Could not boot from %s: %s\n", + root_path, strerror ( rc ) ); + goto err_uriboot; + } + + err_uriboot: + uri_put ( uri ); + err_parse_uri: + err_parse_options: + return rc; } /** SAN commands */ diff --git a/src/image/efi_image.c b/src/image/efi_image.c index 6b6600de..bf2e6f4a 100644 --- a/src/image/efi_image.c +++ b/src/image/efi_image.c @@ -39,7 +39,7 @@ static EFI_EVENT efi_shutdown_event; */ static EFIAPI void efi_shutdown_hook ( EFI_EVENT event __unused, void *context __unused ) { - shutdown ( SHUTDOWN_BOOT ); + shutdown_boot(); } /** diff --git a/src/include/ipxe/device.h b/src/include/ipxe/device.h index 068268ba..635ce59c 100644 --- a/src/include/ipxe/device.h +++ b/src/include/ipxe/device.h @@ -112,6 +112,24 @@ struct root_driver { /** Declare a root device */ #define __root_device __table_entry ( ROOT_DEVICES, 01 ) +extern int device_keep_count; + +/** + * Prevent devices from being removed on shutdown + * + */ +static inline void devices_get ( void ) { + device_keep_count++; +} + +/** + * Allow devices to be removed on shutdown + * + */ +static inline void devices_put ( void ) { + device_keep_count--; +} + extern struct device * identify_device ( struct interface *intf ); #define identify_device_TYPE( object_type ) \ typeof ( struct device * ( object_type ) ) diff --git a/src/include/ipxe/dhcpopts.h b/src/include/ipxe/dhcpopts.h index fea99d59..8fb3d2d7 100644 --- a/src/include/ipxe/dhcpopts.h +++ b/src/include/ipxe/dhcpopts.h @@ -15,20 +15,28 @@ FILE_LICENCE ( GPL2_OR_LATER ); struct dhcp_options { /** Option block raw data */ void *data; - /** Option block length */ - size_t len; - /** Option block maximum length */ - size_t max_len; + /** Option block used length */ + size_t used_len; + /** Option block allocated length */ + size_t alloc_len; + /** Reallocate option block raw data + * + * @v options DHCP option block + * @v len New length + * @ret rc Return status code + */ + int ( * realloc ) ( struct dhcp_options *options, size_t len ); }; extern int dhcpopt_store ( struct dhcp_options *options, unsigned int tag, const void *data, size_t len ); -extern int dhcpopt_extensible_store ( struct dhcp_options *options, - unsigned int tag, - const void *data, size_t len ); extern int dhcpopt_fetch ( struct dhcp_options *options, unsigned int tag, void *data, size_t len ); extern void dhcpopt_init ( struct dhcp_options *options, - void *data, size_t max_len ); + void *data, size_t alloc_len, + int ( * realloc ) ( struct dhcp_options *options, + size_t len ) ); +extern void dhcpopt_update_used_len ( struct dhcp_options *options ); +extern int dhcpopt_no_realloc ( struct dhcp_options *options, size_t len ); #endif /* _IPXE_DHCPOPTS_H */ diff --git a/src/include/ipxe/dhcppkt.h b/src/include/ipxe/dhcppkt.h index 5709cc7d..3179a6bb 100644 --- a/src/include/ipxe/dhcppkt.h +++ b/src/include/ipxe/dhcppkt.h @@ -22,10 +22,6 @@ struct dhcp_packet { struct refcnt refcnt; /** The DHCP packet contents */ struct dhcphdr *dhcphdr; - /** Maximum length of the DHCP packet buffer */ - size_t max_len; - /** Used length of the DHCP packet buffer */ - size_t len; /** DHCP options */ struct dhcp_options options; /** Settings interface */ @@ -54,6 +50,17 @@ dhcppkt_put ( struct dhcp_packet *dhcppkt ) { ref_put ( &dhcppkt->refcnt ); } +/** + * Get used length of DHCP packet + * + * @v dhcppkt DHCP packet + * @ret len Used length + */ +static inline int dhcppkt_len ( struct dhcp_packet *dhcppkt ) { + return ( offsetof ( struct dhcphdr, options ) + + dhcppkt->options.used_len ); +} + extern int dhcppkt_store ( struct dhcp_packet *dhcppkt, unsigned int tag, const void *data, size_t len ); extern int dhcppkt_fetch ( struct dhcp_packet *dhcppkt, unsigned int tag, diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 2d0422e4..24d5b310 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -73,6 +73,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_spi ( ERRFILE_DRIVER | 0x00110000 ) #define ERRFILE_i2c_bit ( ERRFILE_DRIVER | 0x00120000 ) #define ERRFILE_spi_bit ( ERRFILE_DRIVER | 0x00130000 ) +#define ERRFILE_nvsvpd ( ERRFILE_DRIVER | 0x00140000 ) #define ERRFILE_3c509 ( ERRFILE_DRIVER | 0x00200000 ) #define ERRFILE_bnx2 ( ERRFILE_DRIVER | 0x00210000 ) @@ -234,6 +235,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_ifmgmt_cmd ( ERRFILE_OTHER | 0x001d0000 ) #define ERRFILE_fcmgmt_cmd ( ERRFILE_OTHER | 0x001e0000 ) #define ERRFILE_gdbstub_cmd ( ERRFILE_OTHER | 0x001f0000 ) +#define ERRFILE_sanboot_cmd ( ERRFILE_OTHER | 0x00200000 ) /** @} */ diff --git a/src/include/ipxe/init.h b/src/include/ipxe/init.h index 10ff8695..954cda45 100644 --- a/src/include/ipxe/init.h +++ b/src/include/ipxe/init.h @@ -32,16 +32,6 @@ struct init_fn { /** @} */ -/** Shutdown flags */ -enum shutdown_flags { - /** Shutdown is in order to exit (return to iPXE's caller) */ - SHUTDOWN_EXIT = 0x0001, - /** Shutdown is in order to boot an OS */ - SHUTDOWN_BOOT = 0x0002, - /** Do not remove devices */ - SHUTDOWN_KEEP_DEVICES = 0x0004, -}; - /** * A startup/shutdown function * @@ -50,7 +40,7 @@ enum shutdown_flags { */ struct startup_fn { void ( * startup ) ( void ); - void ( * shutdown ) ( int flags ); + void ( * shutdown ) ( int booting ); }; /** Startup/shutdown function table */ @@ -76,6 +66,22 @@ struct startup_fn { extern void initialise ( void ); extern void startup ( void ); -extern void shutdown ( int flags ); +extern void shutdown ( int booting ); + +/** + * Shut down system for OS boot + * + */ +static inline void shutdown_boot ( void ) { + shutdown ( 1 ); +} + +/** + * Shut down system for exit back to firmware + * + */ +static inline void shutdown_exit ( void ) { + shutdown ( 0 ); +} #endif /* _IPXE_INIT_H */ diff --git a/src/include/ipxe/netdevice.h b/src/include/ipxe/netdevice.h index a7852a81..ac7cec52 100644 --- a/src/include/ipxe/netdevice.h +++ b/src/include/ipxe/netdevice.h @@ -236,6 +236,9 @@ struct net_device_operations { * * @v netdev Network device * @v enable Interrupts should be enabled + * + * This method may be NULL to indicate that interrupts are not + * supported. */ void ( * irq ) ( struct net_device *netdev, int enable ); }; @@ -515,6 +518,17 @@ netdev_is_open ( struct net_device *netdev ) { return ( netdev->state & NETDEV_OPEN ); } +/** + * Check whether or not network device supports interrupts + * + * @v netdev Network device + * @ret irq_supported Network device supports interrupts + */ +static inline __attribute__ (( always_inline )) int +netdev_irq_supported ( struct net_device *netdev ) { + return ( netdev->op->irq != NULL ); +} + /** * Check whether or not network device interrupts are currently enabled * diff --git a/src/include/ipxe/nvo.h b/src/include/ipxe/nvo.h index 0a0222b3..995afd74 100644 --- a/src/include/ipxe/nvo.h +++ b/src/include/ipxe/nvo.h @@ -16,16 +16,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); struct nvs_device; struct refcnt; -/** - * A fragment of a non-volatile storage device used for stored options - */ -struct nvo_fragment { - /** Starting address of fragment within NVS device */ - unsigned int address; - /** Length of fragment */ - size_t len; -}; - /** * A block of non-volatile stored options */ @@ -34,21 +24,28 @@ struct nvo_block { struct settings settings; /** Underlying non-volatile storage device */ struct nvs_device *nvs; - /** List of option-containing fragments - * - * The list is terminated by a fragment with a length of zero. - */ - struct nvo_fragment *fragments; - /** Total length of option-containing fragments */ - size_t total_len; + /** Address within NVS device */ + unsigned int address; + /** Length of options data */ + size_t len; /** Option-containing data */ void *data; + /** + * Resize non-volatile stored option block + * + * @v nvo Non-volatile options block + * @v len New size + * @ret rc Return status code + */ + int ( * resize ) ( struct nvo_block *nvo, size_t len ); /** DHCP options block */ struct dhcp_options dhcpopts; }; extern void nvo_init ( struct nvo_block *nvo, struct nvs_device *nvs, - struct nvo_fragment *fragments, struct refcnt *refcnt ); + size_t address, size_t len, + int ( * resize ) ( struct nvo_block *nvo, size_t len ), + struct refcnt *refcnt ); extern int register_nvo ( struct nvo_block *nvo, struct settings *parent ); extern void unregister_nvo ( struct nvo_block *nvo ); diff --git a/src/include/ipxe/nvsvpd.h b/src/include/ipxe/nvsvpd.h index 5f80844e..3450e5c7 100644 --- a/src/include/ipxe/nvsvpd.h +++ b/src/include/ipxe/nvsvpd.h @@ -13,21 +13,21 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include +struct nvo_block; +struct refcnt; + /** An NVS VPD device */ struct nvs_vpd_device { /** NVS device */ struct nvs_device nvs; /** PCI VPD device */ struct pci_vpd vpd; - /** Starting address - * - * This address is added to the NVS address to form the VPD - * address. - */ - unsigned int address; }; -extern int nvs_vpd_init ( struct nvs_vpd_device *nvsvpd, struct pci_device *pci, - unsigned int field ); +extern int nvs_vpd_init ( struct nvs_vpd_device *nvsvpd, + struct pci_device *pci ); +extern void nvs_vpd_nvo_init ( struct nvs_vpd_device *nvsvpd, + unsigned int field, struct nvo_block *nvo, + struct refcnt *refcnt ); #endif /* IPXE_NVSVPD_H */ diff --git a/src/include/ipxe/open.h b/src/include/ipxe/open.h index c000990c..a522f0cd 100644 --- a/src/include/ipxe/open.h +++ b/src/include/ipxe/open.h @@ -89,6 +89,7 @@ struct socket_opener { /** Register a socket opener */ #define __socket_opener __table_entry ( SOCKET_OPENERS, 01 ) +extern struct uri_opener * xfer_uri_opener ( const char *scheme ); extern int xfer_open_uri ( struct interface *intf, struct uri *uri ); extern int xfer_open_uri_string ( struct interface *intf, const char *uri_string ); diff --git a/src/include/ipxe/pcivpd.h b/src/include/ipxe/pcivpd.h index 8b973ea1..0abf8a95 100644 --- a/src/include/ipxe/pcivpd.h +++ b/src/include/ipxe/pcivpd.h @@ -32,6 +32,9 @@ struct pci_vpd_field { uint8_t len; } __attribute__ (( packed )); +/** Maximum PCI VPD field length */ +#define PCI_VPD_MAX_LEN 0xff + /** Construct PCI VPD field descriptor * * @v tag ISAPnP tag @@ -133,6 +136,17 @@ struct pci_vpd { struct pci_vpd_cache cache; }; +/** + * Check for presence of PCI VPD + * + * @v vpd PCI VPD + * @ret is_present VPD is present + */ +static inline __attribute__ (( always_inline )) int +pci_vpd_is_present ( struct pci_vpd *vpd ) { + return ( vpd->cap != 0 ); +} + /** * Check if PCI VPD read cache is valid * @@ -161,5 +175,7 @@ extern int pci_vpd_write ( struct pci_vpd *vpd, unsigned int address, const void *buf, size_t len ); extern int pci_vpd_find ( struct pci_vpd *vpd, unsigned int field, unsigned int *address, size_t *len ); +extern int pci_vpd_resize ( struct pci_vpd *vpd, unsigned int field, + size_t len, unsigned int *address ); #endif /* _IPXE_PCIVPD_H */ diff --git a/src/include/ipxe/settings.h b/src/include/ipxe/settings.h index a764bf0e..d99e5ec0 100644 --- a/src/include/ipxe/settings.h +++ b/src/include/ipxe/settings.h @@ -221,6 +221,7 @@ extern int storef_setting ( struct settings *settings, const char *value ); extern int storef_named_setting ( const char *name, const char *value ); extern int fetchf_named_setting ( const char *name, char *buf, size_t len ); +extern char * expand_settings ( const char *string ); extern struct setting_type setting_type_string __setting_type; extern struct setting_type setting_type_ipv4 __setting_type; diff --git a/src/include/usr/autoboot.h b/src/include/usr/autoboot.h index e9abf833..a608b3dc 100644 --- a/src/include/usr/autoboot.h +++ b/src/include/usr/autoboot.h @@ -11,14 +11,14 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include struct net_device; +struct uri; +struct settings; -extern int shutdown_exit_flags; - +extern int uriboot ( struct uri *filename, struct uri *root_path ); +extern struct uri * +fetch_next_server_and_filename ( struct settings *settings ); extern int netboot ( struct net_device *netdev ); extern int autoboot ( void ); -extern int boot_next_server_and_filename ( struct in_addr next_server, - const char *filename ); -extern int boot_root_path ( const char *root_path ); extern int pxe_menu_boot ( struct net_device *netdev ); diff --git a/src/include/usr/imgmgmt.h b/src/include/usr/imgmgmt.h index 0c8c8cf7..0beab513 100644 --- a/src/include/usr/imgmgmt.h +++ b/src/include/usr/imgmgmt.h @@ -11,6 +11,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); struct image; +extern int imgdownload ( struct image *image, struct uri *uri, + int ( * image_register ) ( struct image *image ) ); extern int imgfetch ( struct image *image, const char *uri_string, int ( * image_register ) ( struct image *image ) ); extern int imgload ( struct image *image ); diff --git a/src/net/cachedhcp.c b/src/net/cachedhcp.c index 294624c8..36f480a9 100644 --- a/src/net/cachedhcp.c +++ b/src/net/cachedhcp.c @@ -58,7 +58,7 @@ void store_cached_dhcpack ( userptr_t data, size_t len ) { dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( * dhcppkt ) ); copy_from_user ( dhcphdr, data, 0, len ); dhcppkt_init ( dhcppkt, dhcphdr, len ); - DBG_HD ( dhcppkt->options.data, dhcppkt->options.len ); + DBG_HD ( dhcppkt->options.data, dhcppkt->options.used_len ); /* Register settings on the last opened network device. * This will have the effect of registering cached settings diff --git a/src/net/dhcpopts.c b/src/net/dhcpopts.c index f351c32e..f04b8e71 100644 --- a/src/net/dhcpopts.c +++ b/src/net/dhcpopts.c @@ -117,7 +117,7 @@ static int find_dhcp_option_with_encap ( struct dhcp_options *options, unsigned int original_tag __attribute__ (( unused )) = tag; struct dhcp_option *option; int offset = 0; - ssize_t remaining = options->len; + ssize_t remaining = options->used_len; unsigned int option_len; /* Sanity check */ @@ -169,6 +169,17 @@ static int find_dhcp_option_with_encap ( struct dhcp_options *options, return -ENOENT; } +/** + * Refuse to reallocate DHCP option block + * + * @v options DHCP option block + * @v len New length + * @ret rc Return status code + */ +int dhcpopt_no_realloc ( struct dhcp_options *options, size_t len ) { + return ( ( len <= options->alloc_len ) ? 0 : -ENOSPC ); +} + /** * Resize a DHCP option * @@ -177,46 +188,44 @@ static int find_dhcp_option_with_encap ( struct dhcp_options *options, * @v encap_offset Offset of encapsulating offset (or -ve for none) * @v old_len Old length (including header) * @v new_len New length (including header) - * @v can_realloc Can reallocate options data if necessary * @ret rc Return status code */ static int resize_dhcp_option ( struct dhcp_options *options, int offset, int encap_offset, - size_t old_len, size_t new_len, - int can_realloc ) { + size_t old_len, size_t new_len ) { struct dhcp_option *encapsulator; struct dhcp_option *option; ssize_t delta = ( new_len - old_len ); - size_t new_options_len; + size_t old_alloc_len; + size_t new_used_len; size_t new_encapsulator_len; - void *new_data; void *source; void *dest; void *end; + int rc; - /* Check for sufficient space, and update length fields */ + /* Check for sufficient space */ if ( new_len > DHCP_MAX_LEN ) { DBGC ( options, "DHCPOPT %p overlength option\n", options ); return -ENOSPC; } - new_options_len = ( options->len + delta ); - if ( new_options_len > options->max_len ) { - /* Reallocate options block if allowed to do so. */ - if ( can_realloc ) { - new_data = realloc ( options->data, new_options_len ); - if ( ! new_data ) { - DBGC ( options, "DHCPOPT %p could not " - "reallocate to %zd bytes\n", options, - new_options_len ); - return -ENOMEM; - } - options->data = new_data; - options->max_len = new_options_len; - } else { - DBGC ( options, "DHCPOPT %p out of space\n", options ); - return -ENOMEM; + new_used_len = ( options->used_len + delta ); + + /* Expand options block, if necessary */ + if ( new_used_len > options->alloc_len ) { + /* Reallocate options block */ + old_alloc_len = options->alloc_len; + if ( ( rc = options->realloc ( options, new_used_len ) ) != 0 ){ + DBGC ( options, "DHCPOPT %p could not reallocate to " + "%zd bytes\n", options, new_used_len ); + return rc; } + /* Clear newly allocated space */ + memset ( ( options->data + old_alloc_len ), 0, + ( options->alloc_len - old_alloc_len ) ); } + + /* Update encapsulator, if applicable */ if ( encap_offset >= 0 ) { encapsulator = dhcp_option ( options, encap_offset ); new_encapsulator_len = ( encapsulator->len + delta ); @@ -227,15 +236,26 @@ static int resize_dhcp_option ( struct dhcp_options *options, } encapsulator->len = new_encapsulator_len; } - options->len = new_options_len; + + /* Update used length */ + options->used_len = new_used_len; /* Move remainder of option data */ option = dhcp_option ( options, offset ); source = ( ( ( void * ) option ) + old_len ); dest = ( ( ( void * ) option ) + new_len ); - end = ( options->data + options->max_len ); + end = ( options->data + options->alloc_len ); memmove ( dest, source, ( end - dest ) ); + /* Shrink options block, if applicable */ + if ( new_used_len < options->alloc_len ) { + if ( ( rc = options->realloc ( options, new_used_len ) ) != 0 ){ + DBGC ( options, "DHCPOPT %p could not reallocate to " + "%zd bytes\n", options, new_used_len ); + return rc; + } + } + return 0; } @@ -246,7 +266,6 @@ static int resize_dhcp_option ( struct dhcp_options *options, * @v tag DHCP option tag * @v data New value for DHCP option * @v len Length of value, in bytes - * @v can_realloc Can reallocate options data if necessary * @ret offset Offset of DHCP option, or negative error * * Sets the value of a DHCP option within the options block. The @@ -258,9 +277,8 @@ static int resize_dhcp_option ( struct dhcp_options *options, * be left with its original value. */ static int set_dhcp_option ( struct dhcp_options *options, unsigned int tag, - const void *data, size_t len, - int can_realloc ) { - static const uint8_t empty_encapsulator[] = { DHCP_END }; + const void *data, size_t len ) { + static const uint8_t empty_encap[] = { DHCP_END }; int offset; int encap_offset = -1; int creation_offset; @@ -277,7 +295,7 @@ static int set_dhcp_option ( struct dhcp_options *options, unsigned int tag, creation_offset = find_dhcp_option_with_encap ( options, DHCP_END, NULL ); if ( creation_offset < 0 ) - creation_offset = options->len; + creation_offset = options->used_len; /* Find old instance of this option, if any */ offset = find_dhcp_option_with_encap ( options, tag, &encap_offset ); if ( offset >= 0 ) { @@ -291,10 +309,12 @@ static int set_dhcp_option ( struct dhcp_options *options, unsigned int tag, /* Ensure that encapsulator exists, if required */ if ( encap_tag ) { - if ( encap_offset < 0 ) - encap_offset = set_dhcp_option ( options, encap_tag, - empty_encapsulator, 1, - can_realloc ); + if ( encap_offset < 0 ) { + encap_offset = + set_dhcp_option ( options, encap_tag, + empty_encap, + sizeof ( empty_encap ) ); + } if ( encap_offset < 0 ) return encap_offset; creation_offset = ( encap_offset + DHCP_OPTION_HEADER_LEN ); @@ -306,8 +326,7 @@ static int set_dhcp_option ( struct dhcp_options *options, unsigned int tag, /* Resize option to fit new data */ if ( ( rc = resize_dhcp_option ( options, offset, encap_offset, - old_len, new_len, - can_realloc ) ) != 0 ) + old_len, new_len ) ) != 0 ) return rc; /* Copy new data into option, if applicable */ @@ -322,7 +341,7 @@ static int set_dhcp_option ( struct dhcp_options *options, unsigned int tag, if ( encap_offset >= 0 ) { option = dhcp_option ( options, encap_offset ); if ( option->len <= 1 ) - set_dhcp_option ( options, encap_tag, NULL, 0, 0 ); + set_dhcp_option ( options, encap_tag, NULL, 0 ); } return offset; @@ -341,26 +360,7 @@ int dhcpopt_store ( struct dhcp_options *options, unsigned int tag, const void *data, size_t len ) { int offset; - offset = set_dhcp_option ( options, tag, data, len, 0 ); - if ( offset < 0 ) - return offset; - return 0; -} - -/** - * Store value of DHCP option setting, extending options block if necessary - * - * @v options DHCP option block - * @v tag Setting tag number - * @v data Setting data, or NULL to clear setting - * @v len Length of setting data - * @ret rc Return status code - */ -int dhcpopt_extensible_store ( struct dhcp_options *options, unsigned int tag, - const void *data, size_t len ) { - int offset; - - offset = set_dhcp_option ( options, tag, data, len, 1 ); + offset = set_dhcp_option ( options, tag, data, len ); if ( offset < 0 ) return offset; return 0; @@ -402,14 +402,14 @@ int dhcpopt_fetch ( struct dhcp_options *options, unsigned int tag, * The "used length" field will be updated based on scanning through * the block to find the end of the options. */ -static void dhcpopt_update_len ( struct dhcp_options *options ) { +void dhcpopt_update_used_len ( struct dhcp_options *options ) { struct dhcp_option *option; int offset = 0; - ssize_t remaining = options->max_len; + ssize_t remaining = options->alloc_len; unsigned int option_len; /* Find last non-pad option */ - options->len = 0; + options->used_len = 0; while ( remaining ) { option = dhcp_option ( options, offset ); option_len = dhcp_option_len ( option ); @@ -418,7 +418,7 @@ static void dhcpopt_update_len ( struct dhcp_options *options ) { break; offset += option_len; if ( option->tag != DHCP_PAD ) - options->len = offset; + options->used_len = offset; } } @@ -427,21 +427,24 @@ static void dhcpopt_update_len ( struct dhcp_options *options ) { * * @v options Uninitialised DHCP option block * @v data Memory for DHCP option data - * @v max_len Length of memory for DHCP option data + * @v alloc_len Length of memory for DHCP option data + * @v realloc DHCP option block reallocator * * The memory content must already be filled with valid DHCP options. * A zeroed block counts as a block of valid DHCP options. */ -void dhcpopt_init ( struct dhcp_options *options, void *data, - size_t max_len ) { +void dhcpopt_init ( struct dhcp_options *options, void *data, size_t alloc_len, + int ( * realloc ) ( struct dhcp_options *options, + size_t len ) ) { /* Fill in fields */ options->data = data; - options->max_len = max_len; + options->alloc_len = alloc_len; + options->realloc = realloc; /* Update length */ - dhcpopt_update_len ( options ); + dhcpopt_update_used_len ( options ); - DBGC ( options, "DHCPOPT %p created (data %p len %#zx max_len %#zx)\n", - options, options->data, options->len, options->max_len ); + DBGC ( options, "DHCPOPT %p created (data %p lengths %#zx,%#zx)\n", + options, options->data, options->used_len, options->alloc_len ); } diff --git a/src/net/dhcppkt.c b/src/net/dhcppkt.c index b68f4e08..237c3e2c 100644 --- a/src/net/dhcppkt.c +++ b/src/net/dhcppkt.c @@ -147,7 +147,6 @@ int dhcppkt_store ( struct dhcp_packet *dhcppkt, unsigned int tag, const void *data, size_t len ) { struct dhcp_packet_field *field; void *field_data; - int rc; /* If this is a special field, fill it in */ if ( ( field = find_dhcp_packet_field ( tag ) ) != NULL ) { @@ -163,13 +162,7 @@ int dhcppkt_store ( struct dhcp_packet *dhcppkt, unsigned int tag, } /* Otherwise, use the generic options block */ - rc = dhcpopt_store ( &dhcppkt->options, tag, data, len ); - - /* Update our used-length field */ - dhcppkt->len = ( offsetof ( struct dhcphdr, options ) + - dhcppkt->options.len ); - - return rc; + return dhcpopt_store ( &dhcppkt->options, tag, data, len ); } /** @@ -273,11 +266,9 @@ void dhcppkt_init ( struct dhcp_packet *dhcppkt, struct dhcphdr *data, size_t len ) { ref_init ( &dhcppkt->refcnt, NULL ); dhcppkt->dhcphdr = data; - dhcppkt->max_len = len; dhcpopt_init ( &dhcppkt->options, &dhcppkt->dhcphdr->options, - ( len - offsetof ( struct dhcphdr, options ) ) ); - dhcppkt->len = ( offsetof ( struct dhcphdr, options ) + - dhcppkt->options.len ); + ( len - offsetof ( struct dhcphdr, options ) ), + dhcpopt_no_realloc ); settings_init ( &dhcppkt->settings, &dhcppkt_settings_operations, &dhcppkt->refcnt, 0 ); } diff --git a/src/net/netdevice.c b/src/net/netdevice.c index c7e907ad..81382376 100644 --- a/src/net/netdevice.c +++ b/src/net/netdevice.c @@ -542,6 +542,10 @@ void unregister_netdev ( struct net_device *netdev ) { */ void netdev_irq ( struct net_device *netdev, int enable ) { + /* Do nothing if device does not support interrupts */ + if ( ! netdev_irq_supported ( netdev ) ) + return; + /* Enable or disable device interrupts */ netdev->op->irq ( netdev, enable ); diff --git a/src/net/udp/dhcp.c b/src/net/udp/dhcp.c index 64c49cdd..e6d127ab 100644 --- a/src/net/udp/dhcp.c +++ b/src/net/udp/dhcp.c @@ -1122,7 +1122,7 @@ static int dhcp_tx ( struct dhcp_session *dhcp ) { } /* Transmit the packet */ - iob_put ( iobuf, dhcppkt.len ); + iob_put ( iobuf, dhcppkt_len ( &dhcppkt ) ); if ( ( rc = xfer_deliver ( &dhcp->xfer, iob_disown ( iobuf ), &meta ) ) != 0 ) { DBGC ( dhcp, "DHCP %p could not transmit UDP packet: %s\n", diff --git a/src/usr/autoboot.c b/src/usr/autoboot.c index 738c3cea..91f1c6e8 100644 --- a/src/usr/autoboot.c +++ b/src/usr/autoboot.c @@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include #include #include @@ -40,9 +41,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ -/** Shutdown flags for exit */ -int shutdown_exit_flags = 0; - /** * Perform PXE menu boot when PXE stack is not available */ @@ -60,30 +58,21 @@ static struct net_device * find_boot_netdev ( void ) { } /** - * Boot using next-server and filename + * Parse next-server and filename into a URI * - * @v filename Boot filename - * @ret rc Return status code + * @v next_server Next-server address + * @v filename Filename + * @ret uri URI, or NULL on failure */ -int boot_next_server_and_filename ( struct in_addr next_server, - const char *filename ) { +static struct uri * parse_next_server_and_filename ( struct in_addr next_server, + const char *filename ) { struct uri *uri; - struct image *image; - char buf[ 23 /* tftp://xxx.xxx.xxx.xxx/ */ + - ( 3 * strlen(filename) ) /* completely URI-encoded */ - + 1 /* NUL */ ]; - int filename_is_absolute; - int rc; + struct uri *tmp; - /* Construct URI */ + /* Parse filename */ uri = parse_uri ( filename ); - if ( ! uri ) { - printf ( "Could not parse \"%s\"\n", filename ); - rc = -ENOMEM; - goto err_parse_uri; - } - filename_is_absolute = uri_is_absolute ( uri ); - uri_put ( uri ); + if ( ! uri ) + return NULL; /* Construct a tftp:// URI for the filename, if applicable. * We can't just rely on the current working URI, because the @@ -91,41 +80,17 @@ int boot_next_server_and_filename ( struct in_addr next_server, * filenames with and without initial slashes, which is * significant for TFTP. */ - if ( ! filename_is_absolute ) { - snprintf ( buf, sizeof ( buf ), "tftp://%s/", - inet_ntoa ( next_server ) ); - uri_encode ( filename, buf + strlen ( buf ), - sizeof ( buf ) - strlen ( buf ), URI_PATH ); - filename = buf; + if ( ! uri_is_absolute ( uri ) ) { + tmp = uri; + tmp->scheme = "tftp"; + tmp->host = inet_ntoa ( next_server ); + uri = uri_dup ( tmp ); + uri_put ( tmp ); + if ( ! uri ) + return NULL; } - /* Download and boot image */ - image = alloc_image(); - if ( ! image ) { - printf ( "Could not allocate image\n" ); - rc = -ENOMEM; - goto err_alloc_image; - } - if ( ( rc = imgfetch ( image, filename, - register_and_autoload_image ) ) != 0 ) { - printf ( "Could not fetch image: %s\n", strerror ( rc ) ); - goto err_imgfetch; - } - if ( ( rc = imgexec ( image ) ) != 0 ) { - printf ( "Could not execute image: %s\n", strerror ( rc ) ); - goto err_imgexec; - } - - /* Drop image reference */ - image_put ( image ); - return 0; - - err_imgexec: - err_imgfetch: - image_put ( image ); - err_alloc_image: - err_parse_uri: - return rc; + return uri; } /** The "keep-san" setting */ @@ -145,72 +110,99 @@ struct setting skip_san_boot_setting __setting = { }; /** - * Boot using root path + * Boot from filename and root-path URIs * + * @v filename Filename * @v root_path Root path * @ret rc Return status code */ -int boot_root_path ( const char *root_path ) { - struct uri *uri; +int uriboot ( struct uri *filename, struct uri *root_path ) { + struct image *image; int drive; int rc; - /* Parse URI */ - uri = parse_uri ( root_path ); - if ( ! uri ) { - printf ( "Could not parse \"%s\"\n", root_path ); + /* Allocate image */ + image = alloc_image(); + if ( ! image ) { + printf ( "Could not allocate image\n" ); rc = -ENOMEM; - goto err_parse_uri; + goto err_alloc_image; } - /* Hook SAN device */ - if ( ( drive = san_hook ( uri, 0 ) ) < 0 ) { - rc = drive; - printf ( "Could not open SAN device: %s\n", - strerror ( rc ) ); - goto err_open; - } - printf ( "Registered as SAN device %#02x\n", drive ); + /* Treat empty URIs as absent */ + if ( filename && ( ! filename->path ) ) + filename = NULL; + if ( root_path && ( ! uri_is_absolute ( root_path ) ) ) + root_path = NULL; - /* Describe SAN device */ - if ( ( rc = san_describe ( drive ) ) != 0 ) { + /* If we have both a filename and a root path, ignore an + * unsupported URI scheme in the root path, since it may + * represent an NFS root. + */ + if ( filename && root_path && + ( xfer_uri_opener ( root_path->scheme ) == NULL ) ) { + printf ( "Ignoring unsupported root path\n" ); + root_path = NULL; + } + + /* Hook SAN device, if applicable */ + if ( root_path ) { + drive = san_hook ( root_path, 0 ); + if ( drive < 0 ) { + rc = drive; + printf ( "Could not open SAN device: %s\n", + strerror ( rc ) ); + goto err_san_hook; + } + printf ( "Registered as SAN device %#02x\n", drive ); + } else { + drive = -ENODEV; + } + + /* Describe SAN device, if applicable */ + if ( ( drive >= 0 ) && ( ( rc = san_describe ( drive ) ) != 0 ) ) { printf ( "Could not describe SAN device %#02x: %s\n", drive, strerror ( rc ) ); - goto err_describe; + goto err_san_describe; } - /* Boot from SAN device */ - if ( fetch_intz_setting ( NULL, &skip_san_boot_setting) != 0 ) { - printf ( "Skipping boot from SAN device %#02x\n", drive ); + /* Attempt filename or SAN boot as applicable */ + if ( filename ) { + if ( ( rc = imgdownload ( image, filename, + register_and_autoexec_image ) ) !=0){ + printf ( "Could not chain image: %s\n", + strerror ( rc ) ); + } + } else if ( root_path ) { + if ( fetch_intz_setting ( NULL, &skip_san_boot_setting) == 0 ) { + printf ( "Booting from SAN device %#02x\n", drive ); + rc = san_boot ( drive ); + printf ( "Boot from SAN device %#02x failed: %s\n", + drive, strerror ( rc ) ); + } else { + printf ( "Skipping boot from SAN device %#02x\n", + drive ); + rc = 0; + } } else { - printf ( "Booting from SAN device %#02x\n", drive ); - rc = san_boot ( drive ); - printf ( "Boot from SAN device %#02x failed: %s\n", - drive, strerror ( rc ) ); + printf ( "No filename or root path specified\n" ); + rc = -ENOENT; } - /* Leave drive registered, if instructed to do so */ - if ( fetch_intz_setting ( NULL, &keep_san_setting ) != 0 ) { - printf ( "Preserving connection to SAN device %#02x\n", - drive ); - shutdown_exit_flags |= SHUTDOWN_KEEP_DEVICES; - goto err_keep_san; + err_san_describe: + /* Unhook SAN device, if applicable */ + if ( drive >= 0 ) { + if ( fetch_intz_setting ( NULL, &keep_san_setting ) == 0 ) { + printf ( "Unregistering SAN device %#02x\n", drive ); + san_unhook ( drive ); + } else { + printf ( "Preserving connection to SAN device %#02x\n", + drive ); + } } - - /* Unhook SAN deivce */ - printf ( "Unregistering SAN device %#02x\n", drive ); - san_unhook ( drive ); - - /* Drop URI reference */ - uri_put ( uri ); - - return 0; - - err_keep_san: - err_describe: - err_open: - uri_put ( uri ); - err_parse_uri: + err_san_hook: + image_put ( image ); + err_alloc_image: return rc; } @@ -231,12 +223,75 @@ static void close_all_netdevs ( void ) { } /** - * Boot from a network device + * Fetch next-server and filename settings into a URI * - * @v netdev Network device - * @ret rc Return status code + * @v settings Settings block + * @ret uri URI, or NULL on failure */ -int netboot ( struct net_device *netdev ) { +struct uri * fetch_next_server_and_filename ( struct settings *settings ) { + struct in_addr next_server; + char buf[256]; + char *filename; + struct uri *uri; + + /* Fetch next-server setting */ + fetch_ipv4_setting ( settings, &next_server_setting, &next_server ); + if ( next_server.s_addr ) + printf ( "Next server: %s\n", inet_ntoa ( next_server ) ); + + /* Fetch filename setting */ + fetch_string_setting ( settings, &filename_setting, + buf, sizeof ( buf ) ); + if ( buf[0] ) + printf ( "Filename: %s\n", buf ); + + /* Expand filename setting */ + filename = expand_settings ( buf ); + if ( ! filename ) + return NULL; + + /* Parse next server and filename */ + uri = parse_next_server_and_filename ( next_server, filename ); + + free ( filename ); + return uri; +} + +/** + * Fetch root-path setting into a URI + * + * @v settings Settings block + * @ret uri URI, or NULL on failure + */ +static struct uri * fetch_root_path ( struct settings *settings ) { + char buf[256]; + char *root_path; + struct uri *uri; + + /* Fetch root-path setting */ + fetch_string_setting ( settings, &root_path_setting, + buf, sizeof ( buf ) ); + if ( buf[0] ) + printf ( "Root path: %s\n", buf ); + + /* Expand filename setting */ + root_path = expand_settings ( buf ); + if ( ! root_path ) + return NULL; + + /* Parse root path */ + uri = parse_uri ( root_path ); + + free ( root_path ); + return uri; +} + +/** + * Check whether or not we have a usable PXE menu + * + * @ret have_menu A usable PXE menu is present + */ +static int have_pxe_menu ( void ) { struct setting vendor_class_id_setting = { .tag = DHCP_VENDOR_CLASS_ID }; struct setting pxe_discovery_control_setting @@ -244,8 +299,28 @@ int netboot ( struct net_device *netdev ) { struct setting pxe_boot_menu_setting = { .tag = DHCP_PXE_BOOT_MENU }; char buf[256]; - struct in_addr next_server; unsigned int pxe_discovery_control; + + fetch_string_setting ( NULL, &vendor_class_id_setting, + buf, sizeof ( buf ) ); + pxe_discovery_control = + fetch_uintz_setting ( NULL, &pxe_discovery_control_setting ); + + return ( ( strcmp ( buf, "PXEClient" ) == 0 ) && + setting_exists ( NULL, &pxe_boot_menu_setting ) && + ( ! ( ( pxe_discovery_control & PXEBS_SKIP ) && + setting_exists ( NULL, &filename_setting ) ) ) ); +} + +/** + * Boot from a network device + * + * @v netdev Network device + * @ret rc Return status code + */ +int netboot ( struct net_device *netdev ) { + struct uri *filename; + struct uri *root_path; int rc; /* Close all other network devices */ @@ -253,44 +328,42 @@ int netboot ( struct net_device *netdev ) { /* Open device and display device status */ if ( ( rc = ifopen ( netdev ) ) != 0 ) - return rc; + goto err_ifopen; ifstat ( netdev ); /* Configure device via DHCP */ if ( ( rc = dhcp ( netdev ) ) != 0 ) - return rc; + goto err_dhcp; route(); /* Try PXE menu boot, if applicable */ - fetch_string_setting ( NULL, &vendor_class_id_setting, - buf, sizeof ( buf ) ); - pxe_discovery_control = - fetch_uintz_setting ( NULL, &pxe_discovery_control_setting ); - if ( ( strcmp ( buf, "PXEClient" ) == 0 ) && - setting_exists ( NULL, &pxe_boot_menu_setting ) && - ( ! ( ( pxe_discovery_control & PXEBS_SKIP ) && - setting_exists ( NULL, &filename_setting ) ) ) ) { + if ( have_pxe_menu() ) { printf ( "Booting from PXE menu\n" ); - return pxe_menu_boot ( netdev ); + rc = pxe_menu_boot ( netdev ); + goto err_pxe_menu_boot; } - /* Try to download and boot whatever we are given as a filename */ - fetch_ipv4_setting ( NULL, &next_server_setting, &next_server ); - fetch_string_setting ( NULL, &filename_setting, buf, sizeof ( buf ) ); - if ( buf[0] ) { - printf ( "Booting from filename \"%s\"\n", buf ); - return boot_next_server_and_filename ( next_server, buf ); - } - - /* No filename; try the root path */ - fetch_string_setting ( NULL, &root_path_setting, buf, sizeof ( buf ) ); - if ( buf[0] ) { - printf ( "Booting from root path \"%s\"\n", buf ); - return boot_root_path ( buf ); - } + /* Fetch next server, filename and root path */ + filename = fetch_next_server_and_filename ( NULL ); + if ( ! filename ) + goto err_filename; + root_path = fetch_root_path ( NULL ); + if ( ! root_path ) + goto err_root_path; - printf ( "No filename or root path specified\n" ); - return -ENOENT; + /* Boot using next server, filename and root path */ + if ( ( rc = uriboot ( filename, root_path ) ) != 0 ) + goto err_uriboot; + + err_uriboot: + uri_put ( root_path ); + err_root_path: + uri_put ( filename ); + err_filename: + err_pxe_menu_boot: + err_dhcp: + err_ifopen: + return rc; } /** diff --git a/src/usr/imgmgmt.c b/src/usr/imgmgmt.c index 5e7629a6..e958bc19 100644 --- a/src/usr/imgmgmt.c +++ b/src/usr/imgmgmt.c @@ -36,24 +36,21 @@ FILE_LICENCE ( GPL2_OR_LATER ); */ /** - * Fetch an image + * Download an image * - * @v uri_string URI as a string (e.g. "http://www.nowhere.com/vmlinuz") - * @v name Name for image, or NULL - * @v register_image Image registration routine + * @v image Image + * @v uri URI + * @v image_register Action to take upon a successful download * @ret rc Return status code */ -int imgfetch ( struct image *image, const char *uri_string, - int ( * image_register ) ( struct image *image ) ) { - char uri_string_redacted[ strlen ( uri_string ) + 3 /* "***" */ - + 1 /* NUL */ ]; - struct uri *uri; +int imgdownload ( struct image *image, struct uri *uri, + int ( * image_register ) ( struct image *image ) ) { + size_t len = ( unparse_uri ( NULL, 0, uri, URI_ALL ) + 1 ); + char uri_string_redacted[len]; const char *password; int rc; - if ( ! ( uri = parse_uri ( uri_string ) ) ) - return -ENOMEM; - + /* Set image URI */ image_set_uri ( image, uri ); /* Redact password portion of URI, if necessary */ @@ -64,9 +61,35 @@ int imgfetch ( struct image *image, const char *uri_string, uri, URI_ALL ); uri->password = password; + /* Create downloader */ if ( ( rc = create_downloader ( &monojob, image, image_register, - LOCATION_URI, uri ) ) == 0 ) - rc = monojob_wait ( uri_string_redacted ); + LOCATION_URI, uri ) ) != 0 ) + return rc; + + /* Wait for download to complete */ + if ( ( rc = monojob_wait ( uri_string_redacted ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Fetch an image + * + * @v image Image + * @v uri_string URI as a string (e.g. "http://www.nowhere.com/vmlinuz") + * @v image_register Action to take upon a successful fetch + * @ret rc Return status code + */ +int imgfetch ( struct image *image, const char *uri_string, + int ( * image_register ) ( struct image *image ) ) { + struct uri *uri; + int rc; + + if ( ! ( uri = parse_uri ( uri_string ) ) ) + return -ENOMEM; + + rc = imgdownload ( image, uri, image_register ); uri_put ( uri ); return rc; diff --git a/src/usr/pxemenu.c b/src/usr/pxemenu.c index bbd7670c..a9ea1f04 100644 --- a/src/usr/pxemenu.c +++ b/src/usr/pxemenu.c @@ -31,6 +31,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include #include @@ -346,8 +347,7 @@ int pxe_menu_boot ( struct net_device *netdev ) { struct pxe_menu *menu; unsigned int pxe_type; struct settings *pxebs_settings; - struct in_addr next_server; - char filename[256]; + struct uri *uri; int rc; /* Parse and allocate boot menu */ @@ -372,12 +372,15 @@ int pxe_menu_boot ( struct net_device *netdev ) { if ( ( rc = pxebs ( netdev, pxe_type ) ) != 0 ) return rc; - /* Attempt boot */ + /* Fetch next server and filename */ pxebs_settings = find_settings ( PXEBS_SETTINGS_NAME ); assert ( pxebs_settings ); - fetch_ipv4_setting ( pxebs_settings, &next_server_setting, - &next_server ); - fetch_string_setting ( pxebs_settings, &filename_setting, - filename, sizeof ( filename ) ); - return boot_next_server_and_filename ( next_server, filename ); + uri = fetch_next_server_and_filename ( pxebs_settings ); + if ( ! uri ) + return -ENOMEM; + + /* Attempt boot */ + rc = uriboot ( uri, NULL ); + uri_put ( uri ); + return rc; }