ANDROID: virt: gunyah: Add pvm firmware boot support
Android protected virtual machines use "pvmfw" in their boot flow. Add support to nominate a region of memory for the pvmfw. When nominated, the region is shared to the VM as a memory parcel and is unmarked as a demand-paged region. Bug: 338347082 Change-Id: I57d203761fbe58eb935a158db7f62f762c3c82dd Signed-off-by: Sreenad Menon <quic_sreemeno@quicinc.com> Signed-off-by: Elliot Berman <elliot.berman@oss.qualcomm.com>
This commit is contained in:
committed by
Treehugger Robot
parent
b9b7abe6f8
commit
91fe754984
@@ -118,6 +118,9 @@ enum gunyah_rm_range_id {
|
||||
GUNYAH_RM_RANGE_ID_FIRMWARE = 1,
|
||||
};
|
||||
|
||||
int gunyah_rm_vm_set_firmware_mem(struct gunyah_rm *rm, u16 vmid, struct gunyah_rm_mem_parcel *parcel,
|
||||
u64 fw_offset, u64 fw_size);
|
||||
|
||||
int gunyah_rm_vm_set_address_layout(struct gunyah_rm *rm, u16 vmid,
|
||||
enum gunyah_rm_range_id range_id,
|
||||
u64 base_address, u64 size);
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#define GUNYAH_RM_RPC_VM_GET_HYP_RESOURCES 0x56000020
|
||||
#define GUNYAH_RM_RPC_VM_GET_VMID 0x56000024
|
||||
#define GUNYAH_RM_RPC_VM_SET_BOOT_CONTEXT 0x56000031
|
||||
#define GUNYAH_RM_RPC_VM_SET_FIRMWARE_MEM 0x56000032
|
||||
#define GUNYAH_RM_RPC_VM_SET_DEMAND_PAGING 0x56000033
|
||||
#define GUNYAH_RM_RPC_VM_SET_ADDRESS_LAYOUT 0x56000034
|
||||
/* clang-format on */
|
||||
@@ -132,6 +133,15 @@ struct gunyah_rm_vm_set_address_layout_req {
|
||||
__le64 range_size;
|
||||
} __packed;
|
||||
|
||||
/* Call: VM_SET_FIRMWARE_MEM */
|
||||
struct gunyah_vm_set_firmware_mem_req {
|
||||
__le16 vmid;
|
||||
__le16 reserved;
|
||||
__le32 mem_handle;
|
||||
__le64 fw_offset;
|
||||
__le64 fw_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.
|
||||
@@ -600,3 +610,25 @@ int gunyah_rm_vm_set_address_layout(struct gunyah_rm *rm, u16 vmid,
|
||||
sizeof(req), NULL, NULL);
|
||||
}
|
||||
ALLOW_ERROR_INJECTION(gunyah_rm_vm_set_address_layout, ERRNO);
|
||||
|
||||
/**
|
||||
* gunyah_rm_vm_set_firmware_mem() - Set the location of firmware for GH_RM_VM_AUTH_QCOM_ANDROID_PVM VMs
|
||||
* @rm: Handle to a Gunyah resource manager.
|
||||
* @vmid: VM identifier allocated with gh_rm_alloc_vmid.
|
||||
* @parcel: Memory parcel where the firmware should be loaded.
|
||||
* @fw_offset: offset into the memory parcel where the firmware should be loaded.
|
||||
* @fw_size: Maxmimum size of the fw that can be loaded.
|
||||
*/
|
||||
int gunyah_rm_vm_set_firmware_mem(struct gunyah_rm *rm, u16 vmid, struct gunyah_rm_mem_parcel *parcel,
|
||||
u64 fw_offset, u64 fw_size)
|
||||
{
|
||||
struct gunyah_vm_set_firmware_mem_req req = {
|
||||
.vmid = cpu_to_le16(vmid),
|
||||
.mem_handle = cpu_to_le32(parcel->mem_handle),
|
||||
.fw_offset = cpu_to_le64(fw_offset),
|
||||
.fw_size = cpu_to_le64(fw_size),
|
||||
};
|
||||
|
||||
return gunyah_rm_call(rm, GUNYAH_RM_RPC_VM_SET_FIRMWARE_MEM, &req, sizeof(req), NULL, NULL);
|
||||
}
|
||||
ALLOW_ERROR_INJECTION(gunyah_rm_vm_set_firmware_mem, ERRNO);
|
||||
|
||||
@@ -615,7 +615,8 @@ int gunyah_gup_setup_demand_paging(struct gunyah_vm *ghvm)
|
||||
|
||||
down_read(&ghvm->bindings_lock);
|
||||
mt_for_each(&ghvm->bindings, b, index, ULONG_MAX)
|
||||
if (b->share_type == VM_MEM_LEND)
|
||||
if (b->share_type == VM_MEM_LEND &&
|
||||
(b->guest_phys_addr != ghvm->fw.config.guest_phys_addr))
|
||||
count++;
|
||||
|
||||
if (!count)
|
||||
@@ -629,7 +630,8 @@ int gunyah_gup_setup_demand_paging(struct gunyah_vm *ghvm)
|
||||
|
||||
index = i = 0;
|
||||
mt_for_each(&ghvm->bindings, b, index, ULONG_MAX) {
|
||||
if (b->share_type != VM_MEM_LEND)
|
||||
if (b->share_type != VM_MEM_LEND ||
|
||||
(b->guest_phys_addr == ghvm->fw.config.guest_phys_addr))
|
||||
continue;
|
||||
entries[i].phys_addr = cpu_to_le64(b->guest_phys_addr);
|
||||
entries[i].size = cpu_to_le64(b->size);
|
||||
@@ -680,6 +682,18 @@ static int gunyah_vm_start(struct gunyah_vm *ghvm)
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (ghvm->auth == GUNYAH_RM_VM_AUTH_QCOM_ANDROID_PVM) {
|
||||
ghvm->fw.parcel_start = ghvm->fw.config.guest_phys_addr >> PAGE_SHIFT;
|
||||
ghvm->fw.parcel_pages = ghvm->fw.config.size >> PAGE_SHIFT;
|
||||
ret = gunyah_gup_share_parcel(ghvm, &ghvm->fw.parcel,
|
||||
&ghvm->fw.parcel_start,
|
||||
&ghvm->fw.parcel_pages);
|
||||
if (ret) {
|
||||
dev_warn(ghvm->parent,
|
||||
"Failed to allocate parcel for FW: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
ret = gunyah_rm_vm_configure(ghvm->rm, ghvm->vmid, ghvm->auth,
|
||||
ghvm->dtb.parcel.mem_handle, 0, 0,
|
||||
ghvm->dtb.config.guest_phys_addr -
|
||||
@@ -691,6 +705,16 @@ static int gunyah_vm_start(struct gunyah_vm *ghvm)
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (ghvm->auth == GUNYAH_RM_VM_AUTH_QCOM_ANDROID_PVM) {
|
||||
ret = gunyah_rm_vm_set_firmware_mem(ghvm->rm, ghvm->vmid, &ghvm->fw.parcel,
|
||||
ghvm->fw.config.guest_phys_addr - (ghvm->fw.parcel_start << PAGE_SHIFT),
|
||||
ghvm->fw.config.size);
|
||||
if (ret) {
|
||||
pr_warn("Failed to configure pVM firmware\n");
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
ret = gunyah_gup_setup_demand_paging(ghvm);
|
||||
if (ret) {
|
||||
dev_warn(ghvm->parent,
|
||||
@@ -716,11 +740,13 @@ static int gunyah_vm_start(struct gunyah_vm *ghvm)
|
||||
}
|
||||
ghvm->vm_status = GUNYAH_RM_VM_STATUS_READY;
|
||||
|
||||
ret = gunyah_vm_fill_boot_context(ghvm);
|
||||
if (ret) {
|
||||
dev_warn(ghvm->parent, "Failed to setup boot context: %d\n",
|
||||
ret);
|
||||
goto err;
|
||||
if (ghvm->auth != GUNYAH_RM_VM_AUTH_QCOM_ANDROID_PVM) {
|
||||
ret = gunyah_vm_fill_boot_context(ghvm);
|
||||
if (ret) {
|
||||
dev_warn(ghvm->parent, "Failed to setup boot context: %d\n",
|
||||
ret);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
ret = gunyah_rm_get_hyp_resources(ghvm->rm, ghvm->vmid, &resources);
|
||||
@@ -820,6 +846,21 @@ static long gunyah_vm_ioctl(struct file *filp, unsigned int cmd,
|
||||
r = 0;
|
||||
break;
|
||||
}
|
||||
case GH_VM_ANDROID_SET_FW_CONFIG: {
|
||||
struct gunyah_vm_firmware_config fw_config;
|
||||
|
||||
if (copy_from_user(&fw_config, argp, sizeof(fw_config)))
|
||||
return -EFAULT;
|
||||
|
||||
if (overflows_type(fw_config.guest_phys_addr + fw_config.size,
|
||||
u64))
|
||||
return -EOVERFLOW;
|
||||
|
||||
ghvm->fw.config = fw_config;
|
||||
ghvm->auth = GUNYAH_RM_VM_AUTH_QCOM_ANDROID_PVM;
|
||||
r = 0;
|
||||
break;
|
||||
}
|
||||
case GUNYAH_VM_START: {
|
||||
r = gunyah_vm_ensure_started(ghvm);
|
||||
break;
|
||||
@@ -994,6 +1035,15 @@ static void _gunyah_vm_put(struct kref *kref)
|
||||
WARN_ON(!mtree_empty(&ghvm->mm));
|
||||
mtree_destroy(&ghvm->mm);
|
||||
|
||||
if (ghvm->auth == GUNYAH_RM_VM_AUTH_QCOM_ANDROID_PVM) {
|
||||
ret = gunyah_gup_reclaim_parcel(ghvm, &ghvm->fw.parcel,
|
||||
ghvm->fw.parcel_start,
|
||||
ghvm->fw.parcel_pages);
|
||||
if (ret)
|
||||
dev_err(ghvm->parent,
|
||||
"Failed to reclaim firmware parcel: %d\n", ret);
|
||||
}
|
||||
|
||||
if (ghvm->vm_status > GUNYAH_RM_VM_STATUS_NO_STATE) {
|
||||
gunyah_rm_notifier_unregister(ghvm->rm, &ghvm->nb);
|
||||
|
||||
|
||||
@@ -137,6 +137,11 @@ struct gunyah_vm {
|
||||
u64 parcel_start, parcel_pages;
|
||||
struct gunyah_rm_mem_parcel parcel;
|
||||
} dtb;
|
||||
struct {
|
||||
struct gunyah_vm_firmware_config config;
|
||||
u64 parcel_start, parcel_pages;
|
||||
struct gunyah_rm_mem_parcel parcel;
|
||||
} fw;
|
||||
struct xarray boot_context;
|
||||
};
|
||||
|
||||
|
||||
@@ -380,4 +380,11 @@ struct gunyah_userspace_memory_region {
|
||||
#define GH_VM_ANDROID_LEND_USER_MEM _IOW(GH_ANDROID_IOCTL_TYPE, 0x11, \
|
||||
struct gunyah_userspace_memory_region)
|
||||
|
||||
struct gunyah_vm_firmware_config {
|
||||
__u64 guest_phys_addr;
|
||||
__u64 size;
|
||||
};
|
||||
|
||||
#define GH_VM_ANDROID_SET_FW_CONFIG _IOW(GH_ANDROID_IOCTL_TYPE, 0x12, \
|
||||
struct gunyah_vm_firmware_config)
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user