Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[romprefix] Split PMM allocations for image source and decompression …
…area

Some BIOSes (at least some AMI BIOSes) tend to refuse to allocate a
single area large enough to hold both the iPXE image source and the
temporary decompression area, despite promising a largest available
PMM memory block of several megabytes.  This causes ROM image
shrinking to fail on these BIOSes, with undesirable consequences:
other option ROMs may be disabled due to shortage of option ROM space,
and the iPXE ROM may itself be corrupted by a further BIOS bug (again,
observed on an AMI BIOS) which causes large ROMs to end up overlapping
reserved areas of memory.  This can potentially render a system
unbootable via any means.

Increase the chances of a successful PMM allocation by dropping the
alignment requirement (which is redundant now that we can enable A20
from within the prefix); this allows us to reduce the allocation size
from 2MB down to only the required size.

Increase the chances still further by using two separate allocations:
one to hold the image source (i.e. the copy of the ROM before being
shrunk) and the other to act as the decompression area.  This allows
ROM image shrinking to take place even on systems that fail to
allocate enough memory for the temporary decompression area.

Improve the behaviour of iPXE in systems with multiple iPXE ROMs by
sharing PMM allocations where possible.  Image source areas can be
shared with any iPXE ROMs with a matching build identifier, and the
temporary decompression area can be shared with any iPXE ROMs with the
same uncompressed size (rounded up to the nearest 128kB).

Signed-off-by: Michael Brown <mcb30@ipxe.org>
  • Loading branch information
mcb30 committed Apr 25, 2010
1 parent f0ae1d5 commit c970797
Showing 1 changed file with 119 additions and 51 deletions.
170 changes: 119 additions & 51 deletions src/arch/i386/prefix/romprefix.S
Expand Up @@ -16,7 +16,14 @@ FILE_LICENCE ( GPL2_OR_LATER )
#define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) )
#define PNP_GET_BBS_VERSION 0x60
#define PMM_ALLOCATE 0x0000
#define PMM_DEALLOCATE 0x0002
#define PMM_FIND 0x0001
#define PMM_HANDLE_BASE ( ( ( 'F' - 'A' + 1 ) << 26 ) + \
( ( 'E' - 'A' + 1 ) << 21 ) + \
( ( 'N' - 'A' + 1 ) << 16 ) )
#define PMM_HANDLE_BASE_IMAGE_SOURCE \
( PMM_HANDLE_BASE | 0x00001000 )
#define PMM_HANDLE_BASE_DECOMPRESS_TO \
( PMM_HANDLE_BASE | 0x00002000 )

/* ROM banner timeout. Based on the configurable BANNER_TIMEOUT in
* config.h, but converted to a number of (18Hz) timer ticks, and
Expand Down Expand Up @@ -310,65 +317,44 @@ pmm_scan:
movw $init_message_pmm, %si
xorw %di, %di
call print_message
/* We have PMM and so a 1kB stack: preserve upper register halves */
/* We have PMM and so a 1kB stack: preserve whole registers */
pushal
/* Calculate required allocation size in %esi */
movzbl romheader_size, %eax
shll $9, %eax
addl $_textdata_memsz, %eax
orw $0xffff, %ax /* Ensure allocation size is at least 64kB */
bsrl %eax, %ecx
subw $15, %cx /* Round up and convert to 64kB count */
movw $1, %si
shlw %cl, %si
pmm_loop:
/* Try to allocate block via PMM */
pushw $0x0006 /* Aligned, extended memory */
pushl $0xffffffff /* No handle */
movzwl %si, %eax
shll $12, %eax
pushl %eax /* Allocation size in paragraphs */
pushw $PMM_ALLOCATE
lcall *%es:7
addw $12, %sp
/* Abort if allocation fails */
testw %dx, %dx /* %ax==0 even on success, since align>=64kB */
jz pmm_fail
/* If block has A20==1, free block and try again with twice
* the allocation size (and hence alignment).
*/
testw $0x0010, %dx
jz got_pmm
pushw %dx
pushw $0
pushw $PMM_DEALLOCATE
lcall *%es:7
addw $6, %sp
addw %si, %si
jmp pmm_loop
got_pmm: /* PMM allocation succeeded */
movw %dx, ( image_source + 2 )
movw %dx, %ax
xorw %di, %di
call print_hex_word
movb $( '@' ), %al
call print_character
movw %si, %ax
call print_hex_byte
/* Copy ROM to PMM block */
/* Allocate image source PMM block */
movzbl romheader_size, %ecx
shll $5, %ecx
movl $PMM_HANDLE_BASE_IMAGE_SOURCE, %ebx
movw $get_pmm_image_source, %bp
call get_pmm
movl %esi, image_source
jc 1f
/* Copy ROM to image source PMM block */
pushw %es
xorw %ax, %ax
movw %ax, %es
movl image_source, %edi
movl %esi, %edi
xorl %esi, %esi
movzbl romheader_size, %ecx
shll $9, %ecx
addr32 rep movsb /* PMM presence implies flat real mode */
movl %edi, decompress_to
popw %es
/* Shrink ROM */
movb shrunk_rom_size, %al
movb %al, romheader_size
pmm_fail:
/* Restore upper register halves */
1: /* Allocate decompression PMM block. Round up the size to the
* nearest 128kB and use the size within the PMM handle; this
* allows the same decompression area to be shared between
* multiple iPXE ROMs even with differing build IDs
*/
movl $_textdata_memsz_pgh, %ecx
addl $0x00001fff, %ecx
andl $0xffffe000, %ecx
movl %ecx, %ebx
shrw $12, %bx
orl $PMM_HANDLE_BASE_DECOMPRESS_TO, %ebx
movw $get_pmm_decompress_to, %bp
call get_pmm
movl %esi, decompress_to
/* Restore registers */
popal
no_pmm:

Expand Down Expand Up @@ -436,6 +422,88 @@ no_pmm:
lret
.size init, . - init

/* Attempt to find or allocate PMM block
*
* Parameters:
* %ecx : size of block to allocate, in paragraphs
* %ebx : PMM handle base
* %bp : routine to check acceptability of found blocks
* %es:0000 : PMM structure
* Returns:
* %ebx : PMM handle
* %esi : allocated block address, or zero (with CF set) if allocation failed
*/
get_pmm:
/* Preserve registers */
pushl %eax
pushw %di
movw $' ', %di
get_pmm_find:
/* Try to find existing block */
pushl %ebx /* PMM handle */
pushw $PMM_FIND
lcall *%es:7
addw $6, %sp
pushw %dx
pushw %ax
popl %esi
testl %esi, %esi
jz get_pmm_allocate
/* Block found - check acceptability */
call *%bp
jnc get_pmm_done
/* Block not acceptable - increment handle and retry */
incl %ebx
jmp get_pmm_find
get_pmm_allocate:
/* Block not found - try to allocate new block */
pushw $0x0002 /* Extended memory */
pushl %ebx /* PMM handle */
pushl %ecx /* Length */
pushw $PMM_ALLOCATE
lcall *%es:7
addw $12, %sp
pushw %dx
pushw %ax
popl %esi
movw $'+', %di /* Indicate allocation attempt */
testl %esi, %esi
jnz get_pmm_done
stc
get_pmm_done:
/* Print block address */
pushfw
movw %di, %ax
xorw %di, %di
call print_character
movl %esi, %eax
call print_hex_dword
popfw
/* Restore registers and return */
popw %di
popl %eax
ret
.size get_pmm, . - get_pmm

/* Check acceptability of image source block */
get_pmm_image_source:
pushw %es
xorw %ax, %ax
movw %ax, %es
movl build_id, %eax
cmpl %es:build_id(%esi), %eax
je 1f
stc
1: popw %es
ret
.size get_pmm_image_source, . - get_pmm_image_source

/* Check acceptability of decompression block */
get_pmm_decompress_to:
clc
ret
.size get_pmm_decompress_to, . - get_pmm_decompress_to

/*
* Note to hardware vendors:
*
Expand All @@ -456,7 +524,7 @@ init_message:
.ascii "\n"
.ascii PRODUCT_NAME
.ascii "\n"
.asciz "iPXE (http://ipxe.org) - "
.asciz "iPXE (http://ipxe.org) "
.size init_message, . - init_message
init_message_pci:
.asciz " PCI"
Expand Down

0 comments on commit c970797

Please sign in to comment.