david/ipxe
david
/
ipxe
Archived
1
0
Fork 0

[romprefix] Remove .xrom prefix

The .xrom prefix provides an experimental mechanism for loading ROM
images greater than 64kB in size by mapping the expansion ROM BAR in
at a hopefully-unused address.  This is unreliable, and potentially
dangerous.  In particular, there is no guarantee that any PCI bridges
between the CPU and the device will respond to accesses for the
"unused" memory region that is chosen, and it is possible that the
process of scanning for the "unused" memory region may end up issuing
reads to other PCI devices.  If this ends up trampling on a register
with read side-effects belonging to an unrelated PCI device, this may
cause undefined behaviour.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown 2010-04-19 20:32:25 +01:00
parent 8406115834
commit 329686c026
6 changed files with 20 additions and 407 deletions

View File

@ -867,8 +867,6 @@ 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
#

View File

@ -12,7 +12,6 @@ LDFLAGS += -N --no-check-sections
#
MEDIA += rom
MEDIA += hrom
MEDIA += xrom
MEDIA += pxe
MEDIA += kpxe
MEDIA += kkpxe
@ -26,7 +25,6 @@ MEDIA += raw
#
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 $@

View File

@ -25,19 +25,6 @@ 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
@ -46,12 +33,10 @@ FILE_LICENCE ( GPL2_OR_LATER )
.org 0x00
romheader:
.word 0xAA55 /* BIOS extension signature */
romheader_size: .byte ROM_SIZE_VALUE /* Size in 512-byte blocks */
romheader_size: .byte 0 /* Size in 512-byte blocks */
jmp init /* Initialisation vector */
checksum:
.byte 0, 0
real_size:
.word 0
.byte 0
.org 0x16
.word undiheader
.org 0x18
@ -61,16 +46,10 @@ real_size:
.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:
@ -82,18 +61,17 @@ pciheader:
.byte 0x03 /* PCI data structure revision */
.byte 0x02, 0x00, 0x00 /* Class code */
pciheader_image_length:
.word ROM_SIZE_VALUE /* Image length */
.word 0 /* Image length */
.word 0x0001 /* Revision level */
.byte 0x00 /* Code type */
.byte 0x80 /* Last image indicator */
pciheader_runtime_length:
.word ROM_SIZE_VALUE /* Maximum run-time image length */
.word 0 /* 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
@ -104,7 +82,6 @@ pciheader_runtime_length:
.long 512
.long 0
.previous
#endif
pnpheader:
.ascii "$PnP" /* Signature */
@ -198,11 +175,6 @@ 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
@ -227,9 +199,6 @@ 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
@ -341,7 +310,7 @@ pmm_scan:
/* We have PMM and so a 1kB stack: preserve upper register halves */
pushal
/* Calculate required allocation size in %esi */
movzwl real_size, %eax
movzbl romheader_size, %eax
shll $9, %eax
addl $_textdata_memsz, %eax
orw $0xffff, %ax /* Ensure allocation size is at least 64kB */
@ -395,7 +364,7 @@ pmm_copy:
movl %edi, decompress_to
/* Shrink ROM */
movb $_prefix_memsz_sect, romheader_size
#if defined(SHRINK_WITHOUT_PMM) || defined(LOAD_ROM_FROM_PCI)
#ifdef SHRINK_WITHOUT_PMM
jmp pmm_done
pmm_fail:
/* Print marker and copy ourselves to high memory */
@ -410,28 +379,8 @@ 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
@ -476,14 +425,14 @@ no_pmm:
movw $init_message_done, %si
call print_message
popf
jnz out
jnz 2f
/* Ctrl-B was pressed: invoke iPXE. The keypress will be
* picked up by the initial shell prompt, and we will drop
* into a shell.
*/
pushw %cs
call exec
out:
2:
/* Restore registers */
popw %gs
popw %fs
@ -530,11 +479,6 @@ 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
@ -560,32 +504,12 @@ 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.
@ -604,289 +528,6 @@ 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
@ -947,14 +588,6 @@ 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

View File

@ -1,9 +0,0 @@
/*
* 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"

View File

@ -194,7 +194,6 @@ 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 );

View File

@ -130,14 +130,14 @@ sub writerom ($$) {
close(R);
}
sub checksum ($$) {
my ($romref, $romsize) = @_;
sub checksum ($) {
my ($romref) = @_;
substr($$romref, 6, 1) = "\x00";
my $sum = unpack('%8C*', substr($$romref, 0, $romsize));
my $sum = unpack('%8C*', $$romref);
substr($$romref, 6, 1) = chr(256 - $sum);
# Double check
$sum = unpack('%8C*', substr($$romref, 0, $romsize));
$sum = unpack('%8C*', $$romref);
if ($sum != 0) {
print "Checksum fails\n"
} elsif ($opts{'v'}) {
@ -146,10 +146,10 @@ sub checksum ($$) {
}
sub makerom () {
my ($rom, $romsize, $stubsize);
my ($rom, $romsize);
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";
getopts('3xi:p:s:v', \%opts);
$ARGV[0] or die "Usage: $0 [-s romsize] [-i ident] [-p vendorid,deviceid] [-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,16 +183,10 @@ sub makerom () {
}
# Pad with 0xFF to $romsize
$rom .= "\xFF" x ($romsize - length($rom));
# 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'};
if ($romsize >= 128 * 1024) {
print "Warning: ROM size exceeds extension BIOS limit\n";
}
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));
@ -202,7 +196,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, $opts{'n'} ? $stubsize : $romsize);
checksum(\$rom);
writerom($ARGV[0], \$rom);
}
@ -219,7 +213,7 @@ sub modrom () {
print "$filesize bytes read\n" if $opts{'v'};
pcipnpheaders(\$rom, undef);
undiheaders(\$rom);
checksum(\$rom, ord(substr($rom, 2, 1)) * 512);
checksum(\$rom);
writerom($ARGV[0], \$rom);
}