[RFC, PATCH 5/24] i386 Vmi code patching

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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());
}


[Index of Archives]     [KVM Development]     [Libvirt Development]     [Libvirt Users]     [CentOS Virtualization]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite Forum]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux