FROMLIST: virt: geniezone: Add demand paging support
This page fault handler helps GenieZone hypervisor to do demand paging. On a lower level translation fault, GenieZone hypervisor will first check the fault GPA (guest physical address or IPA in ARM) is valid e.g. within the registered memory region, then it will setup the vcpu_run->exit_reason with necessary information for returning to gzvm driver. With the fault information, the gzvm driver looks up the physical address and call the MT_HVC_GZVM_MAP_GUEST to request the hypervisor maps the found PA to the fault GPA (IPA). There is one exception, for protected vm, we will populate full VM's memory region in advance in order to improve performance. Change-Id: Ia295ef4c43201d731202d03e74889fa6403e7176 Signed-off-by: Yingshiuan Pan <yingshiuan.pan@mediatek.com> Signed-off-by: Jerry Wang <ze-yu.wang@mediatek.com> Signed-off-by: Yi-De Wu <yi-de.wu@mediatek.com> Bug: 301179926 Link: https://lore.kernel.org/all/20230919111210.19615-14-yi-de.wu@mediatek.com/
This commit is contained in:
parent
6a1a30896d
commit
3fcc07ee5f
@ -24,6 +24,7 @@ enum {
|
||||
GZVM_FUNC_INFORM_EXIT = 14,
|
||||
GZVM_FUNC_MEMREGION_PURPOSE = 15,
|
||||
GZVM_FUNC_SET_DTB_CONFIG = 16,
|
||||
GZVM_FUNC_MAP_GUEST = 17,
|
||||
NR_GZVM_FUNC,
|
||||
};
|
||||
|
||||
@ -48,6 +49,7 @@ enum {
|
||||
#define MT_HVC_GZVM_INFORM_EXIT GZVM_HCALL_ID(GZVM_FUNC_INFORM_EXIT)
|
||||
#define MT_HVC_GZVM_MEMREGION_PURPOSE GZVM_HCALL_ID(GZVM_FUNC_MEMREGION_PURPOSE)
|
||||
#define MT_HVC_GZVM_SET_DTB_CONFIG GZVM_HCALL_ID(GZVM_FUNC_SET_DTB_CONFIG)
|
||||
#define MT_HVC_GZVM_MAP_GUEST GZVM_HCALL_ID(GZVM_FUNC_MAP_GUEST)
|
||||
|
||||
#define GIC_V3_NR_LRS 16
|
||||
|
||||
|
@ -356,3 +356,12 @@ u64 gzvm_hva_to_pa_arch(u64 hva)
|
||||
return GZVM_PA_ERR_BAD;
|
||||
return par;
|
||||
}
|
||||
|
||||
int gzvm_arch_map_guest(u16 vm_id, int memslot_id, u64 pfn, u64 gfn,
|
||||
u64 nr_pages)
|
||||
{
|
||||
struct arm_smccc_res res;
|
||||
|
||||
return gzvm_hypcall_wrapper(MT_HVC_GZVM_MAP_GUEST, vm_id, memslot_id,
|
||||
pfn, gfn, nr_pages, 0, 0, &res);
|
||||
}
|
||||
|
@ -8,4 +8,5 @@ GZVM_DIR ?= ../../../drivers/virt/geniezone
|
||||
|
||||
gzvm-y := $(GZVM_DIR)/gzvm_main.o $(GZVM_DIR)/gzvm_vm.o \
|
||||
$(GZVM_DIR)/gzvm_vcpu.o $(GZVM_DIR)/gzvm_irqfd.o \
|
||||
$(GZVM_DIR)/gzvm_ioeventfd.o $(GZVM_DIR)/gzvm_mmu.o
|
||||
$(GZVM_DIR)/gzvm_ioeventfd.o $(GZVM_DIR)/gzvm_mmu.o \
|
||||
$(GZVM_DIR)/gzvm_exception.o
|
||||
|
39
drivers/virt/geniezone/gzvm_exception.c
Normal file
39
drivers/virt/geniezone/gzvm_exception.c
Normal file
@ -0,0 +1,39 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2023 MediaTek Inc.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/gzvm_drv.h>
|
||||
|
||||
/**
|
||||
* gzvm_handle_guest_exception() - Handle guest exception
|
||||
* @vcpu: Pointer to struct gzvm_vcpu_run in userspace
|
||||
* Return:
|
||||
* * true - This exception has been processed, no need to back to VMM.
|
||||
* * false - This exception has not been processed, require userspace.
|
||||
*/
|
||||
bool gzvm_handle_guest_exception(struct gzvm_vcpu *vcpu)
|
||||
{
|
||||
int ret;
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(vcpu->run->exception.reserved); i++) {
|
||||
if (vcpu->run->exception.reserved[i])
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (vcpu->run->exception.exception) {
|
||||
case GZVM_EXCEPTION_PAGE_FAULT:
|
||||
ret = gzvm_handle_page_fault(vcpu);
|
||||
break;
|
||||
case GZVM_EXCEPTION_UNKNOWN:
|
||||
fallthrough;
|
||||
default:
|
||||
ret = -EFAULT;
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
@ -28,6 +28,8 @@ int gzvm_err_to_errno(unsigned long err)
|
||||
return 0;
|
||||
case ERR_NO_MEMORY:
|
||||
return -ENOMEM;
|
||||
case ERR_INVALID_ARGS:
|
||||
return -EINVAL;
|
||||
case ERR_NOT_SUPPORTED:
|
||||
return -EOPNOTSUPP;
|
||||
case ERR_NOT_IMPLEMENTED:
|
||||
|
@ -106,3 +106,42 @@ int gzvm_gfn_to_pfn_memslot(struct gzvm_memslot *memslot, u64 gfn,
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_single_demand_page(struct gzvm *vm, int memslot_id, u64 gfn)
|
||||
{
|
||||
int ret;
|
||||
u64 pfn;
|
||||
|
||||
ret = gzvm_gfn_to_pfn_memslot(&vm->memslot[memslot_id], gfn, &pfn);
|
||||
if (unlikely(ret))
|
||||
return -EFAULT;
|
||||
|
||||
ret = gzvm_arch_map_guest(vm->vm_id, memslot_id, pfn, gfn, 1);
|
||||
if (unlikely(ret))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* gzvm_handle_page_fault() - Handle guest page fault, find corresponding page
|
||||
* for the faulting gpa
|
||||
* @vcpu: Pointer to struct gzvm_vcpu_run of the faulting vcpu
|
||||
*
|
||||
* Return:
|
||||
* * 0 - Success to handle guest page fault
|
||||
* * -EFAULT - Failed to map phys addr to guest's GPA
|
||||
*/
|
||||
int gzvm_handle_page_fault(struct gzvm_vcpu *vcpu)
|
||||
{
|
||||
struct gzvm *vm = vcpu->gzvm;
|
||||
int memslot_id;
|
||||
u64 gfn;
|
||||
|
||||
gfn = PHYS_PFN(vcpu->run->exception.fault_gpa);
|
||||
memslot_id = gzvm_find_memslot(vm, gfn);
|
||||
if (unlikely(memslot_id < 0))
|
||||
return -EFAULT;
|
||||
|
||||
return handle_single_demand_page(vm, memslot_id, gfn);
|
||||
}
|
||||
|
@ -113,9 +113,11 @@ static long gzvm_vcpu_run(struct gzvm_vcpu *vcpu, void __user *argp)
|
||||
* it's geniezone's responsibility to fill corresponding data
|
||||
* structure
|
||||
*/
|
||||
case GZVM_EXIT_HYPERCALL:
|
||||
fallthrough;
|
||||
case GZVM_EXIT_EXCEPTION:
|
||||
if (!gzvm_handle_guest_exception(vcpu))
|
||||
need_userspace = true;
|
||||
break;
|
||||
case GZVM_EXIT_HYPERCALL:
|
||||
fallthrough;
|
||||
case GZVM_EXIT_DEBUG:
|
||||
fallthrough;
|
||||
|
@ -23,6 +23,31 @@ u64 gzvm_gfn_to_hva_memslot(struct gzvm_memslot *memslot, u64 gfn)
|
||||
return memslot->userspace_addr + offset * PAGE_SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* gzvm_find_memslot() - Find memslot containing this @gpa
|
||||
* @vm: Pointer to struct gzvm
|
||||
* @gfn: Guest frame number
|
||||
*
|
||||
* Return:
|
||||
* * >=0 - Index of memslot
|
||||
* * -EFAULT - Not found
|
||||
*/
|
||||
int gzvm_find_memslot(struct gzvm *vm, u64 gfn)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < GZVM_MAX_MEM_REGION; i++) {
|
||||
if (vm->memslot[i].npages == 0)
|
||||
continue;
|
||||
|
||||
if (gfn >= vm->memslot[i].base_gfn &&
|
||||
gfn < vm->memslot[i].base_gfn + vm->memslot[i].npages)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/**
|
||||
* register_memslot_addr_range() - Register memory region to GenieZone
|
||||
* @gzvm: Pointer to struct gzvm
|
||||
|
@ -29,6 +29,7 @@
|
||||
*/
|
||||
#define NO_ERROR (0)
|
||||
#define ERR_NO_MEMORY (-5)
|
||||
#define ERR_INVALID_ARGS (-8)
|
||||
#define ERR_NOT_SUPPORTED (-24)
|
||||
#define ERR_NOT_IMPLEMENTED (-27)
|
||||
#define ERR_FAULT (-40)
|
||||
@ -123,6 +124,8 @@ int gzvm_arch_set_memregion(u16 vm_id, size_t buf_size,
|
||||
int gzvm_arch_check_extension(struct gzvm *gzvm, __u64 cap, void __user *argp);
|
||||
int gzvm_arch_create_vm(unsigned long vm_type);
|
||||
int gzvm_arch_destroy_vm(u16 vm_id);
|
||||
int gzvm_arch_map_guest(u16 vm_id, int memslot_id, u64 pfn, u64 gfn,
|
||||
u64 nr_pages);
|
||||
int gzvm_vm_ioctl_arch_enable_cap(struct gzvm *gzvm,
|
||||
struct gzvm_enable_cap *cap,
|
||||
void __user *argp);
|
||||
@ -140,6 +143,9 @@ u64 gzvm_gfn_to_hva_memslot(struct gzvm_memslot *memslot, u64 gfn);
|
||||
u64 hva_to_pa_fast(u64 hva);
|
||||
u64 hva_to_pa_slow(u64 hva);
|
||||
int gzvm_gfn_to_pfn_memslot(struct gzvm_memslot *memslot, u64 gfn, u64 *pfn);
|
||||
int gzvm_find_memslot(struct gzvm *vm, u64 gpa);
|
||||
int gzvm_handle_page_fault(struct gzvm_vcpu *vcpu);
|
||||
bool gzvm_handle_guest_exception(struct gzvm_vcpu *vcpu);
|
||||
|
||||
int gzvm_arch_create_device(u16 vm_id, struct gzvm_create_device *gzvm_dev);
|
||||
int gzvm_arch_inject_irq(struct gzvm *gzvm, unsigned int vcpu_idx,
|
||||
|
@ -185,6 +185,12 @@ enum {
|
||||
GZVM_EXIT_GZ = 0x9292000a,
|
||||
};
|
||||
|
||||
/* exception definitions of GZVM_EXIT_EXCEPTION */
|
||||
enum {
|
||||
GZVM_EXCEPTION_UNKNOWN = 0x0,
|
||||
GZVM_EXCEPTION_PAGE_FAULT = 0x1,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct gzvm_vcpu_run: Same purpose as kvm_run, this struct is
|
||||
* shared between userspace, kernel and
|
||||
@ -209,6 +215,9 @@ enum {
|
||||
* Handle exception occurred in VM
|
||||
* @exception: Which exception vector
|
||||
* @error_code: Exception error codes
|
||||
* @fault_gpa: Fault GPA (guest physical address or IPA in ARM)
|
||||
* @reserved: Future-proof reservation and reset to zero in hypervisor.
|
||||
* Fill up to the union size, 256 bytes.
|
||||
* @hypercall: The nested struct in anonymous union.
|
||||
* Some hypercalls issued from VM must be handled
|
||||
* @args: The hypercall's arguments
|
||||
@ -255,6 +264,8 @@ struct gzvm_vcpu_run {
|
||||
struct {
|
||||
__u32 exception;
|
||||
__u32 error_code;
|
||||
__u64 fault_gpa;
|
||||
__u64 reserved[30];
|
||||
} exception;
|
||||
/* GZVM_EXIT_HYPERCALL */
|
||||
struct {
|
||||
|
Loading…
Reference in New Issue
Block a user