summaryrefslogtreecommitdiff
path: root/gpxe
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2008-09-25 13:46:43 -0700
committerH. Peter Anvin <hpa@zytor.com>2008-09-25 13:46:43 -0700
commit400e2b589e6d0bdd9efee9369feb7d5f5e0958d6 (patch)
tree5563ff419701da0868ff0da295fb1a6eb44e79ef /gpxe
parent41e4481d7bb14abf1e53a9b63abce9533c4e66ad (diff)
downloadsyslinux-400e2b589e6d0bdd9efee9369feb7d5f5e0958d6.tar.gz
Update gPXE to: gpxe-for-syslinux 0a1f463e8b3218803b42cd3940e90a7678de0b3e gpxe upstream 3392cfa7df58a5662417f25226cf75dedabeb750 Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Diffstat (limited to 'gpxe')
-rw-r--r--gpxe/src/arch/i386/core/relocate.c15
-rw-r--r--gpxe/src/arch/i386/drivers/net/undirom.c4
-rw-r--r--gpxe/src/arch/i386/firmware/pcbios/e820mangler.S28
-rw-r--r--gpxe/src/arch/i386/firmware/pcbios/fakee820.c90
-rw-r--r--gpxe/src/arch/i386/firmware/pcbios/hidemem.c42
-rw-r--r--gpxe/src/arch/i386/firmware/pcbios/memmap.c39
-rw-r--r--gpxe/src/arch/i386/include/fakee820.h7
-rw-r--r--gpxe/src/arch/i386/interface/pxe/pxe_call.c1
-rw-r--r--gpxe/src/arch/i386/prefix/romprefix.S173
-rw-r--r--gpxe/src/core/settings.c53
-rw-r--r--gpxe/src/core/uri.c78
-rw-r--r--gpxe/src/crypto/axtls/bigint.h2
-rw-r--r--gpxe/src/drivers/infiniband/arbel.c2
-rw-r--r--gpxe/src/drivers/infiniband/hermon.c2
-rw-r--r--gpxe/src/drivers/net/phantom/phantom.c13
-rw-r--r--gpxe/src/include/gpxe/dhcp.h1
-rw-r--r--gpxe/src/include/gpxe/uri.h2
-rw-r--r--gpxe/src/interface/pxe/pxe_loader.c6
-rw-r--r--gpxe/src/net/tcp/iscsi.c2
-rw-r--r--gpxe/src/net/udp/dhcp.c16
20 files changed, 479 insertions, 97 deletions
diff --git a/gpxe/src/arch/i386/core/relocate.c b/gpxe/src/arch/i386/core/relocate.c
index 39d00b09..aa58ad65 100644
--- a/gpxe/src/arch/i386/core/relocate.c
+++ b/gpxe/src/arch/i386/core/relocate.c
@@ -93,11 +93,16 @@ __cdecl void relocate ( struct i386_all_regs *ix86 ) {
/* If last byte that might be used (r_end-1)
* is in an odd megabyte, round down r_end to
* the top of the next even megabyte.
+ *
+ * Make sure that we don't accidentally wrap
+ * r_end below 0.
*/
- r_end = ( r_end - 1 ) & ~0xfffff;
- DBG ( "...end truncated to %lx "
- "(avoid ending in odd megabyte)\n",
- r_end );
+ if ( r_end >= 1 ) {
+ r_end = ( r_end - 1 ) & ~0xfffff;
+ DBG ( "...end truncated to %lx "
+ "(avoid ending in odd megabyte)\n",
+ r_end );
+ }
} else if ( ( r_end - size ) & 0x100000 ) {
/* If the last byte that might be used
* (r_end-1) is in an even megabyte, but the
@@ -108,7 +113,7 @@ __cdecl void relocate ( struct i386_all_regs *ix86 ) {
* Make sure that we don't accidentally wrap
* r_end below 0.
*/
- if ( r_end > 0x100000 ) {
+ if ( r_end >= 0x100000 ) {
r_end = ( r_end - 0x100000 ) & ~0xfffff;
DBG ( "...end truncated to %lx "
"(avoid starting in odd megabyte)\n",
diff --git a/gpxe/src/arch/i386/drivers/net/undirom.c b/gpxe/src/arch/i386/drivers/net/undirom.c
index f977a553..d40fcd35 100644
--- a/gpxe/src/arch/i386/drivers/net/undirom.c
+++ b/gpxe/src/arch/i386/drivers/net/undirom.c
@@ -188,9 +188,9 @@ static void undirom_probe_all_roms ( void ) {
DBG ( "Scanning for PXE expansion ROMs\n" );
- /* Scan through expansion ROM region at 2kB intervals */
+ /* Scan through expansion ROM region at 512 byte intervals */
for ( rom_segment = 0xc000 ; rom_segment < 0x10000 ;
- rom_segment += 0x80 ) {
+ rom_segment += 0x20 ) {
undirom_probe ( rom_segment );
}
diff --git a/gpxe/src/arch/i386/firmware/pcbios/e820mangler.S b/gpxe/src/arch/i386/firmware/pcbios/e820mangler.S
index 3c4cf21b..ad773f74 100644
--- a/gpxe/src/arch/i386/firmware/pcbios/e820mangler.S
+++ b/gpxe/src/arch/i386/firmware/pcbios/e820mangler.S
@@ -245,10 +245,19 @@ get_underlying_e820:
pushl %ebx
pushl %ecx
pushl %edx
+ pushw %es
+ pushw %di
+ pushw %ds
+ popw %es
+ movw $underlying_e820_cache, %di
+ movl $20, %ecx
movl underlying_e820_ebx, %ebx
stc
pushfw
lcall *%cs:int15_vector
+ popw %di
+ popw %es
+ /* Check for error return from underlying e820 call */
jc 1f /* CF set: error */
cmpl $SMAP, %eax
je 2f /* 'SMAP' missing: error */
@@ -262,25 +271,6 @@ get_underlying_e820:
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
diff --git a/gpxe/src/arch/i386/firmware/pcbios/fakee820.c b/gpxe/src/arch/i386/firmware/pcbios/fakee820.c
new file mode 100644
index 00000000..e171edfd
--- /dev/null
+++ b/gpxe/src/arch/i386/firmware/pcbios/fakee820.c
@@ -0,0 +1,90 @@
+/* Copyright (C) 2008 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.
+ */
+
+#include <realmode.h>
+#include <biosint.h>
+
+/** Assembly routine in inline asm */
+extern void int15_fakee820();
+
+/** Original INT 15 handler */
+static struct segoff __text16 ( real_int15_vector );
+#define real_int15_vector __use_text16 ( real_int15_vector )
+
+/** An INT 15,e820 memory map entry */
+struct e820_entry {
+ /** Start of region */
+ uint64_t start;
+ /** Length of region */
+ uint64_t len;
+ /** Type of region */
+ uint32_t type;
+} __attribute__ (( packed ));
+
+#define E820_TYPE_RAM 1 /**< Normal memory */
+#define E820_TYPE_RSVD 2 /**< Reserved and unavailable */
+#define E820_TYPE_ACPI 3 /**< ACPI reclaim memory */
+#define E820_TYPE_NVS 4 /**< ACPI NVS memory */
+
+/** Fake e820 map */
+static struct e820_entry __text16_array ( e820map, [] ) __used = {
+ { 0x00000000ULL, ( 0x000a0000ULL - 0x00000000ULL ), E820_TYPE_RAM },
+ { 0x00100000ULL, ( 0xcfb50000ULL - 0x00100000ULL ), E820_TYPE_RAM },
+ { 0xcfb50000ULL, ( 0xcfb64000ULL - 0xcfb50000ULL ), E820_TYPE_RSVD },
+ { 0xcfb64000ULL, ( 0xcfb66000ULL - 0xcfb64000ULL ), E820_TYPE_RSVD },
+ { 0xcfb66000ULL, ( 0xcfb85c00ULL - 0xcfb66000ULL ), E820_TYPE_ACPI },
+ { 0xcfb85c00ULL, ( 0xd0000000ULL - 0xcfb85c00ULL ), E820_TYPE_RSVD },
+ { 0xe0000000ULL, ( 0xf0000000ULL - 0xe0000000ULL ), E820_TYPE_RSVD },
+ { 0xfe000000ULL, (0x100000000ULL - 0xfe000000ULL ), E820_TYPE_RSVD },
+ {0x100000000ULL, (0x230000000ULL -0x100000000ULL ), E820_TYPE_RAM },
+};
+#define e820map __use_text16 ( e820map )
+
+void fake_e820 ( void ) {
+ __asm__ __volatile__ (
+ TEXT16_CODE ( "\nint15_fakee820:\n\t"
+ "pushfw\n\t"
+ "cmpl $0xe820, %%eax\n\t"
+ "jne 99f\n\t"
+ "cmpl $0x534d4150, %%edx\n\t"
+ "jne 99f\n\t"
+ "pushaw\n\t"
+ "leaw e820map(%%bx), %%si\n\t"
+ "cs rep movsb\n\t"
+ "popaw\n\t"
+ "movl %%edx, %%eax\n\t"
+ "addl $20, %%ebx\n\t"
+ "cmpl %0, %%ebx\n\t"
+ "jne 1f\n\t"
+ "xorl %%ebx,%%ebx\n\t"
+ "\n1:\n\t"
+ "popfw\n\t"
+ "clc\n\t"
+ "lret $2\n\t"
+ "\n99:\n\t"
+ "popfw\n\t"
+ "ljmp *%%cs:real_int15_vector\n\t" )
+ : : "i" ( sizeof ( e820map ) ) );
+
+ hook_bios_interrupt ( 0x15, ( unsigned int ) int15_fakee820,
+ &real_int15_vector );
+}
+
+void unfake_e820 ( void ) {
+ unhook_bios_interrupt ( 0x15, ( unsigned int ) int15_fakee820,
+ &real_int15_vector );
+}
diff --git a/gpxe/src/arch/i386/firmware/pcbios/hidemem.c b/gpxe/src/arch/i386/firmware/pcbios/hidemem.c
index 2e74d3b0..c9df7bd0 100644
--- a/gpxe/src/arch/i386/firmware/pcbios/hidemem.c
+++ b/gpxe/src/arch/i386/firmware/pcbios/hidemem.c
@@ -19,10 +19,14 @@
#include <realmode.h>
#include <biosint.h>
#include <basemem.h>
+#include <fakee820.h>
#include <gpxe/init.h>
#include <gpxe/memmap.h>
#include <gpxe/hidemem.h>
+/** Set to true if you want to test a fake E820 map */
+#define FAKE_E820 0
+
/** Alignment for hidden memory regions */
#define ALIGN_HIDDEN 4096 /* 4kB page alignment should be enough */
@@ -64,6 +68,10 @@ extern struct segoff __text16 ( int15_vector );
/* The linker defines these symbols for us */
extern char _text[];
extern char _end[];
+extern char _text16_size[];
+#define _text16_size ( ( unsigned int ) _text16_size )
+extern char _data16_size[];
+#define _data16_size ( ( unsigned int ) _data16_size )
/**
* Hide region of memory from system memory map
@@ -123,16 +131,46 @@ void hide_text ( void ) {
*/
static void hide_etherboot ( void ) {
struct memory_map memmap;
+ unsigned int rm_ds_top;
+ unsigned int rm_cs_top;
+ unsigned int fbms;
/* Dump memory map before mangling */
DBG ( "Hiding gPXE from system memory map\n" );
get_memmap ( &memmap );
+ /* Hook in fake E820 map, if we're testing one */
+ if ( FAKE_E820 ) {
+ DBG ( "Hooking in fake E820 map\n" );
+ fake_e820();
+ get_memmap ( &memmap );
+ }
+
/* Initialise the hidden regions */
hide_basemem();
hide_umalloc ( virt_to_phys ( _text ), virt_to_phys ( _text ) );
hide_text();
+ /* Some really moronic BIOSes bring up the PXE stack via the
+ * UNDI loader entry point and then don't bother to unload it
+ * before overwriting the code and data segments. If this
+ * happens, we really don't want to leave INT 15 hooked,
+ * because that will cause any loaded OS to die horribly as
+ * soon as it attempts to fetch the system memory map.
+ *
+ * We use a heuristic to guess whether or not we are being
+ * loaded sensibly.
+ */
+ rm_cs_top = ( ( ( rm_cs << 4 ) + _text16_size + 1024 - 1 ) >> 10 );
+ rm_ds_top = ( ( ( rm_ds << 4 ) + _data16_size + 1024 - 1 ) >> 10 );
+ fbms = get_fbms();
+ if ( ( rm_cs_top < fbms ) && ( rm_ds_top < fbms ) ) {
+ DBG ( "Detected potentially unsafe UNDI load at CS=%04x "
+ "DS=%04x FBMS=%dkB\n", rm_cs, rm_ds, fbms );
+ DBG ( "Disabling INT 15 memory hiding\n" );
+ return;
+ }
+
/* Hook INT 15 */
hook_bios_interrupt ( 0x15, ( unsigned int ) int15,
&int15_vector );
@@ -167,6 +205,10 @@ static void unhide_etherboot ( int flags __unused ) {
*/
unhook_bios_interrupt ( 0x15, ( unsigned int ) int15,
&int15_vector );
+
+ /* Unhook fake E820 map, if used */
+ if ( FAKE_E820 )
+ unfake_e820();
}
/** Hide Etherboot startup function */
diff --git a/gpxe/src/arch/i386/firmware/pcbios/memmap.c b/gpxe/src/arch/i386/firmware/pcbios/memmap.c
index fc0d36ac..e6d6428e 100644
--- a/gpxe/src/arch/i386/firmware/pcbios/memmap.c
+++ b/gpxe/src/arch/i386/firmware/pcbios/memmap.c
@@ -86,9 +86,20 @@ static unsigned int extmemsize_e801 ( void ) {
}
extmem = ( extmem_1m_to_16m_k + ( extmem_16m_plus_64k * 64 ) );
- DBG ( "INT 15,e801 extended memory size %d+64*%d=%d kB [100000,%x)\n",
- extmem_1m_to_16m_k, extmem_16m_plus_64k, extmem,
- ( 0x100000 + ( extmem * 1024 ) ) );
+ DBG ( "INT 15,e801 extended memory size %d+64*%d=%d kB "
+ "[100000,%llx)\n", extmem_1m_to_16m_k, extmem_16m_plus_64k,
+ extmem, ( 0x100000 + ( ( ( uint64_t ) extmem ) * 1024 ) ) );
+
+ /* Sanity check. Some BIOSes report the entire 4GB address
+ * space as available, which cannot be correct (since that
+ * would leave no address space available for 32-bit PCI
+ * BARs).
+ */
+ if ( extmem == ( 0x400000 - 0x400 ) ) {
+ DBG ( "INT 15,e801 reported whole 4GB; assuming insane\n" );
+ return 0;
+ }
+
return extmem;
}
@@ -186,6 +197,28 @@ static int meme820 ( struct memory_map *memmap ) {
}
} while ( next != 0 );
+ /* Sanity checks. Some BIOSes report complete garbage via INT
+ * 15,e820 (especially at POST time), despite passing the
+ * signature checks. We currently check for a base memory
+ * region (starting at 0) and at least one high memory region
+ * (starting at 0x100000).
+ */
+ if ( memmap->count < 2 ) {
+ DBG ( "INT 15,e820 returned only %d regions; assuming "
+ "insane\n", memmap->count );
+ return -EINVAL;
+ }
+ if ( memmap->regions[0].start != 0 ) {
+ DBG ( "INT 15,e820 region 0 starts at %llx (expected 0); "
+ "assuming insane\n", memmap->regions[0].start );
+ return -EINVAL;
+ }
+ if ( memmap->regions[1].start != 0x100000 ) {
+ DBG ( "INT 15,e820 region 1 starts at %llx (expected 100000); "
+ "assuming insane\n", memmap->regions[0].start );
+ return -EINVAL;
+ }
+
return 0;
}
diff --git a/gpxe/src/arch/i386/include/fakee820.h b/gpxe/src/arch/i386/include/fakee820.h
new file mode 100644
index 00000000..f1fe8aff
--- /dev/null
+++ b/gpxe/src/arch/i386/include/fakee820.h
@@ -0,0 +1,7 @@
+#ifndef _FAKEE820_H
+#define _FAKEE820_H
+
+extern void fake_e820 ( void );
+extern void unfake_e820 ( void );
+
+#endif /* _FAKEE820_H */
diff --git a/gpxe/src/arch/i386/interface/pxe/pxe_call.c b/gpxe/src/arch/i386/interface/pxe/pxe_call.c
index 3ccb7fb5..7122c4eb 100644
--- a/gpxe/src/arch/i386/interface/pxe/pxe_call.c
+++ b/gpxe/src/arch/i386/interface/pxe/pxe_call.c
@@ -440,6 +440,7 @@ int pxe_start_nbp ( void ) {
__asm__ __volatile__ ( REAL_CODE ( "pushw %%cx\n\t"
"pushw %%ax\n\t"
"movw %%cx, %%es\n\t"
+ "sti\n\t"
"lcall $0, $0x7c00\n\t"
"addw $4, %%sp\n\t" )
: "=a" ( rc ), "=b" ( discard_b ),
diff --git a/gpxe/src/arch/i386/prefix/romprefix.S b/gpxe/src/arch/i386/prefix/romprefix.S
index f9b9e169..872fbf56 100644
--- a/gpxe/src/arch/i386/prefix/romprefix.S
+++ b/gpxe/src/arch/i386/prefix/romprefix.S
@@ -6,6 +6,8 @@
* table so using a noticeable amount of stack space is a no-no.
*/
+#include <config/general.h>
+
#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 ) )
@@ -13,6 +15,13 @@
#define PNP_GET_BBS_VERSION 0x60
#define PMM_ALLOCATE 0x0000
+/* ROM banner timeout. Based on the configurable BANNER_TIMEOUT in
+ * config.h, but converted to a number of (18Hz) timer ticks, and
+ * doubled to allow for BIOSes that switch video modes immediately
+ * beforehand, so rendering the message almost invisible to the user.
+ */
+#define ROM_BANNER_TIMEOUT ( 2 * ( 18 * BANNER_TIMEOUT ) / 10 )
+
.text
.code16
.arch i386
@@ -145,8 +154,6 @@ init:
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
@@ -317,51 +324,23 @@ no_pmm:
movw $init_message_prompt, %si
xorw %di, %di
call print_message
- /* Empty the keyboard buffer before waiting for input */
-empty_keyboard_buffer:
- movb $0x01, %ah
- int $0x16
- jz 1f
- xorw %ax, %ax
- int $0x16
- jmp empty_keyboard_buffer
-1: /* Wait for up to 3s for a key press */
- movw $(18 * 3), %cx /* Approx 3s worth of timer ticks */
-wait_for_key:
- decw %cx
- jz no_key_pressed
- /* Wait for timer tick to be updated */
- movl %fs:(0x6c), %eax
-1: pushf
- sti
- hlt
+ /* Wait for Ctrl-B */
+ movw $0xff02, %bx
+ call wait_for_key
+ /* Clear prompt */
+ pushf
+ movw $clear_message, %si
+ xorw %di, %di
+ call print_message
popf
- cmpl %fs:(0x6c), %eax
- je 1b
- /* Check to see if a key was pressed */
- movb $0x01, %ah
- int $0x16
- jz wait_for_key
- /* Check to see if key was Ctrl-B */
- cmpb $0x02, %al
- je 1f
- /* Key was not Ctrl-B: remove from buffer and stop waiting */
- xorw %ax, %ax
- int $0x16
- jmp no_key_pressed
-1: /* Key was Ctrl-B: leave in keyboard buffer and invoke gPXE.
- * The keypress will be picked up by the initial shell
- * prompt, and we will drop into a shell.
+ jnz 1f
+ /* Ctrl-B was pressed: invoke gPXE. The keypress will be
+ * picked up by the initial shell prompt, and we will drop
+ * into a shell.
*/
pushw %cs
call exec
-no_key_pressed:
-
- /* Print blank lines to terminate messages */
- movw $init_message_end, %si
- xorw %di, %di
- call print_message
-
+1:
/* Restore registers */
popw %gs
popw %fs
@@ -395,9 +374,9 @@ init_message_int19:
init_message_prompt:
.asciz "\nPress Ctrl-B to configure gPXE..."
.size init_message_prompt, . - init_message_prompt
-init_message_end:
- .asciz "\n\n\n"
- .size init_message_end, . - init_message_end
+clear_message:
+ .asciz "\r \n\n"
+ .size clear_message, . - clear_message
/* ROM image location
*
@@ -436,24 +415,52 @@ bev_entry:
/* INT19 entry point
*
* Called via the hooked INT 19 if we detected a non-PnP BIOS. We
- * attempt to return via the original INT 19 vector (if we were able to
- * store it).
+ * attempt to return via the original INT 19 vector (if we were able
+ * to store it).
*/
int19_entry:
pushw %cs
+ popw %ds
+ /* Prompt user to press B to boot */
+ movw $int19_message_prompt, %si
+ xorw %di, %di
+ call print_message
+ movw $prodstr, %si
+ call print_message
+ movw $int19_message_dots, %si
+ call print_message
+ movw $0xdf42, %bx
+ call wait_for_key
+ pushf
+ movw $clear_message, %si
+ xorw %di, %di
+ call print_message
+ popf
+ jnz 1f
+ /* Leave keypress in buffer and start gPXE. The keypress will
+ * cause the usual initial Ctrl-B prompt to be skipped.
+ */
+ pushw %cs
call exec
+1: /* Try to call original INT 19 vector */
movl %cs:orig_int19, %eax
testl %eax, %eax
- je 1f
- /* Chain to original INT 19 vector */
+ je 2f
ljmp *%cs:orig_int19
-1: /* No chained vector: issue INT 18 as a last resort */
+2: /* No chained vector: issue INT 18 as a last resort */
int $0x18
.size int19_entry, . - int19_entry
orig_int19:
.long 0
.size orig_int19, . - orig_int19
+int19_message_prompt:
+ .asciz "Press B to boot from "
+ .size int19_message_prompt, . - int19_message_prompt
+int19_message_dots:
+ .asciz "..."
+ .size int19_message_dots, . - int19_message_dots
+
/* Execute as a boot device
*
*/
@@ -560,3 +567,67 @@ undiloader:
popl %esi
lret
.size undiloader, . - undiloader
+
+/* Wait for key press specified by %bl (masked by %bh)
+ *
+ * Used by init and INT19 code when prompting user. If the specified
+ * key is pressed, it is left in the keyboard buffer.
+ *
+ * Returns with ZF set iff specified key is pressed.
+ */
+wait_for_key:
+ /* Preserve registers */
+ pushw %cx
+ pushw %ax
+1: /* Empty the keyboard buffer before waiting for input */
+ movb $0x01, %ah
+ int $0x16
+ jz 2f
+ xorw %ax, %ax
+ int $0x16
+ jmp 1b
+2: /* Wait for a key press */
+ movw $ROM_BANNER_TIMEOUT, %cx
+3: decw %cx
+ js 99f /* Exit with ZF clear */
+ /* Wait for timer tick to be updated */
+ call wait_for_tick
+ /* Check to see if a key was pressed */
+ movb $0x01, %ah
+ int $0x16
+ jz 3b
+ /* Check to see if key was the specified key */
+ andb %bh, %al
+ cmpb %al, %bl
+ je 99f /* Exit with ZF set */
+ /* Not the specified key: remove from buffer and stop waiting */
+ pushfw
+ xorw %ax, %ax
+ int $0x16
+ popfw /* Exit with ZF clear */
+99: /* Restore registers and return */
+ popw %ax
+ popw %cx
+ ret
+ .size wait_for_key, . - wait_for_key
+
+/* Wait for timer tick
+ *
+ * Used by wait_for_key
+ */
+wait_for_tick:
+ pushl %eax
+ pushw %fs
+ movw $0x40, %ax
+ movw %ax, %fs
+ movl %fs:(0x6c), %eax
+1: pushf
+ sti
+ hlt
+ popf
+ cmpl %fs:(0x6c), %eax
+ je 1b
+ popw %fs
+ popl %eax
+ ret
+ .size wait_for_tick, . - wait_for_tick
diff --git a/gpxe/src/core/settings.c b/gpxe/src/core/settings.c
index e660ae7c..a1299ee2 100644
--- a/gpxe/src/core/settings.c
+++ b/gpxe/src/core/settings.c
@@ -28,6 +28,7 @@
#include <gpxe/vsprintf.h>
#include <gpxe/dhcp.h>
#include <gpxe/uuid.h>
+#include <gpxe/uri.h>
#include <gpxe/settings.h>
/** @file
@@ -746,6 +747,58 @@ struct setting_type setting_type_string __setting_type = {
};
/**
+ * Parse and store value of URI-encoded string setting
+ *
+ * @v settings Settings block
+ * @v setting Setting to store
+ * @v value Formatted setting data
+ * @ret rc Return status code
+ */
+static int storef_uristring ( struct settings *settings,
+ struct setting *setting,
+ const char *value ) {
+ char buf[ strlen ( value ) + 1 ]; /* Decoding never expands string */
+ size_t len;
+
+ len = uri_decode ( value, buf, sizeof ( buf ) );
+ return store_setting ( settings, setting, buf, len );
+}
+
+/**
+ * Fetch and format value of URI-encoded string setting
+ *
+ * @v settings Settings block, or NULL to search all blocks
+ * @v setting Setting to fetch
+ * @v buf Buffer to contain formatted value
+ * @v len Length of buffer
+ * @ret len Length of formatted value, or negative error
+ */
+static int fetchf_uristring ( struct settings *settings,
+ struct setting *setting,
+ char *buf, size_t len ) {
+ size_t raw_len;
+
+ /* We need to always retrieve the full raw string to know the
+ * length of the encoded string.
+ */
+ raw_len = fetch_setting ( settings, setting, NULL, 0 );
+ {
+ char raw_buf[ raw_len + 1 ];
+
+ fetch_string_setting ( settings, setting, raw_buf,
+ sizeof ( raw_buf ) );
+ return uri_encode ( raw_buf, buf, len );
+ }
+}
+
+/** A URI-encoded string setting type */
+struct setting_type setting_type_uristring __setting_type = {
+ .name = "uristring",
+ .storef = storef_uristring,
+ .fetchf = fetchf_uristring,
+};
+
+/**
* Parse and store value of IPv4 address setting
*
* @v settings Settings block
diff --git a/gpxe/src/core/uri.c b/gpxe/src/core/uri.c
index 3b3cf85b..cf2b071d 100644
--- a/gpxe/src/core/uri.c
+++ b/gpxe/src/core/uri.c
@@ -26,6 +26,7 @@
#include <stdlib.h>
#include <string.h>
#include <libgen.h>
+#include <ctype.h>
#include <gpxe/vsprintf.h>
#include <gpxe/uri.h>
@@ -381,3 +382,80 @@ struct uri * resolve_uri ( struct uri *base_uri,
free ( tmp_path );
return new_uri;
}
+
+/**
+ * Test for unreserved URI characters
+ *
+ * @v c Character to test
+ * @ret is_unreserved Character is an unreserved character
+ */
+static int is_unreserved_uri_char ( int c ) {
+ /* According to RFC3986, the unreserved character set is
+ *
+ * A-Z a-z 0-9 - _ . ~
+ */
+ return ( isupper ( c ) || islower ( c ) || isdigit ( c ) ||
+ ( c == '-' ) || ( c == '_' ) ||
+ ( c == '.' ) || ( c == '~' ) );
+}
+
+/**
+ * URI-encode string
+ *
+ * @v raw_string String to be URI-encoded
+ * @v buf Buffer to contain encoded string
+ * @v len Length of buffer
+ * @ret len Length of encoded string (excluding NUL)
+ */
+size_t uri_encode ( const char *raw_string, char *buf, size_t len ) {
+ ssize_t remaining = len;
+ size_t used;
+ unsigned char c;
+
+ if ( len )
+ buf[0] = '\0';
+
+ while ( ( c = *(raw_string++) ) ) {
+ if ( is_unreserved_uri_char ( c ) ) {
+ used = ssnprintf ( buf, remaining, "%c", c );
+ } else {
+ used = ssnprintf ( buf, remaining, "%%%02X", c );
+ }
+ buf += used;
+ remaining -= used;
+ }
+
+ return ( len - remaining );
+}
+
+/**
+ * Decode URI-encoded string
+ *
+ * @v encoded_string URI-encoded string
+ * @v buf Buffer to contain decoded string
+ * @v len Length of buffer
+ * @ret len Length of decoded string (excluding NUL)
+ */
+size_t uri_decode ( const char *encoded_string, char *buf, size_t len ) {
+ ssize_t remaining = len;
+ char hexbuf[3];
+ char *hexbuf_end;
+ unsigned char c;
+
+ if ( len )
+ buf[0] = '\0';
+
+ while ( *encoded_string ) {
+ if ( *encoded_string == '%' ) {
+ encoded_string++;
+ snprintf ( hexbuf, sizeof ( hexbuf ), "%s",
+ encoded_string );
+ c = strtoul ( hexbuf, &hexbuf_end, 16 );
+ encoded_string += ( hexbuf_end - hexbuf );
+ } else {
+ c = *(encoded_string++);
+ }
+ ssnprintf ( buf++, remaining--, "%c", c );
+ }
+ return ( len - remaining );
+}
diff --git a/gpxe/src/crypto/axtls/bigint.h b/gpxe/src/crypto/axtls/bigint.h
index 5a13c5ae..f9a3c70b 100644
--- a/gpxe/src/crypto/axtls/bigint.h
+++ b/gpxe/src/crypto/axtls/bigint.h
@@ -19,8 +19,6 @@
#ifndef BIGINT_HEADER
#define BIGINT_HEADER
-#include "config.h"
-
/* enable features based on a 'super-set' capbaility. */
#if defined(CONFIG_SSL_FULL_MODE)
#define CONFIG_SSL_ENABLE_CLIENT
diff --git a/gpxe/src/drivers/infiniband/arbel.c b/gpxe/src/drivers/infiniband/arbel.c
index 2aced777..0c180833 100644
--- a/gpxe/src/drivers/infiniband/arbel.c
+++ b/gpxe/src/drivers/infiniband/arbel.c
@@ -1714,7 +1714,7 @@ static int arbel_start_firmware ( struct arbel *arbel ) {
/* Allocate firmware pages and map firmware area */
fw_size = ( fw_pages * 4096 );
- arbel->firmware_area = umalloc ( fw_size );
+ arbel->firmware_area = umalloc ( fw_size * 2 );
if ( ! arbel->firmware_area ) {
rc = -ENOMEM;
goto err_alloc_fa;
diff --git a/gpxe/src/drivers/infiniband/hermon.c b/gpxe/src/drivers/infiniband/hermon.c
index 439974eb..9716aba9 100644
--- a/gpxe/src/drivers/infiniband/hermon.c
+++ b/gpxe/src/drivers/infiniband/hermon.c
@@ -1684,7 +1684,7 @@ static int hermon_start_firmware ( struct hermon *hermon ) {
/* Allocate firmware pages and map firmware area */
fw_size = ( fw_pages * HERMON_PAGE_SIZE );
- hermon->firmware_area = umalloc ( fw_size );
+ hermon->firmware_area = umalloc ( fw_size * 2 );
if ( ! hermon->firmware_area ) {
rc = -ENOMEM;
goto err_alloc_fa;
diff --git a/gpxe/src/drivers/net/phantom/phantom.c b/gpxe/src/drivers/net/phantom/phantom.c
index 5644c96d..6c7d1fc9 100644
--- a/gpxe/src/drivers/net/phantom/phantom.c
+++ b/gpxe/src/drivers/net/phantom/phantom.c
@@ -1673,6 +1673,17 @@ static int phantom_init_cmdpeg ( struct phantom_nic *phantom ) {
uint32_t cmdpeg_state;
uint32_t last_cmdpeg_state = 0;
+ /* Check for a previous initialisation. This could have
+ * happened if, for example, the BIOS used the UNDI API to
+ * drive the NIC prior to a full PXE boot.
+ */
+ cmdpeg_state = phantom_readl ( phantom, UNM_NIC_REG_CMDPEG_STATE );
+ if ( cmdpeg_state == UNM_NIC_REG_CMDPEG_STATE_INITIALIZE_ACK ) {
+ DBGC ( phantom, "Phantom %p command PEG already initialized\n",
+ phantom );
+ return 0;
+ }
+
/* If this was a cold boot, check that the hardware came up ok */
cold_boot = phantom_readl ( phantom, UNM_CAM_RAM_COLD_BOOT );
if ( cold_boot == UNM_CAM_RAM_COLD_BOOT_MAGIC ) {
@@ -1692,8 +1703,6 @@ static int phantom_init_cmdpeg ( struct phantom_nic *phantom ) {
phantom_writel ( phantom, 0, UNM_CAM_RAM_COLD_BOOT );
/* Set port modes */
- phantom_writel ( phantom, UNM_CAM_RAM_PORT_MODE_AUTO_NEG,
- UNM_CAM_RAM_PORT_MODE );
phantom_writel ( phantom, UNM_CAM_RAM_PORT_MODE_AUTO_NEG_1G,
UNM_CAM_RAM_WOL_PORT_MODE );
diff --git a/gpxe/src/include/gpxe/dhcp.h b/gpxe/src/include/gpxe/dhcp.h
index c5ed0ead..e89503d3 100644
--- a/gpxe/src/include/gpxe/dhcp.h
+++ b/gpxe/src/include/gpxe/dhcp.h
@@ -102,6 +102,7 @@ struct dhcp_packet;
/** DHCP message type */
#define DHCP_MESSAGE_TYPE 53
+#define DHCPNONE 0
#define DHCPDISCOVER 1
#define DHCPOFFER 2
#define DHCPREQUEST 3
diff --git a/gpxe/src/include/gpxe/uri.h b/gpxe/src/include/gpxe/uri.h
index 514bc479..37f3aac9 100644
--- a/gpxe/src/include/gpxe/uri.h
+++ b/gpxe/src/include/gpxe/uri.h
@@ -135,5 +135,7 @@ extern char * resolve_path ( const char *base_path,
extern struct uri * resolve_uri ( struct uri *base_uri,
struct uri *relative_uri );
extern void churi ( struct uri *uri );
+extern size_t uri_encode ( const char *raw_string, char *buf, size_t len );
+extern size_t uri_decode ( const char *encoded_string, char *buf, size_t len );
#endif /* _GPXE_URI_H */
diff --git a/gpxe/src/interface/pxe/pxe_loader.c b/gpxe/src/interface/pxe/pxe_loader.c
index f815bc25..d228a36d 100644
--- a/gpxe/src/interface/pxe/pxe_loader.c
+++ b/gpxe/src/interface/pxe/pxe_loader.c
@@ -31,12 +31,12 @@
*/
PXENV_EXIT_t undi_loader ( struct s_UNDI_LOADER *undi_loader ) {
- DBG ( "[PXENV_UNDI_LOADER to CS %04x DS %04x]",
- undi_loader->UNDI_CS, undi_loader->UNDI_DS );
-
/* Perform one-time initialisation (e.g. heap) */
initialise();
+ DBG ( "[PXENV_UNDI_LOADER to CS %04x DS %04x]",
+ undi_loader->UNDI_CS, undi_loader->UNDI_DS );
+
/* Set up PXE data structures */
pxe_init_structures();
diff --git a/gpxe/src/net/tcp/iscsi.c b/gpxe/src/net/tcp/iscsi.c
index a67415b6..01a46584 100644
--- a/gpxe/src/net/tcp/iscsi.c
+++ b/gpxe/src/net/tcp/iscsi.c
@@ -1886,7 +1886,7 @@ static struct iscsi_string_setting iscsi_string_settings[] = {
{
.setting = &hostname_setting,
.string = &iscsi_default_initiator_iqn,
- .prefix = "iqn.2000-09.org.etherboot:",
+ .prefix = "iqn.2000-01.org.etherboot:",
},
};
diff --git a/gpxe/src/net/udp/dhcp.c b/gpxe/src/net/udp/dhcp.c
index 5fcd56ea..ab751cd5 100644
--- a/gpxe/src/net/udp/dhcp.c
+++ b/gpxe/src/net/udp/dhcp.c
@@ -65,7 +65,8 @@ static const uint8_t dhcp_op[] = {
/** Raw option data for options common to all DHCP requests */
static uint8_t dhcp_request_options_data[] = {
- DHCP_MAX_MESSAGE_SIZE, DHCP_WORD ( ETH_MAX_MTU ),
+ DHCP_MAX_MESSAGE_SIZE,
+ DHCP_WORD ( ETH_MAX_MTU - 20 /* IP header */ - 8 /* UDP header */ ),
DHCP_CLIENT_ARCHITECTURE, DHCP_WORD ( 0 ),
DHCP_CLIENT_NDI, DHCP_OPTION ( 1 /* UNDI */ , 2, 1 /* v2.1 */ ),
DHCP_VENDOR_CLASS_ID,
@@ -128,7 +129,7 @@ struct dhcp_client_uuid {
*/
static inline const char * dhcp_msgtype_name ( unsigned int msgtype ) {
switch ( msgtype ) {
- case 0: return "BOOTP"; /* Non-DHCP packet */
+ case DHCPNONE: return "BOOTP"; /* Non-DHCP packet */
case DHCPDISCOVER: return "DHCPDISCOVER";
case DHCPOFFER: return "DHCPOFFER";
case DHCPREQUEST: return "DHCPREQUEST";
@@ -685,7 +686,7 @@ static void dhcp_store_dhcpoffer ( struct dhcp_session *dhcp,
*/
static void dhcp_rx_dhcpoffer ( struct dhcp_session *dhcp,
struct dhcp_settings *dhcpoffer ) {
- struct in_addr server_id;
+ struct in_addr server_id = { 0 };
char vci[9]; /* "PXEClient" */
int len;
uint8_t ignore_proxy = 0;
@@ -697,7 +698,7 @@ static void dhcp_rx_dhcpoffer ( struct dhcp_session *dhcp,
!= sizeof ( server_id ) ) {
DBGC ( dhcp, "DHCP %p received DHCPOFFER %p missing server "
"identifier\n", dhcp, dhcpoffer );
- return;
+ /* Could be a valid BOOTP offer; do not abort processing */
}
/* If there is an IP address, it's a normal DHCPOFFER */
@@ -714,7 +715,8 @@ static void dhcp_rx_dhcpoffer ( struct dhcp_session *dhcp,
*/
len = dhcppkt_fetch ( &dhcpoffer->dhcppkt, DHCP_VENDOR_CLASS_ID,
vci, sizeof ( vci ) );
- if ( ( len >= ( int ) sizeof ( vci ) ) &&
+ if ( ( server_id.s_addr != 0 ) &&
+ ( len >= ( int ) sizeof ( vci ) ) &&
( strncmp ( "PXEClient", vci, sizeof ( vci ) ) == 0 ) ) {
DBGC ( dhcp, "DHCP %p received DHCPOFFER %p from %s is a "
"ProxyDHCPOFFER\n",
@@ -924,12 +926,12 @@ static int dhcp_deliver_iob ( struct xfer_interface *xfer,
/* Handle packet based on current state */
switch ( dhcp->state ) {
case DHCP_STATE_DISCOVER:
- if ( ( msgtype == DHCPOFFER ) &&
+ if ( ( ( msgtype == DHCPOFFER ) || ( msgtype == DHCPNONE ) ) &&
( src_port == htons ( BOOTPS_PORT ) ) )
dhcp_rx_dhcpoffer ( dhcp, dhcpset );
break;
case DHCP_STATE_REQUEST:
- if ( ( msgtype == DHCPACK ) &&
+ if ( ( ( msgtype == DHCPACK ) || ( msgtype == DHCPNONE ) ) &&
( src_port == htons ( BOOTPS_PORT ) ) )
dhcp_rx_dhcpack ( dhcp, dhcpset );
break;