mirror of
https://github.com/torvalds/linux.git
synced 2026-05-29 17:43:52 +02:00
Merge branch kvm-arm64/pkvm-np-guest into kvmarm-master/next
* kvm-arm64/pkvm-np-guest:
: .
: pKVM support for non-protected guests using the standard MM
: infrastructure, courtesy of Quentin Perret. From the cover letter:
:
: "This series moves the stage-2 page-table management of non-protected
: guests to EL2 when pKVM is enabled. This is only intended as an
: incremental step towards a 'feature-complete' pKVM, there is however a
: lot more that needs to come on top.
:
: With that series applied, pKVM provides near-parity with standard KVM
: from a functional perspective all while Linux no longer touches the
: stage-2 page-tables itself at EL1. The majority of mm-related KVM
: features work out of the box, including MMU notifiers, dirty logging,
: RO memslots and things of that nature. There are however two gotchas:
:
: - We don't support mapping devices into guests: this requires
: additional hypervisor support for tracking the 'state' of devices,
: which will come in a later series. No device assignment until then.
:
: - Stage-2 mappings are forced to page-granularity even when backed by a
: huge page for the sake of simplicity of this series. I'm only aiming
: at functional parity-ish (from userspace's PoV) for now, support for
: HP can be added on top later as a perf improvement."
: .
KVM: arm64: Plumb the pKVM MMU in KVM
KVM: arm64: Introduce the EL1 pKVM MMU
KVM: arm64: Introduce __pkvm_tlb_flush_vmid()
KVM: arm64: Introduce __pkvm_host_mkyoung_guest()
KVM: arm64: Introduce __pkvm_host_test_clear_young_guest()
KVM: arm64: Introduce __pkvm_host_wrprotect_guest()
KVM: arm64: Introduce __pkvm_host_relax_guest_perms()
KVM: arm64: Introduce __pkvm_host_unshare_guest()
KVM: arm64: Introduce __pkvm_host_share_guest()
KVM: arm64: Introduce __pkvm_vcpu_{load,put}()
KVM: arm64: Add {get,put}_pkvm_hyp_vm() helpers
KVM: arm64: Make kvm_pgtable_stage2_init() a static inline function
KVM: arm64: Pass walk flags to kvm_pgtable_stage2_relax_perms
KVM: arm64: Pass walk flags to kvm_pgtable_stage2_mkyoung
KVM: arm64: Move host page ownership tracking to the hyp vmemmap
KVM: arm64: Make hyp_page::order a u8
KVM: arm64: Move enum pkvm_page_state to memory.h
KVM: arm64: Change the layout of enum pkvm_page_state
Signed-off-by: Marc Zyngier <maz@kernel.org>
# Conflicts:
# arch/arm64/kvm/arm.c
This commit is contained in:
commit
d0670128d4
|
|
@ -64,6 +64,12 @@ enum __kvm_host_smccc_func {
|
|||
/* Hypercalls available after pKVM finalisation */
|
||||
__KVM_HOST_SMCCC_FUNC___pkvm_host_share_hyp,
|
||||
__KVM_HOST_SMCCC_FUNC___pkvm_host_unshare_hyp,
|
||||
__KVM_HOST_SMCCC_FUNC___pkvm_host_share_guest,
|
||||
__KVM_HOST_SMCCC_FUNC___pkvm_host_unshare_guest,
|
||||
__KVM_HOST_SMCCC_FUNC___pkvm_host_relax_perms_guest,
|
||||
__KVM_HOST_SMCCC_FUNC___pkvm_host_wrprotect_guest,
|
||||
__KVM_HOST_SMCCC_FUNC___pkvm_host_test_clear_young_guest,
|
||||
__KVM_HOST_SMCCC_FUNC___pkvm_host_mkyoung_guest,
|
||||
__KVM_HOST_SMCCC_FUNC___kvm_adjust_pc,
|
||||
__KVM_HOST_SMCCC_FUNC___kvm_vcpu_run,
|
||||
__KVM_HOST_SMCCC_FUNC___kvm_flush_vm_context,
|
||||
|
|
@ -78,6 +84,9 @@ enum __kvm_host_smccc_func {
|
|||
__KVM_HOST_SMCCC_FUNC___pkvm_init_vm,
|
||||
__KVM_HOST_SMCCC_FUNC___pkvm_init_vcpu,
|
||||
__KVM_HOST_SMCCC_FUNC___pkvm_teardown_vm,
|
||||
__KVM_HOST_SMCCC_FUNC___pkvm_vcpu_load,
|
||||
__KVM_HOST_SMCCC_FUNC___pkvm_vcpu_put,
|
||||
__KVM_HOST_SMCCC_FUNC___pkvm_tlb_flush_vmid,
|
||||
};
|
||||
|
||||
#define DECLARE_KVM_VHE_SYM(sym) extern char sym[]
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ void kvm_arm_vcpu_destroy(struct kvm_vcpu *vcpu);
|
|||
struct kvm_hyp_memcache {
|
||||
phys_addr_t head;
|
||||
unsigned long nr_pages;
|
||||
struct pkvm_mapping *mapping; /* only used from EL1 */
|
||||
};
|
||||
|
||||
static inline void push_hyp_memcache(struct kvm_hyp_memcache *mc,
|
||||
|
|
@ -775,6 +776,9 @@ struct kvm_vcpu_arch {
|
|||
/* Cache some mmu pages needed inside spinlock regions */
|
||||
struct kvm_mmu_memory_cache mmu_page_cache;
|
||||
|
||||
/* Pages to top-up the pKVM/EL2 guest pool */
|
||||
struct kvm_hyp_memcache pkvm_memcache;
|
||||
|
||||
/* Virtual SError ESR to restore when HCR_EL2.VSE is set */
|
||||
u64 vsesr_el2;
|
||||
|
||||
|
|
|
|||
|
|
@ -353,6 +353,22 @@ static inline bool kvm_is_nested_s2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu)
|
|||
return &kvm->arch.mmu != mmu;
|
||||
}
|
||||
|
||||
static inline void kvm_fault_lock(struct kvm *kvm)
|
||||
{
|
||||
if (is_protected_kvm_enabled())
|
||||
write_lock(&kvm->mmu_lock);
|
||||
else
|
||||
read_lock(&kvm->mmu_lock);
|
||||
}
|
||||
|
||||
static inline void kvm_fault_unlock(struct kvm *kvm)
|
||||
{
|
||||
if (is_protected_kvm_enabled())
|
||||
write_unlock(&kvm->mmu_lock);
|
||||
else
|
||||
read_unlock(&kvm->mmu_lock);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PTDUMP_STAGE2_DEBUGFS
|
||||
void kvm_s2_ptdump_create_debugfs(struct kvm *kvm);
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -412,15 +412,20 @@ static inline bool kvm_pgtable_walk_lock_held(void)
|
|||
* be used instead of block mappings.
|
||||
*/
|
||||
struct kvm_pgtable {
|
||||
u32 ia_bits;
|
||||
s8 start_level;
|
||||
kvm_pteref_t pgd;
|
||||
struct kvm_pgtable_mm_ops *mm_ops;
|
||||
union {
|
||||
struct rb_root pkvm_mappings;
|
||||
struct {
|
||||
u32 ia_bits;
|
||||
s8 start_level;
|
||||
kvm_pteref_t pgd;
|
||||
struct kvm_pgtable_mm_ops *mm_ops;
|
||||
|
||||
/* Stage-2 only */
|
||||
struct kvm_s2_mmu *mmu;
|
||||
enum kvm_pgtable_stage2_flags flags;
|
||||
kvm_pgtable_force_pte_cb_t force_pte_cb;
|
||||
/* Stage-2 only */
|
||||
enum kvm_pgtable_stage2_flags flags;
|
||||
kvm_pgtable_force_pte_cb_t force_pte_cb;
|
||||
};
|
||||
};
|
||||
struct kvm_s2_mmu *mmu;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -526,8 +531,11 @@ int __kvm_pgtable_stage2_init(struct kvm_pgtable *pgt, struct kvm_s2_mmu *mmu,
|
|||
enum kvm_pgtable_stage2_flags flags,
|
||||
kvm_pgtable_force_pte_cb_t force_pte_cb);
|
||||
|
||||
#define kvm_pgtable_stage2_init(pgt, mmu, mm_ops) \
|
||||
__kvm_pgtable_stage2_init(pgt, mmu, mm_ops, 0, NULL)
|
||||
static inline int kvm_pgtable_stage2_init(struct kvm_pgtable *pgt, struct kvm_s2_mmu *mmu,
|
||||
struct kvm_pgtable_mm_ops *mm_ops)
|
||||
{
|
||||
return __kvm_pgtable_stage2_init(pgt, mmu, mm_ops, 0, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_pgtable_stage2_destroy() - Destroy an unused guest stage-2 page-table.
|
||||
|
|
@ -669,13 +677,15 @@ int kvm_pgtable_stage2_wrprotect(struct kvm_pgtable *pgt, u64 addr, u64 size);
|
|||
* kvm_pgtable_stage2_mkyoung() - Set the access flag in a page-table entry.
|
||||
* @pgt: Page-table structure initialised by kvm_pgtable_stage2_init*().
|
||||
* @addr: Intermediate physical address to identify the page-table entry.
|
||||
* @flags: Flags to control the page-table walk (ex. a shared walk)
|
||||
*
|
||||
* The offset of @addr within a page is ignored.
|
||||
*
|
||||
* If there is a valid, leaf page-table entry used to translate @addr, then
|
||||
* set the access flag in that entry.
|
||||
*/
|
||||
void kvm_pgtable_stage2_mkyoung(struct kvm_pgtable *pgt, u64 addr);
|
||||
void kvm_pgtable_stage2_mkyoung(struct kvm_pgtable *pgt, u64 addr,
|
||||
enum kvm_pgtable_walk_flags flags);
|
||||
|
||||
/**
|
||||
* kvm_pgtable_stage2_test_clear_young() - Test and optionally clear the access
|
||||
|
|
@ -705,6 +715,7 @@ bool kvm_pgtable_stage2_test_clear_young(struct kvm_pgtable *pgt, u64 addr,
|
|||
* @pgt: Page-table structure initialised by kvm_pgtable_stage2_init*().
|
||||
* @addr: Intermediate physical address to identify the page-table entry.
|
||||
* @prot: Additional permissions to grant for the mapping.
|
||||
* @flags: Flags to control the page-table walk (ex. a shared walk)
|
||||
*
|
||||
* The offset of @addr within a page is ignored.
|
||||
*
|
||||
|
|
@ -717,7 +728,8 @@ bool kvm_pgtable_stage2_test_clear_young(struct kvm_pgtable *pgt, u64 addr,
|
|||
* Return: 0 on success, negative error code on failure.
|
||||
*/
|
||||
int kvm_pgtable_stage2_relax_perms(struct kvm_pgtable *pgt, u64 addr,
|
||||
enum kvm_pgtable_prot prot);
|
||||
enum kvm_pgtable_prot prot,
|
||||
enum kvm_pgtable_walk_flags flags);
|
||||
|
||||
/**
|
||||
* kvm_pgtable_stage2_flush_range() - Clean and invalidate data cache to Point
|
||||
|
|
|
|||
|
|
@ -137,4 +137,30 @@ static inline size_t pkvm_host_sve_state_size(void)
|
|||
SVE_SIG_REGS_SIZE(sve_vq_from_vl(kvm_host_sve_max_vl)));
|
||||
}
|
||||
|
||||
struct pkvm_mapping {
|
||||
struct rb_node node;
|
||||
u64 gfn;
|
||||
u64 pfn;
|
||||
};
|
||||
|
||||
int pkvm_pgtable_stage2_init(struct kvm_pgtable *pgt, struct kvm_s2_mmu *mmu,
|
||||
struct kvm_pgtable_mm_ops *mm_ops);
|
||||
void pkvm_pgtable_stage2_destroy(struct kvm_pgtable *pgt);
|
||||
int pkvm_pgtable_stage2_map(struct kvm_pgtable *pgt, u64 addr, u64 size, u64 phys,
|
||||
enum kvm_pgtable_prot prot, void *mc,
|
||||
enum kvm_pgtable_walk_flags flags);
|
||||
int pkvm_pgtable_stage2_unmap(struct kvm_pgtable *pgt, u64 addr, u64 size);
|
||||
int pkvm_pgtable_stage2_wrprotect(struct kvm_pgtable *pgt, u64 addr, u64 size);
|
||||
int pkvm_pgtable_stage2_flush(struct kvm_pgtable *pgt, u64 addr, u64 size);
|
||||
bool pkvm_pgtable_stage2_test_clear_young(struct kvm_pgtable *pgt, u64 addr, u64 size, bool mkold);
|
||||
int pkvm_pgtable_stage2_relax_perms(struct kvm_pgtable *pgt, u64 addr, enum kvm_pgtable_prot prot,
|
||||
enum kvm_pgtable_walk_flags flags);
|
||||
void pkvm_pgtable_stage2_mkyoung(struct kvm_pgtable *pgt, u64 addr,
|
||||
enum kvm_pgtable_walk_flags flags);
|
||||
int pkvm_pgtable_stage2_split(struct kvm_pgtable *pgt, u64 addr, u64 size,
|
||||
struct kvm_mmu_memory_cache *mc);
|
||||
void pkvm_pgtable_stage2_free_unlinked(struct kvm_pgtable_mm_ops *mm_ops, void *pgtable, s8 level);
|
||||
kvm_pte_t *pkvm_pgtable_stage2_create_unlinked(struct kvm_pgtable *pgt, u64 phys, s8 level,
|
||||
enum kvm_pgtable_prot prot, void *mc,
|
||||
bool force_pte);
|
||||
#endif /* __ARM64_KVM_PKVM_H__ */
|
||||
|
|
|
|||
|
|
@ -500,7 +500,10 @@ void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu)
|
|||
|
||||
void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
kvm_mmu_free_memory_cache(&vcpu->arch.mmu_page_cache);
|
||||
if (!is_protected_kvm_enabled())
|
||||
kvm_mmu_free_memory_cache(&vcpu->arch.mmu_page_cache);
|
||||
else
|
||||
free_hyp_memcache(&vcpu->arch.pkvm_memcache);
|
||||
kvm_timer_vcpu_terminate(vcpu);
|
||||
kvm_pmu_vcpu_destroy(vcpu);
|
||||
kvm_vgic_vcpu_destroy(vcpu);
|
||||
|
|
@ -572,6 +575,9 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
|
|||
struct kvm_s2_mmu *mmu;
|
||||
int *last_ran;
|
||||
|
||||
if (is_protected_kvm_enabled())
|
||||
goto nommu;
|
||||
|
||||
if (vcpu_has_nv(vcpu))
|
||||
kvm_vcpu_load_hw_mmu(vcpu);
|
||||
|
||||
|
|
@ -592,6 +598,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
|
|||
*last_ran = vcpu->vcpu_idx;
|
||||
}
|
||||
|
||||
nommu:
|
||||
vcpu->cpu = cpu;
|
||||
|
||||
kvm_vgic_load(vcpu);
|
||||
|
|
@ -616,12 +623,26 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
|
|||
|
||||
vcpu_set_pauth_traps(vcpu);
|
||||
|
||||
if (is_protected_kvm_enabled()) {
|
||||
kvm_call_hyp_nvhe(__pkvm_vcpu_load,
|
||||
vcpu->kvm->arch.pkvm.handle,
|
||||
vcpu->vcpu_idx, vcpu->arch.hcr_el2);
|
||||
kvm_call_hyp(__vgic_v3_restore_vmcr_aprs,
|
||||
&vcpu->arch.vgic_cpu.vgic_v3);
|
||||
}
|
||||
|
||||
if (!cpumask_test_cpu(cpu, vcpu->kvm->arch.supported_cpus))
|
||||
vcpu_set_on_unsupported_cpu(vcpu);
|
||||
}
|
||||
|
||||
void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (is_protected_kvm_enabled()) {
|
||||
kvm_call_hyp(__vgic_v3_save_vmcr_aprs,
|
||||
&vcpu->arch.vgic_cpu.vgic_v3);
|
||||
kvm_call_hyp_nvhe(__pkvm_vcpu_put);
|
||||
}
|
||||
|
||||
kvm_vcpu_put_debug(vcpu);
|
||||
kvm_arch_vcpu_put_fp(vcpu);
|
||||
if (has_vhe())
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
#include <nvhe/memory.h>
|
||||
#include <nvhe/spinlock.h>
|
||||
|
||||
#define HYP_NO_ORDER USHRT_MAX
|
||||
#define HYP_NO_ORDER ((u8)(~0))
|
||||
|
||||
struct hyp_pool {
|
||||
/*
|
||||
|
|
@ -19,11 +19,11 @@ struct hyp_pool {
|
|||
struct list_head free_area[NR_PAGE_ORDERS];
|
||||
phys_addr_t range_start;
|
||||
phys_addr_t range_end;
|
||||
unsigned short max_order;
|
||||
u8 max_order;
|
||||
};
|
||||
|
||||
/* Allocation */
|
||||
void *hyp_alloc_pages(struct hyp_pool *pool, unsigned short order);
|
||||
void *hyp_alloc_pages(struct hyp_pool *pool, u8 order);
|
||||
void hyp_split_page(struct hyp_page *page);
|
||||
void hyp_get_page(struct hyp_pool *pool, void *addr);
|
||||
void hyp_put_page(struct hyp_pool *pool, void *addr);
|
||||
|
|
|
|||
|
|
@ -11,40 +11,10 @@
|
|||
#include <asm/kvm_mmu.h>
|
||||
#include <asm/kvm_pgtable.h>
|
||||
#include <asm/virt.h>
|
||||
#include <nvhe/memory.h>
|
||||
#include <nvhe/pkvm.h>
|
||||
#include <nvhe/spinlock.h>
|
||||
|
||||
/*
|
||||
* SW bits 0-1 are reserved to track the memory ownership state of each page:
|
||||
* 00: The page is owned exclusively by the page-table owner.
|
||||
* 01: The page is owned by the page-table owner, but is shared
|
||||
* with another entity.
|
||||
* 10: The page is shared with, but not owned by the page-table owner.
|
||||
* 11: Reserved for future use (lending).
|
||||
*/
|
||||
enum pkvm_page_state {
|
||||
PKVM_PAGE_OWNED = 0ULL,
|
||||
PKVM_PAGE_SHARED_OWNED = KVM_PGTABLE_PROT_SW0,
|
||||
PKVM_PAGE_SHARED_BORROWED = KVM_PGTABLE_PROT_SW1,
|
||||
__PKVM_PAGE_RESERVED = KVM_PGTABLE_PROT_SW0 |
|
||||
KVM_PGTABLE_PROT_SW1,
|
||||
|
||||
/* Meta-states which aren't encoded directly in the PTE's SW bits */
|
||||
PKVM_NOPAGE,
|
||||
};
|
||||
|
||||
#define PKVM_PAGE_STATE_PROT_MASK (KVM_PGTABLE_PROT_SW0 | KVM_PGTABLE_PROT_SW1)
|
||||
static inline enum kvm_pgtable_prot pkvm_mkstate(enum kvm_pgtable_prot prot,
|
||||
enum pkvm_page_state state)
|
||||
{
|
||||
return (prot & ~PKVM_PAGE_STATE_PROT_MASK) | state;
|
||||
}
|
||||
|
||||
static inline enum pkvm_page_state pkvm_getstate(enum kvm_pgtable_prot prot)
|
||||
{
|
||||
return prot & PKVM_PAGE_STATE_PROT_MASK;
|
||||
}
|
||||
|
||||
struct host_mmu {
|
||||
struct kvm_arch arch;
|
||||
struct kvm_pgtable pgt;
|
||||
|
|
@ -69,6 +39,13 @@ int __pkvm_host_donate_hyp(u64 pfn, u64 nr_pages);
|
|||
int __pkvm_hyp_donate_host(u64 pfn, u64 nr_pages);
|
||||
int __pkvm_host_share_ffa(u64 pfn, u64 nr_pages);
|
||||
int __pkvm_host_unshare_ffa(u64 pfn, u64 nr_pages);
|
||||
int __pkvm_host_share_guest(u64 pfn, u64 gfn, struct pkvm_hyp_vcpu *vcpu,
|
||||
enum kvm_pgtable_prot prot);
|
||||
int __pkvm_host_unshare_guest(u64 gfn, struct pkvm_hyp_vm *hyp_vm);
|
||||
int __pkvm_host_relax_perms_guest(u64 gfn, struct pkvm_hyp_vcpu *vcpu, enum kvm_pgtable_prot prot);
|
||||
int __pkvm_host_wrprotect_guest(u64 gfn, struct pkvm_hyp_vm *hyp_vm);
|
||||
int __pkvm_host_test_clear_young_guest(u64 gfn, bool mkold, struct pkvm_hyp_vm *vm);
|
||||
int __pkvm_host_mkyoung_guest(u64 gfn, struct pkvm_hyp_vcpu *vcpu);
|
||||
|
||||
bool addr_is_memory(phys_addr_t phys);
|
||||
int host_stage2_idmap_locked(phys_addr_t addr, u64 size, enum kvm_pgtable_prot prot);
|
||||
|
|
|
|||
|
|
@ -7,9 +7,47 @@
|
|||
|
||||
#include <linux/types.h>
|
||||
|
||||
/*
|
||||
* Bits 0-1 are reserved to track the memory ownership state of each page:
|
||||
* 00: The page is owned exclusively by the page-table owner.
|
||||
* 01: The page is owned by the page-table owner, but is shared
|
||||
* with another entity.
|
||||
* 10: The page is shared with, but not owned by the page-table owner.
|
||||
* 11: Reserved for future use (lending).
|
||||
*/
|
||||
enum pkvm_page_state {
|
||||
PKVM_PAGE_OWNED = 0ULL,
|
||||
PKVM_PAGE_SHARED_OWNED = BIT(0),
|
||||
PKVM_PAGE_SHARED_BORROWED = BIT(1),
|
||||
__PKVM_PAGE_RESERVED = BIT(0) | BIT(1),
|
||||
|
||||
/* Meta-states which aren't encoded directly in the PTE's SW bits */
|
||||
PKVM_NOPAGE = BIT(2),
|
||||
};
|
||||
#define PKVM_PAGE_META_STATES_MASK (~__PKVM_PAGE_RESERVED)
|
||||
|
||||
#define PKVM_PAGE_STATE_PROT_MASK (KVM_PGTABLE_PROT_SW0 | KVM_PGTABLE_PROT_SW1)
|
||||
static inline enum kvm_pgtable_prot pkvm_mkstate(enum kvm_pgtable_prot prot,
|
||||
enum pkvm_page_state state)
|
||||
{
|
||||
prot &= ~PKVM_PAGE_STATE_PROT_MASK;
|
||||
prot |= FIELD_PREP(PKVM_PAGE_STATE_PROT_MASK, state);
|
||||
return prot;
|
||||
}
|
||||
|
||||
static inline enum pkvm_page_state pkvm_getstate(enum kvm_pgtable_prot prot)
|
||||
{
|
||||
return FIELD_GET(PKVM_PAGE_STATE_PROT_MASK, prot);
|
||||
}
|
||||
|
||||
struct hyp_page {
|
||||
unsigned short refcount;
|
||||
unsigned short order;
|
||||
u16 refcount;
|
||||
u8 order;
|
||||
|
||||
/* Host (non-meta) state. Guarded by the host stage-2 lock. */
|
||||
enum pkvm_page_state host_state : 8;
|
||||
|
||||
u32 host_share_guest_count;
|
||||
};
|
||||
|
||||
extern u64 __hyp_vmemmap;
|
||||
|
|
@ -29,7 +67,13 @@ static inline phys_addr_t hyp_virt_to_phys(void *addr)
|
|||
|
||||
#define hyp_phys_to_pfn(phys) ((phys) >> PAGE_SHIFT)
|
||||
#define hyp_pfn_to_phys(pfn) ((phys_addr_t)((pfn) << PAGE_SHIFT))
|
||||
#define hyp_phys_to_page(phys) (&hyp_vmemmap[hyp_phys_to_pfn(phys)])
|
||||
|
||||
static inline struct hyp_page *hyp_phys_to_page(phys_addr_t phys)
|
||||
{
|
||||
BUILD_BUG_ON(sizeof(struct hyp_page) != sizeof(u64));
|
||||
return &hyp_vmemmap[hyp_phys_to_pfn(phys)];
|
||||
}
|
||||
|
||||
#define hyp_virt_to_page(virt) hyp_phys_to_page(__hyp_pa(virt))
|
||||
#define hyp_virt_to_pfn(virt) hyp_phys_to_pfn(__hyp_pa(virt))
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,12 @@ struct pkvm_hyp_vcpu {
|
|||
|
||||
/* Backpointer to the host's (untrusted) vCPU instance. */
|
||||
struct kvm_vcpu *host_vcpu;
|
||||
|
||||
/*
|
||||
* If this hyp vCPU is loaded, then this is a backpointer to the
|
||||
* per-cpu pointer tracking us. Otherwise, NULL if not loaded.
|
||||
*/
|
||||
struct pkvm_hyp_vcpu **loaded_hyp_vcpu;
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
@ -58,6 +64,11 @@ static inline bool pkvm_hyp_vcpu_is_protected(struct pkvm_hyp_vcpu *hyp_vcpu)
|
|||
return vcpu_is_protected(&hyp_vcpu->vcpu);
|
||||
}
|
||||
|
||||
static inline bool pkvm_hyp_vm_is_protected(struct pkvm_hyp_vm *hyp_vm)
|
||||
{
|
||||
return kvm_vm_is_protected(&hyp_vm->kvm);
|
||||
}
|
||||
|
||||
void pkvm_hyp_vm_table_init(void *tbl);
|
||||
|
||||
int __pkvm_init_vm(struct kvm *host_kvm, unsigned long vm_hva,
|
||||
|
|
@ -69,5 +80,10 @@ int __pkvm_teardown_vm(pkvm_handle_t handle);
|
|||
struct pkvm_hyp_vcpu *pkvm_load_hyp_vcpu(pkvm_handle_t handle,
|
||||
unsigned int vcpu_idx);
|
||||
void pkvm_put_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu);
|
||||
struct pkvm_hyp_vcpu *pkvm_get_loaded_hyp_vcpu(void);
|
||||
|
||||
struct pkvm_hyp_vm *get_pkvm_hyp_vm(pkvm_handle_t handle);
|
||||
struct pkvm_hyp_vm *get_np_pkvm_hyp_vm(pkvm_handle_t handle);
|
||||
void put_pkvm_hyp_vm(struct pkvm_hyp_vm *hyp_vm);
|
||||
|
||||
#endif /* __ARM64_KVM_NVHE_PKVM_H__ */
|
||||
|
|
|
|||
|
|
@ -103,8 +103,6 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
|
|||
/* Limit guest vector length to the maximum supported by the host. */
|
||||
hyp_vcpu->vcpu.arch.sve_max_vl = min(host_vcpu->arch.sve_max_vl, kvm_host_sve_max_vl);
|
||||
|
||||
hyp_vcpu->vcpu.arch.hw_mmu = host_vcpu->arch.hw_mmu;
|
||||
|
||||
hyp_vcpu->vcpu.arch.mdcr_el2 = host_vcpu->arch.mdcr_el2;
|
||||
hyp_vcpu->vcpu.arch.hcr_el2 &= ~(HCR_TWI | HCR_TWE);
|
||||
hyp_vcpu->vcpu.arch.hcr_el2 |= READ_ONCE(host_vcpu->arch.hcr_el2) &
|
||||
|
|
@ -139,16 +137,46 @@ static void sync_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
|
|||
host_cpu_if->vgic_lr[i] = hyp_cpu_if->vgic_lr[i];
|
||||
}
|
||||
|
||||
static void handle___pkvm_vcpu_load(struct kvm_cpu_context *host_ctxt)
|
||||
{
|
||||
DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1);
|
||||
DECLARE_REG(unsigned int, vcpu_idx, host_ctxt, 2);
|
||||
DECLARE_REG(u64, hcr_el2, host_ctxt, 3);
|
||||
struct pkvm_hyp_vcpu *hyp_vcpu;
|
||||
|
||||
if (!is_protected_kvm_enabled())
|
||||
return;
|
||||
|
||||
hyp_vcpu = pkvm_load_hyp_vcpu(handle, vcpu_idx);
|
||||
if (!hyp_vcpu)
|
||||
return;
|
||||
|
||||
if (pkvm_hyp_vcpu_is_protected(hyp_vcpu)) {
|
||||
/* Propagate WFx trapping flags */
|
||||
hyp_vcpu->vcpu.arch.hcr_el2 &= ~(HCR_TWE | HCR_TWI);
|
||||
hyp_vcpu->vcpu.arch.hcr_el2 |= hcr_el2 & (HCR_TWE | HCR_TWI);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle___pkvm_vcpu_put(struct kvm_cpu_context *host_ctxt)
|
||||
{
|
||||
struct pkvm_hyp_vcpu *hyp_vcpu;
|
||||
|
||||
if (!is_protected_kvm_enabled())
|
||||
return;
|
||||
|
||||
hyp_vcpu = pkvm_get_loaded_hyp_vcpu();
|
||||
if (hyp_vcpu)
|
||||
pkvm_put_hyp_vcpu(hyp_vcpu);
|
||||
}
|
||||
|
||||
static void handle___kvm_vcpu_run(struct kvm_cpu_context *host_ctxt)
|
||||
{
|
||||
DECLARE_REG(struct kvm_vcpu *, host_vcpu, host_ctxt, 1);
|
||||
int ret;
|
||||
|
||||
host_vcpu = kern_hyp_va(host_vcpu);
|
||||
|
||||
if (unlikely(is_protected_kvm_enabled())) {
|
||||
struct pkvm_hyp_vcpu *hyp_vcpu;
|
||||
struct kvm *host_kvm;
|
||||
struct pkvm_hyp_vcpu *hyp_vcpu = pkvm_get_loaded_hyp_vcpu();
|
||||
|
||||
/*
|
||||
* KVM (and pKVM) doesn't support SME guests for now, and
|
||||
|
|
@ -161,9 +189,6 @@ static void handle___kvm_vcpu_run(struct kvm_cpu_context *host_ctxt)
|
|||
goto out;
|
||||
}
|
||||
|
||||
host_kvm = kern_hyp_va(host_vcpu->kvm);
|
||||
hyp_vcpu = pkvm_load_hyp_vcpu(host_kvm->arch.pkvm.handle,
|
||||
host_vcpu->vcpu_idx);
|
||||
if (!hyp_vcpu) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
|
|
@ -174,12 +199,141 @@ static void handle___kvm_vcpu_run(struct kvm_cpu_context *host_ctxt)
|
|||
ret = __kvm_vcpu_run(&hyp_vcpu->vcpu);
|
||||
|
||||
sync_hyp_vcpu(hyp_vcpu);
|
||||
pkvm_put_hyp_vcpu(hyp_vcpu);
|
||||
} else {
|
||||
/* The host is fully trusted, run its vCPU directly. */
|
||||
ret = __kvm_vcpu_run(host_vcpu);
|
||||
ret = __kvm_vcpu_run(kern_hyp_va(host_vcpu));
|
||||
}
|
||||
out:
|
||||
cpu_reg(host_ctxt, 1) = ret;
|
||||
}
|
||||
|
||||
static int pkvm_refill_memcache(struct pkvm_hyp_vcpu *hyp_vcpu)
|
||||
{
|
||||
struct kvm_vcpu *host_vcpu = hyp_vcpu->host_vcpu;
|
||||
|
||||
return refill_memcache(&hyp_vcpu->vcpu.arch.pkvm_memcache,
|
||||
host_vcpu->arch.pkvm_memcache.nr_pages,
|
||||
&host_vcpu->arch.pkvm_memcache);
|
||||
}
|
||||
|
||||
static void handle___pkvm_host_share_guest(struct kvm_cpu_context *host_ctxt)
|
||||
{
|
||||
DECLARE_REG(u64, pfn, host_ctxt, 1);
|
||||
DECLARE_REG(u64, gfn, host_ctxt, 2);
|
||||
DECLARE_REG(enum kvm_pgtable_prot, prot, host_ctxt, 3);
|
||||
struct pkvm_hyp_vcpu *hyp_vcpu;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (!is_protected_kvm_enabled())
|
||||
goto out;
|
||||
|
||||
hyp_vcpu = pkvm_get_loaded_hyp_vcpu();
|
||||
if (!hyp_vcpu || pkvm_hyp_vcpu_is_protected(hyp_vcpu))
|
||||
goto out;
|
||||
|
||||
ret = pkvm_refill_memcache(hyp_vcpu);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = __pkvm_host_share_guest(pfn, gfn, hyp_vcpu, prot);
|
||||
out:
|
||||
cpu_reg(host_ctxt, 1) = ret;
|
||||
}
|
||||
|
||||
static void handle___pkvm_host_unshare_guest(struct kvm_cpu_context *host_ctxt)
|
||||
{
|
||||
DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1);
|
||||
DECLARE_REG(u64, gfn, host_ctxt, 2);
|
||||
struct pkvm_hyp_vm *hyp_vm;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (!is_protected_kvm_enabled())
|
||||
goto out;
|
||||
|
||||
hyp_vm = get_np_pkvm_hyp_vm(handle);
|
||||
if (!hyp_vm)
|
||||
goto out;
|
||||
|
||||
ret = __pkvm_host_unshare_guest(gfn, hyp_vm);
|
||||
put_pkvm_hyp_vm(hyp_vm);
|
||||
out:
|
||||
cpu_reg(host_ctxt, 1) = ret;
|
||||
}
|
||||
|
||||
static void handle___pkvm_host_relax_perms_guest(struct kvm_cpu_context *host_ctxt)
|
||||
{
|
||||
DECLARE_REG(u64, gfn, host_ctxt, 1);
|
||||
DECLARE_REG(enum kvm_pgtable_prot, prot, host_ctxt, 2);
|
||||
struct pkvm_hyp_vcpu *hyp_vcpu;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (!is_protected_kvm_enabled())
|
||||
goto out;
|
||||
|
||||
hyp_vcpu = pkvm_get_loaded_hyp_vcpu();
|
||||
if (!hyp_vcpu || pkvm_hyp_vcpu_is_protected(hyp_vcpu))
|
||||
goto out;
|
||||
|
||||
ret = __pkvm_host_relax_perms_guest(gfn, hyp_vcpu, prot);
|
||||
out:
|
||||
cpu_reg(host_ctxt, 1) = ret;
|
||||
}
|
||||
|
||||
static void handle___pkvm_host_wrprotect_guest(struct kvm_cpu_context *host_ctxt)
|
||||
{
|
||||
DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1);
|
||||
DECLARE_REG(u64, gfn, host_ctxt, 2);
|
||||
struct pkvm_hyp_vm *hyp_vm;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (!is_protected_kvm_enabled())
|
||||
goto out;
|
||||
|
||||
hyp_vm = get_np_pkvm_hyp_vm(handle);
|
||||
if (!hyp_vm)
|
||||
goto out;
|
||||
|
||||
ret = __pkvm_host_wrprotect_guest(gfn, hyp_vm);
|
||||
put_pkvm_hyp_vm(hyp_vm);
|
||||
out:
|
||||
cpu_reg(host_ctxt, 1) = ret;
|
||||
}
|
||||
|
||||
static void handle___pkvm_host_test_clear_young_guest(struct kvm_cpu_context *host_ctxt)
|
||||
{
|
||||
DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1);
|
||||
DECLARE_REG(u64, gfn, host_ctxt, 2);
|
||||
DECLARE_REG(bool, mkold, host_ctxt, 3);
|
||||
struct pkvm_hyp_vm *hyp_vm;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (!is_protected_kvm_enabled())
|
||||
goto out;
|
||||
|
||||
hyp_vm = get_np_pkvm_hyp_vm(handle);
|
||||
if (!hyp_vm)
|
||||
goto out;
|
||||
|
||||
ret = __pkvm_host_test_clear_young_guest(gfn, mkold, hyp_vm);
|
||||
put_pkvm_hyp_vm(hyp_vm);
|
||||
out:
|
||||
cpu_reg(host_ctxt, 1) = ret;
|
||||
}
|
||||
|
||||
static void handle___pkvm_host_mkyoung_guest(struct kvm_cpu_context *host_ctxt)
|
||||
{
|
||||
DECLARE_REG(u64, gfn, host_ctxt, 1);
|
||||
struct pkvm_hyp_vcpu *hyp_vcpu;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (!is_protected_kvm_enabled())
|
||||
goto out;
|
||||
|
||||
hyp_vcpu = pkvm_get_loaded_hyp_vcpu();
|
||||
if (!hyp_vcpu || pkvm_hyp_vcpu_is_protected(hyp_vcpu))
|
||||
goto out;
|
||||
|
||||
ret = __pkvm_host_mkyoung_guest(gfn, hyp_vcpu);
|
||||
out:
|
||||
cpu_reg(host_ctxt, 1) = ret;
|
||||
}
|
||||
|
|
@ -231,6 +385,22 @@ static void handle___kvm_tlb_flush_vmid(struct kvm_cpu_context *host_ctxt)
|
|||
__kvm_tlb_flush_vmid(kern_hyp_va(mmu));
|
||||
}
|
||||
|
||||
static void handle___pkvm_tlb_flush_vmid(struct kvm_cpu_context *host_ctxt)
|
||||
{
|
||||
DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1);
|
||||
struct pkvm_hyp_vm *hyp_vm;
|
||||
|
||||
if (!is_protected_kvm_enabled())
|
||||
return;
|
||||
|
||||
hyp_vm = get_np_pkvm_hyp_vm(handle);
|
||||
if (!hyp_vm)
|
||||
return;
|
||||
|
||||
__kvm_tlb_flush_vmid(&hyp_vm->kvm.arch.mmu);
|
||||
put_pkvm_hyp_vm(hyp_vm);
|
||||
}
|
||||
|
||||
static void handle___kvm_flush_cpu_context(struct kvm_cpu_context *host_ctxt)
|
||||
{
|
||||
DECLARE_REG(struct kvm_s2_mmu *, mmu, host_ctxt, 1);
|
||||
|
|
@ -387,6 +557,12 @@ static const hcall_t host_hcall[] = {
|
|||
|
||||
HANDLE_FUNC(__pkvm_host_share_hyp),
|
||||
HANDLE_FUNC(__pkvm_host_unshare_hyp),
|
||||
HANDLE_FUNC(__pkvm_host_share_guest),
|
||||
HANDLE_FUNC(__pkvm_host_unshare_guest),
|
||||
HANDLE_FUNC(__pkvm_host_relax_perms_guest),
|
||||
HANDLE_FUNC(__pkvm_host_wrprotect_guest),
|
||||
HANDLE_FUNC(__pkvm_host_test_clear_young_guest),
|
||||
HANDLE_FUNC(__pkvm_host_mkyoung_guest),
|
||||
HANDLE_FUNC(__kvm_adjust_pc),
|
||||
HANDLE_FUNC(__kvm_vcpu_run),
|
||||
HANDLE_FUNC(__kvm_flush_vm_context),
|
||||
|
|
@ -401,6 +577,9 @@ static const hcall_t host_hcall[] = {
|
|||
HANDLE_FUNC(__pkvm_init_vm),
|
||||
HANDLE_FUNC(__pkvm_init_vcpu),
|
||||
HANDLE_FUNC(__pkvm_teardown_vm),
|
||||
HANDLE_FUNC(__pkvm_vcpu_load),
|
||||
HANDLE_FUNC(__pkvm_vcpu_put),
|
||||
HANDLE_FUNC(__pkvm_tlb_flush_vmid),
|
||||
};
|
||||
|
||||
static void handle_host_hcall(struct kvm_cpu_context *host_ctxt)
|
||||
|
|
|
|||
|
|
@ -201,8 +201,8 @@ static void *guest_s2_zalloc_page(void *mc)
|
|||
|
||||
memset(addr, 0, PAGE_SIZE);
|
||||
p = hyp_virt_to_page(addr);
|
||||
memset(p, 0, sizeof(*p));
|
||||
p->refcount = 1;
|
||||
p->order = 0;
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
|
@ -268,6 +268,7 @@ int kvm_guest_prepare_stage2(struct pkvm_hyp_vm *vm, void *pgd)
|
|||
|
||||
void reclaim_guest_pages(struct pkvm_hyp_vm *vm, struct kvm_hyp_memcache *mc)
|
||||
{
|
||||
struct hyp_page *page;
|
||||
void *addr;
|
||||
|
||||
/* Dump all pgtable pages in the hyp_pool */
|
||||
|
|
@ -279,7 +280,9 @@ void reclaim_guest_pages(struct pkvm_hyp_vm *vm, struct kvm_hyp_memcache *mc)
|
|||
/* Drain the hyp_pool into the memcache */
|
||||
addr = hyp_alloc_pages(&vm->pool, 0);
|
||||
while (addr) {
|
||||
memset(hyp_virt_to_page(addr), 0, sizeof(struct hyp_page));
|
||||
page = hyp_virt_to_page(addr);
|
||||
page->refcount = 0;
|
||||
page->order = 0;
|
||||
push_hyp_memcache(mc, addr, hyp_virt_to_phys);
|
||||
WARN_ON(__pkvm_hyp_donate_host(hyp_virt_to_pfn(addr), 1));
|
||||
addr = hyp_alloc_pages(&vm->pool, 0);
|
||||
|
|
@ -382,19 +385,28 @@ bool addr_is_memory(phys_addr_t phys)
|
|||
return !!find_mem_range(phys, &range);
|
||||
}
|
||||
|
||||
static bool addr_is_allowed_memory(phys_addr_t phys)
|
||||
static bool is_in_mem_range(u64 addr, struct kvm_mem_range *range)
|
||||
{
|
||||
return range->start <= addr && addr < range->end;
|
||||
}
|
||||
|
||||
static int check_range_allowed_memory(u64 start, u64 end)
|
||||
{
|
||||
struct memblock_region *reg;
|
||||
struct kvm_mem_range range;
|
||||
|
||||
reg = find_mem_range(phys, &range);
|
||||
/*
|
||||
* Callers can't check the state of a range that overlaps memory and
|
||||
* MMIO regions, so ensure [start, end[ is in the same kvm_mem_range.
|
||||
*/
|
||||
reg = find_mem_range(start, &range);
|
||||
if (!is_in_mem_range(end - 1, &range))
|
||||
return -EINVAL;
|
||||
|
||||
return reg && !(reg->flags & MEMBLOCK_NOMAP);
|
||||
}
|
||||
if (!reg || reg->flags & MEMBLOCK_NOMAP)
|
||||
return -EPERM;
|
||||
|
||||
static bool is_in_mem_range(u64 addr, struct kvm_mem_range *range)
|
||||
{
|
||||
return range->start <= addr && addr < range->end;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool range_is_memory(u64 start, u64 end)
|
||||
|
|
@ -454,8 +466,10 @@ static int host_stage2_adjust_range(u64 addr, struct kvm_mem_range *range)
|
|||
if (kvm_pte_valid(pte))
|
||||
return -EAGAIN;
|
||||
|
||||
if (pte)
|
||||
if (pte) {
|
||||
WARN_ON(addr_is_memory(addr) && hyp_phys_to_page(addr)->host_state != PKVM_NOPAGE);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
do {
|
||||
u64 granule = kvm_granule_size(level);
|
||||
|
|
@ -477,10 +491,33 @@ int host_stage2_idmap_locked(phys_addr_t addr, u64 size,
|
|||
return host_stage2_try(__host_stage2_idmap, addr, addr + size, prot);
|
||||
}
|
||||
|
||||
static void __host_update_page_state(phys_addr_t addr, u64 size, enum pkvm_page_state state)
|
||||
{
|
||||
phys_addr_t end = addr + size;
|
||||
|
||||
for (; addr < end; addr += PAGE_SIZE)
|
||||
hyp_phys_to_page(addr)->host_state = state;
|
||||
}
|
||||
|
||||
int host_stage2_set_owner_locked(phys_addr_t addr, u64 size, u8 owner_id)
|
||||
{
|
||||
return host_stage2_try(kvm_pgtable_stage2_set_owner, &host_mmu.pgt,
|
||||
addr, size, &host_s2_pool, owner_id);
|
||||
int ret;
|
||||
|
||||
if (!addr_is_memory(addr))
|
||||
return -EPERM;
|
||||
|
||||
ret = host_stage2_try(kvm_pgtable_stage2_set_owner, &host_mmu.pgt,
|
||||
addr, size, &host_s2_pool, owner_id);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Don't forget to update the vmemmap tracking for the host */
|
||||
if (owner_id == PKVM_ID_HOST)
|
||||
__host_update_page_state(addr, size, PKVM_PAGE_OWNED);
|
||||
else
|
||||
__host_update_page_state(addr, size, PKVM_NOPAGE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool host_stage2_force_pte_cb(u64 addr, u64 end, enum kvm_pgtable_prot prot)
|
||||
|
|
@ -604,35 +641,38 @@ static int check_page_state_range(struct kvm_pgtable *pgt, u64 addr, u64 size,
|
|||
return kvm_pgtable_walk(pgt, addr, size, &walker);
|
||||
}
|
||||
|
||||
static enum pkvm_page_state host_get_page_state(kvm_pte_t pte, u64 addr)
|
||||
{
|
||||
if (!addr_is_allowed_memory(addr))
|
||||
return PKVM_NOPAGE;
|
||||
|
||||
if (!kvm_pte_valid(pte) && pte)
|
||||
return PKVM_NOPAGE;
|
||||
|
||||
return pkvm_getstate(kvm_pgtable_stage2_pte_prot(pte));
|
||||
}
|
||||
|
||||
static int __host_check_page_state_range(u64 addr, u64 size,
|
||||
enum pkvm_page_state state)
|
||||
{
|
||||
struct check_walk_data d = {
|
||||
.desired = state,
|
||||
.get_page_state = host_get_page_state,
|
||||
};
|
||||
u64 end = addr + size;
|
||||
int ret;
|
||||
|
||||
ret = check_range_allowed_memory(addr, end);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
hyp_assert_lock_held(&host_mmu.lock);
|
||||
return check_page_state_range(&host_mmu.pgt, addr, size, &d);
|
||||
for (; addr < end; addr += PAGE_SIZE) {
|
||||
if (hyp_phys_to_page(addr)->host_state != state)
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __host_set_page_state_range(u64 addr, u64 size,
|
||||
enum pkvm_page_state state)
|
||||
{
|
||||
enum kvm_pgtable_prot prot = pkvm_mkstate(PKVM_HOST_MEM_PROT, state);
|
||||
if (hyp_phys_to_page(addr)->host_state == PKVM_NOPAGE) {
|
||||
int ret = host_stage2_idmap_locked(addr, size, PKVM_HOST_MEM_PROT);
|
||||
|
||||
return host_stage2_idmap_locked(addr, size, prot);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
__host_update_page_state(addr, size, state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int host_request_owned_transition(u64 *completer_addr,
|
||||
|
|
@ -827,6 +867,27 @@ static int hyp_complete_donation(u64 addr,
|
|||
return pkvm_create_mappings_locked(start, end, prot);
|
||||
}
|
||||
|
||||
static enum pkvm_page_state guest_get_page_state(kvm_pte_t pte, u64 addr)
|
||||
{
|
||||
if (!kvm_pte_valid(pte))
|
||||
return PKVM_NOPAGE;
|
||||
|
||||
return pkvm_getstate(kvm_pgtable_stage2_pte_prot(pte));
|
||||
}
|
||||
|
||||
static int __guest_check_page_state_range(struct pkvm_hyp_vcpu *vcpu, u64 addr,
|
||||
u64 size, enum pkvm_page_state state)
|
||||
{
|
||||
struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(vcpu);
|
||||
struct check_walk_data d = {
|
||||
.desired = state,
|
||||
.get_page_state = guest_get_page_state,
|
||||
};
|
||||
|
||||
hyp_assert_lock_held(&vm->lock);
|
||||
return check_page_state_range(&vm->pgt, addr, size, &d);
|
||||
}
|
||||
|
||||
static int check_share(struct pkvm_mem_share *share)
|
||||
{
|
||||
const struct pkvm_mem_transition *tx = &share->tx;
|
||||
|
|
@ -1309,3 +1370,202 @@ int __pkvm_host_unshare_ffa(u64 pfn, u64 nr_pages)
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int __pkvm_host_share_guest(u64 pfn, u64 gfn, struct pkvm_hyp_vcpu *vcpu,
|
||||
enum kvm_pgtable_prot prot)
|
||||
{
|
||||
struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(vcpu);
|
||||
u64 phys = hyp_pfn_to_phys(pfn);
|
||||
u64 ipa = hyp_pfn_to_phys(gfn);
|
||||
struct hyp_page *page;
|
||||
int ret;
|
||||
|
||||
if (prot & ~KVM_PGTABLE_PROT_RWX)
|
||||
return -EINVAL;
|
||||
|
||||
ret = check_range_allowed_memory(phys, phys + PAGE_SIZE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
host_lock_component();
|
||||
guest_lock_component(vm);
|
||||
|
||||
ret = __guest_check_page_state_range(vcpu, ipa, PAGE_SIZE, PKVM_NOPAGE);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
page = hyp_phys_to_page(phys);
|
||||
switch (page->host_state) {
|
||||
case PKVM_PAGE_OWNED:
|
||||
WARN_ON(__host_set_page_state_range(phys, PAGE_SIZE, PKVM_PAGE_SHARED_OWNED));
|
||||
break;
|
||||
case PKVM_PAGE_SHARED_OWNED:
|
||||
if (page->host_share_guest_count)
|
||||
break;
|
||||
/* Only host to np-guest multi-sharing is tolerated */
|
||||
WARN_ON(1);
|
||||
fallthrough;
|
||||
default:
|
||||
ret = -EPERM;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
WARN_ON(kvm_pgtable_stage2_map(&vm->pgt, ipa, PAGE_SIZE, phys,
|
||||
pkvm_mkstate(prot, PKVM_PAGE_SHARED_BORROWED),
|
||||
&vcpu->vcpu.arch.pkvm_memcache, 0));
|
||||
page->host_share_guest_count++;
|
||||
|
||||
unlock:
|
||||
guest_unlock_component(vm);
|
||||
host_unlock_component();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __check_host_shared_guest(struct pkvm_hyp_vm *vm, u64 *__phys, u64 ipa)
|
||||
{
|
||||
enum pkvm_page_state state;
|
||||
struct hyp_page *page;
|
||||
kvm_pte_t pte;
|
||||
u64 phys;
|
||||
s8 level;
|
||||
int ret;
|
||||
|
||||
ret = kvm_pgtable_get_leaf(&vm->pgt, ipa, &pte, &level);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (level != KVM_PGTABLE_LAST_LEVEL)
|
||||
return -E2BIG;
|
||||
if (!kvm_pte_valid(pte))
|
||||
return -ENOENT;
|
||||
|
||||
state = guest_get_page_state(pte, ipa);
|
||||
if (state != PKVM_PAGE_SHARED_BORROWED)
|
||||
return -EPERM;
|
||||
|
||||
phys = kvm_pte_to_phys(pte);
|
||||
ret = check_range_allowed_memory(phys, phys + PAGE_SIZE);
|
||||
if (WARN_ON(ret))
|
||||
return ret;
|
||||
|
||||
page = hyp_phys_to_page(phys);
|
||||
if (page->host_state != PKVM_PAGE_SHARED_OWNED)
|
||||
return -EPERM;
|
||||
if (WARN_ON(!page->host_share_guest_count))
|
||||
return -EINVAL;
|
||||
|
||||
*__phys = phys;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __pkvm_host_unshare_guest(u64 gfn, struct pkvm_hyp_vm *vm)
|
||||
{
|
||||
u64 ipa = hyp_pfn_to_phys(gfn);
|
||||
struct hyp_page *page;
|
||||
u64 phys;
|
||||
int ret;
|
||||
|
||||
host_lock_component();
|
||||
guest_lock_component(vm);
|
||||
|
||||
ret = __check_host_shared_guest(vm, &phys, ipa);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
ret = kvm_pgtable_stage2_unmap(&vm->pgt, ipa, PAGE_SIZE);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
page = hyp_phys_to_page(phys);
|
||||
page->host_share_guest_count--;
|
||||
if (!page->host_share_guest_count)
|
||||
WARN_ON(__host_set_page_state_range(phys, PAGE_SIZE, PKVM_PAGE_OWNED));
|
||||
|
||||
unlock:
|
||||
guest_unlock_component(vm);
|
||||
host_unlock_component();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int __pkvm_host_relax_perms_guest(u64 gfn, struct pkvm_hyp_vcpu *vcpu, enum kvm_pgtable_prot prot)
|
||||
{
|
||||
struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(vcpu);
|
||||
u64 ipa = hyp_pfn_to_phys(gfn);
|
||||
u64 phys;
|
||||
int ret;
|
||||
|
||||
if (prot & ~KVM_PGTABLE_PROT_RWX)
|
||||
return -EINVAL;
|
||||
|
||||
host_lock_component();
|
||||
guest_lock_component(vm);
|
||||
|
||||
ret = __check_host_shared_guest(vm, &phys, ipa);
|
||||
if (!ret)
|
||||
ret = kvm_pgtable_stage2_relax_perms(&vm->pgt, ipa, prot, 0);
|
||||
|
||||
guest_unlock_component(vm);
|
||||
host_unlock_component();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int __pkvm_host_wrprotect_guest(u64 gfn, struct pkvm_hyp_vm *vm)
|
||||
{
|
||||
u64 ipa = hyp_pfn_to_phys(gfn);
|
||||
u64 phys;
|
||||
int ret;
|
||||
|
||||
host_lock_component();
|
||||
guest_lock_component(vm);
|
||||
|
||||
ret = __check_host_shared_guest(vm, &phys, ipa);
|
||||
if (!ret)
|
||||
ret = kvm_pgtable_stage2_wrprotect(&vm->pgt, ipa, PAGE_SIZE);
|
||||
|
||||
guest_unlock_component(vm);
|
||||
host_unlock_component();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int __pkvm_host_test_clear_young_guest(u64 gfn, bool mkold, struct pkvm_hyp_vm *vm)
|
||||
{
|
||||
u64 ipa = hyp_pfn_to_phys(gfn);
|
||||
u64 phys;
|
||||
int ret;
|
||||
|
||||
host_lock_component();
|
||||
guest_lock_component(vm);
|
||||
|
||||
ret = __check_host_shared_guest(vm, &phys, ipa);
|
||||
if (!ret)
|
||||
ret = kvm_pgtable_stage2_test_clear_young(&vm->pgt, ipa, PAGE_SIZE, mkold);
|
||||
|
||||
guest_unlock_component(vm);
|
||||
host_unlock_component();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int __pkvm_host_mkyoung_guest(u64 gfn, struct pkvm_hyp_vcpu *vcpu)
|
||||
{
|
||||
struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(vcpu);
|
||||
u64 ipa = hyp_pfn_to_phys(gfn);
|
||||
u64 phys;
|
||||
int ret;
|
||||
|
||||
host_lock_component();
|
||||
guest_lock_component(vm);
|
||||
|
||||
ret = __check_host_shared_guest(vm, &phys, ipa);
|
||||
if (!ret)
|
||||
kvm_pgtable_stage2_mkyoung(&vm->pgt, ipa, 0);
|
||||
|
||||
guest_unlock_component(vm);
|
||||
host_unlock_component();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ u64 __hyp_vmemmap;
|
|||
*/
|
||||
static struct hyp_page *__find_buddy_nocheck(struct hyp_pool *pool,
|
||||
struct hyp_page *p,
|
||||
unsigned short order)
|
||||
u8 order)
|
||||
{
|
||||
phys_addr_t addr = hyp_page_to_phys(p);
|
||||
|
||||
|
|
@ -51,7 +51,7 @@ static struct hyp_page *__find_buddy_nocheck(struct hyp_pool *pool,
|
|||
/* Find a buddy page currently available for allocation */
|
||||
static struct hyp_page *__find_buddy_avail(struct hyp_pool *pool,
|
||||
struct hyp_page *p,
|
||||
unsigned short order)
|
||||
u8 order)
|
||||
{
|
||||
struct hyp_page *buddy = __find_buddy_nocheck(pool, p, order);
|
||||
|
||||
|
|
@ -94,7 +94,7 @@ static void __hyp_attach_page(struct hyp_pool *pool,
|
|||
struct hyp_page *p)
|
||||
{
|
||||
phys_addr_t phys = hyp_page_to_phys(p);
|
||||
unsigned short order = p->order;
|
||||
u8 order = p->order;
|
||||
struct hyp_page *buddy;
|
||||
|
||||
memset(hyp_page_to_virt(p), 0, PAGE_SIZE << p->order);
|
||||
|
|
@ -129,7 +129,7 @@ static void __hyp_attach_page(struct hyp_pool *pool,
|
|||
|
||||
static struct hyp_page *__hyp_extract_page(struct hyp_pool *pool,
|
||||
struct hyp_page *p,
|
||||
unsigned short order)
|
||||
u8 order)
|
||||
{
|
||||
struct hyp_page *buddy;
|
||||
|
||||
|
|
@ -183,7 +183,7 @@ void hyp_get_page(struct hyp_pool *pool, void *addr)
|
|||
|
||||
void hyp_split_page(struct hyp_page *p)
|
||||
{
|
||||
unsigned short order = p->order;
|
||||
u8 order = p->order;
|
||||
unsigned int i;
|
||||
|
||||
p->order = 0;
|
||||
|
|
@ -195,10 +195,10 @@ void hyp_split_page(struct hyp_page *p)
|
|||
}
|
||||
}
|
||||
|
||||
void *hyp_alloc_pages(struct hyp_pool *pool, unsigned short order)
|
||||
void *hyp_alloc_pages(struct hyp_pool *pool, u8 order)
|
||||
{
|
||||
unsigned short i = order;
|
||||
struct hyp_page *p;
|
||||
u8 i = order;
|
||||
|
||||
hyp_spin_lock(&pool->lock);
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,12 @@ unsigned int kvm_arm_vmid_bits;
|
|||
|
||||
unsigned int kvm_host_sve_max_vl;
|
||||
|
||||
/*
|
||||
* The currently loaded hyp vCPU for each physical CPU. Used only when
|
||||
* protected KVM is enabled, but for both protected and non-protected VMs.
|
||||
*/
|
||||
static DEFINE_PER_CPU(struct pkvm_hyp_vcpu *, loaded_hyp_vcpu);
|
||||
|
||||
/*
|
||||
* Set trap register values based on features in ID_AA64PFR0.
|
||||
*/
|
||||
|
|
@ -306,15 +312,30 @@ struct pkvm_hyp_vcpu *pkvm_load_hyp_vcpu(pkvm_handle_t handle,
|
|||
struct pkvm_hyp_vcpu *hyp_vcpu = NULL;
|
||||
struct pkvm_hyp_vm *hyp_vm;
|
||||
|
||||
/* Cannot load a new vcpu without putting the old one first. */
|
||||
if (__this_cpu_read(loaded_hyp_vcpu))
|
||||
return NULL;
|
||||
|
||||
hyp_spin_lock(&vm_table_lock);
|
||||
hyp_vm = get_vm_by_handle(handle);
|
||||
if (!hyp_vm || hyp_vm->nr_vcpus <= vcpu_idx)
|
||||
goto unlock;
|
||||
|
||||
hyp_vcpu = hyp_vm->vcpus[vcpu_idx];
|
||||
|
||||
/* Ensure vcpu isn't loaded on more than one cpu simultaneously. */
|
||||
if (unlikely(hyp_vcpu->loaded_hyp_vcpu)) {
|
||||
hyp_vcpu = NULL;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
hyp_vcpu->loaded_hyp_vcpu = this_cpu_ptr(&loaded_hyp_vcpu);
|
||||
hyp_page_ref_inc(hyp_virt_to_page(hyp_vm));
|
||||
unlock:
|
||||
hyp_spin_unlock(&vm_table_lock);
|
||||
|
||||
if (hyp_vcpu)
|
||||
__this_cpu_write(loaded_hyp_vcpu, hyp_vcpu);
|
||||
return hyp_vcpu;
|
||||
}
|
||||
|
||||
|
|
@ -323,10 +344,50 @@ void pkvm_put_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
|
|||
struct pkvm_hyp_vm *hyp_vm = pkvm_hyp_vcpu_to_hyp_vm(hyp_vcpu);
|
||||
|
||||
hyp_spin_lock(&vm_table_lock);
|
||||
hyp_vcpu->loaded_hyp_vcpu = NULL;
|
||||
__this_cpu_write(loaded_hyp_vcpu, NULL);
|
||||
hyp_page_ref_dec(hyp_virt_to_page(hyp_vm));
|
||||
hyp_spin_unlock(&vm_table_lock);
|
||||
}
|
||||
|
||||
struct pkvm_hyp_vcpu *pkvm_get_loaded_hyp_vcpu(void)
|
||||
{
|
||||
return __this_cpu_read(loaded_hyp_vcpu);
|
||||
|
||||
}
|
||||
|
||||
struct pkvm_hyp_vm *get_pkvm_hyp_vm(pkvm_handle_t handle)
|
||||
{
|
||||
struct pkvm_hyp_vm *hyp_vm;
|
||||
|
||||
hyp_spin_lock(&vm_table_lock);
|
||||
hyp_vm = get_vm_by_handle(handle);
|
||||
if (hyp_vm)
|
||||
hyp_page_ref_inc(hyp_virt_to_page(hyp_vm));
|
||||
hyp_spin_unlock(&vm_table_lock);
|
||||
|
||||
return hyp_vm;
|
||||
}
|
||||
|
||||
void put_pkvm_hyp_vm(struct pkvm_hyp_vm *hyp_vm)
|
||||
{
|
||||
hyp_spin_lock(&vm_table_lock);
|
||||
hyp_page_ref_dec(hyp_virt_to_page(hyp_vm));
|
||||
hyp_spin_unlock(&vm_table_lock);
|
||||
}
|
||||
|
||||
struct pkvm_hyp_vm *get_np_pkvm_hyp_vm(pkvm_handle_t handle)
|
||||
{
|
||||
struct pkvm_hyp_vm *hyp_vm = get_pkvm_hyp_vm(handle);
|
||||
|
||||
if (hyp_vm && pkvm_hyp_vm_is_protected(hyp_vm)) {
|
||||
put_pkvm_hyp_vm(hyp_vm);
|
||||
hyp_vm = NULL;
|
||||
}
|
||||
|
||||
return hyp_vm;
|
||||
}
|
||||
|
||||
static void pkvm_init_features_from_host(struct pkvm_hyp_vm *hyp_vm, const struct kvm *host_kvm)
|
||||
{
|
||||
struct kvm *kvm = &hyp_vm->kvm;
|
||||
|
|
@ -746,6 +807,14 @@ int __pkvm_teardown_vm(pkvm_handle_t handle)
|
|||
/* Push the metadata pages to the teardown memcache */
|
||||
for (idx = 0; idx < hyp_vm->nr_vcpus; ++idx) {
|
||||
struct pkvm_hyp_vcpu *hyp_vcpu = hyp_vm->vcpus[idx];
|
||||
struct kvm_hyp_memcache *vcpu_mc = &hyp_vcpu->vcpu.arch.pkvm_memcache;
|
||||
|
||||
while (vcpu_mc->nr_pages) {
|
||||
void *addr = pop_hyp_memcache(vcpu_mc, hyp_phys_to_virt);
|
||||
|
||||
push_hyp_memcache(mc, addr, hyp_virt_to_phys);
|
||||
unmap_donated_memory_noclear(addr, PAGE_SIZE);
|
||||
}
|
||||
|
||||
teardown_donated_memory(mc, hyp_vcpu, sizeof(*hyp_vcpu));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -180,7 +180,6 @@ static void hpool_put_page(void *addr)
|
|||
static int fix_host_ownership_walker(const struct kvm_pgtable_visit_ctx *ctx,
|
||||
enum kvm_pgtable_walk_flags visit)
|
||||
{
|
||||
enum kvm_pgtable_prot prot;
|
||||
enum pkvm_page_state state;
|
||||
phys_addr_t phys;
|
||||
|
||||
|
|
@ -203,16 +202,16 @@ static int fix_host_ownership_walker(const struct kvm_pgtable_visit_ctx *ctx,
|
|||
case PKVM_PAGE_OWNED:
|
||||
return host_stage2_set_owner_locked(phys, PAGE_SIZE, PKVM_ID_HYP);
|
||||
case PKVM_PAGE_SHARED_OWNED:
|
||||
prot = pkvm_mkstate(PKVM_HOST_MEM_PROT, PKVM_PAGE_SHARED_BORROWED);
|
||||
hyp_phys_to_page(phys)->host_state = PKVM_PAGE_SHARED_BORROWED;
|
||||
break;
|
||||
case PKVM_PAGE_SHARED_BORROWED:
|
||||
prot = pkvm_mkstate(PKVM_HOST_MEM_PROT, PKVM_PAGE_SHARED_OWNED);
|
||||
hyp_phys_to_page(phys)->host_state = PKVM_PAGE_SHARED_OWNED;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return host_stage2_idmap_locked(phys, PAGE_SIZE, prot);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fix_hyp_pgtable_refcnt_walker(const struct kvm_pgtable_visit_ctx *ctx,
|
||||
|
|
|
|||
|
|
@ -1245,14 +1245,13 @@ int kvm_pgtable_stage2_wrprotect(struct kvm_pgtable *pgt, u64 addr, u64 size)
|
|||
NULL, NULL, 0);
|
||||
}
|
||||
|
||||
void kvm_pgtable_stage2_mkyoung(struct kvm_pgtable *pgt, u64 addr)
|
||||
void kvm_pgtable_stage2_mkyoung(struct kvm_pgtable *pgt, u64 addr,
|
||||
enum kvm_pgtable_walk_flags flags)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = stage2_update_leaf_attrs(pgt, addr, 1, KVM_PTE_LEAF_ATTR_LO_S2_AF, 0,
|
||||
NULL, NULL,
|
||||
KVM_PGTABLE_WALK_HANDLE_FAULT |
|
||||
KVM_PGTABLE_WALK_SHARED);
|
||||
NULL, NULL, flags);
|
||||
if (!ret)
|
||||
dsb(ishst);
|
||||
}
|
||||
|
|
@ -1308,7 +1307,7 @@ bool kvm_pgtable_stage2_test_clear_young(struct kvm_pgtable *pgt, u64 addr,
|
|||
}
|
||||
|
||||
int kvm_pgtable_stage2_relax_perms(struct kvm_pgtable *pgt, u64 addr,
|
||||
enum kvm_pgtable_prot prot)
|
||||
enum kvm_pgtable_prot prot, enum kvm_pgtable_walk_flags flags)
|
||||
{
|
||||
int ret;
|
||||
s8 level;
|
||||
|
|
@ -1326,9 +1325,7 @@ int kvm_pgtable_stage2_relax_perms(struct kvm_pgtable *pgt, u64 addr,
|
|||
if (prot & KVM_PGTABLE_PROT_X)
|
||||
clr |= KVM_PTE_LEAF_ATTR_HI_S2_XN;
|
||||
|
||||
ret = stage2_update_leaf_attrs(pgt, addr, 1, set, clr, NULL, &level,
|
||||
KVM_PGTABLE_WALK_HANDLE_FAULT |
|
||||
KVM_PGTABLE_WALK_SHARED);
|
||||
ret = stage2_update_leaf_attrs(pgt, addr, 1, set, clr, NULL, &level, flags);
|
||||
if (!ret || ret == -EAGAIN)
|
||||
kvm_call_hyp(__kvm_tlb_flush_vmid_ipa_nsh, pgt->mmu, addr, level);
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include <asm/kvm_arm.h>
|
||||
#include <asm/kvm_mmu.h>
|
||||
#include <asm/kvm_pgtable.h>
|
||||
#include <asm/kvm_pkvm.h>
|
||||
#include <asm/kvm_ras.h>
|
||||
#include <asm/kvm_asm.h>
|
||||
#include <asm/kvm_emulate.h>
|
||||
|
|
@ -31,6 +32,8 @@ static phys_addr_t __ro_after_init hyp_idmap_vector;
|
|||
|
||||
static unsigned long __ro_after_init io_map_base;
|
||||
|
||||
#define KVM_PGT_FN(fn) (!is_protected_kvm_enabled() ? fn : p ## fn)
|
||||
|
||||
static phys_addr_t __stage2_range_addr_end(phys_addr_t addr, phys_addr_t end,
|
||||
phys_addr_t size)
|
||||
{
|
||||
|
|
@ -147,7 +150,7 @@ static int kvm_mmu_split_huge_pages(struct kvm *kvm, phys_addr_t addr,
|
|||
return -EINVAL;
|
||||
|
||||
next = __stage2_range_addr_end(addr, end, chunk_size);
|
||||
ret = kvm_pgtable_stage2_split(pgt, addr, next - addr, cache);
|
||||
ret = KVM_PGT_FN(kvm_pgtable_stage2_split)(pgt, addr, next - addr, cache);
|
||||
if (ret)
|
||||
break;
|
||||
} while (addr = next, addr != end);
|
||||
|
|
@ -168,15 +171,23 @@ static bool memslot_is_logging(struct kvm_memory_slot *memslot)
|
|||
*/
|
||||
int kvm_arch_flush_remote_tlbs(struct kvm *kvm)
|
||||
{
|
||||
kvm_call_hyp(__kvm_tlb_flush_vmid, &kvm->arch.mmu);
|
||||
if (is_protected_kvm_enabled())
|
||||
kvm_call_hyp_nvhe(__pkvm_tlb_flush_vmid, kvm->arch.pkvm.handle);
|
||||
else
|
||||
kvm_call_hyp(__kvm_tlb_flush_vmid, &kvm->arch.mmu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kvm_arch_flush_remote_tlbs_range(struct kvm *kvm,
|
||||
gfn_t gfn, u64 nr_pages)
|
||||
{
|
||||
kvm_tlb_flush_vmid_range(&kvm->arch.mmu,
|
||||
gfn << PAGE_SHIFT, nr_pages << PAGE_SHIFT);
|
||||
u64 size = nr_pages << PAGE_SHIFT;
|
||||
u64 addr = gfn << PAGE_SHIFT;
|
||||
|
||||
if (is_protected_kvm_enabled())
|
||||
kvm_call_hyp_nvhe(__pkvm_tlb_flush_vmid, kvm->arch.pkvm.handle);
|
||||
else
|
||||
kvm_tlb_flush_vmid_range(&kvm->arch.mmu, addr, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -225,7 +236,7 @@ static void stage2_free_unlinked_table_rcu_cb(struct rcu_head *head)
|
|||
void *pgtable = page_to_virt(page);
|
||||
s8 level = page_private(page);
|
||||
|
||||
kvm_pgtable_stage2_free_unlinked(&kvm_s2_mm_ops, pgtable, level);
|
||||
KVM_PGT_FN(kvm_pgtable_stage2_free_unlinked)(&kvm_s2_mm_ops, pgtable, level);
|
||||
}
|
||||
|
||||
static void stage2_free_unlinked_table(void *addr, s8 level)
|
||||
|
|
@ -324,7 +335,7 @@ static void __unmap_stage2_range(struct kvm_s2_mmu *mmu, phys_addr_t start, u64
|
|||
|
||||
lockdep_assert_held_write(&kvm->mmu_lock);
|
||||
WARN_ON(size & ~PAGE_MASK);
|
||||
WARN_ON(stage2_apply_range(mmu, start, end, kvm_pgtable_stage2_unmap,
|
||||
WARN_ON(stage2_apply_range(mmu, start, end, KVM_PGT_FN(kvm_pgtable_stage2_unmap),
|
||||
may_block));
|
||||
}
|
||||
|
||||
|
|
@ -336,7 +347,7 @@ void kvm_stage2_unmap_range(struct kvm_s2_mmu *mmu, phys_addr_t start,
|
|||
|
||||
void kvm_stage2_flush_range(struct kvm_s2_mmu *mmu, phys_addr_t addr, phys_addr_t end)
|
||||
{
|
||||
stage2_apply_range_resched(mmu, addr, end, kvm_pgtable_stage2_flush);
|
||||
stage2_apply_range_resched(mmu, addr, end, KVM_PGT_FN(kvm_pgtable_stage2_flush));
|
||||
}
|
||||
|
||||
static void stage2_flush_memslot(struct kvm *kvm,
|
||||
|
|
@ -942,10 +953,14 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long t
|
|||
return -ENOMEM;
|
||||
|
||||
mmu->arch = &kvm->arch;
|
||||
err = kvm_pgtable_stage2_init(pgt, mmu, &kvm_s2_mm_ops);
|
||||
err = KVM_PGT_FN(kvm_pgtable_stage2_init)(pgt, mmu, &kvm_s2_mm_ops);
|
||||
if (err)
|
||||
goto out_free_pgtable;
|
||||
|
||||
mmu->pgt = pgt;
|
||||
if (is_protected_kvm_enabled())
|
||||
return 0;
|
||||
|
||||
mmu->last_vcpu_ran = alloc_percpu(typeof(*mmu->last_vcpu_ran));
|
||||
if (!mmu->last_vcpu_ran) {
|
||||
err = -ENOMEM;
|
||||
|
|
@ -959,7 +974,6 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long t
|
|||
mmu->split_page_chunk_size = KVM_ARM_EAGER_SPLIT_CHUNK_SIZE_DEFAULT;
|
||||
mmu->split_page_cache.gfp_zero = __GFP_ZERO;
|
||||
|
||||
mmu->pgt = pgt;
|
||||
mmu->pgd_phys = __pa(pgt->pgd);
|
||||
|
||||
if (kvm_is_nested_s2_mmu(kvm, mmu))
|
||||
|
|
@ -968,7 +982,7 @@ int kvm_init_stage2_mmu(struct kvm *kvm, struct kvm_s2_mmu *mmu, unsigned long t
|
|||
return 0;
|
||||
|
||||
out_destroy_pgtable:
|
||||
kvm_pgtable_stage2_destroy(pgt);
|
||||
KVM_PGT_FN(kvm_pgtable_stage2_destroy)(pgt);
|
||||
out_free_pgtable:
|
||||
kfree(pgt);
|
||||
return err;
|
||||
|
|
@ -1065,7 +1079,7 @@ void kvm_free_stage2_pgd(struct kvm_s2_mmu *mmu)
|
|||
write_unlock(&kvm->mmu_lock);
|
||||
|
||||
if (pgt) {
|
||||
kvm_pgtable_stage2_destroy(pgt);
|
||||
KVM_PGT_FN(kvm_pgtable_stage2_destroy)(pgt);
|
||||
kfree(pgt);
|
||||
}
|
||||
}
|
||||
|
|
@ -1082,9 +1096,11 @@ static void *hyp_mc_alloc_fn(void *unused)
|
|||
|
||||
void free_hyp_memcache(struct kvm_hyp_memcache *mc)
|
||||
{
|
||||
if (is_protected_kvm_enabled())
|
||||
__free_hyp_memcache(mc, hyp_mc_free_fn,
|
||||
kvm_host_va, NULL);
|
||||
if (!is_protected_kvm_enabled())
|
||||
return;
|
||||
|
||||
kfree(mc->mapping);
|
||||
__free_hyp_memcache(mc, hyp_mc_free_fn, kvm_host_va, NULL);
|
||||
}
|
||||
|
||||
int topup_hyp_memcache(struct kvm_hyp_memcache *mc, unsigned long min_pages)
|
||||
|
|
@ -1092,6 +1108,12 @@ int topup_hyp_memcache(struct kvm_hyp_memcache *mc, unsigned long min_pages)
|
|||
if (!is_protected_kvm_enabled())
|
||||
return 0;
|
||||
|
||||
if (!mc->mapping) {
|
||||
mc->mapping = kzalloc(sizeof(struct pkvm_mapping), GFP_KERNEL_ACCOUNT);
|
||||
if (!mc->mapping)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return __topup_hyp_memcache(mc, min_pages, hyp_mc_alloc_fn,
|
||||
kvm_host_pa, NULL);
|
||||
}
|
||||
|
|
@ -1130,8 +1152,8 @@ int kvm_phys_addr_ioremap(struct kvm *kvm, phys_addr_t guest_ipa,
|
|||
break;
|
||||
|
||||
write_lock(&kvm->mmu_lock);
|
||||
ret = kvm_pgtable_stage2_map(pgt, addr, PAGE_SIZE, pa, prot,
|
||||
&cache, 0);
|
||||
ret = KVM_PGT_FN(kvm_pgtable_stage2_map)(pgt, addr, PAGE_SIZE,
|
||||
pa, prot, &cache, 0);
|
||||
write_unlock(&kvm->mmu_lock);
|
||||
if (ret)
|
||||
break;
|
||||
|
|
@ -1151,7 +1173,7 @@ int kvm_phys_addr_ioremap(struct kvm *kvm, phys_addr_t guest_ipa,
|
|||
*/
|
||||
void kvm_stage2_wp_range(struct kvm_s2_mmu *mmu, phys_addr_t addr, phys_addr_t end)
|
||||
{
|
||||
stage2_apply_range_resched(mmu, addr, end, kvm_pgtable_stage2_wrprotect);
|
||||
stage2_apply_range_resched(mmu, addr, end, KVM_PGT_FN(kvm_pgtable_stage2_wrprotect));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1442,9 +1464,9 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
|
|||
unsigned long mmu_seq;
|
||||
phys_addr_t ipa = fault_ipa;
|
||||
struct kvm *kvm = vcpu->kvm;
|
||||
struct kvm_mmu_memory_cache *memcache = &vcpu->arch.mmu_page_cache;
|
||||
struct vm_area_struct *vma;
|
||||
short vma_shift;
|
||||
void *memcache;
|
||||
gfn_t gfn;
|
||||
kvm_pfn_t pfn;
|
||||
bool logging_active = memslot_is_logging(memslot);
|
||||
|
|
@ -1452,6 +1474,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
|
|||
enum kvm_pgtable_prot prot = KVM_PGTABLE_PROT_R;
|
||||
struct kvm_pgtable *pgt;
|
||||
struct page *page;
|
||||
enum kvm_pgtable_walk_flags flags = KVM_PGTABLE_WALK_HANDLE_FAULT | KVM_PGTABLE_WALK_SHARED;
|
||||
|
||||
if (fault_is_perm)
|
||||
fault_granule = kvm_vcpu_trap_get_perm_fault_granule(vcpu);
|
||||
|
|
@ -1471,8 +1494,15 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
|
|||
* and a write fault needs to collapse a block entry into a table.
|
||||
*/
|
||||
if (!fault_is_perm || (logging_active && write_fault)) {
|
||||
ret = kvm_mmu_topup_memory_cache(memcache,
|
||||
kvm_mmu_cache_min_pages(vcpu->arch.hw_mmu));
|
||||
int min_pages = kvm_mmu_cache_min_pages(vcpu->arch.hw_mmu);
|
||||
|
||||
if (!is_protected_kvm_enabled()) {
|
||||
memcache = &vcpu->arch.mmu_page_cache;
|
||||
ret = kvm_mmu_topup_memory_cache(memcache, min_pages);
|
||||
} else {
|
||||
memcache = &vcpu->arch.pkvm_memcache;
|
||||
ret = topup_hyp_memcache(memcache, min_pages);
|
||||
}
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1493,7 +1523,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
|
|||
* logging_active is guaranteed to never be true for VM_PFNMAP
|
||||
* memslots.
|
||||
*/
|
||||
if (logging_active) {
|
||||
if (logging_active || is_protected_kvm_enabled()) {
|
||||
force_pte = true;
|
||||
vma_shift = PAGE_SHIFT;
|
||||
} else {
|
||||
|
|
@ -1633,7 +1663,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
|
|||
prot |= kvm_encode_nested_level(nested);
|
||||
}
|
||||
|
||||
read_lock(&kvm->mmu_lock);
|
||||
kvm_fault_lock(kvm);
|
||||
pgt = vcpu->arch.hw_mmu->pgt;
|
||||
if (mmu_invalidate_retry(kvm, mmu_seq)) {
|
||||
ret = -EAGAIN;
|
||||
|
|
@ -1695,18 +1725,16 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
|
|||
* PTE, which will be preserved.
|
||||
*/
|
||||
prot &= ~KVM_NV_GUEST_MAP_SZ;
|
||||
ret = kvm_pgtable_stage2_relax_perms(pgt, fault_ipa, prot);
|
||||
ret = KVM_PGT_FN(kvm_pgtable_stage2_relax_perms)(pgt, fault_ipa, prot, flags);
|
||||
} else {
|
||||
ret = kvm_pgtable_stage2_map(pgt, fault_ipa, vma_pagesize,
|
||||
ret = KVM_PGT_FN(kvm_pgtable_stage2_map)(pgt, fault_ipa, vma_pagesize,
|
||||
__pfn_to_phys(pfn), prot,
|
||||
memcache,
|
||||
KVM_PGTABLE_WALK_HANDLE_FAULT |
|
||||
KVM_PGTABLE_WALK_SHARED);
|
||||
memcache, flags);
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
kvm_release_faultin_page(kvm, page, !!ret, writable);
|
||||
read_unlock(&kvm->mmu_lock);
|
||||
kvm_fault_unlock(kvm);
|
||||
|
||||
/* Mark the page dirty only if the fault is handled successfully */
|
||||
if (writable && !ret)
|
||||
|
|
@ -1718,13 +1746,14 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
|
|||
/* Resolve the access fault by making the page young again. */
|
||||
static void handle_access_fault(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa)
|
||||
{
|
||||
enum kvm_pgtable_walk_flags flags = KVM_PGTABLE_WALK_HANDLE_FAULT | KVM_PGTABLE_WALK_SHARED;
|
||||
struct kvm_s2_mmu *mmu;
|
||||
|
||||
trace_kvm_access_fault(fault_ipa);
|
||||
|
||||
read_lock(&vcpu->kvm->mmu_lock);
|
||||
mmu = vcpu->arch.hw_mmu;
|
||||
kvm_pgtable_stage2_mkyoung(mmu->pgt, fault_ipa);
|
||||
KVM_PGT_FN(kvm_pgtable_stage2_mkyoung)(mmu->pgt, fault_ipa, flags);
|
||||
read_unlock(&vcpu->kvm->mmu_lock);
|
||||
}
|
||||
|
||||
|
|
@ -1764,7 +1793,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
|
|||
}
|
||||
|
||||
/* Falls between the IPA range and the PARange? */
|
||||
if (fault_ipa >= BIT_ULL(vcpu->arch.hw_mmu->pgt->ia_bits)) {
|
||||
if (fault_ipa >= BIT_ULL(VTCR_EL2_IPA(vcpu->arch.hw_mmu->vtcr))) {
|
||||
fault_ipa |= kvm_vcpu_get_hfar(vcpu) & GENMASK(11, 0);
|
||||
|
||||
if (is_iabt)
|
||||
|
|
@ -1930,7 +1959,7 @@ bool kvm_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
|
|||
if (!kvm->arch.mmu.pgt)
|
||||
return false;
|
||||
|
||||
return kvm_pgtable_stage2_test_clear_young(kvm->arch.mmu.pgt,
|
||||
return KVM_PGT_FN(kvm_pgtable_stage2_test_clear_young)(kvm->arch.mmu.pgt,
|
||||
range->start << PAGE_SHIFT,
|
||||
size, true);
|
||||
/*
|
||||
|
|
@ -1946,7 +1975,7 @@ bool kvm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
|
|||
if (!kvm->arch.mmu.pgt)
|
||||
return false;
|
||||
|
||||
return kvm_pgtable_stage2_test_clear_young(kvm->arch.mmu.pgt,
|
||||
return KVM_PGT_FN(kvm_pgtable_stage2_test_clear_young)(kvm->arch.mmu.pgt,
|
||||
range->start << PAGE_SHIFT,
|
||||
size, false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include <linux/init.h>
|
||||
#include <linux/kmemleak.h>
|
||||
#include <linux/kvm_host.h>
|
||||
#include <asm/kvm_mmu.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sort.h>
|
||||
|
|
@ -268,3 +269,203 @@ static int __init finalize_pkvm(void)
|
|||
return ret;
|
||||
}
|
||||
device_initcall_sync(finalize_pkvm);
|
||||
|
||||
static int cmp_mappings(struct rb_node *node, const struct rb_node *parent)
|
||||
{
|
||||
struct pkvm_mapping *a = rb_entry(node, struct pkvm_mapping, node);
|
||||
struct pkvm_mapping *b = rb_entry(parent, struct pkvm_mapping, node);
|
||||
|
||||
if (a->gfn < b->gfn)
|
||||
return -1;
|
||||
if (a->gfn > b->gfn)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct rb_node *find_first_mapping_node(struct rb_root *root, u64 gfn)
|
||||
{
|
||||
struct rb_node *node = root->rb_node, *prev = NULL;
|
||||
struct pkvm_mapping *mapping;
|
||||
|
||||
while (node) {
|
||||
mapping = rb_entry(node, struct pkvm_mapping, node);
|
||||
if (mapping->gfn == gfn)
|
||||
return node;
|
||||
prev = node;
|
||||
node = (gfn < mapping->gfn) ? node->rb_left : node->rb_right;
|
||||
}
|
||||
|
||||
return prev;
|
||||
}
|
||||
|
||||
/*
|
||||
* __tmp is updated to rb_next(__tmp) *before* entering the body of the loop to allow freeing
|
||||
* of __map inline.
|
||||
*/
|
||||
#define for_each_mapping_in_range_safe(__pgt, __start, __end, __map) \
|
||||
for (struct rb_node *__tmp = find_first_mapping_node(&(__pgt)->pkvm_mappings, \
|
||||
((__start) >> PAGE_SHIFT)); \
|
||||
__tmp && ({ \
|
||||
__map = rb_entry(__tmp, struct pkvm_mapping, node); \
|
||||
__tmp = rb_next(__tmp); \
|
||||
true; \
|
||||
}); \
|
||||
) \
|
||||
if (__map->gfn < ((__start) >> PAGE_SHIFT)) \
|
||||
continue; \
|
||||
else if (__map->gfn >= ((__end) >> PAGE_SHIFT)) \
|
||||
break; \
|
||||
else
|
||||
|
||||
int pkvm_pgtable_stage2_init(struct kvm_pgtable *pgt, struct kvm_s2_mmu *mmu,
|
||||
struct kvm_pgtable_mm_ops *mm_ops)
|
||||
{
|
||||
pgt->pkvm_mappings = RB_ROOT;
|
||||
pgt->mmu = mmu;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pkvm_pgtable_stage2_destroy(struct kvm_pgtable *pgt)
|
||||
{
|
||||
struct kvm *kvm = kvm_s2_mmu_to_kvm(pgt->mmu);
|
||||
pkvm_handle_t handle = kvm->arch.pkvm.handle;
|
||||
struct pkvm_mapping *mapping;
|
||||
struct rb_node *node;
|
||||
|
||||
if (!handle)
|
||||
return;
|
||||
|
||||
node = rb_first(&pgt->pkvm_mappings);
|
||||
while (node) {
|
||||
mapping = rb_entry(node, struct pkvm_mapping, node);
|
||||
kvm_call_hyp_nvhe(__pkvm_host_unshare_guest, handle, mapping->gfn);
|
||||
node = rb_next(node);
|
||||
rb_erase(&mapping->node, &pgt->pkvm_mappings);
|
||||
kfree(mapping);
|
||||
}
|
||||
}
|
||||
|
||||
int pkvm_pgtable_stage2_map(struct kvm_pgtable *pgt, u64 addr, u64 size,
|
||||
u64 phys, enum kvm_pgtable_prot prot,
|
||||
void *mc, enum kvm_pgtable_walk_flags flags)
|
||||
{
|
||||
struct kvm *kvm = kvm_s2_mmu_to_kvm(pgt->mmu);
|
||||
struct pkvm_mapping *mapping = NULL;
|
||||
struct kvm_hyp_memcache *cache = mc;
|
||||
u64 gfn = addr >> PAGE_SHIFT;
|
||||
u64 pfn = phys >> PAGE_SHIFT;
|
||||
int ret;
|
||||
|
||||
if (size != PAGE_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
lockdep_assert_held_write(&kvm->mmu_lock);
|
||||
ret = kvm_call_hyp_nvhe(__pkvm_host_share_guest, pfn, gfn, prot);
|
||||
if (ret) {
|
||||
/* Is the gfn already mapped due to a racing vCPU? */
|
||||
if (ret == -EPERM)
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
swap(mapping, cache->mapping);
|
||||
mapping->gfn = gfn;
|
||||
mapping->pfn = pfn;
|
||||
WARN_ON(rb_find_add(&mapping->node, &pgt->pkvm_mappings, cmp_mappings));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int pkvm_pgtable_stage2_unmap(struct kvm_pgtable *pgt, u64 addr, u64 size)
|
||||
{
|
||||
struct kvm *kvm = kvm_s2_mmu_to_kvm(pgt->mmu);
|
||||
pkvm_handle_t handle = kvm->arch.pkvm.handle;
|
||||
struct pkvm_mapping *mapping;
|
||||
int ret = 0;
|
||||
|
||||
lockdep_assert_held_write(&kvm->mmu_lock);
|
||||
for_each_mapping_in_range_safe(pgt, addr, addr + size, mapping) {
|
||||
ret = kvm_call_hyp_nvhe(__pkvm_host_unshare_guest, handle, mapping->gfn);
|
||||
if (WARN_ON(ret))
|
||||
break;
|
||||
rb_erase(&mapping->node, &pgt->pkvm_mappings);
|
||||
kfree(mapping);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int pkvm_pgtable_stage2_wrprotect(struct kvm_pgtable *pgt, u64 addr, u64 size)
|
||||
{
|
||||
struct kvm *kvm = kvm_s2_mmu_to_kvm(pgt->mmu);
|
||||
pkvm_handle_t handle = kvm->arch.pkvm.handle;
|
||||
struct pkvm_mapping *mapping;
|
||||
int ret = 0;
|
||||
|
||||
lockdep_assert_held(&kvm->mmu_lock);
|
||||
for_each_mapping_in_range_safe(pgt, addr, addr + size, mapping) {
|
||||
ret = kvm_call_hyp_nvhe(__pkvm_host_wrprotect_guest, handle, mapping->gfn);
|
||||
if (WARN_ON(ret))
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int pkvm_pgtable_stage2_flush(struct kvm_pgtable *pgt, u64 addr, u64 size)
|
||||
{
|
||||
struct kvm *kvm = kvm_s2_mmu_to_kvm(pgt->mmu);
|
||||
struct pkvm_mapping *mapping;
|
||||
|
||||
lockdep_assert_held(&kvm->mmu_lock);
|
||||
for_each_mapping_in_range_safe(pgt, addr, addr + size, mapping)
|
||||
__clean_dcache_guest_page(pfn_to_kaddr(mapping->pfn), PAGE_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool pkvm_pgtable_stage2_test_clear_young(struct kvm_pgtable *pgt, u64 addr, u64 size, bool mkold)
|
||||
{
|
||||
struct kvm *kvm = kvm_s2_mmu_to_kvm(pgt->mmu);
|
||||
pkvm_handle_t handle = kvm->arch.pkvm.handle;
|
||||
struct pkvm_mapping *mapping;
|
||||
bool young = false;
|
||||
|
||||
lockdep_assert_held(&kvm->mmu_lock);
|
||||
for_each_mapping_in_range_safe(pgt, addr, addr + size, mapping)
|
||||
young |= kvm_call_hyp_nvhe(__pkvm_host_test_clear_young_guest, handle, mapping->gfn,
|
||||
mkold);
|
||||
|
||||
return young;
|
||||
}
|
||||
|
||||
int pkvm_pgtable_stage2_relax_perms(struct kvm_pgtable *pgt, u64 addr, enum kvm_pgtable_prot prot,
|
||||
enum kvm_pgtable_walk_flags flags)
|
||||
{
|
||||
return kvm_call_hyp_nvhe(__pkvm_host_relax_perms_guest, addr >> PAGE_SHIFT, prot);
|
||||
}
|
||||
|
||||
void pkvm_pgtable_stage2_mkyoung(struct kvm_pgtable *pgt, u64 addr,
|
||||
enum kvm_pgtable_walk_flags flags)
|
||||
{
|
||||
WARN_ON(kvm_call_hyp_nvhe(__pkvm_host_mkyoung_guest, addr >> PAGE_SHIFT));
|
||||
}
|
||||
|
||||
void pkvm_pgtable_stage2_free_unlinked(struct kvm_pgtable_mm_ops *mm_ops, void *pgtable, s8 level)
|
||||
{
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
|
||||
kvm_pte_t *pkvm_pgtable_stage2_create_unlinked(struct kvm_pgtable *pgt, u64 phys, s8 level,
|
||||
enum kvm_pgtable_prot prot, void *mc, bool force_pte)
|
||||
{
|
||||
WARN_ON_ONCE(1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int pkvm_pgtable_stage2_split(struct kvm_pgtable *pgt, u64 addr, u64 size,
|
||||
struct kvm_mmu_memory_cache *mc)
|
||||
{
|
||||
WARN_ON_ONCE(1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -734,7 +734,8 @@ void vgic_v3_load(struct kvm_vcpu *vcpu)
|
|||
{
|
||||
struct vgic_v3_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v3;
|
||||
|
||||
kvm_call_hyp(__vgic_v3_restore_vmcr_aprs, cpu_if);
|
||||
if (likely(!is_protected_kvm_enabled()))
|
||||
kvm_call_hyp(__vgic_v3_restore_vmcr_aprs, cpu_if);
|
||||
|
||||
if (has_vhe())
|
||||
__vgic_v3_activate_traps(cpu_if);
|
||||
|
|
@ -746,7 +747,8 @@ void vgic_v3_put(struct kvm_vcpu *vcpu)
|
|||
{
|
||||
struct vgic_v3_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v3;
|
||||
|
||||
kvm_call_hyp(__vgic_v3_save_vmcr_aprs, cpu_if);
|
||||
if (likely(!is_protected_kvm_enabled()))
|
||||
kvm_call_hyp(__vgic_v3_save_vmcr_aprs, cpu_if);
|
||||
WARN_ON(vgic_v4_put(vcpu));
|
||||
|
||||
if (has_vhe())
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user