Skip to content

Commit 43d8ac2

Browse files
author
Marc Zyngier
committed
Merge branch kvm-arm64/pkvm-hyp-sharing into kvmarm-master/next
* kvm-arm64/pkvm-hyp-sharing: : . : Series from Quentin Perret, implementing HYP page share/unshare: : : This series implements an unshare hypercall at EL2 in nVHE : protected mode, and makes use of it to unmmap guest-specific : data-structures from EL2 stage-1 during guest tear-down. : Crucially, the implementation of the share and unshare : routines use page refcounts in the host kernel to avoid : accidentally unmapping data-structures that overlap a common : page. : [...] : . KVM: arm64: pkvm: Unshare guest structs during teardown KVM: arm64: Expose unshare hypercall to the host KVM: arm64: Implement do_unshare() helper for unsharing memory KVM: arm64: Implement __pkvm_host_share_hyp() using do_share() KVM: arm64: Implement do_share() helper for sharing memory KVM: arm64: Introduce wrappers for host and hyp spin lock accessors KVM: arm64: Extend pkvm_page_state enumeration to handle absent pages KVM: arm64: pkvm: Refcount the pages shared with EL2 KVM: arm64: Introduce kvm_share_hyp() KVM: arm64: Implement kvm_pgtable_hyp_unmap() at EL2 KVM: arm64: Hook up ->page_count() for hypervisor stage-1 page-table KVM: arm64: Fixup hyp stage-1 refcount KVM: arm64: Refcount hyp stage-1 pgtable pages KVM: arm64: Provide {get,put}_page() stubs for early hyp allocator Signed-off-by: Marc Zyngier <[email protected]>
2 parents ce5b5b0 + 52b2865 commit 43d8ac2

File tree

14 files changed

+739
-119
lines changed

14 files changed

+739
-119
lines changed

arch/arm64/include/asm/kvm_asm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ enum __kvm_host_smccc_func {
6363

6464
/* Hypercalls available after pKVM finalisation */
6565
__KVM_HOST_SMCCC_FUNC___pkvm_host_share_hyp,
66+
__KVM_HOST_SMCCC_FUNC___pkvm_host_unshare_hyp,
6667
__KVM_HOST_SMCCC_FUNC___kvm_adjust_pc,
6768
__KVM_HOST_SMCCC_FUNC___kvm_vcpu_run,
6869
__KVM_HOST_SMCCC_FUNC___kvm_flush_vm_context,

arch/arm64/include/asm/kvm_host.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@ struct kvm_vcpu_arch {
321321
struct kvm_guest_debug_arch external_debug_state;
322322

323323
struct user_fpsimd_state *host_fpsimd_state; /* hyp VA */
324+
struct task_struct *parent_task;
324325

325326
struct {
326327
/* {Break,watch}point registers */
@@ -737,6 +738,7 @@ void kvm_arch_vcpu_load_fp(struct kvm_vcpu *vcpu);
737738
void kvm_arch_vcpu_ctxflush_fp(struct kvm_vcpu *vcpu);
738739
void kvm_arch_vcpu_ctxsync_fp(struct kvm_vcpu *vcpu);
739740
void kvm_arch_vcpu_put_fp(struct kvm_vcpu *vcpu);
741+
void kvm_vcpu_unshare_task_fp(struct kvm_vcpu *vcpu);
740742

741743
static inline bool kvm_pmu_counter_deferred(struct perf_event_attr *attr)
742744
{

arch/arm64/include/asm/kvm_mmu.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,8 @@ static __always_inline unsigned long __kern_hyp_va(unsigned long v)
150150
#include <asm/kvm_pgtable.h>
151151
#include <asm/stage2_pgtable.h>
152152

153+
int kvm_share_hyp(void *from, void *to);
154+
void kvm_unshare_hyp(void *from, void *to);
153155
int create_hyp_mappings(void *from, void *to, enum kvm_pgtable_prot prot);
154156
int create_hyp_io_mappings(phys_addr_t phys_addr, size_t size,
155157
void __iomem **kaddr,

arch/arm64/include/asm/kvm_pgtable.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,27 @@ void kvm_pgtable_hyp_destroy(struct kvm_pgtable *pgt);
251251
int kvm_pgtable_hyp_map(struct kvm_pgtable *pgt, u64 addr, u64 size, u64 phys,
252252
enum kvm_pgtable_prot prot);
253253

254+
/**
255+
* kvm_pgtable_hyp_unmap() - Remove a mapping from a hypervisor stage-1 page-table.
256+
* @pgt: Page-table structure initialised by kvm_pgtable_hyp_init().
257+
* @addr: Virtual address from which to remove the mapping.
258+
* @size: Size of the mapping.
259+
*
260+
* The offset of @addr within a page is ignored, @size is rounded-up to
261+
* the next page boundary and @phys is rounded-down to the previous page
262+
* boundary.
263+
*
264+
* TLB invalidation is performed for each page-table entry cleared during the
265+
* unmapping operation and the reference count for the page-table page
266+
* containing the cleared entry is decremented, with unreferenced pages being
267+
* freed. The unmapping operation will stop early if it encounters either an
268+
* invalid page-table entry or a valid block mapping which maps beyond the range
269+
* being unmapped.
270+
*
271+
* Return: Number of bytes unmapped, which may be 0.
272+
*/
273+
u64 kvm_pgtable_hyp_unmap(struct kvm_pgtable *pgt, u64 addr, u64 size);
274+
254275
/**
255276
* kvm_get_vtcr() - Helper to construct VTCR_EL2
256277
* @mmfr0: Sanitized value of SYS_ID_AA64MMFR0_EL1 register.

arch/arm64/kvm/arm.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
146146
if (ret)
147147
return ret;
148148

149-
ret = create_hyp_mappings(kvm, kvm + 1, PAGE_HYP);
149+
ret = kvm_share_hyp(kvm, kvm + 1);
150150
if (ret)
151151
goto out_free_stage2_pgd;
152152

@@ -188,6 +188,8 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
188188
}
189189
}
190190
atomic_set(&kvm->online_vcpus, 0);
191+
192+
kvm_unshare_hyp(kvm, kvm + 1);
191193
}
192194

193195
int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
@@ -342,7 +344,7 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
342344
if (err)
343345
return err;
344346

345-
return create_hyp_mappings(vcpu, vcpu + 1, PAGE_HYP);
347+
return kvm_share_hyp(vcpu, vcpu + 1);
346348
}
347349

348350
void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu)

arch/arm64/kvm/fpsimd.c

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,19 @@
1414
#include <asm/kvm_mmu.h>
1515
#include <asm/sysreg.h>
1616

17+
void kvm_vcpu_unshare_task_fp(struct kvm_vcpu *vcpu)
18+
{
19+
struct task_struct *p = vcpu->arch.parent_task;
20+
struct user_fpsimd_state *fpsimd;
21+
22+
if (!is_protected_kvm_enabled() || !p)
23+
return;
24+
25+
fpsimd = &p->thread.uw.fpsimd_state;
26+
kvm_unshare_hyp(fpsimd, fpsimd + 1);
27+
put_task_struct(p);
28+
}
29+
1730
/*
1831
* Called on entry to KVM_RUN unless this vcpu previously ran at least
1932
* once and the most recent prior KVM_RUN for this vcpu was called from
@@ -29,12 +42,27 @@ int kvm_arch_vcpu_run_map_fp(struct kvm_vcpu *vcpu)
2942

3043
struct user_fpsimd_state *fpsimd = &current->thread.uw.fpsimd_state;
3144

45+
kvm_vcpu_unshare_task_fp(vcpu);
46+
3247
/* Make sure the host task fpsimd state is visible to hyp: */
33-
ret = create_hyp_mappings(fpsimd, fpsimd + 1, PAGE_HYP);
34-
if (!ret)
35-
vcpu->arch.host_fpsimd_state = kern_hyp_va(fpsimd);
48+
ret = kvm_share_hyp(fpsimd, fpsimd + 1);
49+
if (ret)
50+
return ret;
51+
52+
vcpu->arch.host_fpsimd_state = kern_hyp_va(fpsimd);
53+
54+
/*
55+
* We need to keep current's task_struct pinned until its data has been
56+
* unshared with the hypervisor to make sure it is not re-used by the
57+
* kernel and donated to someone else while already shared -- see
58+
* kvm_vcpu_unshare_task_fp() for the matching put_task_struct().
59+
*/
60+
if (is_protected_kvm_enabled()) {
61+
get_task_struct(current);
62+
vcpu->arch.parent_task = current;
63+
}
3664

37-
return ret;
65+
return 0;
3866
}
3967

4068
/*

arch/arm64/kvm/hyp/include/nvhe/mem_protect.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ enum pkvm_page_state {
2424
PKVM_PAGE_OWNED = 0ULL,
2525
PKVM_PAGE_SHARED_OWNED = KVM_PGTABLE_PROT_SW0,
2626
PKVM_PAGE_SHARED_BORROWED = KVM_PGTABLE_PROT_SW1,
27+
__PKVM_PAGE_RESERVED = KVM_PGTABLE_PROT_SW0 |
28+
KVM_PGTABLE_PROT_SW1,
29+
30+
/* Meta-states which aren't encoded directly in the PTE's SW bits */
31+
PKVM_NOPAGE,
2732
};
2833

2934
#define PKVM_PAGE_STATE_PROT_MASK (KVM_PGTABLE_PROT_SW0 | KVM_PGTABLE_PROT_SW1)
@@ -50,6 +55,7 @@ extern const u8 pkvm_hyp_id;
5055

5156
int __pkvm_prot_finalize(void);
5257
int __pkvm_host_share_hyp(u64 pfn);
58+
int __pkvm_host_unshare_hyp(u64 pfn);
5359

5460
bool addr_is_memory(phys_addr_t phys);
5561
int host_stage2_idmap_locked(phys_addr_t addr, u64 size, enum kvm_pgtable_prot prot);

arch/arm64/kvm/hyp/nvhe/early_alloc.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ void *hyp_early_alloc_page(void *arg)
4343
return hyp_early_alloc_contig(1);
4444
}
4545

46+
static void hyp_early_alloc_get_page(void *addr) { }
47+
static void hyp_early_alloc_put_page(void *addr) { }
48+
4649
void hyp_early_alloc_init(void *virt, unsigned long size)
4750
{
4851
base = cur = (unsigned long)virt;
@@ -51,4 +54,6 @@ void hyp_early_alloc_init(void *virt, unsigned long size)
5154
hyp_early_alloc_mm_ops.zalloc_page = hyp_early_alloc_page;
5255
hyp_early_alloc_mm_ops.phys_to_virt = hyp_phys_to_virt;
5356
hyp_early_alloc_mm_ops.virt_to_phys = hyp_virt_to_phys;
57+
hyp_early_alloc_mm_ops.get_page = hyp_early_alloc_get_page;
58+
hyp_early_alloc_mm_ops.put_page = hyp_early_alloc_put_page;
5459
}

arch/arm64/kvm/hyp/nvhe/hyp-main.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,13 @@ static void handle___pkvm_host_share_hyp(struct kvm_cpu_context *host_ctxt)
147147
cpu_reg(host_ctxt, 1) = __pkvm_host_share_hyp(pfn);
148148
}
149149

150+
static void handle___pkvm_host_unshare_hyp(struct kvm_cpu_context *host_ctxt)
151+
{
152+
DECLARE_REG(u64, pfn, host_ctxt, 1);
153+
154+
cpu_reg(host_ctxt, 1) = __pkvm_host_unshare_hyp(pfn);
155+
}
156+
150157
static void handle___pkvm_create_private_mapping(struct kvm_cpu_context *host_ctxt)
151158
{
152159
DECLARE_REG(phys_addr_t, phys, host_ctxt, 1);
@@ -184,6 +191,7 @@ static const hcall_t host_hcall[] = {
184191
HANDLE_FUNC(__pkvm_prot_finalize),
185192

186193
HANDLE_FUNC(__pkvm_host_share_hyp),
194+
HANDLE_FUNC(__pkvm_host_unshare_hyp),
187195
HANDLE_FUNC(__kvm_adjust_pc),
188196
HANDLE_FUNC(__kvm_vcpu_run),
189197
HANDLE_FUNC(__kvm_flush_vm_context),

0 commit comments

Comments
 (0)