ANDROID: KVM: arm64: Add pcpu fixmap infrastructure at EL2

We will soon need to temporarily map pages into the hypervisor stage-1
in nVHE protected mode. To do this efficiently, let's introduce a
per-cpu fixmap allowing to map a single page without needing to take any
lock or to allocate memory.

Signed-off-by: Quentin Perret <qperret@google.com>
Bug: 209580772
Change-Id: Id5cbfd04c5c465c9c577a5fac67458331096b581
Signed-off-by: Will Deacon <willdeacon@google.com>
This commit is contained in:
Quentin Perret
2021-11-17 18:42:46 +00:00
committed by Will Deacon
parent a6c9e5720b
commit 154cf3e73f
4 changed files with 94 additions and 0 deletions

View File

@@ -53,6 +53,8 @@ extern struct host_kvm host_kvm;
extern const u8 pkvm_hyp_id;
extern unsigned long hyp_nr_cpus;
int __pkvm_prot_finalize(void);
int __pkvm_host_share_hyp(u64 pfn);
int __pkvm_host_unshare_hyp(u64 pfn);

View File

@@ -13,6 +13,10 @@
extern struct kvm_pgtable pkvm_pgtable;
extern hyp_spinlock_t pkvm_pgd_lock;
int hyp_create_pcpu_fixmap(void);
void *hyp_fixmap_map(phys_addr_t phys);
int hyp_fixmap_unmap(void);
int hyp_create_idmap(u32 hyp_va_bits);
int hyp_map_vectors(void);
int hyp_back_vmemmap(phys_addr_t back);

View File

@@ -25,6 +25,7 @@ struct memblock_region hyp_memory[HYP_MEMBLOCK_REGIONS];
unsigned int hyp_memblock_nr;
static u64 __io_map_base;
static DEFINE_PER_CPU(void *, hyp_fixmap_base);
static int __pkvm_create_mappings(unsigned long start, unsigned long size,
unsigned long phys, enum kvm_pgtable_prot prot)
@@ -197,6 +198,89 @@ int hyp_map_vectors(void)
return 0;
}
void *hyp_fixmap_map(phys_addr_t phys)
{
void *addr = *this_cpu_ptr(&hyp_fixmap_base);
int ret = kvm_pgtable_hyp_map(&pkvm_pgtable, (u64)addr, PAGE_SIZE,
phys, PAGE_HYP);
return ret ? NULL : addr;
}
int hyp_fixmap_unmap(void)
{
void *addr = *this_cpu_ptr(&hyp_fixmap_base);
int ret = kvm_pgtable_hyp_unmap(&pkvm_pgtable, (u64)addr, PAGE_SIZE);
return (ret != PAGE_SIZE) ? -EINVAL : 0;
}
static int __pin_pgtable_cb(u64 addr, u64 end, u32 level, kvm_pte_t *ptep,
enum kvm_pgtable_walk_flags flag, void * const arg)
{
if (!kvm_pte_valid(*ptep) || level != KVM_PGTABLE_MAX_LEVELS - 1)
return -EINVAL;
hyp_page_ref_inc(hyp_virt_to_page(ptep));
return 0;
}
static int hyp_pin_pgtable_pages(u64 addr)
{
struct kvm_pgtable_walker walker = {
.cb = __pin_pgtable_cb,
.flags = KVM_PGTABLE_WALK_LEAF,
};
return kvm_pgtable_walk(&pkvm_pgtable, addr, PAGE_SIZE, &walker);
}
int hyp_create_pcpu_fixmap(void)
{
unsigned long i;
int ret = 0;
u64 addr;
hyp_spin_lock(&pkvm_pgd_lock);
for (i = 0; i < hyp_nr_cpus; i++) {
addr = hyp_alloc_private_va_range(PAGE_SIZE);
if (IS_ERR((void *)addr)) {
ret = -ENOMEM;
goto unlock;
}
/*
* Create a dummy mapping, to get the intermediate page-table
* pages allocated, then take a reference on the last level
* page to keep it around at all times.
*/
ret = kvm_pgtable_hyp_map(&pkvm_pgtable, addr, PAGE_SIZE,
__hyp_pa(__hyp_bss_start), PAGE_HYP);
if (ret) {
ret = -EINVAL;
goto unlock;
}
ret = hyp_pin_pgtable_pages(addr);
if (ret)
goto unlock;
ret = kvm_pgtable_hyp_unmap(&pkvm_pgtable, addr, PAGE_SIZE);
if (ret != PAGE_SIZE) {
ret = -EINVAL;
goto unlock;
} else {
ret = 0;
}
*per_cpu_ptr(&hyp_fixmap_base, i) = (void *)addr;
}
unlock:
hyp_spin_unlock(&pkvm_pgd_lock);
return ret;
}
int hyp_create_idmap(u32 hyp_va_bits)
{
unsigned long start, end;

View File

@@ -316,6 +316,10 @@ void __noreturn __pkvm_init_finalise(void)
if (ret)
goto out;
ret = hyp_create_pcpu_fixmap();
if (ret)
goto out;
out:
/*
* We tail-called to here from handle___pkvm_init() and will not return,