Re: [RFC, PATCH 5/24] i386 Vmi code patching
From: Joshua LeVasseur
Date: Fri Mar 17 2006 - 05:07:59 EST
On Mar 16, 2006, at 00:00 , Pavel Machek wrote:
The question of licensing of such ROM code is a completely separate
issue. We are not trying to hide some proprietary code by putting it
inside of a ROM to keep it hidden. In fact, you can disassemble the
ROM code and see it quite readily - and you know all of the entry
points.
Could you disassemble one entry point for us and describe how it
works?
Here is how I construct my ROM. My apologies if my email client
wraps any of the lines.
I have a set of boundary functions between assembler and C code,
identified by a suffix on the function names: _ext (stands for
"extended" in response to the evolution of my ROM). They accept a
parameter called burn_clobbers_frame_t:
struct burn_clobbers_frame_t {
word_t burn_ret_address; /* Return address to the ROM */
word_t frame_pointer; /* This parameter. */
word_t edx;
word_t ecx;
word_t eax;
word_t guest_ret_address;
word_t params[0]; /* Anything the guest pushed on the stack */
};
I use this structure for the non-performance critical functions.
Here are some of the assembler macros for constructing the ROM:
vmi_stub_simple WRMSR, afterburn_cpu_write_msr_ext
vmi_stub_simple RDMSR, afterburn_cpu_read_msr_ext
vmi_stub_simple SetGDT, afterburn_cpu_write_gdt32_ext
vmi_stub_simple SetLDT, afterburn_cpu_lldt_ext
vmi_stub_simple SetIDT, afterburn_cpu_write_idt32_ext
vmi_stub_simple SetTR, afterburn_cpu_ltr_ext
macro vmi_stub_simple name func
/* Invoke a front-end C function that expects a burn_clobbers_frame_t as
* the parameter.
*/
vmi_stub_begin \name
__afterburn_save_clobbers
push %esp
subl $8, 0(%esp)
vmi_call_relocatable \func
__afterburn_restore_clobbers 4
ret
vmi_stub_end
.endm
.macro vmi_stub_begin name
/* Define the prolog of a VMI stub. */
vmi_area_begin \name
burn_counter VMI_\name
burn_counter_inc VMI_\name
.endm
.macro vmi_stub_end
/* Define the epilog of a VMI stub. */
.previous
.endm
.macro vmi_area_begin name
/* Define the start of a VMI area. */
.section .vmi_rom, "ax"
.org vmi_rom_offset_\name
.globl VMI_\name
.type VMI_\name,@function
VMI_\name:
.endm
.macro vmi_call_relocatable func
9191: call \func
.pushsection .vmi_patchups, "aw"
.balign 4
.long 9191b
.popsection
.endm
.macro vmi_jmp_relocatable target
9191: jmp \target
.pushsection .vmi_patchups, "aw"
.balign 4
.long 9191b
.popsection
.endm
extern "C" void
afterburn_cpu_write_gdt32_ext( burn_clobbers_frame_t *frame )
{
get_cpu()->gdtr = *(dtr_t *)frame->eax;
}
The ROM entry points are only half the solution. The other half
involves Xen callbacks and traps. The system call trap is
constructed to directly activate Linux's system call trap.
Everything else jumps directly into the ROM for filtering reasons.
The page-fault handler stays in assembler (because page faults are a
performance issue; on a linux kernel build, they occur almost as
frequently as system calls). The remaining traps enter C code, and
look like this:
trap_wrapper id=8, use_error_code=1 /* Double fault exception */
trap_wrapper id=9, use_error_code=0 /* Coprocessor segment
overrun */
trap_wrapper id=10, use_error_code=1 /* Invalid TSS exception */
trap_wrapper id=11, use_error_code=1 /* Segment not present */
trap_wrapper id=12, use_error_code=1 /* Stack fault exception */
trap_wrapper id=13, use_error_code=1 /* general protection fault */
.macro trap_wrapper id, use_error_code
entry trap_wrapper_\id
.if \use_error_code
subl $4, %esp /* Fault addr. */
.else
subl $8, %esp /* Error code, fault addr. */
.endif
pushl $(\id | (\use_error_code << 31)) /* Frame ID. */
cpu_save_all
movl %esp, %eax /* A pointer to the CPU save frame. */
call trap
jmp afterburn_exit
.endm
entry afterburn_exit
cpu_restore_all 0, 12 /* Error code, fault addr, frame id. */
iret
extern "C" void __attribute__(( regparm(1) ))
trap( xen_frame_t *frame )
{
if( EXPECT_FALSE(frame->iret.ip >= CONFIG_WEDGE_VIRT) ) {
con << "Unexpected fault in the ROM, ip " << (void *)frame-
>iret.ip
<< '\n';
DEBUGGER_ENTER(frame);
panic();
}
u8_t *opstream = (u8_t *)frame->iret.ip;
if( cpu_t::get_segment_privilege(frame->iret.cs) == 3 )
{
// A user-level fault.
... check for TLS issues ...
}
// Update virtual CPU state, and deliver the trap to the guest
kernel.
xen_deliver_async_vector( frame->get_id(), frame,
frame->uses_error_code());
}
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/