mmu_audit.c 6.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
 * mmu_audit.c:
 *
 * Audit code for KVM MMU
 *
 * Copyright (C) 2006 Qumranet, Inc.
 * Copyright 2010 Red Hat, Inc. and/or its affilates.
 *
 * Authors:
 *   Yaniv Kamay  <yaniv@qumranet.com>
 *   Avi Kivity   <avi@qumranet.com>
 *   Marcelo Tosatti <mtosatti@redhat.com>
 *   Xiao Guangrong <xiaoguangrong@cn.fujitsu.com>
 *
 * This work is licensed under the terms of the GNU GPL, version 2.  See
 * the COPYING file in the top-level directory.
 *
 */

20 21
#include <linux/ratelimit.h>

22 23
static const char *audit_msg;

X
Xiao Guangrong 已提交
24
typedef void (*inspect_spte_fn) (struct kvm_vcpu *vcpu, u64 *sptep, int level);
25

X
Xiao Guangrong 已提交
26 27
static void __mmu_spte_walk(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,
			    inspect_spte_fn fn, int level)
28 29 30 31
{
	int i;

	for (i = 0; i < PT64_ENT_PER_PAGE; ++i) {
X
Xiao Guangrong 已提交
32 33 34 35 36 37 38 39 40 41
		u64 *ent = sp->spt;

		fn(vcpu, ent + i, level);

		if (is_shadow_present_pte(ent[i]) &&
		      !is_last_spte(ent[i], level)) {
			struct kvm_mmu_page *child;

			child = page_header(ent[i] & PT64_BASE_ADDR_MASK);
			__mmu_spte_walk(vcpu, child, fn, level - 1);
42 43 44 45 46 47 48 49 50 51 52
		}
	}
}

static void mmu_spte_walk(struct kvm_vcpu *vcpu, inspect_spte_fn fn)
{
	int i;
	struct kvm_mmu_page *sp;

	if (!VALID_PAGE(vcpu->arch.mmu.root_hpa))
		return;
X
Xiao Guangrong 已提交
53

54 55
	if (vcpu->arch.mmu.shadow_root_level == PT64_ROOT_LEVEL) {
		hpa_t root = vcpu->arch.mmu.root_hpa;
X
Xiao Guangrong 已提交
56

57
		sp = page_header(root);
X
Xiao Guangrong 已提交
58
		__mmu_spte_walk(vcpu, sp, fn, PT64_ROOT_LEVEL);
59 60
		return;
	}
X
Xiao Guangrong 已提交
61

62 63 64 65 66 67
	for (i = 0; i < 4; ++i) {
		hpa_t root = vcpu->arch.mmu.pae_root[i];

		if (root && VALID_PAGE(root)) {
			root &= PT64_BASE_ADDR_MASK;
			sp = page_header(root);
X
Xiao Guangrong 已提交
68
			__mmu_spte_walk(vcpu, sp, fn, 2);
69 70
		}
	}
X
Xiao Guangrong 已提交
71

72 73 74
	return;
}

X
Xiao Guangrong 已提交
75 76 77 78 79 80 81 82 83 84
typedef void (*sp_handler) (struct kvm *kvm, struct kvm_mmu_page *sp);

static void walk_all_active_sps(struct kvm *kvm, sp_handler fn)
{
	struct kvm_mmu_page *sp;

	list_for_each_entry(sp, &kvm->arch.active_mmu_pages, link)
		fn(kvm, sp);
}

X
Xiao Guangrong 已提交
85
static void audit_mappings(struct kvm_vcpu *vcpu, u64 *sptep, int level)
86
{
X
Xiao Guangrong 已提交
87 88 89 90
	struct kvm_mmu_page *sp;
	gfn_t gfn;
	pfn_t pfn;
	hpa_t hpa;
91

X
Xiao Guangrong 已提交
92 93 94 95 96 97
	sp = page_header(__pa(sptep));

	if (sp->unsync) {
		if (level != PT_PAGE_TABLE_LEVEL) {
			printk(KERN_ERR "audit: (%s) error: unsync sp: %p level = %d\n",
				audit_msg, sp, level);
98 99 100
			return;
		}

X
Xiao Guangrong 已提交
101 102 103
		if (*sptep == shadow_notrap_nonpresent_pte) {
			printk(KERN_ERR "audit: (%s) error: notrap spte in unsync sp: %p\n",
				audit_msg, sp);
104
			return;
X
Xiao Guangrong 已提交
105 106
		}
	}
107

X
Xiao Guangrong 已提交
108 109 110 111 112
	if (sp->role.direct && *sptep == shadow_notrap_nonpresent_pte) {
		printk(KERN_ERR "audit: (%s) error: notrap spte in direct sp: %p\n",
			audit_msg, sp);
		return;
	}
113

X
Xiao Guangrong 已提交
114 115
	if (!is_shadow_present_pte(*sptep) || !is_last_spte(*sptep, level))
		return;
116

X
Xiao Guangrong 已提交
117 118
	gfn = kvm_mmu_page_get_gfn(sp, sptep - sp->spt);
	pfn = gfn_to_pfn_atomic(vcpu->kvm, gfn);
119

X
Xiao Guangrong 已提交
120 121 122
	if (is_error_pfn(pfn)) {
		kvm_release_pfn_clean(pfn);
		return;
123 124
	}

X
Xiao Guangrong 已提交
125 126 127 128 129 130
	hpa =  pfn << PAGE_SHIFT;
	if ((*sptep & PT64_BASE_ADDR_MASK) != hpa)
		printk(KERN_ERR "xx audit error: (%s) levels %d"
				   "pfn %llx hpa %llx ent %llxn",
				   audit_msg, vcpu->arch.mmu.root_level,
				   pfn, hpa, *sptep);
131 132
}

X
Xiao Guangrong 已提交
133
static void inspect_spte_has_rmap(struct kvm *kvm, u64 *sptep)
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
{
	unsigned long *rmapp;
	struct kvm_mmu_page *rev_sp;
	gfn_t gfn;


	rev_sp = page_header(__pa(sptep));
	gfn = kvm_mmu_page_get_gfn(rev_sp, sptep - rev_sp->spt);

	if (!gfn_to_memslot(kvm, gfn)) {
		if (!printk_ratelimit())
			return;
		printk(KERN_ERR "%s: no memslot for gfn %llx\n",
				 audit_msg, gfn);
		printk(KERN_ERR "%s: index %ld of sp (gfn=%llx)\n",
		       audit_msg, (long int)(sptep - rev_sp->spt),
				rev_sp->gfn);
		dump_stack();
		return;
	}

	rmapp = gfn_to_rmap(kvm, gfn, rev_sp->role.level);
	if (!*rmapp) {
		if (!printk_ratelimit())
			return;
		printk(KERN_ERR "%s: no rmap for writable spte %llx\n",
				 audit_msg, *sptep);
		dump_stack();
	}
}

X
Xiao Guangrong 已提交
165
static void audit_sptes_have_rmaps(struct kvm_vcpu *vcpu, u64 *sptep, int level)
166
{
X
Xiao Guangrong 已提交
167 168
	if (is_shadow_present_pte(*sptep) && is_last_spte(*sptep, level))
		inspect_spte_has_rmap(vcpu->kvm, sptep);
169 170
}

X
Xiao Guangrong 已提交
171
static void check_mappings_rmap(struct kvm *kvm, struct kvm_mmu_page *sp)
172 173 174
{
	int i;

X
Xiao Guangrong 已提交
175 176
	if (sp->role.level != PT_PAGE_TABLE_LEVEL)
		return;
177

X
Xiao Guangrong 已提交
178 179
	for (i = 0; i < PT64_ENT_PER_PAGE; ++i) {
		if (!is_rmap_spte(sp->spt[i]))
180 181
			continue;

X
Xiao Guangrong 已提交
182
		inspect_spte_has_rmap(kvm, sp->spt + i);
183 184 185
	}
}

X
Xiao Guangrong 已提交
186
void audit_write_protection(struct kvm *kvm, struct kvm_mmu_page *sp)
187 188 189 190 191
{
	struct kvm_memory_slot *slot;
	unsigned long *rmapp;
	u64 *spte;

X
Xiao Guangrong 已提交
192 193
	if (sp->role.direct || sp->unsync || sp->role.invalid)
		return;
194

X
Xiao Guangrong 已提交
195 196
	slot = gfn_to_memslot(kvm, sp->gfn);
	rmapp = &slot->rmap[sp->gfn - slot->base_gfn];
197

X
Xiao Guangrong 已提交
198 199 200 201
	spte = rmap_next(kvm, rmapp, NULL);
	while (spte) {
		if (is_writable_pte(*spte))
			printk(KERN_ERR "%s: (%s) shadow page has "
202 203 204
				"writable mappings: gfn %llx role %x\n",
			       __func__, audit_msg, sp->gfn,
			       sp->role.word);
X
Xiao Guangrong 已提交
205
		spte = rmap_next(kvm, rmapp, spte);
206 207 208
	}
}

X
Xiao Guangrong 已提交
209 210 211 212 213 214 215 216 217 218 219
static void audit_sp(struct kvm *kvm, struct kvm_mmu_page *sp)
{
	check_mappings_rmap(kvm, sp);
	audit_write_protection(kvm, sp);
}

static void audit_all_active_sps(struct kvm *kvm)
{
	walk_all_active_sps(kvm, audit_sp);
}

X
Xiao Guangrong 已提交
220 221 222 223 224 225 226 227 228 229 230
static void audit_spte(struct kvm_vcpu *vcpu, u64 *sptep, int level)
{
	audit_sptes_have_rmaps(vcpu, sptep, level);
	audit_mappings(vcpu, sptep, level);
}

static void audit_vcpu_spte(struct kvm_vcpu *vcpu)
{
	mmu_spte_walk(vcpu, audit_spte);
}

231 232
static void kvm_mmu_audit(void *ignore, struct kvm_vcpu *vcpu, int audit_point)
{
233 234 235 236 237
	static DEFINE_RATELIMIT_STATE(ratelimit_state, 5 * HZ, 10);

	if (!__ratelimit(&ratelimit_state))
		return;

238
	audit_msg = audit_point_name[audit_point];
X
Xiao Guangrong 已提交
239
	audit_all_active_sps(vcpu->kvm);
X
Xiao Guangrong 已提交
240
	audit_vcpu_spte(vcpu);
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
}

static bool mmu_audit;

static void mmu_audit_enable(void)
{
	int ret;

	if (mmu_audit)
		return;

	ret = register_trace_kvm_mmu_audit(kvm_mmu_audit, NULL);
	WARN_ON(ret);

	mmu_audit = true;
}

static void mmu_audit_disable(void)
{
	if (!mmu_audit)
		return;

	unregister_trace_kvm_mmu_audit(kvm_mmu_audit, NULL);
	tracepoint_synchronize_unregister();
	mmu_audit = false;
}

static int mmu_audit_set(const char *val, const struct kernel_param *kp)
{
	int ret;
	unsigned long enable;

	ret = strict_strtoul(val, 10, &enable);
	if (ret < 0)
		return -EINVAL;

	switch (enable) {
	case 0:
		mmu_audit_disable();
		break;
	case 1:
		mmu_audit_enable();
		break;
	default:
		return -EINVAL;
	}

	return 0;
}

static struct kernel_param_ops audit_param_ops = {
	.set = mmu_audit_set,
	.get = param_get_bool,
};

module_param_cb(mmu_audit, &audit_param_ops, &mmu_audit, 0644);