diff options
-rw-r--r-- | drivers/kvm/mmu.c | 187 | ||||
-rw-r--r-- | drivers/kvm/paging_tmpl.h | 4 |
2 files changed, 189 insertions, 2 deletions
diff --git a/drivers/kvm/mmu.c b/drivers/kvm/mmu.c index 2fc252813927..6e7381bc2218 100644 --- a/drivers/kvm/mmu.c +++ b/drivers/kvm/mmu.c | |||
@@ -26,8 +26,31 @@ | |||
26 | #include "vmx.h" | 26 | #include "vmx.h" |
27 | #include "kvm.h" | 27 | #include "kvm.h" |
28 | 28 | ||
29 | #define pgprintk(x...) do { printk(x); } while (0) | 29 | #undef MMU_DEBUG |
30 | #define rmap_printk(x...) do { printk(x); } while (0) | 30 | |
31 | #undef AUDIT | ||
32 | |||
33 | #ifdef AUDIT | ||
34 | static void kvm_mmu_audit(struct kvm_vcpu *vcpu, const char *msg); | ||
35 | #else | ||
36 | static void kvm_mmu_audit(struct kvm_vcpu *vcpu, const char *msg) {} | ||
37 | #endif | ||
38 | |||
39 | #ifdef MMU_DEBUG | ||
40 | |||
41 | #define pgprintk(x...) do { if (dbg) printk(x); } while (0) | ||
42 | #define rmap_printk(x...) do { if (dbg) printk(x); } while (0) | ||
43 | |||
44 | #else | ||
45 | |||
46 | #define pgprintk(x...) do { } while (0) | ||
47 | #define rmap_printk(x...) do { } while (0) | ||
48 | |||
49 | #endif | ||
50 | |||
51 | #if defined(MMU_DEBUG) || defined(AUDIT) | ||
52 | static int dbg = 1; | ||
53 | #endif | ||
31 | 54 | ||
32 | #define ASSERT(x) \ | 55 | #define ASSERT(x) \ |
33 | if (!(x)) { \ | 56 | if (!(x)) { \ |
@@ -1271,3 +1294,163 @@ void kvm_mmu_slot_remove_write_access(struct kvm_vcpu *vcpu, int slot) | |||
1271 | } | 1294 | } |
1272 | } | 1295 | } |
1273 | } | 1296 | } |
1297 | |||
1298 | #ifdef AUDIT | ||
1299 | |||
1300 | static const char *audit_msg; | ||
1301 | |||
1302 | static gva_t canonicalize(gva_t gva) | ||
1303 | { | ||
1304 | #ifdef CONFIG_X86_64 | ||
1305 | gva = (long long)(gva << 16) >> 16; | ||
1306 | #endif | ||
1307 | return gva; | ||
1308 | } | ||
1309 | |||
1310 | static void audit_mappings_page(struct kvm_vcpu *vcpu, u64 page_pte, | ||
1311 | gva_t va, int level) | ||
1312 | { | ||
1313 | u64 *pt = __va(page_pte & PT64_BASE_ADDR_MASK); | ||
1314 | int i; | ||
1315 | gva_t va_delta = 1ul << (PAGE_SHIFT + 9 * (level - 1)); | ||
1316 | |||
1317 | for (i = 0; i < PT64_ENT_PER_PAGE; ++i, va += va_delta) { | ||
1318 | u64 ent = pt[i]; | ||
1319 | |||
1320 | if (!ent & PT_PRESENT_MASK) | ||
1321 | continue; | ||
1322 | |||
1323 | va = canonicalize(va); | ||
1324 | if (level > 1) | ||
1325 | audit_mappings_page(vcpu, ent, va, level - 1); | ||
1326 | else { | ||
1327 | gpa_t gpa = vcpu->mmu.gva_to_gpa(vcpu, va); | ||
1328 | hpa_t hpa = gpa_to_hpa(vcpu, gpa); | ||
1329 | |||
1330 | if ((ent & PT_PRESENT_MASK) | ||
1331 | && (ent & PT64_BASE_ADDR_MASK) != hpa) | ||
1332 | printk(KERN_ERR "audit error: (%s) levels %d" | ||
1333 | " gva %lx gpa %llx hpa %llx ent %llx\n", | ||
1334 | audit_msg, vcpu->mmu.root_level, | ||
1335 | va, gpa, hpa, ent); | ||
1336 | } | ||
1337 | } | ||
1338 | } | ||
1339 | |||
1340 | static void audit_mappings(struct kvm_vcpu *vcpu) | ||
1341 | { | ||
1342 | int i; | ||
1343 | |||
1344 | if (vcpu->mmu.root_level == 4) | ||
1345 | audit_mappings_page(vcpu, vcpu->mmu.root_hpa, 0, 4); | ||
1346 | else | ||
1347 | for (i = 0; i < 4; ++i) | ||
1348 | if (vcpu->mmu.pae_root[i] & PT_PRESENT_MASK) | ||
1349 | audit_mappings_page(vcpu, | ||
1350 | vcpu->mmu.pae_root[i], | ||
1351 | i << 30, | ||
1352 | 2); | ||
1353 | } | ||
1354 | |||
1355 | static int count_rmaps(struct kvm_vcpu *vcpu) | ||
1356 | { | ||
1357 | int nmaps = 0; | ||
1358 | int i, j, k; | ||
1359 | |||
1360 | for (i = 0; i < KVM_MEMORY_SLOTS; ++i) { | ||
1361 | struct kvm_memory_slot *m = &vcpu->kvm->memslots[i]; | ||
1362 | struct kvm_rmap_desc *d; | ||
1363 | |||
1364 | for (j = 0; j < m->npages; ++j) { | ||
1365 | struct page *page = m->phys_mem[j]; | ||
1366 | |||
1367 | if (!page->private) | ||
1368 | continue; | ||
1369 | if (!(page->private & 1)) { | ||
1370 | ++nmaps; | ||
1371 | continue; | ||
1372 | } | ||
1373 | d = (struct kvm_rmap_desc *)(page->private & ~1ul); | ||
1374 | while (d) { | ||
1375 | for (k = 0; k < RMAP_EXT; ++k) | ||
1376 | if (d->shadow_ptes[k]) | ||
1377 | ++nmaps; | ||
1378 | else | ||
1379 | break; | ||
1380 | d = d->more; | ||
1381 | } | ||
1382 | } | ||
1383 | } | ||
1384 | return nmaps; | ||
1385 | } | ||
1386 | |||
1387 | static int count_writable_mappings(struct kvm_vcpu *vcpu) | ||
1388 | { | ||
1389 | int nmaps = 0; | ||
1390 | struct kvm_mmu_page *page; | ||
1391 | int i; | ||
1392 | |||
1393 | list_for_each_entry(page, &vcpu->kvm->active_mmu_pages, link) { | ||
1394 | u64 *pt = __va(page->page_hpa); | ||
1395 | |||
1396 | if (page->role.level != PT_PAGE_TABLE_LEVEL) | ||
1397 | continue; | ||
1398 | |||
1399 | for (i = 0; i < PT64_ENT_PER_PAGE; ++i) { | ||
1400 | u64 ent = pt[i]; | ||
1401 | |||
1402 | if (!(ent & PT_PRESENT_MASK)) | ||
1403 | continue; | ||
1404 | if (!(ent & PT_WRITABLE_MASK)) | ||
1405 | continue; | ||
1406 | ++nmaps; | ||
1407 | } | ||
1408 | } | ||
1409 | return nmaps; | ||
1410 | } | ||
1411 | |||
1412 | static void audit_rmap(struct kvm_vcpu *vcpu) | ||
1413 | { | ||
1414 | int n_rmap = count_rmaps(vcpu); | ||
1415 | int n_actual = count_writable_mappings(vcpu); | ||
1416 | |||
1417 | if (n_rmap != n_actual) | ||
1418 | printk(KERN_ERR "%s: (%s) rmap %d actual %d\n", | ||
1419 | __FUNCTION__, audit_msg, n_rmap, n_actual); | ||
1420 | } | ||
1421 | |||
1422 | static void audit_write_protection(struct kvm_vcpu *vcpu) | ||
1423 | { | ||
1424 | struct kvm_mmu_page *page; | ||
1425 | |||
1426 | list_for_each_entry(page, &vcpu->kvm->active_mmu_pages, link) { | ||
1427 | hfn_t hfn; | ||
1428 | struct page *pg; | ||
1429 | |||
1430 | if (page->role.metaphysical) | ||
1431 | continue; | ||
1432 | |||
1433 | hfn = gpa_to_hpa(vcpu, (gpa_t)page->gfn << PAGE_SHIFT) | ||
1434 | >> PAGE_SHIFT; | ||
1435 | pg = pfn_to_page(hfn); | ||
1436 | if (pg->private) | ||
1437 | printk(KERN_ERR "%s: (%s) shadow page has writable" | ||
1438 | " mappings: gfn %lx role %x\n", | ||
1439 | __FUNCTION__, audit_msg, page->gfn, | ||
1440 | page->role.word); | ||
1441 | } | ||
1442 | } | ||
1443 | |||
1444 | static void kvm_mmu_audit(struct kvm_vcpu *vcpu, const char *msg) | ||
1445 | { | ||
1446 | int olddbg = dbg; | ||
1447 | |||
1448 | dbg = 0; | ||
1449 | audit_msg = msg; | ||
1450 | audit_rmap(vcpu); | ||
1451 | audit_write_protection(vcpu); | ||
1452 | audit_mappings(vcpu); | ||
1453 | dbg = olddbg; | ||
1454 | } | ||
1455 | |||
1456 | #endif | ||
diff --git a/drivers/kvm/paging_tmpl.h b/drivers/kvm/paging_tmpl.h index 32b385188454..c894b51ba3f8 100644 --- a/drivers/kvm/paging_tmpl.h +++ b/drivers/kvm/paging_tmpl.h | |||
@@ -355,6 +355,7 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, | |||
355 | int r; | 355 | int r; |
356 | 356 | ||
357 | pgprintk("%s: addr %lx err %x\n", __FUNCTION__, addr, error_code); | 357 | pgprintk("%s: addr %lx err %x\n", __FUNCTION__, addr, error_code); |
358 | kvm_mmu_audit(vcpu, "pre page fault"); | ||
358 | 359 | ||
359 | r = mmu_topup_memory_caches(vcpu); | 360 | r = mmu_topup_memory_caches(vcpu); |
360 | if (r) | 361 | if (r) |
@@ -402,6 +403,7 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, | |||
402 | pgprintk("%s: io work, no access\n", __FUNCTION__); | 403 | pgprintk("%s: io work, no access\n", __FUNCTION__); |
403 | inject_page_fault(vcpu, addr, | 404 | inject_page_fault(vcpu, addr, |
404 | error_code | PFERR_PRESENT_MASK); | 405 | error_code | PFERR_PRESENT_MASK); |
406 | kvm_mmu_audit(vcpu, "post page fault (io)"); | ||
405 | return 0; | 407 | return 0; |
406 | } | 408 | } |
407 | 409 | ||
@@ -410,10 +412,12 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, | |||
410 | */ | 412 | */ |
411 | if (pte_present && !fixed && !write_pt) { | 413 | if (pte_present && !fixed && !write_pt) { |
412 | inject_page_fault(vcpu, addr, error_code); | 414 | inject_page_fault(vcpu, addr, error_code); |
415 | kvm_mmu_audit(vcpu, "post page fault (guest)"); | ||
413 | return 0; | 416 | return 0; |
414 | } | 417 | } |
415 | 418 | ||
416 | ++kvm_stat.pf_fixed; | 419 | ++kvm_stat.pf_fixed; |
420 | kvm_mmu_audit(vcpu, "post page fault (fixed)"); | ||
417 | 421 | ||
418 | return write_pt; | 422 | return write_pt; |
419 | } | 423 | } |