summaryrefslogtreecommitdiffstats
path: root/arch/x86/kvm/mtrr.c
blob: 6f74e2b27c1ed5a32ac3e881ce069b89cd2d53ec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// SPDX-License-Identifier: GPL-2.0-only
/*
 * vMTRR implementation
 *
 * Copyright (C) 2006 Qumranet, Inc.
 * Copyright 2010 Red Hat, Inc. and/or its affiliates.
 * Copyright(C) 2015 Intel Corporation.
 *
 * Authors:
 *   Yaniv Kamay  <yaniv@qumranet.com>
 *   Avi Kivity   <avi@qumranet.com>
 *   Marcelo Tosatti <mtosatti@redhat.com>
 *   Paolo Bonzini <pbonzini@redhat.com>
 *   Xiao Guangrong <guangrong.xiao@linux.intel.com>
 */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/kvm_host.h>
#include <asm/mtrr.h>

#include "cpuid.h"
#include "x86.h"

static u64 *find_mtrr(struct kvm_vcpu *vcpu, unsigned int msr)
{
	int index;

	switch (msr) {
	case MTRRphysBase_MSR(0) ... MTRRphysMask_MSR(KVM_NR_VAR_MTRR - 1):
		index = msr - MTRRphysBase_MSR(0);
		return &vcpu->arch.mtrr_state.var[index];
	case MSR_MTRRfix64K_00000:
		return &vcpu->arch.mtrr_state.fixed_64k;
	case MSR_MTRRfix16K_80000:
	case MSR_MTRRfix16K_A0000:
		index = msr - MSR_MTRRfix16K_80000;
		return &vcpu->arch.mtrr_state.fixed_16k[index];
	case MSR_MTRRfix4K_C0000:
	case MSR_MTRRfix4K_C8000:
	case MSR_MTRRfix4K_D0000:
	case MSR_MTRRfix4K_D8000:
	case MSR_MTRRfix4K_E0000:
	case MSR_MTRRfix4K_E8000:
	case MSR_MTRRfix4K_F0000:
	case MSR_MTRRfix4K_F8000:
		index = msr - MSR_MTRRfix4K_C0000;
		return &vcpu->arch.mtrr_state.fixed_4k[index];
	case MSR_MTRRdefType:
		return &vcpu->arch.mtrr_state.deftype;
	default:
		break;
	}
	return NULL;
}

static bool valid_mtrr_type(unsigned t)
{
	return t < 8 && (1 << t) & 0x73; /* 0, 1, 4, 5, 6 */
}

static bool kvm_mtrr_valid(struct kvm_vcpu *vcpu, u32 msr, u64 data)
{
	int i;
	u64 mask;

	if (msr == MSR_MTRRdefType) {
		if (data & ~0xcff)
			return false;
		return valid_mtrr_type(data & 0xff);
	} else if (msr >= MSR_MTRRfix64K_00000 && msr <= MSR_MTRRfix4K_F8000) {
		for (i = 0; i < 8 ; i++)
			if (!valid_mtrr_type((data >> (i * 8)) & 0xff))
				return false;
		return true;
	}

	/* variable MTRRs */
	if (WARN_ON_ONCE(!(msr >= MTRRphysBase_MSR(0) &&
			   msr <= MTRRphysMask_MSR(KVM_NR_VAR_MTRR - 1))))
		return false;

	mask = kvm_vcpu_reserved_gpa_bits_raw(vcpu);
	if ((msr & 1) == 0) {
		/* MTRR base */
		if (!valid_mtrr_type(data & 0xff))
			return false;
		mask |= 0xf00;
	} else {
		/* MTRR mask */
		mask |= 0x7ff;
	}

	return (data & mask) == 0;
}

int kvm_mtrr_set_msr(struct kvm_vcpu *vcpu, u32 msr, u64 data)
{
	u64 *mtrr;

	mtrr = find_mtrr(vcpu, msr);
	if (!mtrr)
		return 1;

	if (!kvm_mtrr_valid(vcpu, msr, data))
		return 1;

	*mtrr = data;
	return 0;
}

int kvm_mtrr_get_msr(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
{
	u64 *mtrr;

	/* MSR_MTRRcap is a readonly MSR. */
	if (msr == MSR_MTRRcap) {
		/*
		 * SMRR = 0
		 * WC = 1
		 * FIX = 1
		 * VCNT = KVM_NR_VAR_MTRR
		 */
		*pdata = 0x500 | KVM_NR_VAR_MTRR;
		return 0;
	}

	mtrr = find_mtrr(vcpu, msr);
	if (!mtrr)
		return 1;

	*pdata = *mtrr;
	return 0;
}