aboutsummaryrefslogtreecommitdiffstats
path: root/xen/arch/x86/domctl.c
diff options
context:
space:
mode:
authorJan Beulich <jbeulich@novell.com>2011-04-05 13:01:25 +0100
committerJan Beulich <jbeulich@novell.com>2011-04-05 13:01:25 +0100
commit9a70856bb28bb8c9b1d37fb8a005447ac77b0619 (patch)
treee03eabf8a03ef712e5b93a91d4b5e13923b0c4a4 /xen/arch/x86/domctl.c
parent4551775df58d42e2dcfd2a8ac4bcc713709e8b81 (diff)
downloadxen-9a70856bb28bb8c9b1d37fb8a005447ac77b0619.tar.gz
xen-9a70856bb28bb8c9b1d37fb8a005447ac77b0619.tar.bz2
xen-9a70856bb28bb8c9b1d37fb8a005447ac77b0619.zip
x86: split struct vcpu
This is accomplished by splitting the guest_context member, which by itself is larger than a page on x86-64. Quite a number of fields of this structure is completely meaningless for HVM guests, and thus a new struct pv_vcpu gets introduced, which is being overlaid with struct hvm_vcpu in struct arch_vcpu. The one member that is mostly responsible for the large size is trap_ctxt, which now gets allocated separately (unless fitting on the same page as struct arch_vcpu, as is currently the case for x86-32), and only for non-hvm, non-idle domains. This change pointed out a latent problem in arch_set_info_guest(), which is permitted to be called on already initialized vCPU-s, but so far copied the new state into struct arch_vcpu without (in this case) actually going through all the necessary accounting/validation steps. The logic gets changed so that the pieces that bypass accounting will at least be verified to be no different from the currently active bits, and the whole change will fail in case they are. The logic does *not* get adjusted here to do full error recovery, that is, partially modified state continues to not get unrolled in case of failure. Signed-off-by: Jan Beulich <jbeulich@novell.com>
Diffstat (limited to 'xen/arch/x86/domctl.c')
-rw-r--r--xen/arch/x86/domctl.c87
1 files changed, 70 insertions, 17 deletions
diff --git a/xen/arch/x86/domctl.c b/xen/arch/x86/domctl.c
index 3f91f7ac2e..6dec45ccb1 100644
--- a/xen/arch/x86/domctl.c
+++ b/xen/arch/x86/domctl.c
@@ -1469,7 +1469,7 @@ long arch_do_domctl(
}
offset += sizeof(v->arch.xcr0_accum);
if ( copy_to_guest_offset(domctl->u.vcpuextstate.buffer,
- offset, v->arch.xsave_area,
+ offset, (void *)v->arch.xsave_area,
xsave_cntxt_size) )
{
ret = -EFAULT;
@@ -1594,36 +1594,56 @@ long arch_do_domctl(
return ret;
}
+#ifdef CONFIG_COMPAT
+#define xen_vcpu_guest_context vcpu_guest_context
+#define fpu_ctxt fpu_ctxt.x
+CHECK_FIELD_(struct, vcpu_guest_context, fpu_ctxt);
+#undef fpu_ctxt
+#undef xen_vcpu_guest_context
+#endif
+
void arch_get_info_guest(struct vcpu *v, vcpu_guest_context_u c)
{
+ unsigned int i;
+ bool_t compat = is_pv_32on64_domain(v->domain);
#ifdef CONFIG_COMPAT
-#define c(fld) (!is_pv_32on64_domain(v->domain) ? (c.nat->fld) : (c.cmp->fld))
+#define c(fld) (!compat ? (c.nat->fld) : (c.cmp->fld))
#else
#define c(fld) (c.nat->fld)
#endif
- /* Fill legacy context from xsave area first */
- if ( xsave_enabled(v) )
- memcpy(v->arch.xsave_area, &v->arch.guest_context.fpu_ctxt,
- sizeof(v->arch.guest_context.fpu_ctxt));
-
- if ( !is_pv_32on64_domain(v->domain) )
- memcpy(c.nat, &v->arch.guest_context, sizeof(*c.nat));
-#ifdef CONFIG_COMPAT
- else
- XLAT_vcpu_guest_context(c.cmp, &v->arch.guest_context);
-#endif
-
- c(flags &= ~(VGCF_i387_valid|VGCF_in_kernel));
+ if ( is_hvm_vcpu(v) )
+ memset(c.nat, 0, sizeof(*c.nat));
+ memcpy(&c.nat->fpu_ctxt, v->arch.fpu_ctxt, sizeof(c.nat->fpu_ctxt));
+ c(flags = v->arch.vgc_flags & ~(VGCF_i387_valid|VGCF_in_kernel));
if ( v->fpu_initialised )
c(flags |= VGCF_i387_valid);
if ( !test_bit(_VPF_down, &v->pause_flags) )
c(flags |= VGCF_online);
+ if ( !compat )
+ {
+ memcpy(&c.nat->user_regs, &v->arch.user_regs, sizeof(c.nat->user_regs));
+ if ( !is_hvm_vcpu(v) )
+ memcpy(c.nat->trap_ctxt, v->arch.pv_vcpu.trap_ctxt,
+ sizeof(c.nat->trap_ctxt));
+ }
+#ifdef CONFIG_COMPAT
+ else
+ {
+ XLAT_cpu_user_regs(&c.cmp->user_regs, &v->arch.user_regs);
+ for ( i = 0; i < ARRAY_SIZE(c.cmp->trap_ctxt); ++i )
+ XLAT_trap_info(c.cmp->trap_ctxt + i,
+ v->arch.pv_vcpu.trap_ctxt + i);
+ }
+#endif
+
+ for ( i = 0; i < ARRAY_SIZE(v->arch.debugreg); ++i )
+ c(debugreg[i] = v->arch.debugreg[i]);
if ( is_hvm_vcpu(v) )
{
struct segment_register sreg;
- memset(c.nat->ctrlreg, 0, sizeof(c.nat->ctrlreg));
+
c.nat->ctrlreg[0] = v->arch.hvm_vcpu.guest_cr[0];
c.nat->ctrlreg[2] = v->arch.hvm_vcpu.guest_cr[2];
c.nat->ctrlreg[3] = v->arch.hvm_vcpu.guest_cr[3];
@@ -1643,6 +1663,39 @@ void arch_get_info_guest(struct vcpu *v, vcpu_guest_context_u c)
}
else
{
+ c(ldt_base = v->arch.pv_vcpu.ldt_base);
+ c(ldt_ents = v->arch.pv_vcpu.ldt_ents);
+ for ( i = 0; i < ARRAY_SIZE(v->arch.pv_vcpu.gdt_frames); ++i )
+ c(gdt_frames[i] = v->arch.pv_vcpu.gdt_frames[i]);
+#ifdef CONFIG_COMPAT
+ BUILD_BUG_ON(ARRAY_SIZE(c.nat->gdt_frames) !=
+ ARRAY_SIZE(c.cmp->gdt_frames));
+#endif
+ for ( ; i < ARRAY_SIZE(c.nat->gdt_frames); ++i )
+ c(gdt_frames[i] = 0);
+ c(gdt_ents = v->arch.pv_vcpu.gdt_ents);
+ c(kernel_ss = v->arch.pv_vcpu.kernel_ss);
+ c(kernel_sp = v->arch.pv_vcpu.kernel_sp);
+ for ( i = 0; i < ARRAY_SIZE(v->arch.pv_vcpu.ctrlreg); ++i )
+ c(ctrlreg[i] = v->arch.pv_vcpu.ctrlreg[i]);
+ c(event_callback_eip = v->arch.pv_vcpu.event_callback_eip);
+ c(failsafe_callback_eip = v->arch.pv_vcpu.failsafe_callback_eip);
+#ifdef CONFIG_X86_64
+ if ( !compat )
+ {
+ c.nat->syscall_callback_eip = v->arch.pv_vcpu.syscall_callback_eip;
+ c.nat->fs_base = v->arch.pv_vcpu.fs_base;
+ c.nat->gs_base_kernel = v->arch.pv_vcpu.gs_base_kernel;
+ c.nat->gs_base_user = v->arch.pv_vcpu.gs_base_user;
+ }
+ else
+#endif
+ {
+ c(event_callback_cs = v->arch.pv_vcpu.event_callback_cs);
+ c(failsafe_callback_cs = v->arch.pv_vcpu.failsafe_callback_cs);
+ }
+ c(vm_assist = v->arch.pv_vcpu.vm_assist);
+
/* IOPL privileges are virtualised: merge back into returned eflags. */
BUG_ON((c(user_regs.eflags) & X86_EFLAGS_IOPL) != 0);
c(user_regs.eflags |= v->arch.iopl << 12);
@@ -1673,7 +1726,7 @@ void arch_get_info_guest(struct vcpu *v, vcpu_guest_context_u c)
}
#endif
- if ( guest_kernel_mode(v, &v->arch.guest_context.user_regs) )
+ if ( guest_kernel_mode(v, &v->arch.user_regs) )
c(flags |= VGCF_in_kernel);
}