diff --git a/src/arch/i386/Makefile.pcbios b/src/arch/i386/Makefile.pcbios index 10042052..1c1b5a88 100644 --- a/src/arch/i386/Makefile.pcbios +++ b/src/arch/i386/Makefile.pcbios @@ -11,6 +11,7 @@ LDFLAGS += -N --no-check-sections # Media types. # MEDIA += rom +MEDIA += mrom MEDIA += pxe MEDIA += kpxe MEDIA += kkpxe @@ -23,12 +24,14 @@ MEDIA += raw # Padding rules # PAD_rom = $(PADIMG) --blksize=512 --byte=0xff $@ +PAD_mrom = $(PAD_rom) PAD_dsk = $(PADIMG) --blksize=512 $@ PAD_hd = $(PADIMG) --blksize=32768 $@ # Finalisation rules # FINALISE_rom = $(FIXROM) $@ +FINALISE_mrom = $(FINALISE_rom) # rule to make a non-emulation ISO boot image NON_AUTO_MEDIA += iso diff --git a/src/arch/i386/prefix/libprefix.S b/src/arch/i386/prefix/libprefix.S index eeed0ada..340e74be 100644 --- a/src/arch/i386/prefix/libprefix.S +++ b/src/arch/i386/prefix/libprefix.S @@ -479,6 +479,10 @@ install_prealloc: cld /* Sanity: clear the direction flag asap */ pushfw + /* Set up %ds for (read-only) access to .prefix */ + pushw %cs + popw %ds + /* Copy decompression temporary area physical address to %ebp */ movl %edi, %ebp @@ -495,7 +499,6 @@ install_prealloc: call install_block /* .text16.early */ popl %esi - /* Open up access to payload */ #ifndef KEEP_IT_REAL /* Access high memory */ pushw %cs @@ -511,18 +514,39 @@ install_prealloc: 2: jmp 2b .section ".prefix.data", "aw", @progbits a20_death_message: - .asciz "Gate A20 stuck - cannot continue\n" + .asciz "\nHigh memory inaccessible - cannot continue\n" .size a20_death_message, . - a20_death_message .previous 3: #endif + /* Open payload (which may not yet be in memory) */ + pushw %cs + pushw $1f + pushw %ax + pushw $open_payload + lret +1: /* Die if we could not access the payload */ + jnc 3f + xorw %di, %di + movl %esi, %eax + call print_hex_dword + movw $payload_death_message, %si + call print_message +2: jmp 2b + .section ".prefix.data", "aw", @progbits +payload_death_message: + .asciz "\nPayload inaccessible - cannot continue\n" + .size payload_death_message, . - payload_death_message + .previous +3: + /* Calculate physical address of payload (i.e. first source) */ testl %esi, %esi jnz 1f movw %cs, %si shll $4, %esi -1: addl %cs:payload_lma, %esi +1: addl payload_lma, %esi /* Install .text16.late and .data16 */ movl $_text16_late_filesz, %ecx @@ -588,9 +612,11 @@ a20_death_message: /* Copy code to new location */ pushl %edi + pushw %ax xorw %ax, %ax movw %ax, %es es rep addr32 movsb + popw %ax popl %edi /* Initialise librm at new location */ @@ -598,6 +624,10 @@ a20_death_message: skip_relocate: #endif + /* Close access to payload */ + movw %ax, (close_payload_vector+2) + lcall *close_payload_vector + /* Restore registers */ popfw popw %es @@ -625,6 +655,10 @@ prot_call_vector: .word 0 .size prot_call_vector, . - prot_call_vector #endif +close_payload_vector: + .word close_payload + .word 0 + .size close_payload_vector, . - close_payload_vector /* Payload address */ .section ".prefix.lib", "awx", @progbits @@ -637,6 +671,17 @@ payload_lma: .long 0 .previous + /* Dummy routines to open and close payload */ + .section ".text16.early.data", "aw", @progbits + .weak open_payload + .weak close_payload +open_payload: +close_payload: + clc + lret + .size open_payload, . - open_payload + .size close_payload, . - close_payload + /**************************************************************************** * uninstall * diff --git a/src/arch/i386/prefix/mromprefix.S b/src/arch/i386/prefix/mromprefix.S new file mode 100644 index 00000000..989cea1e --- /dev/null +++ b/src/arch/i386/prefix/mromprefix.S @@ -0,0 +1,428 @@ +/* + * Copyright (C) 2010 Michael Brown . + * + * 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_LICENCE ( GPL2_OR_LATER ) + +#define PCIBIOS_READ_CONFIG_WORD 0xb109 +#define PCIBIOS_READ_CONFIG_DWORD 0xb10a +#define PCIBIOS_WRITE_CONFIG_WORD 0xb10c +#define PCIBIOS_WRITE_CONFIG_DWORD 0xb10d +#define PCI_COMMAND 0x04 +#define PCI_COMMAND_MEM 0x02 +#define PCI_BAR_0 0x10 +#define PCI_BAR_5 0x24 +#define PCI_BAR_EXPROM 0x30 + +#define ROMPREFIX_EXCLUDE_PAYLOAD 1 +#include "romprefix.S" + + .text + .arch i386 + .code16 + +/* Obtain access to payload by exposing the expansion ROM BAR at the + * address currently used by a suitably large memory BAR on the same + * device. The memory BAR is temporarily disabled. Using a memory + * BAR on the same device means that we don't have to worry about the + * configuration of any intermediate PCI bridges. + * + * Parameters: + * %ds:0000 : Prefix + * %esi : Buffer for copy of image source (or zero if no buffer available) + * Returns: + * %esi : Valid image source address (buffered or unbuffered) + * CF set on error + */ + .section ".text16.early", "awx", @progbits + .globl open_payload +open_payload: + /* Preserve registers */ + pushl %eax + pushw %bx + pushl %ecx + pushl %edx + pushl %edi + pushw %bp + pushw %ds + + /* Retrieve bus:dev.fn and image source length from .prefix */ + movw init_pci_busdevfn, %bx + movl image_source_len_dword, %ecx + + /* Set up %ds for access to .text16.early */ + pushw %cs + popw %ds + + /* Store bus:dev.fn and image source length to .text16.early */ + movw %bx, payload_pci_busdevfn + movl %ecx, rom_bar_copy_len_dword + + /* Get expansion ROM BAR current value */ + movw $PCI_BAR_EXPROM, %di + call pci_read_bar + movl %eax, rom_bar_orig_value + + /* Get expansion ROM BAR size */ + call pci_size_mem_bar_low + movl %ecx, rom_bar_size + + /* Find a suitable memory BAR to use */ + movw $PCI_BAR_0, %di /* %di is PCI BAR register */ + xorw %bp, %bp /* %bp is increment */ +find_mem_bar: + /* Move to next BAR */ + addw %bp, %di + cmpw $PCI_BAR_5, %di + jle 1f + stc + jmp 99f +1: movw $4, %bp + + /* Get BAR current value */ + call pci_read_bar + + /* Skip non-existent BARs */ + notl %eax + testl %eax, %eax + notl %eax + jz find_mem_bar + + /* Skip I/O BARs */ + testb $0x01, %al + jnz find_mem_bar + + /* Set increment to 8 for 64-bit BARs */ + testb $0x04, %al + jz 1f + movw $8, %bp +1: + /* Skip 64-bit BARs with high dword set; we couldn't use this + * address for the (32-bit) expansion ROM BAR anyway + */ + testl %edx, %edx + jnz find_mem_bar + + /* Get low dword of BAR size */ + call pci_size_mem_bar_low + + /* Skip BARs smaller than the expansion ROM BAR */ + cmpl %ecx, rom_bar_size + ja find_mem_bar + + /* We have a memory BAR with a 32-bit address that is large + * enough to use. Store BAR number and original value. + */ + movw %di, stolen_bar_register + movl %eax, stolen_bar_orig_value + + /* Remove flags from BAR address */ + xorb %al, %al + + /* Write zero to our stolen BAR. This doesn't technically + * disable it, but it's a pretty safe bet that the PCI bridge + * won't pass through accesses to this region anyway. Note + * that the high dword (if any) must already be zero. + */ + xorl %ecx, %ecx + call pci_write_config_dword + + /* Enable expansion ROM BAR at stolen BAR's address */ + movl %eax, %ecx + orb $0x1, %cl + movw $PCI_BAR_EXPROM, %di + call pci_write_config_dword + + /* Copy payload to buffer, or set buffer address to BAR address */ + testl %esi, %esi + jz 1f + /* We have a buffer; copy payload to it */ + pushl %esi + pushw %es + movl %esi, %edi + movl %eax, %esi + movl rom_bar_copy_len_dword, %ecx + xorw %ax, %ax + movw %ax, %es + addr32 es rep movsl + popw %es + popl %esi + jmp 2f +1: /* We have no buffer; set %esi to the BAR address */ + movl %eax, %esi +2: + + clc + /* Restore registers and return */ +99: popw %ds + popw %bp + popl %edi + popl %edx + popl %ecx + popw %bx + popl %eax + lret + .size open_payload, . - open_payload + + .section ".text16.early.data", "aw", @progbits +payload_pci_busdevfn: + .word 0 + .size payload_pci_busdevfn, . - payload_pci_busdevfn + + .section ".text16.early.data", "aw", @progbits +rom_bar_orig_value: + .long 0 + .size rom_bar_orig_value, . - rom_bar_orig_value + + .section ".text16.early.data", "aw", @progbits +rom_bar_size: + .long 0 + .size rom_bar_size, . - rom_bar_size + + .section ".text16.early.data", "aw", @progbits +rom_bar_copy_len_dword: + .long 0 + .size rom_bar_copy_len_dword, . - rom_bar_copy_len_dword + + .section ".text16.early.data", "aw", @progbits +stolen_bar_register: + .word 0 + .size stolen_bar_register, . - stolen_bar_register + + .section ".text16.early.data", "aw", @progbits +stolen_bar_orig_value: + .long 0 + .size stolen_bar_orig_value, . - stolen_bar_orig_value + +/* Restore original BAR values + * + * Parameters: + * none + * Returns: + * none + */ + .section ".text16.early", "awx", @progbits + .globl close_payload +close_payload: + /* Preserve registers */ + pushw %bx + pushw %di + pushl %ecx + pushw %ds + + /* Set up %ds for access to .text16.early */ + pushw %cs + popw %ds + + /* Retrieve stored bus:dev.fn */ + movw payload_pci_busdevfn, %bx + + /* Restore expansion ROM BAR original value */ + movw $PCI_BAR_EXPROM, %di + movl rom_bar_orig_value, %ecx + call pci_write_config_dword + + /* Restore stolen BAR original value */ + movw stolen_bar_register, %di + movl stolen_bar_orig_value, %ecx + call pci_write_config_dword + + /* Restore registers and return */ + popw %ds + popl %ecx + popw %di + popw %bx + lret + .size close_payload, . - close_payload + +/* Get PCI BAR value + * + * Parameters: + * %bx : PCI bus:dev.fn + * %di : PCI BAR register number + * Returns: + * %edx:%eax : PCI BAR value + */ + .section ".text16.early", "awx", @progbits +pci_read_bar: + /* Preserve registers */ + pushl %ecx + pushw %di + + /* Read low dword value */ + call pci_read_config_dword + movl %ecx, %eax + + /* Read high dword value, if applicable */ + xorl %edx, %edx + andb $0x07, %cl + cmpb $0x04, %cl + jne 1f + addw $4, %di + call pci_read_config_dword + movl %ecx, %edx +1: + /* Restore registers and return */ + popw %di + popl %ecx + ret + .size pci_read_bar, . - pci_read_bar + +/* Get low dword of PCI memory BAR size + * + * Parameters: + * %bx : PCI bus:dev.fn + * %di : PCI BAR register number + * %eax : Low dword of current PCI BAR value + * Returns: + * %ecx : PCI BAR size + */ + .section ".text16.early", "awx", @progbits +pci_size_mem_bar_low: + /* Preserve registers */ + pushw %dx + + /* Disable memory accesses */ + xorw %dx, %dx + call pci_set_mem_access + + /* Write all ones to BAR */ + xorl %ecx, %ecx + decl %ecx + call pci_write_config_dword + + /* Read back BAR */ + call pci_read_config_dword + + /* Calculate size */ + notl %ecx + orb $0x0f, %cl + incl %ecx + + /* Restore original value */ + pushl %ecx + movl %eax, %ecx + call pci_write_config_dword + popl %ecx + + /* Enable memory accesses */ + movw $PCI_COMMAND_MEM, %dx + call pci_set_mem_access + + /* Restore registers and return */ + popw %dx + ret + .size pci_size_mem_bar_low, . - pci_size_mem_bar_low + +/* Read PCI config dword + * + * Parameters: + * %bx : PCI bus:dev.fn + * %di : PCI register number + * Returns: + * %ecx : Dword value + */ + .section ".text16.early", "awx", @progbits +pci_read_config_dword: + /* Preserve registers */ + pushl %eax + pushl %ebx + pushl %edx + + /* Issue INT 0x1a,b10a */ + movw $PCIBIOS_READ_CONFIG_DWORD, %ax + int $0x1a + + /* Restore registers and return */ + popl %edx + popl %ebx + popl %eax + ret + .size pci_read_config_dword, . - pci_read_config_dword + +/* Write PCI config dword + * + * Parameters: + * %bx : PCI bus:dev.fn + * %di : PCI register number + * %ecx : PCI BAR value + * Returns: + * none + */ + .section ".text16.early", "awx", @progbits +pci_write_config_dword: + /* Preserve registers */ + pushal + + /* Issue INT 0x1a,b10d */ + movw $PCIBIOS_WRITE_CONFIG_DWORD, %ax + int $0x1a + + /* Restore registers and return */ + popal + ret + .size pci_write_config_dword, . - pci_write_config_dword + +/* Enable/disable memory access response in PCI command word + * + * Parameters: + * %bx : PCI bus:dev.fn + * %dx : PCI_COMMAND_MEM, or zero + * Returns: + * none + */ + .section ".text16.early", "awx", @progbits +pci_set_mem_access: + /* Preserve registers */ + pushal + + /* Read current value of command register */ + pushw %bx + pushw %dx + movw $PCI_COMMAND, %di + movw $PCIBIOS_READ_CONFIG_WORD, %ax + int $0x1a + popw %dx + popw %bx + + /* Set memory access enable as appropriate */ + andw $~PCI_COMMAND_MEM, %cx + orw %dx, %cx + + /* Write new value of command register */ + movw $PCIBIOS_WRITE_CONFIG_WORD, %ax + int $0x1a + + /* Restore registers and return */ + popal + ret + .size pci_set_mem_access, . - pci_set_mem_access + +/* Image source area length (in dwords) + * + */ + .section ".prefix", "ax", @progbits +image_source_len_dword: + .long 0 + .size image_source_len_dword, . - image_source_len_dword + .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ + .ascii "ADDL" + .long image_source_len_dword + .long 4 + .long 0 + .previous diff --git a/src/arch/i386/prefix/romprefix.S b/src/arch/i386/prefix/romprefix.S index 27eda346..c58fcdb7 100644 --- a/src/arch/i386/prefix/romprefix.S +++ b/src/arch/i386/prefix/romprefix.S @@ -32,6 +32,16 @@ FILE_LICENCE ( GPL2_OR_LATER ) */ #define ROM_BANNER_TIMEOUT ( 2 * ( 18 * BANNER_TIMEOUT ) / 10 ) +/* Allow payload to be excluded from ROM size + */ +#if ROMPREFIX_EXCLUDE_PAYLOAD +#define ZINFO_TYPE_ADxB "ADHB" +#define ZINFO_TYPE_ADxW "ADHW" +#else +#define ZINFO_TYPE_ADxB "ADDB" +#define ZINFO_TYPE_ADxW "ADDW" +#endif + .text .code16 .arch i386 @@ -53,7 +63,7 @@ checksum: .size romheader, . - romheader .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ - .ascii "ADDB" + .ascii ZINFO_TYPE_ADxB .long romheader_size .long 512 .long 0 @@ -83,11 +93,11 @@ pciheader_runtime_length: .size pciheader, . - pciheader .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ - .ascii "ADDW" + .ascii ZINFO_TYPE_ADxW .long pciheader_image_length .long 512 .long 0 - .ascii "ADDW" + .ascii ZINFO_TYPE_ADxW .long pciheader_runtime_length .long 512 .long 0 @@ -179,6 +189,9 @@ init: movw %bx, %gs movw %di, %bx + /* Store PCI bus:dev.fn address */ + movw %ax, init_pci_busdevfn + /* Print message as early as possible */ movw $init_message, %si xorw %di, %di @@ -320,8 +333,7 @@ pmm_scan: /* We have PMM and so a 1kB stack: preserve whole registers */ pushal /* Allocate image source PMM block */ - movzbl romheader_size, %ecx - shll $5, %ecx + movzwl image_source_len_pgh, %ecx movl $PMM_HANDLE_BASE_IMAGE_SOURCE, %ebx movw $get_pmm_image_source, %bp call get_pmm @@ -552,6 +564,13 @@ init_message_done: .asciz "\n\n" .size init_message_done, . - init_message_done +/* PCI bus:dev.fn + * + */ +init_pci_busdevfn: + .word 0xffff + .size init_pci_busdevfn, . - init_pci_busdevfn + /* Image source area * * May be either zero (indicating to use option ROM space as source), @@ -562,6 +581,19 @@ image_source: .long 0 .size image_source, . - image_source +/* Image source area length (in paragraphs) + * + */ +image_source_len_pgh: + .word 0 + .size image_source_len_pgh, . - image_source_len_pgh + .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ + .ascii "ADDW" + .long image_source_len_pgh + .long 16 + .long 0 + .previous + /* Shrunk ROM size (in 512-byte sectors) * */ diff --git a/src/util/Option/ROM.pm b/src/util/Option/ROM.pm index a86d3262..9fea4d34 100644 --- a/src/util/Option/ROM.pm +++ b/src/util/Option/ROM.pm @@ -349,7 +349,8 @@ sub checksum { my $hash = shift; my $self = tied(%$hash); - return unpack ( "%8C*", ${$self->{data}} ); + my $raw = substr ( ${$self->{data}}, 0, ( $hash->{length} * 512 ) ); + return unpack ( "%8C*", $raw ); } =pod