mmu_audit.c 6.3 KB
Newer Older
1 2 3 4 5 6
/*
 * mmu_audit.c:
 *
 * Audit code for KVM MMU
 *
 * Copyright (C) 2006 Qumranet, Inc.
N
Nicolas Kaiser 已提交
7
 * Copyright 2010 Red Hat, Inc. and/or its affiliates.
8 9 10 11 12 13 14 15 16 17 18 19
 *
 * 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
#define audit_printk(kvm, fmt, args...)		\
23
	printk(KERN_ERR "audit: (%s) error: "	\
24
		fmt, audit_point_name[kvm->arch.audit_point], ##args)
25

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

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

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

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

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

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

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

74 75 76
	return;
}

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

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

	if (sp->unsync) {
		if (level != PT_PAGE_TABLE_LEVEL) {
98 99
			audit_printk(vcpu->kvm, "unsync sp: %p "
				     "level = %d\n", sp, level);
100 101
			return;
		}
X
Xiao Guangrong 已提交
102
	}
103

X
Xiao Guangrong 已提交
104 105
	if (!is_shadow_present_pte(*sptep) || !is_last_spte(*sptep, level))
		return;
106

X
Xiao Guangrong 已提交
107 108
	gfn = kvm_mmu_page_get_gfn(sp, sptep - sp->spt);
	pfn = gfn_to_pfn_atomic(vcpu->kvm, gfn);
109

X
Xiao Guangrong 已提交
110 111 112
	if (is_error_pfn(pfn)) {
		kvm_release_pfn_clean(pfn);
		return;
113 114
	}

X
Xiao Guangrong 已提交
115 116
	hpa =  pfn << PAGE_SHIFT;
	if ((*sptep & PT64_BASE_ADDR_MASK) != hpa)
117 118 119
		audit_printk(vcpu->kvm, "levels %d pfn %llx hpa %llx "
			     "ent %llxn", vcpu->arch.mmu.root_level, pfn,
			     hpa, *sptep);
120 121
}

X
Xiao Guangrong 已提交
122
static void inspect_spte_has_rmap(struct kvm *kvm, u64 *sptep)
123 124 125 126 127 128 129 130 131 132 133 134
{
	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;
135 136
		audit_printk(kvm, "no memslot for gfn %llx\n", gfn);
		audit_printk(kvm, "index %ld of sp (gfn=%llx)\n",
137
		       (long int)(sptep - rev_sp->spt), rev_sp->gfn);
138 139 140 141 142 143 144 145
		dump_stack();
		return;
	}

	rmapp = gfn_to_rmap(kvm, gfn, rev_sp->role.level);
	if (!*rmapp) {
		if (!printk_ratelimit())
			return;
146 147
		audit_printk(kvm, "no rmap for writable spte %llx\n",
			     *sptep);
148 149 150 151
		dump_stack();
	}
}

X
Xiao Guangrong 已提交
152
static void audit_sptes_have_rmaps(struct kvm_vcpu *vcpu, u64 *sptep, int level)
153
{
X
Xiao Guangrong 已提交
154 155
	if (is_shadow_present_pte(*sptep) && is_last_spte(*sptep, level))
		inspect_spte_has_rmap(vcpu->kvm, sptep);
156 157
}

158 159 160 161
static void audit_spte_after_sync(struct kvm_vcpu *vcpu, u64 *sptep, int level)
{
	struct kvm_mmu_page *sp = page_header(__pa(sptep));

162 163 164
	if (vcpu->kvm->arch.audit_point == AUDIT_POST_SYNC && sp->unsync)
		audit_printk(vcpu->kvm, "meet unsync sp(%p) after sync "
			     "root.\n", sp);
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
	}
}

182
static 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 199
			audit_printk(kvm, "shadow page has writable "
				     "mappings: gfn %llx role %x\n",
				     sp->gfn, sp->role.word);
X
Xiao Guangrong 已提交
200
		spte = rmap_next(kvm, rmapp, spte);
201 202 203
	}
}

X
Xiao Guangrong 已提交
204 205 206 207 208 209 210 211 212 213 214
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 已提交
215 216 217 218
static void audit_spte(struct kvm_vcpu *vcpu, u64 *sptep, int level)
{
	audit_sptes_have_rmaps(vcpu, sptep, level);
	audit_mappings(vcpu, sptep, level);
219
	audit_spte_after_sync(vcpu, sptep, level);
X
Xiao Guangrong 已提交
220 221 222 223 224 225 226
}

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

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

	if (!__ratelimit(&ratelimit_state))
		return;

234
	vcpu->kvm->arch.audit_point = point;
X
Xiao Guangrong 已提交
235
	audit_all_active_sps(vcpu->kvm);
X
Xiao Guangrong 已提交
236
	audit_vcpu_spte(vcpu);
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 291 292
}

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