diff options
author | Michael Chapman <mike@very.puzzling.org> | 2015-12-07 15:54:07 +1100 |
---|---|---|
committer | Paolo Bonzini <pbonzini@redhat.com> | 2015-12-17 17:33:49 +0100 |
commit | 4cae9c97967a0e6311858285a30c44208210b277 (patch) | |
tree | 5c845d41469b338e8f547be95b07130a4f9ca831 /target-i386/kvm.c | |
parent | f6d153f1bfc7839555309c6e090c6f20c1ce3c0e (diff) | |
download | qemu-4cae9c97967a0e6311858285a30c44208210b277.tar.gz qemu-4cae9c97967a0e6311858285a30c44208210b277.tar.bz2 qemu-4cae9c97967a0e6311858285a30c44208210b277.zip |
target-i386: kvm: clear unusable segments' flags in migration
This commit fixes migration of a QEMU/KVM guest from kernel >= v3.9 to
kernel <= v3.7 (e.g. from RHEL 7 to RHEL 6). Without this commit a guest
migrated across these kernel versions fails to resume on the target host
as its segment descriptors are invalid.
Two separate kernel commits combined together to result in this bug:
commit f0495f9b9992f80f82b14306946444b287193390
Author: Avi Kivity <avi@redhat.com>
Date: Thu Jun 7 17:06:10 2012 +0300
KVM: VMX: Relax check on unusable segment
Some userspace (e.g. QEMU 1.1) munge the d and g bits of segment
descriptors, causing us not to recognize them as unusable segments
with emulate_invalid_guest_state=1. Relax the check by testing for
segment not present (a non-present segment cannot be usable).
Signed-off-by: Avi Kivity <avi@redhat.com>
commit 25391454e73e3156202264eb3c473825afe4bc94
Author: Gleb Natapov <gleb@redhat.com>
Date: Mon Jan 21 15:36:46 2013 +0200
KVM: VMX: don't clobber segment AR of unusable segments.
Usability is returned in unusable field, so not need to clobber entire
AR. Callers have to know how to deal with unusable segments already
since if emulate_invalid_guest_state=true AR is not zeroed.
Signed-off-by: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
The first commit changed the KVM_SET_SREGS ioctl so that it did no treat
segment flags == 0 as an unusable segment, instead only looking at the
"present" flag.
The second commit changed KVM_GET_SREGS so that it did not clear the
flags of an unusable segment.
Since QEMU does not itself maintain the "unusable" flag across a
migration, the end result is that unusable segments read from a kernel
with these commits and loaded into a kernel without these commits are
not properly recognised as being unusable.
This commit updates both get_seg and set_seg so that the problem is
avoided even when migrating to or migrating from a QEMU without this
commit. In get_seg, we clear the segment flags if the segment is marked
unusable. In set_seg, we mark the segment unusable if the segment's
"present" flag is not set.
Signed-off-by: Michael Chapman <mike@very.puzzling.org>
Message-Id: <1449464047-17467-1-git-send-email-mike@very.puzzling.org>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'target-i386/kvm.c')
-rw-r--r-- | target-i386/kvm.c | 22 |
1 files changed, 13 insertions, 9 deletions
diff --git a/target-i386/kvm.c b/target-i386/kvm.c index 7b01328ffe..ab65a6ebe5 100644 --- a/target-i386/kvm.c +++ b/target-i386/kvm.c @@ -1145,7 +1145,7 @@ static void set_seg(struct kvm_segment *lhs, const SegmentCache *rhs) lhs->l = (flags >> DESC_L_SHIFT) & 1; lhs->g = (flags & DESC_G_MASK) != 0; lhs->avl = (flags & DESC_AVL_MASK) != 0; - lhs->unusable = 0; + lhs->unusable = !lhs->present; lhs->padding = 0; } @@ -1154,14 +1154,18 @@ static void get_seg(SegmentCache *lhs, const struct kvm_segment *rhs) lhs->selector = rhs->selector; lhs->base = rhs->base; lhs->limit = rhs->limit; - lhs->flags = (rhs->type << DESC_TYPE_SHIFT) | - (rhs->present * DESC_P_MASK) | - (rhs->dpl << DESC_DPL_SHIFT) | - (rhs->db << DESC_B_SHIFT) | - (rhs->s * DESC_S_MASK) | - (rhs->l << DESC_L_SHIFT) | - (rhs->g * DESC_G_MASK) | - (rhs->avl * DESC_AVL_MASK); + if (rhs->unusable) { + lhs->flags = 0; + } else { + lhs->flags = (rhs->type << DESC_TYPE_SHIFT) | + (rhs->present * DESC_P_MASK) | + (rhs->dpl << DESC_DPL_SHIFT) | + (rhs->db << DESC_B_SHIFT) | + (rhs->s * DESC_S_MASK) | + (rhs->l << DESC_L_SHIFT) | + (rhs->g * DESC_G_MASK) | + (rhs->avl * DESC_AVL_MASK); + } } static void kvm_getput_reg(__u64 *kvm_reg, target_ulong *qemu_reg, int set) |