blob: b8e5f80643589ec448fc2ba9405ec0a713107eda [file] [log] [blame] [edit]
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __ARM64_KVM_PKVM_MODULE_H__
#define __ARM64_KVM_PKVM_MODULE_H__
#include <asm/kvm_pgtable.h>
#include <linux/android_kabi.h>
#include <linux/export.h>
typedef void (*dyn_hcall_t)(struct kvm_cpu_context *);
enum pkvm_psci_notification {
PKVM_PSCI_CPU_SUSPEND,
PKVM_PSCI_SYSTEM_SUSPEND,
PKVM_PSCI_CPU_ENTRY,
};
#ifdef CONFIG_MODULES
/**
* struct pkvm_module_ops - pKVM modules callbacks
* @create_private_mapping: Map a memory region into the hypervisor private
* range. @haddr returns the virtual address where
* the mapping starts. It can't be unmapped. Host
* access permissions are unaffected.
* @alloc_module_va: Reserve a range of VA space in the hypervisor
* private range. This is handy for modules that
* need to map plugin code in a similar fashion to
* how pKVM maps module code. That space could also
* be used to map memory temporarily, when the
* fixmap granularity (PAGE_SIZE) is too small.
* @map_module_page: Used in conjunction with @alloc_module_va. When
* @is_protected is not set, the page is also
* unmapped from the host stage-2.
* @register_serial_driver: Register a driver for a serial interface. The
* framework only needs a single callback
* @hyp_putc_cb which is expected to print a single
* character.
* @puts: If a serial interface is registered, print a
* string, else does nothing.
* @putx64: If a serial interface is registered, print a
* 64-bit number, else does nothing.
* @fixmap_map: Map a page in the per-CPU hypervisor fixmap.
* This is intended to be used for temporary
* mappings in the hypervisor VA space.
* @fixmap_unmap must be called between each
* mapping to do cache maintenance and ensure the
* new mapping is visible.
* @fixmap_unmap: Unmap a page from the hypervisor fixmap. This
* call is required between each @fixmap_map().
* @linear_map_early: Map a large portion of memory into the
* hypervisor linear VA space. This is intended to
* be used only for module bootstrap and must be
* unmapped before the host is deprivilged.
* @linear_unmap_early: See @linear_map_early.
* @flush_dcache_to_poc: Clean the data cache to the point of coherency.
* This is not a requirement for any other of the
* pkvm_module_ops callbacks.
* @update_hcr_el2: Modify the running value of HCR_EL2. pKVM will
* save/restore the new value across power
* management transitions.
* @update_hfgwtr_el2: Modify the running value of HFGWTR_EL2. pKVM
* will save/restore the new value across power
* management transitions.
* @register_host_perm_fault_handler:
* @cb is called whenever the host generates an
* abort with the fault status code Permission
* Fault. Returning -EPERM lets pKVM handle the
* abort. This is useful when a module changes the
* host stage-2 permissions for certain pages.
* @host_stage2_mod_prot: Apply @prot to the page @pfn. This requires a
* permission fault handler to be registered (see
* @register_host_perm_fault_handler), otherwise
* pKVM will be unable to handle this fault and the
* CPU will be stuck in an infinite loop.
* @host_stage2_mod_prot_range: Similar to @host_stage2_mod_prot, but takes a
* range as an argument (@nr_pages). This
* considerably speeds up the process for a
* contiguous memory region, compared to the
* per-page @host_stage2_mod_prot.
* @host_stage2_get_leaf: Query the host's stage2 page-table entry for
* the page @phys.
* @register_host_smc_handler: @cb is called whenever the host issues an SMC
* pKVM couldn't handle. If @cb returns false, the
* SMC will be forwarded to EL3.
* @register_default_trap_handler:
* @cb is called whenever EL2 traps EL1 and pKVM
* has not handled it. If @cb returns false, the
* hypervisor will panic. This trap handler must be
* registered whenever changes are made to HCR
* (@update_hcr_el2) or HFGWTR
* (@update_hfgwtr_el2).
* @register_illegal_abt_notifier:
* To notify the module of a pending illegal abort
* from the host. On @cb return, the abort will be
* injected back into the host.
* @register_psci_notifier: To notify the module of a pending PSCI event.
* @register_hyp_panic_notifier:
* To notify the module of a pending hypervisor
* panic. On return from @cb, the panic will occur.
* @host_donate_hyp: The page @pfn is unmapped from the host and
* full control is given to the hypervisor.
* @hyp_donate_host: The page @pfn whom control has previously been
* given to the hypervisor (@host_donate_hyp) is
* given back to the host.
* @host_share_hyp: The page @pfn will be shared between the host
* and the hypervisor. Must be followed by
* @pin_shared_mem.
* @host_unshare_hyp: The page @pfn will be unshared and unmapped from
* the hypervisor. Must be called after
* @unpin_shared_mem.
* @pin_shared_mem: After @host_share_hyp, the newly shared page is
* still owned by the host. @pin_shared_mem will
* prevent the host from reclaiming that page until
* the hypervisor releases it (@unpin_shared_mem)
* @unpin_shared_mem: Enable the host to reclaim the shared memory
* (@host_unshare_hyp).
* @memcpy: Same as kernel memcpy, but use hypervisor VAs.
* @memset: Same as kernel memset, but use a hypervisor VA.
* @hyp_pa: Return the physical address for a hypervisor
* virtual address in the linear range.
* @hyp_va: Convert a physical address into a virtual one.
* @kern_hyp_va: Convert a kernel virtual address into an
* hypervisor virtual one.
*/
struct pkvm_module_ops {
int (*create_private_mapping)(phys_addr_t phys, size_t size,
enum kvm_pgtable_prot prot,
unsigned long *haddr);
void *(*alloc_module_va)(u64 nr_pages);
int (*map_module_page)(u64 pfn, void *va, enum kvm_pgtable_prot prot, bool is_protected);
int (*register_serial_driver)(void (*hyp_putc_cb)(char));
void (*puts)(const char *str);
void (*putx64)(u64 num);
void *(*fixmap_map)(phys_addr_t phys);
void (*fixmap_unmap)(void);
void *(*linear_map_early)(phys_addr_t phys, size_t size, enum kvm_pgtable_prot prot);
void (*linear_unmap_early)(void *addr, size_t size);
void (*flush_dcache_to_poc)(void *addr, size_t size);
void (*update_hcr_el2)(unsigned long set_mask, unsigned long clear_mask);
void (*update_hfgwtr_el2)(unsigned long set_mask, unsigned long clear_mask);
int (*register_host_perm_fault_handler)(int (*cb)(struct kvm_cpu_context *ctxt, u64 esr, u64 addr));
int (*host_stage2_mod_prot)(u64 pfn, enum kvm_pgtable_prot prot);
int (*host_stage2_get_leaf)(phys_addr_t phys, kvm_pte_t *ptep, u32 *level);
int (*register_host_smc_handler)(bool (*cb)(struct kvm_cpu_context *));
int (*register_default_trap_handler)(bool (*cb)(struct kvm_cpu_context *));
int (*register_illegal_abt_notifier)(void (*cb)(struct kvm_cpu_context *));
int (*register_psci_notifier)(void (*cb)(enum pkvm_psci_notification, struct kvm_cpu_context *));
int (*register_hyp_panic_notifier)(void (*cb)(struct kvm_cpu_context *host_ctxt));
int (*host_donate_hyp)(u64 pfn, u64 nr_pages);
int (*hyp_donate_host)(u64 pfn, u64 nr_pages);
int (*host_share_hyp)(u64 pfn);
int (*host_unshare_hyp)(u64 pfn);
int (*pin_shared_mem)(void *from, void *to);
void (*unpin_shared_mem)(void *from, void *to);
void* (*memcpy)(void *to, const void *from, size_t count);
void* (*memset)(void *dst, int c, size_t count);
phys_addr_t (*hyp_pa)(void *x);
void* (*hyp_va)(phys_addr_t phys);
unsigned long (*kern_hyp_va)(unsigned long x);
ANDROID_KABI_USE(1, int (*host_stage2_mod_prot_range)(u64 pfn, enum kvm_pgtable_prot prot, u64 nr_pages));
ANDROID_KABI_RESERVE(2);
ANDROID_KABI_RESERVE(3);
ANDROID_KABI_RESERVE(4);
ANDROID_KABI_RESERVE(5);
ANDROID_KABI_RESERVE(6);
ANDROID_KABI_RESERVE(7);
ANDROID_KABI_RESERVE(8);
ANDROID_KABI_RESERVE(9);
ANDROID_KABI_RESERVE(10);
ANDROID_KABI_RESERVE(11);
ANDROID_KABI_RESERVE(12);
ANDROID_KABI_RESERVE(13);
ANDROID_KABI_RESERVE(14);
ANDROID_KABI_RESERVE(15);
ANDROID_KABI_RESERVE(16);
ANDROID_KABI_RESERVE(17);
ANDROID_KABI_RESERVE(18);
ANDROID_KABI_RESERVE(19);
ANDROID_KABI_RESERVE(20);
ANDROID_KABI_RESERVE(21);
ANDROID_KABI_RESERVE(22);
ANDROID_KABI_RESERVE(23);
ANDROID_KABI_RESERVE(24);
ANDROID_KABI_RESERVE(25);
ANDROID_KABI_RESERVE(26);
ANDROID_KABI_RESERVE(27);
ANDROID_KABI_RESERVE(28);
ANDROID_KABI_RESERVE(29);
ANDROID_KABI_RESERVE(30);
ANDROID_KABI_RESERVE(31);
ANDROID_KABI_RESERVE(32);
};
int __pkvm_load_el2_module(struct module *this, unsigned long *token);
int __pkvm_register_el2_call(unsigned long hfn_hyp_va);
#else
static inline int __pkvm_load_el2_module(struct module *this,
unsigned long *token)
{
return -ENOSYS;
}
static inline int __pkvm_register_el2_call(unsigned long hfn_hyp_va)
{
return -ENOSYS;
}
#endif /* CONFIG_MODULES */
int pkvm_load_early_modules(void);
#ifdef MODULE
/*
* Convert an EL2 module addr from the kernel VA to the hyp VA
*/
#define pkvm_el2_mod_va(kern_va, token) \
({ \
unsigned long hyp_text_kern_va = \
(unsigned long)THIS_MODULE->arch.hyp.text.start; \
unsigned long offset; \
\
offset = (unsigned long)kern_va - hyp_text_kern_va; \
token + offset; \
})
#define pkvm_load_el2_module(init_fn, token) \
({ \
THIS_MODULE->arch.hyp.init = init_fn; \
__pkvm_load_el2_module(THIS_MODULE, token); \
})
#define pkvm_register_el2_mod_call(hfn, token) \
({ \
__pkvm_register_el2_call(pkvm_el2_mod_va(hfn, token)); \
})
#define pkvm_el2_mod_call(id, ...) \
({ \
struct arm_smccc_res res; \
\
arm_smccc_1_1_hvc(KVM_HOST_SMCCC_ID(id), \
##__VA_ARGS__, &res); \
WARN_ON(res.a0 != SMCCC_RET_SUCCESS); \
\
res.a1; \
})
#endif
#endif