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:
Sreenad Menon
2024-04-26 02:51:41 -07:00
committed by Treehugger Robot
parent b9b7abe6f8
commit 91fe754984
5 changed files with 104 additions and 7 deletions
+3
View File
@@ -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);
+32
View File
@@ -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);
+57 -7
View File
@@ -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);
+5
View File
@@ -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;
};
+7
View File
@@ -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