ANDROID: gunyah: qtvm: Support for Qualcomm Trusted Virtual Machines
Qualcomm Trusted Virtual Machine (QTVM) are VMs that are authenticated by Qualcomm firmware before the VM starts. The firmware also defines the VM's image layout. Additionally, each QTVM comes with a reserved VM ID. Introduce an IOCTL to allow VMMs to configure a QTVM and this driver will handle the setup for QTVMs. Bug: 399219478 Change-Id: Ia3fe74a46c0e53134b9b4b10fa59bcaa8f376c87 Signed-off-by: Prakruthi Deepak Heragu <quic_pheragu@quicinc.com>
This commit is contained in:
committed by
Todd Kjos
parent
b41a7537a4
commit
ca44b02c02
@@ -50,3 +50,13 @@ config GUNYAH_IOEVENTFD
|
||||
when a Gunyah virtual machine accesses a memory address.
|
||||
|
||||
Say Y/M here if unsure and you want to support Gunyah VMMs.
|
||||
|
||||
config GUNYAH_QCOM_TRUSTED_VM
|
||||
tristate "Gunyah Qualcomm Trusted VM Support"
|
||||
depends on GUNYAH
|
||||
help
|
||||
Enable kernel support for creating Qualcomm Trusted VMs.
|
||||
They are a kind of protected VM which is authenticated by the
|
||||
firmware.
|
||||
|
||||
Say Y/M here if unsure and you want to support QTVMs.
|
||||
|
||||
@@ -5,6 +5,7 @@ obj-$(CONFIG_GUNYAH_HYPERCALLS) += gunyah.o
|
||||
gunyah_rsc_mgr-y += rsc_mgr.o rsc_mgr_rpc.o vm_mgr.o vm_mgr_mem.o
|
||||
gunyah_rsc_mgr-$(CONFIG_DMA_CMA) += vm_mgr_cma_mem.o
|
||||
obj-$(CONFIG_GUNYAH) += gunyah_rsc_mgr.o gunyah_vcpu.o
|
||||
obj-$(CONFIG_GUNYAH_QCOM_TRUSTED_VM) += gunyah_qtvm.o
|
||||
obj-$(CONFIG_GUNYAH_PLATFORM_HOOKS) += gunyah_platform_hooks.o
|
||||
obj-$(CONFIG_GUNYAH_QCOM_PLATFORM) += gunyah_qcom.o
|
||||
obj-$(CONFIG_GUNYAH_IRQFD) += gunyah_irqfd.o
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/of_platform.h>
|
||||
#include "vm_mgr.h"
|
||||
|
||||
#define PAS_VM_METADATA_SZ 8192
|
||||
|
||||
static DEFINE_MUTEX(gunyah_qtvm_lock);
|
||||
static LIST_HEAD(gunyah_qtvm_list);
|
||||
SRCU_NOTIFIER_HEAD_STATIC(gunyah_vm_notifier);
|
||||
|
||||
struct gunyah_qtvm {
|
||||
struct gunyah_vm *ghvm;
|
||||
struct gunyah_vm_parcel *parcel_list;
|
||||
struct list_head list;
|
||||
u64 vm_image_addr;
|
||||
u64 vm_image_size;
|
||||
u32 pas_id;
|
||||
u16 vmid;
|
||||
};
|
||||
|
||||
static u16 gunyah_qtvm_pre_alloc_vmid(struct gunyah_vm *ghvm)
|
||||
{
|
||||
struct gunyah_qtvm *vm = ghvm->auth_vm_mgr_data;
|
||||
|
||||
return vm->vmid;
|
||||
}
|
||||
|
||||
static int gunyah_qtvm_pre_vm_configure(struct gunyah_vm *ghvm)
|
||||
{
|
||||
struct gunyah_qtvm *vm = ghvm->auth_vm_mgr_data;
|
||||
u64 start_gfn;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* For QTVMs, the metadata is always placed at the beginning of the
|
||||
* main VM memory and will always be of fixed size decided at the
|
||||
* build time while signing the VM image. The metadata contains the
|
||||
* signing information needed by firmware to authenticate the VM image.
|
||||
* VM image once loaded into the memory looks like this:
|
||||
*
|
||||
* start |----------------------|
|
||||
* | MDT header + hashes |
|
||||
* |----------------------|
|
||||
* | Kernel |
|
||||
* |----------------------|
|
||||
* | DTB |
|
||||
* |----------------------|
|
||||
* | CPIO/Ramdisk |
|
||||
* |----------------------|
|
||||
*/
|
||||
|
||||
ghvm->config_image.parcel.start = gunyah_gpa_to_gfn(vm->vm_image_addr);
|
||||
ghvm->config_image.parcel.pages = gunyah_gpa_to_gfn(vm->vm_image_size);
|
||||
|
||||
ghvm->config_image.image_offset = 0;
|
||||
ghvm->config_image.image_size = PAS_VM_METADATA_SZ;
|
||||
|
||||
ghvm->config_image.dtb_offset = ghvm->dtb.config.guest_phys_addr -
|
||||
gunyah_gfn_to_gpa(ghvm->config_image.parcel.start);
|
||||
ghvm->config_image.dtb_size = ghvm->dtb.config.size;
|
||||
|
||||
if ((ghvm->dtb.config.guest_phys_addr + ghvm->config_image.dtb_size) >
|
||||
(gunyah_gfn_to_gpa(ghvm->config_image.parcel.start) +
|
||||
gunyah_gfn_to_gpa(ghvm->config_image.parcel.pages))) {
|
||||
/*
|
||||
* DTB is out of the config image bounds.
|
||||
* This is should not happen!
|
||||
*/
|
||||
dev_err(ghvm->parent, "DTB is outside the image parcel\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* RM would expect to have all the memory mentioned
|
||||
* in the VM DT to be shared/lent before the VM starts.
|
||||
* We will lend the primary memory parcel as
|
||||
* part of the vm_configure operation. So, share the rest
|
||||
* of the VM memory here.
|
||||
*/
|
||||
start_gfn = gunyah_gpa_to_gfn(vm->vm_image_addr + vm->vm_image_size);
|
||||
ret = gunyah_share_range_as_parcels(ghvm, start_gfn, ULONG_MAX, &vm->parcel_list);
|
||||
if (ret) {
|
||||
dev_err(ghvm->parent, "Failed to share non primary parcel(s) before VM start\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gunyah_qtvm_authenticate(struct gunyah_vm *ghvm)
|
||||
{
|
||||
struct gunyah_qtvm *vm = ghvm->auth_vm_mgr_data;
|
||||
struct gunyah_rm_vm_authenticate_param_entry entry;
|
||||
int ret;
|
||||
|
||||
entry.param_type = GUNYAH_VM_AUTH_PARAM_PAS_ID;
|
||||
entry.param = vm->pas_id;
|
||||
|
||||
ret = gunyah_rm_vm_authenticate(ghvm->rm, vm->vmid, 1, &entry);
|
||||
if (ret) {
|
||||
dev_err(ghvm->parent, "Failed to Authenticate VM: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct gunyah_auth_vm_mgr_ops vm_ops = {
|
||||
.pre_alloc_vmid = gunyah_qtvm_pre_alloc_vmid,
|
||||
.pre_vm_configure = gunyah_qtvm_pre_vm_configure,
|
||||
.vm_authenticate = gunyah_qtvm_authenticate,
|
||||
};
|
||||
|
||||
static long gunyah_qtvm_attach(struct gunyah_vm *ghvm, struct gunyah_auth_desc *desc)
|
||||
{
|
||||
struct gunyah_qtvm_auth_arg arg;
|
||||
struct gunyah_qtvm *vm;
|
||||
void __user *argp;
|
||||
|
||||
if (desc->arg_size > sizeof(struct gunyah_qtvm_auth_arg))
|
||||
return -EINVAL;
|
||||
|
||||
argp = u64_to_user_ptr(desc->arg);
|
||||
if (copy_from_user(&arg, argp, desc->arg_size))
|
||||
return -EFAULT;
|
||||
|
||||
if (overflows_type(arg.guest_phys_addr + arg.size,
|
||||
u64))
|
||||
return -EOVERFLOW;
|
||||
|
||||
mutex_lock(&gunyah_qtvm_lock);
|
||||
vm = kzalloc(sizeof(*vm), GFP_KERNEL_ACCOUNT);
|
||||
if (!vm) {
|
||||
mutex_unlock(&gunyah_qtvm_lock);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
vm->vmid = arg.vm_id;
|
||||
vm->pas_id = arg.peripheral_id;
|
||||
|
||||
/* This would be the primary Image parcel */
|
||||
vm->vm_image_addr = arg.guest_phys_addr;
|
||||
vm->vm_image_size = arg.size;
|
||||
vm->ghvm = ghvm;
|
||||
|
||||
ghvm->auth = GUNYAH_RM_VM_AUTH_QCOM_TRUSTED_VM;
|
||||
ghvm->auth_vm_mgr_ops = &vm_ops;
|
||||
ghvm->auth_vm_mgr_data = vm;
|
||||
|
||||
list_add(&vm->list, &gunyah_qtvm_list);
|
||||
mutex_unlock(&gunyah_qtvm_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void gunyah_qtvm_detach(struct gunyah_vm *ghvm)
|
||||
{
|
||||
struct gunyah_qtvm *vm = ghvm->auth_vm_mgr_data;
|
||||
|
||||
kfree(vm->parcel_list);
|
||||
list_del(&vm->list);
|
||||
kfree(vm);
|
||||
ghvm->auth_vm_mgr_ops = NULL;
|
||||
ghvm->auth_vm_mgr_data = NULL;
|
||||
}
|
||||
|
||||
static struct gunyah_auth_vm_mgr auth_vm = {
|
||||
.type = GUNYAH_QCOM_TRUSTED_VM_TYPE,
|
||||
.name = "gunyah_qtvm",
|
||||
.mod = THIS_MODULE,
|
||||
.vm_attach = gunyah_qtvm_attach,
|
||||
.vm_detach = gunyah_qtvm_detach,
|
||||
};
|
||||
|
||||
static int __init gunyah_qtvm_init(void)
|
||||
{
|
||||
mutex_init(&gunyah_qtvm_lock);
|
||||
return gunyah_auth_vm_mgr_register(&auth_vm);
|
||||
}
|
||||
|
||||
static void __exit gunyah_qtvm_exit(void)
|
||||
{
|
||||
gunyah_auth_vm_mgr_unregister(&auth_vm);
|
||||
}
|
||||
|
||||
module_init(gunyah_qtvm_init);
|
||||
module_exit(gunyah_qtvm_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Gunyah Qualcomm Trusted VM Driver");
|
||||
@@ -74,7 +74,7 @@ int gunyah_rm_vm_stop(struct gunyah_rm *rm, u16 vmid);
|
||||
enum gunyah_rm_vm_auth_mechanism {
|
||||
/* clang-format off */
|
||||
GUNYAH_RM_VM_AUTH_NONE = 0,
|
||||
GUNYAH_RM_VM_AUTH_QCOM_PIL_ELF = 1,
|
||||
GUNYAH_RM_VM_AUTH_QCOM_TRUSTED_VM = 1,
|
||||
GUNYAH_RM_VM_AUTH_QCOM_ANDROID_PVM = 2,
|
||||
/* clang-format on */
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user