aboutsummaryrefslogtreecommitdiff
path: root/target-i386
diff options
context:
space:
mode:
authorMichael Chapman <mike@very.puzzling.org>2015-12-07 15:54:07 +1100
committerPaolo Bonzini <pbonzini@redhat.com>2015-12-17 17:33:49 +0100
commit4cae9c97967a0e6311858285a30c44208210b277 (patch)
tree5c845d41469b338e8f547be95b07130a4f9ca831 /target-i386
parentf6d153f1bfc7839555309c6e090c6f20c1ce3c0e (diff)
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')
-rw-r--r--target-i386/kvm.c22
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)