aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJoshua Oreman <oremanj@rwcr.net>2009-10-18 16:12:51 -0400
committerMarty Connor <mdc@etherboot.org>2010-01-20 17:46:48 -0500
commit06a8398422efb613b7ee4f9d8f1abcc813bb3f3b (patch)
tree81dc06685687ee84b6b168e1218852e3f6c175bf /src
parentfa4aec8f034f4999cf933161810b96de83ce31a7 (diff)
downloadipxe-06a8398422efb613b7ee4f9d8f1abcc813bb3f3b.zip
ipxe-06a8398422efb613b7ee4f9d8f1abcc813bb3f3b.tar.gz
ipxe-06a8398422efb613b7ee4f9d8f1abcc813bb3f3b.tar.bz2
[prefix] Add .xrom prefix for a ROM that loads itself by PCI accesses
The standard option ROM format provides a header indicating the size of the entire ROM, which the BIOS will reserve space for, load, and call as necessary. However, this space is strictly limited to 128k for all ROMs. gPXE ameliorates this somewhat by reserving space for itself in high memory and relocating the majority of its code there, but on systems prior to PCI3 enough space must still be present to load the ROM in the first place. Even on PCI3 systems, the BIOS often limits the size of ROM it will load to a bit over 64kB. These space problems can be solved by providing an artificially small size in the ROM header: just enough to let the prefix code (at the beginning of the ROM image) be loaded by the BIOS. To the BIOS, the gPXE ROM will appear to be only a few kilobytes; it can then load the rest of itself by accessing the ROM directly using the PCI interface reserved for that task. There are a few problems with this approach. First, gPXE needs to find an unmapped region in memory to map the ROM so it can read from it; this is done using the crude but effective approach of scanning high memory (over 0xF0000000) for a sufficiently large region of all-ones (0xFF) reads. (In x86 architecture, all-ones is returned for accesses to memory regions that no mapped device can satisfy.) This is not provably valid in all situations, but has worked well in practice. More importantly, this type of ROM access can only work if the PCI ROM BAR exists at all. NICs on physical add-in PCI cards generally must have the BAR in order for the BIOS to be able to load their ROM, but ISA cards and LAN-on-Motherboard cards will both fail to load gPXE using this scheme. Due to these uncertainties, it is recommended that .xrom only be used when a regular .rom image is infeasible due to crowded option ROM space. However, when it works it could allow loading gPXE images as large as a flash chip one could find - 128kB or even higher. Signed-off-by: Marty Connor <mdc@etherboot.org>
Diffstat (limited to 'src')
-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 1642374..1f5e115 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 55ba11d..8b01085 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 952eccd..02e5497 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 0000000..d7c861f
--- /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 77e8c7e..33c75f9 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 aed3a56..68c3be9 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);
}