Skip to content

Commit

Permalink
[prefix] Use 16-bit protected mode for access to high memory
Browse files Browse the repository at this point in the history
Flat real mode works perfectly on real hardware, but seems to cause
problems for some hypervisors.  Revert to using 16-bit protected mode
(and returning to real mode with 4GB limits, so as not to break PMM
BIOSes).

Allow the code specific to the .mrom format to continue to assume that
flat real mode works, since this format is specific to real hardware.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
  • Loading branch information
mcb30 committed Jan 26, 2011
1 parent 21d6f9c commit a84e152
Show file tree
Hide file tree
Showing 2 changed files with 195 additions and 42 deletions.
230 changes: 189 additions & 41 deletions src/arch/i386/prefix/libprefix.S
Expand Up @@ -24,6 +24,9 @@ FILE_LICENCE ( GPL2_OR_LATER )
/* Image compression enabled */
#define COMPRESS 1

/* Protected mode flag */
#define CR0_PE 1

/*****************************************************************************
* Utility function: print character (with LF -> LF,CR translation)
*
Expand Down Expand Up @@ -229,41 +232,153 @@ print_kill_line:
* None
****************************************************************************
*/
#if ! COMPRESS
.section ".prefix.lib", "awx", @progbits
.code16
copy_bytes:
pushl %ecx
pushl %ecx
rep addr32 movsb
popl %ecx
popl %ecx
ret
.size copy_bytes, . - copy_bytes
#endif /* COMPRESS */
.size copy_bytes, . - copy_bytes

/****************************************************************************
* install_block
* zero_bytes
*
* Install block to specified address
* Zero bytes
*
* Parameters:
* %esi : source physical address (must be a multiple of 16)
* %edi : destination physical address (must be a multiple of 16)
* %ecx : length of (decompressed) data
* %edx : total length of block (including any uninitialised data portion)
* %ds:esi : source address
* %es:edi : destination address
* %ecx : length
* Returns:
* %esi : next source physical address (will be a multiple of 16)
* %edi : next destination physical address (will be a multiple of 16)
* %ds:esi : next source address
* %es:edi : next destination address
* Corrupts:
* none
* None
****************************************************************************
*/
.section ".prefix.lib", "awx", @progbits
.code16
install_block:
zero_bytes:
pushl %ecx
pushw %ax
xorw %ax, %ax
rep addr32 stosb
popw %ax
popl %ecx
ret
.size zero_bytes, . - zero_bytes

/****************************************************************************
* process_bytes
*
* Call memcpy()-like function
*
* Parameters:
* %esi : source physical address
* %edi : destination physical address
* %ecx : length
* %bx : memcpy()-like function to call, passing parameters:
* %ds:esi : source address
* %es:edi : destination address
* %ecx : length
* and returning:
* %ds:esi : next source address
* %es:edi : next destination address
* Returns:
* %esi : next source physical address
* %edi : next destination physical address
* Corrupts:
* None
****************************************************************************
*/
.section ".prefix.lib", "awx", @progbits
.code16
process_bytes:

#ifndef KEEP_IT_REAL

/* Preserve registers */
pushfw
pushl %eax
pushl %ebp

/* Construct GDT on stack (since .prefix may not be writable) */
.equ PM_DS, 0x18 /* Flat data segment */
pushl $0x008f9300
pushl $0x0000ffff
.equ PM_SS, 0x10 /* Stack segment based at %ss:0000 */
pushl $0x008f0930
pushw %ss
pushw $0xffff
.equ PM_CS, 0x08 /* Code segment based at %cs:0000 */
pushl $0x008f09b0
pushw %cs
pushw $0xffff
pushl $0 /* Base and length */
pushw %ss
pushw $0x1f
movzwl %sp, %ebp
shll $4, 0x02(%bp)
addl %ebp, 0x02(%bp)
shll $4, 0x0a(%bp)
shll $4, 0x12(%bp)
subw $8, %sp
sgdt -8(%bp)

/* Switch to protected mode */
pushw %gs
pushw %fs
pushw %es
pushw %ds
pushw %ss
pushw %cs
pushw $2f
cli
data32 lgdt (%bp)
movl %cr0, %eax
orb $CR0_PE, %al
movl %eax, %cr0
ljmp $PM_CS, $1f
1: movw $PM_SS, %ax
movw %ax, %ss
movw $PM_DS, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %gs

/* Call memcpy()-like function */
call *%bx

/* Return to (flat) real mode */
movl %cr0, %eax
andb $0!CR0_PE, %al
movl %eax, %cr0
lret
2: /* lret will ljmp to here */
popw %ss
popw %ds
popw %es
popw %fs
popw %gs

/* Restore GDT */
data32 lgdt -8(%bp)
addw $( 8 /* saved GDT */ + ( PM_DS + 8 ) /* GDT on stack */ ), %sp

/* Restore registers and return */
popl %ebp
popl %eax
popfw
ret

#else /* KEEP_IT_REAL */

/* Preserve registers */
pushl %eax
pushw %ds
pushw %es
pushl %ecx

/* Convert %esi and %edi to %ds:esi and %es:edi */
shrl $4, %esi
Expand All @@ -275,42 +390,76 @@ install_block:
xorw %di, %di
shll $4, %edi

/* Call memcpy()-like function */
call *%bx

/* Convert %ds:esi and %es:edi back to physical addresses */
xorl %eax, %eax
movw %ds, %cx
shll $4, %eax
addl %eax, %esi
xorl %eax, %eax
movw %es, %cx
shll $4, %eax
addl %eax, %edi

/* Restore registers and return */
popw %es
popw %ds
popl %eax
ret

#endif /* KEEP_IT_REAL */

.size process_bytes, . - process_bytes

/****************************************************************************
* install_block
*
* Install block to specified address
*
* Parameters:
* %esi : source physical address (must be a multiple of 16)
* %edi : destination physical address (must be a multiple of 16)
* %ecx : length of (decompressed) data
* %edx : total length of block (including any uninitialised data portion)
* Returns:
* %esi : next source physical address (will be a multiple of 16)
* %edi : next destination physical address (will be a multiple of 16)
* Corrupts:
* none
****************************************************************************
*/
.section ".prefix.lib", "awx", @progbits
.code16
install_block:
/* Preserve registers */
pushl %ecx
pushw %bx

/* Decompress (or copy) source to destination */
#if COMPRESS
/* Decompress source to destination */
call decompress16
movw $decompress16, %bx
#else
/* Copy source to destination */
call copy_bytes
movw $copy_bytes, %bx
#endif
call process_bytes

/* Zero .bss portion */
negl %ecx
addl %edx, %ecx
pushw %ax
xorw %ax, %ax
rep addr32 stosb
popw %ax
movw $zero_bytes, %bx
call process_bytes

/* Round up %esi and %edi to start of next blocks */
addl $0xf, %esi
andl $~0xf, %esi
addl $0xf, %edi
andl $~0xf, %edi

/* Convert %ds:esi and %es:edi back to physical addresses */
xorl %ecx, %ecx
movw %ds, %cx
shll $4, %ecx
addl %ecx, %esi
xorl %ecx, %ecx
movw %es, %cx
shll $4, %ecx
addl %ecx, %edi

/* Restore registers and return */
popw %bx
popl %ecx
popw %es
popw %ds
ret
.size install_block, . - install_block

Expand Down Expand Up @@ -612,11 +761,10 @@ payload_death_message:

/* Copy code to new location */
pushl %edi
pushw %ax
xorw %ax, %ax
movw %ax, %es
es rep addr32 movsb
popw %ax
pushw %bx
movw $copy_bytes, %bx
call process_bytes
popw %bx
popl %edi

/* Initialise librm at new location */
Expand Down
7 changes: 6 additions & 1 deletion src/arch/i386/prefix/mromprefix.S
Expand Up @@ -151,7 +151,12 @@ find_mem_bar:
/* Copy payload to buffer, or set buffer address to BAR address */
testl %esi, %esi
jz 1f
/* We have a buffer; copy payload to it */
/* We have a buffer; copy payload to it. Since .mrom is
* designed specifically for real hardware, we assume that
* flat real mode is working properly. (In the unlikely event
* that this code is run inside a hypervisor that doesn't
* properly support flat real mode, it will die horribly.)
*/
pushl %esi
pushw %es
movl %esi, %edi
Expand Down

0 comments on commit a84e152

Please sign in to comment.