diff options
102 files changed, 3803 insertions, 982 deletions
diff --git a/gpxe/contrib/bochs/README b/gpxe/contrib/bochs/README index 57c6f052..e0e88032 100644 --- a/gpxe/contrib/bochs/README +++ b/gpxe/contrib/bochs/README @@ -25,9 +25,10 @@ To get bochs running is fairly simple: 3. Configure bochs with pushd bochs - ./configure --enable-all-optimisations --enable-pci --enable-pnic \ - --enable-debugger --enable-magic-breakpoints \ - --enable-disasm --enable-show-ips --enable-ne2000 + ./configure --enable-all-optimisations --enable-show-ips \ + --enable-cpu-level=6 \ + --enable-pci --enable-pnic --enable-ne2000 \ + --enable-debugger --enable-disasm popd 4. Build bochs: diff --git a/gpxe/contrib/bochs/README.qemu b/gpxe/contrib/bochs/README.qemu index ee20eec4..2582f62b 100644 --- a/gpxe/contrib/bochs/README.qemu +++ b/gpxe/contrib/bochs/README.qemu @@ -58,7 +58,6 @@ To get qemu running is fairly simple: popd 9. Start qemu - export SDL_VIDEO_X11_DGAMOUSE=0 ./qemu/i386-softmmu/qemu -L qemu/pc-bios \ -net nic,model=rtl8139 -net tap,ifname=tap0 \ -boot a -fda ../../src/bin/rtl8139.pdsk diff --git a/gpxe/contrib/bochs/serial-console b/gpxe/contrib/bochs/serial-console index cc4fd005..8cd3835c 100755 --- a/gpxe/contrib/bochs/serial-console +++ b/gpxe/contrib/bochs/serial-console @@ -224,8 +224,8 @@ if ( -t STDIN ) { $restore_termios = POSIX::Termios->new; $termios->getattr ( fileno(STDIN) ); $restore_termios->getattr ( fileno(STDIN) ); - $termios->setlflag ( $termios->getlflag & - ~(ICANON) & ~(ECHO) ); + $termios->setlflag ( $termios->getlflag & ~(ICANON) & ~(ECHO) ); + $termios->setiflag ( $termios->getiflag & ~(ICRNL) ); $termios->setattr ( fileno(STDIN), TCSANOW ); } diff --git a/gpxe/src/Makefile b/gpxe/src/Makefile index c30bf2b3..474e8cfd 100644 --- a/gpxe/src/Makefile +++ b/gpxe/src/Makefile @@ -146,6 +146,7 @@ SRCDIRS += image SRCDIRS += drivers/bus SRCDIRS += drivers/net SRCDIRS += drivers/net/e1000 +SRCDIRS += drivers/net/phantom SRCDIRS += drivers/block SRCDIRS += drivers/nvs SRCDIRS += drivers/bitbash diff --git a/gpxe/src/Makefile.housekeeping b/gpxe/src/Makefile.housekeeping index 6cd85f7d..bbbfc899 100644 --- a/gpxe/src/Makefile.housekeeping +++ b/gpxe/src/Makefile.housekeeping @@ -205,7 +205,7 @@ $(BIN)/deps/%.d : % $(MAKEDEPS) $(PARSEROM) # Calculate and include the list of Makefile rules files # AUTO_DEPS = $(patsubst %,$(BIN)/deps/%.d,$(AUTO_SRCS)) -include $(AUTO_DEPS) +-include $(AUTO_DEPS) autodeps : @$(ECHO) $(AUTO_DEPS) VERYCLEANUP += $(BIN)/deps @@ -459,7 +459,7 @@ $(BIN)/deps/%.media.d : $(MAKEDEPS) MEDIA_DEPS = $(patsubst %,$(BIN)/deps/%.media.d,$(AUTO_MEDIA)) mediadeps : @$(ECHO) $(MEDIA_DEPS) -include $(MEDIA_DEPS) +-include $(MEDIA_DEPS) # The "allXXXs" targets for each suffix # diff --git a/gpxe/src/README.cvs b/gpxe/src/README.cvs deleted file mode 100644 index 56a24f9f..00000000 --- a/gpxe/src/README.cvs +++ /dev/null @@ -1,65 +0,0 @@ -Changes should be committed to the CVS HEAD only when they are in a -working state. The definition of "working" is somewhat liquid; a good -guiding principle is that anyone checking out HEAD should receive a -checkout of working software. - -When you want to work on changes that are likely to temporarily break -large swathes of code, you should probably work on a private branch. -Since CVS branching and merging is something of a black art, here are -some simple step-by-step instructions for creating and using a branch. - -To create your private branch: - - # Get most up-to-date tree before branching - cvs update - # Create a branch called "my-branch" - cvs tag -b my-branch - # Switch working copy to the "my-branch" branch - cvs update -r my-branch - -At this point you'll be on a branch called "my-branch". Any changes -you make will not affect people working on HEAD, or on other branches. - -Use name for your branch that is both descriptive and unique. -Starting the branch name with your SourceForge username -(e.g. "mcb30-realmode-redesign") is a good idea. - -When you want to merge the changes on your branch back into HEAD, do -the following: - - # Ensure there are no not-yet-checked-in modifications in your tree) - cvs -q update - # Tag the merge point in the "my-branch" branch - cvs tag -c my-branch-merge-1 - # Switch working copy back to HEAD - cvs update -A - # Merge changes from the branch - cvs update -j my-branch - # Commit merged changes to HEAD - cvs commit - -If you then want to continue working further on the "my-branch" branch, -do the following - - # Switch working copy back to the "my-branch" branch - cvs update -r my-branch - -and then when you want to merge some more changes back to HEAD: - - # Ensure there are no not-yet-checked-in modifications in your tree) - cvs -q update - # Tag the merge point in the "my-branch" branch - cvs tag -c my-branch-merge-2 - # Switch working copy back to HEAD - cvs update -A - # Merge changes from the branch - cvs update -j my-branch-merge-1 -j my-branch - # Commit merged changes to HEAD - cvs commit - -Note that the format of the "merge changes from the branch" command has -changed, because this time you need to only merge changes since the last -merge point. - -When you have finished with your branch and merged all the changes -back to HEAD, simply stop using the branch. diff --git a/gpxe/src/arch/i386/Makefile b/gpxe/src/arch/i386/Makefile index 926daa1a..97ca0774 100644 --- a/gpxe/src/arch/i386/Makefile +++ b/gpxe/src/arch/i386/Makefile @@ -11,6 +11,7 @@ SRCDIRS += arch/i386/drivers SRCDIRS += arch/i386/drivers/net SRCDIRS += arch/i386/interface/pcbios SRCDIRS += arch/i386/interface/pxe +SRCDIRS += arch/i386/interface/syslinux # The various xxx_loader.c files are #included into core/loader.c and # should not be compiled directly. diff --git a/gpxe/src/arch/i386/core/umalloc.c b/gpxe/src/arch/i386/core/umalloc.c index bfd62ef1..3990488c 100644 --- a/gpxe/src/arch/i386/core/umalloc.c +++ b/gpxe/src/arch/i386/core/umalloc.c @@ -194,8 +194,8 @@ userptr_t urealloc ( userptr_t ptr, size_t new_size ) { /* Collect any free blocks and update hidden memory region */ ecollect_free(); - hide_region ( EXTMEM, user_to_phys ( bottom, -sizeof ( extmem ) ), - user_to_phys ( top, 0 ) ); + hide_umalloc ( user_to_phys ( bottom, -sizeof ( extmem ) ), + user_to_phys ( top, 0 ) ); return ( new_size ? new : UNOWHERE ); } diff --git a/gpxe/src/arch/i386/drivers/net/undiload.c b/gpxe/src/arch/i386/drivers/net/undiload.c index a3284f80..dbd9e7c2 100644 --- a/gpxe/src/arch/i386/drivers/net/undiload.c +++ b/gpxe/src/arch/i386/drivers/net/undiload.c @@ -93,8 +93,8 @@ int undi_load ( struct undi_device *undi, struct undi_rom *undirom ) { "lcall *%c2\n\t" "addw $4, %%sp\n\t" ) : "=a" ( exit ) - : "a" ( & __from_data16 ( undi_loader ) ), - "p" ( & __from_data16 ( undi_loader_entry ) ) + : "a" ( __from_data16 ( &undi_loader ) ), + "p" ( __from_data16 ( &undi_loader_entry ) ) : "ebx", "ecx", "edx", "esi", "edi", "ebp" ); /* UNDI API calls may rudely change the status of A20 and not diff --git a/gpxe/src/arch/i386/drivers/net/undinet.c b/gpxe/src/arch/i386/drivers/net/undinet.c index b1e8cc5a..9576ad60 100644 --- a/gpxe/src/arch/i386/drivers/net/undinet.c +++ b/gpxe/src/arch/i386/drivers/net/undinet.c @@ -176,9 +176,9 @@ static int undinet_call ( struct undi_nic *undinic, unsigned int function, "addw $6, %%sp\n\t" ) : "=a" ( exit ), "=b" ( discard_b ), "=D" ( discard_D ) - : "p" ( &__from_data16 ( undinet_entry_point )), + : "p" ( __from_data16 ( &undinet_entry_point )), "b" ( function ), - "D" ( &__from_data16 ( undinet_params ) ) + "D" ( __from_data16 ( &undinet_params ) ) : "ecx", "edx", "esi", "ebp" ); /* UNDI API calls may rudely change the status of A20 and not @@ -211,7 +211,7 @@ static int undinet_call ( struct undi_nic *undinic, unsigned int function, if ( rc != 0 ) { SEGOFF16_t rm_params = { .segment = rm_ds, - .offset = (intptr_t) &__from_data16 ( undinet_params ), + .offset = __from_data16 ( &undinet_params ), }; DBGC ( undinic, "UNDINIC %p %s failed: %s\n", undinic, @@ -357,17 +357,14 @@ static int undinet_transmit ( struct net_device *netdev, /* Create PXENV_UNDI_TRANSMIT data structure */ memset ( &undi_transmit, 0, sizeof ( undi_transmit ) ); undi_transmit.DestAddr.segment = rm_ds; - undi_transmit.DestAddr.offset - = ( ( unsigned ) & __from_data16 ( undinet_tbd ) ); + undi_transmit.DestAddr.offset = __from_data16 ( &undinet_tbd ); undi_transmit.TBD.segment = rm_ds; - undi_transmit.TBD.offset - = ( ( unsigned ) & __from_data16 ( undinet_tbd ) ); + undi_transmit.TBD.offset = __from_data16 ( &undinet_tbd ); /* Create PXENV_UNDI_TBD data structure */ undinet_tbd.ImmedLength = len; undinet_tbd.Xmit.segment = rm_ds; - undinet_tbd.Xmit.offset - = ( ( unsigned ) & __from_data16 ( basemem_packet ) ); + undinet_tbd.Xmit.offset = __from_data16 ( basemem_packet ); /* Issue PXE API call */ if ( ( rc = undinet_call ( undinic, PXENV_UNDI_TRANSMIT, diff --git a/gpxe/src/arch/i386/firmware/pcbios/e820mangler.S b/gpxe/src/arch/i386/firmware/pcbios/e820mangler.S index e9328041..3c4cf21b 100644 --- a/gpxe/src/arch/i386/firmware/pcbios/e820mangler.S +++ b/gpxe/src/arch/i386/firmware/pcbios/e820mangler.S @@ -26,159 +26,82 @@ #define SMAP 0x534d4150 /**************************************************************************** - * Check for overlap * - * Parameters: - * %edx:%eax Region start - * %ecx:%ebx Region end - * %si Pointer to hidden region descriptor - * Returns: - * CF set Region overlaps - * CF clear No overlap - **************************************************************************** - */ - .section ".text16" -check_overlap: - /* If start >= hidden_end, there is no overlap. */ - testl %edx, %edx - jnz no_overlap - cmpl 4(%si), %eax - jae no_overlap - /* If end <= hidden_start, there is no overlap; equivalently, - * if end > hidden_start, there is overlap. - */ - testl %ecx, %ecx - jnz overlap - cmpl 0(%si), %ebx - ja overlap -no_overlap: - clc - ret -overlap: - stc - ret - .size check_overlap, . - check_overlap - -/**************************************************************************** - * Check for overflow/underflow + * Allowed memory windows * - * Parameters: - * %edx:%eax Region start - * %ecx:%ebx Region end - * Returns: - * CF set start < end - * CF clear start >= end - **************************************************************************** - */ - .section ".text16" -check_overflow: - pushl %ecx - pushl %ebx - subl %eax, %ebx - sbbl %edx, %ecx - popl %ebx - popl %ecx - ret - .size check_overflow, . - check_overflow - -/**************************************************************************** - * Truncate towards start of region + * There are two ways to view this list. The first is as a list of + * (non-overlapping) allowed memory regions, sorted by increasing + * address. The second is as a list of (non-overlapping) hidden + * memory regions, again sorted by increasing address. The second + * view is offset by half an entry from the first: think about this + * for a moment and it should make sense. * - * Parameters: - * %edx:%eax Region start - * %ecx:%ebx Region end - * %si Pointer to hidden region descriptor - * Returns: - * %edx:%eax Modified region start - * %ecx:%ebx Modified region end - * CF set Region was truncated - * CF clear Region was not truncated - **************************************************************************** - */ - .section ".text16" -truncate_to_start: - /* If overlaps, set region end = hidden region start */ - call check_overlap - jnc 99f - movl 0(%si), %ebx - xorl %ecx, %ecx - /* If region end < region start, set region end = region start */ - call check_overflow - jnc 1f - movl %eax, %ebx - movl %edx, %ecx -1: stc -99: ret - .size truncate_to_start, . - truncate_to_start - -/**************************************************************************** - * Truncate towards end of region + * xxx_memory_window is used to indicate an "allowed region" + * structure, hidden_xxx_memory is used to indicate a "hidden region" + * structure. Each structure is 16 bytes in length. * - * Parameters: - * %edx:%eax Region start - * %ecx:%ebx Region end - * %si Pointer to hidden region descriptor - * Returns: - * %edx:%eax Modified region start - * %ecx:%ebx Modified region end - * CF set Region was truncated - * CF clear Region was not truncated **************************************************************************** */ - .section ".text16" -truncate_to_end: - /* If overlaps, set region start = hidden region end */ - call check_overlap - jnc 99f - movl 4(%si), %eax - xorl %edx, %edx - /* If region start > region end, set region start = region end */ - call check_overflow - jnc 1f - movl %ebx, %eax - movl %ecx, %edx -1: stc -99: ret - .size truncate_to_end, . - truncate_to_end - + .section ".data16" + .align 16 + .globl hidemem_base + .globl hidemem_umalloc + .globl hidemem_text +memory_windows: +base_memory_window: .long 0x00000000, 0x00000000 /* Start of memory */ + +hidemem_base: .long 0x000a0000, 0x00000000 /* Changes at runtime */ +ext_memory_window: .long 0x000a0000, 0x00000000 /* 640kB mark */ + +hidemem_umalloc: .long 0xffffffff, 0xffffffff /* Changes at runtime */ + .long 0xffffffff, 0xffffffff /* Changes at runtime */ + +hidemem_text: .long 0xffffffff, 0xffffffff /* Changes at runtime */ + .long 0xffffffff, 0xffffffff /* Changes at runtime */ + + .long 0xffffffff, 0xffffffff /* End of memory */ +memory_windows_end: + /**************************************************************************** - * Truncate region + * Truncate region to memory window * * Parameters: - * %edx:%eax Region start - * %ecx:%ebx Region length (*not* region end) - * %bp truncate_to_start or truncate_to_end + * %edx:%eax Start of region + * %ecx:%ebx Length of region + * %si Memory window * Returns: - * %edx:%eax Modified region start - * %ecx:%ebx Modified region length - * CF set Region was truncated - * CF clear Region was not truncated + * %edx:%eax Start of windowed region + * %ecx:%ebx Length of windowed region **************************************************************************** */ .section ".text16" -truncate: - pushw %si - pushfw - /* Convert (start,len) to (start,end) */ +window_region: + /* Convert (start,len) to (start, end) */ addl %eax, %ebx adcl %edx, %ecx - /* Hide all hidden regions, truncating as directed */ - movw $hidden_regions, %si -1: call *%bp - jnc 2f - popfw /* If CF was set, set stored CF in flags word on stack */ - stc - pushfw -2: addw $8, %si - cmpl $0, 0(%si) - jne 1b - /* Convert modified (start,end) back to (start,len) */ + /* Truncate to window start */ + cmpl 4(%si), %edx + jne 1f + cmpl 0(%si), %eax +1: jae 2f + movl 4(%si), %edx + movl 0(%si), %eax +2: /* Truncate to window end */ + cmpl 12(%si), %ecx + jne 1f + cmpl 8(%si), %ebx +1: jbe 2f + movl 12(%si), %ecx + movl 8(%si), %ebx +2: /* Convert (start, end) back to (start, len) */ subl %eax, %ebx sbbl %edx, %ecx - popfw - popw %si + /* If length is <0, set length to 0 */ + jae 1f + xorl %ebx, %ebx + xorl %ecx, %ecx ret - .size truncate, . - truncate + .size window_region, . - window_region /**************************************************************************** * Patch "memory above 1MB" figure @@ -187,21 +110,19 @@ truncate: * %ax Memory above 1MB, in 1kB blocks * Returns: * %ax Modified memory above 1M in 1kB blocks - * CF set Region was truncated - * CF clear Region was not truncated **************************************************************************** */ .section ".text16" patch_1m: pushal /* Convert to (start,len) format and call truncate */ - movw $truncate_to_start, %bp xorl %ecx, %ecx movzwl %ax, %ebx shll $10, %ebx xorl %edx, %edx movl $0x100000, %eax - call truncate + movw $ext_memory_window, %si + call window_region /* Convert back to "memory above 1MB" format and return via %ax */ pushfw shrl $10, %ebx @@ -219,20 +140,18 @@ patch_1m: * %bx Memory above 16MB, in 64kB blocks * Returns: * %bx Modified memory above 16M in 64kB blocks - * CF set Region was truncated - * CF clear Region was not truncated **************************************************************************** */ .section ".text16" patch_16m: pushal /* Convert to (start,len) format and call truncate */ - movw $truncate_to_start, %bp xorl %ecx, %ecx shll $16, %ebx xorl %edx, %edx movl $0x1000000, %eax - call truncate + movw $ext_memory_window, %si + call window_region /* Convert back to "memory above 16MB" format and return via %bx */ pushfw shrl $16, %ebx @@ -252,103 +171,299 @@ patch_16m: * Returns: * %ax Modified memory between 1MB and 16MB, in 1kB blocks * %bx Modified memory above 16MB, in 64kB blocks - * CF set Region was truncated - * CF clear Region was not truncated **************************************************************************** */ .section ".text16" patch_1m_16m: call patch_1m - jc 1f call patch_16m - ret -1: /* 1m region was truncated; kill the 16m region */ + /* If 1M region is no longer full-length, kill off the 16M region */ + cmpw $( 15 * 1024 ), %ax + je 1f xorw %bx, %bx - ret +1: ret .size patch_1m_16m, . - patch_1m_16m /**************************************************************************** - * Patch E820 memory map entry + * Get underlying e820 memory region to underlying_e820 buffer * * Parameters: - * %es:di Pointer to E820 memory map descriptor - * %bp truncate_to_start or truncate_to_end + * As for INT 15,e820 * Returns: - * %es:di Pointer to now-modified E820 memory map descriptor - * CF set Region was truncated - * CF clear Region was not truncated + * As for INT 15,e820 + * + * Wraps the underlying INT 15,e820 call so that the continuation + * value (%ebx) is a 16-bit simple sequence counter (with the high 16 + * bits ignored), and termination is always via CF=1 rather than + * %ebx=0. + * **************************************************************************** */ .section ".text16" -patch_e820: +get_underlying_e820: + + /* If the requested region is in the cache, return it */ + cmpw %bx, underlying_e820_index + jne 1f + pushw %di + pushw %si + movw $underlying_e820_cache, %si + movw $20, %cx + rep movsb + popw %si + popw %di + movw $20, %cx + incw %bx + movl %edx, %eax + ret +1: + /* If the requested region is earlier than the cached region, + * invalidate the cache. + */ + cmpw %bx, underlying_e820_index + jbe 1f + movw $0xffff, underlying_e820_index +1: + /* If the cache is invalid, reset the underlying %ebx */ + cmpw $0xffff, underlying_e820_index + jne 1f + andl $0, underlying_e820_ebx +1: + /* If the cache is valid but the continuation value is zero, + * this means that the previous underlying call returned with + * %ebx=0. Return with CF=1 in this case. + */ + cmpw $0xffff, underlying_e820_index + je 1f + cmpl $0, underlying_e820_ebx + jne 1f + stc + ret +1: + /* Get the next region into the cache */ + pushl %eax + pushl %ebx + pushl %ecx + pushl %edx + movl underlying_e820_ebx, %ebx + stc + pushfw + lcall *%cs:int15_vector + jc 1f /* CF set: error */ + cmpl $SMAP, %eax + je 2f /* 'SMAP' missing: error */ +1: /* An error occurred: return values returned by underlying e820 call */ + stc /* Force CF set if SMAP was missing */ + addr32 leal 16(%esp), %esp /* avoid changing other flags */ + ret +2: /* No error occurred */ + movl %ebx, underlying_e820_ebx + popl %edx + popl %ecx + popl %ebx + popl %eax + /* Copy result to cache */ + pushw %es + pushw %fs + pushw %si + pushw %di + pushw %cx + pushw %es + popw %fs + movw %di, %si + pushw %ds + popw %es + movw $underlying_e820_cache, %di + movw $20, %cx + fs rep movsb + popw %cx + popw %di + popw %si + popw %fs + popw %es + /* Mark cache as containing this result */ + incw underlying_e820_index + + /* Loop until found */ + jmp get_underlying_e820 + .size get_underlying_e820, . - get_underlying_e820 + + .section ".data16" +underlying_e820_index: + .word 0xffff /* Initialise to an invalid value */ + .size underlying_e820_index, . - underlying_e820_index + + .section ".bss16" +underlying_e820_ebx: + .long 0 + .size underlying_e820_ebx, . - underlying_e820_ebx + + .section ".bss16" +underlying_e820_cache: + .space 20 + .size underlying_e820_cache, . - underlying_e820_cache + +/**************************************************************************** + * Get windowed e820 region, without empty region stripping + * + * Parameters: + * As for INT 15,e820 + * Returns: + * As for INT 15,e820 + * + * Wraps the underlying INT 15,e820 call so that each underlying + * region is returned N times, windowed to fit within N visible-memory + * windows. Termination is always via CF=1. + * + **************************************************************************** + */ + .section ".text16" +get_windowed_e820: + + /* Preserve registers */ + pushl %esi + pushw %bp + + /* Split %ebx into %si:%bx, store original %bx in %bp */ + pushl %ebx + popw %bp + popw %si + + /* %si == 0 => start of memory_windows list */ + testw %si, %si + jne 1f + movw $memory_windows, %si +1: + /* Get (cached) underlying e820 region to buffer */ + call get_underlying_e820 + jc 99f /* Abort on error */ + + /* Preserve registers */ pushal + /* start => %edx:%eax, len => %ecx:%ebx */ movl %es:0(%di), %eax movl %es:4(%di), %edx movl %es:8(%di), %ebx movl %es:12(%di), %ecx - call truncate + /* Truncate region to current window */ + call window_region +1: /* Store modified values in e820 map entry */ movl %eax, %es:0(%di) movl %edx, %es:4(%di) movl %ebx, %es:8(%di) movl %ecx, %es:12(%di) + /* Restore registers */ popal + + /* Derive continuation value for next call */ + addw $16, %si + cmpw $memory_windows_end, %si + jne 1f + /* End of memory windows: reset %si and allow %bx to continue */ + xorw %si, %si + jmp 2f +1: /* More memory windows to go: restore original %bx */ + movw %bp, %bx +2: /* Construct %ebx from %si:%bx */ + pushw %si + pushw %bx + popl %ebx + +98: /* Clear CF */ + clc +99: /* Restore registers and return */ + popw %bp + popl %esi ret - .size patch_e820, . - patch_e820 + .size get_windowed_e820, . - get_windowed_e820 /**************************************************************************** - * Split E820 memory map entry if necessary + * Get windowed e820 region, with empty region stripping * * Parameters: * As for INT 15,e820 * Returns: * As for INT 15,e820 * - * Calls the underlying INT 15,e820 and returns a modified memory map. - * Regions will be split around any hidden regions. + * Wraps the underlying INT 15,e820 call so that each underlying + * region is returned up to N times, windowed to fit within N + * visible-memory windows. Empty windows are never returned. + * Termination is always via CF=1. + * **************************************************************************** */ .section ".text16" -split_e820: - pushw %si - pushw %bp - /* Caller's %bx => %si, real %ebx to %ebx, call previous handler */ - pushfw - movw %bx, %si - testl %ebx, %ebx - jnz 1f - movl %ebx, %cs:real_ebx -1: movl %cs:real_ebx, %ebx - lcall *%cs:int15_vector - pushfw - /* Edit result */ - pushw %ds - pushw %cs:rm_ds - popw %ds - movw $truncate_to_start, %bp - incw %si - jns 2f - movw $truncate_to_end, %bp -2: call patch_e820 - jnc 3f - xorw $0x8000, %si -3: testw %si, %si - js 4f - movl %ebx, %cs:real_ebx - testl %ebx, %ebx - jz 5f -4: movw %si, %bx -5: popw %ds - /* Restore flags returned by previous handler and return */ - popfw - popw %bp - popw %si +get_nonempty_e820: + + /* Record entry parameters */ + pushl %eax + pushl %ecx + pushl %edx + + /* Get next windowed region */ + call get_windowed_e820 + jc 99f /* abort on error */ + + /* If region is non-empty, finish here */ + cmpl $0, %es:8(%di) + jne 98f + cmpl $0, %es:12(%di) + jne 98f + + /* Region was empty: restore entry parameters and go to next region */ + popl %edx + popl %ecx + popl %eax + jmp get_nonempty_e820 + +98: /* Clear CF */ + clc +99: /* Return values from underlying call */ + addr32 leal 12(%esp), %esp /* avoid changing flags */ ret - .size split_e820, . - split_e820 + .size get_nonempty_e820, . - get_nonempty_e820 - .section ".text16.data" -real_ebx: - .long 0 - .size real_ebx, . - real_ebx +/**************************************************************************** + * Get mangled e820 region, with empty region stripping + * + * Parameters: + * As for INT 15,e820 + * Returns: + * As for INT 15,e820 + * + * Wraps the underlying INT 15,e820 call so that underlying regions + * are windowed to the allowed memory regions. Empty regions are + * stripped from the map. Termination is always via %ebx=0. + * + **************************************************************************** + */ + .section ".text16" +get_mangled_e820: + + /* Get a nonempty region */ + call get_nonempty_e820 + jc 99f /* Abort on error */ + + /* Peek ahead to see if there are any further nonempty regions */ + pushal + subw $20, %sp + movl $0xe820, %eax + movl $SMAP, %edx + movl $20, %ecx + pushw %ss + popw %es + movw %sp, %di + call get_nonempty_e820 + addr32 leal 20(%esp), %esp /* avoid changing flags */ + popal + jnc 99f /* There are further nonempty regions */ + + /* No futher nonempty regions: zero %ebx and clear CF */ + xorl %ebx, %ebx + +99: /* Return */ + ret + .size get_mangled_e820, . - get_mangled_e820 /**************************************************************************** * INT 15,e820 handler @@ -356,39 +471,11 @@ real_ebx: */ .section ".text16" int15_e820: - pushl %eax - pushl %ecx - pushl %edx - call split_e820 - pushfw - /* If we've hit an error, exit immediately */ - jc 99f - /* If region is non-empty, return this region */ - pushl %eax - movl %es:8(%di), %eax - orl %es:12(%di), %eax - popl %eax - jnz 99f - /* Region is empty. If this is not the end of the map, - * skip over this region. - */ - testl %ebx, %ebx - jz 1f - popfw - popl %edx - popl %ecx - popl %eax - jmp int15_e820 -1: /* Region is empty and this is the end of the map. Return - * with CF set to avoid placing an empty region at the end of - * the map. - */ - popfw - stc - pushfw -99: /* Restore flags from original INT 15,e820 call and return */ - popfw - addr32 leal 12(%esp), %esp /* avoid changing flags */ + pushw %ds + pushw %cs:rm_ds + popw %ds + call get_mangled_e820 + popw %ds lret $2 .size int15_e820, . - int15_e820 diff --git a/gpxe/src/arch/i386/firmware/pcbios/hidemem.c b/gpxe/src/arch/i386/firmware/pcbios/hidemem.c index eba94007..2e74d3b0 100644 --- a/gpxe/src/arch/i386/firmware/pcbios/hidemem.c +++ b/gpxe/src/arch/i386/firmware/pcbios/hidemem.c @@ -15,17 +15,19 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include <assert.h> #include <realmode.h> #include <biosint.h> #include <basemem.h> #include <gpxe/init.h> +#include <gpxe/memmap.h> #include <gpxe/hidemem.h> /** Alignment for hidden memory regions */ #define ALIGN_HIDDEN 4096 /* 4kB page alignment should be enough */ /** - * A hidden region of Etherboot + * A hidden region of gPXE * * This represents a region that will be edited out of the system's * memory map. @@ -34,24 +36,23 @@ * changed. */ struct hidden_region { - /* Physical start address */ - physaddr_t start; - /* Physical end address */ - physaddr_t end; + /** Physical start address */ + uint64_t start; + /** Physical end address */ + uint64_t end; }; -/** - * List of hidden regions - * - * Must be terminated by a zero entry. - */ -struct hidden_region __data16_array ( hidden_regions, [] ) = { - [TEXT] = { 0, 0 }, - [BASEMEM] = { ( 640 * 1024 ), ( 640 * 1024 ) }, - [EXTMEM] = { 0, 0 }, - { 0, 0, } /* Terminator */ -}; -#define hidden_regions __use_data16 ( hidden_regions ) +/** Hidden base memory */ +extern struct hidden_region __data16 ( hidemem_base ); +#define hidemem_base __use_data16 ( hidemem_base ) + +/** Hidden umalloc memory */ +extern struct hidden_region __data16 ( hidemem_umalloc ); +#define hidemem_umalloc __use_data16 ( hidemem_umalloc ) + +/** Hidden text memory */ +extern struct hidden_region __data16 ( hidemem_text ); +#define hidemem_text __use_data16 ( hidemem_text ) /** Assembly routine in e820mangler.S */ extern void int15(); @@ -60,14 +61,19 @@ extern void int15(); extern struct segoff __text16 ( int15_vector ); #define int15_vector __use_text16 ( int15_vector ) +/* The linker defines these symbols for us */ +extern char _text[]; +extern char _end[]; + /** * Hide region of memory from system memory map * + * @v region Hidden memory region * @v start Start of region * @v end End of region */ -void hide_region ( unsigned int region_id, physaddr_t start, physaddr_t end ) { - struct hidden_region *region = &hidden_regions[region_id]; +static void hide_region ( struct hidden_region *region, + physaddr_t start, physaddr_t end ) { /* Some operating systems get a nasty shock if a region of the * E820 map seems to start on a non-page boundary. Make life @@ -76,21 +82,7 @@ void hide_region ( unsigned int region_id, physaddr_t start, physaddr_t end ) { region->start = ( start & ~( ALIGN_HIDDEN - 1 ) ); region->end = ( ( end + ALIGN_HIDDEN - 1 ) & ~( ALIGN_HIDDEN - 1 ) ); - DBG ( "Hiding region %d [%lx,%lx)\n", - region_id, region->start, region->end ); -} - -/** - * Hide Etherboot text - * - */ -static void hide_text ( void ) { - - /* The linker defines these symbols for us */ - extern char _text[]; - extern char _end[]; - - hide_region ( TEXT, virt_to_phys ( _text ), virt_to_phys ( _end ) ); + DBG ( "Hiding region [%llx,%llx)\n", region->start, region->end ); } /** @@ -102,7 +94,25 @@ void hide_basemem ( void ) { * hide_region(), because we don't want this rounded to the * nearest page boundary. */ - hidden_regions[BASEMEM].start = ( get_fbms() * 1024 ); + hidemem_base.start = ( get_fbms() * 1024 ); +} + +/** + * Hide umalloc() region + * + */ +void hide_umalloc ( physaddr_t start, physaddr_t end ) { + assert ( end <= virt_to_phys ( _text ) ); + hide_region ( &hidemem_umalloc, start, end ); +} + +/** + * Hide .text and .data + * + */ +void hide_text ( void ) { + hide_region ( &hidemem_text, virt_to_phys ( _text ), + virt_to_phys ( _end ) ); } /** @@ -112,14 +122,24 @@ void hide_basemem ( void ) { * returned by the BIOS. */ static void hide_etherboot ( void ) { + struct memory_map memmap; + + /* Dump memory map before mangling */ + DBG ( "Hiding gPXE from system memory map\n" ); + get_memmap ( &memmap ); /* Initialise the hidden regions */ - hide_text(); hide_basemem(); + hide_umalloc ( virt_to_phys ( _text ), virt_to_phys ( _text ) ); + hide_text(); /* Hook INT 15 */ hook_bios_interrupt ( 0x15, ( unsigned int ) int15, &int15_vector ); + + /* Dump memory map after mangling */ + DBG ( "Hidden gPXE from system memory map\n" ); + get_memmap ( &memmap ); } /** @@ -128,7 +148,7 @@ static void hide_etherboot ( void ) { * Uninstalls the INT 15 handler installed by hide_etherboot(), if * possible. */ -static void unhide_etherboot ( void ) { +static void unhide_etherboot ( int flags __unused ) { /* If we have more than one hooked interrupt at this point, it * means that some other vector is still hooked, in which case diff --git a/gpxe/src/arch/i386/firmware/pcbios/memmap.c b/gpxe/src/arch/i386/firmware/pcbios/memmap.c index b6a8ca3c..fc0d36ac 100644 --- a/gpxe/src/arch/i386/firmware/pcbios/memmap.c +++ b/gpxe/src/arch/i386/firmware/pcbios/memmap.c @@ -86,8 +86,9 @@ static unsigned int extmemsize_e801 ( void ) { } extmem = ( extmem_1m_to_16m_k + ( extmem_16m_plus_64k * 64 ) ); - DBG ( "INT 15,e801 extended memory size %d+64*%d=%d kB\n", - extmem_1m_to_16m_k, extmem_16m_plus_64k, extmem ); + DBG ( "INT 15,e801 extended memory size %d+64*%d=%d kB [100000,%x)\n", + extmem_1m_to_16m_k, extmem_16m_plus_64k, extmem, + ( 0x100000 + ( extmem * 1024 ) ) ); return extmem; } @@ -103,7 +104,8 @@ static unsigned int extmemsize_88 ( void ) { __asm__ __volatile__ ( REAL_CODE ( "int $0x15" ) : "=a" ( extmem ) : "a" ( 0x8800 ) ); - DBG ( "INT 15,88 extended memory size %d kB\n", extmem ); + DBG ( "INT 15,88 extended memory size %d kB [100000, %x)\n", + extmem, ( 0x100000 + ( extmem * 1024 ) ) ); return extmem; } @@ -147,7 +149,7 @@ static int meme820 ( struct memory_map *memmap ) { "=b" ( next ), "=D" ( discard_D ), "=c" ( discard_c ), "=d" ( discard_d ) : "a" ( 0xe820 ), "b" ( next ), - "D" ( &__from_data16 ( e820buf ) ), + "D" ( __from_data16 ( &e820buf ) ), "c" ( sizeof ( e820buf ) ), "d" ( SMAP ) : "memory" ); @@ -203,7 +205,8 @@ void get_memmap ( struct memory_map *memmap ) { /* Get base and extended memory sizes */ basemem = basememsize(); - DBG ( "FBMS base memory size %d kB\n", basemem ); + DBG ( "FBMS base memory size %d kB [0,%x)\n", + basemem, ( basemem * 1024 ) ); extmem = extmemsize(); /* Try INT 15,e820 first */ diff --git a/gpxe/src/arch/i386/firmware/pcbios/smbios.c b/gpxe/src/arch/i386/firmware/pcbios/smbios.c index 875d421b..a2cb3b88 100644 --- a/gpxe/src/arch/i386/firmware/pcbios/smbios.c +++ b/gpxe/src/arch/i386/firmware/pcbios/smbios.c @@ -136,8 +136,8 @@ static int find_smbios ( void ) { } /* Fill result structure */ - DBG ( "Found SMBIOS entry point at %04x:%04x\n", - BIOS_SEG, offset ); + DBG ( "Found SMBIOS v%d.%d entry point at %04x:%04x\n", + u.entry.major, u.entry.minor, BIOS_SEG, offset ); smbios.address = phys_to_user ( u.entry.smbios_address ); smbios.len = u.entry.smbios_len; smbios.count = u.entry.smbios_count; diff --git a/gpxe/src/arch/i386/firmware/pcbios/smbios_settings.c b/gpxe/src/arch/i386/firmware/pcbios/smbios_settings.c index de08ec5b..b088e51d 100644 --- a/gpxe/src/arch/i386/firmware/pcbios/smbios_settings.c +++ b/gpxe/src/arch/i386/firmware/pcbios/smbios_settings.c @@ -156,3 +156,31 @@ struct setting uuid_setting __setting = { struct smbios_system_information, uuid ), .type = &setting_type_uuid, }; + +/** Other SMBIOS named settings */ +struct setting smbios_named_settings[] __setting = { + { + .name = "manufacturer", + .description = "Manufacturer", + .tag = SMBIOS_STRING_TAG ( SMBIOS_TYPE_SYSTEM_INFORMATION, + struct smbios_system_information, + manufacturer ), + .type = &setting_type_string, + }, + { + .name = "product", + .description = "Product name", + .tag = SMBIOS_STRING_TAG ( SMBIOS_TYPE_SYSTEM_INFORMATION, + struct smbios_system_information, + product ), + .type = &setting_type_string, + }, + { + .name = "serial", + .description = "Serial number", + .tag = SMBIOS_STRING_TAG ( SMBIOS_TYPE_SYSTEM_INFORMATION, + struct smbios_system_information, + serial ), + .type = &setting_type_string, + }, +}; diff --git a/gpxe/src/arch/i386/image/bzimage.c b/gpxe/src/arch/i386/image/bzimage.c index 9372f55c..e6fd854f 100644 --- a/gpxe/src/arch/i386/image/bzimage.c +++ b/gpxe/src/arch/i386/image/bzimage.c @@ -34,7 +34,6 @@ #include <gpxe/image.h> #include <gpxe/segment.h> #include <gpxe/init.h> -#include <gpxe/initrd.h> #include <gpxe/cpio.h> #include <gpxe/features.h> @@ -188,8 +187,8 @@ static size_t bzimage_load_initrd ( struct image *image, struct cpio_header cpio; size_t offset = 0; - /* Ignore images which aren't initrds */ - if ( initrd->type != &initrd_image_type ) + /* Do not include kernel image itself as an initrd */ + if ( initrd == image ) return 0; /* Create cpio header before non-prebuilt images */ @@ -349,7 +348,7 @@ static int bzimage_exec ( struct image *image ) { sizeof ( bzhdr ) ); /* Prepare for exiting */ - shutdown(); + shutdown ( SHUTDOWN_BOOT ); DBGC ( image, "bzImage %p jumping to RM kernel at %04x:0000 " "(stack %04x:%04zx)\n", image, diff --git a/gpxe/src/arch/i386/image/com32.c b/gpxe/src/arch/i386/image/com32.c new file mode 100644 index 00000000..da604625 --- /dev/null +++ b/gpxe/src/arch/i386/image/com32.c @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2008 Daniel Verkamp <daniel@drv.nu>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/** + * @file + * + * SYSLINUX COM32 image format + * + */ + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <errno.h> +#include <assert.h> +#include <realmode.h> +#include <basemem.h> +#include <comboot.h> +#include <gpxe/uaccess.h> +#include <gpxe/image.h> +#include <gpxe/segment.h> +#include <gpxe/init.h> +#include <gpxe/memmap.h> + +struct image_type com32_image_type __image_type ( PROBE_NORMAL ); + +/** + * Execute COMBOOT image + * + * @v image COM32 image + * @ret rc Return status code + */ +static int com32_exec ( struct image *image ) { + struct memory_map memmap; + unsigned int i; + int state; + uint32_t avail_mem_top; + + state = setjmp ( comboot_return ); + + switch ( state ) { + case 0: /* First time through; invoke COM32 program */ + + /* Get memory map */ + get_memmap ( &memmap ); + + /* Find end of block covering COM32 image loading area */ + for ( i = 0, avail_mem_top = 0 ; i < memmap.count ; i++ ) { + if ( (memmap.regions[i].start <= COM32_START_PHYS) && + (memmap.regions[i].end > COM32_START_PHYS + image->len) ) { + avail_mem_top = memmap.regions[i].end; + break; + } + } + + DBGC ( image, "COM32 %p: available memory top = 0x%x\n", + image, (int)avail_mem_top ); + + assert ( avail_mem_top != 0 ); + + com32_external_esp = phys_to_virt ( avail_mem_top ); + + /* Hook COMBOOT API interrupts */ + hook_comboot_interrupts( ); + + /* Temporarily de-register image, so that a "boot" command + * doesn't throw us into an execution loop. Hold a reference + * to avoid the image's being freed. + */ + image_get ( image ); + unregister_image ( image ); + + __asm__ __volatile__ ( + "movl %%esp, (com32_internal_esp)\n\t" /* Save internal virtual address space ESP */ + "movl (com32_external_esp), %%esp\n\t" /* Switch to COM32 ESP (top of available memory) */ + "call _virt_to_phys\n\t" /* Switch to flat physical address space */ + "pushl %0\n\t" /* Pointer to CDECL helper function */ + "pushl %1\n\t" /* Pointer to FAR call helper function */ + "pushl %2\n\t" /* Size of low memory bounce buffer */ + "pushl %3\n\t" /* Pointer to low memory bounce buffer */ + "pushl %4\n\t" /* Pointer to INT call helper function */ + "pushl %5\n\t" /* Pointer to the command line arguments */ + "pushl $6\n\t" /* Number of additional arguments */ + "call *%6\n\t" /* Execute image */ + "call _phys_to_virt\n\t" /* Switch back to internal virtual address space */ + "movl (com32_internal_esp), %%esp\n\t" /* Switch back to internal stack */ + : + : + /* %0 */ "r" ( virt_to_phys ( com32_cfarcall_wrapper ) ), + /* %1 */ "r" ( virt_to_phys ( com32_farcall_wrapper ) ), + /* %2 */ "r" ( get_fbms() * 1024 - (COM32_BOUNCE_SEG << 4) ), + /* %3 */ "i" ( COM32_BOUNCE_SEG << 4 ), + /* %4 */ "r" ( virt_to_phys ( com32_intcall_wrapper ) ), + /* %5 */ "r" ( virt_to_phys ( image->cmdline ) ), + /* %6 */ "r" ( COM32_START_PHYS ) + : + "memory" ); + break; + + case COMBOOT_RETURN_RUN_KERNEL: + DBGC ( image, "COM32 %p: returned to run kernel...\n", image ); + comboot_run_kernel ( ); + break; + + case COMBOOT_RETURN_EXIT: + break; + + } + + comboot_force_text_mode ( ); + + DBGC ( image, "COM32 %p returned\n", image ); + + /* Re-register image and return */ + register_image ( image ); + image_put ( image ); + + return 0; +} + +/** + * Check image name extension + * + * @v image COM32 image + * @ret rc Return status code + */ +static int com32_identify ( struct image *image ) { + const char *ext; + static const uint8_t magic[] = { 0xB8, 0xFF, 0x4C, 0xCD, 0x21 }; + uint8_t buf[5]; + + if ( image->len >= 5 ) { + /* Check for magic number + * mov eax,21cd4cffh + * B8 FF 4C CD 21 + */ + copy_from_user ( buf, image->data, 0, sizeof(buf) ); + if ( ! memcmp ( buf, magic, sizeof(buf) ) ) { + DBGC ( image, "COM32 %p: found magic number\n", + image ); + return 0; + } + } + + /* Magic number not found; check filename extension */ + + ext = strrchr( image->name, '.' ); + + if ( ! ext ) { + DBGC ( image, "COM32 %p: no extension\n", + image ); + return -ENOEXEC; + } + + ++ext; + + if ( strcasecmp( ext, "c32" ) ) { + DBGC ( image, "COM32 %p: unrecognized extension %s\n", + image, ext ); + return -ENOEXEC; + } + + return 0; +} + + +/** + * Load COM32 image into memory + * @v image COM32 image + * @ret rc Return status code + */ +static int comboot_load_image ( struct image *image ) { + size_t filesz, memsz; + userptr_t buffer; + int rc; + + filesz = image->len; + memsz = filesz; + buffer = phys_to_user ( COM32_START_PHYS ); + if ( ( rc = prep_segment ( buffer, filesz, memsz ) ) != 0 ) { + DBGC ( image, "COM32 %p: could not prepare segment: %s\n", + image, strerror ( rc ) ); + return rc; + } + + /* Copy image to segment */ + memcpy_user ( buffer, 0, image->data, 0, filesz ); + + return 0; +} + +/** + * Prepare COM32 low memory bounce buffer + * @v image COM32 image + * @ret rc Return status code + */ +static int comboot_prepare_bounce_buffer ( struct image * image ) { + unsigned int seg; + userptr_t seg_userptr; + size_t filesz, memsz; + int rc; + + seg = COM32_BOUNCE_SEG; + seg_userptr = real_to_user ( seg, 0 ); + + /* Ensure the entire 64k segment is free */ + memsz = 0xFFFF; + filesz = 0; + + /* Prepare, verify, and load the real-mode segment */ + if ( ( rc = prep_segment ( seg_userptr, filesz, memsz ) ) != 0 ) { + DBGC ( image, "COM32 %p: could not prepare bounce buffer segment: %s\n", + image, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Load COM32 image into memory + * + * @v image COM32 image + * @ret rc Return status code + */ +static int com32_load ( struct image *image ) { + int rc; + + DBGC ( image, "COM32 %p: name '%s', cmdline '%s'\n", + image, image->name, image->cmdline ); + + /* Check if this is a COMBOOT image */ + if ( ( rc = com32_identify ( image ) ) != 0 ) { + return rc; + } + + /* This is a COM32 image, valid or otherwise */ + if ( ! image->type ) + image->type = &com32_image_type; + + /* Load image */ + if ( ( rc = comboot_load_image ( image ) ) != 0 ) { + return rc; + } + + /* Prepare bounce buffer segment */ + if ( ( rc = comboot_prepare_bounce_buffer ( image ) ) != 0 ) { + return rc; + } + + return 0; +} + +/** SYSLINUX COM32 image type */ +struct image_type com32_image_type __image_type ( PROBE_NORMAL ) = { + .name = "COM32", + .load = com32_load, + .exec = com32_exec, +}; diff --git a/gpxe/src/arch/i386/image/comboot.c b/gpxe/src/arch/i386/image/comboot.c new file mode 100644 index 00000000..63d02c0f --- /dev/null +++ b/gpxe/src/arch/i386/image/comboot.c @@ -0,0 +1,312 @@ +/* + * Copyright (C) 2008 Daniel Verkamp <daniel@drv.nu>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/** + * @file + * + * SYSLINUX COMBOOT (16-bit) image format + * + */ + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <errno.h> +#include <assert.h> +#include <realmode.h> +#include <basemem.h> +#include <comboot.h> +#include <gpxe/uaccess.h> +#include <gpxe/image.h> +#include <gpxe/segment.h> +#include <gpxe/init.h> +#include <gpxe/features.h> + +FEATURE ( FEATURE_IMAGE, "COMBOOT", DHCP_EB_FEATURE_COMBOOT, 1 ); + +struct image_type comboot_image_type __image_type ( PROBE_NORMAL ); + +/** + * COMBOOT PSP, copied to offset 0 of code segment + */ +struct comboot_psp { + /** INT 20 instruction, executed if COMBOOT image returns with RET */ + uint16_t int20; + /** Segment of first non-free paragraph of memory */ + uint16_t first_non_free_para; +}; + +/** Offset in PSP of command line */ +#define COMBOOT_PSP_CMDLINE_OFFSET 0x81 + +/** Maximum length of command line in PSP + * (127 bytes minus space and CR) */ +#define COMBOOT_MAX_CMDLINE_LEN 125 + + +/** + * Copy command line to PSP + * + * @v image COMBOOT image + */ +static void comboot_copy_cmdline ( struct image * image, userptr_t seg_userptr ) { + const char *cmdline = ( image->cmdline ? image->cmdline : "" ); + int cmdline_len = strlen ( cmdline ); + if( cmdline_len > COMBOOT_MAX_CMDLINE_LEN ) + cmdline_len = COMBOOT_MAX_CMDLINE_LEN; + uint8_t len_byte = cmdline_len; + char spc = ' ', cr = '\r'; + + /* Copy length to byte before command line */ + copy_to_user ( seg_userptr, COMBOOT_PSP_CMDLINE_OFFSET - 1, + &len_byte, 1 ); + + /* Command line starts with space */ + copy_to_user ( seg_userptr, + COMBOOT_PSP_CMDLINE_OFFSET, + &spc, 1 ); + + /* Copy command line */ + copy_to_user ( seg_userptr, + COMBOOT_PSP_CMDLINE_OFFSET + 1, + cmdline, cmdline_len ); + + /* Command line ends with CR */ + copy_to_user ( seg_userptr, + COMBOOT_PSP_CMDLINE_OFFSET + cmdline_len + 1, + &cr, 1 ); +} + +/** + * Initialize PSP + * + * @v image COMBOOT image + * @v seg_userptr segment to initialize + */ +static void comboot_init_psp ( struct image * image, userptr_t seg_userptr ) { + struct comboot_psp psp; + + /* Fill PSP */ + + /* INT 20h instruction, byte order reversed */ + psp.int20 = 0x20CD; + + /* get_fbms() returns BIOS free base memory counter, which is in + * kilobytes; x * 1024 / 16 == x * 64 == x << 6 */ + psp.first_non_free_para = get_fbms() << 6; + + DBGC ( image, "COMBOOT %p: first non-free paragraph = 0x%x\n", + image, psp.first_non_free_para ); + + /* Copy the PSP to offset 0 of segment. + * The rest of the PSP was already zeroed by + * comboot_prepare_segment. */ + copy_to_user ( seg_userptr, 0, &psp, sizeof( psp ) ); + + /* Copy the command line to the PSP */ + comboot_copy_cmdline ( image, seg_userptr ); +} + +/** + * Execute COMBOOT image + * + * @v image COMBOOT image + * @ret rc Return status code + */ +static int comboot_exec ( struct image *image ) { + userptr_t seg_userptr = real_to_user ( COMBOOT_PSP_SEG, 0 ); + int state; + + state = setjmp ( comboot_return ); + + switch ( state ) { + case 0: /* First time through; invoke COMBOOT program */ + + /* Initialize PSP */ + comboot_init_psp ( image, seg_userptr ); + + /* Hook COMBOOT API interrupts */ + hook_comboot_interrupts ( ); + + DBGC ( image, "executing 16-bit COMBOOT image at %4x:0100\n", + COMBOOT_PSP_SEG ); + + /* Temporarily de-register image, so that a "boot" command + * doesn't throw us into an execution loop. Hold a reference + * to avoid the image's being freed. + */ + image_get ( image ); + unregister_image ( image ); + + /* Store stack segment at 0x38 and stack pointer at 0x3A + * in the PSP and jump to the image */ + __asm__ __volatile__ ( + REAL_CODE ( /* Save return address with segment on old stack */ + "popw %%ax\n\t" + "pushw %%cs\n\t" + "pushw %%ax\n\t" + /* Set DS=ES=segment with image */ + "movw %w0, %%ds\n\t" + "movw %w0, %%es\n\t" + /* Set SS:SP to new stack (end of image segment) */ + "movw %w0, %%ss\n\t" + "xor %%sp, %%sp\n\t" + "pushw $0\n\t" + "pushw %w0\n\t" + "pushw $0x100\n\t" + /* Zero registers (some COM files assume GP regs are 0) */ + "xorw %%ax, %%ax\n\t" + "xorw %%bx, %%bx\n\t" + "xorw %%cx, %%cx\n\t" + "xorw %%dx, %%dx\n\t" + "xorw %%si, %%si\n\t" + "xorw %%di, %%di\n\t" + "xorw %%bp, %%bp\n\t" + "lret\n\t" ) + : : "r" ( COMBOOT_PSP_SEG ) : "eax" ); + break; + + case COMBOOT_RETURN_RUN_KERNEL: + DBGC ( image, "COMBOOT %p: returned to run kernel...\n", image ); + comboot_run_kernel ( ); + break; + + case COMBOOT_RETURN_EXIT: + break; + + } + + comboot_force_text_mode ( ); + + DBGC ( image, "COMBOOT %p returned\n", image ); + + /* Re-register image and return */ + register_image ( image ); + image_put ( image ); + + return 0; +} + +/** + * Check image name extension + * + * @v image COMBOOT image + * @ret rc Return status code + */ +static int comboot_identify ( struct image *image ) { + const char *ext; + + ext = strrchr( image->name, '.' ); + + if ( ! ext ) { + DBGC ( image, "COMBOOT %p: no extension\n", + image ); + return -ENOEXEC; + } + + ++ext; + + if ( strcasecmp( ext, "com" ) && strcasecmp( ext, "cbt" ) ) { + DBGC ( image, "COMBOOT %p: unrecognized extension %s\n", + image, ext ); + return -ENOEXEC; + } + + return 0; +} + +/** + * Load COMBOOT image into memory, preparing a segment and returning it + * @v image COMBOOT image + * @ret rc Return status code + */ +static int comboot_prepare_segment ( struct image *image ) +{ + userptr_t seg_userptr; + size_t filesz, memsz; + int rc; + + /* Load image in segment */ + seg_userptr = real_to_user ( COMBOOT_PSP_SEG, 0 ); + + /* Allow etra 0x100 bytes before image for PSP */ + filesz = image->len + 0x100; + + /* Ensure the entire 64k segment is free */ + memsz = 0xFFFF; + + /* Prepare, verify, and load the real-mode segment */ + if ( ( rc = prep_segment ( seg_userptr, filesz, memsz ) ) != 0 ) { + DBGC ( image, "COMBOOT %p: could not prepare segment: %s\n", + image, strerror ( rc ) ); + return rc; + } + + /* Zero PSP */ + memset_user ( seg_userptr, 0, 0, 0x100 ); + + /* Copy image to segment:0100 */ + memcpy_user ( seg_userptr, 0x100, image->data, 0, image->len ); + + return 0; +} + +/** + * Load COMBOOT image into memory + * + * @v image COMBOOT image + * @ret rc Return status code + */ +static int comboot_load ( struct image *image ) { + int rc; + + DBGC ( image, "COMBOOT %p: name '%s'\n", + image, image->name ); + + /* Check if this is a COMBOOT image */ + if ( ( rc = comboot_identify ( image ) ) != 0 ) { + + return rc; + } + + /* This is a 16-bit COMBOOT image, valid or otherwise */ + if ( ! image->type ) + image->type = &comboot_image_type; + + /* Sanity check for filesize */ + if( image->len >= 0xFF00 ) { + DBGC( image, "COMBOOT %p: image too large\n", + image ); + return -ENOEXEC; + } + + /* Prepare segment and load image */ + if ( ( rc = comboot_prepare_segment ( image ) ) != 0 ) { + return rc; + } + + return 0; +} + +/** SYSLINUX COMBOOT (16-bit) image type */ +struct image_type comboot_image_type __image_type ( PROBE_NORMAL ) = { + .name = "COMBOOT", + .load = comboot_load, + .exec = comboot_exec, +}; diff --git a/gpxe/src/arch/i386/image/elfboot.c b/gpxe/src/arch/i386/image/elfboot.c index 52510aa9..c8daf72b 100644 --- a/gpxe/src/arch/i386/image/elfboot.c +++ b/gpxe/src/arch/i386/image/elfboot.c @@ -46,9 +46,10 @@ 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 ( SHUTDOWN_BOOT ); /* Jump to OS with flat physical addressing */ + DBGC ( image, "ELF %p starting execution at %lx\n", image, entry ); __asm__ __volatile__ ( PHYS_CODE ( "call *%%edi\n\t" ) : : "D" ( entry ) : "eax", "ebx", "ecx", "edx", "esi", "ebp", diff --git a/gpxe/src/arch/i386/image/multiboot.c b/gpxe/src/arch/i386/image/multiboot.c index fbaebd5c..a4a340fd 100644 --- a/gpxe/src/arch/i386/image/multiboot.c +++ b/gpxe/src/arch/i386/image/multiboot.c @@ -277,9 +277,11 @@ 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 ( SHUTDOWN_BOOT ); /* Jump to OS with flat physical addressing */ + DBGC ( image, "MULTIBOOT %p starting execution at %lx\n", + image, entry ); __asm__ __volatile__ ( PHYS_CODE ( "call *%%edi\n\t" ) : : "a" ( MULTIBOOT_BOOTLOADER_MAGIC ), "b" ( virt_to_phys ( &mbinfo ) ), @@ -358,8 +360,11 @@ static int multiboot_load_raw ( struct image *image, /* Verify and prepare segment */ offset = ( hdr->offset - hdr->mb.header_addr + hdr->mb.load_addr ); - filesz = ( hdr->mb.load_end_addr - hdr->mb.load_addr ); - memsz = ( hdr->mb.bss_end_addr - hdr->mb.load_addr ); + filesz = ( hdr->mb.load_end_addr ? + ( hdr->mb.load_end_addr - hdr->mb.load_addr ) : + ( image->len - offset ) ); + memsz = ( hdr->mb.bss_end_addr ? + ( hdr->mb.bss_end_addr - hdr->mb.load_addr ) : filesz ); buffer = phys_to_user ( hdr->mb.load_addr ); if ( ( rc = prep_segment ( buffer, filesz, memsz ) ) != 0 ) { DBGC ( image, "MULTIBOOT %p could not prepare segment: %s\n", diff --git a/gpxe/src/arch/i386/image/nbi.c b/gpxe/src/arch/i386/image/nbi.c index 73791be9..e6a0ab0f 100644 --- a/gpxe/src/arch/i386/image/nbi.c +++ b/gpxe/src/arch/i386/image/nbi.c @@ -429,7 +429,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 ( SHUTDOWN_BOOT ); /* Execute NBI image */ if ( NBI_LINEAR_EXEC_ADDR ( imgheader.flags ) ) { diff --git a/gpxe/src/arch/i386/include/bits/errfile.h b/gpxe/src/arch/i386/include/bits/errfile.h index c5b04a26..99927c28 100644 --- a/gpxe/src/arch/i386/include/bits/errfile.h +++ b/gpxe/src/arch/i386/include/bits/errfile.h @@ -21,6 +21,9 @@ #define ERRFILE_nbi ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00040000 ) #define ERRFILE_pxe_image ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00050000 ) #define ERRFILE_elfboot ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00060000 ) +#define ERRFILE_comboot ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00070000 ) +#define ERRFILE_com32 ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00080000 ) +#define ERRFILE_comboot_resolv ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00090000 ) #define ERRFILE_undi ( ERRFILE_ARCH | ERRFILE_NET | 0x00000000 ) #define ERRFILE_undiload ( ERRFILE_ARCH | ERRFILE_NET | 0x00010000 ) diff --git a/gpxe/src/arch/i386/include/comboot.h b/gpxe/src/arch/i386/include/comboot.h new file mode 100644 index 00000000..1fc3b718 --- /dev/null +++ b/gpxe/src/arch/i386/include/comboot.h @@ -0,0 +1,102 @@ +#ifndef COMBOOT_H +#define COMBOOT_H + +/** + * @file + * + * SYSLINUX COMBOOT + */ + +#include <stdint.h> +#include <setjmp.h> +#include <gpxe/in.h> + +/** Segment used for COMBOOT PSP and image */ +#define COMBOOT_PSP_SEG 0x07C0 + +/** Entry point address of COM32 images */ +#define COM32_START_PHYS 0x101000 + +/** COM32 bounce buffer segment */ +#define COM32_BOUNCE_SEG 0x07C0 + +/** Size of SYSLINUX file block in bytes */ +#define COMBOOT_FILE_BLOCKSZ 512 + +/** COMBOOT feature flags (INT 22h AX=15h) */ +#define COMBOOT_FEATURE_LOCAL_BOOT (1 << 0) +#define COMBOOT_FEATURE_IDLE_LOOP (1 << 1) + +/** Maximum number of shuffle descriptors for + * shuffle and boot functions + * (INT 22h AX=0012h, 001Ah, 001Bh) + */ +#define COMBOOT_MAX_SHUFFLE_DESCRIPTORS 682 + +typedef union { + uint32_t l; + uint16_t w[2]; + uint8_t b[4]; +} com32_reg32_t; + +typedef struct { + uint16_t gs; /* Offset 0 */ + uint16_t fs; /* Offset 2 */ + uint16_t es; /* Offset 4 */ + uint16_t ds; /* Offset 6 */ + + com32_reg32_t edi; /* Offset 8 */ + com32_reg32_t esi; /* Offset 12 */ + com32_reg32_t ebp; /* Offset 16 */ + com32_reg32_t _unused_esp; /* Offset 20 */ + com32_reg32_t ebx; /* Offset 24 */ + com32_reg32_t edx; /* Offset 28 */ + com32_reg32_t ecx; /* Offset 32 */ + com32_reg32_t eax; /* Offset 36 */ + + com32_reg32_t eflags; /* Offset 40 */ +} com32sys_t; + +typedef struct { + uint32_t dest; + uint32_t src; + uint32_t len; +} comboot_shuffle_descriptor; + +extern void hook_comboot_interrupts ( ); + +/* These are not the correct prototypes, but it doens't matter, + * as we only ever get the address of these functions; + * they are only called from COM32 code running in PHYS_CODE + */ +extern void com32_intcall_wrapper ( ); +extern void com32_farcall_wrapper ( ); +extern void com32_cfarcall_wrapper ( ); + +/* Resolve a hostname to an (IPv4) address */ +extern int comboot_resolv ( const char *name, struct in_addr *address ); + +/* setjmp/longjmp context buffer used to return after loading an image */ +extern jmp_buf comboot_return; + +/* Command line to execute when returning via comboot_return + * with COMBOOT_RETURN_RUN_KERNEL + */ +extern char *comboot_kernel_cmdline; + +/* Execute comboot_image_cmdline */ +extern void comboot_run_kernel ( ); + +extern void *com32_external_esp; + +#define COMBOOT_RETURN_EXIT 1 +#define COMBOOT_RETURN_RUN_KERNEL 2 + +extern void comboot_force_text_mode ( void ); + +#define COMBOOT_VIDEO_GRAPHICS 0x01 +#define COMBOOT_VIDEO_NONSTANDARD 0x02 +#define COMBOOT_VIDEO_VESA 0x04 +#define COMBOOT_VIDEO_NOTEXT 0x08 + +#endif diff --git a/gpxe/src/arch/i386/include/int13.h b/gpxe/src/arch/i386/include/int13.h index 2a193831..72ca97d7 100644 --- a/gpxe/src/arch/i386/include/int13.h +++ b/gpxe/src/arch/i386/include/int13.h @@ -69,8 +69,20 @@ struct int13_drive { /** Underlying block device */ struct block_device *blockdev; - /** BIOS drive number (0x80-0xff) */ + /** BIOS in-use drive number (0x80-0xff) */ unsigned int drive; + /** BIOS natural drive number (0x80-0xff) + * + * This is the drive number that would have been assigned by + * 'naturally' appending the drive to the end of the BIOS + * drive list. + * + * If the emulated drive replaces a preexisting drive, this is + * the drive number that the preexisting drive gets remapped + * to. + */ + unsigned int natural_drive; + /** Number of cylinders * * The cylinder number field in an INT 13 call is ten bits diff --git a/gpxe/src/arch/i386/include/libkir.h b/gpxe/src/arch/i386/include/libkir.h index 5f67a56d..1f5b1350 100644 --- a/gpxe/src/arch/i386/include/libkir.h +++ b/gpxe/src/arch/i386/include/libkir.h @@ -19,8 +19,8 @@ #define __text16_array( variable,array ) variable array #define __use_data16( variable ) variable #define __use_text16( variable ) variable -#define __from_data16( variable ) variable -#define __from_text16( variable ) variable +#define __from_data16( pointer ) pointer +#define __from_text16( pointer ) pointer /* Real-mode data and code segments */ static inline __attribute__ (( always_inline )) unsigned int _rm_cs ( void ) { diff --git a/gpxe/src/arch/i386/include/librm.h b/gpxe/src/arch/i386/include/librm.h index 31b50979..07a85c59 100644 --- a/gpxe/src/arch/i386/include/librm.h +++ b/gpxe/src/arch/i386/include/librm.h @@ -51,15 +51,17 @@ extern char *text16; ( * ( ( typeof ( _text16_ ## variable ) * ) \ & ( text16 [ ( size_t ) & ( _text16_ ## variable ) ] ) ) ) -#define __from_data16( variable ) \ - ( * ( ( typeof ( variable ) * ) \ - ( ( ( void * ) &(variable) ) - ( ( void * ) data16 ) ) ) ) +#define __from_data16( pointer ) \ + ( ( unsigned int ) \ + ( ( ( void * ) (pointer) ) - ( ( void * ) data16 ) ) ) -#define __from_text16( variable ) \ - ( * ( ( typeof ( variable ) * ) \ - ( ( ( void * ) &(variable) ) - ( ( void * ) text16 ) ) ) ) +#define __from_text16( pointer ) \ + ( ( unsigned int ) \ + ( ( ( void * ) (pointer) ) - ( ( void * ) text16 ) ) ) /* Variables in librm.S, present in the normal data segment */ +extern uint16_t rm_sp; +extern uint16_t rm_ss; extern uint16_t __data16 ( rm_cs ); #define rm_cs __use_data16 ( rm_cs ) extern uint16_t __text16 ( rm_ds ); @@ -277,6 +279,9 @@ user_to_phys ( userptr_t buffer, off_t offset ) { return virt_to_phys ( ( void * ) buffer + offset ); } +extern uint16_t copy_user_to_rm_stack ( userptr_t data, size_t size ); +extern void remove_user_from_rm_stack ( userptr_t data, size_t size ); + /* TEXT16_CODE: declare a fragment of code that resides in .text16 */ #define TEXT16_CODE( asm_code_str ) \ ".section \".text16\", \"ax\", @progbits\n\t" \ diff --git a/gpxe/src/arch/i386/include/pxe_call.h b/gpxe/src/arch/i386/include/pxe_call.h index dc585310..7a38d314 100644 --- a/gpxe/src/arch/i386/include/pxe_call.h +++ b/gpxe/src/arch/i386/include/pxe_call.h @@ -30,5 +30,6 @@ extern void pxe_hook_int1a ( void ); extern int pxe_unhook_int1a ( void ); extern void pxe_init_structures ( void ); extern int pxe_start_nbp ( void ); +extern __cdecl void pxe_api_call ( struct i386_all_regs *ix86 ); #endif /* _PXE_CALL_H */ diff --git a/gpxe/src/arch/i386/include/setjmp.h b/gpxe/src/arch/i386/include/setjmp.h index ed2be270..bb0a100d 100644 --- a/gpxe/src/arch/i386/include/setjmp.h +++ b/gpxe/src/arch/i386/include/setjmp.h @@ -6,7 +6,7 @@ #define JBLEN 6 typedef unsigned long jmp_buf[JBLEN]; -extern int setjmp (jmp_buf env); -extern void longjmp (jmp_buf env, int val); +extern int __cdecl setjmp (jmp_buf env); +extern void __cdecl longjmp (jmp_buf env, int val); #endif /* ETHERBOOT_SETJMP_H */ diff --git a/gpxe/src/arch/i386/interface/pcbios/int13.c b/gpxe/src/arch/i386/interface/pcbios/int13.c index 7e09fb5f..6f61e4a1 100644 --- a/gpxe/src/arch/i386/interface/pcbios/int13.c +++ b/gpxe/src/arch/i386/interface/pcbios/int13.c @@ -325,15 +325,20 @@ static int int13_get_extended_parameters ( struct int13_drive *drive, static __cdecl void int13 ( struct i386_all_regs *ix86 ) { int command = ix86->regs.ah; unsigned int bios_drive = ix86->regs.dl; - unsigned int original_bios_drive = bios_drive; struct int13_drive *drive; int status; list_for_each_entry ( drive, &drives, list ) { - if ( drive->drive > bios_drive ) - continue; - if ( drive->drive < bios_drive ) { - original_bios_drive--; + + if ( bios_drive != drive->drive ) { + /* Remap any accesses to this drive's natural number */ + if ( bios_drive == drive->natural_drive ) { + DBG ( "INT 13,%04x (%02x) remapped to " + "(%02x)\n", ix86->regs.ax, + bios_drive, drive->drive ); + ix86->regs.dl = drive->drive; + return; + } continue; } @@ -393,13 +398,6 @@ static __cdecl void int13 ( struct i386_all_regs *ix86 ) { return; } - - /* Remap BIOS drive */ - if ( bios_drive != original_bios_drive ) { - DBG ( "INT 13,%04x (%02x) remapped to (%02x)\n", - ix86->regs.ax, bios_drive, original_bios_drive ); - } - ix86->regs.dl = original_bios_drive; } /** @@ -542,19 +540,28 @@ void register_int13_drive ( struct int13_drive *drive ) { /* Give drive a default geometry if none specified */ guess_int13_geometry ( drive ); - /* Assign drive number if none specified, update BIOS drive count */ + /* Assign natural drive number */ get_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES ); - if ( ( drive->drive & 0xff ) == 0xff ) - drive->drive = num_drives; - drive->drive |= 0x80; + drive->natural_drive = ( num_drives | 0x80 ); num_drives++; - if ( num_drives <= ( drive->drive & 0x7f ) ) - num_drives = ( ( drive->drive & 0x7f ) + 1 ); + + /* Assign drive number */ + if ( ( drive->drive & 0xff ) == 0xff ) { + /* Drive number == -1 => use natural drive number */ + drive->drive = drive->natural_drive; + } else { + /* Use specified drive number (+0x80 if necessary) */ + drive->drive |= 0x80; + if ( num_drives <= ( drive->drive & 0x7f ) ) + num_drives = ( ( drive->drive & 0x7f ) + 1 ); + } + + /* Update BIOS drive count */ put_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES ); - DBG ( "Registered INT13 drive %02x with C/H/S geometry %d/%d/%d\n", - drive->drive, drive->cylinders, drive->heads, - drive->sectors_per_track ); + DBG ( "Registered INT13 drive %02x (naturally %02x) with C/H/S " + "geometry %d/%d/%d\n", drive->drive, drive->natural_drive, + drive->cylinders, drive->heads, drive->sectors_per_track ); /* Hook INT 13 vector if not already hooked */ if ( list_empty ( &drives ) ) diff --git a/gpxe/src/arch/i386/interface/pxe/pxe_call.c b/gpxe/src/arch/i386/interface/pxe/pxe_call.c index 5b307d40..3ccb7fb5 100644 --- a/gpxe/src/arch/i386/interface/pxe/pxe_call.c +++ b/gpxe/src/arch/i386/interface/pxe/pxe_call.c @@ -347,6 +347,9 @@ __cdecl void pxe_loader_call ( struct i386_all_regs *ix86 ) { /* Copy parameter block from caller */ copy_from_user ( ¶ms, uparams, 0, sizeof ( params ) ); + /* Fill in ROM segment address */ + ppxe.UNDIROMID.segment = ix86->segs.ds; + /* Set default status in case child routine fails to do so */ params.Status = PXENV_STATUS_FAILURE; @@ -441,8 +444,8 @@ int pxe_start_nbp ( void ) { "addw $4, %%sp\n\t" ) : "=a" ( rc ), "=b" ( discard_b ), "=c" ( discard_c ) - : "a" ( & __from_text16 ( ppxe ) ), - "b" ( & __from_text16 ( pxenv ) ), + : "a" ( __from_text16 ( &ppxe ) ), + "b" ( __from_text16 ( &pxenv ) ), "c" ( rm_cs ) : "edx", "esi", "edi", "ebp", "memory" ); diff --git a/gpxe/src/arch/i386/interface/pxe/pxe_entry.S b/gpxe/src/arch/i386/interface/pxe/pxe_entry.S index 204894ef..e5d327a5 100644 --- a/gpxe/src/arch/i386/interface/pxe/pxe_entry.S +++ b/gpxe/src/arch/i386/interface/pxe/pxe_entry.S @@ -35,7 +35,7 @@ ppxe: .byte 0 /* StructCksum */ .byte 0 /* StructRev */ .byte 0 /* reserved_1 */ - .word 0, 0 /* UNDIROMID */ + .word undiheader, 0 /* UNDIROMID */ .word 0, 0 /* BaseROMID */ .word pxe_entry_sp, 0 /* EntryPointSP */ .word pxe_entry_esp, 0 /* EntryPointESP */ @@ -55,6 +55,11 @@ pxe_segments: .equ pxe_length, . - ppxe .size ppxe, . - ppxe + /* Define undiheader=0 as a weak symbol for non-ROM builds */ + .section ".weak" + .weak undiheader +undiheader: + /**************************************************************************** * PXENV+ structure **************************************************************************** @@ -98,6 +103,16 @@ pxenv: * none **************************************************************************** */ + /* Wyse Streaming Manager server (WLDRM13.BIN) assumes that + * the PXENV+ entry point is at UNDI_CS:0000; apparently, + * somebody at Wyse has difficulty distinguishing between the + * words "may" and "must"... + */ + .section ".text16.null" + .code16 +pxenv_null_entry: + jmp pxenv_entry + .section ".text16" .code16 pxenv_entry: diff --git a/gpxe/src/arch/i386/interface/syslinux/com32_call.c b/gpxe/src/arch/i386/interface/syslinux/com32_call.c new file mode 100644 index 00000000..586730cf --- /dev/null +++ b/gpxe/src/arch/i386/interface/syslinux/com32_call.c @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2008 Daniel Verkamp <daniel@drv.nu>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/** + * @file SYSLINUX COM32 helpers + * + */ + +#include <stdint.h> +#include <realmode.h> +#include <comboot.h> +#include <assert.h> +#include <gpxe/uaccess.h> + +static com32sys_t __bss16 ( com32_regs ); +#define com32_regs __use_data16 ( com32_regs ) + +static uint8_t __bss16 ( com32_int_vector ); +#define com32_int_vector __use_data16 ( com32_int_vector ) + +static uint32_t __bss16 ( com32_farcall_proc ); +#define com32_farcall_proc __use_data16 ( com32_farcall_proc ) + +uint16_t __bss16 ( com32_saved_sp ); + +/** + * Interrupt call helper + */ +void __cdecl com32_intcall ( uint8_t interrupt, physaddr_t inregs_phys, physaddr_t outregs_phys ) { + + memcpy_user ( virt_to_user( &com32_regs ), 0, + phys_to_user ( inregs_phys ), 0, + sizeof(com32sys_t) ); + + com32_int_vector = interrupt; + + __asm__ __volatile__ ( + REAL_CODE ( /* Save all registers */ + "pushal\n\t" + "pushw %%ds\n\t" + "pushw %%es\n\t" + "pushw %%fs\n\t" + "pushw %%gs\n\t" + /* Mask off unsafe flags */ + "movl (com32_regs + 40), %%eax\n\t" + "andl $0x200cd7, %%eax\n\t" + "movl %%eax, (com32_regs + 40)\n\t" + /* Load com32_regs into the actual registers */ + "movw %%sp, %%ss:(com32_saved_sp)\n\t" + "movw $com32_regs, %%sp\n\t" + "popw %%gs\n\t" + "popw %%fs\n\t" + "popw %%es\n\t" + "popw %%ds\n\t" + "popal\n\t" + "popfl\n\t" + "movw %%ss:(com32_saved_sp), %%sp\n\t" + /* patch INT instruction */ + "pushw %%ax\n\t" + "movb %%ss:(com32_int_vector), %%al\n\t" + "movb %%al, %%cs:(com32_intcall_instr + 1)\n\t" + /* perform a jump to avoid problems with cache + * consistency in self-modifying code on some CPUs (486) + */ + "jmp 1f\n" + "1:\n\t" + "popw %%ax\n\t" + "com32_intcall_instr:\n\t" + /* INT instruction to be patched */ + "int $0xFF\n\t" + /* Copy regs back to com32_regs */ + "movw %%sp, %%ss:(com32_saved_sp)\n\t" + "movw $(com32_regs + 44), %%sp\n\t" + "pushfl\n\t" + "pushal\n\t" + "pushw %%ds\n\t" + "pushw %%es\n\t" + "pushw %%fs\n\t" + "pushw %%gs\n\t" + "movw %%ss:(com32_saved_sp), %%sp\n\t" + /* Restore registers */ + "popw %%gs\n\t" + "popw %%fs\n\t" + "popw %%es\n\t" + "popw %%ds\n\t" + "popal\n\t") + : : ); + + if ( outregs_phys ) { + memcpy_user ( phys_to_user ( outregs_phys ), 0, + virt_to_user( &com32_regs ), 0, + sizeof(com32sys_t) ); + } +} + +/** + * Farcall helper + */ +void __cdecl com32_farcall ( uint32_t proc, physaddr_t inregs_phys, physaddr_t outregs_phys ) { + + memcpy_user ( virt_to_user( &com32_regs ), 0, + phys_to_user ( inregs_phys ), 0, + sizeof(com32sys_t) ); + + com32_farcall_proc = proc; + + __asm__ __volatile__ ( + REAL_CODE ( /* Save all registers */ + "pushal\n\t" + "pushw %%ds\n\t" + "pushw %%es\n\t" + "pushw %%fs\n\t" + "pushw %%gs\n\t" + /* Mask off unsafe flags */ + "movl (com32_regs + 40), %%eax\n\t" + "andl $0x200cd7, %%eax\n\t" + "movl %%eax, (com32_regs + 40)\n\t" + /* Load com32_regs into the actual registers */ + "movw %%sp, %%ss:(com32_saved_sp)\n\t" + "movw $com32_regs, %%sp\n\t" + "popw %%gs\n\t" + "popw %%fs\n\t" + "popw %%es\n\t" + "popw %%ds\n\t" + "popal\n\t" + "popfl\n\t" + "movw %%ss:(com32_saved_sp), %%sp\n\t" + /* Call procedure */ + "lcall *%%ss:(com32_farcall_proc)\n\t" + /* Copy regs back to com32_regs */ + "movw %%sp, %%ss:(com32_saved_sp)\n\t" + "movw $(com32_regs + 44), %%sp\n\t" + "pushfl\n\t" + "pushal\n\t" + "pushw %%ds\n\t" + "pushw %%es\n\t" + "pushw %%fs\n\t" + "pushw %%gs\n\t" + "movw %%ss:(com32_saved_sp), %%sp\n\t" + /* Restore registers */ + "popw %%gs\n\t" + "popw %%fs\n\t" + "popw %%es\n\t" + "popw %%ds\n\t" + "popal\n\t") + : : ); + + if ( outregs_phys ) { + memcpy_user ( phys_to_user ( outregs_phys ), 0, + virt_to_user( &com32_regs ), 0, + sizeof(com32sys_t) ); + } +} + +/** + * CDECL farcall helper + */ +int __cdecl com32_cfarcall ( uint32_t proc, physaddr_t stack, size_t stacksz ) { + int32_t eax; + + copy_user_to_rm_stack ( phys_to_user ( stack ), stacksz ); + com32_farcall_proc = proc; + + __asm__ __volatile__ ( + REAL_CODE ( "lcall *%%ss:(com32_farcall_proc)\n\t" ) + : "=a" (eax) + : + : "ecx", "edx" ); + + remove_user_from_rm_stack ( 0, stacksz ); + + return eax; +} diff --git a/gpxe/src/arch/i386/interface/syslinux/com32_wrapper.S b/gpxe/src/arch/i386/interface/syslinux/com32_wrapper.S new file mode 100644 index 00000000..08d7398a --- /dev/null +++ b/gpxe/src/arch/i386/interface/syslinux/com32_wrapper.S @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2008 Daniel Verkamp <daniel@drv.nu>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + .text + .arch i386 + .code32 + + .globl com32_farcall_wrapper +com32_farcall_wrapper: + + movl $com32_farcall, %eax + jmp com32_wrapper + + + .globl com32_cfarcall_wrapper +com32_cfarcall_wrapper: + + movl $com32_cfarcall, %eax + jmp com32_wrapper + + + .globl com32_intcall_wrapper +com32_intcall_wrapper: + + movl $com32_intcall, %eax + /*jmp com32_wrapper*/ /* fall through */ + +com32_wrapper: + + /* Switch to internal virtual address space */ + call _phys_to_virt + + mov %eax, (com32_helper_function) + + /* Save external COM32 stack pointer */ + movl %esp, (com32_external_esp) + + /* Copy arguments to caller-save registers */ + movl 12(%esp), %eax + movl 8(%esp), %ecx + movl 4(%esp), %edx + + /* Switch to internal stack */ + movl (com32_internal_esp), %esp + + /* Copy arguments to internal stack */ + pushl %eax + pushl %ecx + pushl %edx + + call *(com32_helper_function) + + /* Clean up stack */ + addl $12, %esp + + /* Save internal stack pointer and restore external stack pointer */ + movl %esp, (com32_internal_esp) + movl (com32_external_esp), %esp + + /* Switch to external flat physical address space */ + call _virt_to_phys + + ret + + + .data + +/* Internal gPXE virtual address space %esp */ +.globl com32_internal_esp +.lcomm com32_internal_esp, 4 + +/* External flat physical address space %esp */ +.globl com32_external_esp +.lcomm com32_external_esp, 4 + +/* Function pointer of helper to call */ +.lcomm com32_helper_function, 4 diff --git a/gpxe/src/arch/i386/interface/syslinux/comboot_call.c b/gpxe/src/arch/i386/interface/syslinux/comboot_call.c new file mode 100644 index 00000000..5a400ede --- /dev/null +++ b/gpxe/src/arch/i386/interface/syslinux/comboot_call.c @@ -0,0 +1,598 @@ +/* + * Copyright (C) 2008 Daniel Verkamp <daniel@drv.nu>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/** + * @file SYSLINUX COMBOOT API + * + */ + +#include <errno.h> +#include <realmode.h> +#include <biosint.h> +#include <console.h> +#include <stdlib.h> +#include <comboot.h> +#include <bzimage.h> +#include <pxe_call.h> +#include <setjmp.h> +#include <string.h> +#include <gpxe/posix_io.h> +#include <gpxe/process.h> +#include <gpxe/serial.h> +#include <gpxe/init.h> + +/** The "SYSLINUX" version string */ +static char __data16_array ( syslinux_version, [] ) = "gPXE " VERSION; +#define syslinux_version __use_data16 ( syslinux_version ) + +/** The "SYSLINUX" copyright string */ +static char __data16_array ( syslinux_copyright, [] ) = "http://etherboot.org"; +#define syslinux_copyright __use_data16 ( syslinux_copyright ) + +static char __data16_array ( syslinux_configuration_file, [] ) = ""; +#define syslinux_configuration_file __use_data16 ( syslinux_configuration_file ) + +/** Feature flags */ +static uint8_t __data16 ( comboot_feature_flags ) = COMBOOT_FEATURE_IDLE_LOOP; +#define comboot_feature_flags __use_data16 ( comboot_feature_flags ) + +static struct segoff __text16 ( int20_vector ); +#define int20_vector __use_text16 ( int20_vector ) + +static struct segoff __text16 ( int21_vector ); +#define int21_vector __use_text16 ( int21_vector ) + +static struct segoff __text16 ( int22_vector ); +#define int22_vector __use_text16 ( int22_vector ) + +extern void int20_wrapper ( void ); +extern void int21_wrapper ( void ); +extern void int22_wrapper ( void ); + +/* setjmp/longjmp context buffer used to return after loading an image */ +jmp_buf comboot_return; + +/* Command line to execute when returning via comboot_return + * with COMBOOT_RETURN_RUN_KERNEL + */ +char *comboot_kernel_cmdline; + +/* Mode flags set by INT 22h AX=0017h */ +static uint16_t comboot_graphics_mode = 0; + + +/** + * Print a string with a particular terminator + */ +static void print_user_string ( unsigned int segment, unsigned int offset, char terminator ) { + int i = 0; + char c; + userptr_t str = real_to_user ( segment, offset ); + for ( ; ; ) { + copy_from_user ( &c, str, i, 1 ); + if ( c == terminator ) break; + putchar ( c ); + i++; + } +} + + +/** + * Perform a series of memory copies from a list in low memory + */ +static void shuffle ( unsigned int list_segment, unsigned int list_offset, unsigned int count ) +{ + comboot_shuffle_descriptor shuf[COMBOOT_MAX_SHUFFLE_DESCRIPTORS]; + unsigned int i; + + /* Copy shuffle descriptor list so it doesn't get overwritten */ + copy_from_user ( shuf, real_to_user ( list_segment, list_offset ), 0, + count * sizeof( comboot_shuffle_descriptor ) ); + + /* Do the copies */ + for ( i = 0; i < count; i++ ) { + userptr_t src_u = phys_to_user ( shuf[ i ].src ); + userptr_t dest_u = phys_to_user ( shuf[ i ].dest ); + + if ( shuf[ i ].src == 0xFFFFFFFF ) { + /* Fill with 0 instead of copying */ + memset_user ( dest_u, 0, 0, shuf[ i ].len ); + } else if ( shuf[ i ].dest == 0xFFFFFFFF ) { + /* Copy new list of descriptors */ + count = shuf[ i ].len / sizeof( comboot_shuffle_descriptor ); + assert ( count <= COMBOOT_MAX_SHUFFLE_DESCRIPTORS ); + copy_from_user ( shuf, src_u, 0, shuf[ i ].len ); + i = -1; + } else { + /* Regular copy */ + memmove_user ( dest_u, 0, src_u, 0, shuf[ i ].len ); + } + } +} + + +/** + * Set default text mode + */ +void comboot_force_text_mode ( void ) { + if ( comboot_graphics_mode & COMBOOT_VIDEO_VESA ) { + /* Set VGA mode 3 via VESA VBE mode set */ + __asm__ __volatile__ ( + REAL_CODE ( + "mov $0x4F02, %%ax\n\t" + "mov $0x03, %%bx\n\t" + "int $0x10\n\t" + ) + : : ); + } else if ( comboot_graphics_mode & COMBOOT_VIDEO_GRAPHICS ) { + /* Set VGA mode 3 via standard VGA mode set */ + __asm__ __volatile__ ( + REAL_CODE ( + "mov $0x03, %%ax\n\t" + "int $0x10\n\t" + ) + : : ); + } + + comboot_graphics_mode = 0; +} + + +/** + * Run the kernel specified in comboot_kernel_cmdline + */ +void comboot_run_kernel ( ) +{ + char *initrd; + + comboot_force_text_mode ( ); + + DBG ( "COMBOOT: executing image '%s'\n", comboot_kernel_cmdline ); + + /* Find initrd= parameter, if any */ + if ( ( initrd = strstr ( comboot_kernel_cmdline, "initrd=" ) ) ) { + char old_char = '\0'; + char *initrd_end = strchr( initrd, ' ' ); + + /* Replace space after end of parameter + * with a nul terminator if this is not + * the last parameter + */ + if ( initrd_end ) { + old_char = *initrd_end; + *initrd_end = '\0'; + } + + /* Replace = with space to get 'initrd filename' + * command suitable for system() + */ + initrd[6] = ' '; + + DBG( "COMBOOT: loading initrd '%s'\n", initrd ); + + system ( initrd ); + + /* Restore space after parameter */ + if ( initrd_end ) { + *initrd_end = old_char; + } + + /* Restore = */ + initrd[6] = '='; + } + + /* Load kernel */ + DBG ( "COMBOOT: loading kernel '%s'\n", comboot_kernel_cmdline ); + system ( comboot_kernel_cmdline ); + + free ( comboot_kernel_cmdline ); + + /* Boot */ + system ( "boot" ); + + DBG ( "COMBOOT: back from executing command\n" ); +} + + +/** + * Terminate program interrupt handler + */ +static __cdecl void int20 ( struct i386_all_regs *ix86 __unused ) { + longjmp ( comboot_return, COMBOOT_RETURN_EXIT ); +} + + +/** + * DOS-compatible API + */ +static __cdecl void int21 ( struct i386_all_regs *ix86 ) { + ix86->flags |= CF; + + switch ( ix86->regs.ah ) { + case 0x00: + case 0x4C: /* Terminate program */ + longjmp ( comboot_return, COMBOOT_RETURN_EXIT ); + break; + + case 0x01: /* Get Key with Echo */ + case 0x08: /* Get Key without Echo */ + /* TODO: handle extended characters? */ + ix86->regs.al = getchar( ); + + /* Enter */ + if ( ix86->regs.al == 0x0A ) + ix86->regs.al = 0x0D; + + if ( ix86->regs.ah == 0x01 ) + putchar ( ix86->regs.al ); + + ix86->flags &= ~CF; + break; + + case 0x02: /* Write Character */ + putchar ( ix86->regs.dl ); + ix86->flags &= ~CF; + break; + + case 0x04: /* Write Character to Serial Port */ + serial_putc ( ix86->regs.dl ); + ix86->flags &= ~CF; + break; + + case 0x09: /* Write DOS String to Console */ + print_user_string ( ix86->segs.ds, ix86->regs.dx, '$' ); + ix86->flags &= ~CF; + break; + + case 0x0B: /* Check Keyboard */ + if ( iskey() ) + ix86->regs.al = 0xFF; + else + ix86->regs.al = 0x00; + + ix86->flags &= ~CF; + break; + + case 0x30: /* Check DOS Version */ + /* Bottom halves all 0; top halves spell "SYSLINUX" */ + ix86->regs.eax = 0x59530000; + ix86->regs.ebx = 0x4C530000; + ix86->regs.ecx = 0x4E490000; + ix86->regs.edx = 0x58550000; + ix86->flags &= ~CF; + break; + + default: + DBG ( "COMBOOT unknown int21 function %02x\n", ix86->regs.ah ); + break; + } +} + + +/** + * SYSLINUX API + */ +static __cdecl void int22 ( struct i386_all_regs *ix86 ) { + ix86->flags |= CF; + + switch ( ix86->regs.ax ) { + case 0x0001: /* Get Version */ + + /* Number of INT 22h API functions available */ + ix86->regs.ax = 0x0018; + + /* SYSLINUX version number */ + ix86->regs.ch = 0; /* major */ + ix86->regs.cl = 0; /* minor */ + + /* SYSLINUX derivative ID */ + ix86->regs.dl = BZI_LOADER_TYPE_GPXE; + + /* SYSLINUX version and copyright strings */ + ix86->segs.es = rm_ds; + ix86->regs.si = ( ( unsigned ) __from_data16 ( syslinux_version ) ); + ix86->regs.di = ( ( unsigned ) __from_data16 ( syslinux_copyright ) ); + + ix86->flags &= ~CF; + break; + + case 0x0002: /* Write String */ + print_user_string ( ix86->segs.es, ix86->regs.bx, '\0' ); + ix86->flags &= ~CF; + break; + + case 0x0003: /* Run command */ + { + userptr_t cmd_u = real_to_user ( ix86->segs.es, ix86->regs.bx ); + int len = strlen_user ( cmd_u, 0 ); + char cmd[len + 1]; + copy_from_user ( cmd, cmd_u, 0, len + 1 ); + DBG ( "COMBOOT: executing command '%s'\n", cmd ); + + comboot_kernel_cmdline = strdup ( cmd ); + + DBG ( "COMBOOT: returning to run image...\n" ); + longjmp ( comboot_return, COMBOOT_RETURN_RUN_KERNEL ); + } + break; + + case 0x0004: /* Run default command */ + /* FIXME: just exit for now */ + longjmp ( comboot_return, COMBOOT_RETURN_EXIT ); + break; + + case 0x0005: /* Force text mode */ + comboot_force_text_mode ( ); + ix86->flags &= ~CF; + break; + + case 0x0006: /* Open file */ + { + int fd; + userptr_t file_u = real_to_user ( ix86->segs.es, ix86->regs.si ); + int len = strlen_user ( file_u, 0 ); + char file[len + 1]; + + copy_from_user ( file, file_u, 0, len + 1 ); + + if ( file[0] == '\0' ) { + DBG ( "COMBOOT: attempted open with empty file name\n" ); + break; + } + + DBG ( "COMBOOT: opening file '%s'\n", file ); + + fd = open ( file ); + + if ( fd < 0 ) { + DBG ( "COMBOOT: error opening file %s\n", file ); + break; + } + + /* This relies on the fact that a gPXE POSIX fd will + * always fit in 16 bits. + */ +#if (POSIX_FD_MAX > 65535) +#error POSIX_FD_MAX too large +#endif + ix86->regs.si = (uint16_t) fd; + + ix86->regs.cx = COMBOOT_FILE_BLOCKSZ; + ix86->regs.eax = fsize ( fd ); + ix86->flags &= ~CF; + } + break; + + case 0x0007: /* Read file */ + { + int fd = ix86->regs.si; + int len = ix86->regs.cx * COMBOOT_FILE_BLOCKSZ; + int rc; + fd_set fds; + userptr_t buf = real_to_user ( ix86->segs.es, ix86->regs.bx ); + + /* Wait for data ready to read */ + FD_ZERO ( &fds ); + FD_SET ( fd, &fds ); + + select ( &fds, 1 ); + + rc = read_user ( fd, buf, 0, len ); + if ( rc < 0 ) { + DBG ( "COMBOOT: read failed\n" ); + ix86->regs.si = 0; + break; + } + + ix86->regs.ecx = rc; + ix86->flags &= ~CF; + } + break; + + case 0x0008: /* Close file */ + { + int fd = ix86->regs.si; + close ( fd ); + ix86->flags &= ~CF; + } + break; + + case 0x0009: /* Call PXE Stack */ + pxe_api_call ( ix86 ); + ix86->flags &= ~CF; + break; + + case 0x000A: /* Get Derivative-Specific Information */ + + /* gPXE has its own derivative ID, so there is no defined + * output here; just return AL for now */ + ix86->regs.al = BZI_LOADER_TYPE_GPXE; + ix86->flags &= ~CF; + break; + + case 0x000B: /* Get Serial Console Configuration */ + /* FIXME: stub */ + ix86->regs.dx = 0; + ix86->flags &= ~CF; + break; + + case 0x000E: /* Get configuration file name */ + /* FIXME: stub */ + ix86->segs.es = rm_ds; + ix86->regs.bx = ( ( unsigned ) __from_data16 ( syslinux_configuration_file ) ); + ix86->flags &= ~CF; + break; + + case 0x000F: /* Get IPAPPEND strings */ + /* FIXME: stub */ + ix86->regs.cx = 0; + ix86->segs.es = 0; + ix86->regs.bx = 0; + ix86->flags &= ~CF; + break; + + case 0x0010: /* Resolve hostname */ + { + userptr_t hostname_u = real_to_user ( ix86->segs.es, ix86->regs.bx ); + int len = strlen_user ( hostname_u, 0 ); + char hostname[len]; + struct in_addr addr; + + copy_from_user ( hostname, hostname_u, 0, len + 1 ); + + /* TODO: + * "If the hostname does not contain a dot (.), the + * local domain name is automatically appended." + */ + + comboot_resolv ( hostname, &addr ); + + ix86->regs.eax = addr.s_addr; + ix86->flags &= ~CF; + } + break; + + case 0x0011: /* Maximum number of shuffle descriptors */ + ix86->regs.cx = COMBOOT_MAX_SHUFFLE_DESCRIPTORS; + ix86->flags &= ~CF; + break; + + case 0x0012: /* Cleanup, shuffle and boot */ + if ( ix86->regs.cx > COMBOOT_MAX_SHUFFLE_DESCRIPTORS ) + break; + + /* Perform final cleanup */ + shutdown ( SHUTDOWN_BOOT ); + + /* Perform sequence of copies */ + shuffle ( ix86->segs.es, ix86->regs.di, ix86->regs.cx ); + + /* Jump to real-mode entry point */ + __asm__ __volatile__ ( + REAL_CODE ( + "pushw %0\n\t" + "popw %%ds\n\t" + "pushl %1\n\t" + "lret\n\t" + ) + : + : "r" ( ix86->segs.ds ), + "r" ( ix86->regs.ebp ), + "d" ( ix86->regs.ebx ), + "S" ( ix86->regs.esi ) ); + + assert ( 0 ); /* Execution should never reach this point */ + + break; + + case 0x0013: /* Idle loop call */ + step ( ); + ix86->flags &= ~CF; + break; + + case 0x0015: /* Get feature flags */ + ix86->segs.es = rm_ds; + ix86->regs.bx = ( ( unsigned ) __from_data16 ( &comboot_feature_flags ) ); + ix86->regs.cx = 1; /* Number of feature flag bytes */ + ix86->flags &= ~CF; + break; + + case 0x0016: /* Run kernel image */ + { + userptr_t file_u = real_to_user ( ix86->segs.ds, ix86->regs.si ); + userptr_t cmd_u = real_to_user ( ix86->segs.es, ix86->regs.bx ); + int file_len = strlen_user ( file_u, 0 ); + int cmd_len = strlen_user ( cmd_u, 0 ); + char file[file_len + 1 + cmd_len + 7 + 1]; + char cmd[cmd_len + 1]; + + memcpy( file, "kernel ", 7 ); + copy_from_user ( file + 7, file_u, 0, file_len + 1 ); + copy_from_user ( cmd, cmd_u, 0, cmd_len + 1 ); + strcat ( file, " " ); + strcat ( file, cmd ); + + DBG ( "COMBOOT: run kernel image '%s'\n", file ); + + comboot_kernel_cmdline = strdup ( file ); + + DBG ( "COMBOOT: returning to run image...\n" ); + longjmp ( comboot_return, COMBOOT_RETURN_RUN_KERNEL ); + } + break; + + case 0x0017: /* Report video mode change */ + comboot_graphics_mode = ix86->regs.bx; + ix86->flags &= ~CF; + break; + + case 0x0018: /* Query custom font */ + /* FIXME: stub */ + ix86->regs.al = 0; + ix86->segs.es = 0; + ix86->regs.bx = 0; + ix86->flags &= ~CF; + break; + + default: + DBG ( "COMBOOT unknown int22 function %04x\n", ix86->regs.ax ); + break; + } +} + +/** + * Hook BIOS interrupts related to COMBOOT API (INT 20h, 21h, 22h) + */ +void hook_comboot_interrupts ( ) { + + __asm__ __volatile__ ( + TEXT16_CODE ( "\nint20_wrapper:\n\t" + "pushl %0\n\t" + "pushw %%cs\n\t" + "call prot_call\n\t" + "addw $4, %%sp\n\t" + "iret\n\t" ) + : : "i" ( int20 ) ); + + hook_bios_interrupt ( 0x20, ( unsigned int ) int20_wrapper, + &int20_vector ); + + __asm__ __volatile__ ( + TEXT16_CODE ( "\nint21_wrapper:\n\t" + "pushl %0\n\t" + "pushw %%cs\n\t" + "call prot_call\n\t" + "addw $4, %%sp\n\t" + "iret\n\t" ) + : : "i" ( int21 ) ); + + hook_bios_interrupt ( 0x21, ( unsigned int ) int21_wrapper, + &int21_vector ); + + __asm__ __volatile__ ( + TEXT16_CODE ( "\nint22_wrapper:\n\t" + "pushl %0\n\t" + "pushw %%cs\n\t" + "call prot_call\n\t" + "addw $4, %%sp\n\t" + "iret\n\t" ) + : : "i" ( int22) ); + + hook_bios_interrupt ( 0x22, ( unsigned int ) int22_wrapper, + &int22_vector ); +} diff --git a/gpxe/src/arch/i386/interface/syslinux/comboot_resolv.c b/gpxe/src/arch/i386/interface/syslinux/comboot_resolv.c new file mode 100644 index 00000000..41c3af7a --- /dev/null +++ b/gpxe/src/arch/i386/interface/syslinux/comboot_resolv.c @@ -0,0 +1,58 @@ +#include <errno.h> +#include <comboot.h> +#include <gpxe/in.h> +#include <gpxe/list.h> +#include <gpxe/process.h> +#include <gpxe/resolv.h> + +static int comboot_resolv_rc; +static struct in_addr comboot_resolv_addr; + +static void comboot_resolv_done ( struct resolv_interface *resolv, + struct sockaddr *sa, int rc ) { + struct sockaddr_in *sin; + + resolv_unplug ( resolv ); + + if ( rc != 0 ) { + comboot_resolv_rc = rc; + return; + } + + if ( sa->sa_family != AF_INET ) { + comboot_resolv_rc = -EAFNOSUPPORT; + return; + } + + sin = ( ( struct sockaddr_in * ) sa ); + comboot_resolv_addr = sin->sin_addr; + + comboot_resolv_rc = 0; +} + +static struct resolv_interface_operations comboot_resolv_ops = { + .done = comboot_resolv_done, +}; + +static struct resolv_interface comboot_resolver = { + .intf = { + .dest = &null_resolv.intf, + .refcnt = NULL, + }, + .op = &comboot_resolv_ops, +}; + +int comboot_resolv ( const char *name, struct in_addr *address ) { + int rc; + + comboot_resolv_rc = -EINPROGRESS; + + if ( ( rc = resolv ( &comboot_resolver, name, NULL ) ) != 0 ) + return rc; + + while ( comboot_resolv_rc == -EINPROGRESS ) + step(); + + *address = comboot_resolv_addr; + return comboot_resolv_rc; +} diff --git a/gpxe/src/arch/i386/prefix/libprefix.S b/gpxe/src/arch/i386/prefix/libprefix.S index cb091112..ae2a491f 100644 --- a/gpxe/src/arch/i386/prefix/libprefix.S +++ b/gpxe/src/arch/i386/prefix/libprefix.S @@ -294,7 +294,7 @@ pm_call: movw %ss, %ax shll $4, %eax movzwl %bp, %edi - leal PM_CALL_VAR(gdt)(%eax, %edi), %eax + addr32 leal PM_CALL_VAR(gdt)(%eax, %edi), %eax movl %eax, PM_CALL_VAR(gdt_base)(%bp) movw %cs, %ax movw $PM_CALL_VAR(pm_cs), %di diff --git a/gpxe/src/arch/i386/prefix/lkrnprefix.S b/gpxe/src/arch/i386/prefix/lkrnprefix.S index 59e70cd1..c1e92f57 100644 --- a/gpxe/src/arch/i386/prefix/lkrnprefix.S +++ b/gpxe/src/arch/i386/prefix/lkrnprefix.S @@ -110,7 +110,10 @@ root_dev: boot_flag: .word 0xAA55 jump: - jmp setup_code + /* Manually specify a two-byte jmp instruction here rather + * than leaving it up to the assembler. */ + .byte 0xeb + .byte setup_code - header header: .byte 'H', 'd', 'r', 'S' version: @@ -142,7 +145,10 @@ pad1: cmd_line_ptr: .long 0 initrd_addr_max: - .long 0 + /* We don't use an initrd but some bootloaders (e.g. SYSLINUX) have + * been known to require this field. Set the value to 2 GB. This + * value is also used by the Linux kernel. */ + .long 0x7fffffff kernel_alignment: .long 0 relocatable_kernel: diff --git a/gpxe/src/arch/i386/prefix/romprefix.S b/gpxe/src/arch/i386/prefix/romprefix.S index 727cffcb..f9b9e169 100644 --- a/gpxe/src/arch/i386/prefix/romprefix.S +++ b/gpxe/src/arch/i386/prefix/romprefix.S @@ -8,8 +8,10 @@ #define PNP_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'n' << 16 ) + ( 'P' << 24 ) ) #define PMM_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'M' << 16 ) + ( 'M' << 24 ) ) +#define PCI_SIGNATURE ( 'P' + ( 'C' << 8 ) + ( 'I' << 16 ) + ( ' ' << 24 ) ) #define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) ) #define PNP_GET_BBS_VERSION 0x60 +#define PMM_ALLOCATE 0x0000 .text .code16 @@ -40,25 +42,31 @@ checksum: pciheader: .ascii "PCIR" /* Signature */ - .word pci_vendor_id /* Vendor ID */ - .word pci_device_id /* Device ID */ - .word 0x0000 /* pointer to vital product data */ + .word pci_vendor_id /* Vendor identification */ + .word pci_device_id /* Device identification */ + .word 0x0000 /* Device list pointer */ .word pciheader_len /* PCI data structure length */ - .byte 0x00 /* PCI data structure revision */ - .byte 0x02 /* Device Base Type code */ - .byte 0x00 /* Device Sub-Type code */ - .byte 0x00 /* Device Interface Type code */ -pciheader_size: .word _load_size_sect /* Image length same as offset 02h */ - .word 0x0001 /* revision level of code/data */ - .byte 0x00 /* code type */ - .byte 0x80 /* Flags (last PCI data structure) */ - .word 0x0000 /* reserved */ + .byte 0x03 /* PCI data structure revision */ + .byte 0x02, 0x00, 0x00 /* Class code */ +pciheader_image_length: + .word _load_size_sect /* Image length */ + .word 0x0001 /* Revision level */ + .byte 0x00 /* Code type */ + .byte 0x80 /* Last image indicator */ +pciheader_runtime_length: + .word _load_size_sect /* Maximum run-time image length */ + .word 0x0000 /* Configuration utility code header */ + .word 0x0000 /* DMTF CLP entry point */ .equ pciheader_len, . - pciheader .size pciheader, . - pciheader .section ".zinfo.fixup", "a" /* Compressor fixup information */ .ascii "SUBW" - .long pciheader_size + .long pciheader_image_length + .long 512 + .long 0 + .ascii "SUBW" + .long pciheader_runtime_length .long 512 .long 0 .previous @@ -76,7 +84,7 @@ pnpheader: .byte 0x02 /* Device base type code */ .byte 0x00 /* Device sub-type code */ .byte 0x00 /* Device interface type code */ - .byte 0x54 /* Device indicator */ + .byte 0xf4 /* Device indicator */ .word 0x0000 /* Boot connection vector */ .word 0x0000 /* Disconnect vector */ .word bev_entry /* Boot execution vector */ @@ -104,7 +112,8 @@ prodstr_separator: prodstr_pci_id: .asciz "xx:xx.x)" /* Filled in by init code */ .size prodstr, . - prodstr - + + .globl undiheader undiheader: .ascii "UNDI" /* Signature */ .byte undiheader_len /* Length of structure */ @@ -115,6 +124,7 @@ undiheader: .word _data16_size /* Stack segment size */ .word _data16_size /* Data segment size */ .word _text16_size /* Code segment size */ + .ascii "PCIR" /* Bus type */ .equ undiheader_len, . - undiheader .size undiheader, . - undiheader @@ -131,29 +141,79 @@ init: pushw %ds pushw %es pushw %fs + pushw %gs cld pushw %cs popw %ds pushw $0x40 popw %fs + + /* Shuffle some registers around. We need %di available for + * the print_xxx functions, and in a register that's + * addressable from %es, so shuffle as follows: + * + * %di (pointer to PnP structure) => %bx + * %bx (runtime segment address, for PCI 3.0) => %gs + */ + movw %bx, %gs movw %di, %bx - xorw %di, %di + /* Print message as early as possible */ movw $init_message, %si + xorw %di, %di call print_message call print_pci_busdevfn + /* Fill in product name string, if possible */ movw $prodstr_pci_id, %di call print_pci_busdevfn movb $' ', prodstr_separator + + /* Print segment address */ + movb $' ', %al xorw %di, %di + call print_character + movw %cs, %ax + call print_hex_word + + /* Check for PCI BIOS version */ + pushl %ebx + pushl %edx + pushl %edi + stc + movw $0xb101, %ax + int $0x1a + jc 1f + cmpl $PCI_SIGNATURE, %edx + jne 1f + testb %ah, %ah + jnz 1f + movw $init_message_pci, %si + xorw %di, %di + call print_message + movb %bh, %al + call print_hex_nibble + movb $'.', %al + call print_character + movb %bl, %al + call print_hex_byte + cmpb $3, %bh + jae 2f +1: /* PCI <3.0: set %gs (runtime segment) = %cs (init-time segment) */ + pushw %cs + popw %gs +2: popl %edi + popl %edx + popl %ebx + /* Check for PnP BIOS */ testw $0x0f, %bx /* PnP signature must be aligned - bochs */ - jnz hook_int19 /* uses unalignment to indicate 'fake' PnP. */ + jnz no_bbs /* uses unalignment to indicate 'fake' PnP. */ cmpl $PNP_SIGNATURE, %es:0(%bx) - jne hook_int19 + jne no_bbs /* Is PnP: print PnP message */ movw $init_message_pnp, %si + xorw %di, %di call print_message /* Check for BBS */ pushw %es:0x1b(%bx) /* Real-mode data segment */ @@ -163,22 +223,25 @@ init: lcall *%es:0xd(%bx) addw $8, %sp testw %ax, %ax - jne hook_int19 - movw $init_message_bbs, %si - call print_message - jmp hook_bbs - /* Not BBS-compliant - must hook INT 19 */ -hook_int19: + je got_bbs +no_bbs: /* Not BBS-compliant - must hook INT 19 */ movw $init_message_int19, %si + xorw %di, %di call print_message xorw %ax, %ax movw %ax, %es pushl %es:( 0x19 * 4 ) popl orig_int19 - pushw %cs + pushw %gs /* %gs contains runtime %cs */ pushw $int19_entry popl %es:( 0x19 * 4 ) -hook_bbs: + jmp bbs_done +got_bbs: /* BBS compliant - no need to hook INT 19 */ + movw $init_message_bbs, %si + xorw %di, %di + call print_message +bbs_done: + /* Check for PMM */ movw $( 0xe000 - 1 ), %bx pmm_scan: @@ -196,26 +259,26 @@ pmm_scan: jnz pmm_scan /* PMM found: print PMM message */ movw $init_message_pmm, %si + xorw %di, %di call print_message /* Try to allocate 2MB block via PMM */ pushw $0x0006 /* Aligned, extended memory */ pushl $0xffffffff /* No handle */ pushl $( 0x00200000 / 16 ) /* 2MB in paragraphs */ - pushw $0x0000 /* pmmAllocate */ + pushw $PMM_ALLOCATE lcall *%es:7 addw $12, %sp + movw %dx, %ax + xorw %di, %di + call print_hex_word + movw %dx, ( image_source + 2 ) testw %dx, %dx /* %ax==0 even on success, since align=2MB */ - jnz gotpmm - movw $init_message_pmm_failed, %si - call print_message - jmp no_pmm -gotpmm: /* PMM allocation succeeded: copy ROM to PMM block */ + jz no_pmm + /* PMM allocation succeeded: copy ROM to PMM block */ pushal /* PMM presence implies 1kB stack */ - movw %ax, %es /* %ax=0 already - see above */ - pushw %dx - pushw %ax - popl %edi - movl %edi, image_source + xorw %ax, %ax + movw %ax, %es + movl image_source, %edi xorl %esi, %esi movzbl romheader_size, %ecx shll $9, %ecx @@ -232,8 +295,27 @@ gotpmm: /* PMM allocation succeeded: copy ROM to PMM block */ loop 1b subb %bl, checksum popal -no_pmm: /* Prompt for POST-time shell */ +no_pmm: + + /* Copy self to option ROM space. Required for PCI3.0, which + * loads us to a temporary location in low memory. Will be a + * no-op for lower PCI versions. + */ + movb $' ', %al + xorw %di, %di + call print_character + movw %gs, %ax + call print_hex_word + movzbw romheader_size, %cx + shlw $9, %cx + movw %ax, %es + xorw %si, %si + xorw %di, %di + cs rep movsb + + /* Prompt for POST-time shell */ movw $init_message_prompt, %si + xorw %di, %di call print_message /* Empty the keyboard buffer before waiting for input */ empty_keyboard_buffer: @@ -274,22 +356,30 @@ wait_for_key: pushw %cs call exec no_key_pressed: + /* Print blank lines to terminate messages */ movw $init_message_end, %si + xorw %di, %di call print_message + /* Restore registers */ + popw %gs popw %fs popw %es popw %ds popaw + /* Indicate boot capability to PnP BIOS, if present */ movw $0x20, %ax lret .size init, . - init init_message: - .asciz "gPXE (http://etherboot.org) - PCI " + .asciz "gPXE (http://etherboot.org) - " .size init_message, . - init_message +init_message_pci: + .asciz " PCI" + .size init_message_pci, . - init_message_pci init_message_pnp: .asciz " PnP" .size init_message_pnp, . - init_message_pnp @@ -299,9 +389,6 @@ init_message_bbs: init_message_pmm: .asciz " PMM" .size init_message_pmm, . - init_message_pmm -init_message_pmm_failed: - .asciz "(failed)" - .size init_message_pmm_failed, . - init_message_pmm_failed init_message_int19: .asciz " INT19" .size init_message_int19, . - init_message_int19 @@ -334,6 +421,7 @@ decompress_to: */ bbs_version: .word 0 + .size bbs_version, . - bbs_version /* Boot Execution Vector entry point * @@ -374,9 +462,11 @@ exec: /* Set %ds = %cs */ popw %ds /* Print message as soon as possible */ - movw $exec_message, %si + movw $prodstr, %si xorw %di, %di call print_message + movw $exec_message, %si + call print_message /* Store magic word on BIOS stack and remember BIOS %ss:sp */ pushl $STACK_MAGIC @@ -424,7 +514,7 @@ exec: /* Set %ds = %cs */ .previous exec_message: - .asciz "Entering gPXE\n" + .asciz " starting execution\n" .size exec_message, . - exec_message /* UNDI loader @@ -435,18 +525,22 @@ undiloader: /* Save registers */ pushl %esi pushl %edi + pushw %ds pushw %es pushw %bx + /* ROM segment address to %ds */ + pushw %cs + popw %ds /* UNDI loader parameter structure address into %es:%di */ movw %sp, %bx - movw %ss:12(%bx), %di - movw %ss:14(%bx), %es + movw %ss:18(%bx), %di + movw %ss:20(%bx), %es /* Install to specified real-mode addresses */ pushw %di movw %es:12(%di), %bx movw %es:14(%di), %ax - movl %cs:image_source, %esi - movl %cs:decompress_to, %edi + movl image_source, %esi + movl decompress_to, %edi call install_prealloc popw %di /* Call UNDI loader C code */ @@ -461,6 +555,7 @@ undiloader: /* Restore registers and return */ popw %bx popw %es + popw %ds popl %edi popl %esi lret diff --git a/gpxe/src/arch/i386/scripts/i386.lds b/gpxe/src/arch/i386/scripts/i386.lds index a5a01056..729ad30a 100644 --- a/gpxe/src/arch/i386/scripts/i386.lds +++ b/gpxe/src/arch/i386/scripts/i386.lds @@ -31,6 +31,16 @@ SECTIONS { */ /* + * Weak symbols that need zero values if not otherwise defined + */ + + . = 0; + .weak : AT ( 0 ) { + *(.weak) + } + _assert = ASSERT ( ( . == 0 ), ".weak is non-zero length" ); + + /* * The prefix */ @@ -56,10 +66,12 @@ SECTIONS { . = _text16_link_addr; _text16 = .; - . += 1; /* Prevent NULL being valid */ + /* We need to allow code at the NULL address in .text16 */ .text16 : AT ( _text16_load_offset + __text16 ) { __text16 = .; + *(.text16.null) + . += 1; /* Prevent NULL being valid */ *(.text16) *(.text16.*) _etext16_progbits = .; diff --git a/gpxe/src/arch/i386/transitions/librm.S b/gpxe/src/arch/i386/transitions/librm.S index 45e0d0ff..ff4b1d97 100644 --- a/gpxe/src/arch/i386/transitions/librm.S +++ b/gpxe/src/arch/i386/transitions/librm.S @@ -552,7 +552,9 @@ rc_function: .word 0, 0 **************************************************************************** */ .section ".data" + .globl rm_sp rm_sp: .word 0 + .globl rm_ss rm_ss: .word 0 pm_esp: .long _estack diff --git a/gpxe/src/arch/i386/transitions/librm_mgmt.c b/gpxe/src/arch/i386/transitions/librm_mgmt.c new file mode 100644 index 00000000..59b2eabc --- /dev/null +++ b/gpxe/src/arch/i386/transitions/librm_mgmt.c @@ -0,0 +1,45 @@ +/*
+ * librm: a library for interfacing to real-mode code
+ *
+ * Michael Brown <mbrown@fensystems.co.uk>
+ *
+ */
+
+#include <stdint.h>
+#include <librm.h>
+
+/*
+ * This file provides functions for managing librm.
+ *
+ */
+
+/**
+ * Allocate space on the real-mode stack and copy data there from a
+ * user buffer
+ *
+ * @v data User buffer
+ * @v size Size of stack data
+ * @ret sp New value of real-mode stack pointer
+ */
+uint16_t copy_user_to_rm_stack ( userptr_t data, size_t size ) {
+ userptr_t rm_stack;
+ rm_sp -= size;
+ rm_stack = real_to_user ( rm_ss, rm_sp );
+ memcpy_user ( rm_stack, 0, data, 0, size );
+ return rm_sp;
+};
+
+/**
+ * Deallocate space on the real-mode stack, optionally copying back
+ * data to a user buffer.
+ *
+ * @v data User buffer
+ * @v size Size of stack data
+ */
+void remove_user_from_rm_stack ( userptr_t data, size_t size ) {
+ if ( data ) {
+ userptr_t rm_stack = real_to_user ( rm_ss, rm_sp );
+ memcpy_user ( rm_stack, 0, data, 0, size );
+ }
+ rm_sp += size;
+};
diff --git a/gpxe/src/config.h b/gpxe/src/config.h index f43da040..ae39fb55 100644 --- a/gpxe/src/config.h +++ b/gpxe/src/config.h @@ -58,6 +58,8 @@ */ #define TIMER_BIOS /* 18Hz BIOS timer */ #define TIMER_RDTSC /* CPU TimeStamp Counter timer */ +#define BANNER_TIMEOUT 20 /* Tenths of a second for which the shell + banner should appear */ /* @END general.h */ @@ -129,6 +131,7 @@ #define IMAGE_PXE /* PXE image support */ #define IMAGE_SCRIPT /* gPXE script image support */ #define IMAGE_BZIMAGE /* Linux bzImage image support */ +#define IMAGE_COMBOOT /* SYSLINUX COMBOOT image support */ /* @END general.h */ diff --git a/gpxe/src/core/config.c b/gpxe/src/core/config.c index 42026827..b5624fae 100644 --- a/gpxe/src/core/config.c +++ b/gpxe/src/core/config.c @@ -158,6 +158,14 @@ REQUIRE_OBJECT ( bzimage ); #ifdef IMAGE_ELTORITO REQUIRE_OBJECT ( eltorito ); #endif +#ifdef IMAGE_COMBOOT +REQUIRE_OBJECT ( comboot ); +REQUIRE_OBJECT ( com32 ); +REQUIRE_OBJECT ( comboot_call ); +REQUIRE_OBJECT ( com32_call ); +REQUIRE_OBJECT ( com32_wrapper ); +REQUIRE_OBJECT ( comboot_resolv ); +#endif /* * Drag in all requested commands @@ -205,3 +213,10 @@ REQUIRE_OBJECT ( gdbidt ); REQUIRE_OBJECT ( gdbudp ); REQUIRE_OBJECT ( gdbstub_cmd ); #endif + +/* + * Drag in objects that are always required, but not dragged in via + * symbol dependencies. + * + */ +REQUIRE_OBJECT ( device ); diff --git a/gpxe/src/core/device.c b/gpxe/src/core/device.c index b1b148e8..84915c2d 100644 --- a/gpxe/src/core/device.c +++ b/gpxe/src/core/device.c @@ -20,6 +20,7 @@ #include <gpxe/list.h> #include <gpxe/tables.h> #include <gpxe/device.h> +#include <gpxe/init.h> /** * @file @@ -68,13 +69,11 @@ static void rootdev_remove ( struct root_device *rootdev ) { /** * Probe all devices * - * @ret rc Return status code - * * This initiates probing for all devices in the system. After this * call, the device hierarchy will be populated, and all hardware * should be ready to use. */ -int probe_devices ( void ) { +static void probe_devices ( void ) { struct root_device *rootdev; int rc; @@ -84,19 +83,28 @@ int probe_devices ( void ) { if ( ( rc = rootdev_probe ( rootdev ) ) != 0 ) list_del ( &rootdev->dev.siblings ); } - return 0; } /** * Remove all devices * */ -void remove_devices ( void ) { +static void remove_devices ( int flags ) { struct root_device *rootdev; struct root_device *tmp; + if ( flags & SHUTDOWN_KEEP_DEVICES ) { + DBG ( "Refusing to remove devices on shutdown\n" ); + return; + } + list_for_each_entry_safe ( rootdev, tmp, &devices, dev.siblings ) { rootdev_remove ( rootdev ); list_del ( &rootdev->dev.siblings ); } } + +struct startup_fn startup_devices __startup_fn ( STARTUP_NORMAL ) = { + .startup = probe_devices, + .shutdown = remove_devices, +}; diff --git a/gpxe/src/core/exec.c b/gpxe/src/core/exec.c index a1c073e3..a9861b60 100644 --- a/gpxe/src/core/exec.c +++ b/gpxe/src/core/exec.c @@ -26,6 +26,7 @@ #include <assert.h> #include <gpxe/tables.h> #include <gpxe/command.h> +#include <gpxe/settings.h> /** @file * @@ -87,6 +88,76 @@ int execv ( const char *command, char * const argv[] ) { } /** + * 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 opener */ + start = strstr ( expcmd, "${" ); + if ( ! start ) + break; + *start = '\0'; + name = ( start + 2 ); + + /* Locate closer */ + end = strstr ( name, "}" ); + if ( ! end ) + break; + *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 argv array * * @v args Command line @@ -135,8 +206,8 @@ int system ( const char *command ) { int argc; int rc = 0; - /* Obtain temporary modifiable copy of command line */ - args = strdup ( command ); + /* Perform variable expansion */ + args = expand_command ( command ); if ( ! args ) return -ENOMEM; @@ -157,3 +228,26 @@ int system ( const char *command ) { free ( args ); return rc; } + +/** + * The "echo" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Exit code + */ +static int echo_exec ( int argc, char **argv ) { + int i; + + for ( i = 1 ; i < argc ; i++ ) { + printf ( "%s%s", ( ( i == 1 ) ? "" : " " ), argv[i] ); + } + printf ( "\n" ); + return 0; +} + +/** "echo" command */ +struct command echo_command __command = { + .name = "echo", + .exec = echo_exec, +}; diff --git a/gpxe/src/core/ibft.c b/gpxe/src/core/ibft.c index fda14704..ffa65964 100644 --- a/gpxe/src/core/ibft.c +++ b/gpxe/src/core/ibft.c @@ -165,16 +165,19 @@ static int ibft_alloc_string ( struct ibft_string_block *strings, * * @v strings iBFT string block descriptor * @v string String field - * @v data String to fill in + * @v data String to fill in, or NULL * @ret rc Return status code */ static int ibft_set_string ( struct ibft_string_block *strings, struct ibft_string *string, const char *data ) { - size_t len = strlen ( data ); char *dest; int rc; - if ( ( rc = ibft_alloc_string ( strings, string, len ) ) != 0 ) + if ( ! data ) + return 0; + + if ( ( rc = ibft_alloc_string ( strings, string, + strlen ( data ) ) ) != 0 ) return rc; dest = ( ( ( char * ) strings->table ) + string->offset ); strcpy ( dest, data ); @@ -271,6 +274,62 @@ static int ibft_fill_initiator ( struct ibft_initiator *initiator, } /** + * Fill in Target CHAP portion of iBFT + * + * @v target Target portion of iBFT + * @v strings iBFT string block descriptor + * @v iscsi iSCSI session + * @ret rc Return status code + */ +static int ibft_fill_target_chap ( struct ibft_target *target, + struct ibft_string_block *strings, + struct iscsi_session *iscsi ) { + int rc; + + if ( ! iscsi->initiator_username ) + return 0; + assert ( iscsi->initiator_password ); + + target->chap_type = IBFT_CHAP_ONE_WAY; + if ( ( rc = ibft_set_string ( strings, &target->chap_name, + iscsi->initiator_username ) ) != 0 ) + return rc; + if ( ( rc = ibft_set_string ( strings, &target->chap_secret, + iscsi->initiator_password ) ) != 0 ) + return rc; + return 0; +} + +/** + * Fill in Target Reverse CHAP portion of iBFT + * + * @v target Target portion of iBFT + * @v strings iBFT string block descriptor + * @v iscsi iSCSI session + * @ret rc Return status code + */ +static int ibft_fill_target_reverse_chap ( struct ibft_target *target, + struct ibft_string_block *strings, + struct iscsi_session *iscsi ) { + int rc; + + if ( ! iscsi->target_username ) + return 0; + assert ( iscsi->target_password ); + assert ( iscsi->initiator_username ); + assert ( iscsi->initiator_password ); + + target->chap_type = IBFT_CHAP_MUTUAL; + if ( ( rc = ibft_set_string ( strings, &target->reverse_chap_name, + iscsi->target_username ) ) != 0 ) + return rc; + if ( ( rc = ibft_set_string ( strings, &target->reverse_chap_secret, + iscsi->target_password ) ) != 0 ) + return rc; + return 0; +} + +/** * Fill in Target portion of iBFT * * @v target Target portion of iBFT @@ -291,17 +350,11 @@ static int ibft_fill_target ( struct ibft_target *target, if ( ( rc = ibft_set_string ( strings, &target->target_name, iscsi->target_iqn ) ) != 0 ) return rc; - if ( iscsi->username ) { - if ( ( rc = ibft_set_string ( strings, &target->chap_name, - iscsi->username ) ) != 0 ) - return rc; - } - if ( iscsi->password ) { - if ( ( rc = ibft_set_string ( strings, &target->chap_secret, - iscsi->password ) ) != 0 ) - return rc; - target->chap_type = IBFT_CHAP_ONE_WAY; - } + if ( ( rc = ibft_fill_target_chap ( target, strings, iscsi ) ) != 0 ) + return rc; + if ( ( rc = ibft_fill_target_reverse_chap ( target, strings, + iscsi ) ) != 0 ) + return rc; return 0; } diff --git a/gpxe/src/core/init.c b/gpxe/src/core/init.c index ed91bf36..50e199ce 100644 --- a/gpxe/src/core/init.c +++ b/gpxe/src/core/init.c @@ -79,17 +79,14 @@ void startup ( void ) { startup_fn->startup(); } - /* Probe for all devices. Treated separately because nothing - * else will drag in device.o - */ - probe_devices(); - started = 1; } /** * Shut down gPXE * + * @v flags Shutdown behaviour flags + * * This function reverses the actions of startup(), and leaves gPXE in * a state ready to be removed from memory. You may call startup() * again after calling shutdown(). @@ -97,20 +94,17 @@ void startup ( void ) { * Call this function only once, before either exiting main() or * starting up a non-returnable image. */ -void shutdown ( void ) { +void shutdown ( int flags ) { struct startup_fn *startup_fn; if ( ! started ) return; - /* Remove all devices */ - remove_devices(); - /* Call registered shutdown functions (in reverse order) */ for ( startup_fn = startup_fns_end - 1 ; startup_fn >= startup_fns ; startup_fn-- ) { if ( startup_fn->shutdown ) - startup_fn->shutdown(); + startup_fn->shutdown ( flags ); } started = 0; diff --git a/gpxe/src/core/main.c b/gpxe/src/core/main.c index ca62db25..d5892261 100644 --- a/gpxe/src/core/main.c +++ b/gpxe/src/core/main.c @@ -62,7 +62,7 @@ __cdecl int main ( void ) { shell(); } - shutdown(); + shutdown ( SHUTDOWN_EXIT | shutdown_exit_flags ); return 0; } diff --git a/gpxe/src/core/monojob.c b/gpxe/src/core/monojob.c index ea9bc834..2c91e132 100644 --- a/gpxe/src/core/monojob.c +++ b/gpxe/src/core/monojob.c @@ -24,6 +24,7 @@ #include <gpxe/keys.h> #include <gpxe/job.h> #include <gpxe/monojob.h> +#include <gpxe/timer.h> /** @file * @@ -62,9 +63,11 @@ struct job_interface monojob = { int monojob_wait ( const char *string ) { int key; int rc; + tick_t last_progress_dot; - printf ( "%s... ", string ); + printf ( "%s.", string ); monojob_rc = -EINPROGRESS; + last_progress_dot = currticks(); while ( monojob_rc == -EINPROGRESS ) { step(); if ( iskey() ) { @@ -78,14 +81,18 @@ int monojob_wait ( const char *string ) { break; } } + if ( ( currticks() - last_progress_dot ) > TICKS_PER_SEC ) { + printf ( "." ); + last_progress_dot = currticks(); + } } rc = monojob_rc; done: if ( rc ) { - printf ( "%s\n", strerror ( rc ) ); + printf ( " %s\n", strerror ( rc ) ); } else { - printf ( "ok\n" ); + printf ( " ok\n" ); } return rc; } diff --git a/gpxe/src/core/serial.c b/gpxe/src/core/serial.c index 54c22954..97640f93 100644 --- a/gpxe/src/core/serial.c +++ b/gpxe/src/core/serial.c @@ -224,7 +224,7 @@ static void serial_init ( void ) { * Cleanup our use of the serial port, in particular flush the * output buffer so we don't accidentially lose characters. */ -static void serial_fini ( void ) { +static void serial_fini ( int flags __unused ) { int i, status; /* Flush the output buffer to avoid dropping characters, * if we are reinitializing the serial port. @@ -247,6 +247,6 @@ struct init_fn serial_init_fn __init_fn ( INIT_SERIAL ) = { }; /** Serial driver startup function */ -struct startup_fn serial_startup_fn __startup_fn ( STARTUP_NORMAL ) = { +struct startup_fn serial_startup_fn __startup_fn ( STARTUP_EARLY ) = { .shutdown = serial_fini, }; diff --git a/gpxe/src/core/settings.c b/gpxe/src/core/settings.c index 75253186..e660ae7c 100644 --- a/gpxe/src/core/settings.c +++ b/gpxe/src/core/settings.c @@ -381,7 +381,8 @@ int fetch_setting_len ( struct settings *settings, struct setting *setting ) { int fetch_string_setting ( struct settings *settings, struct setting *setting, char *data, size_t len ) { memset ( data, 0, len ); - return fetch_setting ( settings, setting, data, ( len - 1 ) ); + return fetch_setting ( settings, setting, data, + ( ( len > 0 ) ? ( len - 1 ) : 0 ) ); } /** diff --git a/gpxe/src/crypto/chap.c b/gpxe/src/crypto/chap.c index 13b8fda2..59b70e39 100644 --- a/gpxe/src/crypto/chap.c +++ b/gpxe/src/crypto/chap.c @@ -41,7 +41,7 @@ * allocates memory, and so may fail. The allocated memory must * eventually be freed by a call to chap_finish(). */ -int chap_init ( struct chap_challenge *chap, +int chap_init ( struct chap_response *chap, struct crypto_algorithm *digest ) { size_t state_len; void *state; @@ -71,11 +71,11 @@ int chap_init ( struct chap_challenge *chap, /** * Add data to the CHAP challenge * - * @v chap CHAP challenge/response + * @v chap CHAP response * @v data Data to add * @v len Length of data to add */ -void chap_update ( struct chap_challenge *chap, const void *data, +void chap_update ( struct chap_response *chap, const void *data, size_t len ) { assert ( chap->digest != NULL ); assert ( chap->digest_context != NULL ); @@ -89,12 +89,12 @@ void chap_update ( struct chap_challenge *chap, const void *data, /** * Respond to the CHAP challenge * - * @v chap CHAP challenge/response + * @v chap CHAP response * * Calculates the final CHAP response value, and places it in @c * chap->response, with a length of @c chap->response_len. */ -void chap_respond ( struct chap_challenge *chap ) { +void chap_respond ( struct chap_response *chap ) { assert ( chap->digest != NULL ); assert ( chap->digest_context != NULL ); assert ( chap->response != NULL ); @@ -108,11 +108,11 @@ void chap_respond ( struct chap_challenge *chap ) { } /** - * Free resources used by a CHAP challenge/response + * Free resources used by a CHAP response * - * @v chap CHAP challenge/response + * @v chap CHAP response */ -void chap_finish ( struct chap_challenge *chap ) { +void chap_finish ( struct chap_response *chap ) { void *state = chap->digest_context; DBG ( "CHAP %p finished\n", chap ); diff --git a/gpxe/src/drivers/infiniband/hermon.c b/gpxe/src/drivers/infiniband/hermon.c index c198556e..439974eb 100644 --- a/gpxe/src/drivers/infiniband/hermon.c +++ b/gpxe/src/drivers/infiniband/hermon.c @@ -2290,6 +2290,7 @@ static void hermon_remove ( struct pci_device *pci ) { static struct pci_device_id hermon_nics[] = { PCI_ROM ( 0x15b3, 0x6340, "mt25408", "MT25408 HCA driver" ), PCI_ROM ( 0x15b3, 0x634a, "mt25418", "MT25418 HCA driver" ), + PCI_ROM ( 0x15b3, 0x6732, "mt26418", "MT26418 HCA driver" ), }; struct pci_driver hermon_driver __pci_driver = { diff --git a/gpxe/src/drivers/net/forcedeth.c b/gpxe/src/drivers/net/forcedeth.c index f6195885..54fadbd7 100644 --- a/gpxe/src/drivers/net/forcedeth.c +++ b/gpxe/src/drivers/net/forcedeth.c @@ -82,6 +82,7 @@ static unsigned long BASE; #define PCI_DEVICE_ID_NVIDIA_NVENET_9 0x0057 #define PCI_DEVICE_ID_NVIDIA_NVENET_10 0x0037 #define PCI_DEVICE_ID_NVIDIA_NVENET_11 0x0038 +#define PCI_DEVICE_ID_NVIDIA_NVENET_15 0x0373 /* @@ -1338,6 +1339,8 @@ static int forcedeth_probe ( struct nic *nic, struct pci_device *pci ) { else np->tx_flags |= NV_TX2_LASTPACKET1; break; + case 0x0373: + /* Fall Through */ case 0x0086: /* Fall Through */ case 0x008c: @@ -1420,6 +1423,7 @@ PCI_ROM(0x10de, 0x0056, "nforce8", "nForce NVENET_8 Ethernet Controller"), PCI_ROM(0x10de, 0x0057, "nforce9", "nForce NVENET_9 Ethernet Controller"), PCI_ROM(0x10de, 0x0037, "nforce10", "nForce NVENET_10 Ethernet Controller"), PCI_ROM(0x10de, 0x0038, "nforce11", "nForce NVENET_11 Ethernet Controller"), +PCI_ROM(0x10de, 0x0373, "nforce15", "nForce NVENET_15 Ethernet Controller") }; PCI_DRIVER ( forcedeth_driver, forcedeth_nics, PCI_NO_CLASS ); diff --git a/gpxe/src/drivers/net/ipoib.c b/gpxe/src/drivers/net/ipoib.c index e3baa14f..16b2a0c8 100644 --- a/gpxe/src/drivers/net/ipoib.c +++ b/gpxe/src/drivers/net/ipoib.c @@ -153,18 +153,17 @@ static struct ipoib_mac ipoib_broadcast = { }; /** - * Transmit IPoIB packet + * Add IPoIB link-layer header * * @v iobuf I/O buffer * @v netdev Network device * @v net_protocol Network-layer protocol * @v ll_dest Link-layer destination address - * - * Prepends the IPoIB link-layer header and transmits the packet. */ -static int ipoib_tx ( struct io_buffer *iobuf, struct net_device *netdev, - struct net_protocol *net_protocol, - const void *ll_dest ) { +static int ipoib_push ( struct io_buffer *iobuf, + struct net_device *netdev __unused, + struct net_protocol *net_protocol, + const void *ll_dest ) { struct ipoib_hdr *ipoib_hdr = iob_push ( iobuf, sizeof ( *ipoib_hdr ) ); @@ -174,36 +173,38 @@ static int ipoib_tx ( struct io_buffer *iobuf, struct net_device *netdev, ipoib_hdr->real.proto = net_protocol->net_proto; ipoib_hdr->real.reserved = 0; - /* Hand off to network device */ - return netdev_tx ( netdev, iobuf ); + return 0; } /** - * Process received IPoIB packet - * - * @v iobuf I/O buffer - * @v netdev Network device + * Remove IPoIB link-layer header * - * Strips off the IPoIB link-layer header and passes up to the - * network-layer protocol. + * @v iobuf I/O buffer + * @v netdev Network device + * @v net_proto Network-layer protocol, in network-byte order + * @v ll_source Source link-layer address + * @ret rc Return status code */ -static int ipoib_rx ( struct io_buffer *iobuf, struct net_device *netdev ) { +static int ipoib_pull ( struct io_buffer *iobuf, + struct net_device *netdev __unused, + uint16_t *net_proto, const void **ll_source ) { struct ipoib_hdr *ipoib_hdr = iobuf->data; /* Sanity check */ if ( iob_len ( iobuf ) < sizeof ( *ipoib_hdr ) ) { DBG ( "IPoIB packet too short for link-layer header\n" ); DBG_HD ( iobuf->data, iob_len ( iobuf ) ); - free_iob ( iobuf ); return -EINVAL; } /* Strip off IPoIB header */ iob_pull ( iobuf, sizeof ( *ipoib_hdr ) ); - /* Hand off to network-layer protocol */ - return net_rx ( iobuf, netdev, ipoib_hdr->real.proto, - &ipoib_hdr->pseudo.peer ); + /* Fill in required fields */ + *net_proto = ipoib_hdr->real.proto; + *ll_source = &ipoib_hdr->pseudo.peer; + + return 0; } /** @@ -231,8 +232,8 @@ struct ll_protocol ipoib_protocol __ll_protocol = { .ll_addr_len = IPOIB_ALEN, .ll_header_len = IPOIB_HLEN, .ll_broadcast = ( uint8_t * ) &ipoib_broadcast, - .tx = ipoib_tx, - .rx = ipoib_rx, + .push = ipoib_push, + .pull = ipoib_pull, .ntoa = ipoib_ntoa, }; diff --git a/gpxe/src/drivers/net/phantom/phantom.c b/gpxe/src/drivers/net/phantom/phantom.c index 509a7096..5644c96d 100644 --- a/gpxe/src/drivers/net/phantom/phantom.c +++ b/gpxe/src/drivers/net/phantom/phantom.c @@ -1554,9 +1554,6 @@ static int phantom_map_crb ( struct phantom_nic *phantom, unsigned long bar0_start; unsigned long bar0_size; - /* CRB window is always in the last 32MB of BAR0 (which may be - * a 32MB or a 128MB BAR). - */ bar0_start = pci_bar_start ( pci, PCI_BASE_ADDRESS_0 ); bar0_size = pci_bar_size ( pci, PCI_BASE_ADDRESS_0 ); DBGC ( phantom, "Phantom %p BAR0 is %08lx+%lx\n", @@ -1711,7 +1708,8 @@ static int phantom_init_cmdpeg ( struct phantom_nic *phantom ) { UNM_NIC_REG_DUMMY_BUF ); /* Tell the hardware that tuning is complete */ - phantom_writel ( phantom, 1, UNM_ROMUSB_GLB_PEGTUNE_DONE ); + phantom_writel ( phantom, UNM_ROMUSB_GLB_PEGTUNE_DONE_MAGIC, + UNM_ROMUSB_GLB_PEGTUNE_DONE ); /* Wait for command PEG to finish initialising */ DBGC ( phantom, "Phantom %p initialising command PEG (will take up to " @@ -1859,6 +1857,19 @@ static int phantom_probe ( struct pci_device *pci, phantom_port->port = i; } + /* BUG5945 - need to hack PCI config space on P3 B1 silicon. + * B2 will have this fixed; remove this hack when B1 is no + * longer in use. + */ + for ( i = 0 ; i < 8 ; i++ ) { + uint32_t temp; + pci->devfn = PCI_DEVFN ( PCI_SLOT ( pci->devfn ), i ); + pci_read_config_dword ( pci, 0xc8, &temp ); + pci_read_config_dword ( pci, 0xc8, &temp ); + pci_write_config_dword ( pci, 0xc8, 0xf1000 ); + } + pci->devfn = PCI_DEVFN ( PCI_SLOT ( pci->devfn ), 0 ); + /* Allocate dummy DMA buffer and perform initial hardware handshake */ phantom->dma_buf = malloc_dma ( sizeof ( *(phantom->dma_buf) ), UNM_DMA_BUFFER_ALIGN ); diff --git a/gpxe/src/drivers/net/phantom/phantom.h b/gpxe/src/drivers/net/phantom/phantom.h index 3c759989..110c1226 100644 --- a/gpxe/src/drivers/net/phantom/phantom.h +++ b/gpxe/src/drivers/net/phantom/phantom.h @@ -139,6 +139,7 @@ enum unm_reg_blocks { #define UNM_ROMUSB_GLB_SW_RESET ( UNM_ROMUSB_GLB + 0x00008 ) #define UNM_ROMUSB_GLB_SW_RESET_MAGIC 0x0080000fUL #define UNM_ROMUSB_GLB_PEGTUNE_DONE ( UNM_ROMUSB_GLB + 0x0005c ) +#define UNM_ROMUSB_GLB_PEGTUNE_DONE_MAGIC 0x31 #define UNM_ROMUSB_ROM ( UNM_CRB_ROMUSB + 0x10000 ) #define UNM_ROMUSB_ROM_INSTR_OPCODE ( UNM_ROMUSB_ROM + 0x00004 ) diff --git a/gpxe/src/drivers/net/tg3.c b/gpxe/src/drivers/net/tg3.c index 5d1bf407..82b37fab 100644 --- a/gpxe/src/drivers/net/tg3.c +++ b/gpxe/src/drivers/net/tg3.c @@ -1431,7 +1431,8 @@ static int tg3_stop_block(struct tg3 *tp, unsigned long ofs, uint32_t enable_bit unsigned int i; uint32_t val; - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) { + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787) { switch(ofs) { case RCVLSC_MODE: case DMAC_MODE: @@ -1439,7 +1440,7 @@ static int tg3_stop_block(struct tg3 *tp, unsigned long ofs, uint32_t enable_bit case BUFMGR_MODE: case MEMARB_MODE: /* We can't enable/disable these bits of the - * 5705, just say success. + * 5705 or 5787, just say success. */ return 0; default: @@ -1470,6 +1471,7 @@ static int tg3_stop_block(struct tg3 *tp, unsigned long ofs, uint32_t enable_bit static int tg3_abort_hw(struct tg3 *tp) { int i, err; + uint32_t val; tg3_disable_ints(tp); @@ -1513,8 +1515,14 @@ static int tg3_abort_hw(struct tg3 *tp) err |= tg3_stop_block(tp, WDMAC_MODE, WDMAC_MODE_ENABLE); err |= tg3_stop_block(tp, MBFREE_MODE, MBFREE_MODE_ENABLE); - tw32(FTQ_RESET, 0xffffffff); - tw32(FTQ_RESET, 0x00000000); + val = tr32(FTQ_RESET); + val |= FTQ_RESET_DMA_READ_QUEUE | FTQ_RESET_DMA_HIGH_PRI_READ | + FTQ_RESET_SEND_BD_COMPLETION | FTQ_RESET_DMA_WRITE | + FTQ_RESET_DMA_HIGH_PRI_WRITE | FTQ_RESET_SEND_DATA_COMPLETION | + FTQ_RESET_HOST_COALESCING | FTQ_RESET_MAC_TX | + FTQ_RESET_RX_BD_COMPLETE | FTQ_RESET_RX_LIST_PLCMT | + FTQ_RESET_RX_DATA_COMPLETION; + tw32(FTQ_RESET, val); err |= tg3_stop_block(tp, BUFMGR_MODE, BUFMGR_MODE_ENABLE); err |= tg3_stop_block(tp, MEMARB_MODE, MEMARB_MODE_ENABLE); @@ -1554,8 +1562,19 @@ static void tg3_chip_reset(struct tg3 *tp) // Alf: here patched /* do the reset */ val = GRC_MISC_CFG_CORECLK_RESET; + if (tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) { + if (tr32(0x7e2c) == 0x60) { + tw32(0x7e2c, 0x20); + } + if (tp->pci_chip_rev_id != CHIPREV_ID_5750_A0) { + tw32(GRC_MISC_CFG, (1 << 29)); + val |= (1 << 29); + } + } + if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) - || (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750)) { + || (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) + || (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787)) { val |= GRC_MISC_CFG_KEEP_GPHY_POWER; } @@ -1644,7 +1663,8 @@ static int tg3_restart_fw(struct tg3 *tp, uint32_t state) udelay(10); } if (i >= 100000 && - !(tp->tg3_flags2 & TG3_FLG2_SUN_5704)) { + !(tp->tg3_flags2 & TG3_FLG2_SUN_5704) && + !(GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787)) { printf ( "Firmware will not restart magic=%#lx\n", val ); return -ENODEV; @@ -1879,7 +1899,9 @@ static int tg3_setup_hw(struct tg3 *tp) (65 << GRC_MISC_CFG_PRESCALAR_SHIFT)); /* Initialize MBUF/DESC pool. */ - if ((GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) && + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787) { + /* Do nothing. */ + } else if ((GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) && (tp->pci_chip_rev_id != CHIPREV_ID_5721)) { tw32(BUFMGR_MB_POOL_ADDR, NIC_SRAM_MBUF_POOL_BASE); if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) @@ -1976,7 +1998,8 @@ static int tg3_setup_hw(struct tg3 *tp) TG3_WRITE_SETTINGS(table_all); tw32(RCVDBDI_STD_BD + TG3_BDINFO_HOST_ADDR + TG3_64BIT_REG_LOW, virt_to_bus(tp->rx_std)); - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) { + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787) { tw32(RCVDBDI_STD_BD + TG3_BDINFO_MAXLEN_FLAGS, RX_STD_MAX_SIZE_5705 << BDINFO_FLAGS_MAXLEN_SHIFT); } else { @@ -1985,10 +2008,11 @@ static int tg3_setup_hw(struct tg3 *tp) } - /* There is only one send ring on 5705, no need to explicitly + /* There is only one send ring on 5705 and 5787, no need to explicitly * disable the others. */ - if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) { + if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705 && + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5787) { /* Clear out send RCB ring in SRAM. */ for (i = NIC_SRAM_SEND_RCB; i < NIC_SRAM_RCV_RET_RCB; i += TG3_BDINFO_SIZE) tg3_write_mem(i + TG3_BDINFO_MAXLEN_FLAGS, BDINFO_FLAGS_DISABLED); @@ -2004,10 +2028,11 @@ static int tg3_setup_hw(struct tg3 *tp) (TG3_TX_RING_SIZE << BDINFO_FLAGS_MAXLEN_SHIFT), NIC_SRAM_TX_BUFFER_DESC); - /* There is only one receive return ring on 5705, no need to explicitly - * disable the others. + /* There is only one receive return ring on 5705 and 5787, no need to + * explicitly disable the others. */ - if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) { + if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705 && + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5787) { for (i = NIC_SRAM_RCV_RET_RCB; i < NIC_SRAM_STATS_BLK; i += TG3_BDINFO_SIZE) { tg3_write_mem(i + TG3_BDINFO_MAXLEN_FLAGS, BDINFO_FLAGS_DISABLED); @@ -2086,6 +2111,11 @@ static int tg3_setup_hw(struct tg3 *tp) !(tp->tg3_flags2 & TG3_FLG2_IS_5788)) { val |= WDMAC_MODE_RX_ACCEL; } + + /* Host coalescing bug fix */ + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787) + val |= (1 << 29); + tw32_carefully(WDMAC_MODE, val); if ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) != 0) { @@ -2182,7 +2212,8 @@ static int tg3_setup_hw(struct tg3 *tp) virt_to_bus(tp->hw_stats)); tw32(HOSTCC_STATUS_BLK_HOST_ADDR + TG3_64BIT_REG_LOW, virt_to_bus(tp->hw_status)); - if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) { + if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705 && + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5787) { TG3_WRITE_SETTINGS(table_not_5705); } } @@ -2762,15 +2793,9 @@ static int tg3_get_invariants(struct tg3 *tp) /* determine if it is PCIE system */ // Alf : I have no idea what this is about... // But it's definitely usefull - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) { - val = tr32(TG3PCI_MSI_CAP_ID) ; - if (((val >> 8) & 0xff) == T3_PCIE_CAPABILITY_ID_REG) { - val = tr32(T3_PCIE_CAPABILITY_ID_REG) ; - if ((val & 0xff) == T3_PCIE_CAPABILITY_ID) { - tp->tg3_flags2 |= TG3_FLG2_PCI_EXPRESS ; - } - } - } + val = pci_find_capability(tp->pdev, PCI_CAP_ID_EXP); + if (val) + tp->tg3_flags2 |= TG3_FLG2_PCI_EXPRESS; /* Force the chip into D0. */ tg3_set_power_state_0(tp); @@ -3010,6 +3035,7 @@ static const char * tg3_phy_string(struct tg3 *tp) case PHY_ID_BCM5705: return "5705"; case PHY_ID_BCM5750: return "5750"; case PHY_ID_BCM5751: return "5751"; + case PHY_ID_BCM5787: return "5787"; case PHY_ID_BCM8002: return "8002/serdes"; case PHY_ID_SERDES: return "serdes"; default: return "unknown"; @@ -3370,6 +3396,7 @@ PCI_ROM(0x14e4, 0x1659, "tg3-5721", "Broadcom Tigon 3 5721"), PCI_ROM(0x14e4, 0x165d, "tg3-5705M", "Broadcom Tigon 3 5705M"), PCI_ROM(0x14e4, 0x165e, "tg3-5705M_2", "Broadcom Tigon 3 5705M_2"), PCI_ROM(0x14e4, 0x1677, "tg3-5751", "Broadcom Tigon 3 5751"), +PCI_ROM(0x14e4, 0x167a, "tg3-5754", "Broadcom Tigon 3 5754"), PCI_ROM(0x14e4, 0x1696, "tg3-5782", "Broadcom Tigon 3 5782"), PCI_ROM(0x14e4, 0x169c, "tg3-5788", "Broadcom Tigon 3 5788"), PCI_ROM(0x14e4, 0x169d, "tg3-5789", "Broadcom Tigon 3 5789"), diff --git a/gpxe/src/drivers/net/tg3.h b/gpxe/src/drivers/net/tg3.h index 9077f80a..d1c09e03 100644 --- a/gpxe/src/drivers/net/tg3.h +++ b/gpxe/src/drivers/net/tg3.h @@ -294,6 +294,7 @@ typedef unsigned long dma_addr_t; #define ASIC_REV_5704 0x02 #define ASIC_REV_5705 0x03 #define ASIC_REV_5750 0x04 +#define ASIC_REV_5787 0x0b #define GET_CHIP_REV(CHIP_REV_ID) ((CHIP_REV_ID) >> 8) #define CHIPREV_5700_AX 0x70 #define CHIPREV_5700_BX 0x71 @@ -1273,6 +1274,17 @@ typedef unsigned long dma_addr_t; /* Flow Through queues */ #define FTQ_RESET 0x00005c00 +#define FTQ_RESET_DMA_READ_QUEUE (1 << 1) +#define FTQ_RESET_DMA_HIGH_PRI_READ (1 << 2) +#define FTQ_RESET_SEND_BD_COMPLETION (1 << 4) +#define FTQ_RESET_DMA_WRITE (1 << 6) +#define FTQ_RESET_DMA_HIGH_PRI_WRITE (1 << 7) +#define FTQ_RESET_SEND_DATA_COMPLETION (1 << 9) +#define FTQ_RESET_HOST_COALESCING (1 << 10) +#define FTQ_RESET_MAC_TX (1 << 11) +#define FTQ_RESET_RX_BD_COMPLETE (1 << 13) +#define FTQ_RESET_RX_LIST_PLCMT (1 << 14) +#define FTQ_RESET_RX_DATA_COMPLETION (1 << 16) /* 0x5c04 --> 0x5c10 unused */ #define FTQ_DMA_NORM_READ_CTL 0x00005c10 #define FTQ_DMA_NORM_READ_FULL_CNT 0x00005c14 @@ -2130,7 +2142,8 @@ struct tg3 { #define PHY_ID_BCM5703 0x60008160 #define PHY_ID_BCM5704 0x60008190 #define PHY_ID_BCM5705 0x600081a0 -#define PHY_ID_BCM5750 0x60008180 +#define PHY_ID_BCM5750 0x60008180 +#define PHY_ID_BCM5787 0xbc050ce0 #define PHY_ID_BCM8002 0x60010140 #define PHY_ID_BCM5751 0x00206180 #define PHY_ID_SERDES 0xfeedbee0 @@ -2157,7 +2170,8 @@ struct tg3 { ((X) == PHY_ID_BCM5400 || (X) == PHY_ID_BCM5401 || \ (X) == PHY_ID_BCM5411 || (X) == PHY_ID_BCM5701 || \ (X) == PHY_ID_BCM5703 || (X) == PHY_ID_BCM5704 || \ - (X) == PHY_ID_BCM5705 || (X) == PHY_ID_BCM5750 || (X) == PHY_ID_BCM5751 || \ + (X) == PHY_ID_BCM5705 || (X) == PHY_ID_BCM5750 || \ + (X) == PHY_ID_BCM5751 || (X) == PHY_ID_BCM5787 || \ (X) == PHY_ID_BCM8002 || (X) == PHY_ID_SERDES) unsigned long regs; diff --git a/gpxe/src/hci/commands/config_cmd.c b/gpxe/src/hci/commands/config_cmd.c index 5a34cea2..660c6423 100644 --- a/gpxe/src/hci/commands/config_cmd.c +++ b/gpxe/src/hci/commands/config_cmd.c @@ -5,7 +5,8 @@ #include <gpxe/settings_ui.h> static int config_exec ( int argc, char **argv ) { - struct settings *settings = NULL; + char *settings_name; + struct settings *settings; int rc; if ( argc > 2 ) { @@ -14,12 +15,11 @@ static int config_exec ( int argc, char **argv ) { return 1; } - if ( argc == 2 ) { - settings = find_settings ( argv[1] ); - if ( ! settings ) { - printf ( "No such scope \"%s\"\n", argv[1] ); - return 1; - } + settings_name = ( ( argc == 2 ) ? argv[1] : "" ); + settings = find_settings ( argv[1] ); + if ( ! settings ) { + printf ( "No such scope \"%s\"\n", settings_name ); + return 1; } if ( ( rc = settings_ui ( settings ) ) != 0 ) { diff --git a/gpxe/src/hci/commands/image_cmd.c b/gpxe/src/hci/commands/image_cmd.c index b651078b..d1a38c47 100644 --- a/gpxe/src/hci/commands/image_cmd.c +++ b/gpxe/src/hci/commands/image_cmd.c @@ -24,7 +24,6 @@ #include <getopt.h> #include <gpxe/image.h> #include <gpxe/command.h> -#include <gpxe/initrd.h> #include <usr/imgmgmt.h> /** @file @@ -223,23 +222,6 @@ static int kernel_exec ( int argc, char **argv ) { } /** - * The "initrd" command - * - * @v argc Argument count - * @v argv Argument list - * @ret rc Exit code - */ -static int initrd_exec ( int argc, char **argv ) { - int rc; - - if ( ( rc = imgfetch_core_exec ( &initrd_image_type, IMG_FETCH, - argc, argv ) ) != 0 ) - return rc; - - return 0; -} - -/** * "imgload" command syntax message * * @v argv Argument list @@ -425,7 +407,7 @@ static int imgexec_exec ( int argc, char **argv ) { } else { image = imgautoselect(); if ( ! image ) { - printf ( "No loaded images\n" ); + printf ( "No (unique) loaded image\n" ); return 1; } } @@ -557,12 +539,12 @@ struct command image_commands[] __command = { .exec = imgfetch_exec, /* synonym for "imgfetch" */ }, { - .name = "kernel", - .exec = kernel_exec, + .name = "initrd", + .exec = imgfetch_exec, /* synonym for "imgfetch" */ }, { - .name = "initrd", - .exec = initrd_exec, + .name = "kernel", + .exec = kernel_exec, }, { .name = "imgload", diff --git a/gpxe/src/hci/shell_banner.c b/gpxe/src/hci/shell_banner.c index 92cd17dd..3271c483 100644 --- a/gpxe/src/hci/shell_banner.c +++ b/gpxe/src/hci/shell_banner.c @@ -18,7 +18,9 @@ #include <stdio.h> #include <console.h> -#include <gpxe/timer.h> +#include <unistd.h> +#include <config/general.h> +#include <gpxe/keys.h> #include <gpxe/shell_banner.h> /** @file @@ -27,28 +29,27 @@ * */ -#define BANNER_TIMEOUT ( 2 * TICKS_PER_SEC ) - /** * Print shell banner and prompt for shell entry * * @ret enter_shell User wants to enter shell */ int shell_banner ( void ) { - unsigned long timeout = ( currticks() + BANNER_TIMEOUT ); int enter_shell = 0; + int wait_count; int key; printf ( "\nPress Ctrl-B for the gPXE command line..." ); /* Wait for key */ - while ( currticks() < timeout ) { + for ( wait_count = 0 ; wait_count < BANNER_TIMEOUT ; wait_count++ ) { if ( iskey() ) { key = getchar(); - if ( key == 0x02 /* Ctrl-B */ ) + if ( key == CTRL_B ) enter_shell = 1; break; } + mdelay(100); } /* Clear the "Press Ctrl-B" line */ diff --git a/gpxe/src/image/initrd.c b/gpxe/src/image/initrd.c deleted file mode 100644 index f03564a8..00000000 --- a/gpxe/src/image/initrd.c +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -/** - * @file - * - * Linux initrd image format - * - * This file does nothing except provide a way to mark images as being - * initrds. The actual processing is done in the Linux kernel image - * code; this file exists so that we can include the "initrd" command - * without necessarily dragging in the Linux image format. - * - */ - -#include <gpxe/image.h> -#include <gpxe/initrd.h> - -/** Linux initrd image type */ -struct image_type initrd_image_type __image_type ( PROBE_NORMAL ) = { - .name = "initrd", -}; diff --git a/gpxe/src/image/segment.c b/gpxe/src/image/segment.c index 8deaef79..9bd60f9c 100644 --- a/gpxe/src/image/segment.c +++ b/gpxe/src/image/segment.c @@ -43,6 +43,8 @@ int prep_segment ( userptr_t segment, size_t filesz, size_t memsz ) { physaddr_t end = user_to_phys ( segment, memsz ); unsigned int i; + DBG ( "Preparing segment [%lx,%lx,%lx)\n", start, mid, end ); + /* Sanity check */ if ( filesz > memsz ) { DBG ( "Insane segment [%lx,%lx,%lx)\n", start, mid, end ); diff --git a/gpxe/src/include/gpxe/chap.h b/gpxe/src/include/gpxe/chap.h index 0ea7ac5c..a7059cdb 100644 --- a/gpxe/src/include/gpxe/chap.h +++ b/gpxe/src/include/gpxe/chap.h @@ -12,8 +12,8 @@ struct crypto_algorithm; -/** A CHAP challenge/response */ -struct chap_challenge { +/** A CHAP response */ +struct chap_response { /** Digest algorithm used for the response */ struct crypto_algorithm *digest; /** Context used by the digest algorithm */ @@ -24,24 +24,24 @@ struct chap_challenge { size_t response_len; }; -extern int chap_init ( struct chap_challenge *chap, +extern int chap_init ( struct chap_response *chap, struct crypto_algorithm *digest ); -extern void chap_update ( struct chap_challenge *chap, const void *data, +extern void chap_update ( struct chap_response *chap, const void *data, size_t len ); -extern void chap_respond ( struct chap_challenge *chap ); -extern void chap_finish ( struct chap_challenge *chap ); +extern void chap_respond ( struct chap_response *chap ); +extern void chap_finish ( struct chap_response *chap ); /** * Add identifier data to the CHAP challenge * - * @v chap CHAP challenge/response + * @v chap CHAP response * @v identifier CHAP identifier * * The CHAP identifier is the first byte of the CHAP challenge. This * function is a notational convenience for calling chap_update() for * the identifier byte. */ -static inline void chap_set_identifier ( struct chap_challenge *chap, +static inline void chap_set_identifier ( struct chap_response *chap, unsigned int identifier ) { uint8_t ident_byte = identifier; diff --git a/gpxe/src/include/gpxe/device.h b/gpxe/src/include/gpxe/device.h index caabdae5..f40cc95a 100644 --- a/gpxe/src/include/gpxe/device.h +++ b/gpxe/src/include/gpxe/device.h @@ -105,7 +105,4 @@ struct root_driver { /** Declare a root device */ #define __root_device __table ( struct root_device, root_devices, 01 ) -extern int probe_devices ( void ); -extern void remove_devices ( void ); - #endif /* _GPXE_DEVICE_H */ diff --git a/gpxe/src/include/gpxe/dhcp.h b/gpxe/src/include/gpxe/dhcp.h index 61445977..c5ed0ead 100644 --- a/gpxe/src/include/gpxe/dhcp.h +++ b/gpxe/src/include/gpxe/dhcp.h @@ -164,7 +164,7 @@ struct dhcp_packet; * priority of multiple option blocks (e.g. options from non-volatile * storage versus options from a DHCP server). */ -#define DHCP_EB_PRIORITY DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 1 ) +#define DHCP_EB_PRIORITY DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0x01 ) /** "Your" IP address * @@ -172,7 +172,7 @@ struct dhcp_packet; * field, in order to provide a consistent approach to storing and * processing options. It should never be present in a DHCP packet. */ -#define DHCP_EB_YIADDR DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 2 ) +#define DHCP_EB_YIADDR DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0x02 ) /** "Server" IP address * @@ -180,7 +180,16 @@ struct dhcp_packet; * field, in order to provide a consistent approach to storing and * processing options. It should never be present in a DHCP packet. */ -#define DHCP_EB_SIADDR DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 3 ) +#define DHCP_EB_SIADDR DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0x03 ) + +/** Keep SAN drive registered + * + * If set to a non-zero value, gPXE will not detach any SAN drive + * after failing to boot from it. (This option is required in order + * to perform a Windows Server 2008 installation direct to an iSCSI + * target.) + */ +#define DHCP_EB_KEEP_SAN DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0x08 ) /* * Tags in the range 0x10-0x7f are reserved for feature markers @@ -232,6 +241,24 @@ struct dhcp_packet; */ #define DHCP_EB_PASSWORD DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xbf ) +/** Reverse username + * + * This will be used as the reverse username (i.e. the username + * provided by the server) for any required authentication. It is + * expected that this option's value will be held in non-volatile + * storage, rather than transmitted as part of a DHCP packet. + */ +#define DHCP_EB_REVERSE_USERNAME DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xc0 ) + +/** Reverse password + * + * This will be used as the reverse password (i.e. the password + * provided by the server) for any required authentication. It is + * expected that this option's value will be held in non-volatile + * storage, rather than transmitted as part of a DHCP packet. + */ +#define DHCP_EB_REVERSE_PASSWORD DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xc1 ) + /** iSCSI primary target IQN */ #define DHCP_ISCSI_PRIMARY_TARGET_IQN 201 @@ -439,6 +466,10 @@ struct dhcphdr { /** Maximum time that we will wait for ProxyDHCP responses */ #define PROXYDHCP_WAIT_TIME ( TICKS_PER_SEC * 1 ) +/** Timeouts for sending DHCP packets */ +#define DHCP_MIN_TIMEOUT ( 1 * TICKS_PER_SEC ) +#define DHCP_MAX_TIMEOUT ( 10 * TICKS_PER_SEC ) + /** Settings block name used for DHCP responses */ #define DHCP_SETTINGS_NAME "dhcp" diff --git a/gpxe/src/include/gpxe/errfile.h b/gpxe/src/include/gpxe/errfile.h index a0591952..ca0abebf 100644 --- a/gpxe/src/include/gpxe/errfile.h +++ b/gpxe/src/include/gpxe/errfile.h @@ -105,6 +105,7 @@ #define ERRFILE_e1000 ( ERRFILE_DRIVER | 0x00480000 ) #define ERRFILE_e1000_hw ( ERRFILE_DRIVER | 0x00490000 ) #define ERRFILE_mtnic ( ERRFILE_DRIVER | 0x004a0000 ) +#define ERRFILE_phantom ( ERRFILE_DRIVER | 0x004b0000 ) #define ERRFILE_scsi ( ERRFILE_DRIVER | 0x00700000 ) #define ERRFILE_arbel ( ERRFILE_DRIVER | 0x00710000 ) @@ -154,6 +155,7 @@ #define ERRFILE_ibft ( ERRFILE_OTHER | 0x000c0000 ) #define ERRFILE_tls ( ERRFILE_OTHER | 0x000d0000 ) #define ERRFILE_ifmgmt ( ERRFILE_OTHER | 0x000e0000 ) +#define ERRFILE_iscsiboot ( ERRFILE_OTHER | 0x000f0000 ) /** @} */ diff --git a/gpxe/src/include/gpxe/features.h b/gpxe/src/include/gpxe/features.h index 70daac37..107ff085 100644 --- a/gpxe/src/include/gpxe/features.h +++ b/gpxe/src/include/gpxe/features.h @@ -45,6 +45,7 @@ #define DHCP_EB_FEATURE_NBI 0x20 /**< NBI format */ #define DHCP_EB_FEATURE_PXE 0x21 /**< PXE format */ #define DHCP_EB_FEATURE_ELF 0x22 /**< ELF format */ +#define DHCP_EB_FEATURE_COMBOOT 0x23 /**< COMBOOT format */ /** @} */ diff --git a/gpxe/src/include/gpxe/hidemem.h b/gpxe/src/include/gpxe/hidemem.h index 547f8881..010fdb58 100644 --- a/gpxe/src/include/gpxe/hidemem.h +++ b/gpxe/src/include/gpxe/hidemem.h @@ -8,16 +8,8 @@ * */ -/** - * Unique IDs for hidden regions - */ -enum hidemem_region_id { - TEXT = 0, - BASEMEM, - EXTMEM, -}; +#include <stdint.h> -extern void hide_region ( unsigned int region_id, physaddr_t start, - physaddr_t end ); +extern void hide_umalloc ( physaddr_t start, physaddr_t end ); #endif /* _GPXE_HIDEMEM_H */ diff --git a/gpxe/src/include/gpxe/init.h b/gpxe/src/include/gpxe/init.h index c468213e..d2b450d7 100644 --- a/gpxe/src/include/gpxe/init.h +++ b/gpxe/src/include/gpxe/init.h @@ -28,6 +28,16 @@ struct init_fn { /** @} */ +/** Shutdown flags */ +enum shutdown_flags { + /** Shutdown is in order to exit (return to gPXE'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 * @@ -36,7 +46,7 @@ struct init_fn { */ struct startup_fn { void ( * startup ) ( void ); - void ( * shutdown ) ( void ); + void ( * shutdown ) ( int flags ); }; /** Declare a startup/shutdown function */ @@ -58,6 +68,6 @@ struct startup_fn { extern void initialise ( void ); extern void startup ( void ); -extern void shutdown ( void ); +extern void shutdown ( int flags ); #endif /* _GPXE_INIT_H */ diff --git a/gpxe/src/include/gpxe/initrd.h b/gpxe/src/include/gpxe/initrd.h deleted file mode 100644 index 1871bf78..00000000 --- a/gpxe/src/include/gpxe/initrd.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef _GPXE_INITRD_H -#define _GPXE_INITRD_H - -/** - * @file - * - * Linux initrd image format - * - */ - -#include <gpxe/image.h> -extern struct image_type initrd_image_type __image_type ( PROBE_NORMAL ); - -#endif /* _GPXE_INITRD_H */ diff --git a/gpxe/src/include/gpxe/iscsi.h b/gpxe/src/include/gpxe/iscsi.h index 5c446757..fd96fdfe 100644 --- a/gpxe/src/include/gpxe/iscsi.h +++ b/gpxe/src/include/gpxe/iscsi.h @@ -522,12 +522,25 @@ struct iscsi_session { */ int retry_count; - /** Username (if any) */ - char *username; - /** Password (if any) */ - char *password; - /** CHAP challenge/response */ - struct chap_challenge chap; + /** Initiator username (if any) */ + char *initiator_username; + /** Initiator password (if any) */ + char *initiator_password; + /** Target username (if any) */ + char *target_username; + /** Target password (if any) */ + char *target_password; + /** Target has authenticated acceptably */ + int target_auth_ok; + /** CHAP challenge (for target auth only) + * + * This is a block of random data; the first byte is used as + * the CHAP identifier (CHAP_I) and the remainder as the CHAP + * challenge (CHAP_C). + */ + unsigned char chap_challenge[17]; + /** CHAP response (used for both initiator and target auth) */ + struct chap_response chap; /** Target session identifying handle * @@ -642,8 +655,11 @@ struct iscsi_session { /** iSCSI session needs to send the CHAP response */ #define ISCSI_STATUS_STRINGS_CHAP_RESPONSE 0x0400 +/** iSCSI session needs to send the mutual CHAP challenge */ +#define ISCSI_STATUS_STRINGS_CHAP_CHALLENGE 0x0800 + /** iSCSI session needs to send the operational negotiation strings */ -#define ISCSI_STATUS_STRINGS_OPERATIONAL 0x0800 +#define ISCSI_STATUS_STRINGS_OPERATIONAL 0x1000 /** Mask for all iSCSI "needs to send" flags */ #define ISCSI_STATUS_STRINGS_MASK 0xff00 diff --git a/gpxe/src/include/gpxe/netdevice.h b/gpxe/src/include/gpxe/netdevice.h index 1ef648e1..cdc8cbad 100644 --- a/gpxe/src/include/gpxe/netdevice.h +++ b/gpxe/src/include/gpxe/netdevice.h @@ -76,7 +76,7 @@ struct ll_protocol { /** Protocol name */ const char *name; /** - * Transmit network-layer packet via network device + * Add link-layer header * * @v iobuf I/O buffer * @v netdev Network device @@ -85,24 +85,28 @@ struct ll_protocol { * @ret rc Return status code * * This method should prepend in the link-layer header - * (e.g. the Ethernet DIX header) and transmit the packet. - * This method takes ownership of the I/O buffer. + * (e.g. the Ethernet DIX header). */ - int ( * tx ) ( struct io_buffer *iobuf, struct net_device *netdev, - struct net_protocol *net_protocol, - const void *ll_dest ); + int ( * push ) ( struct io_buffer *iobuf, struct net_device *netdev, + struct net_protocol *net_protocol, + const void *ll_dest ); /** - * Handle received packet + * Remove link-layer header * * @v iobuf I/O buffer * @v netdev Network device + * @v net_proto Network-layer protocol, in network-byte order + * @v ll_source Source link-layer address + * @ret rc Return status code * * This method should strip off the link-layer header - * (e.g. the Ethernet DIX header) and pass the packet to - * net_rx(). This method takes ownership of the packet - * buffer. + * (e.g. the Ethernet DIX header) and return the protocol and + * source link-layer address. The method must not alter the + * packet content, and may return the link-layer address as a + * pointer to data within the packet. */ - int ( * rx ) ( struct io_buffer *iobuf, struct net_device *netdev ); + int ( * pull ) ( struct io_buffer *iobuf, struct net_device *netdev, + uint16_t *net_proto, const void **ll_source ); /** * Transcribe link-layer address * diff --git a/gpxe/src/include/gpxe/pci.h b/gpxe/src/include/gpxe/pci.h index fdcecb6d..90721574 100644 --- a/gpxe/src/include/gpxe/pci.h +++ b/gpxe/src/include/gpxe/pci.h @@ -156,6 +156,7 @@ #define PCI_CAP_ID_SLOTID 0x04 /* Slot Identification */ #define PCI_CAP_ID_MSI 0x05 /* Message Signalled Interrupts */ #define PCI_CAP_ID_CHSWP 0x06 /* CompactPCI HotSwap */ +#define PCI_CAP_ID_EXP 0x10 /* PCI Express */ #define PCI_CAP_LIST_NEXT 1 /* Next capability in the list */ #define PCI_CAP_FLAGS 2 /* Capability defined flags (16 bits) */ #define PCI_CAP_SIZEOF 4 diff --git a/gpxe/src/include/gpxe/retry.h b/gpxe/src/include/gpxe/retry.h index 71982fca..ec57db9e 100644 --- a/gpxe/src/include/gpxe/retry.h +++ b/gpxe/src/include/gpxe/retry.h @@ -9,12 +9,28 @@ #include <gpxe/list.h> +/** Default timeout value */ +#define DEFAULT_MIN_TIMEOUT ( TICKS_PER_SEC / 4 ) + +/** Limit after which the timeout will be deemed permanent */ +#define DEFAULT_MAX_TIMEOUT ( 10 * TICKS_PER_SEC ) + /** A retry timer */ struct retry_timer { /** List of active timers */ struct list_head list; /** Timeout value (in ticks) */ unsigned long timeout; + /** Minimum timeout value (in ticks) + * + * A value of zero means "use default timeout." + */ + unsigned long min_timeout; + /** Maximum timeout value before failure (in ticks) + * + * A value of zero means "use default timeout." + */ + unsigned long max_timeout; /** Start time (in ticks) * * A start time of zero indicates a stopped timer. diff --git a/gpxe/src/include/gpxe/settings.h b/gpxe/src/include/gpxe/settings.h index ae5a259d..78f3e698 100644 --- a/gpxe/src/include/gpxe/settings.h +++ b/gpxe/src/include/gpxe/settings.h @@ -215,6 +215,7 @@ extern struct setting password_setting __setting; extern struct setting priority_setting __setting; extern struct setting bios_drive_setting __setting; extern struct setting uuid_setting __setting; +extern struct setting next_server_setting __setting; /** * Initialise a settings block diff --git a/gpxe/src/include/gpxe/vsprintf.h b/gpxe/src/include/gpxe/vsprintf.h index 9360f29b..aa8f8a33 100644 --- a/gpxe/src/include/gpxe/vsprintf.h +++ b/gpxe/src/include/gpxe/vsprintf.h @@ -66,6 +66,7 @@ extern size_t vcprintf ( struct printf_context *ctx, const char *fmt, va_list args ); extern int vssnprintf ( char *buf, ssize_t ssize, const char *fmt, va_list args ); -extern int ssnprintf ( char *buf, ssize_t ssize, const char *fmt, ... ); +extern int __attribute__ (( format ( printf, 3, 4 ) )) +ssnprintf ( char *buf, ssize_t ssize, const char *fmt, ... ); #endif /* _GPXE_VSPRINTF_H */ diff --git a/gpxe/src/include/usr/autoboot.h b/gpxe/src/include/usr/autoboot.h index b451a8c1..b64cbb8e 100644 --- a/gpxe/src/include/usr/autoboot.h +++ b/gpxe/src/include/usr/autoboot.h @@ -7,6 +7,8 @@ * */ +extern int shutdown_exit_flags; + extern void autoboot ( void ); extern int boot_root_path ( const char *root_path ); diff --git a/gpxe/src/interface/pxe/pxe_loader.c b/gpxe/src/interface/pxe/pxe_loader.c index 708d203a..f815bc25 100644 --- a/gpxe/src/interface/pxe/pxe_loader.c +++ b/gpxe/src/interface/pxe/pxe_loader.c @@ -42,11 +42,9 @@ PXENV_EXIT_t undi_loader ( struct s_UNDI_LOADER *undi_loader ) { /* Fill in UNDI loader structure */ undi_loader->PXEptr.segment = rm_cs; - undi_loader->PXEptr.offset = - ( ( unsigned ) & __from_text16 ( ppxe ) ); + undi_loader->PXEptr.offset = __from_text16 ( &ppxe ); undi_loader->PXENVptr.segment = rm_cs; - undi_loader->PXENVptr.offset = - ( ( unsigned ) & __from_text16 ( pxenv ) ); + undi_loader->PXENVptr.offset = __from_text16 ( &pxenv ); undi_loader->Status = PXENV_STATUS_SUCCESS; return PXENV_EXIT_SUCCESS; diff --git a/gpxe/src/interface/pxe/pxe_preboot.c b/gpxe/src/interface/pxe/pxe_preboot.c index 302953eb..8220d1f2 100644 --- a/gpxe/src/interface/pxe/pxe_preboot.c +++ b/gpxe/src/interface/pxe/pxe_preboot.c @@ -196,8 +196,7 @@ PXENV_EXIT_t pxenv_get_cached_info ( struct s_PXENV_GET_CACHED_INFO * fill it in. */ get_cached_info->Buffer.segment = rm_ds; - get_cached_info->Buffer.offset = - ( unsigned ) ( __from_data16 ( info ) ); + get_cached_info->Buffer.offset = __from_data16 ( info ); get_cached_info->BufferSize = sizeof ( *info ); DBG ( " returning %04x:%04x+%04x['%x']", get_cached_info->Buffer.segment, @@ -319,7 +318,7 @@ PXENV_EXIT_t pxenv_stop_undi ( struct s_PXENV_STOP_UNDI *stop_undi ) { pxe_set_netdev ( NULL ); /* Prepare for unload */ - shutdown(); + shutdown ( SHUTDOWN_BOOT ); stop_undi->Status = PXENV_STATUS_SUCCESS; return PXENV_EXIT_SUCCESS; diff --git a/gpxe/src/interface/pxe/pxe_udp.c b/gpxe/src/interface/pxe/pxe_udp.c index 40c2b2e5..033b1ad9 100644 --- a/gpxe/src/interface/pxe/pxe_udp.c +++ b/gpxe/src/interface/pxe/pxe_udp.c @@ -166,6 +166,7 @@ PXENV_EXIT_t pxenv_udp_open ( struct s_PXENV_UDP_OPEN *pxenv_udp_open ) { /* Record source IP address */ pxe_udp.local.sin_addr.s_addr = pxenv_udp_open->src_ip; + DBG ( " %s", inet_ntoa ( pxe_udp.local.sin_addr ) ); /* Open promiscuous UDP connection */ xfer_close ( &pxe_udp.xfer, 0 ); @@ -255,6 +256,7 @@ PXENV_EXIT_t pxenv_udp_write ( struct s_PXENV_UDP_WRITE *pxenv_udp_write ) { struct xfer_metadata meta = { .src = ( struct sockaddr * ) &pxe_udp.local, .dest = ( struct sockaddr * ) &dest, + .netdev = pxe_netdev, }; size_t len; struct io_buffer *iobuf; @@ -354,8 +356,10 @@ PXENV_EXIT_t pxenv_udp_write ( struct s_PXENV_UDP_WRITE *pxenv_udp_write ) { * */ PXENV_EXIT_t pxenv_udp_read ( struct s_PXENV_UDP_READ *pxenv_udp_read ) { - struct in_addr dest_ip = { .s_addr = pxenv_udp_read->dest_ip }; - uint16_t d_port = pxenv_udp_read->d_port; + struct in_addr dest_ip_wanted = { .s_addr = pxenv_udp_read->dest_ip }; + struct in_addr dest_ip; + uint16_t d_port_wanted = pxenv_udp_read->d_port; + uint16_t d_port; DBG ( "PXENV_UDP_READ" ); @@ -367,12 +371,21 @@ PXENV_EXIT_t pxenv_udp_read ( struct s_PXENV_UDP_READ *pxenv_udp_read ) { pxe_udp.pxenv_udp_read = NULL; goto no_packet; } + dest_ip.s_addr = pxenv_udp_read->dest_ip; + d_port = pxenv_udp_read->d_port; /* Filter on destination address and/or port */ - if ( dest_ip.s_addr && ( dest_ip.s_addr != pxenv_udp_read->dest_ip ) ) + if ( dest_ip_wanted.s_addr && + ( dest_ip_wanted.s_addr != dest_ip.s_addr ) ) { + DBG ( " wrong IP %s", inet_ntoa ( dest_ip ) ); + DBG ( " (wanted %s)", inet_ntoa ( dest_ip_wanted ) ); goto no_packet; - if ( d_port && ( d_port != pxenv_udp_read->d_port ) ) + } + if ( d_port_wanted && ( d_port_wanted != d_port ) ) { + DBG ( " wrong port %d ", htons ( d_port ) ); + DBG ( " (wanted %d)", htons ( d_port_wanted ) ); goto no_packet; + } DBG ( " %04x:%04x+%x %s:", pxenv_udp_read->buffer.segment, pxenv_udp_read->buffer.offset, pxenv_udp_read->buffer_size, diff --git a/gpxe/src/interface/pxe/pxe_undi.c b/gpxe/src/interface/pxe/pxe_undi.c index 76b55df9..5d06f2d8 100644 --- a/gpxe/src/interface/pxe/pxe_undi.c +++ b/gpxe/src/interface/pxe/pxe_undi.c @@ -221,7 +221,7 @@ PXENV_EXIT_t pxenv_undi_transmit ( struct s_PXENV_UNDI_TRANSMIT undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER; return PXENV_EXIT_FAILURE; } - DBG ( " %s", ( net_protocol ? net_protocol->name : "UNKNOWN" ) ); + DBG ( " %s", ( net_protocol ? net_protocol->name : "RAW" ) ); /* Calculate total packet length */ copy_from_real ( &tbd, undi_transmit->TBD.segment, @@ -251,11 +251,9 @@ PXENV_EXIT_t pxenv_undi_transmit ( struct s_PXENV_UNDI_TRANSMIT datablk->TDDataLen ); } - /* Transmit packet */ - if ( net_protocol == NULL ) { - /* Link-layer header already present */ - rc = netdev_tx ( pxe_netdev, iobuf ); - } else { + /* Add link-layer header, if required to do so */ + if ( net_protocol != NULL ) { + /* Calculate destination address */ if ( undi_transmit->XmitFlag == XMT_DESTADDR ) { copy_from_real ( destaddr, @@ -264,16 +262,31 @@ PXENV_EXIT_t pxenv_undi_transmit ( struct s_PXENV_UNDI_TRANSMIT pxe_netdev->ll_protocol->ll_addr_len ); ll_dest = destaddr; } else { + DBG ( " BCAST" ); ll_dest = pxe_netdev->ll_protocol->ll_broadcast; } - rc = net_tx ( iobuf, pxe_netdev, net_protocol, ll_dest ); + + /* Add link-layer header */ + if ( ( rc = pxe_netdev->ll_protocol->push ( iobuf, pxe_netdev, + net_protocol, + ll_dest )) != 0 ){ + free_iob ( iobuf ); + undi_transmit->Status = PXENV_STATUS ( rc ); + return PXENV_EXIT_FAILURE; + } + } + + /* Transmit packet */ + if ( ( rc = netdev_tx ( pxe_netdev, iobuf ) ) != 0 ) { + undi_transmit->Status = PXENV_STATUS ( rc ); + return PXENV_EXIT_FAILURE; } /* Flag transmission as in-progress */ undi_tx_count++; - undi_transmit->Status = PXENV_STATUS ( rc ); - return ( ( rc == 0 ) ? PXENV_EXIT_SUCCESS : PXENV_EXIT_FAILURE ); + undi_transmit->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; } /* PXENV_UNDI_SET_MCAST_ADDRESS @@ -531,6 +544,13 @@ PXENV_EXIT_t pxenv_undi_get_state ( struct s_PXENV_UNDI_GET_STATE PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) { struct io_buffer *iobuf; size_t len; + struct ll_protocol *ll_protocol; + const void *ll_source; + uint16_t net_proto; + size_t ll_hlen; + struct net_protocol *net_protocol; + unsigned int prottype; + int rc; DBG ( "PXENV_UNDI_ISR" ); @@ -559,13 +579,25 @@ PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) { undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS; break; case PXENV_UNDI_ISR_IN_PROCESS : + DBG ( " PROCESS" ); + /* Fall through */ case PXENV_UNDI_ISR_IN_GET_NEXT : - DBG ( " PROCESS/GET_NEXT" ); + DBG ( " GET_NEXT" ); + + /* Some dumb NBPs (e.g. emBoot's winBoot/i) never call + * PXENV_UNDI_ISR with FuncFlag=PXENV_UNDI_ISR_START; + * they just sit in a tight polling loop merrily + * violating the PXE spec with repeated calls to + * PXENV_UNDI_ISR_IN_PROCESS. Force extra polls to + * cope with these out-of-spec clients. + */ + netdev_poll ( pxe_netdev ); /* If we have not yet marked a TX as complete, and the * netdev TX queue is empty, report the TX completion. */ if ( undi_tx_count && list_empty ( &pxe_netdev->tx_queue ) ) { + DBG ( " TXC" ); undi_tx_count--; undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_TRANSMIT; break; @@ -574,6 +606,7 @@ PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) { /* Remove first packet from netdev RX queue */ iobuf = netdev_rx_dequeue ( pxe_netdev ); if ( ! iobuf ) { + DBG ( " DONE" ); /* No more packets remaining */ undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE; /* Re-enable interrupts */ @@ -583,24 +616,53 @@ PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) { /* Copy packet to base memory buffer */ len = iob_len ( iobuf ); - DBG ( " RECEIVE %zd", len ); + DBG ( " RX %zd", len ); if ( len > sizeof ( basemem_packet ) ) { /* Should never happen */ len = sizeof ( basemem_packet ); } memcpy ( basemem_packet, iobuf->data, len ); + /* Strip link-layer header */ + ll_protocol = pxe_netdev->ll_protocol; + if ( ( rc = ll_protocol->pull ( iobuf, pxe_netdev, + &net_proto, + &ll_source ) ) != 0 ) { + /* Assume unknown net_proto and no ll_source */ + net_proto = 0; + ll_source = NULL; + } + ll_hlen = ( len - iob_len ( iobuf ) ); + + /* Determine network-layer protocol */ + switch ( net_proto ) { + case htons ( ETH_P_IP ): + net_protocol = &ipv4_protocol; + prottype = P_IP; + break; + case htons ( ETH_P_ARP ): + net_protocol = &arp_protocol; + prottype = P_ARP; + break; + case htons ( ETH_P_RARP ): + net_protocol = &rarp_protocol; + prottype = P_RARP; + break; + default: + net_protocol = NULL; + prottype = P_UNKNOWN; + break; + } + DBG ( " %s", ( net_protocol ? net_protocol->name : "RAW" ) ); + /* Fill in UNDI_ISR structure */ undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_RECEIVE; undi_isr->BufferLength = len; undi_isr->FrameLength = len; - undi_isr->FrameHeaderLength = - pxe_netdev->ll_protocol->ll_header_len; + undi_isr->FrameHeaderLength = ll_hlen; undi_isr->Frame.segment = rm_ds; - undi_isr->Frame.offset = - ( ( unsigned ) & __from_data16 ( basemem_packet ) ); - /* Probably ought to fill in packet type */ - undi_isr->ProtType = P_UNKNOWN; + undi_isr->Frame.offset = __from_data16 ( basemem_packet ); + undi_isr->ProtType = prottype; undi_isr->PktType = XMT_DESTADDR; /* Free packet */ diff --git a/gpxe/src/net/dhcppkt.c b/gpxe/src/net/dhcppkt.c index 2537e254..c8bf215b 100644 --- a/gpxe/src/net/dhcppkt.c +++ b/gpxe/src/net/dhcppkt.c @@ -138,12 +138,15 @@ find_dhcp_packet_field ( unsigned int tag ) { 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 ) { if ( len > field->len ) return -ENOSPC; + field_data = dhcp_packet_field ( dhcppkt->dhcphdr, field ); + memset ( field_data, 0, field->len ); memcpy ( dhcp_packet_field ( dhcppkt->dhcphdr, field ), data, len ); return 0; diff --git a/gpxe/src/net/ethernet.c b/gpxe/src/net/ethernet.c index 55035de5..7b1c496f 100644 --- a/gpxe/src/net/ethernet.c +++ b/gpxe/src/net/ethernet.c @@ -38,17 +38,16 @@ static uint8_t eth_broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; /** - * Transmit Ethernet packet + * Add Ethernet link-layer header * * @v iobuf I/O buffer * @v netdev Network device * @v net_protocol Network-layer protocol * @v ll_dest Link-layer destination address - * - * Prepends the Ethernet link-layer header and transmits the packet. */ -static int eth_tx ( struct io_buffer *iobuf, struct net_device *netdev, - struct net_protocol *net_protocol, const void *ll_dest ) { +static int eth_push ( struct io_buffer *iobuf, struct net_device *netdev, + struct net_protocol *net_protocol, + const void *ll_dest ) { struct ethhdr *ethhdr = iob_push ( iobuf, sizeof ( *ethhdr ) ); /* Build Ethernet header */ @@ -56,35 +55,38 @@ static int eth_tx ( struct io_buffer *iobuf, struct net_device *netdev, memcpy ( ethhdr->h_source, netdev->ll_addr, ETH_ALEN ); ethhdr->h_protocol = net_protocol->net_proto; - /* Hand off to network device */ - return netdev_tx ( netdev, iobuf ); + return 0; } /** - * Process received Ethernet packet - * - * @v iobuf I/O buffer - * @v netdev Network device + * Remove Ethernet link-layer header * - * Strips off the Ethernet link-layer header and passes up to the - * network-layer protocol. + * @v iobuf I/O buffer + * @v netdev Network device + * @v net_proto Network-layer protocol, in network-byte order + * @v ll_source Source link-layer address + * @ret rc Return status code */ -static int eth_rx ( struct io_buffer *iobuf, struct net_device *netdev ) { +static int eth_pull ( struct io_buffer *iobuf, + struct net_device *netdev __unused, + uint16_t *net_proto, const void **ll_source ) { struct ethhdr *ethhdr = iobuf->data; /* Sanity check */ if ( iob_len ( iobuf ) < sizeof ( *ethhdr ) ) { DBG ( "Ethernet packet too short (%zd bytes)\n", iob_len ( iobuf ) ); - free_iob ( iobuf ); return -EINVAL; } /* Strip off Ethernet header */ iob_pull ( iobuf, sizeof ( *ethhdr ) ); - /* Hand off to network-layer protocol */ - return net_rx ( iobuf, netdev, ethhdr->h_protocol, ethhdr->h_source ); + /* Fill in required fields */ + *net_proto = ethhdr->h_protocol; + *ll_source = ethhdr->h_source; + + return 0; } /** @@ -110,7 +112,7 @@ struct ll_protocol ethernet_protocol __ll_protocol = { .ll_addr_len = ETH_ALEN, .ll_header_len = ETH_HLEN, .ll_broadcast = eth_broadcast, - .tx = eth_tx, - .rx = eth_rx, + .push = eth_push, + .pull = eth_pull, .ntoa = eth_ntoa, }; diff --git a/gpxe/src/net/fakedhcp.c b/gpxe/src/net/fakedhcp.c index a10e442b..60264756 100644 --- a/gpxe/src/net/fakedhcp.c +++ b/gpxe/src/net/fakedhcp.c @@ -181,11 +181,9 @@ int create_fakeproxydhcpack ( struct net_device *netdev, /* Identify ProxyDHCP settings */ settings = find_settings ( PROXYDHCP_SETTINGS_NAME ); - /* No ProxyDHCP settings => return empty block */ - if ( ! settings ) { - memset ( data, 0, max_len ); - return 0; - } + /* No ProxyDHCP settings => use normal DHCPACK */ + if ( ! settings ) + return create_fakedhcpack ( netdev, data, max_len ); /* Create base DHCPACK packet */ if ( ( rc = dhcp_create_packet ( &dhcppkt, netdev, DHCPACK, NULL, diff --git a/gpxe/src/net/netdevice.c b/gpxe/src/net/netdevice.c index 6875b3ba..3721b334 100644 --- a/gpxe/src/net/netdevice.c +++ b/gpxe/src/net/netdevice.c @@ -439,6 +439,7 @@ struct net_device * find_netdev_by_location ( unsigned int bus_type, */ int net_tx ( struct io_buffer *iobuf, struct net_device *netdev, struct net_protocol *net_protocol, const void *ll_dest ) { + int rc; /* Force a poll on the netdevice to (potentially) clear any * backed-up TX completions. This is needed on some network @@ -447,7 +448,15 @@ int net_tx ( struct io_buffer *iobuf, struct net_device *netdev, */ netdev_poll ( netdev ); - return netdev->ll_protocol->tx ( iobuf, netdev, net_protocol, ll_dest ); + /* Add link-layer header */ + if ( ( rc = netdev->ll_protocol->push ( iobuf, netdev, net_protocol, + ll_dest ) ) != 0 ) { + free_iob ( iobuf ); + return rc; + } + + /* Transmit packet */ + return netdev_tx ( netdev, iobuf ); } /** @@ -485,6 +494,10 @@ int net_rx ( struct io_buffer *iobuf, struct net_device *netdev, static void net_step ( struct process *process __unused ) { struct net_device *netdev; struct io_buffer *iobuf; + struct ll_protocol *ll_protocol; + uint16_t net_proto; + const void *ll_source; + int rc; /* Poll and process each network device */ list_for_each_entry ( netdev, &net_devices, list ) { @@ -499,10 +512,21 @@ static void net_step ( struct process *process __unused ) { * NIC faster than they arrive. */ if ( ( iobuf = netdev_rx_dequeue ( netdev ) ) ) { + DBGC ( netdev, "NETDEV %p processing %p (%p+%zx)\n", netdev, iobuf, iobuf->data, iob_len ( iobuf ) ); - netdev->ll_protocol->rx ( iobuf, netdev ); + + /* Remove link-layer header */ + ll_protocol = netdev->ll_protocol; + if ( ( rc = ll_protocol->pull ( iobuf, netdev, + &net_proto, + &ll_source ) ) != 0 ) { + free_iob ( iobuf ); + continue; + } + + net_rx ( iobuf, netdev, net_proto, ll_source ); } } } diff --git a/gpxe/src/net/retry.c b/gpxe/src/net/retry.c index 3c934018..2a645c97 100644 --- a/gpxe/src/net/retry.c +++ b/gpxe/src/net/retry.c @@ -36,20 +36,11 @@ * */ -/** Default timeout value */ -#define MIN_TIMEOUT ( TICKS_PER_SEC / 4 ) - -/** Limit after which the timeout will be deemed permanent */ -#define MAX_TIMEOUT ( 10 * TICKS_PER_SEC ) - /* The theoretical minimum that the algorithm in stop_timer() can * adjust the timeout back down to is seven ticks, so set the minimum * timeout to at least that value for the sake of consistency. */ -#if MIN_TIMEOUT < 7 -#undef MIN_TIMEOUT #define MIN_TIMEOUT 7 -#endif /** List of running timers */ static LIST_HEAD ( timers ); @@ -67,8 +58,17 @@ void start_timer ( struct retry_timer *timer ) { if ( ! timer_running ( timer ) ) list_add ( &timer->list, &timers ); timer->start = currticks(); - if ( timer->timeout < MIN_TIMEOUT ) - timer->timeout = MIN_TIMEOUT; + + /* 0 means "use default timeout" */ + if ( timer->min_timeout == 0 ) + timer->min_timeout = DEFAULT_MIN_TIMEOUT; + /* We must never be less than MIN_TIMEOUT under any circumstances */ + if ( timer->min_timeout < MIN_TIMEOUT ) + timer->min_timeout = MIN_TIMEOUT; + /* Honor user-specified minimum timeout */ + if ( timer->timeout < timer->min_timeout ) + timer->timeout = timer->min_timeout; + DBG2 ( "Timer %p started at time %ld (expires at %ld)\n", timer, timer->start, ( timer->start + timer->timeout ) ); } @@ -150,8 +150,10 @@ static void timer_expired ( struct retry_timer *timer ) { /* Back off the timeout value */ timer->timeout <<= 1; - if ( ( fail = ( timer->timeout > MAX_TIMEOUT ) ) ) - timer->timeout = MAX_TIMEOUT; + if ( timer->max_timeout == 0 ) /* 0 means "use default timeout" */ + timer->max_timeout = DEFAULT_MAX_TIMEOUT; + if ( ( fail = ( timer->timeout > timer->max_timeout ) ) ) + timer->timeout = timer->max_timeout; DBG ( "Timer %p timeout backed off to %ld\n", timer, timer->timeout ); diff --git a/gpxe/src/net/tcp/ftp.c b/gpxe/src/net/tcp/ftp.c index ffb2fbff..3b88f7b6 100644 --- a/gpxe/src/net/tcp/ftp.c +++ b/gpxe/src/net/tcp/ftp.c @@ -35,6 +35,7 @@ enum ftp_state { FTP_TYPE, FTP_PASV, FTP_RETR, + FTP_WAIT, FTP_QUIT, FTP_DONE, }; @@ -116,14 +117,15 @@ static void ftp_done ( struct ftp_request *ftp, int rc ) { * snprintf() call. */ static const char * ftp_strings[] = { - [FTP_CONNECT] = "", + [FTP_CONNECT] = NULL, [FTP_USER] = "USER anonymous\r\n", [FTP_PASS] = "PASS etherboot@etherboot.org\r\n", [FTP_TYPE] = "TYPE I\r\n", [FTP_PASV] = "PASV\r\n", - [FTP_RETR] = "RETR %s\r\n", + [FTP_RETR] = "RETR %s\r\n", + [FTP_WAIT] = NULL, [FTP_QUIT] = "QUIT\r\n", - [FTP_DONE] = "", + [FTP_DONE] = NULL, }; /** @@ -170,6 +172,27 @@ static void ftp_parse_value ( char **text, uint8_t *value, size_t len ) { } /** + * Move to next state and send the appropriate FTP control string + * + * @v ftp FTP request + * + */ +static void ftp_next_state ( struct ftp_request *ftp ) { + + /* Move to next state */ + if ( ftp->state < FTP_DONE ) + ftp->state++; + + /* Send control string if needed */ + if ( ftp_strings[ftp->state] != NULL ) { + DBGC ( ftp, "FTP %p sending ", ftp ); + DBGC ( ftp, ftp_strings[ftp->state], ftp->uri->path ); + xfer_printf ( &ftp->control, ftp_strings[ftp->state], + ftp->uri->path ); + } +} + +/** * Handle an FTP control channel response * * @v ftp FTP request @@ -198,6 +221,7 @@ static void ftp_reply ( struct ftp_request *ftp ) { ( ( status_major == '3' ) && ( ftp->state == FTP_USER ) ) ) ){ /* Flag protocol error and close connections */ ftp_done ( ftp, -EPROTO ); + return; } /* Open passive connection when we get "PASV" response */ @@ -223,17 +247,9 @@ static void ftp_reply ( struct ftp_request *ftp ) { } } - /* Move to next state */ - if ( ftp->state < FTP_DONE ) - ftp->state++; - - /* Send control string */ - if ( ftp->state < FTP_DONE ) { - DBGC ( ftp, "FTP %p sending ", ftp ); - DBGC ( ftp, ftp_strings[ftp->state], ftp->uri->path ); - xfer_printf ( &ftp->control, ftp_strings[ftp->state], - ftp->uri->path ); - } + /* Move to next state and send control string */ + ftp_next_state ( ftp ); + } /** @@ -331,8 +347,11 @@ static void ftp_data_closed ( struct xfer_interface *data, int rc ) { ftp, strerror ( rc ) ); /* If there was an error, close control channel and record status */ - if ( rc ) + if ( rc ) { ftp_done ( ftp, rc ); + } else { + ftp_next_state ( ftp ); + } } /** diff --git a/gpxe/src/net/tcp/iscsi.c b/gpxe/src/net/tcp/iscsi.c index a12fca85..a67415b6 100644 --- a/gpxe/src/net/tcp/iscsi.c +++ b/gpxe/src/net/tcp/iscsi.c @@ -49,11 +49,17 @@ static char *iscsi_explicit_initiator_iqn; /** Default iSCSI initiator name (constructed from hostname) */ static char *iscsi_default_initiator_iqn; -/** iSCSI username */ -static char *iscsi_username; +/** iSCSI initiator username */ +static char *iscsi_initiator_username; -/** iSCSI password */ -static char *iscsi_password; +/** iSCSI initiator password */ +static char *iscsi_initiator_password; + +/** iSCSI target username */ +static char *iscsi_target_username; + +/** iSCSI target password */ +static char *iscsi_target_password; static void iscsi_start_tx ( struct iscsi_session *iscsi ); static void iscsi_start_login ( struct iscsi_session *iscsi ); @@ -81,8 +87,10 @@ static void iscsi_free ( struct refcnt *refcnt ) { free ( iscsi->target_address ); free ( iscsi->target_iqn ); - free ( iscsi->username ); - free ( iscsi->password ); + free ( iscsi->initiator_username ); + free ( iscsi->initiator_password ); + free ( iscsi->target_username ); + free ( iscsi->target_password ); chap_finish ( &iscsi->chap ); iscsi_rx_buffered_data_done ( iscsi ); free ( iscsi ); @@ -144,6 +152,9 @@ static void iscsi_close_connection ( struct iscsi_session *iscsi, int rc ) { /* Clear connection status */ iscsi->status = 0; + /* Deauthenticate target */ + iscsi->target_auth_ok = 0; + /* Reset TX and RX state machines */ iscsi->tx_state = ISCSI_TX_IDLE; iscsi->rx_state = ISCSI_RX_BHS; @@ -213,11 +224,12 @@ static void iscsi_start_command ( struct iscsi_session *iscsi ) { command->cmdsn = htonl ( iscsi->cmdsn ); command->expstatsn = htonl ( iscsi->statsn + 1 ); memcpy ( &command->cdb, &iscsi->command->cdb, sizeof ( command->cdb )); - DBGC ( iscsi, "iSCSI %p start " SCSI_CDB_FORMAT " %s %#zx\n", - iscsi, SCSI_CDB_DATA ( command->cdb ), - ( iscsi->command->data_in ? "in" : "out" ), - ( iscsi->command->data_in ? - iscsi->command->data_in_len : iscsi->command->data_out_len )); + DBGC2 ( iscsi, "iSCSI %p start " SCSI_CDB_FORMAT " %s %#zx\n", + iscsi, SCSI_CDB_DATA ( command->cdb ), + ( iscsi->command->data_in ? "in" : "out" ), + ( iscsi->command->data_in ? + iscsi->command->data_in_len : + iscsi->command->data_out_len ) ); } /** @@ -450,17 +462,25 @@ static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi, void *data, size_t len ) { unsigned int used = 0; unsigned int i; + const char *auth_method; if ( iscsi->status & ISCSI_STATUS_STRINGS_SECURITY ) { + /* Default to allowing no authentication */ + auth_method = "None"; + /* If we have a credential to supply, permit CHAP */ + if ( iscsi->initiator_username ) + auth_method = "CHAP,None"; + /* If we have a credential to check, force CHAP */ + if ( iscsi->target_username ) + auth_method = "CHAP"; used += ssnprintf ( data + used, len - used, "InitiatorName=%s%c" "TargetName=%s%c" "SessionType=Normal%c" - "AuthMethod=%sNone%c", + "AuthMethod=%s%c", iscsi_initiator_iqn(), 0, iscsi->target_iqn, 0, 0, - ( ( iscsi->username && iscsi->password ) ? - "CHAP," : "" ), 0 ); + auth_method, 0 ); } if ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_ALGORITHM ) { @@ -468,9 +488,10 @@ static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi, } if ( ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_RESPONSE ) ) { + assert ( iscsi->initiator_username != NULL ); used += ssnprintf ( data + used, len - used, "CHAP_N=%s%cCHAP_R=0x", - iscsi->username, 0 ); + iscsi->initiator_username, 0 ); for ( i = 0 ; i < iscsi->chap.response_len ; i++ ) { used += ssnprintf ( data + used, len - used, "%02x", iscsi->chap.response[i] ); @@ -478,6 +499,17 @@ static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi, used += ssnprintf ( data + used, len - used, "%c", 0 ); } + if ( ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_CHALLENGE ) ) { + used += ssnprintf ( data + used, len - used, + "CHAP_I=%d%cCHAP_C=0x", + iscsi->chap_challenge[0], 0 ); + for ( i = 1 ; i < sizeof ( iscsi->chap_challenge ) ; i++ ) { + used += ssnprintf ( data + used, len - used, "%02x", + iscsi->chap_challenge[i] ); + } + used += ssnprintf ( data + used, len - used, "%c", 0 ); + } + if ( iscsi->status & ISCSI_STATUS_STRINGS_OPERATIONAL ) { used += ssnprintf ( data + used, len - used, "HeaderDigest=None%c" @@ -602,12 +634,17 @@ static int iscsi_handle_targetaddress_value ( struct iscsi_session *iscsi, static int iscsi_handle_authmethod_value ( struct iscsi_session *iscsi, const char *value ) { + /* Mark target as authenticated if no authentication required */ + if ( ! iscsi->target_username ) + iscsi->target_auth_ok = 1; + /* If server requests CHAP, send the CHAP_A string */ if ( strcmp ( value, "CHAP" ) == 0 ) { DBGC ( iscsi, "iSCSI %p initiating CHAP authentication\n", iscsi ); iscsi->status |= ISCSI_STATUS_STRINGS_CHAP_ALGORITHM; } + return 0; } @@ -620,7 +657,6 @@ static int iscsi_handle_authmethod_value ( struct iscsi_session *iscsi, */ static int iscsi_handle_chap_a_value ( struct iscsi_session *iscsi, const char *value ) { - int rc; /* We only ever offer "5" (i.e. MD5) as an algorithm, so if * the server responds with anything else it is a protocol @@ -632,13 +668,6 @@ static int iscsi_handle_chap_a_value ( struct iscsi_session *iscsi, return -EPROTO; } - /* Prepare for CHAP with MD5 */ - if ( ( rc = chap_init ( &iscsi->chap, &md5_algorithm ) ) != 0 ) { - DBGC ( iscsi, "iSCSI %p could not initialise CHAP: %s\n", - iscsi, strerror ( rc ) ); - return rc; - } - return 0; } @@ -653,6 +682,7 @@ static int iscsi_handle_chap_i_value ( struct iscsi_session *iscsi, const char *value ) { unsigned int identifier; char *endp; + int rc; /* The CHAP identifier is an integer value */ identifier = strtoul ( value, &endp, 0 ); @@ -662,13 +692,21 @@ static int iscsi_handle_chap_i_value ( struct iscsi_session *iscsi, return -EPROTO; } + /* Prepare for CHAP with MD5 */ + chap_finish ( &iscsi->chap ); + if ( ( rc = chap_init ( &iscsi->chap, &md5_algorithm ) ) != 0 ) { + DBGC ( iscsi, "iSCSI %p could not initialise CHAP: %s\n", + iscsi, strerror ( rc ) ); + return rc; + } + /* Identifier and secret are the first two components of the * challenge. */ chap_set_identifier ( &iscsi->chap, identifier ); - if ( iscsi->password ) { - chap_update ( &iscsi->chap, iscsi->password, - strlen ( iscsi->password ) ); + if ( iscsi->initiator_password ) { + chap_update ( &iscsi->chap, iscsi->initiator_password, + strlen ( iscsi->initiator_password ) ); } return 0; @@ -686,11 +724,13 @@ static int iscsi_handle_chap_c_value ( struct iscsi_session *iscsi, char buf[3]; char *endp; uint8_t byte; + unsigned int i; /* Check and strip leading "0x" */ if ( ( value[0] != '0' ) || ( value[1] != 'x' ) ) { DBGC ( iscsi, "iSCSI %p saw invalid CHAP challenge \"%s\"\n", iscsi, value ); + return -EPROTO; } value += 2; @@ -712,6 +752,114 @@ static int iscsi_handle_chap_c_value ( struct iscsi_session *iscsi, chap_respond ( &iscsi->chap ); iscsi->status |= ISCSI_STATUS_STRINGS_CHAP_RESPONSE; + /* Send CHAP challenge, if applicable */ + if ( iscsi->target_username ) { + iscsi->status |= ISCSI_STATUS_STRINGS_CHAP_CHALLENGE; + /* Generate CHAP challenge data */ + for ( i = 0 ; i < sizeof ( iscsi->chap_challenge ) ; i++ ) { + iscsi->chap_challenge[i] = random(); + } + } + + return 0; +} + +/** + * Handle iSCSI CHAP_N text value + * + * @v iscsi iSCSI session + * @v value CHAP_N value + * @ret rc Return status code + */ +static int iscsi_handle_chap_n_value ( struct iscsi_session *iscsi, + const char *value ) { + + /* The target username isn't actually involved at any point in + * the authentication process; it merely serves to identify + * which password the target is using to generate the CHAP + * response. We unnecessarily verify that the username is as + * expected, in order to provide mildly helpful diagnostics if + * the target is supplying the wrong username/password + * combination. + */ + if ( iscsi->target_username && + ( strcmp ( iscsi->target_username, value ) != 0 ) ) { + DBGC ( iscsi, "iSCSI %p target username \"%s\" incorrect " + "(wanted \"%s\")\n", + iscsi, value, iscsi->target_username ); + return -EACCES; + } + + return 0; +} + +/** + * Handle iSCSI CHAP_R text value + * + * @v iscsi iSCSI session + * @v value CHAP_R value + * @ret rc Return status code + */ +static int iscsi_handle_chap_r_value ( struct iscsi_session *iscsi, + const char *value ) { + char buf[3]; + char *endp; + uint8_t byte; + unsigned int i; + int rc; + + /* Generate CHAP response for verification */ + chap_finish ( &iscsi->chap ); + if ( ( rc = chap_init ( &iscsi->chap, &md5_algorithm ) ) != 0 ) { + DBGC ( iscsi, "iSCSI %p could not initialise CHAP: %s\n", + iscsi, strerror ( rc ) ); + return rc; + } + chap_set_identifier ( &iscsi->chap, iscsi->chap_challenge[0] ); + if ( iscsi->target_password ) { + chap_update ( &iscsi->chap, iscsi->target_password, + strlen ( iscsi->target_password ) ); + } + chap_update ( &iscsi->chap, &iscsi->chap_challenge[1], + ( sizeof ( iscsi->chap_challenge ) - 1 ) ); + chap_respond ( &iscsi->chap ); + + /* Check and strip leading "0x" */ + if ( ( value[0] != '0' ) || ( value[1] != 'x' ) ) { + DBGC ( iscsi, "iSCSI %p saw invalid CHAP response \"%s\"\n", + iscsi, value ); + return -EPROTO; + } + value += 2; + + /* Check CHAP response length */ + if ( strlen ( value ) != ( 2 * iscsi->chap.response_len ) ) { + DBGC ( iscsi, "iSCSI %p invalid CHAP response length\n", + iscsi ); + return -EPROTO; + } + + /* Process response an octet at a time */ + for ( i = 0 ; ( value[0] && value[1] ) ; value += 2, i++ ) { + memcpy ( buf, value, 2 ); + buf[2] = 0; + byte = strtoul ( buf, &endp, 16 ); + if ( *endp != '\0' ) { + DBGC ( iscsi, "iSCSI %p saw invalid CHAP response " + "byte \"%s\"\n", iscsi, buf ); + return -EPROTO; + } + if ( byte != iscsi->chap.response[i] ) { + DBGC ( iscsi, "iSCSI %p saw incorrect CHAP " + "response\n", iscsi ); + return -EACCES; + } + } + assert ( i == iscsi->chap.response_len ); + + /* Mark session as authenticated */ + iscsi->target_auth_ok = 1; + return 0; } @@ -739,6 +887,8 @@ static struct iscsi_string_type iscsi_string_types[] = { { "CHAP_A=", iscsi_handle_chap_a_value }, { "CHAP_I=", iscsi_handle_chap_i_value }, { "CHAP_C=", iscsi_handle_chap_c_value }, + { "CHAP_N=", iscsi_handle_chap_n_value }, + { "CHAP_R=", iscsi_handle_chap_r_value }, { NULL, NULL } }; @@ -939,6 +1089,13 @@ static int iscsi_rx_login_response ( struct iscsi_session *iscsi, return 0; } + /* Check that target authentication was successful (if required) */ + if ( ! iscsi->target_auth_ok ) { + DBGC ( iscsi, "iSCSI %p nefarious target tried to bypass " + "authentication\n", iscsi ); + return -EPROTO; + } + /* Reset retry count */ iscsi->retry_count = 0; @@ -1148,9 +1305,9 @@ static int iscsi_rx_bhs ( struct iscsi_session *iscsi, const void *data, size_t len, size_t remaining __unused ) { memcpy ( &iscsi->rx_bhs.bytes[iscsi->rx_offset], data, len ); if ( ( iscsi->rx_offset + len ) >= sizeof ( iscsi->rx_bhs ) ) { - DBGC ( iscsi, "iSCSI %p received PDU opcode %#x len %#lx\n", - iscsi, iscsi->rx_bhs.common.opcode, - ISCSI_DATA_LEN ( iscsi->rx_bhs.common.lengths ) ); + DBGC2 ( iscsi, "iSCSI %p received PDU opcode %#x len %#lx\n", + iscsi, iscsi->rx_bhs.common.opcode, + ISCSI_DATA_LEN ( iscsi->rx_bhs.common.lengths ) ); } return 0; } @@ -1546,26 +1703,61 @@ static int iscsi_parse_root_path ( struct iscsi_session *iscsi, * Set iSCSI authentication details * * @v iscsi iSCSI session - * @v username Username, if any - * @v password Password, if any + * @v initiator_username Initiator username, if any + * @v initiator_password Initiator password, if any + * @v target_username Target username, if any + * @v target_password Target password, if any * @ret rc Return status code */ static int iscsi_set_auth ( struct iscsi_session *iscsi, - const char *username, const char *password ) { - - if ( username ) { - iscsi->username = strdup ( username ); - if ( ! iscsi->username ) + const char *initiator_username, + const char *initiator_password, + const char *target_username, + const char *target_password ) { + + /* Check for initiator or target credentials */ + if ( initiator_username || initiator_password || + target_username || target_password ) { + + /* We must have at least an initiator username+password */ + if ( ! ( initiator_username && initiator_password ) ) + goto invalid_auth; + + /* Store initiator credentials */ + iscsi->initiator_username = strdup ( initiator_username ); + if ( ! iscsi->initiator_username ) return -ENOMEM; - } - - if ( password ) { - iscsi->password = strdup ( password ); - if ( ! iscsi->password ) + iscsi->initiator_password = strdup ( initiator_password ); + if ( ! iscsi->initiator_password ) return -ENOMEM; + + /* Check for target credentials */ + if ( target_username || target_password ) { + + /* We must have target username+password */ + if ( ! ( target_username && target_password ) ) + goto invalid_auth; + + /* Store target credentials */ + iscsi->target_username = strdup ( target_username ); + if ( ! iscsi->target_username ) + return -ENOMEM; + iscsi->target_password = strdup ( target_password ); + if ( ! iscsi->target_password ) + return -ENOMEM; + } } return 0; + + invalid_auth: + DBGC ( iscsi, "iSCSI %p invalid credentials: initiator " + "%sname,%spw, target %sname,%spw\n", iscsi, + ( initiator_username ? "" : "no " ), + ( initiator_password ? "" : "no " ), + ( target_username ? "" : "no " ), + ( target_password ? "" : "no " ) ); + return -EINVAL; } /** @@ -1591,8 +1783,11 @@ int iscsi_attach ( struct scsi_device *scsi, const char *root_path ) { if ( ( rc = iscsi_parse_root_path ( iscsi, root_path ) ) != 0 ) goto err; /* Set fields not specified by root path */ - if ( ( rc = iscsi_set_auth ( iscsi, iscsi_username, - iscsi_password ) ) != 0 ) + if ( ( rc = iscsi_set_auth ( iscsi, + iscsi_initiator_username, + iscsi_initiator_password, + iscsi_target_username, + iscsi_target_password ) ) != 0 ) goto err; /* Sanity checks */ @@ -1635,6 +1830,22 @@ struct setting initiator_iqn_setting __setting = { .type = &setting_type_string, }; +/** iSCSI reverse username setting */ +struct setting reverse_username_setting __setting = { + .name = "reverse-username", + .description = "Reverse user name", + .tag = DHCP_EB_REVERSE_USERNAME, + .type = &setting_type_string, +}; + +/** iSCSI reverse password setting */ +struct setting reverse_password_setting __setting = { + .name = "reverse-password", + .description = "Reverse password", + .tag = DHCP_EB_REVERSE_PASSWORD, + .type = &setting_type_string, +}; + /** An iSCSI string setting */ struct iscsi_string_setting { /** Setting */ @@ -1654,12 +1865,22 @@ static struct iscsi_string_setting iscsi_string_settings[] = { }, { .setting = &username_setting, - .string = &iscsi_username, + .string = &iscsi_initiator_username, .prefix = "", }, { .setting = &password_setting, - .string = &iscsi_password, + .string = &iscsi_initiator_password, + .prefix = "", + }, + { + .setting = &reverse_username_setting, + .string = &iscsi_target_username, + .prefix = "", + }, + { + .setting = &reverse_password_setting, + .string = &iscsi_target_password, .prefix = "", }, { diff --git a/gpxe/src/net/udp.c b/gpxe/src/net/udp.c index 8df76a44..407ea14d 100644 --- a/gpxe/src/net/udp.c +++ b/gpxe/src/net/udp.c @@ -55,11 +55,12 @@ struct tcpip_protocol udp_protocol; */ static int udp_bind ( struct udp_connection *udp ) { struct udp_connection *existing; - static uint16_t try_port = 1024; + static uint16_t try_port = 1023; /* If no port specified, find the first available port */ if ( ! udp->local.st_port ) { - for ( ; try_port ; try_port++ ) { + while ( try_port ) { + try_port++; if ( try_port < 1024 ) continue; udp->local.st_port = htons ( try_port ); diff --git a/gpxe/src/net/udp/dhcp.c b/gpxe/src/net/udp/dhcp.c index 6ff8afe9..5fcd56ea 100644 --- a/gpxe/src/net/udp/dhcp.c +++ b/gpxe/src/net/udp/dhcp.c @@ -66,12 +66,12 @@ static const uint8_t dhcp_op[] = { /** Raw option data for options common to all DHCP requests */ static uint8_t dhcp_request_options_data[] = { DHCP_MAX_MESSAGE_SIZE, DHCP_WORD ( ETH_MAX_MTU ), + DHCP_CLIENT_ARCHITECTURE, DHCP_WORD ( 0 ), + DHCP_CLIENT_NDI, DHCP_OPTION ( 1 /* UNDI */ , 2, 1 /* v2.1 */ ), DHCP_VENDOR_CLASS_ID, DHCP_STRING ( 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':', 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '0', ':', 'U', 'N', 'D', 'I', ':', '0', '0', '2', '0', '0', '1' ), - DHCP_CLIENT_ARCHITECTURE, DHCP_WORD ( 0 ), - DHCP_CLIENT_NDI, DHCP_OPTION ( 1 /* UNDI */ , 2, 1 /* v2.1 */ ), DHCP_PARAMETER_REQUEST_LIST, DHCP_OPTION ( DHCP_SUBNET_MASK, DHCP_ROUTERS, DHCP_DNS_SERVERS, DHCP_LOG_SERVERS, DHCP_HOST_NAME, DHCP_DOMAIN_NAME, @@ -685,6 +685,7 @@ static void dhcp_store_dhcpoffer ( struct dhcp_session *dhcp, */ static void dhcp_rx_dhcpoffer ( struct dhcp_session *dhcp, struct dhcp_settings *dhcpoffer ) { + struct in_addr server_id; char vci[9]; /* "PXEClient" */ int len; uint8_t ignore_proxy = 0; @@ -692,7 +693,8 @@ static void dhcp_rx_dhcpoffer ( struct dhcp_session *dhcp, /* Check for presence of DHCP server ID */ if ( dhcppkt_fetch ( &dhcpoffer->dhcppkt, DHCP_SERVER_IDENTIFIER, - NULL, 0 ) != sizeof ( struct in_addr ) ) { + &server_id, sizeof ( server_id ) ) + != sizeof ( server_id ) ) { DBGC ( dhcp, "DHCP %p received DHCPOFFER %p missing server " "identifier\n", dhcp, dhcpoffer ); return; @@ -700,8 +702,9 @@ static void dhcp_rx_dhcpoffer ( struct dhcp_session *dhcp, /* If there is an IP address, it's a normal DHCPOFFER */ if ( dhcpoffer->dhcppkt.dhcphdr->yiaddr.s_addr != 0 ) { - DBGC ( dhcp, "DHCP %p received DHCPOFFER %p has IP address\n", - dhcp, dhcpoffer ); + DBGC ( dhcp, "DHCP %p received DHCPOFFER %p from %s has IP " + "address\n", + dhcp, dhcpoffer, inet_ntoa ( server_id ) ); dhcp_store_dhcpoffer ( dhcp, dhcpoffer, &dhcp->dhcpoffer ); } @@ -713,8 +716,9 @@ static void dhcp_rx_dhcpoffer ( struct dhcp_session *dhcp, vci, sizeof ( vci ) ); if ( ( len >= ( int ) sizeof ( vci ) ) && ( strncmp ( "PXEClient", vci, sizeof ( vci ) ) == 0 ) ) { - DBGC ( dhcp, "DHCP %p received DHCPOFFER %p is a " - "ProxyDHCPOFFER\n", dhcp, dhcpoffer ); + DBGC ( dhcp, "DHCP %p received DHCPOFFER %p from %s is a " + "ProxyDHCPOFFER\n", + dhcp, dhcpoffer, inet_ntoa ( server_id ) ); dhcp_store_dhcpoffer ( dhcp, dhcpoffer, &dhcp->proxydhcpoffer ); } @@ -802,8 +806,8 @@ static void dhcp_rx_dhcpack ( struct dhcp_session *dhcp, dhcppkt_fetch ( &dhcpack->dhcppkt, DHCP_SERVER_IDENTIFIER, &ack_server_id, sizeof ( ack_server_id ) ); if ( offer_server_id.s_addr != ack_server_id.s_addr ) { - DBGC ( dhcp, "DHCP %p ignoring DHCPACK with wrong server ID\n", - dhcp ); + DBGC ( dhcp, "DHCP %p ignoring DHCPACK with wrong server ID " + "%s\n", dhcp, inet_ntoa ( ack_server_id ) ); return; } @@ -814,6 +818,7 @@ static void dhcp_rx_dhcpack ( struct dhcp_session *dhcp, /* If we have a ProxyDHCPOFFER, transition to PROXYDHCPREQUEST */ if ( dhcp->proxydhcpoffer ) { + dhcp->timer.min_timeout = 0; dhcp_set_state ( dhcp, DHCP_STATE_PROXYREQUEST ); return; } @@ -830,8 +835,22 @@ static void dhcp_rx_dhcpack ( struct dhcp_session *dhcp, */ static void dhcp_rx_proxydhcpack ( struct dhcp_session *dhcp, struct dhcp_settings *proxydhcpack ) { + struct in_addr offer_server_id = { 0 }; + struct in_addr ack_server_id = { 0 }; int rc; + /* Verify server ID matches */ + assert ( dhcp->proxydhcpoffer != NULL ); + dhcppkt_fetch ( &dhcp->proxydhcpoffer->dhcppkt, DHCP_SERVER_IDENTIFIER, + &offer_server_id, sizeof ( offer_server_id ) ); + dhcppkt_fetch ( &proxydhcpack->dhcppkt, DHCP_SERVER_IDENTIFIER, + &ack_server_id, sizeof ( ack_server_id ) ); + if ( offer_server_id.s_addr != ack_server_id.s_addr ) { + DBGC ( dhcp, "DHCP %p ignoring ProxyDHCPACK with wrong server " + "ID %s\n", dhcp, inet_ntoa ( ack_server_id ) ); + return; + } + /* Rename settings */ proxydhcpack->settings.name = PROXYDHCP_SETTINGS_NAME; @@ -847,51 +866,76 @@ static void dhcp_rx_proxydhcpack ( struct dhcp_session *dhcp, * Receive new data * * @v xfer Data transfer interface - * @v data Received data - * @v len Length of received data + * @v iobuf I/O buffer + * @v meta Transfer metadata * @ret rc Return status code */ -static int dhcp_deliver_raw ( struct xfer_interface *xfer, - const void *data, size_t len ) { +static int dhcp_deliver_iob ( struct xfer_interface *xfer, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { struct dhcp_session *dhcp = container_of ( xfer, struct dhcp_session, xfer ); + struct sockaddr_tcpip *st_src; + unsigned int src_port; struct dhcp_settings *dhcpset; struct dhcphdr *dhcphdr; uint8_t msgtype = 0; + int rc = 0; + + /* Sanity checks */ + if ( ! meta ) { + DBGC ( dhcp, "DHCP %p received packet without metadata\n", + dhcp ); + rc = -EINVAL; + goto err_no_meta; + } + if ( ! meta->src ) { + DBGC ( dhcp, "DHCP %p received packet without source port\n", + dhcp ); + rc = -EINVAL; + goto err_no_src; + } + st_src = ( struct sockaddr_tcpip * ) meta->src; + src_port = st_src->st_port; /* Convert packet into a DHCP settings block */ - dhcpset = dhcpset_create ( data, len ); + dhcpset = dhcpset_create ( iobuf->data, iob_len ( iobuf ) ); if ( ! dhcpset ) { DBGC ( dhcp, "DHCP %p could not store DHCP packet\n", dhcp ); - return -ENOMEM; + rc = -ENOMEM; + goto err_dhcpset_create; } dhcphdr = dhcpset->dhcppkt.dhcphdr; /* Identify message type */ dhcppkt_fetch ( &dhcpset->dhcppkt, DHCP_MESSAGE_TYPE, &msgtype, sizeof ( msgtype ) ); - DBGC ( dhcp, "DHCP %p received %s %p\n", - dhcp, dhcp_msgtype_name ( msgtype ), dhcpset ); + DBGC ( dhcp, "DHCP %p received %s %p from port %d\n", dhcp, + dhcp_msgtype_name ( msgtype ), dhcpset, ntohs ( src_port ) ); /* Check for matching transaction ID */ if ( dhcphdr->xid != dhcp_xid ( dhcp->netdev ) ) { DBGC ( dhcp, "DHCP %p received %s %p has bad transaction ID\n", dhcp, dhcp_msgtype_name ( msgtype ), dhcpset ); - goto out; + rc = -EINVAL; + goto err_xid; }; /* Handle packet based on current state */ switch ( dhcp->state ) { case DHCP_STATE_DISCOVER: - if ( msgtype == DHCPOFFER ) + if ( ( msgtype == DHCPOFFER ) && + ( src_port == htons ( BOOTPS_PORT ) ) ) dhcp_rx_dhcpoffer ( dhcp, dhcpset ); break; case DHCP_STATE_REQUEST: - if ( msgtype == DHCPACK ) + if ( ( msgtype == DHCPACK ) && + ( src_port == htons ( BOOTPS_PORT ) ) ) dhcp_rx_dhcpack ( dhcp, dhcpset ); break; case DHCP_STATE_PROXYREQUEST: - if ( msgtype == DHCPACK ) + if ( ( msgtype == DHCPACK ) && + ( src_port == htons ( PROXYDHCP_PORT ) ) ) dhcp_rx_proxydhcpack ( dhcp, dhcpset ); break; default: @@ -899,9 +943,13 @@ static int dhcp_deliver_raw ( struct xfer_interface *xfer, break; } - out: + err_xid: dhcpset_put ( dhcpset ); - return 0; + err_dhcpset_create: + err_no_src: + err_no_meta: + free_iob ( iobuf ); + return rc; } /** DHCP data transfer interface operations */ @@ -910,8 +958,8 @@ static struct xfer_interface_operations dhcp_xfer_operations = { .vredirect = xfer_vopen, .window = unlimited_xfer_window, .alloc_iob = default_xfer_alloc_iob, - .deliver_iob = xfer_deliver_as_raw, - .deliver_raw = dhcp_deliver_raw, + .deliver_iob = dhcp_deliver_iob, + .deliver_raw = xfer_deliver_as_iob, }; /** @@ -1012,6 +1060,8 @@ int start_dhcp ( struct job_interface *job, struct net_device *netdev ) { xfer_init ( &dhcp->xfer, &dhcp_xfer_operations, &dhcp->refcnt ); dhcp->netdev = netdev_get ( netdev ); dhcp->timer.expired = dhcp_timer_expired; + dhcp->timer.min_timeout = DHCP_MIN_TIMEOUT; + dhcp->timer.max_timeout = DHCP_MAX_TIMEOUT; dhcp->start = currticks(); /* Instantiate child objects and attach to our interfaces */ diff --git a/gpxe/src/net/udp/tftp.c b/gpxe/src/net/udp/tftp.c index e49bcf9f..8fdb3714 100644 --- a/gpxe/src/net/udp/tftp.c +++ b/gpxe/src/net/udp/tftp.c @@ -316,17 +316,30 @@ void tftp_set_mtftp_port ( unsigned int port ) { */ static int tftp_send_rrq ( struct tftp_request *tftp ) { struct tftp_rrq *rrq; - const char *path = tftp->uri->path; - size_t len = ( sizeof ( *rrq ) + strlen ( path ) + 1 /* NUL */ - + 5 + 1 /* "octet" + NUL */ - + 7 + 1 + 5 + 1 /* "blksize" + NUL + ddddd + NUL */ - + 5 + 1 + 1 + 1 /* "tsize" + NUL + "0" + NUL */ - + 9 + 1 + 1 /* "multicast" + NUL + NUL */ ); + const char *path; + size_t len; struct io_buffer *iobuf; + /* Strip initial '/' if present. If we were opened via the + * URI interface, then there will be an initial '/', since a + * full tftp:// URI provides no way to specify a non-absolute + * path. However, many TFTP servers (particularly Windows + * TFTP servers) complain about having an initial '/', and it + * violates user expectations to have a '/' silently added to + * the DHCP-specified filename. + */ + path = tftp->uri->path; + if ( *path == '/' ) + path++; + DBGC ( tftp, "TFTP %p requesting \"%s\"\n", tftp, path ); /* Allocate buffer */ + len = ( sizeof ( *rrq ) + strlen ( path ) + 1 /* NUL */ + + 5 + 1 /* "octet" + NUL */ + + 7 + 1 + 5 + 1 /* "blksize" + NUL + ddddd + NUL */ + + 5 + 1 + 1 + 1 /* "tsize" + NUL + "0" + NUL */ + + 9 + 1 + 1 /* "multicast" + NUL + NUL */ ); iobuf = xfer_alloc_iob ( &tftp->socket, len ); if ( ! iobuf ) return -ENOMEM; diff --git a/gpxe/src/tests/comboot/shuffle-simple.asm b/gpxe/src/tests/comboot/shuffle-simple.asm new file mode 100644 index 00000000..efc7d9b4 --- /dev/null +++ b/gpxe/src/tests/comboot/shuffle-simple.asm @@ -0,0 +1,40 @@ + bits 16 + org 100h + + jmp start + +shuffle_start: + push 0xB800 + pop es + mov cx, 80*24*2 + mov ax, 'AA' + xor di, di + rep stosw +.lbl: jmp .lbl +shuffle_end: + nop +shuffle_len equ (shuffle_end - shuffle_start + 1) + +start: + ; calculate physical address of shuffled part + xor eax, eax + push ds + pop ax + shl eax, 4 + add ax, shuffle_start + mov dword [source], eax + + mov ax, 0012h + mov di, shuffle_descriptors + mov cx, num_shuffle_descriptors + mov ebp, 0x7c00 + int 22h + int3 + +shuffle_descriptors: + dd 0x7C00 +source: dd 0 + dd shuffle_len + +num_shuffle_descriptors equ 1 + diff --git a/gpxe/src/tests/comboot/version.asm b/gpxe/src/tests/comboot/version.asm new file mode 100644 index 00000000..01140423 --- /dev/null +++ b/gpxe/src/tests/comboot/version.asm @@ -0,0 +1,136 @@ + bits 16 + org 100h + +_start: + ; first check for SYSLINUX + mov ah, 30h + int 21h + + cmp eax, 59530000h + jne .not_syslinux + cmp ebx, 4c530000h + jne .not_syslinux + cmp ecx, 4e490000h + jne .not_syslinux + cmp edx, 58550000h + jne .not_syslinux + + ; now get syslinux version + mov ax, 0001h + int 22h + + push cx + push dx + push di + push si + push es + + ; print version string + mov dx, str_version + mov ah, 09h + int 21h + + pop es + pop bx + push es + mov ax, 0002h + int 22h + + ; print copyright string + mov dx, str_copyright + mov ah, 09h + int 21h + + pop es + pop bx + mov ax, 0002h + int 22h + + ; print syslinux derivative id + mov dx, str_derivative + mov ah, 09h + int 21h + + pop ax + call print_hex_byte + + ; print version number + mov dx, str_version_num + mov ah, 09h + int 21h + + pop cx + push cx + mov ax, cx + and ax, 0FFh + call print_dec_word + + mov dl, '.' + mov ah, 02h + int 21h + + pop cx + mov ax, cx + shr ax, 8 + call print_dec_word + + ret + + +.not_syslinux: + mov dx, str_not_syslinux + mov ah, 09h + int 21h + ret + +; input: al = byte to print in hex +print_hex_byte: + push ax + shr al, 4 + call print_hex_nybble + pop ax + call print_hex_nybble + ret + +; input: bottom half of al = nybble to print in hex +print_hex_nybble: + push ax + mov bl, al + and bx, 1111b + mov dl, [str_hex + bx] + mov ah, 02h + int 21h + pop ax + ret + +str_hex: db "01234567890abcdef" + +; input: ax = word to print +print_dec_word: + mov cx, 10 + mov word [.count], 0 +.loop: + xor dx, dx + div cx + inc word [.count] + push dx + test ax, ax + jnz .loop + +.print: + pop dx + add dx, '0' + mov ah, 02h + int 21h + dec word [.count] + jnz .print + + ret + +.count: dw 0 + +str_not_syslinux: db "Not SYSLINUX or derivative (running on DOS?)$" +str_version: db "Version: $" +str_copyright: db 10, "Copyright: $" +str_derivative: db 10, "Derivative ID: 0x$" +str_version_num: db 10, "Version number: $" diff --git a/gpxe/src/usr/autoboot.c b/gpxe/src/usr/autoboot.c index cff6e95d..326292b4 100644 --- a/gpxe/src/usr/autoboot.c +++ b/gpxe/src/usr/autoboot.c @@ -24,6 +24,7 @@ #include <gpxe/settings.h> #include <gpxe/image.h> #include <gpxe/embedded.h> +#include <gpxe/uri.h> #include <usr/ifmgmt.h> #include <usr/route.h> #include <usr/dhcpmgmt.h> @@ -41,6 +42,9 @@ /** Time to wait for link-up */ #define LINK_WAIT_MS 15000 +/** Shutdown flags for exit */ +int shutdown_exit_flags = 0; + /** * Identify the boot network device * @@ -75,15 +79,39 @@ static int boot_embedded_image ( void ) { } /** - * Boot using filename + * Boot using next-server and filename * * @v filename Boot filename * @ret rc Return status code */ -static int boot_filename ( const char *filename ) { +static int boot_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/ */ + strlen(filename) + 1 ]; + int filename_is_absolute; int rc; + /* Construct URI */ + uri = parse_uri ( filename ); + if ( ! uri ) { + printf ( "Out of memory\n" ); + return -ENOMEM; + } + filename_is_absolute = uri_is_absolute ( uri ); + uri_put ( uri ); + if ( ! filename_is_absolute ) { + /* Construct a tftp:// URI for the filename. We can't + * just rely on the current working URI, because the + * relative URI resolution will remove the distinction + * between filenames with and without initial slashes, + * which is significant for TFTP. + */ + snprintf ( buf, sizeof ( buf ), "tftp://%s/%s", + inet_ntoa ( next_server ), filename ); + filename = buf; + } + image = alloc_image(); if ( ! image ) { printf ( "Out of memory\n" ); @@ -132,6 +160,7 @@ int boot_root_path ( const char *root_path ) { */ static int netboot ( struct net_device *netdev ) { char buf[256]; + struct in_addr next_server; int rc; /* Open device and display device status */ @@ -158,10 +187,11 @@ static int netboot ( struct net_device *netdev ) { return rc; /* 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_filename ( buf ); + return boot_next_server_and_filename ( next_server, buf ); } /* No filename; try the root path */ diff --git a/gpxe/src/usr/imgmgmt.c b/gpxe/src/usr/imgmgmt.c index bead4867..be153f87 100644 --- a/gpxe/src/usr/imgmgmt.c +++ b/gpxe/src/usr/imgmgmt.c @@ -86,19 +86,23 @@ int imgexec ( struct image *image ) { } /** - * Identify the first loaded image + * Identify the only loaded image * - * @ret image Image, or NULL + * @ret image Image, or NULL if 0 or >1 images are loaded */ struct image * imgautoselect ( void ) { struct image *image; + struct image *selected_image = NULL; + int flagged_images = 0; for_each_image ( image ) { - if ( image->flags & IMAGE_LOADED ) - return image; + if ( image->flags & IMAGE_LOADED ) { + selected_image = image; + flagged_images++; + } } - return NULL; + return ( ( flagged_images == 1 ) ? selected_image : NULL ); } /** diff --git a/gpxe/src/usr/iscsiboot.c b/gpxe/src/usr/iscsiboot.c index 99edc879..84d77c45 100644 --- a/gpxe/src/usr/iscsiboot.c +++ b/gpxe/src/usr/iscsiboot.c @@ -1,13 +1,25 @@ #include <stdint.h> #include <string.h> +#include <stdlib.h> #include <stdio.h> +#include <errno.h> #include <gpxe/iscsi.h> #include <gpxe/settings.h> +#include <gpxe/dhcp.h> #include <gpxe/netdevice.h> #include <gpxe/ibft.h> +#include <gpxe/init.h> #include <int13.h> +#include <usr/autoboot.h> #include <usr/iscsiboot.h> +struct setting keep_san_setting __setting = { + .name = "keep-san", + .description = "Preserve SAN connection", + .tag = DHCP_EB_KEEP_SAN, + .type = &setting_type_int8, +}; + /** * Guess boot network device * @@ -25,45 +37,66 @@ static struct net_device * guess_boot_netdev ( void ) { } int iscsiboot ( const char *root_path ) { - struct scsi_device scsi; - struct int13_drive drive; + struct scsi_device *scsi; + struct int13_drive *drive; + int keep_san; int rc; - memset ( &scsi, 0, sizeof ( scsi ) ); - memset ( &drive, 0, sizeof ( drive ) ); + scsi = zalloc ( sizeof ( *scsi ) ); + if ( ! scsi ) { + rc = -ENOMEM; + goto err_alloc_scsi; + } + drive = zalloc ( sizeof ( *drive ) ); + if ( ! drive ) { + rc = -ENOMEM; + goto err_alloc_drive; + } printf ( "iSCSI booting from %s\n", root_path ); - if ( ( rc = iscsi_attach ( &scsi, root_path ) ) != 0 ) { + if ( ( rc = iscsi_attach ( scsi, root_path ) ) != 0 ) { printf ( "Could not attach iSCSI device: %s\n", strerror ( rc ) ); - goto error_attach; + goto err_attach; } - if ( ( rc = init_scsidev ( &scsi ) ) != 0 ) { + if ( ( rc = init_scsidev ( scsi ) ) != 0 ) { printf ( "Could not initialise iSCSI device: %s\n", strerror ( rc ) ); - goto error_init; + goto err_init; } - drive.blockdev = &scsi.blockdev; + drive->blockdev = &scsi->blockdev; /* FIXME: ugly, ugly hack */ struct net_device *netdev = guess_boot_netdev(); struct iscsi_session *iscsi = - container_of ( scsi.backend, struct iscsi_session, refcnt ); + container_of ( scsi->backend, struct iscsi_session, refcnt ); ibft_fill_data ( netdev, iscsi ); - register_int13_drive ( &drive ); - printf ( "Registered as BIOS drive %#02x\n", drive.drive ); - printf ( "Booting from BIOS drive %#02x\n", drive.drive ); - rc = int13_boot ( drive.drive ); + register_int13_drive ( drive ); + printf ( "Registered as BIOS drive %#02x\n", drive->drive ); + printf ( "Booting from BIOS drive %#02x\n", drive->drive ); + rc = int13_boot ( drive->drive ); printf ( "Boot failed\n" ); - printf ( "Unregistering BIOS drive %#02x\n", drive.drive ); - unregister_int13_drive ( &drive ); + /* Leave drive registered, if instructed to do so */ + keep_san = fetch_intz_setting ( NULL, &keep_san_setting ); + if ( keep_san ) { + printf ( "Preserving connection to SAN disk\n" ); + shutdown_exit_flags |= SHUTDOWN_KEEP_DEVICES; + return rc; + } + + printf ( "Unregistering BIOS drive %#02x\n", drive->drive ); + unregister_int13_drive ( drive ); - error_init: - iscsi_detach ( &scsi ); - error_attach: + err_init: + iscsi_detach ( scsi ); + err_attach: + free ( drive ); + err_alloc_drive: + free ( scsi ); + err_alloc_scsi: return rc; } diff --git a/gpxe/src/util/Option/ROM.pm b/gpxe/src/util/Option/ROM.pm index f5c33f8a..a86d3262 100644 --- a/gpxe/src/util/Option/ROM.pm +++ b/gpxe/src/util/Option/ROM.pm @@ -73,7 +73,10 @@ sub FETCH { my $raw = substr ( ${$self->{data}}, ( $self->{offset} + $self->{fields}->{$key}->{offset} ), $self->{fields}->{$key}->{length} ); - return unpack ( $self->{fields}->{$key}->{pack}, $raw ); + my $unpack = ( ref $self->{fields}->{$key}->{unpack} ? + $self->{fields}->{$key}->{unpack} : + sub { unpack ( $self->{fields}->{$key}->{pack}, shift ); } ); + return &$unpack ( $raw ); } sub STORE { @@ -82,7 +85,10 @@ sub STORE { my $value = shift; croak "Nonexistent field \"$key\"" unless $self->EXISTS ( $key ); - my $raw = pack ( $self->{fields}->{$key}->{pack}, $value ); + my $pack = ( ref $self->{fields}->{$key}->{pack} ? + $self->{fields}->{$key}->{pack} : + sub { pack ( $self->{fields}->{$key}->{pack}, shift ); } ); + my $raw = &$pack ( $value ); substr ( ${$self->{data}}, ( $self->{offset} + $self->{fields}->{$key}->{offset} ), $self->{fields}->{$key}->{length} ) = $raw; @@ -168,6 +174,38 @@ use constant PNP_SIGNATURE => '$PnP'; our @EXPORT_OK = qw ( ROM_SIGNATURE PCI_SIGNATURE PNP_SIGNATURE ); our %EXPORT_TAGS = ( all => [ @EXPORT_OK ] ); +use constant JMP_SHORT => 0xeb; +use constant JMP_NEAR => 0xe9; + +sub pack_init { + my $dest = shift; + + # Always create a near jump; it's simpler + if ( $dest ) { + return pack ( "CS", JMP_NEAR, ( $dest - 6 ) ); + } else { + return pack ( "CS", 0, 0 ); + } +} + +sub unpack_init { + my $instr = shift; + + # Accept both short and near jumps + my $jump = unpack ( "C", $instr ); + if ( $jump == JMP_SHORT ) { + my $offset = unpack ( "xC", $instr ); + return ( $offset + 5 ); + } elsif ( $jump == JMP_NEAR ) { + my $offset = unpack ( "xS", $instr ); + return ( $offset + 6 ); + } elsif ( $jump == 0 ) { + return 0; + } else { + croak "Unrecognised jump instruction in init vector\n"; + } +} + =pod =item C<< new () >> @@ -187,7 +225,11 @@ sub new { fields => { signature => { offset => 0x00, length => 0x02, pack => "S" }, length => { offset => 0x02, length => 0x01, pack => "C" }, + # "init" is part of a jump instruction + init => { offset => 0x03, length => 0x03, + pack => \&pack_init, unpack => \&unpack_init }, checksum => { offset => 0x06, length => 0x01, pack => "C" }, + bofm_header => { offset => 0x14, length => 0x02, pack => "S" }, undi_header => { offset => 0x16, length => 0x02, pack => "S" }, pci_header => { offset => 0x18, length => 0x02, pack => "S" }, pnp_header => { offset => 0x1a, length => 0x02, pack => "S" }, diff --git a/gpxe/src/util/disrom.pl b/gpxe/src/util/disrom.pl index 64128698..80ac4af8 100755 --- a/gpxe/src/util/disrom.pl +++ b/gpxe/src/util/disrom.pl @@ -1,114 +1,76 @@ #!/usr/bin/perl -w # -# Program to display key information about a boot ROM -# including PCI and PnP structures +# Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>. # -# GPL, Ken Yap 2001 +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or any later version. # +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -use bytes; - -sub getid ($) -{ - my ($offset) = @_; - - return '' if ($offset == 0 or $offset > $len); - my ($string) = unpack('Z32', substr($data, $offset, 32)); - return ($string); -} +use strict; +use warnings; -sub dispci -{ - my ($pcidata) = substr($data, $pci, 0x18); - my ($dummy, $vendorid, $deviceid, $vpd, $pcilen, $pcirev, - $class1, $class2, $class3, $imglen, $coderev, $codetype, - $indicator) = unpack('a4v4C4v2C2', $pcidata); - $imglen *= 512; - my $vendorstr = sprintf('%#04x', $vendorid); - my $devicestr = sprintf('%#04x', $deviceid); - my $coderevstr = sprintf('%#04x', $coderev); - print <<EOF; -PCI structure: +use FindBin; +use lib "$FindBin::Bin"; +use Option::ROM qw ( :all ); -Vital product data: $vpd -Vendor ID: $vendorstr -Device ID: $devicestr -Device base type: $class1 -Device sub type: $class2 -Device interface type: $class3 -Image length: $imglen -Code revision: $coderevstr -Code type: $codetype -Indicator: $indicator +my $romfile = shift || "-"; +my $rom = new Option::ROM; +$rom->load ( $romfile ); -EOF -} +die "Not an option ROM image\n" + unless $rom->{signature} == ROM_SIGNATURE; -sub dispnp -{ - my ($pnpdata) = substr($data, $pnp, 0x20); - my ($dummy1, $pnprev, $pnplen, $nextpnp, $dummy2, - $pnpcsum, $deviceid, $mfrid, $productid, - $class1, $class2, $class3, $indicator, - $bcv, $dv, $bev, $dummy, $sri) = unpack('a4C2vC2a4v2C4v5', $pnpdata); - print <<EOF; -PnP structure: +my $romlength = ( $rom->{length} * 512 ); +my $filelength = $rom->length; +die "ROM image truncated (is $filelength, should be $romlength)\n" + if $filelength < $romlength; -EOF - print 'Vendor: ', &getid($mfrid), "\n"; - print 'Device: ', &getid($productid), "\n"; - my $indicatorstr = sprintf('%#02x', $indicator); - my $bcvstr = sprintf('%#04x', $bcv); - my $dvstr = sprintf('%#04x', $dv); - my $bevstr = sprintf('%#04x', $bev); - my $sristr = sprintf('%#04x', $sri); - my $checksum = unpack('%8C*', $pnpdata); - print <<EOF; -Device base type: $class1 -Device sub type: $class2 -Device interface type: $class3 -Device indicator: $indicatorstr -Boot connection vector: $bcvstr -Disconnect vector: $dvstr -Bootstrap entry vector: $bevstr -Static resource information vector: $sristr -Checksum: $checksum +printf "ROM header:\n\n"; +printf " Length:\t0x%02x (%d)\n", $rom->{length}, ( $rom->{length} * 512 ); +printf " Checksum:\t0x%02x (0x%02x)\n", $rom->{checksum}, $rom->checksum; +printf " Init:\t\t0x%04x\n", $rom->{init}; +printf " UNDI header:\t0x%04x\n", $rom->{undi_header}; +printf " PCI header:\t0x%04x\n", $rom->{pci_header}; +printf " PnP header:\t0x%04x\n", $rom->{pnp_header}; +printf "\n"; -EOF +my $pci = $rom->pci_header(); +if ( $pci ) { + printf "PCI header:\n\n"; + printf " Signature:\t%s\n", $pci->{signature}; + printf " Vendor id:\t0x%04x\n", $pci->{vendor_id}; + printf " Device id:\t0x%04x\n", $pci->{device_id}; + printf " Device class:\t0x%02x%02x%02x\n", + $pci->{base_class}, $pci->{sub_class}, $pci->{prog_intf}; + printf " Image length:\t0x%04x (%d)\n", + $pci->{image_length}, ( $pci->{image_length} * 512 ); + printf " Runtime length:\t0x%04x (%d)\n", + $pci->{runtime_length}, ( $pci->{runtime_length} * 512 ); + printf " Config header:\t0x%04x\n", $pci->{conf_header}; + printf " CLP entry:\t0x%04x\n", $pci->{clp_entry}; + printf "\n"; } -sub pcipnp -{ - ($pci, $pnp) = unpack('v2', substr($data, 0x18, 4)); - if ($pci >= $len or $pnp >= $len) { - print "$file: Not a PCI PnP ROM image\n"; - return; - } - if (substr($data, $pci, 4) ne 'PCIR' or substr($data, $pnp, 4) ne '$PnP') { - print "$file: No PCI and PNP structures, not a PCI PNP ROM image\n"; - return; - } - &dispci(); - &dispnp(); +my $pnp = $rom->pnp_header(); +if ( $pnp ) { + printf "PnP header:\n\n"; + printf " Signature:\t%s\n", $pnp->{signature}; + printf " Checksum:\t0x%02x (0x%02x)\n", $pnp->{checksum}, $pnp->checksum; + printf " Manufacturer:\t0x%04x \"%s\"\n", + $pnp->{manufacturer}, $pnp->manufacturer; + printf " Product:\t0x%04x \"%s\"\n", $pnp->{product}, $pnp->product; + printf " BCV:\t\t0x%04x\n", $pnp->{bcv}; + printf " BDV:\t\t0x%04x\n", $pnp->{bdv}; + printf " BEV:\t\t0x%04x\n", $pnp->{bev}; + printf "\n"; } - -$file = $#ARGV >= 0 ? $ARGV[0] : '-'; -open(F, "$file") or die "$file: $!\n"; -binmode(F); -# Handle up to 64kB ROM images -$len = read(F, $data, 64*1024); -close(F); -defined($len) or die "$file: $!\n"; -substr($data, 0, 2) eq "\x55\xAA" or die "$file: Not a boot ROM image\n"; -my ($codelen) = unpack('C', substr($data, 2, 1)); -$codelen *= 512; -if ($codelen < $len) { - my $pad = $len - $codelen; - print "Image is $codelen bytes and has $pad bytes of padding following\n"; - $data = substr($data, 0, $codelen); -} elsif ($codelen > $len) { - print "Image should be $codelen bytes but is truncated to $len bytes\n";} -&pcipnp(); -($csum) = unpack('%8C*', $data); -print "ROM checksum: $csum \n"; -exit(0); diff --git a/gpxe/src/util/mergerom.pl b/gpxe/src/util/mergerom.pl index ce1befb7..f9c52502 100644 --- a/gpxe/src/util/mergerom.pl +++ b/gpxe/src/util/mergerom.pl @@ -23,6 +23,18 @@ use FindBin; use lib "$FindBin::Bin"; use Option::ROM qw ( :all ); +sub merge_entry_points { + my $baserom_entry = \shift; + my $rom_entry = \shift; + my $offset = shift; + + if ( $$rom_entry ) { + my $old_entry = $$baserom_entry; + $$baserom_entry = ( $offset + $$rom_entry ); + $$rom_entry = $old_entry; + } +} + my @romfiles = @ARGV; my @roms = map { my $rom = new Option::ROM; $rom->load($_); $rom } @romfiles; @@ -34,6 +46,12 @@ foreach my $rom ( @roms ) { # Update base length $baserom->{length} += $rom->{length}; + # Merge initialisation entry point + merge_entry_points ( $baserom->{init}, $rom->{init}, $offset ); + + # Merge BOFM header + merge_entry_points ( $baserom->{bofm_header}, $rom->{bofm_header}, $offset ); + # Update PCI header, if present in both my $baserom_pci = $baserom->pci_header; my $rom_pci = $rom->pci_header; @@ -52,8 +70,8 @@ foreach my $rom ( @roms ) { # Merge CLP entry point if ( exists ( $baserom_pci->{clp_entry} ) && exists ( $rom_pci->{clp_entry} ) ) { - $baserom_pci->{clp_entry} = ( $offset + $rom_pci->{clp_entry} ) - if $rom_pci->{clp_entry}; + merge_entry_points ( $baserom_pci->{clp_entry}, $rom_pci->{clp_entry}, + $offset ); } } @@ -61,9 +79,9 @@ foreach my $rom ( @roms ) { my $baserom_pnp = $baserom->pnp_header; my $rom_pnp = $rom->pnp_header; if ( $baserom_pnp && $rom_pnp ) { - $baserom_pnp->{bcv} = ( $offset + $rom_pnp->{bcv} ) if $rom_pnp->{bcv}; - $baserom_pnp->{bdv} = ( $offset + $rom_pnp->{bdv} ) if $rom_pnp->{bdv}; - $baserom_pnp->{bev} = ( $offset + $rom_pnp->{bev} ) if $rom_pnp->{bev}; + merge_entry_points ( $baserom_pnp->{bcv}, $rom_pnp->{bcv}, $offset ); + merge_entry_points ( $baserom_pnp->{bdv}, $rom_pnp->{bdv}, $offset ); + merge_entry_points ( $baserom_pnp->{bev}, $rom_pnp->{bev}, $offset ); } # Fix checksum for this ROM segment diff --git a/gpxe/src/util/zbin.c b/gpxe/src/util/zbin.c index f47fa36b..b24f401e 100644 --- a/gpxe/src/util/zbin.c +++ b/gpxe/src/util/zbin.c @@ -90,7 +90,8 @@ static int read_file ( const char *filename, void **buf, size_t *len ) { return 0; err: - fclose ( file ); + if ( file ) + fclose ( file ); return -1; } |