Skip to content

Commit 1e93d62

Browse files
committed
Support the COVH promote_to_tvm() ABI which causes a TVM to be created in a
single-step. Preload VM pages into memory, fill the NACL shared memory with boot vcpu state, and reflect the promote_to_tvm() call to the TSM. Support CoVE implementations that do not support dynamic page conversion. A TSM that does not support dynamic page conversion does not require the donation of pages to store VCPU state in confidential memory. Signed-off-by: Wojciech Ozga <woz@zurich.ibm.com>
1 parent 68723d9 commit 1e93d62

File tree

11 files changed

+226
-29
lines changed

11 files changed

+226
-29
lines changed

arch/riscv/include/asm/kvm_cove.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,10 @@ int kvm_riscv_cove_init(void);
138138

139139
/* TVM related functions */
140140
void kvm_riscv_cove_vm_destroy(struct kvm *kvm);
141-
int kvm_riscv_cove_vm_init(struct kvm *kvm);
141+
int kvm_riscv_cove_vm_single_step_init(struct kvm_vcpu *vcpu,
142+
unsigned long fdt_address,
143+
unsigned long tap_addr);
144+
int kvm_riscv_cove_vm_multi_step_init(struct kvm *kvm);
142145

143146
/* TVM VCPU related functions */
144147
void kvm_riscv_cove_vcpu_destroy(struct kvm_vcpu *vcpu);
@@ -172,7 +175,13 @@ static inline int kvm_riscv_cove_hardware_enable(void) {return 0; }
172175

173176
/* TVM related functions */
174177
static inline void kvm_riscv_cove_vm_destroy(struct kvm *kvm) {}
175-
static inline int kvm_riscv_cove_vm_init(struct kvm *kvm) {return -1; }
178+
static inline int kvm_riscv_cove_vm_single_step_init(struct kvm_vcpu *vcpu,
179+
unsigned long fdt_address,
180+
unsigned long tap_addr)
181+
{
182+
return -1;
183+
}
184+
static inline int kvm_riscv_cove_vm_multi_step_init(struct kvm *kvm) {return -1; }
176185

177186
/* TVM VCPU related functions */
178187
static inline void kvm_riscv_cove_vcpu_destroy(struct kvm_vcpu *vcpu) {}

arch/riscv/include/asm/kvm_cove_sbi.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ int sbi_covh_tvm_demote_page(unsigned long tvmid,
7878
int sbi_covh_tvm_remove_pages(unsigned long tvmid,
7979
unsigned long tvm_base_page_addr,
8080
unsigned long len);
81+
int sbi_covh_tsm_promote_to_tvm(unsigned long fdt_address,
82+
unsigned long tap_addr,
83+
unsigned long sepc,
84+
unsigned long *tvmid);
8185

8286
/* Functions related to CoVE Interrupt Management(COVI) Extension */
8387
int sbi_covi_tvm_aia_init(unsigned long tvm_gid, struct sbi_cove_tvm_aia_params *tvm_aia_params);

arch/riscv/include/asm/kvm_vcpu_sbi.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_experimental;
6868
extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_vendor;
6969
#ifdef CONFIG_RISCV_COVE_HOST
7070
extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_covg;
71+
extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_covh;
7172
#endif
7273

7374
#endif /* __RISCV_KVM_VCPU_SBI_H__ */

arch/riscv/include/uapi/asm/kvm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ enum KVM_RISCV_SBI_EXT_ID {
149149
KVM_RISCV_SBI_EXT_VENDOR,
150150
KVM_RISCV_SBI_EXT_DBCN,
151151
KVM_RISCV_SBI_EXT_COVG,
152+
KVM_RISCV_SBI_EXT_COVH,
152153
KVM_RISCV_SBI_EXT_MAX,
153154
};
154155

arch/riscv/kvm/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,4 @@ kvm-y += aia.o
3131
kvm-y += aia_device.o
3232
kvm-y += aia_aplic.o
3333
kvm-y += aia_imsic.o
34-
kvm-$(CONFIG_RISCV_COVE_HOST) += cove_sbi.o cove.o vcpu_sbi_covg.o
34+
kvm-$(CONFIG_RISCV_COVE_HOST) += cove_sbi.o cove.o vcpu_sbi_covg.o vcpu_sbi_covh.o

arch/riscv/kvm/cove.c

Lines changed: 89 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -633,12 +633,12 @@ void kvm_riscv_cove_vcpu_destroy(struct kvm_vcpu *vcpu)
633633

634634
int kvm_riscv_cove_vcpu_init(struct kvm_vcpu *vcpu)
635635
{
636-
int rc;
637-
struct kvm *kvm;
638636
struct kvm_cove_tvm_vcpu_context *tvcpuc;
639637
struct kvm_cove_tvm_context *tvmc;
640-
struct page *vcpus_page;
641638
unsigned long vcpus_phys_addr;
639+
struct page *vcpus_page;
640+
struct kvm *kvm;
641+
int rc;
642642

643643
if (!vcpu)
644644
return -EINVAL;
@@ -659,36 +659,39 @@ int kvm_riscv_cove_vcpu_init(struct kvm_vcpu *vcpu)
659659
if (!tvcpuc)
660660
return -ENOMEM;
661661

662-
vcpus_page = alloc_pages(GFP_KERNEL | __GFP_ZERO,
663-
get_order_num_pages(tinfo.tvcpu_pages_needed));
664-
if (!vcpus_page) {
665-
rc = -ENOMEM;
666-
goto alloc_page_failed;
667-
}
668-
669662
tvcpuc->vcpu = vcpu;
670663
tvcpuc->vcpu_state.npages = tinfo.tvcpu_pages_needed;
671-
tvcpuc->vcpu_state.page = vcpus_page;
672-
vcpus_phys_addr = page_to_phys(vcpus_page);
673664

674-
rc = cove_convert_pages(vcpus_phys_addr, tvcpuc->vcpu_state.npages, true);
675-
if (rc)
676-
goto convert_failed;
665+
if (kvm_riscv_cove_capability(KVM_COVE_TSM_CAP_MEMORY_ALLOCATION)) {
666+
vcpus_page = alloc_pages(GFP_KERNEL | __GFP_ZERO,
667+
get_order_num_pages(tinfo.tvcpu_pages_needed));
668+
if (!vcpus_page) {
669+
rc = -ENOMEM;
670+
goto alloc_page_failed;
671+
}
672+
tvcpuc->vcpu_state.page = vcpus_page;
673+
vcpus_phys_addr = page_to_phys(vcpus_page);
677674

678-
rc = sbi_covh_create_tvm_vcpu(tvmc->tvm_guest_id, vcpu->vcpu_idx, vcpus_phys_addr);
679-
if (rc)
680-
goto vcpu_create_failed;
675+
rc = cove_convert_pages(vcpus_phys_addr, tvcpuc->vcpu_state.npages, true);
676+
if (rc)
677+
goto convert_failed;
681678

679+
rc = sbi_covh_create_tvm_vcpu(tvmc->tvm_guest_id, vcpu->vcpu_idx, vcpus_phys_addr);
680+
if (rc)
681+
goto vcpu_create_failed;
682+
}
682683
vcpu->arch.tc = tvcpuc;
683684

684685
return 0;
685686

686687
vcpu_create_failed:
687688
/* Reclaim all the pages or return to the confidential page pool */
688-
sbi_covh_tsm_reclaim_pages(vcpus_phys_addr, tvcpuc->vcpu_state.npages);
689+
if (kvm_riscv_cove_capability(KVM_COVE_TSM_CAP_MEMORY_ALLOCATION))
690+
sbi_covh_tsm_reclaim_pages(vcpus_phys_addr, tvcpuc->vcpu_state.npages);
689691

690692
convert_failed:
691-
__free_pages(vcpus_page, get_order_num_pages(tinfo.tvcpu_pages_needed));
693+
if (kvm_riscv_cove_capability(KVM_COVE_TSM_CAP_MEMORY_ALLOCATION))
694+
__free_pages(vcpus_page, get_order_num_pages(tinfo.tvcpu_pages_needed));
692695

693696
alloc_page_failed:
694697
kfree(tvcpuc);
@@ -882,14 +885,18 @@ void kvm_riscv_cove_vm_destroy(struct kvm *kvm)
882885
kvm_err("Memory reclaim failed with rc %d\n", rc);
883886
}
884887

885-
int kvm_riscv_cove_vm_init(struct kvm *kvm)
888+
int kvm_riscv_cove_vm_multi_step_init(struct kvm *kvm)
886889
{
887890
struct kvm_cove_tvm_context *tvmc;
888891
struct page *tvms_page, *pgt_page;
889892
unsigned long tvm_gid, pgt_phys_addr, tvms_phys_addr;
890893
unsigned long gstage_pgd_size = kvm_riscv_gstage_pgd_size();
891894
int rc = 0;
892895

896+
// Multi-step TVM creation requires TSM that supports dynamic page conversion
897+
if (!kvm_riscv_cove_capability(KVM_COVE_TSM_CAP_MEMORY_ALLOCATION))
898+
return -EOPNOTSUPP;
899+
893900
tvmc = kzalloc(sizeof(*tvmc), GFP_KERNEL);
894901
if (!tvmc)
895902
return -ENOMEM;
@@ -985,6 +992,67 @@ int kvm_riscv_cove_vm_init(struct kvm *kvm)
985992
return rc;
986993
}
987994

995+
int kvm_riscv_cove_vm_single_step_init(struct kvm_vcpu *vcpu, unsigned long fdt_address,
996+
unsigned long tap_addr)
997+
{
998+
struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
999+
unsigned long tvm_gid, target_vcpuid;
1000+
struct kvm_cove_tvm_context *tvmc;
1001+
struct kvm_vcpu *target_vcpu;
1002+
struct kvm *kvm = vcpu->kvm;
1003+
void *nshmem = nacl_shmem();
1004+
int rc = 0, gpr_id, offset;
1005+
1006+
if (!kvm_riscv_cove_capability(KVM_COVE_TSM_CAP_PROMOTE_TVM))
1007+
return -EOPNOTSUPP;
1008+
1009+
tvmc = kzalloc(sizeof(*tvmc), GFP_KERNEL);
1010+
if (!tvmc)
1011+
return -ENOMEM;
1012+
1013+
for (gpr_id = 1; gpr_id < 32; gpr_id++) {
1014+
offset = KVM_ARCH_GUEST_ZERO + gpr_id * sizeof(unsigned long);
1015+
nacl_shmem_gpr_write_cove(nshmem, offset,
1016+
((unsigned long *)cp)[gpr_id]);
1017+
}
1018+
kvm_arch_vcpu_load(vcpu, smp_processor_id());
1019+
rc = sbi_covh_tsm_promote_to_tvm(fdt_address, tap_addr, cp->sepc, &tvm_gid);
1020+
if (rc)
1021+
goto done;
1022+
1023+
INIT_LIST_HEAD(&tvmc->measured_pages);
1024+
INIT_LIST_HEAD(&tvmc->zero_pages);
1025+
INIT_LIST_HEAD(&tvmc->shared_pages);
1026+
INIT_LIST_HEAD(&tvmc->reclaim_pending_pages);
1027+
1028+
tvmc->tvm_guest_id = tvm_gid;
1029+
tvmc->kvm = kvm;
1030+
kvm->arch.tvmc = tvmc;
1031+
1032+
kvm_for_each_vcpu(target_vcpuid, target_vcpu, kvm) {
1033+
rc = kvm_riscv_cove_vcpu_init(target_vcpu);
1034+
if (rc)
1035+
goto vcpus_allocated;
1036+
1037+
target_vcpu->requests = 0;
1038+
if (target_vcpu->vcpu_idx != 0)
1039+
kvm_riscv_vcpu_power_off(target_vcpu);
1040+
}
1041+
1042+
tvmc->finalized_done = true;
1043+
kvm_info("Guest VM creation successful with guest id %lx\n", tvm_gid);
1044+
return 0;
1045+
1046+
vcpus_allocated:
1047+
kvm_for_each_vcpu(target_vcpuid, target_vcpu, kvm)
1048+
if (target_vcpu->arch.tc)
1049+
kfree(target_vcpu->arch.tc);
1050+
1051+
done:
1052+
kfree(tvmc);
1053+
return rc;
1054+
}
1055+
9881056
int kvm_riscv_cove_init(void)
9891057
{
9901058
int rc;

arch/riscv/kvm/cove_sbi.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,3 +488,23 @@ int sbi_covh_tvm_remove_pages(unsigned long tvmid,
488488

489489
return 0;
490490
}
491+
492+
int sbi_covh_tsm_promote_to_tvm(unsigned long fdt_address,
493+
unsigned long tap_addr,
494+
unsigned long sepc,
495+
unsigned long *tvmid)
496+
{
497+
struct sbiret ret;
498+
int rc = 0;
499+
500+
ret = sbi_ecall(SBI_EXT_COVH, SBI_EXT_COVH_PROMOTE_TO_TVM, fdt_address,
501+
tap_addr, sepc, 0, 0, 0);
502+
if (ret.error) {
503+
rc = sbi_err_map_linux_errno(ret.error);
504+
goto done;
505+
}
506+
507+
*tvmid = ret.value;
508+
done:
509+
return rc;
510+
}

arch/riscv/kvm/main.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ int kvm_arch_hardware_enable(void)
3232

3333
/*
3434
* We just need to invoke aia enable for CoVE if host is in VS mode and TSM
35-
* supports AIA (COVI extension). However, if the host is running in HS mode,
36-
* we need to initialize other CSRs as well for legacy VMs.
35+
* supports AIA (COVI extension). However, if the host is running in HS
36+
* mode, we need to initialize other CSRs as well for legacy VMs.
3737
*/
3838
if (unlikely(kvm_riscv_cove_enabled()) && kvm_riscv_cove_capability(KVM_COVE_TSM_CAP_AIA))
3939
goto enable_aia;

arch/riscv/kvm/vcpu_sbi.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ static const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_covg = {
4040
.extid_end = -1UL,
4141
.handler = NULL,
4242
};
43+
static const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_covh = {
44+
.extid_start = -1UL,
45+
.extid_end = -1UL,
46+
.handler = NULL,
47+
};
4348
#endif
4449

4550
struct kvm_riscv_sbi_extension_entry {
@@ -96,6 +101,10 @@ static const struct kvm_riscv_sbi_extension_entry sbi_ext[] = {
96101
.dis_idx = KVM_RISCV_SBI_EXT_COVG,
97102
.ext_ptr = &vcpu_sbi_ext_covg,
98103
},
104+
{
105+
.dis_idx = KVM_RISCV_SBI_EXT_COVH,
106+
.ext_ptr = &vcpu_sbi_ext_covh,
107+
},
99108
};
100109

101110
void kvm_riscv_vcpu_sbi_forward(struct kvm_vcpu *vcpu, struct kvm_run *run)

arch/riscv/kvm/vcpu_sbi_covh.c

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Copyright (c) 2024 IBM.
4+
*
5+
* Authors:
6+
* Wojciech Ozga <woz@zurich.ibm.com>
7+
*/
8+
9+
#include <linux/errno.h>
10+
#include <linux/err.h>
11+
#include <linux/kvm_host.h>
12+
#include <linux/list.h>
13+
#include <linux/mm.h>
14+
#include <linux/spinlock.h>
15+
#include <asm/csr.h>
16+
#include <asm/sbi.h>
17+
#include <asm/kvm_vcpu_sbi.h>
18+
#include <asm/kvm_cove.h>
19+
#include <asm/kvm_cove_sbi.h>
20+
#include <asm/kvm_nacl.h>
21+
#include <linux/rbtree.h>
22+
#include <linux/pgtable.h>
23+
24+
static int preload_pages(struct kvm_vcpu *vcpu) {
25+
unsigned long hva, fault_addr, page;
26+
struct kvm_memory_slot *memslot;
27+
bool writable;
28+
29+
memslot = search_memslots(kvm_memslots(vcpu->kvm),
30+
kernel_map.phys_addr, true);
31+
if (memslot) {
32+
for (page = 0; page < memslot->npages; page++) {
33+
fault_addr = gfn_to_gpa(memslot->base_gfn) +
34+
page * PAGE_SIZE;
35+
hva = gfn_to_hva_memslot_prot(memslot,
36+
gpa_to_gfn(fault_addr),
37+
&writable);
38+
if (!kvm_is_error_hva(hva))
39+
kvm_riscv_gstage_map(vcpu, memslot, fault_addr,
40+
hva, NULL);
41+
}
42+
}
43+
44+
return 0;
45+
}
46+
47+
static int kvm_riscv_cove_promote_to_tvm(struct kvm_vcpu *vcpu,
48+
unsigned long fdt_address,
49+
unsigned long tap_addr) {
50+
int rc;
51+
52+
preload_pages(vcpu);
53+
rc = kvm_riscv_cove_vm_single_step_init(vcpu, fdt_address, tap_addr);
54+
if (rc)
55+
goto done;
56+
57+
vcpu->kvm->arch.vm_type = KVM_VM_TYPE_RISCV_COVE;
58+
done:
59+
return rc;
60+
}
61+
62+
static int kvm_sbi_ext_covh_handler(struct kvm_vcpu *vcpu, struct kvm_run *run,
63+
struct kvm_vcpu_sbi_return *retdata)
64+
{
65+
struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
66+
unsigned long funcid = cp->a6;
67+
int ret;
68+
69+
switch (funcid) {
70+
case SBI_EXT_COVH_PROMOTE_TO_TVM:
71+
ret = kvm_riscv_cove_promote_to_tvm(vcpu, cp->a0, cp->a1);
72+
return 0;
73+
74+
default:
75+
kvm_err("%s: Unsupported guest SBI %ld.\n", __func__, funcid);
76+
retdata->err_val = SBI_ERR_NOT_SUPPORTED;
77+
return -EOPNOTSUPP;
78+
}
79+
}
80+
81+
const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_covh = {
82+
.extid_start = SBI_EXT_COVH,
83+
.extid_end = SBI_EXT_COVH,
84+
.handler = kvm_sbi_ext_covh_handler,
85+
};

arch/riscv/kvm/vm.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,15 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
4444

4545
if (unlikely(type == KVM_VM_TYPE_RISCV_COVE)) {
4646
if (!kvm_riscv_cove_enabled()) {
47-
kvm_err("Unable to init CoVE VM because cove is not enabled\n");
47+
kvm_err("Unable to init CoVE VM because CoVE extension is not enabled\n");
4848
return -EPERM;
4949
}
5050

51-
r = kvm_riscv_cove_vm_init(kvm);
51+
r = kvm_riscv_cove_vm_multi_step_init(kvm);
5252
if (r)
5353
return r;
5454
kvm->arch.vm_type = type;
55-
kvm_info("Trusted VM instance init successful\n");
55+
kvm_info("CoVE VM instance init successful\n");
5656
}
5757

5858
kvm_riscv_aia_init_vm(kvm);

0 commit comments

Comments
 (0)