[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>
This commit is contained in:
parent
9ede74e0af
commit
37a7d8b046
|
@ -26,8 +26,31 @@
|
||||||
#include "vmx.h"
|
#include "vmx.h"
|
||||||
#include "kvm.h"
|
#include "kvm.h"
|
||||||
|
|
||||||
#define pgprintk(x...) do { printk(x); } while (0)
|
#undef MMU_DEBUG
|
||||||
#define rmap_printk(x...) do { printk(x); } while (0)
|
|
||||||
|
#undef AUDIT
|
||||||
|
|
||||||
|
#ifdef AUDIT
|
||||||
|
static void kvm_mmu_audit(struct kvm_vcpu *vcpu, const char *msg);
|
||||||
|
#else
|
||||||
|
static void kvm_mmu_audit(struct kvm_vcpu *vcpu, const char *msg) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef MMU_DEBUG
|
||||||
|
|
||||||
|
#define pgprintk(x...) do { if (dbg) printk(x); } while (0)
|
||||||
|
#define rmap_printk(x...) do { if (dbg) printk(x); } while (0)
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define pgprintk(x...) do { } while (0)
|
||||||
|
#define rmap_printk(x...) do { } while (0)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(MMU_DEBUG) || defined(AUDIT)
|
||||||
|
static int dbg = 1;
|
||||||
|
#endif
|
||||||
|
|
||||||
#define ASSERT(x) \
|
#define ASSERT(x) \
|
||||||
if (!(x)) { \
|
if (!(x)) { \
|
||||||
|
@ -1271,3 +1294,163 @@ void kvm_mmu_slot_remove_write_access(struct kvm_vcpu *vcpu, int slot)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef AUDIT
|
||||||
|
|
||||||
|
static const char *audit_msg;
|
||||||
|
|
||||||
|
static gva_t canonicalize(gva_t gva)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_X86_64
|
||||||
|
gva = (long long)(gva << 16) >> 16;
|
||||||
|
#endif
|
||||||
|
return gva;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void audit_mappings_page(struct kvm_vcpu *vcpu, u64 page_pte,
|
||||||
|
gva_t va, int level)
|
||||||
|
{
|
||||||
|
u64 *pt = __va(page_pte & PT64_BASE_ADDR_MASK);
|
||||||
|
int i;
|
||||||
|
gva_t va_delta = 1ul << (PAGE_SHIFT + 9 * (level - 1));
|
||||||
|
|
||||||
|
for (i = 0; i < PT64_ENT_PER_PAGE; ++i, va += va_delta) {
|
||||||
|
u64 ent = pt[i];
|
||||||
|
|
||||||
|
if (!ent & PT_PRESENT_MASK)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
va = canonicalize(va);
|
||||||
|
if (level > 1)
|
||||||
|
audit_mappings_page(vcpu, ent, va, level - 1);
|
||||||
|
else {
|
||||||
|
gpa_t gpa = vcpu->mmu.gva_to_gpa(vcpu, va);
|
||||||
|
hpa_t hpa = gpa_to_hpa(vcpu, gpa);
|
||||||
|
|
||||||
|
if ((ent & PT_PRESENT_MASK)
|
||||||
|
&& (ent & PT64_BASE_ADDR_MASK) != hpa)
|
||||||
|
printk(KERN_ERR "audit error: (%s) levels %d"
|
||||||
|
" gva %lx gpa %llx hpa %llx ent %llx\n",
|
||||||
|
audit_msg, vcpu->mmu.root_level,
|
||||||
|
va, gpa, hpa, ent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void audit_mappings(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (vcpu->mmu.root_level == 4)
|
||||||
|
audit_mappings_page(vcpu, vcpu->mmu.root_hpa, 0, 4);
|
||||||
|
else
|
||||||
|
for (i = 0; i < 4; ++i)
|
||||||
|
if (vcpu->mmu.pae_root[i] & PT_PRESENT_MASK)
|
||||||
|
audit_mappings_page(vcpu,
|
||||||
|
vcpu->mmu.pae_root[i],
|
||||||
|
i << 30,
|
||||||
|
2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int count_rmaps(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
int nmaps = 0;
|
||||||
|
int i, j, k;
|
||||||
|
|
||||||
|
for (i = 0; i < KVM_MEMORY_SLOTS; ++i) {
|
||||||
|
struct kvm_memory_slot *m = &vcpu->kvm->memslots[i];
|
||||||
|
struct kvm_rmap_desc *d;
|
||||||
|
|
||||||
|
for (j = 0; j < m->npages; ++j) {
|
||||||
|
struct page *page = m->phys_mem[j];
|
||||||
|
|
||||||
|
if (!page->private)
|
||||||
|
continue;
|
||||||
|
if (!(page->private & 1)) {
|
||||||
|
++nmaps;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
d = (struct kvm_rmap_desc *)(page->private & ~1ul);
|
||||||
|
while (d) {
|
||||||
|
for (k = 0; k < RMAP_EXT; ++k)
|
||||||
|
if (d->shadow_ptes[k])
|
||||||
|
++nmaps;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
d = d->more;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nmaps;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int count_writable_mappings(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
int nmaps = 0;
|
||||||
|
struct kvm_mmu_page *page;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
list_for_each_entry(page, &vcpu->kvm->active_mmu_pages, link) {
|
||||||
|
u64 *pt = __va(page->page_hpa);
|
||||||
|
|
||||||
|
if (page->role.level != PT_PAGE_TABLE_LEVEL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (i = 0; i < PT64_ENT_PER_PAGE; ++i) {
|
||||||
|
u64 ent = pt[i];
|
||||||
|
|
||||||
|
if (!(ent & PT_PRESENT_MASK))
|
||||||
|
continue;
|
||||||
|
if (!(ent & PT_WRITABLE_MASK))
|
||||||
|
continue;
|
||||||
|
++nmaps;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nmaps;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void audit_rmap(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
int n_rmap = count_rmaps(vcpu);
|
||||||
|
int n_actual = count_writable_mappings(vcpu);
|
||||||
|
|
||||||
|
if (n_rmap != n_actual)
|
||||||
|
printk(KERN_ERR "%s: (%s) rmap %d actual %d\n",
|
||||||
|
__FUNCTION__, audit_msg, n_rmap, n_actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void audit_write_protection(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
struct kvm_mmu_page *page;
|
||||||
|
|
||||||
|
list_for_each_entry(page, &vcpu->kvm->active_mmu_pages, link) {
|
||||||
|
hfn_t hfn;
|
||||||
|
struct page *pg;
|
||||||
|
|
||||||
|
if (page->role.metaphysical)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
hfn = gpa_to_hpa(vcpu, (gpa_t)page->gfn << PAGE_SHIFT)
|
||||||
|
>> PAGE_SHIFT;
|
||||||
|
pg = pfn_to_page(hfn);
|
||||||
|
if (pg->private)
|
||||||
|
printk(KERN_ERR "%s: (%s) shadow page has writable"
|
||||||
|
" mappings: gfn %lx role %x\n",
|
||||||
|
__FUNCTION__, audit_msg, page->gfn,
|
||||||
|
page->role.word);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kvm_mmu_audit(struct kvm_vcpu *vcpu, const char *msg)
|
||||||
|
{
|
||||||
|
int olddbg = dbg;
|
||||||
|
|
||||||
|
dbg = 0;
|
||||||
|
audit_msg = msg;
|
||||||
|
audit_rmap(vcpu);
|
||||||
|
audit_write_protection(vcpu);
|
||||||
|
audit_mappings(vcpu);
|
||||||
|
dbg = olddbg;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -355,6 +355,7 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr,
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
pgprintk("%s: addr %lx err %x\n", __FUNCTION__, addr, error_code);
|
pgprintk("%s: addr %lx err %x\n", __FUNCTION__, addr, error_code);
|
||||||
|
kvm_mmu_audit(vcpu, "pre page fault");
|
||||||
|
|
||||||
r = mmu_topup_memory_caches(vcpu);
|
r = mmu_topup_memory_caches(vcpu);
|
||||||
if (r)
|
if (r)
|
||||||
|
@ -402,6 +403,7 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr,
|
||||||
pgprintk("%s: io work, no access\n", __FUNCTION__);
|
pgprintk("%s: io work, no access\n", __FUNCTION__);
|
||||||
inject_page_fault(vcpu, addr,
|
inject_page_fault(vcpu, addr,
|
||||||
error_code | PFERR_PRESENT_MASK);
|
error_code | PFERR_PRESENT_MASK);
|
||||||
|
kvm_mmu_audit(vcpu, "post page fault (io)");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -410,10 +412,12 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr,
|
||||||
*/
|
*/
|
||||||
if (pte_present && !fixed && !write_pt) {
|
if (pte_present && !fixed && !write_pt) {
|
||||||
inject_page_fault(vcpu, addr, error_code);
|
inject_page_fault(vcpu, addr, error_code);
|
||||||
|
kvm_mmu_audit(vcpu, "post page fault (guest)");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
++kvm_stat.pf_fixed;
|
++kvm_stat.pf_fixed;
|
||||||
|
kvm_mmu_audit(vcpu, "post page fault (fixed)");
|
||||||
|
|
||||||
return write_pt;
|
return write_pt;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue