Skip to content

Commit d5702dd

Browse files
author
Marc Zyngier
committed
Merge branch kvm-arm64/pkvm-selftest-6.16 into kvm-arm64/pkvm-np-thp-6.16
* kvm-arm64/pkvm-selftest-6.16: : . : pKVM selftests covering the memory ownership transitions by : Quentin Perret. From the initial cover letter: : : "We have recently found a bug [1] in the pKVM memory ownership : transitions by code inspection, but it could have been caught with a : test. : : Introduce a boot-time selftest exercising all the known pKVM memory : transitions and importantly checks the rejection of illegal transitions. : : The new test is hidden behind a new Kconfig option separate from : CONFIG_EL2_NVHE_DEBUG on purpose as that has side effects on the : transition checks ([1] doesn't reproduce with EL2 debug enabled). : : [1] https://lore.kernel.org/kvmarm/20241128154406.602875-1-qperret@google.com/" : . KVM: arm64: Extend pKVM selftest for np-guests KVM: arm64: Selftest for pKVM transitions KVM: arm64: Don't WARN from __pkvm_host_share_guest() KVM: arm64: Add .hyp.data section Signed-off-by: Marc Zyngier <maz@kernel.org>
2 parents cd6b97b + 48d5645 commit d5702dd

File tree

10 files changed

+250
-4
lines changed

10 files changed

+250
-4
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/include/asm/sections.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ extern char __alt_instructions[], __alt_instructions_end[];
1111
extern char __hibernate_exit_text_start[], __hibernate_exit_text_end[];
1212
extern char __hyp_idmap_text_start[], __hyp_idmap_text_end[];
1313
extern char __hyp_text_start[], __hyp_text_end[];
14+
extern char __hyp_data_start[], __hyp_data_end[];
1415
extern char __hyp_rodata_start[], __hyp_rodata_end[];
1516
extern char __hyp_reloc_begin[], __hyp_reloc_end[];
1617
extern char __hyp_bss_start[], __hyp_bss_end[];

arch/arm64/kernel/image-vars.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ KVM_NVHE_ALIAS(__hyp_text_start);
127127
KVM_NVHE_ALIAS(__hyp_text_end);
128128
KVM_NVHE_ALIAS(__hyp_bss_start);
129129
KVM_NVHE_ALIAS(__hyp_bss_end);
130+
KVM_NVHE_ALIAS(__hyp_data_start);
131+
KVM_NVHE_ALIAS(__hyp_data_end);
130132
KVM_NVHE_ALIAS(__hyp_rodata_start);
131133
KVM_NVHE_ALIAS(__hyp_rodata_end);
132134

arch/arm64/kernel/vmlinux.lds.S

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
*(__kvm_ex_table) \
1414
__stop___kvm_ex_table = .;
1515

16-
#define HYPERVISOR_DATA_SECTIONS \
16+
#define HYPERVISOR_RODATA_SECTIONS \
1717
HYP_SECTION_NAME(.rodata) : { \
1818
. = ALIGN(PAGE_SIZE); \
1919
__hyp_rodata_start = .; \
@@ -23,6 +23,15 @@
2323
__hyp_rodata_end = .; \
2424
}
2525

26+
#define HYPERVISOR_DATA_SECTION \
27+
HYP_SECTION_NAME(.data) : { \
28+
. = ALIGN(PAGE_SIZE); \
29+
__hyp_data_start = .; \
30+
*(HYP_SECTION_NAME(.data)) \
31+
. = ALIGN(PAGE_SIZE); \
32+
__hyp_data_end = .; \
33+
}
34+
2635
#define HYPERVISOR_PERCPU_SECTION \
2736
. = ALIGN(PAGE_SIZE); \
2837
HYP_SECTION_NAME(.data..percpu) : { \
@@ -51,7 +60,8 @@
5160
#define SBSS_ALIGN PAGE_SIZE
5261
#else /* CONFIG_KVM */
5362
#define HYPERVISOR_EXTABLE
54-
#define HYPERVISOR_DATA_SECTIONS
63+
#define HYPERVISOR_RODATA_SECTIONS
64+
#define HYPERVISOR_DATA_SECTION
5565
#define HYPERVISOR_PERCPU_SECTION
5666
#define HYPERVISOR_RELOC_SECTION
5767
#define SBSS_ALIGN 0
@@ -190,7 +200,7 @@ SECTIONS
190200
/* everything from this point to __init_begin will be marked RO NX */
191201
RO_DATA(PAGE_SIZE)
192202

193-
HYPERVISOR_DATA_SECTIONS
203+
HYPERVISOR_RODATA_SECTIONS
194204

195205
.got : { *(.got) }
196206
/*
@@ -295,6 +305,8 @@ SECTIONS
295305
_sdata = .;
296306
RW_DATA(L1_CACHE_BYTES, PAGE_SIZE, THREAD_ALIGN)
297307

308+
HYPERVISOR_DATA_SECTION
309+
298310
/*
299311
* Data written with the MMU off but read with the MMU on requires
300312
* cache lines to be invalidated, discarding up to a Cache Writeback

arch/arm64/kvm/arm.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2604,6 +2604,13 @@ static int __init init_hyp_mode(void)
26042604
goto out_err;
26052605
}
26062606

2607+
err = create_hyp_mappings(kvm_ksym_ref(__hyp_data_start),
2608+
kvm_ksym_ref(__hyp_data_end), PAGE_HYP);
2609+
if (err) {
2610+
kvm_err("Cannot map .hyp.data section\n");
2611+
goto out_err;
2612+
}
2613+
26072614
err = create_hyp_mappings(kvm_ksym_ref(__hyp_rodata_start),
26082615
kvm_ksym_ref(__hyp_rodata_end), PAGE_HYP_RO);
26092616
if (err) {

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,10 @@ static __always_inline void __load_host_stage2(void)
6767
else
6868
write_sysreg(0, vttbr_el2);
6969
}
70+
71+
#ifdef CONFIG_NVHE_EL2_DEBUG
72+
void pkvm_ownership_selftest(void *base);
73+
#else
74+
static inline void pkvm_ownership_selftest(void *base) { }
75+
#endif
7076
#endif /* __KVM_NVHE_MEM_PROTECT__ */

arch/arm64/kvm/hyp/nvhe/hyp.lds.S

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,7 @@ SECTIONS {
2525
BEGIN_HYP_SECTION(.data..percpu)
2626
PERCPU_INPUT(L1_CACHE_BYTES)
2727
END_HYP_SECTION
28+
2829
HYP_SECTION(.bss)
30+
HYP_SECTION(.data)
2931
}

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

Lines changed: 197 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -921,7 +921,6 @@ int __pkvm_host_share_guest(u64 pfn, u64 gfn, struct pkvm_hyp_vcpu *vcpu,
921921
if (page->host_share_guest_count)
922922
break;
923923
/* Only host to np-guest multi-sharing is tolerated */
924-
WARN_ON(1);
925924
fallthrough;
926925
default:
927926
ret = -EPERM;
@@ -1093,3 +1092,200 @@ int __pkvm_host_mkyoung_guest(u64 gfn, struct pkvm_hyp_vcpu *vcpu)
10931092

10941093
return 0;
10951094
}
1095+
1096+
#ifdef CONFIG_NVHE_EL2_DEBUG
1097+
struct pkvm_expected_state {
1098+
enum pkvm_page_state host;
1099+
enum pkvm_page_state hyp;
1100+
enum pkvm_page_state guest[2]; /* [ gfn, gfn + 1 ] */
1101+
};
1102+
1103+
static struct pkvm_expected_state selftest_state;
1104+
static struct hyp_page *selftest_page;
1105+
1106+
static struct pkvm_hyp_vm selftest_vm = {
1107+
.kvm = {
1108+
.arch = {
1109+
.mmu = {
1110+
.arch = &selftest_vm.kvm.arch,
1111+
.pgt = &selftest_vm.pgt,
1112+
},
1113+
},
1114+
},
1115+
};
1116+
1117+
static struct pkvm_hyp_vcpu selftest_vcpu = {
1118+
.vcpu = {
1119+
.arch = {
1120+
.hw_mmu = &selftest_vm.kvm.arch.mmu,
1121+
},
1122+
.kvm = &selftest_vm.kvm,
1123+
},
1124+
};
1125+
1126+
static void init_selftest_vm(void *virt)
1127+
{
1128+
struct hyp_page *p = hyp_virt_to_page(virt);
1129+
int i;
1130+
1131+
selftest_vm.kvm.arch.mmu.vtcr = host_mmu.arch.mmu.vtcr;
1132+
WARN_ON(kvm_guest_prepare_stage2(&selftest_vm, virt));
1133+
1134+
for (i = 0; i < pkvm_selftest_pages(); i++) {
1135+
if (p[i].refcount)
1136+
continue;
1137+
p[i].refcount = 1;
1138+
hyp_put_page(&selftest_vm.pool, hyp_page_to_virt(&p[i]));
1139+
}
1140+
}
1141+
1142+
static u64 selftest_ipa(void)
1143+
{
1144+
return BIT(selftest_vm.pgt.ia_bits - 1);
1145+
}
1146+
1147+
static void assert_page_state(void)
1148+
{
1149+
void *virt = hyp_page_to_virt(selftest_page);
1150+
u64 size = PAGE_SIZE << selftest_page->order;
1151+
struct pkvm_hyp_vcpu *vcpu = &selftest_vcpu;
1152+
u64 phys = hyp_virt_to_phys(virt);
1153+
u64 ipa[2] = { selftest_ipa(), selftest_ipa() + PAGE_SIZE };
1154+
1155+
host_lock_component();
1156+
WARN_ON(__host_check_page_state_range(phys, size, selftest_state.host));
1157+
host_unlock_component();
1158+
1159+
hyp_lock_component();
1160+
WARN_ON(__hyp_check_page_state_range(phys, size, selftest_state.hyp));
1161+
hyp_unlock_component();
1162+
1163+
guest_lock_component(&selftest_vm);
1164+
WARN_ON(__guest_check_page_state_range(vcpu, ipa[0], size, selftest_state.guest[0]));
1165+
WARN_ON(__guest_check_page_state_range(vcpu, ipa[1], size, selftest_state.guest[1]));
1166+
guest_unlock_component(&selftest_vm);
1167+
}
1168+
1169+
#define assert_transition_res(res, fn, ...) \
1170+
do { \
1171+
WARN_ON(fn(__VA_ARGS__) != res); \
1172+
assert_page_state(); \
1173+
} while (0)
1174+
1175+
void pkvm_ownership_selftest(void *base)
1176+
{
1177+
enum kvm_pgtable_prot prot = KVM_PGTABLE_PROT_RWX;
1178+
void *virt = hyp_alloc_pages(&host_s2_pool, 0);
1179+
struct pkvm_hyp_vcpu *vcpu = &selftest_vcpu;
1180+
struct pkvm_hyp_vm *vm = &selftest_vm;
1181+
u64 phys, size, pfn, gfn;
1182+
1183+
WARN_ON(!virt);
1184+
selftest_page = hyp_virt_to_page(virt);
1185+
selftest_page->refcount = 0;
1186+
init_selftest_vm(base);
1187+
1188+
size = PAGE_SIZE << selftest_page->order;
1189+
phys = hyp_virt_to_phys(virt);
1190+
pfn = hyp_phys_to_pfn(phys);
1191+
gfn = hyp_phys_to_pfn(selftest_ipa());
1192+
1193+
selftest_state.host = PKVM_NOPAGE;
1194+
selftest_state.hyp = PKVM_PAGE_OWNED;
1195+
selftest_state.guest[0] = selftest_state.guest[1] = PKVM_NOPAGE;
1196+
assert_page_state();
1197+
assert_transition_res(-EPERM, __pkvm_host_donate_hyp, pfn, 1);
1198+
assert_transition_res(-EPERM, __pkvm_host_share_hyp, pfn);
1199+
assert_transition_res(-EPERM, __pkvm_host_unshare_hyp, pfn);
1200+
assert_transition_res(-EPERM, __pkvm_host_share_ffa, pfn, 1);
1201+
assert_transition_res(-EPERM, __pkvm_host_unshare_ffa, pfn, 1);
1202+
assert_transition_res(-EPERM, hyp_pin_shared_mem, virt, virt + size);
1203+
assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn, vcpu, prot);
1204+
assert_transition_res(-ENOENT, __pkvm_host_unshare_guest, gfn, vm);
1205+
1206+
selftest_state.host = PKVM_PAGE_OWNED;
1207+
selftest_state.hyp = PKVM_NOPAGE;
1208+
assert_transition_res(0, __pkvm_hyp_donate_host, pfn, 1);
1209+
assert_transition_res(-EPERM, __pkvm_hyp_donate_host, pfn, 1);
1210+
assert_transition_res(-EPERM, __pkvm_host_unshare_hyp, pfn);
1211+
assert_transition_res(-EPERM, __pkvm_host_unshare_ffa, pfn, 1);
1212+
assert_transition_res(-ENOENT, __pkvm_host_unshare_guest, gfn, vm);
1213+
assert_transition_res(-EPERM, hyp_pin_shared_mem, virt, virt + size);
1214+
1215+
selftest_state.host = PKVM_PAGE_SHARED_OWNED;
1216+
selftest_state.hyp = PKVM_PAGE_SHARED_BORROWED;
1217+
assert_transition_res(0, __pkvm_host_share_hyp, pfn);
1218+
assert_transition_res(-EPERM, __pkvm_host_share_hyp, pfn);
1219+
assert_transition_res(-EPERM, __pkvm_host_donate_hyp, pfn, 1);
1220+
assert_transition_res(-EPERM, __pkvm_host_share_ffa, pfn, 1);
1221+
assert_transition_res(-EPERM, __pkvm_hyp_donate_host, pfn, 1);
1222+
assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn, vcpu, prot);
1223+
assert_transition_res(-ENOENT, __pkvm_host_unshare_guest, gfn, vm);
1224+
1225+
assert_transition_res(0, hyp_pin_shared_mem, virt, virt + size);
1226+
assert_transition_res(0, hyp_pin_shared_mem, virt, virt + size);
1227+
hyp_unpin_shared_mem(virt, virt + size);
1228+
WARN_ON(hyp_page_count(virt) != 1);
1229+
assert_transition_res(-EBUSY, __pkvm_host_unshare_hyp, pfn);
1230+
assert_transition_res(-EPERM, __pkvm_host_share_hyp, pfn);
1231+
assert_transition_res(-EPERM, __pkvm_host_donate_hyp, pfn, 1);
1232+
assert_transition_res(-EPERM, __pkvm_host_share_ffa, pfn, 1);
1233+
assert_transition_res(-EPERM, __pkvm_hyp_donate_host, pfn, 1);
1234+
assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn, vcpu, prot);
1235+
assert_transition_res(-ENOENT, __pkvm_host_unshare_guest, gfn, vm);
1236+
1237+
hyp_unpin_shared_mem(virt, virt + size);
1238+
assert_page_state();
1239+
WARN_ON(hyp_page_count(virt));
1240+
1241+
selftest_state.host = PKVM_PAGE_OWNED;
1242+
selftest_state.hyp = PKVM_NOPAGE;
1243+
assert_transition_res(0, __pkvm_host_unshare_hyp, pfn);
1244+
1245+
selftest_state.host = PKVM_PAGE_SHARED_OWNED;
1246+
selftest_state.hyp = PKVM_NOPAGE;
1247+
assert_transition_res(0, __pkvm_host_share_ffa, pfn, 1);
1248+
assert_transition_res(-EPERM, __pkvm_host_share_ffa, pfn, 1);
1249+
assert_transition_res(-EPERM, __pkvm_host_donate_hyp, pfn, 1);
1250+
assert_transition_res(-EPERM, __pkvm_host_share_hyp, pfn);
1251+
assert_transition_res(-EPERM, __pkvm_host_unshare_hyp, pfn);
1252+
assert_transition_res(-EPERM, __pkvm_hyp_donate_host, pfn, 1);
1253+
assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn, vcpu, prot);
1254+
assert_transition_res(-ENOENT, __pkvm_host_unshare_guest, gfn, vm);
1255+
assert_transition_res(-EPERM, hyp_pin_shared_mem, virt, virt + size);
1256+
1257+
selftest_state.host = PKVM_PAGE_OWNED;
1258+
selftest_state.hyp = PKVM_NOPAGE;
1259+
assert_transition_res(0, __pkvm_host_unshare_ffa, pfn, 1);
1260+
assert_transition_res(-EPERM, __pkvm_host_unshare_ffa, pfn, 1);
1261+
1262+
selftest_state.host = PKVM_PAGE_SHARED_OWNED;
1263+
selftest_state.guest[0] = PKVM_PAGE_SHARED_BORROWED;
1264+
assert_transition_res(0, __pkvm_host_share_guest, pfn, gfn, vcpu, prot);
1265+
assert_transition_res(-EPERM, __pkvm_host_share_guest, pfn, gfn, vcpu, prot);
1266+
assert_transition_res(-EPERM, __pkvm_host_share_ffa, pfn, 1);
1267+
assert_transition_res(-EPERM, __pkvm_host_donate_hyp, pfn, 1);
1268+
assert_transition_res(-EPERM, __pkvm_host_share_hyp, pfn);
1269+
assert_transition_res(-EPERM, __pkvm_host_unshare_hyp, pfn);
1270+
assert_transition_res(-EPERM, __pkvm_hyp_donate_host, pfn, 1);
1271+
assert_transition_res(-EPERM, hyp_pin_shared_mem, virt, virt + size);
1272+
1273+
selftest_state.guest[1] = PKVM_PAGE_SHARED_BORROWED;
1274+
assert_transition_res(0, __pkvm_host_share_guest, pfn, gfn + 1, vcpu, prot);
1275+
WARN_ON(hyp_virt_to_page(virt)->host_share_guest_count != 2);
1276+
1277+
selftest_state.guest[0] = PKVM_NOPAGE;
1278+
assert_transition_res(0, __pkvm_host_unshare_guest, gfn, vm);
1279+
1280+
selftest_state.guest[1] = PKVM_NOPAGE;
1281+
selftest_state.host = PKVM_PAGE_OWNED;
1282+
assert_transition_res(0, __pkvm_host_unshare_guest, gfn + 1, vm);
1283+
1284+
selftest_state.host = PKVM_NOPAGE;
1285+
selftest_state.hyp = PKVM_PAGE_OWNED;
1286+
assert_transition_res(0, __pkvm_host_donate_hyp, pfn, 1);
1287+
1288+
selftest_page->refcount = 1;
1289+
hyp_put_page(&host_s2_pool, virt);
1290+
}
1291+
#endif

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

Lines changed: 12 additions & 0 deletions
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)
@@ -119,6 +125,10 @@ static int recreate_hyp_mappings(phys_addr_t phys, unsigned long size,
119125
if (ret)
120126
return ret;
121127

128+
ret = pkvm_create_mappings(__hyp_data_start, __hyp_data_end, PAGE_HYP);
129+
if (ret)
130+
return ret;
131+
122132
ret = pkvm_create_mappings(__hyp_rodata_start, __hyp_rodata_end, PAGE_HYP_RO);
123133
if (ret)
124134
return ret;
@@ -308,6 +318,8 @@ void __noreturn __pkvm_init_finalise(void)
308318
goto out;
309319

310320
pkvm_hyp_vm_table_init(vm_table_base);
321+
322+
pkvm_ownership_selftest(selftest_base);
311323
out:
312324
/*
313325
* We tail-called to here from handle___pkvm_init() and will not return,

arch/arm64/kvm/pkvm.c

Lines changed: 2 additions & 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
/*
@@ -262,6 +263,7 @@ static int __init finalize_pkvm(void)
262263
* at, which would end badly once inaccessible.
263264
*/
264265
kmemleak_free_part(__hyp_bss_start, __hyp_bss_end - __hyp_bss_start);
266+
kmemleak_free_part(__hyp_data_start, __hyp_data_end - __hyp_data_start);
265267
kmemleak_free_part(__hyp_rodata_start, __hyp_rodata_end - __hyp_rodata_start);
266268
kmemleak_free_part_phys(hyp_mem_base, hyp_mem_size);
267269

0 commit comments

Comments
 (0)