mmu_audit.c 6.4 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
}

167 168 169 170 171 172 173 174
static void audit_spte_after_sync(struct kvm_vcpu *vcpu, u64 *sptep, int level)
{
	struct kvm_mmu_page *sp = page_header(__pa(sptep));

	if (audit_point == AUDIT_POST_SYNC && sp->unsync)
		audit_printk("meet unsync sp(%p) after sync root.\n", sp);
}

X
Xiao Guangrong 已提交
175
static void check_mappings_rmap(struct kvm *kvm, struct kvm_mmu_page *sp)
176 177 178
{
	int i;

X
Xiao Guangrong 已提交
179 180
	if (sp->role.level != PT_PAGE_TABLE_LEVEL)
		return;
181

X
Xiao Guangrong 已提交
182 183
	for (i = 0; i < PT64_ENT_PER_PAGE; ++i) {
		if (!is_rmap_spte(sp->spt[i]))
184 185
			continue;

X
Xiao Guangrong 已提交
186
		inspect_spte_has_rmap(kvm, sp->spt + i);
187 188 189
	}
}

190
static void audit_write_protection(struct kvm *kvm, struct kvm_mmu_page *sp)
191 192 193 194 195
{
	struct kvm_memory_slot *slot;
	unsigned long *rmapp;
	u64 *spte;

X
Xiao Guangrong 已提交
196 197
	if (sp->role.direct || sp->unsync || sp->role.invalid)
		return;
198

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

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

X
Xiao Guangrong 已提交
211 212 213 214 215 216 217 218 219 220 221
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 已提交
222 223 224 225
static void audit_spte(struct kvm_vcpu *vcpu, u64 *sptep, int level)
{
	audit_sptes_have_rmaps(vcpu, sptep, level);
	audit_mappings(vcpu, sptep, level);
226
	audit_spte_after_sync(vcpu, sptep, level);
X
Xiao Guangrong 已提交
227 228 229 230 231 232 233
}

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

234
static void kvm_mmu_audit(void *ignore, struct kvm_vcpu *vcpu, int point)
235
{
236 237 238 239 240
	static DEFINE_RATELIMIT_STATE(ratelimit_state, 5 * HZ, 10);

	if (!__ratelimit(&ratelimit_state))
		return;

241
	audit_point = point;
X
Xiao Guangrong 已提交
242
	audit_all_active_sps(vcpu->kvm);
X
Xiao Guangrong 已提交
243
	audit_vcpu_spte(vcpu);
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 297 298 299
}

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