mirror of
https://github.com/ipxe/ipxe
synced 2026-01-11 07:58:34 +03:00
[bios] Add bin-x86_64-pcbios build platform
Move most arch/i386 files to arch/x86, and adjust the contents of the Makefiles and the include/bits/*.h headers to reflect the new locations. This patch makes no substantive code changes, as can be seen using a rename-aware diff (e.g. "git show -M5"). This patch does not make the pcbios platform functional for x86_64; it merely allows it to compile without errors. Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
313
src/arch/x86/transitions/liba20.S
Normal file
313
src/arch/x86/transitions/liba20.S
Normal file
@@ -0,0 +1,313 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>.
|
||||
*
|
||||
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
* You can also choose to distribute this program under the terms of
|
||||
* the Unmodified Binary Distribution Licence (as given in the file
|
||||
* COPYING.UBDL), provided that you have satisfied its requirements.
|
||||
*
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
|
||||
|
||||
.arch i386
|
||||
|
||||
/****************************************************************************
|
||||
* test_a20_short, test_a20_long
|
||||
*
|
||||
* Check to see if A20 line is enabled
|
||||
*
|
||||
* Parameters:
|
||||
* none
|
||||
* Returns:
|
||||
* CF set if A20 line is not enabled
|
||||
* Corrupts:
|
||||
* none
|
||||
****************************************************************************
|
||||
*/
|
||||
#define TEST_A20_SHORT_MAX_RETRIES 0x20
|
||||
#define TEST_A20_LONG_MAX_RETRIES 0x200000
|
||||
.section ".text16.early", "awx", @progbits
|
||||
.code16
|
||||
test_a20_short:
|
||||
pushl %ecx
|
||||
movl $TEST_A20_SHORT_MAX_RETRIES, %ecx
|
||||
jmp 1f
|
||||
.size test_a20_short, . - test_a20_short
|
||||
test_a20_long:
|
||||
pushl %ecx
|
||||
movl $TEST_A20_LONG_MAX_RETRIES, %ecx
|
||||
1: pushw %ax
|
||||
pushw %ds
|
||||
pushw %es
|
||||
|
||||
/* Set up segment registers for access across the 1MB boundary */
|
||||
xorw %ax, %ax
|
||||
movw %ax, %ds
|
||||
decw %ax
|
||||
movw %ax, %es
|
||||
|
||||
2: /* Modify and check test pattern; succeed if we see a difference */
|
||||
pushfw
|
||||
cli
|
||||
xchgw %ds:0, %cx
|
||||
movw %es:0x10, %ax
|
||||
xchgw %ds:0, %cx
|
||||
popfw
|
||||
cmpw %ax, %cx
|
||||
clc
|
||||
jnz 99f
|
||||
|
||||
/* Delay and retry */
|
||||
outb %al, $0x80
|
||||
addr32 loop 2b
|
||||
stc
|
||||
|
||||
99: /* Restore registers and return */
|
||||
popw %es
|
||||
popw %ds
|
||||
popw %ax
|
||||
popl %ecx
|
||||
ret
|
||||
.size test_a20_long, . - test_a20_long
|
||||
|
||||
/****************************************************************************
|
||||
* enable_a20_bios
|
||||
*
|
||||
* Try enabling A20 line via BIOS
|
||||
*
|
||||
* Parameters:
|
||||
* none
|
||||
* Returns:
|
||||
* CF set if A20 line is not enabled
|
||||
* Corrupts:
|
||||
* none
|
||||
****************************************************************************
|
||||
*/
|
||||
.section ".text16.early", "awx", @progbits
|
||||
.code16
|
||||
enable_a20_bios:
|
||||
|
||||
/* Preserve registers. Be very paranoid, since some BIOSes
|
||||
* are reported to clobber %ebx
|
||||
*/
|
||||
pushal
|
||||
|
||||
/* Attempt INT 15,2401 */
|
||||
movw $0x2401, %ax
|
||||
int $0x15
|
||||
jc 99f
|
||||
|
||||
/* Check that success was really successful */
|
||||
call test_a20_short
|
||||
|
||||
99: /* Restore registers and return */
|
||||
popal
|
||||
ret
|
||||
.size enable_a20_bios, . - enable_a20_bios
|
||||
|
||||
/****************************************************************************
|
||||
* enable_a20_kbc
|
||||
*
|
||||
* Try enabling A20 line via keyboard controller
|
||||
*
|
||||
* Parameters:
|
||||
* none
|
||||
* Returns:
|
||||
* CF set if A20 line is not enabled
|
||||
* Corrupts:
|
||||
* none
|
||||
****************************************************************************
|
||||
*/
|
||||
#define KC_RDWR 0x60
|
||||
#define KC_RDWR_SET_A20 0xdf
|
||||
#define KC_CMD 0x64
|
||||
#define KC_CMD_WOUT 0xd1
|
||||
#define KC_CMD_NULL 0xff
|
||||
#define KC_STATUS 0x64
|
||||
#define KC_STATUS_OBUF_FULL 0x01
|
||||
#define KC_STATUS_IBUF_FULL 0x02
|
||||
#define KC_MAX_RETRIES 100000
|
||||
.section ".text16.early", "awx", @progbits
|
||||
.code16
|
||||
enable_a20_kbc:
|
||||
/* Preserve registers */
|
||||
pushw %ax
|
||||
|
||||
/* Try keyboard controller */
|
||||
call empty_kbc
|
||||
movb $KC_CMD_WOUT, %al
|
||||
outb %al, $KC_CMD
|
||||
call empty_kbc
|
||||
movb $KC_RDWR_SET_A20, %al
|
||||
outb %al, $KC_RDWR
|
||||
call empty_kbc
|
||||
movb $KC_CMD_NULL, %al
|
||||
outb %al, $KC_CMD
|
||||
call empty_kbc
|
||||
|
||||
/* Check to see if it worked */
|
||||
call test_a20_long
|
||||
|
||||
/* Restore registers and return */
|
||||
popw %ax
|
||||
ret
|
||||
.size enable_a20_kbc, . - enable_a20_kbc
|
||||
|
||||
.section ".text16.early", "awx", @progbits
|
||||
.code16
|
||||
empty_kbc:
|
||||
/* Preserve registers */
|
||||
pushl %ecx
|
||||
pushw %ax
|
||||
|
||||
/* Wait for KBC to become empty */
|
||||
movl $KC_MAX_RETRIES, %ecx
|
||||
1: outb %al, $0x80
|
||||
inb $KC_STATUS, %al
|
||||
testb $( KC_STATUS_OBUF_FULL | KC_STATUS_IBUF_FULL ), %al
|
||||
jz 99f
|
||||
testb $KC_STATUS_OBUF_FULL, %al
|
||||
jz 2f
|
||||
outb %al, $0x80
|
||||
inb $KC_RDWR, %al
|
||||
2: addr32 loop 1b
|
||||
|
||||
99: /* Restore registers and return */
|
||||
popw %ax
|
||||
popl %ecx
|
||||
ret
|
||||
.size empty_kbc, . - empty_kbc
|
||||
|
||||
/****************************************************************************
|
||||
* enable_a20_fast
|
||||
*
|
||||
* Try enabling A20 line via "Fast Gate A20"
|
||||
*
|
||||
* Parameters:
|
||||
* none
|
||||
* Returns:
|
||||
* CF set if A20 line is not enabled
|
||||
* Corrupts:
|
||||
* none
|
||||
****************************************************************************
|
||||
*/
|
||||
#define SCP_A 0x92
|
||||
.section ".text16.early", "awx", @progbits
|
||||
.code16
|
||||
enable_a20_fast:
|
||||
/* Preserve registers */
|
||||
pushw %ax
|
||||
|
||||
/* Try "Fast Gate A20" */
|
||||
inb $SCP_A, %al
|
||||
orb $0x02, %al
|
||||
andb $~0x01, %al
|
||||
outb %al, $SCP_A
|
||||
|
||||
/* Check to see if it worked */
|
||||
call test_a20_long
|
||||
|
||||
/* Restore registers and return */
|
||||
popw %ax
|
||||
ret
|
||||
.size enable_a20_fast, . - enable_a20_fast
|
||||
|
||||
/****************************************************************************
|
||||
* enable_a20
|
||||
*
|
||||
* Try enabling A20 line via any available method
|
||||
*
|
||||
* Parameters:
|
||||
* none
|
||||
* Returns:
|
||||
* CF set if A20 line is not enabled
|
||||
* Corrupts:
|
||||
* none
|
||||
****************************************************************************
|
||||
*/
|
||||
#define ENABLE_A20_RETRIES 255
|
||||
.section ".text16.early", "awx", @progbits
|
||||
.code16
|
||||
.globl enable_a20
|
||||
enable_a20:
|
||||
/* Preserve registers */
|
||||
pushl %ecx
|
||||
pushw %ax
|
||||
|
||||
/* Check to see if A20 is already enabled */
|
||||
call test_a20_short
|
||||
jnc 99f
|
||||
|
||||
/* Use known working method, if we have one */
|
||||
movw %cs:enable_a20_method, %ax
|
||||
testw %ax, %ax
|
||||
jz 1f
|
||||
call *%ax
|
||||
jmp 99f
|
||||
1:
|
||||
/* Try all methods in turn until one works */
|
||||
movl $ENABLE_A20_RETRIES, %ecx
|
||||
2: movw $enable_a20_bios, %ax
|
||||
movw %ax, %cs:enable_a20_method
|
||||
call *%ax
|
||||
jnc 99f
|
||||
movw $enable_a20_kbc, %ax
|
||||
movw %ax, %cs:enable_a20_method
|
||||
call *%ax
|
||||
jnc 99f
|
||||
movw $enable_a20_fast, %ax
|
||||
movw %ax, %cs:enable_a20_method
|
||||
call *%ax
|
||||
jnc 99f
|
||||
addr32 loop 2b
|
||||
/* Failure; exit with carry set */
|
||||
movw $0, %cs:enable_a20_method
|
||||
stc
|
||||
|
||||
99: /* Restore registers and return */
|
||||
popw %ax
|
||||
popl %ecx
|
||||
ret
|
||||
|
||||
.section ".text16.early.data", "aw", @progbits
|
||||
.align 2
|
||||
enable_a20_method:
|
||||
.word 0
|
||||
.size enable_a20_method, . - enable_a20_method
|
||||
|
||||
/****************************************************************************
|
||||
* access_highmem (real mode far call)
|
||||
*
|
||||
* Open up access to high memory with A20 enabled
|
||||
*
|
||||
* Parameters:
|
||||
* none
|
||||
* Returns:
|
||||
* CF set if high memory could not be accessed
|
||||
* Corrupts:
|
||||
* none
|
||||
****************************************************************************
|
||||
*/
|
||||
.section ".text16.early", "awx", @progbits
|
||||
.code16
|
||||
.globl access_highmem
|
||||
access_highmem:
|
||||
/* Enable A20 line */
|
||||
call enable_a20
|
||||
lret
|
||||
.size access_highmem, . - access_highmem
|
||||
256
src/arch/x86/transitions/libkir.S
Normal file
256
src/arch/x86/transitions/libkir.S
Normal file
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
* libkir: a transition library for -DKEEP_IT_REAL
|
||||
*
|
||||
* Michael Brown <mbrown@fensystems.co.uk>
|
||||
*
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
|
||||
|
||||
/****************************************************************************
|
||||
* This file defines libkir: an interface between external and
|
||||
* internal environments when -DKEEP_IT_REAL is used, so that both
|
||||
* internal and external environments are in real mode. It deals with
|
||||
* switching data segments and the stack. It provides the following
|
||||
* functions:
|
||||
*
|
||||
* ext_to_kir & switch between external and internal (kir)
|
||||
* kir_to_ext environments, preserving all non-segment
|
||||
* registers
|
||||
*
|
||||
* kir_call issue a call to an internal routine from external
|
||||
* code
|
||||
*
|
||||
* libkir is written to avoid assuming that segments are anything
|
||||
* other than opaque data types, and also avoids assuming that the
|
||||
* stack pointer is 16-bit. This should enable it to run just as well
|
||||
* in 16:16 or 16:32 protected mode as in real mode.
|
||||
****************************************************************************
|
||||
*/
|
||||
|
||||
/* Breakpoint for when debugging under bochs */
|
||||
#define BOCHSBP xchgw %bx, %bx
|
||||
|
||||
.text
|
||||
.arch i386
|
||||
.section ".text16", "awx", @progbits
|
||||
.code16
|
||||
|
||||
/****************************************************************************
|
||||
* init_libkir (real-mode or 16:xx protected-mode far call)
|
||||
*
|
||||
* Initialise libkir ready for transitions to the kir environment
|
||||
*
|
||||
* Parameters:
|
||||
* %cs : .text16 segment
|
||||
* %ds : .data16 segment
|
||||
****************************************************************************
|
||||
*/
|
||||
.globl init_libkir
|
||||
init_libkir:
|
||||
/* Record segment registers */
|
||||
pushw %ds
|
||||
popw %cs:kir_ds
|
||||
lret
|
||||
|
||||
/****************************************************************************
|
||||
* ext_to_kir (real-mode or 16:xx protected-mode near call)
|
||||
*
|
||||
* Switch from external stack and segment registers to internal stack
|
||||
* and segment registers. %ss:sp is restored from the saved kir_ds
|
||||
* and kir_sp. %ds, %es, %fs and %gs are all restored from the saved
|
||||
* kir_ds. All other registers are preserved.
|
||||
*
|
||||
* %cs:0000 must point to the start of the runtime image code segment
|
||||
* on entry.
|
||||
*
|
||||
* Parameters: none
|
||||
****************************************************************************
|
||||
*/
|
||||
|
||||
.globl ext_to_kir
|
||||
ext_to_kir:
|
||||
/* Record external segment registers */
|
||||
movw %ds, %cs:ext_ds
|
||||
pushw %cs
|
||||
popw %ds /* Set %ds = %cs for easier access to variables */
|
||||
movw %es, %ds:ext_es
|
||||
movw %fs, %ds:ext_fs
|
||||
movw %gs, %ds:ext_fs
|
||||
|
||||
/* Preserve registers */
|
||||
movw %ax, %ds:save_ax
|
||||
|
||||
/* Extract near return address from stack */
|
||||
popw %ds:save_retaddr
|
||||
|
||||
/* Record external %ss:esp */
|
||||
movw %ss, %ds:ext_ss
|
||||
movl %esp, %ds:ext_esp
|
||||
|
||||
/* Load internal segment registers and stack pointer */
|
||||
movw %ds:kir_ds, %ax
|
||||
movw %ax, %ss
|
||||
movzwl %ds:kir_sp, %esp
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
movw %ax, %fs
|
||||
movw %ax, %gs
|
||||
1:
|
||||
|
||||
/* Place return address on new stack */
|
||||
pushw %cs:save_retaddr
|
||||
|
||||
/* Restore registers and return */
|
||||
movw %cs:save_ax, %ax
|
||||
ret
|
||||
|
||||
/****************************************************************************
|
||||
* kir_to_ext (real-mode or 16:xx protected-mode near call)
|
||||
*
|
||||
* Switch from internal stack and segment registers to external stack
|
||||
* and segment registers. %ss:%esp is restored from the saved ext_ss
|
||||
* and ext_esp. Other segment registers are restored from the
|
||||
* corresponding locations. All other registers are preserved.
|
||||
*
|
||||
* Note that it is actually %ss that is recorded as kir_ds, on the
|
||||
* assumption that %ss == %ds when kir_to_ext is called.
|
||||
*
|
||||
* Parameters: none
|
||||
****************************************************************************
|
||||
*/
|
||||
|
||||
.globl kir_to_ext
|
||||
kir_to_ext:
|
||||
/* Record near return address */
|
||||
pushw %cs
|
||||
popw %ds /* Set %ds = %cs for easier access to variables */
|
||||
popw %ds:save_retaddr
|
||||
|
||||
/* Record internal segment registers and %sp */
|
||||
movw %ss, %ds:kir_ds
|
||||
movw %sp, %ds:kir_sp
|
||||
|
||||
/* Load external segment registers and stack pointer */
|
||||
movw %ds:ext_ss, %ss
|
||||
movl %ds:ext_esp, %esp
|
||||
movw %ds:ext_gs, %gs
|
||||
movw %ds:ext_fs, %fs
|
||||
movw %ds:ext_es, %es
|
||||
movw %ds:ext_ds, %ds
|
||||
|
||||
/* Return */
|
||||
pushw %cs:save_retaddr
|
||||
ret
|
||||
|
||||
/****************************************************************************
|
||||
* kir_call (real-mode or 16:xx protected-mode far call)
|
||||
*
|
||||
* Call a specific C function in the internal code. The prototype of
|
||||
* the C function must be
|
||||
* void function ( struct i386_all_resg *ix86 );
|
||||
* ix86 will point to a struct containing the real-mode registers
|
||||
* at entry to kir_call.
|
||||
*
|
||||
* All registers will be preserved across kir_call(), unless the C
|
||||
* function explicitly overwrites values in ix86. Interrupt status
|
||||
* will also be preserved.
|
||||
*
|
||||
* Parameters:
|
||||
* function : (32-bit) virtual address of C function to call
|
||||
*
|
||||
* Example usage:
|
||||
* pushl $pxe_api_call
|
||||
* lcall $UNDI_CS, $kir_call
|
||||
* addw $4, %sp
|
||||
* to call in to the C function
|
||||
* void pxe_api_call ( struct i386_all_regs *ix86 );
|
||||
****************************************************************************
|
||||
*/
|
||||
|
||||
.globl kir_call
|
||||
kir_call:
|
||||
/* Preserve flags. Must do this before any operation that may
|
||||
* affect flags.
|
||||
*/
|
||||
pushfl
|
||||
popl %cs:save_flags
|
||||
|
||||
/* Disable interrupts. We do funny things with the stack, and
|
||||
* we're not re-entrant.
|
||||
*/
|
||||
cli
|
||||
|
||||
/* Extract address of internal routine from stack. We must do
|
||||
* this without using (%bp), because we may be called with
|
||||
* either a 16-bit or a 32-bit stack segment.
|
||||
*/
|
||||
popl %cs:save_retaddr /* Scratch location */
|
||||
popl %cs:save_function
|
||||
subl $8, %esp /* Restore %esp */
|
||||
|
||||
/* Switch to internal stack. Note that the external stack is
|
||||
* inaccessible once we're running internally (since we have
|
||||
* no concept of 48-bit far pointers)
|
||||
*/
|
||||
call ext_to_kir
|
||||
|
||||
/* Store external registers on internal stack */
|
||||
pushl %cs:save_flags
|
||||
pushal
|
||||
pushl %cs:ext_fs_and_gs
|
||||
pushl %cs:ext_ds_and_es
|
||||
pushl %cs:ext_cs_and_ss
|
||||
|
||||
/* Push &ix86 on stack and call function */
|
||||
sti
|
||||
pushl %esp
|
||||
data32 call *%cs:save_function
|
||||
popl %eax /* discard */
|
||||
|
||||
/* Restore external registers from internal stack */
|
||||
popl %cs:ext_cs_and_ss
|
||||
popl %cs:ext_ds_and_es
|
||||
popl %cs:ext_fs_and_gs
|
||||
popal
|
||||
popl %cs:save_flags
|
||||
|
||||
/* Switch to external stack */
|
||||
call kir_to_ext
|
||||
|
||||
/* Restore flags */
|
||||
pushl %cs:save_flags
|
||||
popfl
|
||||
|
||||
/* Return */
|
||||
lret
|
||||
|
||||
/****************************************************************************
|
||||
* Stored internal and external stack and segment registers
|
||||
****************************************************************************
|
||||
*/
|
||||
|
||||
ext_cs_and_ss:
|
||||
ext_cs: .word 0
|
||||
ext_ss: .word 0
|
||||
ext_ds_and_es:
|
||||
ext_ds: .word 0
|
||||
ext_es: .word 0
|
||||
ext_fs_and_gs:
|
||||
ext_fs: .word 0
|
||||
ext_gs: .word 0
|
||||
ext_esp: .long 0
|
||||
|
||||
.globl kir_ds
|
||||
kir_ds: .word 0
|
||||
.globl kir_sp
|
||||
kir_sp: .word _estack
|
||||
|
||||
/****************************************************************************
|
||||
* Temporary variables
|
||||
****************************************************************************
|
||||
*/
|
||||
save_ax: .word 0
|
||||
save_retaddr: .long 0
|
||||
save_flags: .long 0
|
||||
save_function: .long 0
|
||||
0
src/arch/x86/transitions/libpm.S
Normal file
0
src/arch/x86/transitions/libpm.S
Normal file
671
src/arch/x86/transitions/librm.S
Normal file
671
src/arch/x86/transitions/librm.S
Normal file
@@ -0,0 +1,671 @@
|
||||
/*
|
||||
* librm: a library for interfacing to real-mode code
|
||||
*
|
||||
* Michael Brown <mbrown@fensystems.co.uk>
|
||||
*
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
|
||||
|
||||
/* Drag in local definitions */
|
||||
#include "librm.h"
|
||||
|
||||
/* For switches to/from protected mode */
|
||||
#define CR0_PE 1
|
||||
|
||||
/* Size of various C data structures */
|
||||
#define SIZEOF_I386_SEG_REGS 12
|
||||
#define SIZEOF_I386_REGS 32
|
||||
#define SIZEOF_REAL_MODE_REGS ( SIZEOF_I386_SEG_REGS + SIZEOF_I386_REGS )
|
||||
#define SIZEOF_I386_FLAGS 4
|
||||
#define SIZEOF_I386_ALL_REGS ( SIZEOF_REAL_MODE_REGS + SIZEOF_I386_FLAGS )
|
||||
|
||||
.arch i386
|
||||
|
||||
/****************************************************************************
|
||||
* Global descriptor table
|
||||
*
|
||||
* Call init_librm to set up the GDT before attempting to use any
|
||||
* protected-mode code.
|
||||
*
|
||||
* NOTE: This must be located before prot_to_real, otherwise gas
|
||||
* throws a "can't handle non absolute segment in `ljmp'" error due to
|
||||
* not knowing the value of REAL_CS when the ljmp is encountered.
|
||||
*
|
||||
* Note also that putting ".word gdt_end - gdt - 1" directly into
|
||||
* gdt_limit, rather than going via gdt_length, will also produce the
|
||||
* "non absolute segment" error. This is most probably a bug in gas.
|
||||
****************************************************************************
|
||||
*/
|
||||
.section ".data16", "aw", @progbits
|
||||
.align 16
|
||||
gdt:
|
||||
gdtr: /* The first GDT entry is unused, the GDTR can fit here. */
|
||||
gdt_limit: .word gdt_length - 1
|
||||
gdt_base: .long 0
|
||||
.word 0 /* padding */
|
||||
|
||||
.org gdt + VIRTUAL_CS, 0
|
||||
virtual_cs: /* 32 bit protected mode code segment, virtual addresses */
|
||||
.word 0xffff, 0
|
||||
.byte 0, 0x9f, 0xcf, 0
|
||||
|
||||
.org gdt + VIRTUAL_DS, 0
|
||||
virtual_ds: /* 32 bit protected mode data segment, virtual addresses */
|
||||
.word 0xffff, 0
|
||||
.byte 0, 0x93, 0xcf, 0
|
||||
|
||||
.org gdt + PHYSICAL_CS, 0
|
||||
physical_cs: /* 32 bit protected mode code segment, physical addresses */
|
||||
.word 0xffff, 0
|
||||
.byte 0, 0x9f, 0xcf, 0
|
||||
|
||||
.org gdt + PHYSICAL_DS, 0
|
||||
physical_ds: /* 32 bit protected mode data segment, physical addresses */
|
||||
.word 0xffff, 0
|
||||
.byte 0, 0x93, 0xcf, 0
|
||||
|
||||
.org gdt + REAL_CS, 0
|
||||
real_cs: /* 16 bit real mode code segment */
|
||||
.word 0xffff, 0
|
||||
.byte 0, 0x9b, 0x00, 0
|
||||
|
||||
.org gdt + REAL_DS
|
||||
real_ds: /* 16 bit real mode data segment */
|
||||
.word 0xffff, ( REAL_DS << 4 )
|
||||
.byte 0, 0x93, 0x00, 0
|
||||
|
||||
gdt_end:
|
||||
.equ gdt_length, gdt_end - gdt
|
||||
|
||||
/****************************************************************************
|
||||
* init_librm (real-mode far call, 16-bit real-mode far return address)
|
||||
*
|
||||
* Initialise the GDT ready for transitions to protected mode.
|
||||
*
|
||||
* Parameters:
|
||||
* %cs : .text16 segment
|
||||
* %ds : .data16 segment
|
||||
* %edi : Physical base of protected-mode code (virt_offset)
|
||||
****************************************************************************
|
||||
*/
|
||||
.section ".text16", "ax", @progbits
|
||||
.code16
|
||||
.globl init_librm
|
||||
init_librm:
|
||||
/* Preserve registers */
|
||||
pushl %eax
|
||||
pushl %ebx
|
||||
|
||||
/* Store virt_offset and set up virtual_cs and virtual_ds segments */
|
||||
movl %edi, %eax
|
||||
movw $virtual_cs, %bx
|
||||
call set_seg_base
|
||||
movw $virtual_ds, %bx
|
||||
call set_seg_base
|
||||
movl %edi, rm_virt_offset
|
||||
|
||||
/* Negate virt_offset */
|
||||
negl %edi
|
||||
|
||||
/* Store rm_cs and text16, set up real_cs segment */
|
||||
xorl %eax, %eax
|
||||
movw %cs, %ax
|
||||
movw %ax, %cs:rm_cs
|
||||
shll $4, %eax
|
||||
movw $real_cs, %bx
|
||||
call set_seg_base
|
||||
addr32 leal (%eax, %edi), %ebx
|
||||
movl %ebx, rm_text16
|
||||
|
||||
/* Store rm_ds and data16 */
|
||||
xorl %eax, %eax
|
||||
movw %ds, %ax
|
||||
movw %ax, %cs:rm_ds
|
||||
shll $4, %eax
|
||||
addr32 leal (%eax, %edi), %ebx
|
||||
movl %ebx, rm_data16
|
||||
|
||||
/* Set GDT base */
|
||||
movl %eax, gdt_base
|
||||
addl $gdt, gdt_base
|
||||
|
||||
/* Initialise IDT */
|
||||
pushl $init_idt
|
||||
pushw %cs
|
||||
call prot_call
|
||||
popl %eax /* discard */
|
||||
|
||||
/* Restore registers */
|
||||
negl %edi
|
||||
popl %ebx
|
||||
popl %eax
|
||||
lret
|
||||
|
||||
.section ".text16", "ax", @progbits
|
||||
.code16
|
||||
set_seg_base:
|
||||
1: movw %ax, 2(%bx)
|
||||
rorl $16, %eax
|
||||
movb %al, 4(%bx)
|
||||
movb %ah, 7(%bx)
|
||||
roll $16, %eax
|
||||
ret
|
||||
|
||||
/****************************************************************************
|
||||
* real_to_prot (real-mode near call, 32-bit virtual return address)
|
||||
*
|
||||
* Switch from 16-bit real-mode to 32-bit protected mode with virtual
|
||||
* addresses. The real-mode %ss:sp is stored in rm_ss and rm_sp, and
|
||||
* the protected-mode %esp is restored from the saved pm_esp.
|
||||
* Interrupts are disabled. All other registers may be destroyed.
|
||||
*
|
||||
* The return address for this function should be a 32-bit virtual
|
||||
* address.
|
||||
*
|
||||
* Parameters:
|
||||
* %ecx : number of bytes to move from RM stack to PM stack
|
||||
*
|
||||
****************************************************************************
|
||||
*/
|
||||
.section ".text16", "ax", @progbits
|
||||
.code16
|
||||
real_to_prot:
|
||||
/* Enable A20 line */
|
||||
call enable_a20
|
||||
/* A failure at this point is fatal, and there's nothing we
|
||||
* can do about it other than lock the machine to make the
|
||||
* problem immediately visible.
|
||||
*/
|
||||
1: jc 1b
|
||||
|
||||
/* Make sure we have our data segment available */
|
||||
movw %cs:rm_ds, %ax
|
||||
movw %ax, %ds
|
||||
|
||||
/* Add virt_offset, text16 and data16 to stack to be
|
||||
* copied, and also copy the return address.
|
||||
*/
|
||||
pushl rm_virt_offset
|
||||
pushl rm_text16
|
||||
pushl rm_data16
|
||||
addw $16, %cx /* %ecx must be less than 64kB anyway */
|
||||
|
||||
/* Real-mode %ss:%sp => %ebp:%edx and virtual address => %esi */
|
||||
xorl %ebp, %ebp
|
||||
movw %ss, %bp
|
||||
movzwl %sp, %edx
|
||||
movl %ebp, %eax
|
||||
shll $4, %eax
|
||||
addr32 leal (%eax,%edx), %esi
|
||||
subl rm_virt_offset, %esi
|
||||
|
||||
/* Load protected-mode global descriptor table */
|
||||
data32 lgdt gdtr
|
||||
|
||||
/* Zero segment registers. This wastes around 12 cycles on
|
||||
* real hardware, but saves a substantial number of emulated
|
||||
* instructions under KVM.
|
||||
*/
|
||||
xorw %ax, %ax
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
movw %ax, %fs
|
||||
movw %ax, %gs
|
||||
movw %ax, %ss
|
||||
|
||||
/* Switch to protected mode */
|
||||
cli
|
||||
movl %cr0, %eax
|
||||
orb $CR0_PE, %al
|
||||
movl %eax, %cr0
|
||||
data32 ljmp $VIRTUAL_CS, $r2p_pmode
|
||||
.section ".text", "ax", @progbits
|
||||
.code32
|
||||
r2p_pmode:
|
||||
/* Set up protected-mode data segments and stack pointer */
|
||||
movw $VIRTUAL_DS, %ax
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
movw %ax, %fs
|
||||
movw %ax, %gs
|
||||
movw %ax, %ss
|
||||
movl pm_esp, %esp
|
||||
|
||||
/* Load protected-mode interrupt descriptor table */
|
||||
lidt idtr
|
||||
|
||||
/* Record real-mode %ss:sp (after removal of data) */
|
||||
movw %bp, rm_ss
|
||||
addl %ecx, %edx
|
||||
movw %dx, rm_sp
|
||||
|
||||
/* Move data from RM stack to PM stack */
|
||||
subl %ecx, %esp
|
||||
movl %esp, %edi
|
||||
rep movsb
|
||||
|
||||
/* Publish virt_offset, text16 and data16 for PM code to use */
|
||||
popl data16
|
||||
popl text16
|
||||
popl virt_offset
|
||||
|
||||
/* Return to virtual address */
|
||||
ret
|
||||
|
||||
/****************************************************************************
|
||||
* prot_to_real (protected-mode near call, 32-bit real-mode return address)
|
||||
*
|
||||
* Switch from 32-bit protected mode with virtual addresses to 16-bit
|
||||
* real mode. The protected-mode %esp is stored in pm_esp and the
|
||||
* real-mode %ss:sp is restored from the saved rm_ss and rm_sp. The
|
||||
* high word of the real-mode %esp is set to zero. All real-mode data
|
||||
* segment registers are loaded from the saved rm_ds. Interrupts are
|
||||
* *not* enabled, since we want to be able to use prot_to_real in an
|
||||
* ISR. All other registers may be destroyed.
|
||||
*
|
||||
* The return address for this function should be a 32-bit (sic)
|
||||
* real-mode offset within .code16.
|
||||
*
|
||||
* Parameters:
|
||||
* %ecx : number of bytes to move from PM stack to RM stack
|
||||
* %esi : real-mode global and interrupt descriptor table registers
|
||||
*
|
||||
****************************************************************************
|
||||
*/
|
||||
.section ".text", "ax", @progbits
|
||||
.code32
|
||||
prot_to_real:
|
||||
/* Copy real-mode global descriptor table register to RM code segment */
|
||||
movl text16, %edi
|
||||
leal rm_gdtr(%edi), %edi
|
||||
movsw
|
||||
movsl
|
||||
|
||||
/* Load real-mode interrupt descriptor table register */
|
||||
lidt (%esi)
|
||||
|
||||
/* Add return address to data to be moved to RM stack */
|
||||
addl $4, %ecx
|
||||
|
||||
/* Real-mode %ss:sp => %ebp:edx and virtual address => %edi */
|
||||
movzwl rm_ss, %ebp
|
||||
movzwl rm_sp, %edx
|
||||
subl %ecx, %edx
|
||||
movl %ebp, %eax
|
||||
shll $4, %eax
|
||||
leal (%eax,%edx), %edi
|
||||
subl virt_offset, %edi
|
||||
|
||||
/* Move data from PM stack to RM stack */
|
||||
movl %esp, %esi
|
||||
rep movsb
|
||||
|
||||
/* Record protected-mode %esp (after removal of data) */
|
||||
movl %esi, pm_esp
|
||||
|
||||
/* Load real-mode segment limits */
|
||||
movw $REAL_DS, %ax
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
movw %ax, %fs
|
||||
movw %ax, %gs
|
||||
movw %ax, %ss
|
||||
ljmp $REAL_CS, $p2r_rmode
|
||||
.section ".text16", "ax", @progbits
|
||||
.code16
|
||||
p2r_rmode:
|
||||
/* Load real-mode GDT */
|
||||
data32 lgdt %cs:rm_gdtr
|
||||
/* Switch to real mode */
|
||||
movl %cr0, %eax
|
||||
andb $0!CR0_PE, %al
|
||||
movl %eax, %cr0
|
||||
p2r_ljmp_rm_cs:
|
||||
ljmp $0, $1f
|
||||
1:
|
||||
/* Set up real-mode data segments and stack pointer */
|
||||
movw %cs:rm_ds, %ax
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
movw %ax, %fs
|
||||
movw %ax, %gs
|
||||
movw %bp, %ss
|
||||
movl %edx, %esp
|
||||
|
||||
/* Return to real-mode address */
|
||||
data32 ret
|
||||
|
||||
|
||||
/* Real-mode code and data segments. Assigned by the call to
|
||||
* init_librm. rm_cs doubles as the segment part of the jump
|
||||
* instruction used by prot_to_real. Both are located in
|
||||
* .text16 rather than .data16: rm_cs since it forms part of
|
||||
* the jump instruction within the code segment, and rm_ds
|
||||
* since real-mode code needs to be able to locate the data
|
||||
* segment with no other reference available.
|
||||
*/
|
||||
.globl rm_cs
|
||||
.equ rm_cs, ( p2r_ljmp_rm_cs + 3 )
|
||||
|
||||
.section ".text16.data", "aw", @progbits
|
||||
.globl rm_ds
|
||||
rm_ds: .word 0
|
||||
|
||||
/* Real-mode global and interrupt descriptor table registers */
|
||||
.section ".text16.data", "aw", @progbits
|
||||
rm_gdtr:
|
||||
.word 0 /* Limit */
|
||||
.long 0 /* Base */
|
||||
|
||||
/****************************************************************************
|
||||
* prot_call (real-mode far call, 16-bit real-mode far return address)
|
||||
*
|
||||
* Call a specific C function in the protected-mode code. The
|
||||
* prototype of the C function must be
|
||||
* void function ( struct i386_all_regs *ix86 );
|
||||
* ix86 will point to a struct containing the real-mode registers
|
||||
* at entry to prot_call.
|
||||
*
|
||||
* All registers will be preserved across prot_call(), unless the C
|
||||
* function explicitly overwrites values in ix86. Interrupt status
|
||||
* and GDT will also be preserved. Gate A20 will be enabled.
|
||||
*
|
||||
* Note that prot_call() does not rely on the real-mode stack
|
||||
* remaining intact in order to return, since everything relevant is
|
||||
* copied to the protected-mode stack for the duration of the call.
|
||||
* In particular, this means that a real-mode prefix can make a call
|
||||
* to main() which will return correctly even if the prefix's stack
|
||||
* gets vapourised during the Etherboot run. (The prefix cannot rely
|
||||
* on anything else on the stack being preserved, so should move any
|
||||
* critical data to registers before calling main()).
|
||||
*
|
||||
* Parameters:
|
||||
* function : virtual address of protected-mode function to call
|
||||
*
|
||||
* Example usage:
|
||||
* pushl $pxe_api_call
|
||||
* call prot_call
|
||||
* addw $4, %sp
|
||||
* to call in to the C function
|
||||
* void pxe_api_call ( struct i386_all_regs *ix86 );
|
||||
****************************************************************************
|
||||
*/
|
||||
|
||||
#define PC_OFFSET_GDT ( 0 )
|
||||
#define PC_OFFSET_IDT ( PC_OFFSET_GDT + 6 )
|
||||
#define PC_OFFSET_IX86 ( PC_OFFSET_IDT + 6 )
|
||||
#define PC_OFFSET_RETADDR ( PC_OFFSET_IX86 + SIZEOF_I386_ALL_REGS )
|
||||
#define PC_OFFSET_FUNCTION ( PC_OFFSET_RETADDR + 4 )
|
||||
#define PC_OFFSET_END ( PC_OFFSET_FUNCTION + 4 )
|
||||
|
||||
.section ".text16", "ax", @progbits
|
||||
.code16
|
||||
.globl prot_call
|
||||
prot_call:
|
||||
/* Preserve registers, flags and GDT on external RM stack */
|
||||
pushfl
|
||||
pushal
|
||||
pushw %gs
|
||||
pushw %fs
|
||||
pushw %es
|
||||
pushw %ds
|
||||
pushw %ss
|
||||
pushw %cs
|
||||
subw $PC_OFFSET_IX86, %sp
|
||||
movw %sp, %bp
|
||||
sidt PC_OFFSET_IDT(%bp)
|
||||
sgdt PC_OFFSET_GDT(%bp)
|
||||
|
||||
/* For sanity's sake, clear the direction flag as soon as possible */
|
||||
cld
|
||||
|
||||
/* Switch to protected mode and move register dump to PM stack */
|
||||
movl $PC_OFFSET_END, %ecx
|
||||
pushl $pc_pmode
|
||||
jmp real_to_prot
|
||||
.section ".text", "ax", @progbits
|
||||
.code32
|
||||
pc_pmode:
|
||||
/* Call function */
|
||||
leal PC_OFFSET_IX86(%esp), %eax
|
||||
pushl %eax
|
||||
call *(PC_OFFSET_FUNCTION+4)(%esp)
|
||||
popl %eax /* discard */
|
||||
|
||||
/* Switch to real mode and move register dump back to RM stack */
|
||||
movl $PC_OFFSET_END, %ecx
|
||||
movl %esp, %esi
|
||||
pushl $pc_rmode
|
||||
jmp prot_to_real
|
||||
.section ".text16", "ax", @progbits
|
||||
.code16
|
||||
pc_rmode:
|
||||
/* Restore registers and flags and return */
|
||||
addw $( PC_OFFSET_IX86 + 4 /* also skip %cs and %ss */ ), %sp
|
||||
popw %ds
|
||||
popw %es
|
||||
popw %fs
|
||||
popw %gs
|
||||
popal
|
||||
/* popal skips %esp. We therefore want to do "movl -20(%sp),
|
||||
* %esp", but -20(%sp) is not a valid 80386 expression.
|
||||
* Fortunately, prot_to_real() zeroes the high word of %esp, so
|
||||
* we can just use -20(%esp) instead.
|
||||
*/
|
||||
addr32 movl -20(%esp), %esp
|
||||
popfl
|
||||
lret
|
||||
|
||||
/****************************************************************************
|
||||
* real_call (protected-mode near call, 32-bit virtual return address)
|
||||
*
|
||||
* Call a real-mode function from protected-mode code.
|
||||
*
|
||||
* The non-segment register values will be passed directly to the
|
||||
* real-mode code. The segment registers will be set as per
|
||||
* prot_to_real. The non-segment register values set by the real-mode
|
||||
* function will be passed back to the protected-mode caller. A
|
||||
* result of this is that this routine cannot be called directly from
|
||||
* C code, since it clobbers registers that the C ABI expects the
|
||||
* callee to preserve.
|
||||
*
|
||||
* librm.h defines a convenient macro REAL_CODE() for using real_call.
|
||||
* See librm.h and realmode.h for details and examples.
|
||||
*
|
||||
* Parameters:
|
||||
* (32-bit) near pointer to real-mode function to call
|
||||
*
|
||||
* Returns: none
|
||||
****************************************************************************
|
||||
*/
|
||||
|
||||
#define RC_OFFSET_PRESERVE_REGS ( 0 )
|
||||
#define RC_OFFSET_RETADDR ( RC_OFFSET_PRESERVE_REGS + SIZEOF_I386_REGS )
|
||||
#define RC_OFFSET_FUNCTION ( RC_OFFSET_RETADDR + 4 )
|
||||
#define RC_OFFSET_END ( RC_OFFSET_FUNCTION + 4 )
|
||||
|
||||
.section ".text", "ax", @progbits
|
||||
.code32
|
||||
.globl real_call
|
||||
real_call:
|
||||
/* Create register dump and function pointer copy on PM stack */
|
||||
pushal
|
||||
pushl RC_OFFSET_FUNCTION(%esp)
|
||||
|
||||
/* Switch to real mode and move register dump to RM stack */
|
||||
movl $( RC_OFFSET_RETADDR + 4 /* function pointer copy */ ), %ecx
|
||||
pushl $rc_rmode
|
||||
movl $rm_default_gdtr_idtr, %esi
|
||||
jmp prot_to_real
|
||||
.section ".text16", "ax", @progbits
|
||||
.code16
|
||||
rc_rmode:
|
||||
/* Call real-mode function */
|
||||
popl rc_function
|
||||
popal
|
||||
call *rc_function
|
||||
pushal
|
||||
|
||||
/* For sanity's sake, clear the direction flag as soon as possible */
|
||||
cld
|
||||
|
||||
/* Switch to protected mode and move register dump back to PM stack */
|
||||
movl $RC_OFFSET_RETADDR, %ecx
|
||||
pushl $rc_pmode
|
||||
jmp real_to_prot
|
||||
.section ".text", "ax", @progbits
|
||||
.code32
|
||||
rc_pmode:
|
||||
/* Restore registers and return */
|
||||
popal
|
||||
ret
|
||||
|
||||
|
||||
/* Function vector, used because "call xx(%sp)" is not a valid
|
||||
* 16-bit expression.
|
||||
*/
|
||||
.section ".data16", "aw", @progbits
|
||||
rc_function: .word 0, 0
|
||||
|
||||
/* Default real-mode global and interrupt descriptor table registers */
|
||||
.section ".data", "aw", @progbits
|
||||
rm_default_gdtr_idtr:
|
||||
.word 0 /* Global descriptor table limit */
|
||||
.long 0 /* Global descriptor table base */
|
||||
.word 0x03ff /* Interrupt descriptor table limit */
|
||||
.long 0 /* Interrupt descriptor table base */
|
||||
|
||||
/****************************************************************************
|
||||
* flatten_real_mode (real-mode near call)
|
||||
*
|
||||
* Switch to flat real mode
|
||||
*
|
||||
****************************************************************************
|
||||
*/
|
||||
.section ".text16", "ax", @progbits
|
||||
.code16
|
||||
.globl flatten_real_mode
|
||||
flatten_real_mode:
|
||||
/* Modify GDT to use flat real mode */
|
||||
movb $0x8f, real_cs + 6
|
||||
movb $0x8f, real_ds + 6
|
||||
/* Call dummy protected-mode function */
|
||||
pushl $flatten_dummy
|
||||
pushw %cs
|
||||
call prot_call
|
||||
addw $4, %sp
|
||||
/* Restore GDT */
|
||||
movb $0x00, real_cs + 6
|
||||
movb $0x00, real_ds + 6
|
||||
/* Return */
|
||||
ret
|
||||
|
||||
.section ".text", "ax", @progbits
|
||||
.code32
|
||||
flatten_dummy:
|
||||
ret
|
||||
|
||||
/****************************************************************************
|
||||
* Interrupt wrapper
|
||||
*
|
||||
* Used by the protected-mode interrupt vectors to call the
|
||||
* interrupt() function.
|
||||
*
|
||||
* May be entered with either physical or virtual stack segment.
|
||||
****************************************************************************
|
||||
*/
|
||||
.globl interrupt_wrapper
|
||||
interrupt_wrapper:
|
||||
/* Preserve segment registers and original %esp */
|
||||
pushl %ds
|
||||
pushl %es
|
||||
pushl %fs
|
||||
pushl %gs
|
||||
pushl %ss
|
||||
pushl %esp
|
||||
|
||||
/* Switch to virtual addressing */
|
||||
call _intr_to_virt
|
||||
|
||||
/* Expand IRQ number to whole %eax register */
|
||||
movzbl %al, %eax
|
||||
|
||||
/* Call interrupt handler */
|
||||
call interrupt
|
||||
|
||||
/* Restore original stack and segment registers */
|
||||
lss (%esp), %esp
|
||||
popl %ss
|
||||
popl %gs
|
||||
popl %fs
|
||||
popl %es
|
||||
popl %ds
|
||||
|
||||
/* Restore registers and return */
|
||||
popal
|
||||
iret
|
||||
|
||||
/****************************************************************************
|
||||
* Stored real-mode and protected-mode stack pointers
|
||||
*
|
||||
* The real-mode stack pointer is stored here whenever real_to_prot
|
||||
* is called and restored whenever prot_to_real is called. The
|
||||
* converse happens for the protected-mode stack pointer.
|
||||
*
|
||||
* Despite initial appearances this scheme is, in fact re-entrant,
|
||||
* because program flow dictates that we always return via the point
|
||||
* we left by. For example:
|
||||
* PXE API call entry
|
||||
* 1 real => prot
|
||||
* ...
|
||||
* Print a text string
|
||||
* ...
|
||||
* 2 prot => real
|
||||
* INT 10
|
||||
* 3 real => prot
|
||||
* ...
|
||||
* ...
|
||||
* 4 prot => real
|
||||
* PXE API call exit
|
||||
*
|
||||
* At point 1, the RM mode stack value, say RPXE, is stored in
|
||||
* rm_ss,sp. We want this value to still be present in rm_ss,sp when
|
||||
* we reach point 4.
|
||||
*
|
||||
* At point 2, the RM stack value is restored from RPXE. At point 3,
|
||||
* the RM stack value is again stored in rm_ss,sp. This *does*
|
||||
* overwrite the RPXE that we have stored there, but it's the same
|
||||
* value, since the code between points 2 and 3 has managed to return
|
||||
* to us.
|
||||
****************************************************************************
|
||||
*/
|
||||
.section ".data", "aw", @progbits
|
||||
.globl rm_sp
|
||||
rm_sp: .word 0
|
||||
.globl rm_ss
|
||||
rm_ss: .word 0
|
||||
pm_esp: .long _estack
|
||||
|
||||
/****************************************************************************
|
||||
* Virtual address offsets
|
||||
*
|
||||
* These are used by the protected-mode code to map between virtual
|
||||
* and physical addresses, and to access variables in the .text16 or
|
||||
* .data16 segments.
|
||||
****************************************************************************
|
||||
*/
|
||||
/* Internal copies, created by init_librm (which runs in real mode) */
|
||||
.section ".data16", "aw", @progbits
|
||||
rm_virt_offset: .long 0
|
||||
rm_text16: .long 0
|
||||
rm_data16: .long 0
|
||||
|
||||
/* Externally-visible copies, created by real_to_prot */
|
||||
.section ".data", "aw", @progbits
|
||||
.globl virt_offset
|
||||
virt_offset: .long 0
|
||||
.globl text16
|
||||
text16: .long 0
|
||||
.globl data16
|
||||
data16: .long 0
|
||||
158
src/arch/x86/transitions/librm_mgmt.c
Normal file
158
src/arch/x86/transitions/librm_mgmt.c
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* librm: a library for interfacing to real-mode code
|
||||
*
|
||||
* Michael Brown <mbrown@fensystems.co.uk>
|
||||
*
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
#include <stdint.h>
|
||||
#include <ipxe/profile.h>
|
||||
#include <realmode.h>
|
||||
#include <pic8259.h>
|
||||
|
||||
/*
|
||||
* This file provides functions for managing librm.
|
||||
*
|
||||
*/
|
||||
|
||||
/** The interrupt wrapper */
|
||||
extern char interrupt_wrapper[];
|
||||
|
||||
/** The interrupt vectors */
|
||||
static struct interrupt_vector intr_vec[NUM_INT];
|
||||
|
||||
/** The interrupt descriptor table */
|
||||
struct interrupt_descriptor idt[NUM_INT] __attribute__ (( aligned ( 16 ) ));
|
||||
|
||||
/** The interrupt descriptor table register */
|
||||
struct idtr idtr = {
|
||||
.limit = ( sizeof ( idt ) - 1 ),
|
||||
};
|
||||
|
||||
/** Timer interrupt profiler */
|
||||
static struct profiler timer_irq_profiler __profiler = { .name = "irq.timer" };
|
||||
|
||||
/** Other interrupt profiler */
|
||||
static struct profiler other_irq_profiler __profiler = { .name = "irq.other" };
|
||||
|
||||
/**
|
||||
* Allocate space on the real-mode stack and copy data there from a
|
||||
* user buffer
|
||||
*
|
||||
* @v data User buffer
|
||||
* @v size Size of stack data
|
||||
* @ret sp New value of real-mode stack pointer
|
||||
*/
|
||||
uint16_t copy_user_to_rm_stack ( userptr_t data, size_t size ) {
|
||||
userptr_t rm_stack;
|
||||
rm_sp -= size;
|
||||
rm_stack = real_to_user ( rm_ss, rm_sp );
|
||||
memcpy_user ( rm_stack, 0, data, 0, size );
|
||||
return rm_sp;
|
||||
};
|
||||
|
||||
/**
|
||||
* Deallocate space on the real-mode stack, optionally copying back
|
||||
* data to a user buffer.
|
||||
*
|
||||
* @v data User buffer
|
||||
* @v size Size of stack data
|
||||
*/
|
||||
void remove_user_from_rm_stack ( userptr_t data, size_t size ) {
|
||||
if ( data ) {
|
||||
userptr_t rm_stack = real_to_user ( rm_ss, rm_sp );
|
||||
memcpy_user ( rm_stack, 0, data, 0, size );
|
||||
}
|
||||
rm_sp += size;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set interrupt vector
|
||||
*
|
||||
* @v intr Interrupt number
|
||||
* @v vector Interrupt vector, or NULL to disable
|
||||
*/
|
||||
void set_interrupt_vector ( unsigned int intr, void *vector ) {
|
||||
struct interrupt_descriptor *idte;
|
||||
|
||||
idte = &idt[intr];
|
||||
idte->segment = VIRTUAL_CS;
|
||||
idte->attr = ( vector ? ( IDTE_PRESENT | IDTE_TYPE_IRQ32 ) : 0 );
|
||||
idte->low = ( ( ( intptr_t ) vector ) & 0xffff );
|
||||
idte->high = ( ( ( intptr_t ) vector ) >> 16 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise interrupt descriptor table
|
||||
*
|
||||
*/
|
||||
void init_idt ( void ) {
|
||||
struct interrupt_vector *vec;
|
||||
unsigned int intr;
|
||||
|
||||
/* Initialise the interrupt descriptor table and interrupt vectors */
|
||||
for ( intr = 0 ; intr < NUM_INT ; intr++ ) {
|
||||
vec = &intr_vec[intr];
|
||||
vec->pushal = PUSHAL_INSN;
|
||||
vec->movb = MOVB_INSN;
|
||||
vec->intr = intr;
|
||||
vec->jmp = JMP_INSN;
|
||||
vec->offset = ( ( intptr_t ) interrupt_wrapper -
|
||||
( intptr_t ) vec->next );
|
||||
set_interrupt_vector ( intr, vec );
|
||||
}
|
||||
DBGC ( &intr_vec[0], "INTn vector at %p+%zxn (phys %#lx+%zxn)\n",
|
||||
intr_vec, sizeof ( intr_vec[0] ),
|
||||
virt_to_phys ( intr_vec ), sizeof ( intr_vec[0] ) );
|
||||
|
||||
/* Initialise the interrupt descriptor table register */
|
||||
idtr.base = virt_to_phys ( idt );
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine interrupt profiler (for debugging)
|
||||
*
|
||||
* @v intr Interrupt number
|
||||
* @ret profiler Profiler
|
||||
*/
|
||||
static struct profiler * interrupt_profiler ( int intr ) {
|
||||
|
||||
switch ( intr ) {
|
||||
case IRQ_INT ( 0 ) :
|
||||
return &timer_irq_profiler;
|
||||
default:
|
||||
return &other_irq_profiler;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interrupt handler
|
||||
*
|
||||
* @v intr Interrupt number
|
||||
*/
|
||||
void __attribute__ (( regparm ( 1 ) )) interrupt ( int intr ) {
|
||||
struct profiler *profiler = interrupt_profiler ( intr );
|
||||
uint32_t discard_eax;
|
||||
|
||||
/* Reissue interrupt in real mode */
|
||||
profile_start ( profiler );
|
||||
__asm__ __volatile__ ( REAL_CODE ( "movb %%al, %%cs:(1f + 1)\n\t"
|
||||
"\n1:\n\t"
|
||||
"int $0x00\n\t" )
|
||||
: "=a" ( discard_eax ) : "0" ( intr ) );
|
||||
profile_stop ( profiler );
|
||||
profile_exclude ( profiler );
|
||||
}
|
||||
|
||||
PROVIDE_UACCESS_INLINE ( librm, phys_to_user );
|
||||
PROVIDE_UACCESS_INLINE ( librm, user_to_phys );
|
||||
PROVIDE_UACCESS_INLINE ( librm, virt_to_user );
|
||||
PROVIDE_UACCESS_INLINE ( librm, user_to_virt );
|
||||
PROVIDE_UACCESS_INLINE ( librm, userptr_add );
|
||||
PROVIDE_UACCESS_INLINE ( librm, memcpy_user );
|
||||
PROVIDE_UACCESS_INLINE ( librm, memmove_user );
|
||||
PROVIDE_UACCESS_INLINE ( librm, memset_user );
|
||||
PROVIDE_UACCESS_INLINE ( librm, strlen_user );
|
||||
PROVIDE_UACCESS_INLINE ( librm, memchr_user );
|
||||
128
src/arch/x86/transitions/librm_test.c
Normal file
128
src/arch/x86/transitions/librm_test.c
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
|
||||
*
|
||||
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
* You can also choose to distribute this program under the terms of
|
||||
* the Unmodified Binary Distribution Licence (as given in the file
|
||||
* COPYING.UBDL), provided that you have satisfied its requirements.
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
/** @file
|
||||
*
|
||||
* Real mode transition self-tests
|
||||
*
|
||||
* This file allows for easy measurement of the time taken to perform
|
||||
* real mode transitions, which may have a substantial overhead when
|
||||
* running under a hypervisor.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Forcibly enable assertions */
|
||||
#undef NDEBUG
|
||||
|
||||
#include <ipxe/test.h>
|
||||
#include <ipxe/profile.h>
|
||||
#include <realmode.h>
|
||||
|
||||
/** Number of sample iterations for profiling */
|
||||
#define PROFILE_COUNT 4096
|
||||
|
||||
/** Protected-to-real mode transition profiler */
|
||||
static struct profiler p2r_profiler __profiler = { .name = "p2r" };
|
||||
|
||||
/** Real-to-protected mode transition profiler */
|
||||
static struct profiler r2p_profiler __profiler = { .name = "r2p" };
|
||||
|
||||
/** Real-mode call profiler */
|
||||
static struct profiler real_call_profiler __profiler = { .name = "real_call" };
|
||||
|
||||
/** Protected-mode call profiler */
|
||||
static struct profiler prot_call_profiler __profiler = { .name = "prot_call" };
|
||||
|
||||
/**
|
||||
* Dummy protected-mode function
|
||||
*/
|
||||
static void librm_test_prot_call ( void ) {
|
||||
/* Do nothing */
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform real mode transition self-tests
|
||||
*
|
||||
*/
|
||||
static void librm_test_exec ( void ) {
|
||||
unsigned int i;
|
||||
unsigned long timestamp;
|
||||
uint32_t timestamp_lo;
|
||||
uint32_t timestamp_hi;
|
||||
uint32_t started;
|
||||
uint32_t stopped;
|
||||
uint32_t discard_d;
|
||||
|
||||
/* Profile mode transitions. We want to profile each
|
||||
* direction of the transition separately, so perform an RDTSC
|
||||
* while in real mode and tweak the profilers' start/stop
|
||||
* times appropriately.
|
||||
*/
|
||||
for ( i = 0 ; i < PROFILE_COUNT ; i++ ) {
|
||||
profile_start ( &p2r_profiler );
|
||||
__asm__ __volatile__ ( REAL_CODE ( "rdtsc\n\t" )
|
||||
: "=a" ( timestamp_lo ),
|
||||
"=d" ( timestamp_hi )
|
||||
: );
|
||||
timestamp = timestamp_lo;
|
||||
if ( sizeof ( timestamp ) > sizeof ( timestamp_lo ) )
|
||||
timestamp |= ( ( ( uint64_t ) timestamp_hi ) << 32 );
|
||||
profile_start_at ( &r2p_profiler, timestamp );
|
||||
profile_stop ( &r2p_profiler );
|
||||
profile_stop_at ( &p2r_profiler, timestamp );
|
||||
}
|
||||
|
||||
/* Profile complete real-mode call cycle */
|
||||
for ( i = 0 ; i < PROFILE_COUNT ; i++ ) {
|
||||
profile_start ( &real_call_profiler );
|
||||
__asm__ __volatile__ ( REAL_CODE ( "" ) : : );
|
||||
profile_stop ( &real_call_profiler );
|
||||
}
|
||||
|
||||
/* Profile complete protected-mode call cycle */
|
||||
for ( i = 0 ; i < PROFILE_COUNT ; i++ ) {
|
||||
__asm__ __volatile__ ( REAL_CODE ( "rdtsc\n\t"
|
||||
"movl %k0, %k2\n\t"
|
||||
"pushl %k3\n\t"
|
||||
"pushw %%cs\n\t"
|
||||
"call prot_call\n\t"
|
||||
"addw $4, %%sp\n\t"
|
||||
"rdtsc\n\t" )
|
||||
: "=a" ( stopped ), "=d" ( discard_d ),
|
||||
"=R" ( started )
|
||||
: "i" ( librm_test_prot_call ) );
|
||||
profile_start_at ( &prot_call_profiler, started );
|
||||
profile_stop_at ( &prot_call_profiler, stopped );
|
||||
}
|
||||
}
|
||||
|
||||
/** Real mode transition self-test */
|
||||
struct self_test librm_test __self_test = {
|
||||
.name = "librm",
|
||||
.exec = librm_test_exec,
|
||||
};
|
||||
|
||||
REQUIRING_SYMBOL ( librm_test );
|
||||
REQUIRE_OBJECT ( test );
|
||||
Reference in New Issue
Block a user