diff options
| author | Avi Kivity <avi@qumranet.com> | 2007-01-05 19:36:56 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@woody.osdl.org> | 2007-01-06 02:55:27 -0500 |
| commit | 37a7d8b046da6254718be1409140cd7bf3126f8f (patch) | |
| tree | a52e3a60234920301c881b8b534e85ae1bed5cf8 | |
| parent | 9ede74e0af549d75d4ea870bed8b178983816745 (diff) | |
[PATCH] KVM: MMU: add audit code to check mappings, etc are correct
Signed-off-by: Avi Kivity <avi@qumranet.com>
Acked-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
| -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 | } |
