KVM: x86: add SMM to the MMU role, support SMRAM address space
This is now very simple to do. The only interesting part is a simple trick to find the right memslot in gfn_to_rmap, retrieving the address space from the spte role word. The same trick is used in the auditing code. The comment on top of union kvm_mmu_page_role has been stale forever, so remove it. Speaking of stale code, remove pad_for_nice_hex_output too: it was splitting the "access" bitfield across two bytes and thus had effectively turned into pad_for_ugly_hex_output. Reviewed-by: Radim Krčmář <rkrcmar@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
9da0e4d5ac
commit
699023e239
|
@ -173,6 +173,12 @@ Shadow pages contain the following information:
|
||||||
Contains the value of cr4.smap && !cr0.wp for which the page is valid
|
Contains the value of cr4.smap && !cr0.wp for which the page is valid
|
||||||
(pages for which this is true are different from other pages; see the
|
(pages for which this is true are different from other pages; see the
|
||||||
treatment of cr0.wp=0 below).
|
treatment of cr0.wp=0 below).
|
||||||
|
role.smm:
|
||||||
|
Is 1 if the page is valid in system management mode. This field
|
||||||
|
determines which of the kvm_memslots array was used to build this
|
||||||
|
shadow page; it is also used to go back from a struct kvm_mmu_page
|
||||||
|
to a memslot, through the kvm_memslots_for_spte_role macro and
|
||||||
|
__gfn_to_memslot.
|
||||||
gfn:
|
gfn:
|
||||||
Either the guest page table containing the translations shadowed by this
|
Either the guest page table containing the translations shadowed by this
|
||||||
page, or the base page frame for linear translations. See role.direct.
|
page, or the base page frame for linear translations. See role.direct.
|
||||||
|
|
|
@ -184,23 +184,12 @@ struct kvm_mmu_memory_cache {
|
||||||
void *objects[KVM_NR_MEM_OBJS];
|
void *objects[KVM_NR_MEM_OBJS];
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
* kvm_mmu_page_role, below, is defined as:
|
|
||||||
*
|
|
||||||
* bits 0:3 - total guest paging levels (2-4, or zero for real mode)
|
|
||||||
* bits 4:7 - page table level for this shadow (1-4)
|
|
||||||
* bits 8:9 - page table quadrant for 2-level guests
|
|
||||||
* bit 16 - direct mapping of virtual to physical mapping at gfn
|
|
||||||
* used for real mode and two-dimensional paging
|
|
||||||
* bits 17:19 - common access permissions for all ptes in this shadow page
|
|
||||||
*/
|
|
||||||
union kvm_mmu_page_role {
|
union kvm_mmu_page_role {
|
||||||
unsigned word;
|
unsigned word;
|
||||||
struct {
|
struct {
|
||||||
unsigned level:4;
|
unsigned level:4;
|
||||||
unsigned cr4_pae:1;
|
unsigned cr4_pae:1;
|
||||||
unsigned quadrant:2;
|
unsigned quadrant:2;
|
||||||
unsigned pad_for_nice_hex_output:6;
|
|
||||||
unsigned direct:1;
|
unsigned direct:1;
|
||||||
unsigned access:3;
|
unsigned access:3;
|
||||||
unsigned invalid:1;
|
unsigned invalid:1;
|
||||||
|
@ -208,6 +197,15 @@ union kvm_mmu_page_role {
|
||||||
unsigned cr0_wp:1;
|
unsigned cr0_wp:1;
|
||||||
unsigned smep_andnot_wp:1;
|
unsigned smep_andnot_wp:1;
|
||||||
unsigned smap_andnot_wp:1;
|
unsigned smap_andnot_wp:1;
|
||||||
|
unsigned :8;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is left at the top of the word so that
|
||||||
|
* kvm_memslots_for_spte_role can extract it with a
|
||||||
|
* simple shift. While there is room, give it a whole
|
||||||
|
* byte so it is also faster to load it from memory.
|
||||||
|
*/
|
||||||
|
unsigned smm:8;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1120,6 +1118,12 @@ enum {
|
||||||
#define HF_SMM_MASK (1 << 6)
|
#define HF_SMM_MASK (1 << 6)
|
||||||
#define HF_SMM_INSIDE_NMI_MASK (1 << 7)
|
#define HF_SMM_INSIDE_NMI_MASK (1 << 7)
|
||||||
|
|
||||||
|
#define __KVM_VCPU_MULTIPLE_ADDRESS_SPACE
|
||||||
|
#define KVM_ADDRESS_SPACE_NUM 2
|
||||||
|
|
||||||
|
#define kvm_arch_vcpu_memslots_id(vcpu) ((vcpu)->arch.hflags & HF_SMM_MASK ? 1 : 0)
|
||||||
|
#define kvm_memslots_for_spte_role(kvm, role) __kvm_memslots(kvm, (role).smm)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Hardware virtualization extension instructions may fault if a
|
* Hardware virtualization extension instructions may fault if a
|
||||||
* reboot turns off virtualization while processes are running.
|
* reboot turns off virtualization while processes are running.
|
||||||
|
|
|
@ -806,13 +806,15 @@ static struct kvm_lpage_info *lpage_info_slot(gfn_t gfn,
|
||||||
|
|
||||||
static void account_shadowed(struct kvm *kvm, struct kvm_mmu_page *sp)
|
static void account_shadowed(struct kvm *kvm, struct kvm_mmu_page *sp)
|
||||||
{
|
{
|
||||||
|
struct kvm_memslots *slots;
|
||||||
struct kvm_memory_slot *slot;
|
struct kvm_memory_slot *slot;
|
||||||
struct kvm_lpage_info *linfo;
|
struct kvm_lpage_info *linfo;
|
||||||
gfn_t gfn;
|
gfn_t gfn;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
gfn = sp->gfn;
|
gfn = sp->gfn;
|
||||||
slot = gfn_to_memslot(kvm, gfn);
|
slots = kvm_memslots_for_spte_role(kvm, sp->role);
|
||||||
|
slot = __gfn_to_memslot(slots, gfn);
|
||||||
for (i = PT_DIRECTORY_LEVEL; i <= PT_MAX_HUGEPAGE_LEVEL; ++i) {
|
for (i = PT_DIRECTORY_LEVEL; i <= PT_MAX_HUGEPAGE_LEVEL; ++i) {
|
||||||
linfo = lpage_info_slot(gfn, slot, i);
|
linfo = lpage_info_slot(gfn, slot, i);
|
||||||
linfo->write_count += 1;
|
linfo->write_count += 1;
|
||||||
|
@ -822,13 +824,15 @@ static void account_shadowed(struct kvm *kvm, struct kvm_mmu_page *sp)
|
||||||
|
|
||||||
static void unaccount_shadowed(struct kvm *kvm, struct kvm_mmu_page *sp)
|
static void unaccount_shadowed(struct kvm *kvm, struct kvm_mmu_page *sp)
|
||||||
{
|
{
|
||||||
|
struct kvm_memslots *slots;
|
||||||
struct kvm_memory_slot *slot;
|
struct kvm_memory_slot *slot;
|
||||||
struct kvm_lpage_info *linfo;
|
struct kvm_lpage_info *linfo;
|
||||||
gfn_t gfn;
|
gfn_t gfn;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
gfn = sp->gfn;
|
gfn = sp->gfn;
|
||||||
slot = gfn_to_memslot(kvm, gfn);
|
slots = kvm_memslots_for_spte_role(kvm, sp->role);
|
||||||
|
slot = __gfn_to_memslot(slots, gfn);
|
||||||
for (i = PT_DIRECTORY_LEVEL; i <= PT_MAX_HUGEPAGE_LEVEL; ++i) {
|
for (i = PT_DIRECTORY_LEVEL; i <= PT_MAX_HUGEPAGE_LEVEL; ++i) {
|
||||||
linfo = lpage_info_slot(gfn, slot, i);
|
linfo = lpage_info_slot(gfn, slot, i);
|
||||||
linfo->write_count -= 1;
|
linfo->write_count -= 1;
|
||||||
|
@ -1045,9 +1049,11 @@ static unsigned long *__gfn_to_rmap(gfn_t gfn, int level,
|
||||||
*/
|
*/
|
||||||
static unsigned long *gfn_to_rmap(struct kvm *kvm, gfn_t gfn, struct kvm_mmu_page *sp)
|
static unsigned long *gfn_to_rmap(struct kvm *kvm, gfn_t gfn, struct kvm_mmu_page *sp)
|
||||||
{
|
{
|
||||||
|
struct kvm_memslots *slots;
|
||||||
struct kvm_memory_slot *slot;
|
struct kvm_memory_slot *slot;
|
||||||
|
|
||||||
slot = gfn_to_memslot(kvm, gfn);
|
slots = kvm_memslots_for_spte_role(kvm, sp->role);
|
||||||
|
slot = __gfn_to_memslot(slots, gfn);
|
||||||
return __gfn_to_rmap(gfn, sp->role.level, slot);
|
return __gfn_to_rmap(gfn, sp->role.level, slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3924,6 +3930,7 @@ static void init_kvm_tdp_mmu(struct kvm_vcpu *vcpu)
|
||||||
struct kvm_mmu *context = &vcpu->arch.mmu;
|
struct kvm_mmu *context = &vcpu->arch.mmu;
|
||||||
|
|
||||||
context->base_role.word = 0;
|
context->base_role.word = 0;
|
||||||
|
context->base_role.smm = is_smm(vcpu);
|
||||||
context->page_fault = tdp_page_fault;
|
context->page_fault = tdp_page_fault;
|
||||||
context->sync_page = nonpaging_sync_page;
|
context->sync_page = nonpaging_sync_page;
|
||||||
context->invlpg = nonpaging_invlpg;
|
context->invlpg = nonpaging_invlpg;
|
||||||
|
@ -3985,6 +3992,7 @@ void kvm_init_shadow_mmu(struct kvm_vcpu *vcpu)
|
||||||
= smep && !is_write_protection(vcpu);
|
= smep && !is_write_protection(vcpu);
|
||||||
context->base_role.smap_andnot_wp
|
context->base_role.smap_andnot_wp
|
||||||
= smap && !is_write_protection(vcpu);
|
= smap && !is_write_protection(vcpu);
|
||||||
|
context->base_role.smm = is_smm(vcpu);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(kvm_init_shadow_mmu);
|
EXPORT_SYMBOL_GPL(kvm_init_shadow_mmu);
|
||||||
|
|
||||||
|
@ -4268,6 +4276,7 @@ void kvm_mmu_pte_write(struct kvm_vcpu *vcpu, gpa_t gpa,
|
||||||
mask.nxe = 1;
|
mask.nxe = 1;
|
||||||
mask.smep_andnot_wp = 1;
|
mask.smep_andnot_wp = 1;
|
||||||
mask.smap_andnot_wp = 1;
|
mask.smap_andnot_wp = 1;
|
||||||
|
mask.smm = 1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we don't have indirect shadow pages, it means no page is
|
* If we don't have indirect shadow pages, it means no page is
|
||||||
|
|
|
@ -131,12 +131,16 @@ static void inspect_spte_has_rmap(struct kvm *kvm, u64 *sptep)
|
||||||
static DEFINE_RATELIMIT_STATE(ratelimit_state, 5 * HZ, 10);
|
static DEFINE_RATELIMIT_STATE(ratelimit_state, 5 * HZ, 10);
|
||||||
unsigned long *rmapp;
|
unsigned long *rmapp;
|
||||||
struct kvm_mmu_page *rev_sp;
|
struct kvm_mmu_page *rev_sp;
|
||||||
|
struct kvm_memslots *slots;
|
||||||
|
struct kvm_memory_slot *slot;
|
||||||
gfn_t gfn;
|
gfn_t gfn;
|
||||||
|
|
||||||
rev_sp = page_header(__pa(sptep));
|
rev_sp = page_header(__pa(sptep));
|
||||||
gfn = kvm_mmu_page_get_gfn(rev_sp, sptep - rev_sp->spt);
|
gfn = kvm_mmu_page_get_gfn(rev_sp, sptep - rev_sp->spt);
|
||||||
|
|
||||||
if (!gfn_to_memslot(kvm, gfn)) {
|
slots = kvm_memslots_for_spte_role(kvm, rev_sp->role);
|
||||||
|
slot = __gfn_to_memslot(slots, gfn);
|
||||||
|
if (!slot) {
|
||||||
if (!__ratelimit(&ratelimit_state))
|
if (!__ratelimit(&ratelimit_state))
|
||||||
return;
|
return;
|
||||||
audit_printk(kvm, "no memslot for gfn %llx\n", gfn);
|
audit_printk(kvm, "no memslot for gfn %llx\n", gfn);
|
||||||
|
@ -146,7 +150,7 @@ static void inspect_spte_has_rmap(struct kvm *kvm, u64 *sptep)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
rmapp = gfn_to_rmap(kvm, gfn, rev_sp);
|
rmapp = __gfn_to_rmap(gfn, rev_sp->role.level, slot);
|
||||||
if (!*rmapp) {
|
if (!*rmapp) {
|
||||||
if (!__ratelimit(&ratelimit_state))
|
if (!__ratelimit(&ratelimit_state))
|
||||||
return;
|
return;
|
||||||
|
@ -197,7 +201,7 @@ static void audit_write_protection(struct kvm *kvm, struct kvm_mmu_page *sp)
|
||||||
if (sp->role.direct || sp->unsync || sp->role.invalid)
|
if (sp->role.direct || sp->unsync || sp->role.invalid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
slots = kvm_memslots(kvm);
|
slots = kvm_memslots_for_spte_role(kvm, sp->role);
|
||||||
slot = __gfn_to_memslot(slots, sp->gfn);
|
slot = __gfn_to_memslot(slots, sp->gfn);
|
||||||
rmapp = __gfn_to_rmap(sp->gfn, PT_PAGE_TABLE_LEVEL, slot);
|
rmapp = __gfn_to_rmap(sp->gfn, PT_PAGE_TABLE_LEVEL, slot);
|
||||||
|
|
||||||
|
|
|
@ -5490,6 +5490,8 @@ static void kvm_smm_changed(struct kvm_vcpu *vcpu)
|
||||||
kvm_make_request(KVM_REQ_EVENT, vcpu);
|
kvm_make_request(KVM_REQ_EVENT, vcpu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kvm_mmu_reset_context(vcpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void kvm_set_hflags(struct kvm_vcpu *vcpu, unsigned emul_flags)
|
static void kvm_set_hflags(struct kvm_vcpu *vcpu, unsigned emul_flags)
|
||||||
|
|
Loading…
Reference in New Issue