summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/Makefile.housekeeping2
-rw-r--r--src/arch/i386/Makefile.pcbios2
-rw-r--r--src/arch/i386/prefix/romprefix.S387
-rw-r--r--src/arch/i386/prefix/xromprefix.S9
-rw-r--r--src/arch/i386/scripts/i386.lds1
-rwxr-xr-xsrc/util/makerom.pl30
6 files changed, 409 insertions, 22 deletions
diff --git a/src/Makefile.housekeeping b/src/Makefile.housekeeping
index 1642374c..1f5e115f 100644
--- a/src/Makefile.housekeeping
+++ b/src/Makefile.housekeeping
@@ -831,6 +831,8 @@ endif # defined(BIN)
FINALISE_rom = $(MAKEROM) $(MAKEROM_FLAGS) $(TGT_MAKEROM_FLAGS) \
-i$(IDENT) -s 0 $@
FINALISE_hrom = $(FINALISE_rom)
+FINALISE_xrom = $(MAKEROM) $(MAKEROM_FLAGS) $(TGT_MAKEROM_FLAGS) \
+ -i$(IDENT) -n -s 0 $@
# Some ROMs require specific flags to be passed to makerom.pl
#
diff --git a/src/arch/i386/Makefile.pcbios b/src/arch/i386/Makefile.pcbios
index 55ba11d7..8b010859 100644
--- a/src/arch/i386/Makefile.pcbios
+++ b/src/arch/i386/Makefile.pcbios
@@ -12,6 +12,7 @@ LDFLAGS += -N --no-check-sections
#
MEDIA += rom
MEDIA += hrom
+MEDIA += xrom
MEDIA += pxe
MEDIA += kpxe
MEDIA += kkpxe
@@ -32,6 +33,7 @@ MEDIA += exe
#
PAD_rom = $(PADIMG) --blksize=512 --byte=0xff $@
PAD_hrom = $(PAD_rom)
+PAD_xrom = $(PAD_rom)
PAD_dsk = $(PADIMG) --blksize=512 $@
PAD_hd = $(PADIMG) --blksize=32768 $@
diff --git a/src/arch/i386/prefix/romprefix.S b/src/arch/i386/prefix/romprefix.S
index 952eccdd..02e54976 100644
--- a/src/arch/i386/prefix/romprefix.S
+++ b/src/arch/i386/prefix/romprefix.S
@@ -25,6 +25,19 @@ FILE_LICENCE ( GPL2_OR_LATER )
*/
#define ROM_BANNER_TIMEOUT ( 2 * ( 18 * BANNER_TIMEOUT ) / 10 )
+/* We can load a ROM in two ways: have the BIOS load all of it (.rom prefix)
+ * or have the BIOS load a stub that loads the rest using PCI (.xrom prefix).
+ * The latter is not as widely supported, but allows the use of large ROMs
+ * on some systems with crowded option ROM space.
+ */
+
+#ifdef LOAD_ROM_FROM_PCI
+#define ROM_SIZE_VALUE _prefix_filesz_sect /* Amount to load in BIOS */
+#else
+#define ROM_SIZE_VALUE 0 /* Load amount (before compr. fixup) */
+#endif
+
+
.text
.code16
.arch i386
@@ -33,10 +46,12 @@ FILE_LICENCE ( GPL2_OR_LATER )
.org 0x00
romheader:
.word 0xAA55 /* BIOS extension signature */
-romheader_size: .byte 0 /* Size in 512-byte blocks */
+romheader_size: .byte ROM_SIZE_VALUE /* Size in 512-byte blocks */
jmp init /* Initialisation vector */
checksum:
- .byte 0
+ .byte 0, 0
+real_size:
+ .word 0
.org 0x16
.word undiheader
.org 0x18
@@ -44,12 +59,18 @@ checksum:
.org 0x1a
.word pnpheader
.size romheader, . - romheader
-
+
.section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
+#ifndef LOAD_ROM_FROM_PCI
.ascii "ADDB"
.long romheader_size
.long 512
.long 0
+#endif
+ .ascii "ADDB"
+ .long real_size
+ .long 512
+ .long 0
.previous
pciheader:
@@ -61,17 +82,18 @@ pciheader:
.byte 0x03 /* PCI data structure revision */
.byte 0x02, 0x00, 0x00 /* Class code */
pciheader_image_length:
- .word 0 /* Image length */
+ .word ROM_SIZE_VALUE /* Image length */
.word 0x0001 /* Revision level */
.byte 0x00 /* Code type */
.byte 0x80 /* Last image indicator */
pciheader_runtime_length:
- .word 0 /* Maximum run-time image length */
+ .word ROM_SIZE_VALUE /* Maximum run-time image length */
.word 0x0000 /* Configuration utility code header */
.word 0x0000 /* DMTF CLP entry point */
.equ pciheader_len, . - pciheader
.size pciheader, . - pciheader
-
+
+#ifndef LOAD_ROM_FROM_PCI
.section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
.ascii "ADDW"
.long pciheader_image_length
@@ -82,6 +104,7 @@ pciheader_runtime_length:
.long 512
.long 0
.previous
+#endif
pnpheader:
.ascii "$PnP" /* Signature */
@@ -175,6 +198,11 @@ init:
call print_message
call print_pci_busdevfn
+#ifdef LOAD_ROM_FROM_PCI
+ /* Save PCI bus:dev.fn for later use */
+ movw %ax, pci_busdevfn
+#endif
+
/* Fill in product name string, if possible */
movw $prodstr_pci_id, %di
call print_pci_busdevfn
@@ -199,6 +227,9 @@ init:
jne no_pci3
testb %ah, %ah
jnz no_pci3
+#ifdef LOAD_ROM_FROM_PCI
+ incb pcibios_present
+#endif
movw $init_message_pci, %si
xorw %di, %di
call print_message
@@ -310,7 +341,7 @@ pmm_scan:
/* We have PMM and so a 1kB stack: preserve upper register halves */
pushal
/* Calculate required allocation size in %esi */
- movzbl romheader_size, %eax
+ movzwl real_size, %eax
shll $9, %eax
addl $_textdata_memsz, %eax
orw $0xffff, %ax /* Ensure allocation size is at least 64kB */
@@ -364,7 +395,7 @@ pmm_copy:
movl %edi, decompress_to
/* Shrink ROM */
movb $_prefix_memsz_sect, romheader_size
-#ifdef SHRINK_WITHOUT_PMM
+#if defined(SHRINK_WITHOUT_PMM) || defined(LOAD_ROM_FROM_PCI)
jmp pmm_done
pmm_fail:
/* Print marker and copy ourselves to high memory */
@@ -379,8 +410,28 @@ pmm_fail:
#endif
/* Restore upper register halves */
popal
+#if defined(LOAD_ROM_FROM_PCI)
+ call load_from_pci
+ jc load_err
+ jmp load_ok
no_pmm:
+ /* Cannot continue without PMM - print error message */
+ xorw %di, %di
+ movw $init_message_no_pmm, %si
+ call print_message
+load_err:
+ /* Wait for five seconds to let user see message */
+ movw $90, %cx
+1: call wait_for_tick
+ loop 1b
+ /* Mark environment as invalid and return */
+ movl $0, decompress_to
+ jmp out
+load_ok:
+#else
+no_pmm:
+#endif
/* Update checksum */
xorw %bx, %bx
xorw %si, %si
@@ -425,14 +476,14 @@ no_pmm:
movw $init_message_done, %si
call print_message
popf
- jnz 2f
+ jnz out
/* 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
-2:
+out:
/* Restore registers */
popw %gs
popw %fs
@@ -479,6 +530,11 @@ init_message_bbs:
init_message_pmm:
.asciz " PMM"
.size init_message_pmm, . - init_message_pmm
+#ifdef LOAD_ROM_FROM_PCI
+init_message_no_pmm:
+ .asciz "\nPMM required but not present!\n"
+ .size init_message_no_pmm, . - init_message_no_pmm
+#endif
init_message_int19:
.asciz " INT19"
.size init_message_int19, . - init_message_int19
@@ -504,12 +560,32 @@ image_source:
/* Temporary decompression area
*
* May be either at HIGHMEM_LOADPOINT, or within PMM-allocated block.
+ * If a PCI ROM load fails, this will be set to zero.
*/
.globl decompress_to
decompress_to:
.long HIGHMEM_LOADPOINT
.size decompress_to, . - decompress_to
+#ifdef LOAD_ROM_FROM_PCI
+
+/* Set if the PCI BIOS is present, even <3.0 */
+pcibios_present:
+ .byte 0
+ .byte 0 /* for alignment */
+ .size pcibios_present, . - pcibios_present
+
+/* PCI bus:device.function word
+ *
+ * Filled in by init in the .xrom case, so the remainder of the ROM
+ * can be located.
+ */
+pci_busdevfn:
+ .word 0
+ .size pci_busdevfn, . - pci_busdevfn
+
+#endif
+
/* BBS version
*
* Filled in by BBS BIOS. We ignore the value.
@@ -528,6 +604,289 @@ bev_entry:
lret
.size bev_entry, . - bev_entry
+
+#ifdef LOAD_ROM_FROM_PCI
+
+#define PCI_ROM_ADDRESS 0x30 /* Bits 31:11 address, 10:1 reserved */
+#define PCI_ROM_ADDRESS_ENABLE 0x00000001
+#define PCI_ROM_ADDRESS_MASK 0xfffff800
+
+#define PCIBIOS_READ_WORD 0xb109
+#define PCIBIOS_READ_DWORD 0xb10a
+#define PCIBIOS_WRITE_WORD 0xb10c
+#define PCIBIOS_WRITE_DWORD 0xb10d
+
+/* Determine size of PCI BAR
+ *
+ * %bx : PCI bus:dev.fn to probe
+ * %di : Address of BAR to find size of
+ * %edx : Mask of address bits within BAR
+ *
+ * %ecx : Size for a memory resource,
+ * 1 for an I/O resource (bit 0 set).
+ * CF : Set on error or nonexistent device (all-ones read)
+ *
+ * All other registers saved.
+ */
+pci_bar_size:
+ /* Save registers */
+ pushw %ax
+ pushl %esi
+ pushl %edx
+
+ /* Read current BAR value */
+ movw $PCIBIOS_READ_DWORD, %ax
+ int $0x1a
+
+ /* Check for device existence and save it */
+ testb $1, %cl /* I/O bit? */
+ jz 1f
+ andl $1, %ecx /* If so, exit with %ecx = 1 */
+ jmp 99f
+1: notl %ecx
+ testl %ecx, %ecx /* Set ZF iff %ecx was all-ones */
+ notl %ecx
+ jnz 1f
+ stc /* All ones - exit with CF set */
+ jmp 99f
+1: movl %ecx, %esi /* Save in %esi */
+
+ /* Write all ones to BAR */
+ movl %edx, %ecx
+ movw $PCIBIOS_WRITE_DWORD, %ax
+ int $0x1a
+
+ /* Read back BAR */
+ movw $PCIBIOS_READ_DWORD, %ax
+ int $0x1a
+
+ /* Find decode size from least set bit in mask BAR */
+ bsfl %ecx, %ecx /* Find least set bit, log2(decode size) */
+ jz 1f /* Mask BAR should not be zero */
+ xorl %edx, %edx
+ incl %edx
+ shll %cl, %edx /* %edx = decode size */
+ jmp 2f
+1: xorl %edx, %edx /* Return zero size for mask BAR zero */
+
+ /* Restore old BAR value */
+2: movl %esi, %ecx
+ movw $PCIBIOS_WRITE_DWORD, %ax
+ int $0x1a
+
+ movl %edx, %ecx /* Return size in %ecx */
+
+ /* Restore registers and return */
+99: popl %edx
+ popl %esi
+ popw %ax
+ ret
+
+ .size pci_bar_size, . - pci_bar_size
+
+/* PCI ROM loader
+ *
+ * Called from init in the .xrom case to load the non-prefix code
+ * using the PCI ROM BAR.
+ *
+ * Returns with carry flag set on error. All registers saved.
+ */
+load_from_pci:
+ /*
+ * Use PCI BIOS access to config space. The calls take
+ *
+ * %ah : 0xb1 %al : function
+ * %bx : bus/dev/fn
+ * %di : config space address
+ * %ecx : value to write (for writes)
+ *
+ * %ecx : value read (for reads)
+ * %ah : return code
+ * CF : error indication
+ *
+ * All registers not used for return are preserved.
+ */
+
+ /* Save registers and set up %es for big real mode */
+ pushal
+ pushw %es
+ xorw %ax, %ax
+ movw %ax, %es
+
+ /* Check PCI BIOS presence */
+ cmpb $0, pcibios_present
+ jz err_pcibios
+
+ /* Load existing PCI ROM BAR */
+ movw $PCIBIOS_READ_DWORD, %ax
+ movw pci_busdevfn, %bx
+ movw $PCI_ROM_ADDRESS, %di
+ int $0x1a
+
+ /* Maybe it's already enabled? */
+ testb $PCI_ROM_ADDRESS_ENABLE, %cl
+ jz 1f
+ movb $1, %dl /* Flag indicating no deinit required */
+ movl %ecx, %ebp
+ jmp check_rom
+
+ /* Determine PCI BAR decode size */
+1: movl $PCI_ROM_ADDRESS_MASK, %edx
+ call pci_bar_size /* Returns decode size in %ecx */
+ jc err_size_insane /* CF => no ROM BAR, %ecx == ffffffff */
+
+ /* Check sanity of decode size */
+ xorl %eax, %eax
+ movw real_size, %ax
+ shll $9, %eax /* %eax = ROM size */
+ cmpl %ecx, %eax
+ ja err_size_insane /* Insane if decode size < ROM size */
+ cmpl $0x100000, %ecx
+ jae err_size_insane /* Insane if decode size >= 1MB */
+
+ /* Find a place to map the BAR
+ * In theory we should examine e820 and all PCI BARs to find a
+ * free region. However, we run at POST when e820 may not be
+ * available, and memory reads of an unmapped location are
+ * de facto standardized to return all-ones. Thus, we can get
+ * away with searching high memory (0xf0000000 and up) on
+ * multiples of the ROM BAR decode size for a sufficiently
+ * large all-ones region.
+ */
+ movl %ecx, %edx /* Save ROM BAR size in %edx */
+ movl $0xf0000000, %ebp
+ xorl %eax, %eax
+ notl %eax /* %eax = all ones */
+bar_search:
+ movl %ebp, %edi
+ movl %edx, %ecx
+ shrl $2, %ecx
+ addr32 repe scasl /* Scan %es:edi for anything not all-ones */
+ jz bar_found
+ addl %edx, %ebp
+ testl $0x80000000, %ebp
+ jz err_no_bar
+ jmp bar_search
+
+bar_found:
+ movl %edi, %ebp
+ /* Save current BAR value on stack to restore later */
+ movw $PCIBIOS_READ_DWORD, %ax
+ movw $PCI_ROM_ADDRESS, %di
+ int $0x1a
+ pushl %ecx
+
+ /* Map the ROM */
+ movw $PCIBIOS_WRITE_DWORD, %ax
+ movl %ebp, %ecx
+ orb $PCI_ROM_ADDRESS_ENABLE, %cl
+ int $0x1a
+
+ xorb %dl, %dl /* %dl = 0 : ROM was not already mapped */
+check_rom:
+ /* Check and copy ROM - enter with %dl set to skip unmapping,
+ * %ebp set to mapped ROM BAR address.
+ * We check up to prodstr_separator for equality, since anything past
+ * that may have been modified. Since our check includes the checksum
+ * byte over the whole ROM stub, that should be sufficient.
+ */
+ xorb %dh, %dh /* %dh = 0 : ROM did not fail integrity check */
+
+ /* Verify ROM integrity */
+ xorl %esi, %esi
+ movl %ebp, %edi
+ movl $prodstr_separator, %ecx
+ addr32 repe cmpsb
+ jz copy_rom
+ incb %dh /* ROM failed integrity check */
+ movl %ecx, %ebp /* Save number of bytes left */
+ jmp skip_load
+
+copy_rom:
+ /* Print BAR address and indicate whether we mapped it ourselves */
+ movb $( ' ' ), %al
+ xorw %di, %di
+ call print_character
+ movl %ebp, %eax
+ call print_hex_dword
+ movb $( '-' ), %al /* '-' for self-mapped */
+ subb %dl, %al
+ subb %dl, %al /* '+' = '-' - 2 for BIOS-mapped */
+ call print_character
+
+ /* Copy ROM at %ebp to PMM or highmem block */
+ movl %ebp, %esi
+ movl image_source, %edi
+ movzwl real_size, %ecx
+ shll $9, %ecx
+ addr32 es rep movsb
+ movl %edi, decompress_to
+skip_load:
+ testb %dl, %dl /* Was ROM already mapped? */
+ jnz skip_unmap
+
+ /* Unmap the ROM by restoring old ROM BAR */
+ movw $PCIBIOS_WRITE_DWORD, %ax
+ movw $PCI_ROM_ADDRESS, %di
+ popl %ecx
+ int $0x1a
+
+skip_unmap:
+ /* Error handling */
+ testb %dh, %dh
+ jnz err_rom_invalid
+ clc
+ jmp 99f
+
+err_pcibios: /* No PCI BIOS available */
+ movw $load_message_no_pcibios, %si
+ xorl %eax, %eax /* "error code" is zero */
+ jmp 1f
+err_size_insane: /* BAR has size (%ecx) that is insane */
+ movw $load_message_size_insane, %si
+ movl %ecx, %eax
+ jmp 1f
+err_no_bar: /* No space of sufficient size (%edx) found */
+ movw $load_message_no_bar, %si
+ movl %edx, %eax
+ jmp 1f
+err_rom_invalid: /* Loaded ROM does not match (%ebp bytes left) */
+ movw $load_message_rom_invalid, %si
+ movzbl romheader_size, %eax
+ shll $9, %eax
+ subl %ebp, %eax
+ decl %eax /* %eax is now byte index of failure */
+
+1: /* Error handler - print message at %si and dword in %eax */
+ xorw %di, %di
+ call print_message
+ call print_hex_dword
+ stc
+99: popw %es
+ popal
+ ret
+
+ .size load_from_pci, . - load_from_pci
+
+load_message_no_pcibios:
+ .asciz "\nNo PCI BIOS found! "
+ .size load_message_no_pcibios, . - load_message_no_pcibios
+
+load_message_size_insane:
+ .asciz "\nROM resource has invalid size "
+ .size load_message_size_insane, . - load_message_size_insane
+
+load_message_no_bar:
+ .asciz "\nNo memory hole of sufficient size "
+ .size load_message_no_bar, . - load_message_no_bar
+
+load_message_rom_invalid:
+ .asciz "\nLoaded ROM is invalid at "
+ .size load_message_rom_invalid, . - load_message_rom_invalid
+
+#endif /* LOAD_ROM_FROM_PCI */
+
+
/* INT19 entry point
*
* Called via the hooked INT 19 if we detected a non-PnP BIOS. We
@@ -588,6 +947,14 @@ exec: /* Set %ds = %cs */
pushw %cs
popw %ds
+#ifdef LOAD_ROM_FROM_PCI
+ /* Don't execute if load was invalid */
+ cmpl $0, decompress_to
+ jne 1f
+ lret
+1:
+#endif
+
/* Print message as soon as possible */
movw $prodstr, %si
xorw %di, %di
diff --git a/src/arch/i386/prefix/xromprefix.S b/src/arch/i386/prefix/xromprefix.S
new file mode 100644
index 00000000..d7c861f5
--- /dev/null
+++ b/src/arch/i386/prefix/xromprefix.S
@@ -0,0 +1,9 @@
+/*
+ * ROM prefix that loads the bulk of the ROM using direct PCI accesses,
+ * so as not to take up much option ROM space on PCI <3.0 systems.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER )
+
+#define LOAD_ROM_FROM_PCI
+#include "romprefix.S"
diff --git a/src/arch/i386/scripts/i386.lds b/src/arch/i386/scripts/i386.lds
index 77e8c7e7..33c75f90 100644
--- a/src/arch/i386/scripts/i386.lds
+++ b/src/arch/i386/scripts/i386.lds
@@ -194,6 +194,7 @@ SECTIONS {
* Values calculated to save code from doing it
*
*/
+ _prefix_filesz_sect = ( ( _prefix_filesz + 511 ) / 512 );
_prefix_memsz_pgh = ( ( _prefix_memsz + 15 ) / 16 );
_prefix_memsz_sect = ( ( _prefix_memsz + 511 ) / 512 );
_text16_memsz_pgh = ( ( _text16_memsz + 15 ) / 16 );
diff --git a/src/util/makerom.pl b/src/util/makerom.pl
index aed3a569..68c3be98 100755
--- a/src/util/makerom.pl
+++ b/src/util/makerom.pl
@@ -130,14 +130,14 @@ sub writerom ($$) {
close(R);
}
-sub checksum ($) {
- my ($romref) = @_;
+sub checksum ($$) {
+ my ($romref, $romsize) = @_;
substr($$romref, 6, 1) = "\x00";
- my $sum = unpack('%8C*', $$romref);
+ my $sum = unpack('%8C*', substr($$romref, 0, $romsize));
substr($$romref, 6, 1) = chr(256 - $sum);
# Double check
- $sum = unpack('%8C*', $$romref);
+ $sum = unpack('%8C*', substr($$romref, 0, $romsize));
if ($sum != 0) {
print "Checksum fails\n"
} elsif ($opts{'v'}) {
@@ -146,10 +146,10 @@ sub checksum ($) {
}
sub makerom () {
- my ($rom, $romsize);
+ my ($rom, $romsize, $stubsize);
- getopts('3xi:p:s:v', \%opts);
- $ARGV[0] or die "Usage: $0 [-s romsize] [-i ident] [-p vendorid,deviceid] [-x] [-3] rom-file\n";
+ getopts('3xni:p:s:v', \%opts);
+ $ARGV[0] or die "Usage: $0 [-s romsize] [-i ident] [-p vendorid,deviceid] [-n] [-x] [-3] rom-file\n";
open(R, $ARGV[0]) or die "$ARGV[0]: $!\n";
# Read in the whole ROM in one gulp
my $filesize = read(R, $rom, MAXROMSIZE+1);
@@ -183,10 +183,16 @@ sub makerom () {
}
# Pad with 0xFF to $romsize
$rom .= "\xFF" x ($romsize - length($rom));
- if ($romsize >= 128 * 1024) {
- print "Warning: ROM size exceeds extension BIOS limit\n";
+ # If this is a stub ROM, don't force header size to the full amount
+ if (!$opts{'n'}) {
+ if ($romsize >= 128 * 1024) {
+ print "Warning: ROM size exceeds extension BIOS limit\n";
+ }
+ substr($rom, 2, 1) = chr(($romsize / 512) % 256);
+ } else {
+ $stubsize = ord(substr($rom, 2, 1)) * 512;
+ print "Stub size is $stubsize\n" if $opts{'v'};
}
- substr($rom, 2, 1) = chr(($romsize / 512) % 256);
print "ROM size is $romsize\n" if $opts{'v'};
# set the product string only if we don't have one yet
my $pnp_hdr_offset = unpack('v', substr($rom, PNP_PTR_LOC, 2));
@@ -196,7 +202,7 @@ sub makerom () {
# 3c503 requires last two bytes to be 0x80
substr($rom, MINROMSIZE-2, 2) = "\x80\x80"
if ($opts{'3'} and $romsize == MINROMSIZE);
- checksum(\$rom);
+ checksum(\$rom, $opts{'n'} ? $stubsize : $romsize);
writerom($ARGV[0], \$rom);
}
@@ -213,7 +219,7 @@ sub modrom () {
print "$filesize bytes read\n" if $opts{'v'};
pcipnpheaders(\$rom, undef);
undiheaders(\$rom);
- checksum(\$rom);
+ checksum(\$rom, ord(substr($rom, 2, 1)) * 512);
writerom($ARGV[0], \$rom);
}