/* 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;							\
})

/*
 * function_nocfi() does not work with function pointers, hence the macro in
 * lieu of a function.
 */
#define pkvm_load_el2_module(init_fn, token)				\
({									\
	THIS_MODULE->arch.hyp.init = function_nocfi(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(function_nocfi(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
