Skip to content

Commit cd6b97b

Browse files
author
Marc Zyngier
committed
Merge branch kvm-arm64/pkvm-6.16 into kvm-arm64/pkvm-np-thp-6.16
* kvm-arm64/pkvm-6.16: : . : pKVM memory management cleanups, courtesy of Quentin Perret. : From the cover letter: : : "This series moves the hypervisor's ownership state to the hyp_vmemmap, : as discussed in [1]. The two main benefits are: : : 1. much cheaper hyp state lookups, since we can avoid the hyp stage-1 : page-table walk; : : 2. de-correlates the hyp state from the presence of a mapping in the : linear map range of the hypervisor; which enables a bunch of : clean-ups in the existing code and will simplify the introduction of : other features in the future (hyp tracing, ...)" : . KVM: arm64: Unconditionally cross check hyp state KVM: arm64: Defer EL2 stage-1 mapping on share KVM: arm64: Move hyp state to hyp_vmemmap KVM: arm64: Introduce {get,set}_host_state() helpers KVM: arm64: Use 0b11 for encoding PKVM_NOPAGE KVM: arm64: Fix pKVM page-tracking comments KVM: arm64: Track SVE state in the hypervisor vcpu structure Signed-off-by: Marc Zyngier <maz@kernel.org>
2 parents b443265 + 43c4755 commit cd6b97b

File tree

6 files changed

+158
-79
lines changed

6 files changed

+158
-79
lines changed

arch/arm64/include/asm/kvm_host.h

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -971,20 +971,22 @@ struct kvm_vcpu_arch {
971971
#define vcpu_sve_zcr_elx(vcpu) \
972972
(unlikely(is_hyp_ctxt(vcpu)) ? ZCR_EL2 : ZCR_EL1)
973973

974-
#define vcpu_sve_state_size(vcpu) ({ \
974+
#define sve_state_size_from_vl(sve_max_vl) ({ \
975975
size_t __size_ret; \
976-
unsigned int __vcpu_vq; \
976+
unsigned int __vq; \
977977
\
978-
if (WARN_ON(!sve_vl_valid((vcpu)->arch.sve_max_vl))) { \
978+
if (WARN_ON(!sve_vl_valid(sve_max_vl))) { \
979979
__size_ret = 0; \
980980
} else { \
981-
__vcpu_vq = vcpu_sve_max_vq(vcpu); \
982-
__size_ret = SVE_SIG_REGS_SIZE(__vcpu_vq); \
981+
__vq = sve_vq_from_vl(sve_max_vl); \
982+
__size_ret = SVE_SIG_REGS_SIZE(__vq); \
983983
} \
984984
\
985985
__size_ret; \
986986
})
987987

988+
#define vcpu_sve_state_size(vcpu) sve_state_size_from_vl((vcpu)->arch.sve_max_vl)
989+
988990
#define KVM_GUESTDBG_VALID_MASK (KVM_GUESTDBG_ENABLE | \
989991
KVM_GUESTDBG_USE_SW_BP | \
990992
KVM_GUESTDBG_USE_HW | \

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

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,30 @@
88
#include <linux/types.h>
99

1010
/*
11-
* Bits 0-1 are reserved to track the memory ownership state of each page:
12-
* 00: The page is owned exclusively by the page-table owner.
13-
* 01: The page is owned by the page-table owner, but is shared
14-
* with another entity.
15-
* 10: The page is shared with, but not owned by the page-table owner.
16-
* 11: Reserved for future use (lending).
11+
* Bits 0-1 are used to encode the memory ownership state of each page from the
12+
* point of view of a pKVM "component" (host, hyp, guest, ... see enum
13+
* pkvm_component_id):
14+
* 00: The page is owned and exclusively accessible by the component;
15+
* 01: The page is owned and accessible by the component, but is also
16+
* accessible by another component;
17+
* 10: The page is accessible but not owned by the component;
18+
* The storage of this state depends on the component: either in the
19+
* hyp_vmemmap for the host and hyp states or in PTE software bits for guests.
1720
*/
1821
enum pkvm_page_state {
1922
PKVM_PAGE_OWNED = 0ULL,
2023
PKVM_PAGE_SHARED_OWNED = BIT(0),
2124
PKVM_PAGE_SHARED_BORROWED = BIT(1),
22-
__PKVM_PAGE_RESERVED = BIT(0) | BIT(1),
2325

24-
/* Meta-states which aren't encoded directly in the PTE's SW bits */
25-
PKVM_NOPAGE = BIT(2),
26+
/*
27+
* 'Meta-states' are not stored directly in PTE SW bits for guest
28+
* states, but inferred from the context (e.g. invalid PTE entries).
29+
* For the host and hyp, meta-states are stored directly in the
30+
* struct hyp_page.
31+
*/
32+
PKVM_NOPAGE = BIT(0) | BIT(1),
2633
};
27-
#define PKVM_PAGE_META_STATES_MASK (~__PKVM_PAGE_RESERVED)
34+
#define PKVM_PAGE_STATE_MASK (BIT(0) | BIT(1))
2835

2936
#define PKVM_PAGE_STATE_PROT_MASK (KVM_PGTABLE_PROT_SW0 | KVM_PGTABLE_PROT_SW1)
3037
static inline enum kvm_pgtable_prot pkvm_mkstate(enum kvm_pgtable_prot prot,
@@ -44,8 +51,15 @@ struct hyp_page {
4451
u16 refcount;
4552
u8 order;
4653

47-
/* Host (non-meta) state. Guarded by the host stage-2 lock. */
48-
enum pkvm_page_state host_state : 8;
54+
/* Host state. Guarded by the host stage-2 lock. */
55+
unsigned __host_state : 4;
56+
57+
/*
58+
* Complement of the hyp state. Guarded by the hyp stage-1 lock. We use
59+
* the complement so that the initial 0 in __hyp_state_comp (due to the
60+
* entire vmemmap starting off zeroed) encodes PKVM_NOPAGE.
61+
*/
62+
unsigned __hyp_state_comp : 4;
4963

5064
u32 host_share_guest_count;
5165
};
@@ -82,6 +96,26 @@ static inline struct hyp_page *hyp_phys_to_page(phys_addr_t phys)
8296
#define hyp_page_to_virt(page) __hyp_va(hyp_page_to_phys(page))
8397
#define hyp_page_to_pool(page) (((struct hyp_page *)page)->pool)
8498

99+
static inline enum pkvm_page_state get_host_state(phys_addr_t phys)
100+
{
101+
return (enum pkvm_page_state)hyp_phys_to_page(phys)->__host_state;
102+
}
103+
104+
static inline void set_host_state(phys_addr_t phys, enum pkvm_page_state state)
105+
{
106+
hyp_phys_to_page(phys)->__host_state = state;
107+
}
108+
109+
static inline enum pkvm_page_state get_hyp_state(phys_addr_t phys)
110+
{
111+
return hyp_phys_to_page(phys)->__hyp_state_comp ^ PKVM_PAGE_STATE_MASK;
112+
}
113+
114+
static inline void set_hyp_state(phys_addr_t phys, enum pkvm_page_state state)
115+
{
116+
hyp_phys_to_page(phys)->__hyp_state_comp = state ^ PKVM_PAGE_STATE_MASK;
117+
}
118+
85119
/*
86120
* Refcounting for 'struct hyp_page'.
87121
* hyp_pool::lock must be held if atomic access to the refcount is required.

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,10 +123,6 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
123123

124124
hyp_vcpu->vcpu.arch.ctxt = host_vcpu->arch.ctxt;
125125

126-
hyp_vcpu->vcpu.arch.sve_state = kern_hyp_va(host_vcpu->arch.sve_state);
127-
/* Limit guest vector length to the maximum supported by the host. */
128-
hyp_vcpu->vcpu.arch.sve_max_vl = min(host_vcpu->arch.sve_max_vl, kvm_host_sve_max_vl);
129-
130126
hyp_vcpu->vcpu.arch.mdcr_el2 = host_vcpu->arch.mdcr_el2;
131127
hyp_vcpu->vcpu.arch.hcr_el2 &= ~(HCR_TWI | HCR_TWE);
132128
hyp_vcpu->vcpu.arch.hcr_el2 |= READ_ONCE(host_vcpu->arch.hcr_el2) &

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

Lines changed: 54 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@ static int host_stage2_adjust_range(u64 addr, struct kvm_mem_range *range)
467467
return -EAGAIN;
468468

469469
if (pte) {
470-
WARN_ON(addr_is_memory(addr) && hyp_phys_to_page(addr)->host_state != PKVM_NOPAGE);
470+
WARN_ON(addr_is_memory(addr) && get_host_state(addr) != PKVM_NOPAGE);
471471
return -EPERM;
472472
}
473473

@@ -496,7 +496,7 @@ static void __host_update_page_state(phys_addr_t addr, u64 size, enum pkvm_page_
496496
phys_addr_t end = addr + size;
497497

498498
for (; addr < end; addr += PAGE_SIZE)
499-
hyp_phys_to_page(addr)->host_state = state;
499+
set_host_state(addr, state);
500500
}
501501

502502
int host_stage2_set_owner_locked(phys_addr_t addr, u64 size, u8 owner_id)
@@ -627,7 +627,7 @@ static int __host_check_page_state_range(u64 addr, u64 size,
627627

628628
hyp_assert_lock_held(&host_mmu.lock);
629629
for (; addr < end; addr += PAGE_SIZE) {
630-
if (hyp_phys_to_page(addr)->host_state != state)
630+
if (get_host_state(addr) != state)
631631
return -EPERM;
632632
}
633633

@@ -637,7 +637,7 @@ static int __host_check_page_state_range(u64 addr, u64 size,
637637
static int __host_set_page_state_range(u64 addr, u64 size,
638638
enum pkvm_page_state state)
639639
{
640-
if (hyp_phys_to_page(addr)->host_state == PKVM_NOPAGE) {
640+
if (get_host_state(addr) == PKVM_NOPAGE) {
641641
int ret = host_stage2_idmap_locked(addr, size, PKVM_HOST_MEM_PROT);
642642

643643
if (ret)
@@ -649,24 +649,24 @@ static int __host_set_page_state_range(u64 addr, u64 size,
649649
return 0;
650650
}
651651

652-
static enum pkvm_page_state hyp_get_page_state(kvm_pte_t pte, u64 addr)
652+
static void __hyp_set_page_state_range(phys_addr_t phys, u64 size, enum pkvm_page_state state)
653653
{
654-
if (!kvm_pte_valid(pte))
655-
return PKVM_NOPAGE;
654+
phys_addr_t end = phys + size;
656655

657-
return pkvm_getstate(kvm_pgtable_hyp_pte_prot(pte));
656+
for (; phys < end; phys += PAGE_SIZE)
657+
set_hyp_state(phys, state);
658658
}
659659

660-
static int __hyp_check_page_state_range(u64 addr, u64 size,
661-
enum pkvm_page_state state)
660+
static int __hyp_check_page_state_range(phys_addr_t phys, u64 size, enum pkvm_page_state state)
662661
{
663-
struct check_walk_data d = {
664-
.desired = state,
665-
.get_page_state = hyp_get_page_state,
666-
};
662+
phys_addr_t end = phys + size;
663+
664+
for (; phys < end; phys += PAGE_SIZE) {
665+
if (get_hyp_state(phys) != state)
666+
return -EPERM;
667+
}
667668

668-
hyp_assert_lock_held(&pkvm_pgd_lock);
669-
return check_page_state_range(&pkvm_pgtable, addr, size, &d);
669+
return 0;
670670
}
671671

672672
static enum pkvm_page_state guest_get_page_state(kvm_pte_t pte, u64 addr)
@@ -693,8 +693,6 @@ static int __guest_check_page_state_range(struct pkvm_hyp_vcpu *vcpu, u64 addr,
693693
int __pkvm_host_share_hyp(u64 pfn)
694694
{
695695
u64 phys = hyp_pfn_to_phys(pfn);
696-
void *virt = __hyp_va(phys);
697-
enum kvm_pgtable_prot prot;
698696
u64 size = PAGE_SIZE;
699697
int ret;
700698

@@ -704,14 +702,11 @@ int __pkvm_host_share_hyp(u64 pfn)
704702
ret = __host_check_page_state_range(phys, size, PKVM_PAGE_OWNED);
705703
if (ret)
706704
goto unlock;
707-
if (IS_ENABLED(CONFIG_NVHE_EL2_DEBUG)) {
708-
ret = __hyp_check_page_state_range((u64)virt, size, PKVM_NOPAGE);
709-
if (ret)
710-
goto unlock;
711-
}
705+
ret = __hyp_check_page_state_range(phys, size, PKVM_NOPAGE);
706+
if (ret)
707+
goto unlock;
712708

713-
prot = pkvm_mkstate(PAGE_HYP, PKVM_PAGE_SHARED_BORROWED);
714-
WARN_ON(pkvm_create_mappings_locked(virt, virt + size, prot));
709+
__hyp_set_page_state_range(phys, size, PKVM_PAGE_SHARED_BORROWED);
715710
WARN_ON(__host_set_page_state_range(phys, size, PKVM_PAGE_SHARED_OWNED));
716711

717712
unlock:
@@ -734,15 +729,15 @@ int __pkvm_host_unshare_hyp(u64 pfn)
734729
ret = __host_check_page_state_range(phys, size, PKVM_PAGE_SHARED_OWNED);
735730
if (ret)
736731
goto unlock;
737-
ret = __hyp_check_page_state_range(virt, size, PKVM_PAGE_SHARED_BORROWED);
732+
ret = __hyp_check_page_state_range(phys, size, PKVM_PAGE_SHARED_BORROWED);
738733
if (ret)
739734
goto unlock;
740735
if (hyp_page_count((void *)virt)) {
741736
ret = -EBUSY;
742737
goto unlock;
743738
}
744739

745-
WARN_ON(kvm_pgtable_hyp_unmap(&pkvm_pgtable, virt, size) != size);
740+
__hyp_set_page_state_range(phys, size, PKVM_NOPAGE);
746741
WARN_ON(__host_set_page_state_range(phys, size, PKVM_PAGE_OWNED));
747742

748743
unlock:
@@ -757,7 +752,6 @@ int __pkvm_host_donate_hyp(u64 pfn, u64 nr_pages)
757752
u64 phys = hyp_pfn_to_phys(pfn);
758753
u64 size = PAGE_SIZE * nr_pages;
759754
void *virt = __hyp_va(phys);
760-
enum kvm_pgtable_prot prot;
761755
int ret;
762756

763757
host_lock_component();
@@ -766,14 +760,12 @@ int __pkvm_host_donate_hyp(u64 pfn, u64 nr_pages)
766760
ret = __host_check_page_state_range(phys, size, PKVM_PAGE_OWNED);
767761
if (ret)
768762
goto unlock;
769-
if (IS_ENABLED(CONFIG_NVHE_EL2_DEBUG)) {
770-
ret = __hyp_check_page_state_range((u64)virt, size, PKVM_NOPAGE);
771-
if (ret)
772-
goto unlock;
773-
}
763+
ret = __hyp_check_page_state_range(phys, size, PKVM_NOPAGE);
764+
if (ret)
765+
goto unlock;
774766

775-
prot = pkvm_mkstate(PAGE_HYP, PKVM_PAGE_OWNED);
776-
WARN_ON(pkvm_create_mappings_locked(virt, virt + size, prot));
767+
__hyp_set_page_state_range(phys, size, PKVM_PAGE_OWNED);
768+
WARN_ON(pkvm_create_mappings_locked(virt, virt + size, PAGE_HYP));
777769
WARN_ON(host_stage2_set_owner_locked(phys, size, PKVM_ID_HYP));
778770

779771
unlock:
@@ -793,15 +785,14 @@ int __pkvm_hyp_donate_host(u64 pfn, u64 nr_pages)
793785
host_lock_component();
794786
hyp_lock_component();
795787

796-
ret = __hyp_check_page_state_range(virt, size, PKVM_PAGE_OWNED);
788+
ret = __hyp_check_page_state_range(phys, size, PKVM_PAGE_OWNED);
789+
if (ret)
790+
goto unlock;
791+
ret = __host_check_page_state_range(phys, size, PKVM_NOPAGE);
797792
if (ret)
798793
goto unlock;
799-
if (IS_ENABLED(CONFIG_NVHE_EL2_DEBUG)) {
800-
ret = __host_check_page_state_range(phys, size, PKVM_NOPAGE);
801-
if (ret)
802-
goto unlock;
803-
}
804794

795+
__hyp_set_page_state_range(phys, size, PKVM_NOPAGE);
805796
WARN_ON(kvm_pgtable_hyp_unmap(&pkvm_pgtable, virt, size) != size);
806797
WARN_ON(host_stage2_set_owner_locked(phys, size, PKVM_ID_HOST));
807798

@@ -816,24 +807,30 @@ int hyp_pin_shared_mem(void *from, void *to)
816807
{
817808
u64 cur, start = ALIGN_DOWN((u64)from, PAGE_SIZE);
818809
u64 end = PAGE_ALIGN((u64)to);
810+
u64 phys = __hyp_pa(start);
819811
u64 size = end - start;
812+
struct hyp_page *p;
820813
int ret;
821814

822815
host_lock_component();
823816
hyp_lock_component();
824817

825-
ret = __host_check_page_state_range(__hyp_pa(start), size,
826-
PKVM_PAGE_SHARED_OWNED);
818+
ret = __host_check_page_state_range(phys, size, PKVM_PAGE_SHARED_OWNED);
827819
if (ret)
828820
goto unlock;
829821

830-
ret = __hyp_check_page_state_range(start, size,
831-
PKVM_PAGE_SHARED_BORROWED);
822+
ret = __hyp_check_page_state_range(phys, size, PKVM_PAGE_SHARED_BORROWED);
832823
if (ret)
833824
goto unlock;
834825

835-
for (cur = start; cur < end; cur += PAGE_SIZE)
836-
hyp_page_ref_inc(hyp_virt_to_page(cur));
826+
for (cur = start; cur < end; cur += PAGE_SIZE) {
827+
p = hyp_virt_to_page(cur);
828+
hyp_page_ref_inc(p);
829+
if (p->refcount == 1)
830+
WARN_ON(pkvm_create_mappings_locked((void *)cur,
831+
(void *)cur + PAGE_SIZE,
832+
PAGE_HYP));
833+
}
837834

838835
unlock:
839836
hyp_unlock_component();
@@ -846,12 +843,17 @@ void hyp_unpin_shared_mem(void *from, void *to)
846843
{
847844
u64 cur, start = ALIGN_DOWN((u64)from, PAGE_SIZE);
848845
u64 end = PAGE_ALIGN((u64)to);
846+
struct hyp_page *p;
849847

850848
host_lock_component();
851849
hyp_lock_component();
852850

853-
for (cur = start; cur < end; cur += PAGE_SIZE)
854-
hyp_page_ref_dec(hyp_virt_to_page(cur));
851+
for (cur = start; cur < end; cur += PAGE_SIZE) {
852+
p = hyp_virt_to_page(cur);
853+
if (p->refcount == 1)
854+
WARN_ON(kvm_pgtable_hyp_unmap(&pkvm_pgtable, cur, PAGE_SIZE) != PAGE_SIZE);
855+
hyp_page_ref_dec(p);
856+
}
855857

856858
hyp_unlock_component();
857859
host_unlock_component();
@@ -911,7 +913,7 @@ int __pkvm_host_share_guest(u64 pfn, u64 gfn, struct pkvm_hyp_vcpu *vcpu,
911913
goto unlock;
912914

913915
page = hyp_phys_to_page(phys);
914-
switch (page->host_state) {
916+
switch (get_host_state(phys)) {
915917
case PKVM_PAGE_OWNED:
916918
WARN_ON(__host_set_page_state_range(phys, PAGE_SIZE, PKVM_PAGE_SHARED_OWNED));
917919
break;
@@ -964,9 +966,9 @@ static int __check_host_shared_guest(struct pkvm_hyp_vm *vm, u64 *__phys, u64 ip
964966
if (WARN_ON(ret))
965967
return ret;
966968

967-
page = hyp_phys_to_page(phys);
968-
if (page->host_state != PKVM_PAGE_SHARED_OWNED)
969+
if (get_host_state(phys) != PKVM_PAGE_SHARED_OWNED)
969970
return -EPERM;
971+
page = hyp_phys_to_page(phys);
970972
if (WARN_ON(!page->host_share_guest_count))
971973
return -EINVAL;
972974

0 commit comments

Comments
 (0)