| /* 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/export.h> |
| |
| typedef void (*dyn_hcall_t)(struct kvm_cpu_context *); |
| |
| struct pkvm_module_ops { |
| int (*create_private_mapping)(phys_addr_t phys, size_t size, |
| enum kvm_pgtable_prot prot, |
| unsigned long *haddr); |
| 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 (*flush_dcache_to_poc)(void *addr, size_t size); |
| int (*register_host_perm_fault_handler)(int (*cb)(struct kvm_cpu_context *ctxt, u64 esr, u64 addr)); |
| int (*protect_host_page)(u64 pfn, enum kvm_pgtable_prot prot); |
| }; |
| |
| struct pkvm_module_section { |
| void *start; |
| void *end; |
| }; |
| |
| typedef s32 kvm_nvhe_reloc_t; |
| |
| struct pkvm_el2_module { |
| struct pkvm_module_section text; |
| struct pkvm_module_section bss; |
| struct pkvm_module_section rodata; |
| struct pkvm_module_section data; |
| kvm_nvhe_reloc_t *relocs; |
| unsigned int nr_relocs; |
| int (*init)(const struct pkvm_module_ops *ops); |
| }; |
| |
| #ifdef MODULE |
| int __pkvm_load_el2_module(struct pkvm_el2_module *mod, struct module *this, |
| unsigned long *token); |
| |
| #define pkvm_load_el2_module(init_fn, token) \ |
| ({ \ |
| extern char __kvm_nvhe___hypmod_text_start[]; \ |
| extern char __kvm_nvhe___hypmod_text_end[]; \ |
| extern char __kvm_nvhe___hypmod_bss_start[]; \ |
| extern char __kvm_nvhe___hypmod_bss_end[]; \ |
| extern char __kvm_nvhe___hypmod_rodata_start[]; \ |
| extern char __kvm_nvhe___hypmod_rodata_end[]; \ |
| extern char __kvm_nvhe___hypmod_data_start[]; \ |
| extern char __kvm_nvhe___hypmod_data_end[]; \ |
| extern char __kvm_nvhe___hyprel_start[]; \ |
| extern char __kvm_nvhe___hyprel_end[]; \ |
| struct pkvm_el2_module mod; \ |
| \ |
| mod.text.start = __kvm_nvhe___hypmod_text_start; \ |
| mod.text.end = __kvm_nvhe___hypmod_text_end; \ |
| mod.bss.start = __kvm_nvhe___hypmod_bss_start; \ |
| mod.bss.end = __kvm_nvhe___hypmod_bss_end; \ |
| mod.rodata.start = __kvm_nvhe___hypmod_rodata_start; \ |
| mod.rodata.end = __kvm_nvhe___hypmod_rodata_end; \ |
| mod.data.start = __kvm_nvhe___hypmod_data_start; \ |
| mod.data.end = __kvm_nvhe___hypmod_data_end; \ |
| mod.relocs = (kvm_nvhe_reloc_t *)__kvm_nvhe___hyprel_start; \ |
| mod.nr_relocs = (__kvm_nvhe___hyprel_end - __kvm_nvhe___hyprel_start) / \ |
| sizeof(*mod.relocs); \ |
| mod.init = init_fn; \ |
| \ |
| __pkvm_load_el2_module(&mod, THIS_MODULE, token); \ |
| }) |
| |
| int __pkvm_register_el2_call(dyn_hcall_t hfn, unsigned long token, |
| unsigned long hyp_text_kern_va); |
| |
| #define pkvm_register_el2_mod_call(hfn, token) \ |
| ({ \ |
| extern char __kvm_nvhe___hypmod_text_start[]; \ |
| unsigned long hyp_text_kern_va = \ |
| (unsigned long)__kvm_nvhe___hypmod_text_start; \ |
| __pkvm_register_el2_call(function_nocfi(hfn), token, \ |
| hyp_text_kern_va); \ |
| }) |
| |
| #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 |