Skip to content

Commit 48d5645

Browse files
Quentin PerretMarc Zyngier
authored andcommitted
KVM: arm64: Extend pKVM selftest for np-guests
The pKVM selftest intends to test as many memory 'transitions' as possible, so extend it to cover sharing pages with non-protected guests, including in the case of multi-sharing. Signed-off-by: Quentin Perret <qperret@google.com> Link: https://lore.kernel.org/r/20250416160900.3078417-5-qperret@google.com Signed-off-by: Marc Zyngier <maz@kernel.org>
1 parent 6c2d4c3 commit 48d5645

File tree

5 files changed

+104
-5
lines changed

5 files changed

+104
-5
lines changed

arch/arm64/include/asm/kvm_pkvm.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,12 @@ static inline unsigned long host_s2_pgtable_pages(void)
135135
return res;
136136
}
137137

138+
#ifdef CONFIG_NVHE_EL2_DEBUG
139+
static inline unsigned long pkvm_selftest_pages(void) { return 32; }
140+
#else
141+
static inline unsigned long pkvm_selftest_pages(void) { return 0; }
142+
#endif
143+
138144
#define KVM_FFA_MBOX_NR_PAGES 1
139145

140146
static inline unsigned long hyp_ffa_proxy_pages(void)

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ static __always_inline void __load_host_stage2(void)
6969
}
7070

7171
#ifdef CONFIG_NVHE_EL2_DEBUG
72-
void pkvm_ownership_selftest(void);
72+
void pkvm_ownership_selftest(void *base);
7373
#else
74-
static inline void pkvm_ownership_selftest(void) { }
74+
static inline void pkvm_ownership_selftest(void *base) { }
7575
#endif
7676
#endif /* __KVM_NVHE_MEM_PROTECT__ */

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

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1095,16 +1095,60 @@ int __pkvm_host_mkyoung_guest(u64 gfn, struct pkvm_hyp_vcpu *vcpu)
10951095
struct pkvm_expected_state {
10961096
enum pkvm_page_state host;
10971097
enum pkvm_page_state hyp;
1098+
enum pkvm_page_state guest[2]; /* [ gfn, gfn + 1 ] */
10981099
};
10991100

11001101
static struct pkvm_expected_state selftest_state;
11011102
static struct hyp_page *selftest_page;
11021103

1104+
static struct pkvm_hyp_vm selftest_vm = {
1105+
.kvm = {
1106+
.arch = {
1107+
.mmu = {
1108+
.arch = &selftest_vm.kvm.arch,
1109+
.pgt = &selftest_vm.pgt,
1110+
},
1111+
},
1112+
},
1113+
};
1114+
1115+
static struct pkvm_hyp_vcpu selftest_vcpu = {
1116+
.vcpu = {
1117+
.arch = {
1118+
.hw_mmu = &selftest_vm.kvm.arch.mmu,
1119+
},
1120+
.kvm = &selftest_vm.kvm,
1121+
},
1122+
};
1123+
1124+
static void init_selftest_vm(void *virt)
1125+
{
1126+
struct hyp_page *p = hyp_virt_to_page(virt);
1127+
int i;
1128+
1129+
selftest_vm.kvm.arch.mmu.vtcr = host_mmu.arch.mmu.vtcr;
1130+
WARN_ON(kvm_guest_prepare_stage2(&selftest_vm, virt));
1131+
1132+
for (i = 0; i < pkvm_selftest_pages(); i++) {
1133+
if (p[i].refcount)
1134+
continue;
1135+
p[i].refcount = 1;
1136+
hyp_put_page(&selftest_vm.pool, hyp_page_to_virt(&p[i]));
1137+
}
1138+
}
1139+
1140+
static u64 selftest_ipa(void)
1141+
{
1142+
return BIT(selftest_vm.pgt.ia_bits - 1);
1143+
}
1144+
11031145
static void assert_page_state(void)
11041146
{
11051147
void *virt = hyp_page_to_virt(selftest_page);
11061148
u64 size = PAGE_SIZE << selftest_page->order;
1149+
struct pkvm_hyp_vcpu *vcpu = &selftest_vcpu;
11071150
u64 phys = hyp_virt_to_phys(virt);
1151+
u64 ipa[2] = { selftest_ipa(), selftest_ipa() + PAGE_SIZE };
11081152

11091153
host_lock_component();
11101154
WARN_ON(__host_check_page_state_range(phys, size, selftest_state.host));
@@ -1113,6 +1157,11 @@ static void assert_page_state(void)
11131157
hyp_lock_component();
11141158
WARN_ON(__hyp_check_page_state_range(phys, size, selftest_state.hyp));
11151159
hyp_unlock_component();
1160+
1161+
guest_lock_component(&selftest_vm);
1162+
WARN_ON(__guest_check_page_state_range(vcpu, ipa[0], size, selftest_state.guest[0]));
1163+
WARN_ON(__guest_check_page_state_range(vcpu, ipa[1], size, selftest_state.guest[1]));
1164+
guest_unlock_component(&selftest_vm);
11161165
}
11171166

11181167
#define assert_transition_res(res, fn, ...) \
@@ -1121,35 +1170,44 @@ static void assert_page_state(void)
11211170
assert_page_state(); \
11221171
} while (0)
11231172

1124-
void pkvm_ownership_selftest(void)
1173+
void pkvm_ownership_selftest(void *base)
11251174
{
1175+
enum kvm_pgtable_prot prot = KVM_PGTABLE_PROT_RWX;
11261176
void *virt = hyp_alloc_pages(&host_s2_pool, 0);
1127-
u64 phys, size, pfn;
1177+
struct pkvm_hyp_vcpu *vcpu = &selftest_vcpu;
1178+
struct pkvm_hyp_vm *vm = &selftest_vm;
1179+
u64 phys, size, pfn, gfn;
11281180

11291181
WARN_ON(!virt);
11301182
selftest_page = hyp_virt_to_page(virt);
11311183
selftest_page->refcount = 0;
1184+
init_selftest_vm(base);
11321185

11331186
size = PAGE_SIZE << selftest_page->order;
11341187
phys = hyp_virt_to_phys(virt);
11351188
pfn = hyp_phys_to_pfn(phys);
1189+
gfn = hyp_phys_to_pfn(selftest_ipa());
11361190

11371191
selftest_state.host = PKVM_NOPAGE;
11381192
selftest_state.hyp = PKVM_PAGE_OWNED;
1193+
selftest_state.guest[0] = selftest_state.guest[1] = PKVM_NOPAGE;
11391194
assert_page_state();
11401195
assert_transition_res(-EPERM, __pkvm_host_donate_hyp, pfn, 1);
11411196
assert_transition_res(-EPERM, __pkvm_host_share_hyp, pfn);
11421197
assert_transition_res(-EPERM, __pkvm_host_unshare_hyp, pfn);
11431198
assert_transition_res(-EPERM, __pkvm_host_share_ffa, pfn, 1);
11441199
assert_transition_res(-EPERM, __pkvm_host_unshare_ffa, pfn, 1);
11451200
assert_transition_res(-EPERM, hyp_pin_shared_mem, virt, virt + size);
1201+
assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn, vcpu, prot);
1202+
assert_transition_res(-ENOENT, __pkvm_host_unshare_guest, gfn, vm);
11461203

11471204
selftest_state.host = PKVM_PAGE_OWNED;
11481205
selftest_state.hyp = PKVM_NOPAGE;
11491206
assert_transition_res(0, __pkvm_hyp_donate_host, pfn, 1);
11501207
assert_transition_res(-EPERM, __pkvm_hyp_donate_host, pfn, 1);
11511208
assert_transition_res(-EPERM, __pkvm_host_unshare_hyp, pfn);
11521209
assert_transition_res(-EPERM, __pkvm_host_unshare_ffa, pfn, 1);
1210+
assert_transition_res(-ENOENT, __pkvm_host_unshare_guest, gfn, vm);
11531211
assert_transition_res(-EPERM, hyp_pin_shared_mem, virt, virt + size);
11541212

11551213
selftest_state.host = PKVM_PAGE_SHARED_OWNED;
@@ -1159,6 +1217,8 @@ void pkvm_ownership_selftest(void)
11591217
assert_transition_res(-EPERM, __pkvm_host_donate_hyp, pfn, 1);
11601218
assert_transition_res(-EPERM, __pkvm_host_share_ffa, pfn, 1);
11611219
assert_transition_res(-EPERM, __pkvm_hyp_donate_host, pfn, 1);
1220+
assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn, vcpu, prot);
1221+
assert_transition_res(-ENOENT, __pkvm_host_unshare_guest, gfn, vm);
11621222

11631223
assert_transition_res(0, hyp_pin_shared_mem, virt, virt + size);
11641224
assert_transition_res(0, hyp_pin_shared_mem, virt, virt + size);
@@ -1169,6 +1229,8 @@ void pkvm_ownership_selftest(void)
11691229
assert_transition_res(-EPERM, __pkvm_host_donate_hyp, pfn, 1);
11701230
assert_transition_res(-EPERM, __pkvm_host_share_ffa, pfn, 1);
11711231
assert_transition_res(-EPERM, __pkvm_hyp_donate_host, pfn, 1);
1232+
assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn, vcpu, prot);
1233+
assert_transition_res(-ENOENT, __pkvm_host_unshare_guest, gfn, vm);
11721234

11731235
hyp_unpin_shared_mem(virt, virt + size);
11741236
assert_page_state();
@@ -1186,13 +1248,37 @@ void pkvm_ownership_selftest(void)
11861248
assert_transition_res(-EPERM, __pkvm_host_share_hyp, pfn);
11871249
assert_transition_res(-EPERM, __pkvm_host_unshare_hyp, pfn);
11881250
assert_transition_res(-EPERM, __pkvm_hyp_donate_host, pfn, 1);
1251+
assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn, vcpu, prot);
1252+
assert_transition_res(-ENOENT, __pkvm_host_unshare_guest, gfn, vm);
11891253
assert_transition_res(-EPERM, hyp_pin_shared_mem, virt, virt + size);
11901254

11911255
selftest_state.host = PKVM_PAGE_OWNED;
11921256
selftest_state.hyp = PKVM_NOPAGE;
11931257
assert_transition_res(0, __pkvm_host_unshare_ffa, pfn, 1);
11941258
assert_transition_res(-EPERM, __pkvm_host_unshare_ffa, pfn, 1);
11951259

1260+
selftest_state.host = PKVM_PAGE_SHARED_OWNED;
1261+
selftest_state.guest[0] = PKVM_PAGE_SHARED_BORROWED;
1262+
assert_transition_res(0, __pkvm_host_share_guest, pfn, gfn, vcpu, prot);
1263+
assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn, vcpu, prot);
1264+
assert_transition_res(-EPERM, __pkvm_host_share_ffa, pfn, 1);
1265+
assert_transition_res(-EPERM, __pkvm_host_donate_hyp, pfn, 1);
1266+
assert_transition_res(-EPERM, __pkvm_host_share_hyp, pfn);
1267+
assert_transition_res(-EPERM, __pkvm_host_unshare_hyp, pfn);
1268+
assert_transition_res(-EPERM, __pkvm_hyp_donate_host, pfn, 1);
1269+
assert_transition_res(-EPERM, hyp_pin_shared_mem, virt, virt + size);
1270+
1271+
selftest_state.guest[1] = PKVM_PAGE_SHARED_BORROWED;
1272+
assert_transition_res(0, __pkvm_host_share_guest, pfn, gfn + 1, vcpu, prot);
1273+
WARN_ON(hyp_virt_to_page(virt)->host_share_guest_count != 2);
1274+
1275+
selftest_state.guest[0] = PKVM_NOPAGE;
1276+
assert_transition_res(0, __pkvm_host_unshare_guest, gfn, vm);
1277+
1278+
selftest_state.guest[1] = PKVM_NOPAGE;
1279+
selftest_state.host = PKVM_PAGE_OWNED;
1280+
assert_transition_res(0, __pkvm_host_unshare_guest, gfn + 1, vm);
1281+
11961282
selftest_state.host = PKVM_NOPAGE;
11971283
selftest_state.hyp = PKVM_PAGE_OWNED;
11981284
assert_transition_res(0, __pkvm_host_donate_hyp, pfn, 1);

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ static void *vmemmap_base;
2828
static void *vm_table_base;
2929
static void *hyp_pgt_base;
3030
static void *host_s2_pgt_base;
31+
static void *selftest_base;
3132
static void *ffa_proxy_pages;
3233
static struct kvm_pgtable_mm_ops pkvm_pgtable_mm_ops;
3334
static struct hyp_pool hpool;
@@ -38,6 +39,11 @@ static int divide_memory_pool(void *virt, unsigned long size)
3839

3940
hyp_early_alloc_init(virt, size);
4041

42+
nr_pages = pkvm_selftest_pages();
43+
selftest_base = hyp_early_alloc_contig(nr_pages);
44+
if (nr_pages && !selftest_base)
45+
return -ENOMEM;
46+
4147
nr_pages = hyp_vmemmap_pages(sizeof(struct hyp_page));
4248
vmemmap_base = hyp_early_alloc_contig(nr_pages);
4349
if (!vmemmap_base)
@@ -309,7 +315,7 @@ void __noreturn __pkvm_init_finalise(void)
309315

310316
pkvm_hyp_vm_table_init(vm_table_base);
311317

312-
pkvm_ownership_selftest();
318+
pkvm_ownership_selftest(selftest_base);
313319
out:
314320
/*
315321
* We tail-called to here from handle___pkvm_init() and will not return,

arch/arm64/kvm/pkvm.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ void __init kvm_hyp_reserve(void)
7979
hyp_mem_pages += host_s2_pgtable_pages();
8080
hyp_mem_pages += hyp_vm_table_pages();
8181
hyp_mem_pages += hyp_vmemmap_pages(STRUCT_HYP_PAGE_SIZE);
82+
hyp_mem_pages += pkvm_selftest_pages();
8283
hyp_mem_pages += hyp_ffa_proxy_pages();
8384

8485
/*

0 commit comments

Comments
 (0)