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:
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
$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 );
}
SRCDIRS += drivers/bus
SRCDIRS += drivers/net
SRCDIRS += drivers/net/e1000
+SRCDIRS += drivers/net/phantom
SRCDIRS += drivers/block
SRCDIRS += drivers/nvs
SRCDIRS += drivers/bitbash
# 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
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
#
+++ /dev/null
-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.
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.
/* 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 );
}
"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
"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
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,
/* 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,
#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
* %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
* %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
* 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
*/
.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
* 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();
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
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 );
}
/**
* 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 ) );
}
/**
* 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 );
}
/**
* 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
}
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;
}
__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;
}
"=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" );
/* 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 */
}
/* 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;
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,
+ },
+};
#include <gpxe/image.h>
#include <gpxe/segment.h>
#include <gpxe/init.h>
-#include <gpxe/initrd.h>
#include <gpxe/cpio.h>
#include <gpxe/features.h>
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 */
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,
--- /dev/null
+/*
+ * 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,
+};
--- /dev/null
+/*
+ * 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,
+};
/* 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",
/* 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 ) ),
/* 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",
/* 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 ) ) {
#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 )
--- /dev/null
+#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
/** 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
#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 ) {
( * ( ( 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 );
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" \
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 */
#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 */
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;
}
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;
}
/**
/* 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 ) )
/* Copy parameter block from caller */
copy_from_user ( ¶ms, uparams, 0, sizeof ( params ) );
+ /* Fill in ROM segment address */
+ ppxe.UNDIROMID.segment = ix86->segs.ds;
+
/* Set default status in case child routine fails to do so */
params.Status = PXENV_STATUS_FAILURE;
"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" );
.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 */
.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
****************************************************************************
* 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:
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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 );
+}
--- /dev/null
+#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;
+}
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
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:
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:
#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
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
.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 */
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 */
.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
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 */
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:
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
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:
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
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
*/
bbs_version:
.word 0
+ .size bbs_version, . - bbs_version
/* Boot Execution Vector entry point
*
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
.previous
exec_message:
- .asciz "Entering gPXE\n"
+ .asciz " starting execution\n"
.size exec_message, . - exec_message
/* UNDI loader
/* 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 */
/* Restore registers and return */
popw %bx
popw %es
+ popw %ds
popl %edi
popl %esi
lret
*/
/*
+ * 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
*/
. = _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 = .;
****************************************************************************
*/
.section ".data"
+ .globl rm_sp
rm_sp: .word 0
+ .globl rm_ss
rm_ss: .word 0
pm_esp: .long _estack
--- /dev/null
+/*\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
*/
#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 */
#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
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 );
#include <gpxe/list.h>
#include <gpxe/tables.h>
#include <gpxe/device.h>
+#include <gpxe/init.h>
/**
* @file
/**
* 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;
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,
+};
#include <assert.h>
#include <gpxe/tables.h>
#include <gpxe/command.h>
+#include <gpxe/settings.h>
/** @file
*
}
/**
+ * 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
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;
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,
+};
*
* @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 );
}
/**
+ * 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
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;
}
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().
* 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;
shell();
}
- shutdown();
+ shutdown ( SHUTDOWN_EXIT | shutdown_exit_flags );
return 0;
}
#include <gpxe/keys.h>
#include <gpxe/job.h>
#include <gpxe/monojob.h>
+#include <gpxe/timer.h>
/** @file
*
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() ) {
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;
}
* 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.
};
/** 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,
};
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 ) );
}
/**
* 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;
/**
* 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 );
/**
* 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 );
}
/**
- * 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 );
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 = {
#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
/*
else
np->tx_flags |= NV_TX2_LASTPACKET1;
break;
+ case 0x0373:
+ /* Fall Through */
case 0x0086:
/* Fall Through */
case 0x008c:
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 );
};
/**
- * 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 ) );
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;
}
/**
.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,
};
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",
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 "
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 );
#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 )
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:
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:
static int tg3_abort_hw(struct tg3 *tp)
{
int i, err;
+ uint32_t val;
tg3_disable_ints(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);
// 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;
}
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;
(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)
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 {
}
- /* 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);
(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);
!(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) {
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);
}
}
/* 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);
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";
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"),
#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
/* 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
#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
((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;
#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 ) {
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 ) {
#include <getopt.h>
#include <gpxe/image.h>
#include <gpxe/command.h>
-#include <gpxe/initrd.h>
#include <usr/imgmgmt.h>
/** @file
}
/**
- * 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
} else {
image = imgautoselect();
if ( ! image ) {
- printf ( "No loaded images\n" );
+ printf ( "No (unique) loaded image\n" );
return 1;
}
}
.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",
#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 */
+++ /dev/null
-/*
- * 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",
-};
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 );
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 */
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;
/** 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 */
* 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
*
* 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
*
* 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
*/
#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
/** 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"
#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 )
/** @} */
#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 */
/** @} */
*
*/
-/**
- * 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 */
/** @} */
+/** 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
*
*/
struct startup_fn {
void ( * startup ) ( void );
- void ( * shutdown ) ( void );
+ void ( * shutdown ) ( int flags );
};
/** Declare a startup/shutdown function */
extern void initialise ( void );
extern void startup ( void );
-extern void shutdown ( void );
+extern void shutdown ( int flags );
#endif /* _GPXE_INIT_H */
+++ /dev/null
-#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 */
*/
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
*
/** 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
/** 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
* @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
*
#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
#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.
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
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 */
*
*/
+extern int shutdown_exit_flags;
+
extern void autoboot ( void );
extern int boot_root_path ( const char *root_path );
/* 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;
* 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,
pxe_set_netdev ( NULL );
/* Prepare for unload */
- shutdown();
+ shutdown ( SHUTDOWN_BOOT );
stop_undi->Status = PXENV_STATUS_SUCCESS;
return PXENV_EXIT_SUCCESS;
/* 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 );
struct xfer_metadata meta = {
.src = ( struct sockaddr * ) &pxe_udp.local,
.dest = ( struct sockaddr * ) &dest,
+ .netdev = pxe_netdev,
};
size_t len;
struct io_buffer *iobuf;
*
*/
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" );
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,
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,
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,
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
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" );
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;
/* 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 */
/* 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 */
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;
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 */
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;
}
/**
.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,
};
/* 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,
*/
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
*/
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 );
}
/**
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 ) {
* 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 );
}
}
}
*
*/
-/** 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 );
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 ) );
}
/* 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 );
FTP_TYPE,
FTP_PASV,
FTP_RETR,
+ FTP_WAIT,
FTP_QUIT,
FTP_DONE,
};
* 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,
};
/**
}
/**
+ * 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
( ( 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 */
}
}
- /* 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 );
+
}
/**
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 );
+ }
}
/**
/** 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 );
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 );
/* 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;
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 ) );
}
/**
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 ) {
}
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] );
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"
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;
}
*/
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
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;
}
const char *value ) {
unsigned int identifier;
char *endp;
+ int rc;
/* The CHAP identifier is an integer value */
identifier = strtoul ( value, &endp, 0 );
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;
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;
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;
}
{ "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 }
};
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;
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;
}
* 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;
}
/**
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 */
.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 */
},
{
.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 = "",
},
{
*/
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 );
/** 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,
*/
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;
/* 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;
/* 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 );
}
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 );
}
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;
}
/* If we have a ProxyDHCPOFFER, transition to PROXYDHCPREQUEST */
if ( dhcp->proxydhcpoffer ) {
+ dhcp->timer.min_timeout = 0;
dhcp_set_state ( dhcp, DHCP_STATE_PROXYREQUEST );
return;
}
*/
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;
* 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:
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 */
.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,
};
/**
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 */
*/
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;
--- /dev/null
+ 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
+
--- /dev/null
+ 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: $"
#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>
/** 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
*
}
/**
- * 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" );
*/
static int netboot ( struct net_device *netdev ) {
char buf[256];
+ struct in_addr next_server;
int rc;
/* Open device and display device status */
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 */
}
/**
- * 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 );
}
/**
#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
*
}
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;
}
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 {
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;
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 () >>
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" },
#!/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);
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;
# 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;
# 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 );
}
}
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
return 0;
err:
- fclose ( file );
+ if ( file )
+ fclose ( file );
return -1;
}