From 04a99841e6f174233751eca7f57c336abe75a371 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 10 Apr 2005 15:51:10 +0000 Subject: [PATCH] Make prot_call() able to transparently return via the newly installed copy of librm. --- src/arch/i386/transitions/librm.S | 70 ++++++++++++++++++++------ src/arch/i386/transitions/librm_mgmt.c | 36 ++++++------- 2 files changed, 73 insertions(+), 33 deletions(-) diff --git a/src/arch/i386/transitions/librm.S b/src/arch/i386/transitions/librm.S index d0ff1f39..6e2f1229 100644 --- a/src/arch/i386/transitions/librm.S +++ b/src/arch/i386/transitions/librm.S @@ -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 diff --git a/src/arch/i386/transitions/librm_mgmt.c b/src/arch/i386/transitions/librm_mgmt.c index b03716a2..565a9a06 100644 --- a/src/arch/i386/transitions/librm_mgmt.c +++ b/src/arch/i386/transitions/librm_mgmt.c @@ -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();