xen的超级调用

超级调用是特权操作,有xen来实现.采用的是软中断的方式.它是整个xen的基础.

具体的实现是首先我们有一些超级调用的处理函数,和他们对应的超级调用号,有一个索引文件来记录他们的对应关系.

超级调用发生在guest的内核层,某一个函数如果需要使用超级调用,则会先跳转到超级调用页的相应地方,通过int82h 内陷进入xen中,搜索索引来跳转到超级调用的处理程序.

如果是guest的用户态,必须先进入内核态,通过调用内核态的函数来调用超级调用.

通过xen4.4的代码来分析一下这个过程:

在Hypercall-x86_64.h中记录了guest中内核超级调用函数

static inline int
HYPERVISOR_set_trap_table(
	trap_info_t *table)
{
	return _hypercall1(int, set_trap_table, table);
}

这个超级调用调用了_hypercall1   (和上面文件的地方相同)

#define _hypercall1(type, name, a1)				\
({								\
	long __res, __ign1;					\
	asm volatile (						\
		"call hypercall_page + ("STR(__HYPERVISOR_##name)" * 32)"\
		: "=a" (__res), "=D" (__ign1)			\
		: "1" ((long)(a1))				\
		: "memory" );					\
	(type)__res;						\
})

_hypercallN其实都是跳转到超级调用对应的

超级调用页的初始化(xen/arch/x86/x86_64/traps.c)

void hypercall_page_initialise(struct domain *d, void *hypercall_page)
{
    memset(hypercall_page, 0xCC, PAGE_SIZE);
    if ( has_hvm_container_domain(d) )
        hvm_hypercall_page_initialise(d, hypercall_page);
    else if ( !is_pv_32bit_domain(d) )
        hypercall_page_initialise_ring3_kernel(hypercall_page);
    else
        hypercall_page_initialise_ring1_kernel(hypercall_page);
}
超级调用页中写的一些什么呢,我们看其中一个

static void hypercall_page_initialise_ring3_kernel(void *hypercall_page)
{
    char *p;
    int i;

    /* Fill in all the transfer points with template machine code. */
    for ( i = 0; i < (PAGE_SIZE / 32); i++ )
    {
        if ( i == __HYPERVISOR_iret )
            continue;

        p = (char *)(hypercall_page + (i * 32));
        *(u8  *)(p+ 0) = 0x51;    /* push %rcx */
        *(u16 *)(p+ 1) = 0x5341;  /* push %r11 */
        *(u8  *)(p+ 3) = 0xb8;    /* mov  $<i>,%eax */
        *(u32 *)(p+ 4) = i;
        *(u16 *)(p+ 8) = 0x050f;  /* syscall */
        *(u16 *)(p+10) = 0x5b41;  /* pop  %r11 */
        *(u8  *)(p+12) = 0x59;    /* pop  %rcx */
        *(u8  *)(p+13) = 0xc3;    /* ret */
    }

    /*
     * HYPERVISOR_iret is special because it doesn't return and expects a
     * special stack frame. Guests jump at this transfer point instead of
     * calling it.
     */
    p = (char *)(hypercall_page + (__HYPERVISOR_iret * 32));
    *(u8  *)(p+ 0) = 0x51;    /* push %rcx */
    *(u16 *)(p+ 1) = 0x5341;  /* push %r11 */
    *(u8  *)(p+ 3) = 0x50;    /* push %rax */
    *(u8  *)(p+ 4) = 0xb8;    /* mov  $__HYPERVISOR_iret,%eax */
    *(u32 *)(p+ 5) = __HYPERVISOR_iret;
    *(u16 *)(p+ 9) = 0x050f;  /* syscall */
}

直接用机器码写的指令,然后其中会用syscall(和int82h可以认为是一样)来进入xen中.这样才可以执行这些特权指令.这是规范,出于安全的考虑.

xen先保存内核的状态,然后根据索引来跳转到的不同的超级调用的处理函数.

索引文件 xen/arch/x86/x86_64/entry.S

ENTRY(hypercall_table)
        .quad do_set_trap_table     /*  0 */
        .quad do_mmu_update
        .quad do_set_gdt
        .quad do_stack_switch
        .quad do_set_callbacks
        .quad do_fpu_taskswitch     /*  5 */
        .quad do_sched_op_compat
        .quad do_platform_op
        .quad do_set_debugreg
        .quad do_get_debugreg
        .quad do_update_descriptor  /* 10 */
        .quad do_ni_hypercall
        .quad do_memory_op
        .quad do_multicall
        .quad do_update_va_mapping
        .quad do_set_timer_op       /* 15 */
        .quad do_event_channel_op_compat

具体的处理函数在xen/arch/x86/traps.c中

long do_set_trap_table(XEN_GUEST_HANDLE_PARAM(const_trap_info_t) traps)
{
    struct trap_info cur;
    struct vcpu *curr = current;
    struct trap_info *dst = curr->arch.pv_vcpu.trap_ctxt;
    long rc = 0;

    /* If no table is presented then clear the entire virtual IDT. */
    if ( guest_handle_is_null(traps) )
    {
        memset(dst, 0, NR_VECTORS * sizeof(*dst));
        init_int80_direct_trap(curr);
        return 0;
    }

    for ( ; ; )
    {
        if ( hypercall_preempt_check() )
        {
            rc = hypercall_create_continuation(
                __HYPERVISOR_set_trap_table, "h", traps);
            break;
        }

        if ( copy_from_guest(&cur, traps, 1) )
        {
            rc = -EFAULT;
            break;
        }

        if ( cur.address == 0 )
            break;

        if ( !is_canonical_address(cur.address) )
            return -EINVAL;

        fixup_guest_code_selector(curr->domain, cur.cs);

        memcpy(&dst[cur.vector], &cur, sizeof(cur));

        if ( cur.vector == 0x80 )
            init_int80_direct_trap(curr);

        guest_handle_add_offset(traps, 1);
    }

    return rc;
}

这是超级调用的整个过程,整个过程中唯一一点不清楚的是syscall进入xen中的汇编语言 xen/arch/x86/x86_64/entry.S(姑且认为就是保存了内核状态,并跳转)

这些知识只是在更改超级调用的时候才会用到,超级调用已经成为了xen的一种机制,提供特权操作的机制.我们可以再别的地方任意使用,不考虑其中的细节.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值