crypto: ccp: remove multi-level pointers processing for vpsp
In the past, running TKM on a regular VM required copying TKM request data to the kernel buffer. However, in the CSV VM, all data is encrypted. If TKM requests contain pointers, the data pointed to by the pointers cannot be correctly parsed. Therefore, we have removed the handling of multi-level pointers in TKM requests. Now, all TKM commands must follow an offset-based model and cannot include pointers. Signed-off-by: xiongmengbiao <xiongmengbiao@hygon.cn>
This commit is contained in:
parent
bb33b91fa9
commit
ec92e554e7
|
@ -5252,8 +5252,8 @@ static int kvm_hygon_arch_hypercall(struct kvm *kvm, u64 nr, u64 a0, u64 a1, u64
|
|||
.read_guest = kvm_read_guest
|
||||
};
|
||||
switch (nr) {
|
||||
case KVM_HC_PSP_OP:
|
||||
ret = kvm_pv_psp_op(&vpsp, a0, a1, a2, a3);
|
||||
case KVM_HC_PSP_COPY_FORWARD_OP:
|
||||
ret = kvm_pv_psp_copy_forward_op(&vpsp, a0, a1, a2);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
@ -9879,7 +9879,9 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu)
|
|||
}
|
||||
|
||||
if (static_call(kvm_x86_get_cpl)(vcpu) != 0 &&
|
||||
!(is_x86_vendor_hygon() && (nr == KVM_HC_VM_ATTESTATION || nr == KVM_HC_PSP_OP))) {
|
||||
!(is_x86_vendor_hygon() && (nr == KVM_HC_VM_ATTESTATION
|
||||
|| nr == KVM_HC_PSP_OP_OBSOLETE
|
||||
|| nr == KVM_HC_PSP_COPY_FORWARD_OP))) {
|
||||
ret = -KVM_EPERM;
|
||||
goto out;
|
||||
}
|
||||
|
@ -9916,7 +9918,8 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu)
|
|||
kvm_sched_yield(vcpu, a0);
|
||||
ret = 0;
|
||||
break;
|
||||
case KVM_HC_PSP_OP:
|
||||
case KVM_HC_PSP_OP_OBSOLETE:
|
||||
case KVM_HC_PSP_COPY_FORWARD_OP:
|
||||
ret = -KVM_ENOSYS;
|
||||
if (kvm_arch_hypercall)
|
||||
ret = kvm_arch_hypercall(vcpu->kvm, nr, a0, a1, a2, a3);
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/psp.h>
|
||||
#include <linux/psp-hygon.h>
|
||||
#include <uapi/linux/psp-hygon.h>
|
||||
#include <linux/bitfield.h>
|
||||
|
||||
#include <asm/csv.h>
|
||||
|
||||
|
@ -1065,7 +1066,97 @@ end:
|
|||
return rb_supported;
|
||||
}
|
||||
|
||||
int __vpsp_do_cmd_locked(uint32_t vid, int cmd, void *data, int *psp_ret);
|
||||
int __vpsp_do_cmd_locked(uint32_t vid, int cmd, void *data, int *psp_ret)
|
||||
{
|
||||
struct psp_device *psp = psp_master;
|
||||
struct sev_device *sev;
|
||||
phys_addr_t phys_addr;
|
||||
unsigned int phys_lsb, phys_msb;
|
||||
unsigned int reg, ret = 0;
|
||||
|
||||
if (!psp || !psp->sev_data)
|
||||
return -ENODEV;
|
||||
|
||||
if (*hygon_psp_hooks.psp_dead)
|
||||
return -EBUSY;
|
||||
|
||||
sev = psp->sev_data;
|
||||
|
||||
if (data && WARN_ON_ONCE(!virt_addr_valid(data)))
|
||||
return -EINVAL;
|
||||
|
||||
/* Get the physical address of the command buffer */
|
||||
phys_addr = PUT_PSP_VID(__psp_pa(data), vid);
|
||||
phys_lsb = data ? lower_32_bits(phys_addr) : 0;
|
||||
phys_msb = data ? upper_32_bits(phys_addr) : 0;
|
||||
|
||||
dev_dbg(sev->dev, "sev command id %#x buffer 0x%08x%08x timeout %us\n",
|
||||
cmd, phys_msb, phys_lsb, *hygon_psp_hooks.psp_timeout);
|
||||
|
||||
print_hex_dump_debug("(in): ", DUMP_PREFIX_OFFSET, 16, 2, data,
|
||||
hygon_psp_hooks.sev_cmd_buffer_len(cmd), false);
|
||||
|
||||
iowrite32(phys_lsb, sev->io_regs + sev->vdata->cmdbuff_addr_lo_reg);
|
||||
iowrite32(phys_msb, sev->io_regs + sev->vdata->cmdbuff_addr_hi_reg);
|
||||
|
||||
sev->int_rcvd = 0;
|
||||
|
||||
reg = FIELD_PREP(SEV_CMDRESP_CMD, cmd) | SEV_CMDRESP_IOC;
|
||||
iowrite32(reg, sev->io_regs + sev->vdata->cmdresp_reg);
|
||||
|
||||
/* wait for command completion */
|
||||
ret = hygon_psp_hooks.sev_wait_cmd_ioc(sev, ®, *hygon_psp_hooks.psp_timeout);
|
||||
if (ret) {
|
||||
if (psp_ret)
|
||||
*psp_ret = 0;
|
||||
|
||||
dev_err(sev->dev, "sev command %#x timed out, disabling PSP\n", cmd);
|
||||
*hygon_psp_hooks.psp_dead = true;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
*hygon_psp_hooks.psp_timeout = *hygon_psp_hooks.psp_cmd_timeout;
|
||||
|
||||
if (psp_ret)
|
||||
*psp_ret = FIELD_GET(PSP_CMDRESP_STS, reg);
|
||||
|
||||
if (FIELD_GET(PSP_CMDRESP_STS, reg)) {
|
||||
dev_dbg(sev->dev, "sev command %#x failed (%#010lx)\n",
|
||||
cmd, FIELD_GET(PSP_CMDRESP_STS, reg));
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
print_hex_dump_debug("(out): ", DUMP_PREFIX_OFFSET, 16, 2, data,
|
||||
hygon_psp_hooks.sev_cmd_buffer_len(cmd), false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int vpsp_do_cmd(uint32_t vid, int cmd, void *data, int *psp_ret)
|
||||
{
|
||||
int rc;
|
||||
int mutex_enabled = READ_ONCE(hygon_psp_hooks.psp_mutex_enabled);
|
||||
|
||||
if (is_vendor_hygon() && mutex_enabled) {
|
||||
if (psp_mutex_lock_timeout(&hygon_psp_hooks.psp_misc->data_pg_aligned->mb_mutex,
|
||||
PSP_MUTEX_TIMEOUT) != 1) {
|
||||
return -EBUSY;
|
||||
}
|
||||
} else {
|
||||
mutex_lock(hygon_psp_hooks.sev_cmd_mutex);
|
||||
}
|
||||
|
||||
rc = __vpsp_do_cmd_locked(vid, cmd, data, psp_ret);
|
||||
|
||||
if (is_vendor_hygon() && mutex_enabled)
|
||||
psp_mutex_unlock(&hygon_psp_hooks.psp_misc->data_pg_aligned->mb_mutex);
|
||||
else
|
||||
mutex_unlock(hygon_psp_hooks.sev_cmd_mutex);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to obtain the result again by the command index, this
|
||||
* interface is used in ringbuffer mode
|
||||
|
|
|
@ -507,100 +507,6 @@ static int __psp_do_cmd_locked(int cmd, void *data, int *psp_ret)
|
|||
return ret;
|
||||
}
|
||||
|
||||
int __vpsp_do_cmd_locked(uint32_t vid, int cmd, void *data, int *psp_ret)
|
||||
{
|
||||
struct psp_device *psp = psp_master;
|
||||
struct sev_device *sev;
|
||||
phys_addr_t phys_addr;
|
||||
unsigned int phys_lsb, phys_msb;
|
||||
unsigned int reg, ret = 0;
|
||||
|
||||
if (!psp || !psp->sev_data || !hygon_psp_hooks.sev_dev_hooks_installed)
|
||||
return -ENODEV;
|
||||
|
||||
if (*hygon_psp_hooks.psp_dead)
|
||||
return -EBUSY;
|
||||
|
||||
sev = psp->sev_data;
|
||||
|
||||
if (data && WARN_ON_ONCE(!virt_addr_valid(data)))
|
||||
return -EINVAL;
|
||||
|
||||
/* Get the physical address of the command buffer */
|
||||
phys_addr = PUT_PSP_VID(__psp_pa(data), vid);
|
||||
phys_lsb = data ? lower_32_bits(phys_addr) : 0;
|
||||
phys_msb = data ? upper_32_bits(phys_addr) : 0;
|
||||
|
||||
dev_dbg(sev->dev, "sev command id %#x buffer 0x%08x%08x timeout %us\n",
|
||||
cmd, phys_msb, phys_lsb, *hygon_psp_hooks.psp_timeout);
|
||||
|
||||
print_hex_dump_debug("(in): ", DUMP_PREFIX_OFFSET, 16, 2, data,
|
||||
hygon_psp_hooks.sev_cmd_buffer_len(cmd), false);
|
||||
|
||||
iowrite32(phys_lsb, sev->io_regs + sev->vdata->cmdbuff_addr_lo_reg);
|
||||
iowrite32(phys_msb, sev->io_regs + sev->vdata->cmdbuff_addr_hi_reg);
|
||||
|
||||
sev->int_rcvd = 0;
|
||||
|
||||
reg = FIELD_PREP(SEV_CMDRESP_CMD, cmd) | SEV_CMDRESP_IOC;
|
||||
iowrite32(reg, sev->io_regs + sev->vdata->cmdresp_reg);
|
||||
|
||||
/* wait for command completion */
|
||||
ret = hygon_psp_hooks.sev_wait_cmd_ioc(sev, ®, *hygon_psp_hooks.psp_timeout);
|
||||
if (ret) {
|
||||
if (psp_ret)
|
||||
*psp_ret = 0;
|
||||
|
||||
dev_err(sev->dev, "sev command %#x timed out, disabling PSP\n", cmd);
|
||||
*hygon_psp_hooks.psp_dead = true;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
*hygon_psp_hooks.psp_timeout = *hygon_psp_hooks.psp_cmd_timeout;
|
||||
|
||||
if (psp_ret)
|
||||
*psp_ret = FIELD_GET(PSP_CMDRESP_STS, reg);
|
||||
|
||||
if (FIELD_GET(PSP_CMDRESP_STS, reg)) {
|
||||
dev_dbg(sev->dev, "sev command %#x failed (%#010lx)\n",
|
||||
cmd, FIELD_GET(PSP_CMDRESP_STS, reg));
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
print_hex_dump_debug("(out): ", DUMP_PREFIX_OFFSET, 16, 2, data,
|
||||
hygon_psp_hooks.sev_cmd_buffer_len(cmd), false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int vpsp_do_cmd(uint32_t vid, int cmd, void *data, int *psp_ret)
|
||||
{
|
||||
int rc;
|
||||
int mutex_enabled = READ_ONCE(hygon_psp_hooks.psp_mutex_enabled);
|
||||
|
||||
if (!hygon_psp_hooks.sev_dev_hooks_installed)
|
||||
return -ENODEV;
|
||||
|
||||
if (mutex_enabled) {
|
||||
if (psp_mutex_lock_timeout(&psp_misc->data_pg_aligned->mb_mutex,
|
||||
PSP_MUTEX_TIMEOUT) != 1) {
|
||||
return -EBUSY;
|
||||
}
|
||||
} else {
|
||||
mutex_lock(hygon_psp_hooks.sev_cmd_mutex);
|
||||
}
|
||||
|
||||
rc = __vpsp_do_cmd_locked(vid, cmd, data, psp_ret);
|
||||
|
||||
if (is_vendor_hygon() && mutex_enabled)
|
||||
psp_mutex_unlock(&psp_misc->data_pg_aligned->mb_mutex);
|
||||
else
|
||||
mutex_unlock(hygon_psp_hooks.sev_cmd_mutex);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int psp_do_cmd(int cmd, void *data, int *psp_ret)
|
||||
{
|
||||
int rc;
|
||||
|
|
|
@ -33,379 +33,53 @@
|
|||
* The primary implementation logic of virtual PSP in kernel mode
|
||||
* call trace:
|
||||
* guest command(vmmcall)
|
||||
* |
|
||||
* | |-> kvm_pv_psp_cmd_pre_op
|
||||
* | |
|
||||
* | | -> guest_addr_map_table_op
|
||||
* | |
|
||||
* | | -> guest_multiple_level_gpa_replace
|
||||
* |
|
||||
* kvm_pv_psp_op->|-> vpsp_try_do_cmd/vpsp_try_get_result <====> psp device driver
|
||||
* |-> kvm_pv_psp_cmd_pre_op
|
||||
* |
|
||||
* kvm_pv_psp_copy_forward_op->|-> vpsp_try_do_cmd/vpsp_try_get_result <====> psp device driver
|
||||
* |
|
||||
* |-> kvm_pv_psp_cmd_post_op
|
||||
* |
|
||||
* | -> guest_addr_map_table_op
|
||||
* |
|
||||
* | -> guest_multiple_level_gpa_restore
|
||||
*/
|
||||
|
||||
#define TKM_CMD_ID_MIN 0x120
|
||||
#define TKM_CMD_ID_MAX 0x12f
|
||||
|
||||
struct psp_cmdresp_head {
|
||||
uint32_t buf_size;
|
||||
uint32_t cmdresp_size;
|
||||
uint32_t cmdresp_code;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct map_tbl - multilevel pointer address mapping table
|
||||
*
|
||||
* @parent_pa: parent address block's physics address
|
||||
* @offset: offset in parent address block
|
||||
* @size: submemory size
|
||||
* @align: submemory align size, hva need to keep size alignment in kernel
|
||||
* @hva: submemory copy block in kernel virtual address
|
||||
*/
|
||||
struct map_tbl {
|
||||
uint64_t parent_pa;
|
||||
uint32_t offset;
|
||||
uint32_t size;
|
||||
uint32_t align;
|
||||
uint64_t hva;
|
||||
} __packed;
|
||||
|
||||
struct addr_map_tbls {
|
||||
uint32_t tbl_nums;
|
||||
struct map_tbl tbl[];
|
||||
} __packed;
|
||||
|
||||
/* gpa and hva conversion maintenance table for internal use */
|
||||
struct gpa2hva_t {
|
||||
void *hva;
|
||||
gpa_t gpa;
|
||||
};
|
||||
|
||||
struct gpa2hva_tbls {
|
||||
uint32_t max_nums;
|
||||
uint32_t tbl_nums;
|
||||
struct gpa2hva_t tbl[];
|
||||
};
|
||||
|
||||
/* save command data for restoring later */
|
||||
struct vpsp_hbuf_wrapper {
|
||||
void *data;
|
||||
uint32_t data_size;
|
||||
struct addr_map_tbls *map_tbls;
|
||||
struct gpa2hva_tbls *g2h_tbls;
|
||||
};
|
||||
|
||||
/* Virtual PSP host memory information maintenance, used in ringbuffer mode */
|
||||
struct vpsp_hbuf_wrapper
|
||||
g_hbuf_wrap[CSV_COMMAND_PRIORITY_NUM][CSV_RING_BUFFER_SIZE / CSV_RING_BUFFER_ESIZE] = {0};
|
||||
|
||||
void __maybe_unused map_tbl_dump(const char *title, struct addr_map_tbls *tbls)
|
||||
{
|
||||
int i;
|
||||
|
||||
pr_info("[%s]-> map_tbl_nums: %d", title, tbls->tbl_nums);
|
||||
for (i = 0; i < tbls->tbl_nums; i++) {
|
||||
pr_info("\t[%d]: parent_pa: 0x%llx, offset: 0x%x, size: 0x%x, align: 0x%x hva: 0x%llx",
|
||||
i, tbls->tbl[i].parent_pa, tbls->tbl[i].offset,
|
||||
tbls->tbl[i].size, tbls->tbl[i].align, tbls->tbl[i].hva);
|
||||
}
|
||||
pr_info("\n");
|
||||
}
|
||||
|
||||
void __maybe_unused g2h_tbl_dump(const char *title, struct gpa2hva_tbls *tbls)
|
||||
{
|
||||
int i;
|
||||
|
||||
pr_info("[%s]-> g2h_tbl_nums: %d, max_nums: %d", title, tbls->tbl_nums,
|
||||
tbls->max_nums);
|
||||
for (i = 0; i < tbls->tbl_nums; i++)
|
||||
pr_info("\t[%d]: hva: 0x%llx, gpa: 0x%llx", i,
|
||||
(uint64_t)tbls->tbl[i].hva, tbls->tbl[i].gpa);
|
||||
pr_info("\n");
|
||||
}
|
||||
|
||||
static int gpa2hva_tbl_fill(struct gpa2hva_tbls *tbls, void *hva, gpa_t gpa)
|
||||
{
|
||||
uint32_t fill_idx = tbls->tbl_nums;
|
||||
|
||||
if (fill_idx >= tbls->max_nums)
|
||||
return -EFAULT;
|
||||
|
||||
tbls->tbl[fill_idx].hva = hva;
|
||||
tbls->tbl[fill_idx].gpa = gpa;
|
||||
tbls->tbl_nums = fill_idx + 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void clear_hva_in_g2h_tbls(struct gpa2hva_tbls *g2h, void *hva)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < g2h->tbl_nums; i++) {
|
||||
if (g2h->tbl[i].hva == hva)
|
||||
g2h->tbl[i].hva = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void *get_hva_from_gpa(struct gpa2hva_tbls *g2h, gpa_t gpa)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < g2h->tbl_nums; i++) {
|
||||
if (g2h->tbl[i].gpa == gpa)
|
||||
return (void *)g2h->tbl[i].hva;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static gpa_t get_gpa_from_hva(struct gpa2hva_tbls *g2h, void *hva)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < g2h->tbl_nums; i++) {
|
||||
if (g2h->tbl[i].hva == hva)
|
||||
return g2h->tbl[i].gpa;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The virtual machine multilevel pointer command buffer handles the
|
||||
* execution entity, synchronizes the data in the original gpa to the
|
||||
* newly allocated hva(host virtual address) and updates the mapping
|
||||
* relationship in the parent memory
|
||||
*/
|
||||
static int guest_multiple_level_gpa_replace(struct kvm_vpsp *vpsp,
|
||||
struct map_tbl *tbl, struct gpa2hva_tbls *g2h)
|
||||
{
|
||||
int ret = 0;
|
||||
uint32_t sub_block_size;
|
||||
uint64_t sub_paddr;
|
||||
void *parent_kva = NULL;
|
||||
|
||||
/* kmalloc memory for child block */
|
||||
sub_block_size = max(tbl->size, tbl->align);
|
||||
tbl->hva = (uint64_t)kzalloc(sub_block_size, GFP_KERNEL);
|
||||
if (!tbl->hva)
|
||||
return -ENOMEM;
|
||||
|
||||
/* get child gpa from parent gpa */
|
||||
if (unlikely(vpsp->read_guest(vpsp->kvm, tbl->parent_pa + tbl->offset,
|
||||
&sub_paddr, sizeof(sub_paddr)))) {
|
||||
pr_err("[%s]: kvm_read_guest for parent gpa failed\n",
|
||||
__func__);
|
||||
ret = -EFAULT;
|
||||
goto e_free;
|
||||
}
|
||||
|
||||
/* copy child block data from gpa to hva */
|
||||
if (unlikely(vpsp->read_guest(vpsp->kvm, sub_paddr, (void *)tbl->hva,
|
||||
tbl->size))) {
|
||||
pr_err("[%s]: kvm_read_guest for sub_data failed\n",
|
||||
__func__);
|
||||
ret = -EFAULT;
|
||||
goto e_free;
|
||||
}
|
||||
|
||||
/* get hva from gpa */
|
||||
parent_kva = get_hva_from_gpa(g2h, tbl->parent_pa);
|
||||
if (unlikely(!parent_kva)) {
|
||||
pr_err("[%s]: get_hva_from_gpa for parent_pa failed\n",
|
||||
__func__);
|
||||
ret = -EFAULT;
|
||||
goto e_free;
|
||||
}
|
||||
|
||||
/* replace pa of hva from gpa */
|
||||
*(uint64_t *)((uint8_t *)parent_kva + tbl->offset) = __psp_pa(tbl->hva);
|
||||
|
||||
/* fill in gpa and hva to map table for restoring later */
|
||||
if (unlikely(gpa2hva_tbl_fill(g2h, (void *)tbl->hva, sub_paddr))) {
|
||||
pr_err("[%s]: gpa2hva_tbl_fill for sub_addr failed\n",
|
||||
__func__);
|
||||
ret = -EFAULT;
|
||||
goto e_free;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
e_free:
|
||||
kfree((const void *)tbl->hva);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* The virtual machine multi-level pointer command memory handles the
|
||||
* execution entity, synchronizes the data in the hva(host virtual
|
||||
* address) back to the memory corresponding to the gpa, and restores
|
||||
* the mapping relationship in the original parent memory
|
||||
*/
|
||||
static int guest_multiple_level_gpa_restore(struct kvm_vpsp *vpsp,
|
||||
struct map_tbl *tbl, struct gpa2hva_tbls *g2h)
|
||||
{
|
||||
int ret = 0;
|
||||
gpa_t sub_gpa;
|
||||
void *parent_hva = NULL;
|
||||
|
||||
/* get gpa from hva */
|
||||
sub_gpa = get_gpa_from_hva(g2h, (void *)tbl->hva);
|
||||
if (unlikely(!sub_gpa)) {
|
||||
pr_err("[%s]: get_gpa_from_hva for sub_gpa failed\n",
|
||||
__func__);
|
||||
ret = -EFAULT;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* copy child block data from hva to gpa */
|
||||
if (unlikely(vpsp->write_guest(vpsp->kvm, sub_gpa, (void *)tbl->hva,
|
||||
tbl->size))) {
|
||||
pr_err("[%s]: kvm_write_guest for sub_gpa failed\n",
|
||||
__func__);
|
||||
ret = -EFAULT;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* get parent hva from parent gpa */
|
||||
parent_hva = get_hva_from_gpa(g2h, tbl->parent_pa);
|
||||
if (unlikely(!parent_hva)) {
|
||||
pr_err("[%s]: get_hva_from_gpa for parent_pa failed\n",
|
||||
__func__);
|
||||
ret = -EFAULT;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* restore gpa from pa of hva in parent block */
|
||||
*(uint64_t *)((uint8_t *)parent_hva + tbl->offset) = sub_gpa;
|
||||
|
||||
/* free child block memory */
|
||||
clear_hva_in_g2h_tbls(g2h, (void *)tbl->hva);
|
||||
kfree((const void *)tbl->hva);
|
||||
tbl->hva = 0;
|
||||
|
||||
end:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* The virtual machine multilevel pointer command memory processing
|
||||
* executes upper-layer abstract interfaces, including replacing and
|
||||
* restoring two sub-processing functions
|
||||
*/
|
||||
static int guest_addr_map_table_op(struct kvm_vpsp *vpsp, struct gpa2hva_tbls *g2h,
|
||||
struct addr_map_tbls *map_tbls, int op)
|
||||
{
|
||||
int ret = 0;
|
||||
int i;
|
||||
uint64_t *sub_paddr_ptr;
|
||||
|
||||
if (op) {
|
||||
for (i = map_tbls->tbl_nums - 1; i >= 0; i--) {
|
||||
/* check if the gpa of root points to itself */
|
||||
if (map_tbls->tbl[i].parent_pa == g2h->tbl[0].gpa) {
|
||||
sub_paddr_ptr = (uint64_t *)((uint8_t *)g2h->tbl[0].hva
|
||||
+ map_tbls->tbl[i].offset);
|
||||
/* if the child paddr is equal to the parent paddr */
|
||||
if ((uint64_t)g2h->tbl[0].hva == map_tbls->tbl[i].hva) {
|
||||
*sub_paddr_ptr = g2h->tbl[0].gpa;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* restore new pa of kva with the gpa from guest */
|
||||
if (unlikely(guest_multiple_level_gpa_restore(vpsp,
|
||||
&map_tbls->tbl[i], g2h))) {
|
||||
pr_err("[%s]: guest_multiple_level_gpa_restore failed\n",
|
||||
__func__);
|
||||
ret = -EFAULT;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < map_tbls->tbl_nums; i++) {
|
||||
/* check if the gpa of root points to itself */
|
||||
if (map_tbls->tbl[i].parent_pa == g2h->tbl[0].gpa) {
|
||||
sub_paddr_ptr = (uint64_t *)((uint8_t *)g2h->tbl[0].hva
|
||||
+ map_tbls->tbl[i].offset);
|
||||
/* if the child paddr is equal to the parent paddr */
|
||||
if (*sub_paddr_ptr == map_tbls->tbl[i].parent_pa) {
|
||||
*sub_paddr_ptr = __psp_pa(g2h->tbl[0].hva);
|
||||
map_tbls->tbl[i].hva = (uint64_t)g2h->tbl[0].hva;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* check if parent_pa is valid */
|
||||
if (unlikely(!get_hva_from_gpa(g2h, map_tbls->tbl[i].parent_pa))) {
|
||||
pr_err("[%s]: g2h->tbl[%d].parent_pa: 0x%llx is invalid\n",
|
||||
__func__, i, map_tbls->tbl[i].parent_pa);
|
||||
ret = -EFAULT;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* replace the gpa from guest with the new pa of kva */
|
||||
if (unlikely(guest_multiple_level_gpa_replace(vpsp,
|
||||
&map_tbls->tbl[i], g2h))) {
|
||||
pr_err("[%s]: guest_multiple_level_gpa_replace failed\n",
|
||||
__func__);
|
||||
ret = -EFAULT;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void kvm_pv_psp_mem_free(struct gpa2hva_tbls *g2h, struct addr_map_tbls
|
||||
*map_tbl, void *data)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (g2h) {
|
||||
for (i = 0; i < g2h->tbl_nums; i++) {
|
||||
if (g2h->tbl[i].hva && (g2h->tbl[i].hva != data)) {
|
||||
kfree(g2h->tbl[i].hva);
|
||||
g2h->tbl[i].hva = NULL;
|
||||
}
|
||||
}
|
||||
kfree(g2h);
|
||||
}
|
||||
|
||||
kfree(map_tbl);
|
||||
kfree(data);
|
||||
}
|
||||
|
||||
/*
|
||||
* Obtain the VM command and preprocess the pointer mapping table
|
||||
* information in the command buffer, the processed data will be
|
||||
* used to interact with the psp device
|
||||
*/
|
||||
static int kvm_pv_psp_cmd_pre_op(struct kvm_vpsp *vpsp, gpa_t data_gpa,
|
||||
gpa_t table_gpa, struct vpsp_hbuf_wrapper *hbuf)
|
||||
struct vpsp_hbuf_wrapper *hbuf)
|
||||
{
|
||||
int ret = 0;
|
||||
void *data = NULL;
|
||||
struct psp_cmdresp_head psp_head;
|
||||
uint32_t data_size;
|
||||
struct addr_map_tbls map_head, *map_tbls = NULL;
|
||||
uint32_t map_tbl_size;
|
||||
struct gpa2hva_tbls *g2h = NULL;
|
||||
uint32_t g2h_tbl_size;
|
||||
|
||||
if (unlikely(vpsp->read_guest(vpsp->kvm, data_gpa, &psp_head,
|
||||
sizeof(struct psp_cmdresp_head))))
|
||||
return -EFAULT;
|
||||
|
||||
data_size = psp_head.buf_size;
|
||||
if ((((uintptr_t)data_gpa + data_size - 1) & ~PSP_2MB_MASK)
|
||||
!= ((uintptr_t)data_gpa & ~PSP_2MB_MASK)) {
|
||||
pr_err("data_gpa %llx, data_size %d crossing 2MB\n", (u64)data_gpa, data_size);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
data = kzalloc(data_size, GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
@ -415,87 +89,18 @@ static int kvm_pv_psp_cmd_pre_op(struct kvm_vpsp *vpsp, gpa_t data_gpa,
|
|||
goto end;
|
||||
}
|
||||
|
||||
if (table_gpa) {
|
||||
/* parse address map table from guest */
|
||||
if (unlikely(vpsp->read_guest(vpsp->kvm, table_gpa, &map_head,
|
||||
sizeof(struct addr_map_tbls)))) {
|
||||
pr_err("[%s]: kvm_read_guest for map_head failed\n",
|
||||
__func__);
|
||||
ret = -EFAULT;
|
||||
goto end;
|
||||
}
|
||||
|
||||
map_tbl_size = sizeof(struct addr_map_tbls) + map_head.tbl_nums
|
||||
* sizeof(struct map_tbl);
|
||||
map_tbls = kzalloc(map_tbl_size, GFP_KERNEL);
|
||||
if (!map_tbls) {
|
||||
ret = -ENOMEM;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (unlikely(vpsp->read_guest(vpsp->kvm, table_gpa, map_tbls,
|
||||
map_tbl_size))) {
|
||||
pr_err("[%s]: kvm_read_guest for map_tbls failed\n",
|
||||
__func__);
|
||||
ret = -EFAULT;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* init for gpa2hva table*/
|
||||
g2h_tbl_size = sizeof(struct gpa2hva_tbls) + (map_head.tbl_nums
|
||||
+ 1) * sizeof(struct gpa2hva_t);
|
||||
g2h = kzalloc(g2h_tbl_size, GFP_KERNEL);
|
||||
if (!g2h) {
|
||||
ret = -ENOMEM;
|
||||
goto end;
|
||||
}
|
||||
g2h->max_nums = map_head.tbl_nums + 1;
|
||||
|
||||
/* fill the root parent address */
|
||||
if (gpa2hva_tbl_fill(g2h, data, data_gpa)) {
|
||||
pr_err("[%s]: gpa2hva_tbl_fill for root data address failed\n",
|
||||
__func__);
|
||||
ret = -EFAULT;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (guest_addr_map_table_op(vpsp, g2h, map_tbls, 0)) {
|
||||
pr_err("[%s]: guest_addr_map_table_op for replacing failed\n",
|
||||
__func__);
|
||||
ret = -EFAULT;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
hbuf->data = data;
|
||||
hbuf->data_size = data_size;
|
||||
hbuf->map_tbls = map_tbls;
|
||||
hbuf->g2h_tbls = g2h;
|
||||
|
||||
end:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* The executed command data is recovered according to the multilevel
|
||||
* pointer of the mapping table when the command has finished
|
||||
* interacting with the psp device
|
||||
*/
|
||||
static int kvm_pv_psp_cmd_post_op(struct kvm_vpsp *vpsp, gpa_t data_gpa,
|
||||
struct vpsp_hbuf_wrapper *hbuf)
|
||||
struct vpsp_hbuf_wrapper *hbuf)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (hbuf->map_tbls) {
|
||||
if (guest_addr_map_table_op(vpsp, hbuf->g2h_tbls,
|
||||
hbuf->map_tbls, 1)) {
|
||||
pr_err("[%s]: guest_addr_map_table_op for restoring failed\n",
|
||||
__func__);
|
||||
ret = -EFAULT;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
/* restore cmdresp's buffer from context */
|
||||
if (unlikely(vpsp->write_guest(vpsp->kvm, data_gpa, hbuf->data,
|
||||
hbuf->data_size))) {
|
||||
|
@ -504,12 +109,9 @@ static int kvm_pv_psp_cmd_post_op(struct kvm_vpsp *vpsp, gpa_t data_gpa,
|
|||
ret = -EFAULT;
|
||||
goto end;
|
||||
}
|
||||
|
||||
end:
|
||||
/* release memory and clear hbuf */
|
||||
kvm_pv_psp_mem_free(hbuf->g2h_tbls, hbuf->map_tbls, hbuf->data);
|
||||
kfree(hbuf->data);
|
||||
memset(hbuf, 0, sizeof(*hbuf));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -520,11 +122,16 @@ static int cmd_type_is_tkm(int cmd)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The primary implementation interface of virtual PSP in kernel mode
|
||||
/**
|
||||
* @brief kvm_pv_psp_copy_forward_op is used for ordinary virtual machines to copy data
|
||||
* in gpa to host memory and send it to psp for processing.
|
||||
*
|
||||
* @param vpsp points to kvm related data
|
||||
* @param cmd psp cmd id, bit 31 indicates queue priority
|
||||
* @param data_gpa guest physical address of input data
|
||||
* @param psp_ret_gpa guest physical address of psp_ret
|
||||
*/
|
||||
int kvm_pv_psp_op(struct kvm_vpsp *vpsp, int cmd, gpa_t data_gpa, gpa_t psp_ret_gpa,
|
||||
gpa_t table_gpa)
|
||||
int kvm_pv_psp_copy_forward_op(struct kvm_vpsp *vpsp, int cmd, gpa_t data_gpa, gpa_t psp_ret_gpa)
|
||||
{
|
||||
int ret = 0;
|
||||
struct vpsp_ret psp_ret = {0};
|
||||
|
@ -534,13 +141,18 @@ int kvm_pv_psp_op(struct kvm_vpsp *vpsp, int cmd, gpa_t data_gpa, gpa_t psp_ret_
|
|||
uint32_t index = 0;
|
||||
uint32_t vid = 0;
|
||||
|
||||
if (vcmd->cmd_id != TKM_PSP_CMDID_OFFSET) {
|
||||
pr_err("[%s]: unsupported cmd id %x\n", __func__, vcmd->cmd_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
// only tkm cmd need vid
|
||||
if (cmd_type_is_tkm(vcmd->cmd_id)) {
|
||||
// check the permission to use the default vid when no vid is set
|
||||
ret = vpsp_get_vid(&vid, vpsp->kvm->userspace_pid);
|
||||
if (ret && !vpsp_get_default_vid_permission()) {
|
||||
pr_err("[%s]: not allowed tkm command without vid\n", __func__);
|
||||
return -EFAULT;
|
||||
return -EPERM;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -550,8 +162,8 @@ int kvm_pv_psp_op(struct kvm_vpsp *vpsp, int cmd, gpa_t data_gpa, gpa_t psp_ret_
|
|||
|
||||
switch (psp_ret.status) {
|
||||
case VPSP_INIT:
|
||||
/* multilevel pointer replace*/
|
||||
ret = kvm_pv_psp_cmd_pre_op(vpsp, data_gpa, table_gpa, &hbuf);
|
||||
/* copy data from guest */
|
||||
ret = kvm_pv_psp_cmd_pre_op(vpsp, data_gpa, &hbuf);
|
||||
if (unlikely(ret)) {
|
||||
psp_ret.status = VPSP_FINISH;
|
||||
pr_err("[%s]: kvm_pv_psp_cmd_pre_op failed\n",
|
||||
|
@ -564,21 +176,18 @@ int kvm_pv_psp_op(struct kvm_vpsp *vpsp, int cmd, gpa_t data_gpa, gpa_t psp_ret_
|
|||
ret = vpsp_try_do_cmd(vid, cmd, (void *)hbuf.data,
|
||||
(struct vpsp_ret *)&psp_ret);
|
||||
if (unlikely(ret)) {
|
||||
pr_err("[%s]: vpsp_do_cmd failed\n", __func__);
|
||||
pr_err("[%s]: vpsp_try_do_cmd failed\n", __func__);
|
||||
ret = -EFAULT;
|
||||
goto end;
|
||||
}
|
||||
|
||||
switch (psp_ret.status) {
|
||||
case VPSP_RUNNING:
|
||||
/* backup host memory message for restoring later*/
|
||||
if (psp_ret.status == VPSP_RUNNING) {
|
||||
prio = vcmd->is_high_rb ? CSV_COMMAND_PRIORITY_HIGH :
|
||||
CSV_COMMAND_PRIORITY_LOW;
|
||||
g_hbuf_wrap[prio][psp_ret.index] = hbuf;
|
||||
break;
|
||||
|
||||
case VPSP_FINISH:
|
||||
/* restore multilevel pointer data */
|
||||
} else if (psp_ret.status == VPSP_FINISH) {
|
||||
ret = kvm_pv_psp_cmd_post_op(vpsp, data_gpa, &hbuf);
|
||||
if (unlikely(ret)) {
|
||||
pr_err("[%s]: kvm_pv_psp_cmd_post_op failed\n",
|
||||
|
@ -586,11 +195,6 @@ int kvm_pv_psp_op(struct kvm_vpsp *vpsp, int cmd, gpa_t data_gpa, gpa_t psp_ret_
|
|||
ret = -EFAULT;
|
||||
goto end;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -607,26 +211,21 @@ int kvm_pv_psp_op(struct kvm_vpsp *vpsp, int cmd, gpa_t data_gpa, gpa_t psp_ret_
|
|||
goto end;
|
||||
}
|
||||
|
||||
switch (psp_ret.status) {
|
||||
case VPSP_RUNNING:
|
||||
break;
|
||||
|
||||
case VPSP_FINISH:
|
||||
/* restore multilevel pointer data */
|
||||
if (psp_ret.status == VPSP_RUNNING) {
|
||||
ret = 0;
|
||||
goto end;
|
||||
} else if (psp_ret.status == VPSP_FINISH) {
|
||||
/* copy data to guest */
|
||||
ret = kvm_pv_psp_cmd_post_op(vpsp, data_gpa,
|
||||
&g_hbuf_wrap[prio][index]);
|
||||
if (unlikely(ret)) {
|
||||
pr_err("[%s]: kvm_pv_psp_cmd_post_op failed\n",
|
||||
__func__);
|
||||
ret = -EFAULT;
|
||||
goto end;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
goto end;
|
||||
}
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -638,4 +237,5 @@ end:
|
|||
/* return psp_ret to guest */
|
||||
vpsp->write_guest(vpsp->kvm, psp_ret_gpa, &psp_ret, sizeof(psp_ret));
|
||||
return ret;
|
||||
} EXPORT_SYMBOL_GPL(kvm_pv_psp_op);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kvm_pv_psp_copy_forward_op);
|
||||
|
|
|
@ -449,6 +449,11 @@ struct kvm_vpsp {
|
|||
int (*read_guest)(struct kvm *kvm, gpa_t gpa, void *data, unsigned long len);
|
||||
};
|
||||
|
||||
#define PSP_2MB_MASK (2*1024*1024 - 1)
|
||||
#define TKM_CMD_ID_MIN 0x120
|
||||
#define TKM_CMD_ID_MAX 0x12f
|
||||
#define TKM_PSP_CMDID TKM_CMD_ID_MIN
|
||||
#define TKM_PSP_CMDID_OFFSET 0x128
|
||||
#define PSP_VID_MASK 0xff
|
||||
#define PSP_VID_SHIFT 56
|
||||
#define PUT_PSP_VID(hpa, vid) ((__u64)(hpa) | ((__u64)(PSP_VID_MASK & vid) << PSP_VID_SHIFT))
|
||||
|
@ -457,8 +462,6 @@ struct kvm_vpsp {
|
|||
|
||||
#ifdef CONFIG_CRYPTO_DEV_SP_PSP
|
||||
|
||||
int vpsp_do_cmd(uint32_t vid, int cmd, void *data, int *psp_ret);
|
||||
|
||||
int psp_do_cmd(int cmd, void *data, int *psp_ret);
|
||||
|
||||
int csv_ring_buffer_queue_init(void);
|
||||
|
@ -481,11 +484,9 @@ int vpsp_get_vid(uint32_t *vid, pid_t pid);
|
|||
|
||||
int vpsp_get_default_vid_permission(void);
|
||||
|
||||
int kvm_pv_psp_op(struct kvm_vpsp *vpsp, int cmd, gpa_t data_gpa, gpa_t psp_ret_gpa,
|
||||
gpa_t table_gpa);
|
||||
#else /* !CONFIG_CRYPTO_DEV_SP_PSP */
|
||||
int kvm_pv_psp_copy_forward_op(struct kvm_vpsp *vpsp, int cmd, gpa_t data_gpa, gpa_t psp_ret_gpa);
|
||||
|
||||
static inline int vpsp_do_cmd(uint32_t vid, int cmd, void *data, int *psp_ret) { return -ENODEV; }
|
||||
#else /* !CONFIG_CRYPTO_DEV_SP_PSP */
|
||||
|
||||
static inline int psp_do_cmd(int cmd, void *data, int *psp_ret) { return -ENODEV; }
|
||||
|
||||
|
@ -512,8 +513,8 @@ static inline int
|
|||
vpsp_get_default_vid_permission(void) { return -ENODEV; }
|
||||
|
||||
static inline int
|
||||
kvm_pv_psp_op(struct kvm_vpsp *vpsp, int cmd, gpa_t data_gpa,
|
||||
gpa_t psp_ret_gpa, gpa_t table_gpa) { return -ENODEV; }
|
||||
kvm_pv_psp_copy_forward_op(struct kvm_vpsp *vpsp, int cmd, gpa_t data_gpa,
|
||||
gpa_t psp_ret_gpa) { return -ENODEV; }
|
||||
#endif /* CONFIG_CRYPTO_DEV_SP_PSP */
|
||||
|
||||
typedef int (*p2c_notifier_t)(uint32_t id, uint64_t data);
|
||||
|
|
|
@ -31,7 +31,8 @@
|
|||
#define KVM_HC_SCHED_YIELD 11
|
||||
#define KVM_HC_MAP_GPA_RANGE 12
|
||||
#define KVM_HC_VM_ATTESTATION 100 /* Specific to Hygon CPU */
|
||||
#define KVM_HC_PSP_OP 101 /* Specific to Hygon platform */
|
||||
#define KVM_HC_PSP_OP_OBSOLETE 101 /* Specific to Hygon platform */
|
||||
#define KVM_HC_PSP_COPY_FORWARD_OP 102 /* Specific to Hygon platform */
|
||||
|
||||
/*
|
||||
* hypercalls use architecture specific
|
||||
|
|
Loading…
Reference in New Issue