FROMLIST: gunyah: rsc_mgr: Add VM lifecycle RPC

Add Gunyah Resource Manager RPC to launch an unauthenticated VM.

Change-Id: I1ff2e58b1f3249a9387ec10adeea1b04600369e4
Signed-off-by: Elliot Berman <quic_eberman@quicinc.com>
Bug: 268234781
Link: https://lore.kernel.org/all/20230304010632.2127470-10-quic_eberman@quicinc.com/
This commit is contained in:
Elliot Berman
2022-10-14 10:49:01 -07:00
committed by Aleksei Vetrov
parent d5e8df2cc4
commit 7cfd6e6f7d
3 changed files with 334 additions and 1 deletions

View File

@@ -2,5 +2,5 @@
obj-$(CONFIG_GUNYAH) += gunyah.o
gunyah_rsc_mgr-y += rsc_mgr.o
gunyah_rsc_mgr-y += rsc_mgr.o rsc_mgr_rpc.o
obj-$(CONFIG_GUNYAH) += gunyah_rsc_mgr.o

View File

@@ -0,0 +1,260 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/gunyah_rsc_mgr.h>
#include "rsc_mgr.h"
/* Message IDs: VM Management */
#define GH_RM_RPC_VM_ALLOC_VMID 0x56000001
#define GH_RM_RPC_VM_DEALLOC_VMID 0x56000002
#define GH_RM_RPC_VM_START 0x56000004
#define GH_RM_RPC_VM_STOP 0x56000005
#define GH_RM_RPC_VM_RESET 0x56000006
#define GH_RM_RPC_VM_CONFIG_IMAGE 0x56000009
#define GH_RM_RPC_VM_INIT 0x5600000B
#define GH_RM_RPC_VM_GET_HYP_RESOURCES 0x56000020
#define GH_RM_RPC_VM_GET_VMID 0x56000024
struct gh_rm_vm_common_vmid_req {
__le16 vmid;
__le16 _padding;
} __packed;
/* Call: VM_ALLOC */
struct gh_rm_vm_alloc_vmid_resp {
__le16 vmid;
__le16 _padding;
} __packed;
/* Call: VM_STOP */
#define GH_RM_VM_STOP_FLAG_FORCE_STOP BIT(0)
#define GH_RM_VM_STOP_REASON_FORCE_STOP 3
struct gh_rm_vm_stop_req {
__le16 vmid;
u8 flags;
u8 _padding;
__le32 stop_reason;
} __packed;
/* Call: VM_CONFIG_IMAGE */
struct gh_rm_vm_config_image_req {
__le16 vmid;
__le16 auth_mech;
__le32 mem_handle;
__le64 image_offset;
__le64 image_size;
__le64 dtb_offset;
__le64 dtb_size;
} __packed;
/*
* Several RM calls take only a VMID as a parameter and give only standard
* response back. Deduplicate boilerplate code by using this common call.
*/
static int gh_rm_common_vmid_call(struct gh_rm *rm, u32 message_id, u16 vmid)
{
struct gh_rm_vm_common_vmid_req req_payload = {
.vmid = cpu_to_le16(vmid),
};
return gh_rm_call(rm, message_id, &req_payload, sizeof(req_payload), NULL, NULL);
}
/**
* gh_rm_alloc_vmid() - Allocate a new VM in Gunyah. Returns the VM identifier.
* @rm: Handle to a Gunyah resource manager
* @vmid: Use 0 to dynamically allocate a VM. A reserved VMID can be supplied
* to request allocation of a platform-defined VM.
*
* Returns - the allocated VMID or negative value on error
*/
int gh_rm_alloc_vmid(struct gh_rm *rm, u16 vmid)
{
struct gh_rm_vm_common_vmid_req req_payload = {
.vmid = vmid,
};
struct gh_rm_vm_alloc_vmid_resp *resp_payload;
size_t resp_size;
void *resp;
int ret;
ret = gh_rm_call(rm, GH_RM_RPC_VM_ALLOC_VMID, &req_payload, sizeof(req_payload), &resp,
&resp_size);
if (ret)
return ret;
if (!vmid) {
resp_payload = resp;
ret = le16_to_cpu(resp_payload->vmid);
kfree(resp);
}
return ret;
}
/**
* gh_rm_dealloc_vmid() - Dispose the VMID
* @rm: Handle to a Gunyah resource manager
* @vmid: VM identifier allocated with gh_rm_alloc_vmid
*/
int gh_rm_dealloc_vmid(struct gh_rm *rm, u16 vmid)
{
return gh_rm_common_vmid_call(rm, GH_RM_RPC_VM_DEALLOC_VMID, vmid);
}
/**
* gh_rm_vm_reset() - Reset the VM's resources
* @rm: Handle to a Gunyah resource manager
* @vmid: VM identifier allocated with gh_rm_alloc_vmid
*
* While tearing down the VM, request RM to clean up all the VM resources
* associated with the VM. Only after this, Linux can clean up all the
* references it maintains to resources.
*/
int gh_rm_vm_reset(struct gh_rm *rm, u16 vmid)
{
return gh_rm_common_vmid_call(rm, GH_RM_RPC_VM_RESET, vmid);
}
/**
* gh_rm_vm_start() - Move the VM into "ready to run" state
* @rm: Handle to a Gunyah resource manager
* @vmid: VM identifier allocated with gh_rm_alloc_vmid
*
* On VMs which use proxy scheduling, vcpu_run is needed to actually run the VM.
* On VMs which use Gunyah's scheduling, the vCPUs start executing in accordance with Gunyah
* scheduling policies.
*/
int gh_rm_vm_start(struct gh_rm *rm, u16 vmid)
{
return gh_rm_common_vmid_call(rm, GH_RM_RPC_VM_START, vmid);
}
/**
* gh_rm_vm_stop() - Send a request to Resource Manager VM to forcibly stop a VM.
* @rm: Handle to a Gunyah resource manager
* @vmid: VM identifier allocated with gh_rm_alloc_vmid
*/
int gh_rm_vm_stop(struct gh_rm *rm, u16 vmid)
{
struct gh_rm_vm_stop_req req_payload = {
.vmid = cpu_to_le16(vmid),
.flags = GH_RM_VM_STOP_FLAG_FORCE_STOP,
.stop_reason = cpu_to_le32(GH_RM_VM_STOP_REASON_FORCE_STOP),
};
return gh_rm_call(rm, GH_RM_RPC_VM_STOP, &req_payload, sizeof(req_payload), NULL, NULL);
}
/**
* gh_rm_vm_configure() - Prepare a VM to start and provide the common
* configuration needed by RM to configure a VM
* @rm: Handle to a Gunyah resource manager
* @vmid: VM identifier allocated with gh_rm_alloc_vmid
* @auth_mechanism: Authentication mechanism used by resource manager to verify
* the virtual machine
* @mem_handle: Handle to a previously shared memparcel that contains all parts
* of the VM image subject to authentication.
* @image_offset: Start address of VM image, relative to the start of memparcel
* @image_size: Size of the VM image
* @dtb_offset: Start address of the devicetree binary with VM configuration,
* relative to start of memparcel.
* @dtb_size: Maximum size of devicetree binary. Resource manager applies
* an overlay to the DTB and dtb_size should include room for
* the overlay.
*/
int gh_rm_vm_configure(struct gh_rm *rm, u16 vmid, enum gh_rm_vm_auth_mechanism auth_mechanism,
u32 mem_handle, u64 image_offset, u64 image_size, u64 dtb_offset, u64 dtb_size)
{
struct gh_rm_vm_config_image_req req_payload = {
.vmid = cpu_to_le16(vmid),
.auth_mech = cpu_to_le16(auth_mechanism),
.mem_handle = cpu_to_le32(mem_handle),
.image_offset = cpu_to_le64(image_offset),
.image_size = cpu_to_le64(image_size),
.dtb_offset = cpu_to_le64(dtb_offset),
.dtb_size = cpu_to_le64(dtb_size),
};
return gh_rm_call(rm, GH_RM_RPC_VM_CONFIG_IMAGE, &req_payload, sizeof(req_payload),
NULL, NULL);
}
/**
* gh_rm_vm_init() - Move the VM to initialized state.
* @rm: Handle to a Gunyah resource manager
* @vmid: VM identifier
*
* RM will allocate needed resources for the VM.
*/
int gh_rm_vm_init(struct gh_rm *rm, u16 vmid)
{
return gh_rm_common_vmid_call(rm, GH_RM_RPC_VM_INIT, vmid);
}
/**
* gh_rm_get_hyp_resources() - Retrieve hypervisor resources (capabilities) associated with a VM
* @rm: Handle to a Gunyah resource manager
* @vmid: VMID of the other VM to get the resources of
* @resources: Set by gh_rm_get_hyp_resources and contains the returned hypervisor resources.
*/
int gh_rm_get_hyp_resources(struct gh_rm *rm, u16 vmid,
struct gh_rm_hyp_resources **resources)
{
struct gh_rm_vm_common_vmid_req req_payload = {
.vmid = cpu_to_le16(vmid),
};
struct gh_rm_hyp_resources *resp;
size_t resp_size;
int ret;
ret = gh_rm_call(rm, GH_RM_RPC_VM_GET_HYP_RESOURCES,
&req_payload, sizeof(req_payload),
(void **)&resp, &resp_size);
if (ret)
return ret;
if (!resp_size)
return -EBADMSG;
if (resp_size < struct_size(resp, entries, 0) ||
resp_size != struct_size(resp, entries, le32_to_cpu(resp->n_entries))) {
kfree(resp);
return -EBADMSG;
}
*resources = resp;
return 0;
}
/**
* gh_rm_get_vmid() - Retrieve VMID of this virtual machine
* @rm: Handle to a Gunyah resource manager
* @vmid: Filled with the VMID of this VM
*/
int gh_rm_get_vmid(struct gh_rm *rm, u16 *vmid)
{
static u16 cached_vmid = GH_VMID_INVAL;
size_t resp_size;
__le32 *resp;
int ret;
if (cached_vmid != GH_VMID_INVAL) {
*vmid = cached_vmid;
return 0;
}
ret = gh_rm_call(rm, GH_RM_RPC_VM_GET_VMID, NULL, 0, (void **)&resp, &resp_size);
if (ret)
return ret;
*vmid = cached_vmid = lower_16_bits(le32_to_cpu(*resp));
kfree(resp);
return ret;
}
EXPORT_SYMBOL_GPL(gh_rm_get_vmid);

View File

@@ -18,4 +18,77 @@ int gh_rm_notifier_unregister(struct gh_rm *rm, struct notifier_block *nb);
struct device *gh_rm_get(struct gh_rm *rm);
void gh_rm_put(struct gh_rm *rm);
struct gh_rm_vm_exited_payload {
__le16 vmid;
__le16 exit_type;
__le32 exit_reason_size;
u8 exit_reason[];
} __packed;
#define GH_RM_NOTIFICATION_VM_EXITED 0x56100001
enum gh_rm_vm_status {
GH_RM_VM_STATUS_NO_STATE = 0,
GH_RM_VM_STATUS_INIT = 1,
GH_RM_VM_STATUS_READY = 2,
GH_RM_VM_STATUS_RUNNING = 3,
GH_RM_VM_STATUS_PAUSED = 4,
GH_RM_VM_STATUS_LOAD = 5,
GH_RM_VM_STATUS_AUTH = 6,
GH_RM_VM_STATUS_INIT_FAILED = 8,
GH_RM_VM_STATUS_EXITED = 9,
GH_RM_VM_STATUS_RESETTING = 10,
GH_RM_VM_STATUS_RESET = 11,
};
struct gh_rm_vm_status_payload {
__le16 vmid;
u16 reserved;
u8 vm_status;
u8 os_status;
__le16 app_status;
} __packed;
#define GH_RM_NOTIFICATION_VM_STATUS 0x56100008
/* RPC Calls */
int gh_rm_alloc_vmid(struct gh_rm *rm, u16 vmid);
int gh_rm_dealloc_vmid(struct gh_rm *rm, u16 vmid);
int gh_rm_vm_reset(struct gh_rm *rm, u16 vmid);
int gh_rm_vm_start(struct gh_rm *rm, u16 vmid);
int gh_rm_vm_stop(struct gh_rm *rm, u16 vmid);
enum gh_rm_vm_auth_mechanism {
GH_RM_VM_AUTH_NONE = 0,
GH_RM_VM_AUTH_QCOM_PIL_ELF = 1,
GH_RM_VM_AUTH_QCOM_ANDROID_PVM = 2,
};
int gh_rm_vm_configure(struct gh_rm *rm, u16 vmid, enum gh_rm_vm_auth_mechanism auth_mechanism,
u32 mem_handle, u64 image_offset, u64 image_size,
u64 dtb_offset, u64 dtb_size);
int gh_rm_vm_init(struct gh_rm *rm, u16 vmid);
struct gh_rm_hyp_resource {
u8 type;
u8 reserved;
__le16 partner_vmid;
__le32 resource_handle;
__le32 resource_label;
__le64 cap_id;
__le32 virq_handle;
__le32 virq;
__le64 base;
__le64 size;
} __packed;
struct gh_rm_hyp_resources {
__le32 n_entries;
struct gh_rm_hyp_resource entries[];
} __packed;
int gh_rm_get_hyp_resources(struct gh_rm *rm, u16 vmid,
struct gh_rm_hyp_resources **resources);
int gh_rm_get_vmid(struct gh_rm *rm, u16 *vmid);
#endif