mmu_audit.c 6.1 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 24 25 26
static int audit_point;

#define audit_printk(fmt, args...)		\
	printk(KERN_ERR "audit: (%s) error: "	\
		fmt, audit_point_name[audit_point], ##args)
27

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

X
Xiao Guangrong 已提交
30 31
static void __mmu_spte_walk(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,
			    inspect_spte_fn fn, int level)
32 33 34 35
{
	int i;

	for (i = 0; i < PT64_ENT_PER_PAGE; ++i) {
X
Xiao Guangrong 已提交
36 37 38 39 40 41 42 43 44 45
		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);
46 47 48 49 50 51 52 53 54 55 56
		}
	}
}

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 已提交
57

58
	if (vcpu->arch.mmu.root_level == PT64_ROOT_LEVEL) {
59
		hpa_t root = vcpu->arch.mmu.root_hpa;
X
Xiao Guangrong 已提交
60

61
		sp = page_header(root);
X
Xiao Guangrong 已提交
62
		__mmu_spte_walk(vcpu, sp, fn, PT64_ROOT_LEVEL);
63 64
		return;
	}
X
Xiao Guangrong 已提交
65

66 67 68 69 70 71
	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 已提交
72
			__mmu_spte_walk(vcpu, sp, fn, 2);
73 74
		}
	}
X
Xiao Guangrong 已提交
75

76 77 78
	return;
}

X
Xiao Guangrong 已提交
79 80 81 82 83 84 85 86 87 88
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 已提交
89
static void audit_mappings(struct kvm_vcpu *vcpu, u64 *sptep, int level)
90
{
X
Xiao Guangrong 已提交
91 92 93 94
	struct kvm_mmu_page *sp;
	gfn_t gfn;
	pfn_t pfn;
	hpa_t hpa;
95

X
Xiao Guangrong 已提交
96 97 98 99
	sp = page_header(__pa(sptep));

	if (sp->unsync) {
		if (level != PT_PAGE_TABLE_LEVEL) {
100
			audit_printk("unsync sp: %p level = %d\n", sp, level);
101 102 103
			return;
		}

X
Xiao Guangrong 已提交
104
		if (*sptep == shadow_notrap_nonpresent_pte) {
105
			audit_printk("notrap spte in unsync sp: %p\n", sp);
106
			return;
X
Xiao Guangrong 已提交
107 108
		}
	}
109

X
Xiao Guangrong 已提交
110
	if (sp->role.direct && *sptep == shadow_notrap_nonpresent_pte) {
111
		audit_printk("notrap spte in direct sp: %p\n", sp);
X
Xiao Guangrong 已提交
112 113
		return;
	}
114

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

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

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

X
Xiao Guangrong 已提交
126 127
	hpa =  pfn << PAGE_SHIFT;
	if ((*sptep & PT64_BASE_ADDR_MASK) != hpa)
128 129
		audit_printk("levels %d pfn %llx hpa %llx ent %llxn",
				   vcpu->arch.mmu.root_level, pfn, hpa, *sptep);
130 131
}

X
Xiao Guangrong 已提交
132
static void inspect_spte_has_rmap(struct kvm *kvm, u64 *sptep)
133 134 135 136 137 138 139 140 141 142 143 144
{
	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;
145 146 147
		audit_printk("no memslot for gfn %llx\n", gfn);
		audit_printk("index %ld of sp (gfn=%llx)\n",
		       (long int)(sptep - rev_sp->spt), rev_sp->gfn);
148 149 150 151 152 153 154 155
		dump_stack();
		return;
	}

	rmapp = gfn_to_rmap(kvm, gfn, rev_sp->role.level);
	if (!*rmapp) {
		if (!printk_ratelimit())
			return;
156
		audit_printk("no rmap for writable spte %llx\n", *sptep);
157 158 159 160
		dump_stack();
	}
}

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

X
Xiao Guangrong 已提交
167
static void check_mappings_rmap(struct kvm *kvm, struct kvm_mmu_page *sp)
168 169 170
{
	int i;

X
Xiao Guangrong 已提交
171 172
	if (sp->role.level != PT_PAGE_TABLE_LEVEL)
		return;
173

X
Xiao Guangrong 已提交
174 175
	for (i = 0; i < PT64_ENT_PER_PAGE; ++i) {
		if (!is_rmap_spte(sp->spt[i]))
176 177
			continue;

X
Xiao Guangrong 已提交
178
		inspect_spte_has_rmap(kvm, sp->spt + i);
179 180 181
	}
}

X
Xiao Guangrong 已提交
182
void audit_write_protection(struct kvm *kvm, struct kvm_mmu_page *sp)
183 184 185 186 187
{
	struct kvm_memory_slot *slot;
	unsigned long *rmapp;
	u64 *spte;

X
Xiao Guangrong 已提交
188 189
	if (sp->role.direct || sp->unsync || sp->role.invalid)
		return;
190

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

X
Xiao Guangrong 已提交
194 195 196
	spte = rmap_next(kvm, rmapp, NULL);
	while (spte) {
		if (is_writable_pte(*spte))
197 198
			audit_printk("shadow page has writable mappings: gfn "
				     "%llx role %x\n", sp->gfn, sp->role.word);
X
Xiao Guangrong 已提交
199
		spte = rmap_next(kvm, rmapp, spte);
200 201 202
	}
}

X
Xiao Guangrong 已提交
203 204 205 206 207 208 209 210 211 212 213
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 已提交
214 215 216 217 218 219 220 221 222 223 224
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);
}

225
static void kvm_mmu_audit(void *ignore, struct kvm_vcpu *vcpu, int point)
226
{
227 228 229 230 231
	static DEFINE_RATELIMIT_STATE(ratelimit_state, 5 * HZ, 10);

	if (!__ratelimit(&ratelimit_state))
		return;

232
	audit_point = point;
X
Xiao Guangrong 已提交
233
	audit_all_active_sps(vcpu->kvm);
X
Xiao Guangrong 已提交
234
	audit_vcpu_spte(vcpu);
235 236 237 238 239 240 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
}

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);