gPXE: update gPXE to current git
authorH. Peter Anvin <hpa@zytor.com>
Mon, 8 Sep 2008 05:41:29 +0000 (22:41 -0700)
committerH. Peter Anvin <hpa@zytor.com>
Mon, 8 Sep 2008 05:41:29 +0000 (22:41 -0700)
Update gPXE to current git.

gpxe-for-syslinux e3ef2094cfa26f874c5f8dbd687eb311830efcf0
gpxe main tree 8223084afc206000312611a3fcfa30a28500d1a3

102 files changed:
gpxe/contrib/bochs/README
gpxe/contrib/bochs/README.qemu
gpxe/contrib/bochs/serial-console
gpxe/src/Makefile
gpxe/src/Makefile.housekeeping
gpxe/src/README.cvs [deleted file]
gpxe/src/arch/i386/Makefile
gpxe/src/arch/i386/core/umalloc.c
gpxe/src/arch/i386/drivers/net/undiload.c
gpxe/src/arch/i386/drivers/net/undinet.c
gpxe/src/arch/i386/firmware/pcbios/e820mangler.S
gpxe/src/arch/i386/firmware/pcbios/hidemem.c
gpxe/src/arch/i386/firmware/pcbios/memmap.c
gpxe/src/arch/i386/firmware/pcbios/smbios.c
gpxe/src/arch/i386/firmware/pcbios/smbios_settings.c
gpxe/src/arch/i386/image/bzimage.c
gpxe/src/arch/i386/image/com32.c [new file with mode: 0644]
gpxe/src/arch/i386/image/comboot.c [new file with mode: 0644]
gpxe/src/arch/i386/image/elfboot.c
gpxe/src/arch/i386/image/multiboot.c
gpxe/src/arch/i386/image/nbi.c
gpxe/src/arch/i386/include/bits/errfile.h
gpxe/src/arch/i386/include/comboot.h [new file with mode: 0644]
gpxe/src/arch/i386/include/int13.h
gpxe/src/arch/i386/include/libkir.h
gpxe/src/arch/i386/include/librm.h
gpxe/src/arch/i386/include/pxe_call.h
gpxe/src/arch/i386/include/setjmp.h
gpxe/src/arch/i386/interface/pcbios/int13.c
gpxe/src/arch/i386/interface/pxe/pxe_call.c
gpxe/src/arch/i386/interface/pxe/pxe_entry.S
gpxe/src/arch/i386/interface/syslinux/com32_call.c [new file with mode: 0644]
gpxe/src/arch/i386/interface/syslinux/com32_wrapper.S [new file with mode: 0644]
gpxe/src/arch/i386/interface/syslinux/comboot_call.c [new file with mode: 0644]
gpxe/src/arch/i386/interface/syslinux/comboot_resolv.c [new file with mode: 0644]
gpxe/src/arch/i386/prefix/libprefix.S
gpxe/src/arch/i386/prefix/lkrnprefix.S
gpxe/src/arch/i386/prefix/romprefix.S
gpxe/src/arch/i386/scripts/i386.lds
gpxe/src/arch/i386/transitions/librm.S
gpxe/src/arch/i386/transitions/librm_mgmt.c [new file with mode: 0644]
gpxe/src/config.h
gpxe/src/core/config.c
gpxe/src/core/device.c
gpxe/src/core/exec.c
gpxe/src/core/ibft.c
gpxe/src/core/init.c
gpxe/src/core/main.c
gpxe/src/core/monojob.c
gpxe/src/core/serial.c
gpxe/src/core/settings.c
gpxe/src/crypto/chap.c
gpxe/src/drivers/infiniband/hermon.c
gpxe/src/drivers/net/forcedeth.c
gpxe/src/drivers/net/ipoib.c
gpxe/src/drivers/net/phantom/phantom.c
gpxe/src/drivers/net/phantom/phantom.h
gpxe/src/drivers/net/tg3.c
gpxe/src/drivers/net/tg3.h
gpxe/src/hci/commands/config_cmd.c
gpxe/src/hci/commands/image_cmd.c
gpxe/src/hci/shell_banner.c
gpxe/src/image/initrd.c [deleted file]
gpxe/src/image/segment.c
gpxe/src/include/gpxe/chap.h
gpxe/src/include/gpxe/device.h
gpxe/src/include/gpxe/dhcp.h
gpxe/src/include/gpxe/errfile.h
gpxe/src/include/gpxe/features.h
gpxe/src/include/gpxe/hidemem.h
gpxe/src/include/gpxe/init.h
gpxe/src/include/gpxe/initrd.h [deleted file]
gpxe/src/include/gpxe/iscsi.h
gpxe/src/include/gpxe/netdevice.h
gpxe/src/include/gpxe/pci.h
gpxe/src/include/gpxe/retry.h
gpxe/src/include/gpxe/settings.h
gpxe/src/include/gpxe/vsprintf.h
gpxe/src/include/usr/autoboot.h
gpxe/src/interface/pxe/pxe_loader.c
gpxe/src/interface/pxe/pxe_preboot.c
gpxe/src/interface/pxe/pxe_udp.c
gpxe/src/interface/pxe/pxe_undi.c
gpxe/src/net/dhcppkt.c
gpxe/src/net/ethernet.c
gpxe/src/net/fakedhcp.c
gpxe/src/net/netdevice.c
gpxe/src/net/retry.c
gpxe/src/net/tcp/ftp.c
gpxe/src/net/tcp/iscsi.c
gpxe/src/net/udp.c
gpxe/src/net/udp/dhcp.c
gpxe/src/net/udp/tftp.c
gpxe/src/tests/comboot/shuffle-simple.asm [new file with mode: 0644]
gpxe/src/tests/comboot/version.asm [new file with mode: 0644]
gpxe/src/usr/autoboot.c
gpxe/src/usr/imgmgmt.c
gpxe/src/usr/iscsiboot.c
gpxe/src/util/Option/ROM.pm
gpxe/src/util/disrom.pl
gpxe/src/util/mergerom.pl
gpxe/src/util/zbin.c

index 57c6f05..e0e8803 100644 (file)
@@ -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:
index ee20eec..2582f62 100644 (file)
@@ -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 
index cc4fd00..8cd3835 100755 (executable)
@@ -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 );
 }
 
index c30bf2b..474e8cf 100644 (file)
@@ -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
index 6cd85f7..bbbfc89 100644 (file)
@@ -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 (file)
index 56a24f9..0000000
+++ /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.
index 926daa1..97ca077 100644 (file)
@@ -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.
index bfd62ef..3990488 100644 (file)
@@ -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 );
 }
index a3284f8..dbd9e7c 100644 (file)
@@ -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
index b1e8cc5..9576ad6 100644 (file)
@@ -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,
index e932804..3c4cf21 100644 (file)
 #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
        
index eba9400..2e74d3b 100644 (file)
  * 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.
  * 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
index b6a8ca3..fc0d36a 100644 (file)
@@ -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 */
index 875d421..a2cb3b8 100644 (file)
@@ -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;
index de08ec5..b088e51 100644 (file)
@@ -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,
+       },
+};
index 9372f55..e6fd854 100644 (file)
@@ -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 (file)
index 0000000..da60462
--- /dev/null
@@ -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 (file)
index 0000000..63d02c0
--- /dev/null
@@ -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,
+};
index 52510aa..c8daf72 100644 (file)
@@ -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",
index fbaebd5..a4a340f 100644 (file)
@@ -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",
index 73791be..e6a0ab0 100644 (file)
@@ -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 ) ) {
index c5b04a2..99927c2 100644 (file)
@@ -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 (file)
index 0000000..1fc3b71
--- /dev/null
@@ -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
index 2a19383..72ca97d 100644 (file)
@@ -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
index 5f67a56..1f5b135 100644 (file)
@@ -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 ) {
index 31b5097..07a85c5 100644 (file)
@@ -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"   \
index dc58531..7a38d31 100644 (file)
@@ -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 */
index ed2be27..bb0a100 100644 (file)
@@ -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 */
index 7e09fb5..6f61e4a 100644 (file)
@@ -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 ) )
index 5b307d4..3ccb7fb 100644 (file)
@@ -347,6 +347,9 @@ __cdecl void pxe_loader_call ( struct i386_all_regs *ix86 ) {
        /* Copy parameter block from caller */
        copy_from_user ( &params, 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" );
 
index 204894e..e5d327a 100644 (file)
@@ -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 (file)
index 0000000..586730c
--- /dev/null
@@ -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 (file)
index 0000000..08d7398
--- /dev/null
@@ -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 (file)
index 0000000..5a400ed
--- /dev/null
@@ -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 (file)
index 0000000..41c3af7
--- /dev/null
@@ -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;
+}
index cb09111..ae2a491 100644 (file)
@@ -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
index 59e70cd..c1e92f5 100644 (file)
@@ -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:
index 727cffc..f9b9e16 100644 (file)
@@ -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
index a5a0105..729ad30 100644 (file)
@@ -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 = .;
index 45e0d0f..ff4b1d9 100644 (file)
@@ -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 (file)
index 0000000..59b2eab
--- /dev/null
@@ -0,0 +1,45 @@
+/*\r
+ * librm: a library for interfacing to real-mode code\r
+ *\r
+ * Michael Brown <mbrown@fensystems.co.uk>\r
+ *\r
+ */\r
+\r
+#include <stdint.h>\r
+#include <librm.h>\r
+\r
+/*\r
+ * This file provides functions for managing librm.\r
+ *\r
+ */\r
+\r
+/**\r
+ * Allocate space on the real-mode stack and copy data there from a\r
+ * user buffer\r
+ *\r
+ * @v data                     User buffer\r
+ * @v size                     Size of stack data\r
+ * @ret sp                     New value of real-mode stack pointer\r
+ */\r
+uint16_t copy_user_to_rm_stack ( userptr_t data, size_t size ) {\r
+       userptr_t rm_stack;\r
+       rm_sp -= size;\r
+       rm_stack = real_to_user ( rm_ss, rm_sp );\r
+       memcpy_user ( rm_stack, 0, data, 0, size );\r
+       return rm_sp;\r
+};\r
+\r
+/**\r
+ * Deallocate space on the real-mode stack, optionally copying back\r
+ * data to a user buffer.\r
+ *\r
+ * @v data                     User buffer\r
+ * @v size                     Size of stack data\r
+ */\r
+void remove_user_from_rm_stack ( userptr_t data, size_t size ) {\r
+       if ( data ) {\r
+               userptr_t rm_stack = real_to_user ( rm_ss, rm_sp );\r
+               memcpy_user ( rm_stack, 0, data, 0, size );\r
+       }\r
+       rm_sp += size;\r
+};\r
index f43da04..ae39fb5 100644 (file)
@@ -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 */
 
 #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 */ 
 
index 4202682..b5624fa 100644 (file)
@@ -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 );
index b1b148e..84915c2 100644 (file)
@@ -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,
+};
index a1c073e..a9861b6 100644 (file)
@@ -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,
+};
index fda1470..ffa6596 100644 (file)
@@ -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;
 }
index ed91bf3..50e199c 100644 (file)
@@ -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;
index ca62db2..d589226 100644 (file)
@@ -62,7 +62,7 @@ __cdecl int main ( void ) {
                        shell();
        }
 
-       shutdown();
+       shutdown ( SHUTDOWN_EXIT | shutdown_exit_flags );
 
        return 0;
 }
index ea9bc83..2c91e13 100644 (file)
@@ -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;
 }
index 54c2295..97640f9 100644 (file)
@@ -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,
 };
index 7525318..e660ae7 100644 (file)
@@ -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 ) );
 }
 
 /**
index 13b8fda..59b70e3 100644 (file)
@@ -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 );
index c198556..439974e 100644 (file)
@@ -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 = {
index f619588..54fadbd 100644 (file)
@@ -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 );
index e3baa14..16b2a0c 100644 (file)
@@ -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,
 };
 
index 509a709..5644c96 100644 (file)
@@ -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 );
index 3c75998..110c122 100644 (file)
@@ -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 )
index 5d1bf40..82b37fa 100644 (file)
@@ -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"),
index 9077f80..d1c09e0 100644 (file)
@@ -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;
index 5a34cea..660c642 100644 (file)
@@ -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 ) {
index b651078..d1a38c4 100644 (file)
@@ -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",
index 92cd17d..3271c48 100644 (file)
@@ -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
  *
  */
 
-#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 (file)
index f03564a..0000000
+++ /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",
-};
index 8deaef7..9bd60f9 100644 (file)
@@ -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 );
index 0ea7ac5..a7059cd 100644 (file)
@@ -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;
 
index caabdae..f40cc95 100644 (file)
@@ -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 */
index 6144597..c5ed0ea 100644 (file)
@@ -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"
 
index a059195..ca0abeb 100644 (file)
 #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 )
 #define ERRFILE_ibft                 ( ERRFILE_OTHER | 0x000c0000 )
 #define ERRFILE_tls                  ( ERRFILE_OTHER | 0x000d0000 )
 #define ERRFILE_ifmgmt               ( ERRFILE_OTHER | 0x000e0000 )
+#define ERRFILE_iscsiboot            ( ERRFILE_OTHER | 0x000f0000 )
 
 /** @} */
 
index 70daac3..107ff08 100644 (file)
@@ -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 */
 
 /** @} */
 
index 547f888..010fdb5 100644 (file)
@@ -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 */
index c468213..d2b450d 100644 (file)
@@ -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 (file)
index 1871bf7..0000000
+++ /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 */
index 5c44675..fd96fdf 100644 (file)
@@ -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
index 1ef648e..cdc8cba 100644 (file)
@@ -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
         *
index fdcecb6..9072157 100644 (file)
 #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
index 71982fc..ec57db9 100644 (file)
@@ -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.
index ae5a259..78f3e69 100644 (file)
@@ -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
index 9360f29..aa8f8a3 100644 (file)
@@ -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 */
index b451a8c..b64cbb8 100644 (file)
@@ -7,6 +7,8 @@
  *
  */
 
+extern int shutdown_exit_flags;
+
 extern void autoboot ( void );
 extern int boot_root_path ( const char *root_path );
 
index 708d203..f815bc2 100644 (file)
@@ -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;
index 302953e..8220d1f 100644 (file)
@@ -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;
index 40c2b2e..033b1ad 100644 (file)
@@ -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,
index 76b55df..5d06f2d 100644 (file)
@@ -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 */
index 2537e25..c8bf215 100644 (file)
@@ -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;
index 55035de..7b1c496 100644 (file)
 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,
 };
index a10e442..6026475 100644 (file)
@@ -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,
index 6875b3b..3721b33 100644 (file)
@@ -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 );
                }
        }
 }
index 3c93401..2a645c9 100644 (file)
  * 
  */
 
-/** 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 );
 
index ffb2fbf..3b88f7b 100644 (file)
@@ -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 );
+       }
 }
 
 /**
index a12fca8..a67415b 100644 (file)
@@ -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 = "",
        },
        {
index 8df76a4..407ea14 100644 (file)
@@ -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 );
index 6ff8afe..5fcd56e 100644 (file)
@@ -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 */
index e49bcf9..8fdb371 100644 (file)
@@ -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 (file)
index 0000000..efc7d9b
--- /dev/null
@@ -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 (file)
index 0000000..0114042
--- /dev/null
@@ -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: $"
index cff6e95..326292b 100644 (file)
@@ -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 */
index bead486..be153f8 100644 (file)
@@ -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 );
 }
 
 /**
index 99edc87..84d77c4 100644 (file)
@@ -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;
 }
index f5c33f8..a86d326 100644 (file)
@@ -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" },
index 6412869..80ac4af 100755 (executable)
 #!/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);
index ce1befb..f9c5250 100644 (file)
@@ -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
index f47fa36..b24f401 100644 (file)
@@ -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;
 }