ANDROID: KVM: Add guest support for range-based hypercalls

The pKVM hypervisor provides a set of HVCs for MMIO guard and memory
sharing that takes a number of pages as an argument to limit the
guest-hypervisor back and forths.

Bug: 357781595
Bug: 243642516
Change-Id: I23e92628bc36b39c6f4cb44694063dcaba63020f
Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
This commit is contained in:
Vincent Donnefort
2024-12-12 16:40:46 +00:00
parent 5bc414a902
commit d533eaa81d
+42 -27
View File
@@ -18,12 +18,11 @@
#include <asm/hypervisor.h>
static size_t pkvm_granule;
static bool pkvm_func_range;
static int arm_smccc_do_one_page(u32 func_id, phys_addr_t phys)
static int __arm_smccc_do(u32 func_id, phys_addr_t phys, int numgranules)
{
phys_addr_t end = phys + PAGE_SIZE;
while (phys < end) {
while (numgranules--) {
struct arm_smccc_res res;
arm_smccc_1_1_invoke(func_id, phys, 0, 0, &res);
@@ -36,33 +35,53 @@ static int arm_smccc_do_one_page(u32 func_id, phys_addr_t phys)
return 0;
}
static int __set_memory_range(u32 func_id, unsigned long start, int numpages)
static int __arm_smccc_do_range(u32 func_id, phys_addr_t phys, int numgranules)
{
void *addr = (void *)start, *end = addr + numpages * PAGE_SIZE;
while (numgranules) {
struct arm_smccc_res res;
while (addr < end) {
int err;
arm_smccc_1_1_invoke(func_id, phys, numgranules, 0, &res);
if (res.a0 != SMCCC_RET_SUCCESS)
return -EPERM;
err = arm_smccc_do_one_page(func_id, virt_to_phys(addr));
if (err)
return err;
addr += PAGE_SIZE;
phys += pkvm_granule * res.a1;
numgranules -= res.a1;
}
return 0;
}
/*
* Apply func_id on the range [phys : phys + numpages * PAGE_SIZE)
*
* Use with cautious. Boundaries of the range will be aligned with pkvm_granule.
* This might lead to overshooting when pkvm_granule > PAGE_SIZE.
*/
static int arm_smccc_do_range(u32 func_id, phys_addr_t phys, int numpages,
bool func_has_range)
{
size_t size = numpages * PAGE_SIZE;
int numgranules;
numgranules = DIV_ROUND_UP(size, pkvm_granule);
phys = ALIGN_DOWN(phys, pkvm_granule);
if (func_has_range)
return __arm_smccc_do_range(func_id, phys, numgranules);
return __arm_smccc_do(func_id, phys, numgranules);
}
static int pkvm_set_memory_encrypted(unsigned long addr, int numpages)
{
return __set_memory_range(ARM_SMCCC_VENDOR_HYP_KVM_MEM_UNSHARE_FUNC_ID,
addr, numpages);
return arm_smccc_do_range(ARM_SMCCC_VENDOR_HYP_KVM_MEM_UNSHARE_FUNC_ID,
virt_to_phys((void *)addr), numpages, pkvm_func_range);
}
static int pkvm_set_memory_decrypted(unsigned long addr, int numpages)
{
return __set_memory_range(ARM_SMCCC_VENDOR_HYP_KVM_MEM_SHARE_FUNC_ID,
addr, numpages);
return arm_smccc_do_range(ARM_SMCCC_VENDOR_HYP_KVM_MEM_SHARE_FUNC_ID,
virt_to_phys((void *)addr), numpages, pkvm_func_range);
}
static const struct arm64_mem_crypt_ops pkvm_crypt_ops = {
@@ -73,8 +92,10 @@ static const struct arm64_mem_crypt_ops pkvm_crypt_ops = {
static int mmio_guard_ioremap_hook(phys_addr_t phys, size_t size,
pgprot_t *prot)
{
phys_addr_t end;
pteval_t protval = pgprot_val(*prot);
u32 func_id = pkvm_func_range ?
ARM_SMCCC_VENDOR_HYP_KVM_MMIO_RGUARD_MAP_FUNC_ID :
ARM_SMCCC_VENDOR_HYP_KVM_MMIO_GUARD_MAP_FUNC_ID;
/*
* We only expect MMIO emulation for regions mapped with device
@@ -83,15 +104,8 @@ static int mmio_guard_ioremap_hook(phys_addr_t phys, size_t size,
if (protval != PROT_DEVICE_nGnRE && protval != PROT_DEVICE_nGnRnE)
return 0;
phys = PAGE_ALIGN_DOWN(phys);
end = phys + PAGE_ALIGN(size);
while (phys < end) {
const int func_id = ARM_SMCCC_VENDOR_HYP_KVM_MMIO_GUARD_MAP_FUNC_ID;
WARN_ON_ONCE(arm_smccc_do_one_page(func_id, phys));
phys += PAGE_SIZE;
}
WARN_ON_ONCE(arm_smccc_do_range(func_id, phys, DIV_ROUND_UP(size, PAGE_SIZE),
pkvm_func_range));
return 0;
}
@@ -137,6 +151,7 @@ void pkvm_init_hyp_services(void)
return;
pkvm_granule = res.a0;
pkvm_func_range = !!res.a1;
if (kvm_arm_hyp_service_available(ARM_SMCCC_KVM_FUNC_MEM_SHARE) &&
kvm_arm_hyp_service_available(ARM_SMCCC_KVM_FUNC_MEM_UNSHARE))