david/ipxe
david
/
ipxe
Archived
1
0
Fork 0

Make prot_call() able to transparently return via the newly installed copy

of librm.
This commit is contained in:
Michael Brown 2005-04-10 15:51:10 +00:00
parent 44eee81d11
commit 04a99841e6
2 changed files with 73 additions and 33 deletions

View File

@ -144,7 +144,17 @@ _librm_start:
****************************************************************************
*/
.fill FREE_BASEMEM_HEADER_SIZE, 1, 0
/****************************************************************************
* Record of the current physical location of the installed copy.
* Used by prot_call in order to return via the current installed copy
* even if Etherboot has been relocated during the protected-mode
* call.
****************************************************************************
*/
EXPORT(librm_base):
librm_base: .long 0
/****************************************************************************
* GDT for initial transition to protected mode
*
@ -266,6 +276,9 @@ EXPORT(real_to_prot):
xorl %ebx, %ebx
movw %cs, %bx
shll $4, %ebx
/* Record physical base address of librm */
movl %ebx, %ds:OFFSET(librm_base)
/* Check base address of stored protected-mode GDT. If it's
* zero, set it up to use our internal GDT (with physical
@ -360,6 +373,9 @@ EXPORT(prot_to_real):
popl OFFSET(save_ebx)(%ebx)
movl %eax, OFFSET(save_eax)(%ebx)
/* Record physical base address of librm */
movl %ebx, OFFSET(librm_base)(%ebx)
/* Extract return address from the stack, convert to offset
* within librm and save in save_retaddr
*/
@ -445,22 +461,32 @@ p2r_ljmp:
*
* Call a specific C function in the protected-mode code. The
* prototype of the C function must be
* void function ( struct real_mode_regs *rm_regs,
* void (*retaddr) (void) );
* void function ( struct real_mode_regs *rm_regs );
* rm_regs will point to a struct containing the real-mode registers
* at entry to prot_call. retaddr will point to the (virtual) return
* address from "function". This return address will point into
* librm. It is included so that "function" may, if desired, relocate
* librm and return via the new copy. It must not be directly called
* as a function, i.e. you may not do "*retaddr()"; you must instead
* do something like:
* *retaddr += ( new_librm_location - old_librm_location );
* return;
* at entry to prot_call.
*
* All registers will be preserved across prot_call(), unless the C
* function explicitly overwrites values in rm_regs. Interrupt status
* will also be preserved. Gate A20 will be enabled.
*
* The protected-mode code may install librm to a new location. If it
* does so, it must update librm_base in *this* copy of librm to point
* to the new physical location. prot_call will then return via the
* newly installed copy.
*
* Note that when Etherboot performs its initial relocation, "*this*"
* copy in the above paragraph will refer to the "master" copy, since
* that is the initial installed copy. Etherboot will return to
* prot_call using a virtual address, so will return to the master
* copy in high memory (rather than the original copy in base memory).
* The master copy in high memory will have the physical address of
* the newly installed copy in librm_base, since install_librm()
* writes it there. Thus, Etherboot's initialise() function will
* return to the master copy of prot_call(), which will then jump to
* the installed copy.
*
* It works, trust me.
*
* Parameters:
* function : virtual address of protected-mode function to call
*
@ -529,14 +555,10 @@ EXPORT(prot_call):
popl %eax /* discard */
popal
/* Push &rm_regs and &retaddr on the stack, and call function */
movl %esp, %ebp
/* Push &rm_regs on the stack, and call function */
pushl %esp
subl $12, 0(%esp)
pushl %ebp
call *%ebx
popl %eax /* discard */
popl %eax /* discard */
/* Switch to physical addresses, discard PM register store */
lcall $VIRTUAL_CS, $_virt_to_phys
@ -553,6 +575,22 @@ EXPORT(prot_call):
rep movsb
movl %esi, %esp /* remove rm_regs from PM stack */
/* Obtain physical base address of installed copy of librm in
* %ebx. (It's possible that this *isn't* the physical base
* address of the copy we're currently executing in, because
* the protected-mode call could have moved librm. If it does
* so, it must update librm_base in our copy to reflect the
* new location.
*/
call 1f
1: popl %ebp
movl OFFSET(librm_base-1b)(%ebp), %ebx
/* Jump to running in installed copy of librm */
addl $OFFSET(1f), %ebx
jmp *%ebx
1:
/* Switch to real mode */
call prot_to_real
.code16

View File

@ -23,7 +23,6 @@
/* Current location of librm in base memory */
char *installed_librm = librm;
static uint32_t installed_librm_phys;
/* Whether or not we have base memory currently allocated for librm.
* Note that we *can* have a working librm present in unallocated base
@ -67,7 +66,8 @@ void remove_from_rm_stack ( void *data, size_t size ) {
* Install librm to base memory
*
*/
static inline void install_librm ( char *addr ) {
static void install_librm ( char *addr ) {
librm_base = virt_to_phys ( addr );
memcpy ( addr, librm, librm_size );
installed_librm = addr;
}
@ -79,21 +79,25 @@ static inline void install_librm ( char *addr ) {
* copy.
*
*/
static inline void uninstall_librm ( void ) {
static void uninstall_librm ( void ) {
memcpy ( librm, installed_librm, librm_size );
librm_base = 0;
}
/*
* On entry, record the physical location of librm. Do this so that
* we can update installed_librm after relocation.
*
* Doing this is probably more efficient than making installed_librm
* be a physical address, because of the number of times that
* installed_librm gets referenced in the remainder of the code.
* If librm isn't installed (i.e. if we have librm, but weren't
* entered via it), then install librm and a real-mode stack to a
* fixed temporary location, just so that we can e.g. issue printf()
*
* [ If we were entered via librm, then the real_to_prot call will
* have filled in librm_base. ]
*/
static void librm_init ( void ) {
installed_librm_phys = virt_to_phys ( installed_librm );
if ( ! librm_base ) {
install_librm ( phys_to_virt ( 0x7c00 ) );
inst_rm_stack.segment = 0x7c0;
inst_rm_stack.offset = 0x1000;
}
}
/*
@ -112,19 +116,17 @@ static void librm_exit ( void ) {
}
/*
* On reset, we want to free up our old installed copy of librm, if
* any, then allocate a new base memory block and install there.
* Reset gets called immediately after relocation.
*
*/
static void librm_reset ( void ) {
char *new_librm;
/* Point installed_librm back at last known physical location */
installed_librm = phys_to_virt ( installed_librm_phys );
/* Uninstall old librm */
uninstall_librm();
/* Point installed_librm back at last known physical location.
* Do this in case we have just relocated and the virtual
* address has therefore changed. */
installed_librm = phys_to_virt ( librm_base );
/* Free allocated base memory, if applicable */
librm_exit();