diff -r fed20c1dc0ec xen/arch/x86/domain.c --- a/xen/arch/x86/domain.c Tue Sep 25 17:10:13 2007 +0100 +++ b/xen/arch/x86/domain.c Wed Sep 26 09:32:05 2007 +0100 @@ -155,9 +155,6 @@ int setup_arg_xlat_area(struct vcpu *v, clear_page(d->arch.mm_arg_xlat_l3); } - l4tab[l4_table_offset(COMPAT_ARG_XLAT_VIRT_BASE)] = - l4e_from_paddr(__pa(d->arch.mm_arg_xlat_l3), __PAGE_HYPERVISOR); - for ( i = 0; i < COMPAT_ARG_XLAT_PAGES; ++i ) { unsigned long va = COMPAT_ARG_XLAT_VIRT_START(v->vcpu_id) + i * PAGE_SIZE; @@ -227,11 +224,27 @@ static void release_arg_xlat_area(struct } } -static int setup_compat_l4(struct vcpu *v) -{ +static int setup_native_l4(struct vcpu *v, int do_xlat) +{ + struct domain *d = v->domain; + l4_pgentry_t *l4tab = (l4_pgentry_t *)__va(pagetable_get_paddr(v->arch.guest_table)); + int rc = 0; + + if ( do_xlat ) + rc = setup_arg_xlat_area(v, l4tab); + + l4tab[l4_table_offset(COMPAT_ARG_XLAT_VIRT_BASE)] = + l4e_from_paddr(__pa(d->arch.mm_arg_xlat_l3), __PAGE_HYPERVISOR); + + return rc; +} + +static int setup_compat_l4(struct vcpu *v, int do_xlat) +{ + struct domain *d = v->domain; struct page_info *pg = alloc_domheap_page(NULL); l4_pgentry_t *l4tab; - int rc; + int rc = 0; if ( pg == NULL ) return -ENOMEM; @@ -247,16 +260,16 @@ static int setup_compat_l4(struct vcpu * l4e_from_paddr(__pa(v->domain->arch.mm_perdomain_l3), __PAGE_HYPERVISOR); - if ( (rc = setup_arg_xlat_area(v, l4tab)) < 0 ) - { - free_domheap_page(pg); - return rc; - } + if ( do_xlat ) + rc = setup_arg_xlat_area(v, l4tab); + + l4tab[l4_table_offset(COMPAT_ARG_XLAT_VIRT_BASE)] = + l4e_from_paddr(__pa(d->arch.mm_arg_xlat_l3), __PAGE_HYPERVISOR); v->arch.guest_table = pagetable_from_page(pg); v->arch.guest_table_user = v->arch.guest_table; - return 0; + return rc; } static void release_compat_l4(struct vcpu *v) @@ -284,7 +297,6 @@ int switch_native(struct domain *d) return 0; d->arch.is_32bit_pv = d->arch.has_32bit_shinfo = 0; - release_arg_xlat_area(d); /* switch gdt */ gdt_l1e = l1e_from_page(virt_to_page(gdt_table), PAGE_HYPERVISOR); @@ -320,7 +332,7 @@ int switch_compat(struct domain *d) for ( vcpuid = 0; vcpuid < MAX_VIRT_CPUS; vcpuid++ ) { if ( (d->vcpu[vcpuid] != NULL) && - (setup_compat_l4(d->vcpu[vcpuid]) != 0) ) + (setup_compat_l4(d->vcpu[vcpuid], 0) != 0) ) goto undo_and_fail; d->arch.mm_perdomain_pt[((vcpuid << GDT_LDT_VCPU_SHIFT) + FIRST_RESERVED_GDT_PAGE)] = gdt_l1e; @@ -334,7 +346,6 @@ int switch_compat(struct domain *d) undo_and_fail: d->arch.is_32bit_pv = d->arch.has_32bit_shinfo = 0; - release_arg_xlat_area(d); gdt_l1e = l1e_from_page(virt_to_page(gdt_table), PAGE_HYPERVISOR); while ( vcpuid-- != 0 ) { @@ -348,7 +359,8 @@ int switch_compat(struct domain *d) #else #define release_arg_xlat_area(d) ((void)0) -#define setup_compat_l4(v) 0 +#define setup_native_l4(v,x) 0 +#define setup_compat_l4(v,x) 0 #define release_compat_l4(v) ((void)0) #endif @@ -393,7 +405,7 @@ int vcpu_initialise(struct vcpu *v) v->arch.perdomain_ptes = d->arch.mm_perdomain_pt + (v->vcpu_id << GDT_LDT_VCPU_SHIFT); - return (is_pv_32on64_vcpu(v) ? setup_compat_l4(v) : 0); + return (is_pv_32on64_vcpu(v) ? setup_compat_l4(v, 1) : setup_native_l4(v, 1)); } void vcpu_destroy(struct vcpu *v) @@ -1394,7 +1406,7 @@ unsigned long hypercall_create_continuat for ( i = 0; *p != '\0'; i++ ) mcs->call.args[i] = next_arg(p, args); - if ( is_pv_32on64_domain(current->domain) ) + if ( in_32on64_hypercall(current) ) { for ( ; i < 6; i++ ) mcs->call.args[i] = 0; @@ -1408,7 +1420,7 @@ unsigned long hypercall_create_continuat #ifdef __x86_64__ if ( !is_hvm_vcpu(current) ? - !is_pv_32on64_vcpu(current) : + !in_32on64_hypercall(current) : (hvm_guest_x86_mode(current) == 8) ) { for ( i = 0; *p != '\0'; i++ ) diff -r fed20c1dc0ec xen/arch/x86/domain_build.c --- a/xen/arch/x86/domain_build.c Tue Sep 25 17:10:13 2007 +0100 +++ b/xen/arch/x86/domain_build.c Wed Sep 26 09:32:05 2007 +0100 @@ -650,8 +650,8 @@ int __init construct_dom0( if ( is_pv_32on64_domain(d) ) { v->arch.guest_table_user = v->arch.guest_table; - if ( setup_arg_xlat_area(v, l4start) < 0 ) - panic("Not enough RAM for domain 0 hypercall argument translation.\n"); + l4tab[l4_table_offset(COMPAT_ARG_XLAT_VIRT_BASE)] = + l4e_from_paddr(__pa(v->domain->arch.mm_arg_xlat_l3), __PAGE_HYPERVISOR); } l4tab += l4_table_offset(v_start); diff -r fed20c1dc0ec xen/arch/x86/mm.c --- a/xen/arch/x86/mm.c Tue Sep 25 17:10:13 2007 +0100 +++ b/xen/arch/x86/mm.c Wed Sep 26 09:32:05 2007 +0100 @@ -1191,10 +1191,9 @@ static int alloc_l4_table(struct page_in pl4e[l4_table_offset(PERDOMAIN_VIRT_START)] = l4e_from_page(virt_to_page(d->arch.mm_perdomain_l3), __PAGE_HYPERVISOR); - if ( is_pv_32on64_domain(d) ) - pl4e[l4_table_offset(COMPAT_ARG_XLAT_VIRT_BASE)] = - l4e_from_page(virt_to_page(d->arch.mm_arg_xlat_l3), - __PAGE_HYPERVISOR); + pl4e[l4_table_offset(COMPAT_ARG_XLAT_VIRT_BASE)] = + l4e_from_page(virt_to_page(d->arch.mm_arg_xlat_l3), + __PAGE_HYPERVISOR); return 1; diff -r fed20c1dc0ec xen/arch/x86/x86_64/asm-offsets.c --- a/xen/arch/x86/x86_64/asm-offsets.c Tue Sep 25 17:10:13 2007 +0100 +++ b/xen/arch/x86/x86_64/asm-offsets.c Wed Sep 26 09:32:05 2007 +0100 @@ -116,6 +116,9 @@ void __dummy__(void) OFFSET(VCPU_vmx_cr2, struct vcpu, arch.hvm_vmx.cpu_cr2); BLANK(); + OFFSET(VCPU_in_32on64_hypercall, struct vcpu, arch.in_32on64_hypercall); + BLANK(); + OFFSET(DOMAIN_is_32bit_pv, struct domain, arch.is_32bit_pv); BLANK(); diff -r fed20c1dc0ec xen/arch/x86/x86_64/compat/entry.S --- a/xen/arch/x86/x86_64/compat/entry.S Tue Sep 25 17:10:13 2007 +0100 +++ b/xen/arch/x86/x86_64/compat/entry.S Wed Sep 26 09:32:05 2007 +0100 @@ -29,6 +29,7 @@ ENTRY(compat_hypercall) SAVE_ALL GET_CURRENT(%rbx) + movl $1,VCPU_in_32on64_hypercall(%rbx) cmpl $NR_hypercalls,%eax jae compat_bad_hypercall #ifndef NDEBUG @@ -77,6 +78,7 @@ compat_skip_clobber: compat_skip_clobber: #endif movl %eax,UREGS_rax(%rsp) # save the return value + movl $0,VCPU_in_32on64_hypercall(%rbx) /* %rbx: struct vcpu */ ENTRY(compat_test_all_events) diff -r fed20c1dc0ec xen/arch/x86/x86_64/entry.S --- a/xen/arch/x86/x86_64/entry.S Tue Sep 25 17:10:13 2007 +0100 +++ b/xen/arch/x86/x86_64/entry.S Wed Sep 26 09:32:05 2007 +0100 @@ -195,6 +195,65 @@ test_guest_events: movb $TBF_INTERRUPT,TRAPBOUNCE_flags(%rdx) call create_bounce_frame jmp test_all_events + + ALIGN +ENTRY(compat_privcmd) + pushq $0 + movl $0x83,4(%rsp) + SAVE_ALL + GET_CURRENT(%rbx) + + movl $1,VCPU_in_32on64_hypercall(%rbx) + cmpl $NR_hypercalls,%eax + jae bad_hypercall +#ifndef NDEBUG + /* Deliberately corrupt parameter regs not used by this hypercall. */ + pushq UREGS_rbx(%rsp); pushq %rcx; pushq %rdx; pushq %rsi; pushq %rdi + pushq UREGS_rbp+5*8(%rsp) + leaq compat_hypercall_args_table(%rip),%r10 + movq $6,%rcx + subb (%r10,%rax,1),%cl + movq %rsp,%rdi + movl $0xDEADBEEF,%eax + rep stosq + popq %r8 ; popq %r9 ; xchgl %r8d,%r9d /* Args 5&6: zero extend */ + popq %rdx; popq %rcx; xchgl %edx,%ecx /* Args 3&4: zero extend */ + popq %rdi; popq %rsi; xchgl %edi,%esi /* Args 1&2: zero extend */ + movl UREGS_rax(%rsp),%eax + pushq %rax + pushq UREGS_rip+8(%rsp) +#else + /* Relocate argument registers and zero-extend to 64 bits. */ + movl %eax,%eax /* Hypercall # */ + xchgl %ecx,%esi /* Arg 2, Arg 4 */ + movl %edx,%edx /* Arg 3 */ + movl %edi,%r8d /* Arg 5 */ + movl %ebp,%r9d /* Arg 6 */ + movl UREGS_rbx(%rsp),%edi /* Arg 1 */ +#endif + leaq compat_hypercall_table(%rip),%r10 + PERFC_INCR(PERFC_hypercalls, %rax, %rbx) + callq *(%r10,%rax,8) +#ifndef NDEBUG + /* Deliberately corrupt parameter regs used by this hypercall. */ + popq %r10 # Shadow RIP + cmpq %r10,UREGS_rip+8(%rsp) + popq %rcx # Shadow hypercall index + jne privcmd_skip_clobber /* If RIP has changed then don't clobber. */ + leaq compat_hypercall_args_table(%rip),%r10 + movb (%r10,%rcx,1),%cl + movl $0xDEADBEEF,%r10d + testb %cl,%cl; jz privcmd_skip_clobber; movl %r10d,UREGS_rbx(%rsp) + cmpb $2, %cl; jb privcmd_skip_clobber; movl %r10d,UREGS_rcx(%rsp) + cmpb $3, %cl; jb privcmd_skip_clobber; movl %r10d,UREGS_rdx(%rsp) + cmpb $4, %cl; jb privcmd_skip_clobber; movl %r10d,UREGS_rsi(%rsp) + cmpb $5, %cl; jb privcmd_skip_clobber; movl %r10d,UREGS_rdi(%rsp) + cmpb $6, %cl; jb privcmd_skip_clobber; movl %r10d,UREGS_rbp(%rsp) +privcmd_skip_clobber: +#endif + movl %eax,UREGS_rax(%rsp) # save the return value + movl $0,VCPU_in_32on64_hypercall(%rbx) + jmp test_all_events ALIGN /* %rbx: struct vcpu */ diff -r fed20c1dc0ec xen/arch/x86/x86_64/traps.c --- a/xen/arch/x86/x86_64/traps.c Tue Sep 25 17:10:13 2007 +0100 +++ b/xen/arch/x86/x86_64/traps.c Wed Sep 26 09:32:05 2007 +0100 @@ -23,6 +23,7 @@ asmlinkage void syscall_enter(void); asmlinkage void compat_hypercall(void); +asmlinkage void compat_privcmd(void); asmlinkage void int80_direct_trap(void); static void print_xen_info(void) @@ -303,6 +304,7 @@ void __init percpu_traps_init(void) * Also note that this is a trap gate, not an interrupt gate. */ _set_gate(idt_table+HYPERCALL_VECTOR, 15, 1, &compat_hypercall); + _set_gate(idt_table+0x83, 15, 3, &compat_privcmd); /* Fast trap for int80 (faster than taking the #GP-fixup path). */ _set_gate(idt_table+0x80, 15, 3, &int80_direct_trap); diff -r fed20c1dc0ec xen/include/asm-x86/config.h --- a/xen/include/asm-x86/config.h Tue Sep 25 17:10:13 2007 +0100 +++ b/xen/include/asm-x86/config.h Wed Sep 26 09:32:05 2007 +0100 @@ -237,7 +237,9 @@ extern unsigned char trampoline_cpu_star #endif -#define COMPAT_ARG_XLAT_VIRT_BASE (1UL << ROOT_PAGETABLE_SHIFT) +/* XLAT area actually covers 270-271 since we sometimes check p- + * but only actually access from p onwards. */ +#define COMPAT_ARG_XLAT_VIRT_BASE PML4_ADDR(271) #define COMPAT_ARG_XLAT_SHIFT 0 #define COMPAT_ARG_XLAT_PAGES (1U << COMPAT_ARG_XLAT_SHIFT) #define COMPAT_ARG_XLAT_SIZE (COMPAT_ARG_XLAT_PAGES << PAGE_SHIFT) diff -r fed20c1dc0ec xen/include/asm-x86/domain.h --- a/xen/include/asm-x86/domain.h Tue Sep 25 17:10:13 2007 +0100 +++ b/xen/include/asm-x86/domain.h Wed Sep 26 09:32:05 2007 +0100 @@ -17,6 +17,8 @@ #endif #define is_pv_32on64_vcpu(v) (is_pv_32on64_domain((v)->domain)) #define IS_COMPAT(d) (is_pv_32on64_domain(d)) + +#define in_32on64_hypercall(v) (v->arch.in_32on64_hypercall) struct trap_bounce { uint32_t error_code; @@ -320,6 +322,8 @@ struct arch_vcpu /* Guest-specified relocation of vcpu_info. */ unsigned long vcpu_info_mfn; + + int in_32on64_hypercall; } __cacheline_aligned; /* shorthands to improve code legibility */ diff -r fed20c1dc0ec xen/include/asm-x86/x86_64/uaccess.h --- a/xen/include/asm-x86/x86_64/uaccess.h Tue Sep 25 17:10:13 2007 +0100 +++ b/xen/include/asm-x86/x86_64/uaccess.h Wed Sep 26 09:32:07 2007 +0100 @@ -9,7 +9,9 @@ */ #define __addr_ok(addr) \ (((unsigned long)(addr) < (1UL<<48)) || \ - ((unsigned long)(addr) >= HYPERVISOR_VIRT_END)) + ((unsigned long)(addr) >= HYPERVISOR_VIRT_END) || \ + (((unsigned long)(addr) >= COMPAT_ARG_XLAT_VIRT_BASE - PML4_ENTRY_BYTES) && \ + ((unsigned long)(addr) < COMPAT_ARG_XLAT_VIRT_BASE + PML4_ENTRY_BYTES))) #define access_ok(addr, size) (__addr_ok(addr)) @@ -18,7 +20,9 @@ #ifdef CONFIG_COMPAT #define __compat_addr_ok(addr) \ - ((unsigned long)(addr) < HYPERVISOR_COMPAT_VIRT_START(current->domain)) + (is_pv_32bit_vcpu(current) \ + ? ((unsigned long)(addr) < HYPERVISOR_COMPAT_VIRT_START(current->domain)) \ + : __addr_ok(addr)) #define compat_access_ok(addr, size) \ __compat_addr_ok((unsigned long)(addr) + ((size) ? (size) - 1 : 0))