diff options
author | Avi Kivity <avi@qumranet.com> | 2007-05-07 03:55:37 -0400 |
---|---|---|
committer | Avi Kivity <avi@qumranet.com> | 2007-07-16 05:05:41 -0400 |
commit | 653e3108b7d6097d25089d25ab4e99bc58b28962 (patch) | |
tree | ff92977a15ddf12807d3a9f32eb6f5446fb59a79 /drivers | |
parent | eff708bc2bacd4f22cf844871341bef341bd096a (diff) |
KVM: Avoid corrupting tr in real mode
The real mode tr needs to be set to a specific tss so that I/O
instructions can function. Divert the new tr values to the real
mode save area from where they will be restored on transition to
protected mode.
This fixes some crashes on reboot when the bios accesses an I/O
instruction.
Signed-off-by: Avi Kivity <avi@qumranet.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/kvm/vmx.c | 45 |
1 files changed, 31 insertions, 14 deletions
diff --git a/drivers/kvm/vmx.c b/drivers/kvm/vmx.c index b353eaa0a441..e39ebe0b6958 100644 --- a/drivers/kvm/vmx.c +++ b/drivers/kvm/vmx.c | |||
@@ -1042,23 +1042,11 @@ static void vmx_get_segment(struct kvm_vcpu *vcpu, | |||
1042 | var->unusable = (ar >> 16) & 1; | 1042 | var->unusable = (ar >> 16) & 1; |
1043 | } | 1043 | } |
1044 | 1044 | ||
1045 | static void vmx_set_segment(struct kvm_vcpu *vcpu, | 1045 | static u32 vmx_segment_access_rights(struct kvm_segment *var) |
1046 | struct kvm_segment *var, int seg) | ||
1047 | { | 1046 | { |
1048 | struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg]; | ||
1049 | u32 ar; | 1047 | u32 ar; |
1050 | 1048 | ||
1051 | vmcs_writel(sf->base, var->base); | 1049 | if (var->unusable) |
1052 | vmcs_write32(sf->limit, var->limit); | ||
1053 | vmcs_write16(sf->selector, var->selector); | ||
1054 | if (vcpu->rmode.active && var->s) { | ||
1055 | /* | ||
1056 | * Hack real-mode segments into vm86 compatibility. | ||
1057 | */ | ||
1058 | if (var->base == 0xffff0000 && var->selector == 0xf000) | ||
1059 | vmcs_writel(sf->base, 0xf0000); | ||
1060 | ar = 0xf3; | ||
1061 | } else if (var->unusable) | ||
1062 | ar = 1 << 16; | 1050 | ar = 1 << 16; |
1063 | else { | 1051 | else { |
1064 | ar = var->type & 15; | 1052 | ar = var->type & 15; |
@@ -1072,6 +1060,35 @@ static void vmx_set_segment(struct kvm_vcpu *vcpu, | |||
1072 | } | 1060 | } |
1073 | if (ar == 0) /* a 0 value means unusable */ | 1061 | if (ar == 0) /* a 0 value means unusable */ |
1074 | ar = AR_UNUSABLE_MASK; | 1062 | ar = AR_UNUSABLE_MASK; |
1063 | |||
1064 | return ar; | ||
1065 | } | ||
1066 | |||
1067 | static void vmx_set_segment(struct kvm_vcpu *vcpu, | ||
1068 | struct kvm_segment *var, int seg) | ||
1069 | { | ||
1070 | struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg]; | ||
1071 | u32 ar; | ||
1072 | |||
1073 | if (vcpu->rmode.active && seg == VCPU_SREG_TR) { | ||
1074 | vcpu->rmode.tr.selector = var->selector; | ||
1075 | vcpu->rmode.tr.base = var->base; | ||
1076 | vcpu->rmode.tr.limit = var->limit; | ||
1077 | vcpu->rmode.tr.ar = vmx_segment_access_rights(var); | ||
1078 | return; | ||
1079 | } | ||
1080 | vmcs_writel(sf->base, var->base); | ||
1081 | vmcs_write32(sf->limit, var->limit); | ||
1082 | vmcs_write16(sf->selector, var->selector); | ||
1083 | if (vcpu->rmode.active && var->s) { | ||
1084 | /* | ||
1085 | * Hack real-mode segments into vm86 compatibility. | ||
1086 | */ | ||
1087 | if (var->base == 0xffff0000 && var->selector == 0xf000) | ||
1088 | vmcs_writel(sf->base, 0xf0000); | ||
1089 | ar = 0xf3; | ||
1090 | } else | ||
1091 | ar = vmx_segment_access_rights(var); | ||
1075 | vmcs_write32(sf->ar_bytes, ar); | 1092 | vmcs_write32(sf->ar_bytes, ar); |
1076 | } | 1093 | } |
1077 | 1094 | ||