/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
/*
 * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
 */

#ifndef _UAPI_LINUX_GUNYAH_H
#define _UAPI_LINUX_GUNYAH_H

/*
 * Userspace interface for /dev/gunyah - gunyah based virtual machine
 */

#include <linux/types.h>
#include <linux/ioctl.h>

#define GUNYAH_IOCTL_TYPE 'G'

/*
 * ioctls for /dev/gunyah fds:
 */
#define GUNYAH_CREATE_VM _IO(GUNYAH_IOCTL_TYPE, 0x0) /* Returns a Gunyah VM fd */

/*
 * ioctls for gunyah-vm fds (returned by GUNYAH_CREATE_VM)
 */

/**
 * struct gunyah_vm_dtb_config - Set the location of the VM's devicetree blob
 * @guest_phys_addr: Address of the VM's devicetree in guest memory.
 * @size: Maximum size of the devicetree including space for overlays.
 *        Resource manager applies an overlay to the DTB and dtb_size should
 *        include room for the overlay. A page of memory is typicaly plenty.
 */
struct gunyah_vm_dtb_config {
	__u64 guest_phys_addr;
	__u64 size;
};
#define GUNYAH_VM_SET_DTB_CONFIG	_IOW(GUNYAH_IOCTL_TYPE, 0x2, struct gunyah_vm_dtb_config)

#define GUNYAH_VM_START		_IO(GUNYAH_IOCTL_TYPE, 0x3)

/**
 * enum gunyah_fn_type - Valid types of Gunyah VM functions
 * @GUNYAH_FN_VCPU: create a vCPU instance to control a vCPU
 *              &struct gunyah_fn_desc.arg is a pointer to &struct gunyah_fn_vcpu_arg
 *              Return: file descriptor to manipulate the vcpu.
 * @GUNYAH_FN_IRQFD: register eventfd to assert a Gunyah doorbell
 *               &struct gunyah_fn_desc.arg is a pointer to &struct gunyah_fn_irqfd_arg
 * @GUNYAH_FN_IOEVENTFD: register ioeventfd to trigger when VM faults on parameter
 *                   &struct gunyah_fn_desc.arg is a pointer to &struct gunyah_fn_ioeventfd_arg
 */
enum gunyah_fn_type {
	GUNYAH_FN_VCPU = 1,
	GUNYAH_FN_IRQFD,
	GUNYAH_FN_IOEVENTFD,
};

#define GUNYAH_FN_MAX_ARG_SIZE		256

/**
 * struct gunyah_fn_vcpu_arg - Arguments to create a vCPU.
 * @id: vcpu id
 *
 * Create this function with &GUNYAH_VM_ADD_FUNCTION using type &GUNYAH_FN_VCPU.
 *
 * The vcpu type will register with the VM Manager to expect to control
 * vCPU number `vcpu_id`. It returns a file descriptor allowing interaction with
 * the vCPU. See the Gunyah vCPU API description sections for interacting with
 * the Gunyah vCPU file descriptors.
 */
struct gunyah_fn_vcpu_arg {
	__u32 id;
};

/**
 * enum gunyah_irqfd_flags - flags for use in gunyah_fn_irqfd_arg
 * @GUNYAH_IRQFD_FLAGS_LEVEL: make the interrupt operate like a level triggered
 *                        interrupt on guest side. Triggering IRQFD before
 *                        guest handles the interrupt causes interrupt to
 *                        stay asserted.
 */
enum gunyah_irqfd_flags {
	GUNYAH_IRQFD_FLAGS_LEVEL		= 1UL << 0,
};

/**
 * struct gunyah_fn_irqfd_arg - Arguments to create an irqfd function.
 *
 * Create this function with &GUNYAH_VM_ADD_FUNCTION using type &GUNYAH_FN_IRQFD.
 *
 * Allows setting an eventfd to directly trigger a guest interrupt.
 * irqfd.fd specifies the file descriptor to use as the eventfd.
 * irqfd.label corresponds to the doorbell label used in the guest VM's devicetree.
 *
 * @fd: an eventfd which when written to will raise a doorbell
 * @label: Label of the doorbell created on the guest VM
 * @flags: see &enum gunyah_irqfd_flags
 * @padding: padding bytes
 */
struct gunyah_fn_irqfd_arg {
	__u32 fd;
	__u32 label;
	__u32 flags;
	__u32 padding;
};

/**
 * enum gunyah_ioeventfd_flags - flags for use in gunyah_fn_ioeventfd_arg
 * @GUNYAH_IOEVENTFD_FLAGS_DATAMATCH: the event will be signaled only if the
 *                                written value to the registered address is
 *                                equal to &struct gunyah_fn_ioeventfd_arg.datamatch
 */
enum gunyah_ioeventfd_flags {
	GUNYAH_IOEVENTFD_FLAGS_DATAMATCH	= 1UL << 0,
};

/**
 * struct gunyah_fn_ioeventfd_arg - Arguments to create an ioeventfd function
 * @datamatch: data used when GUNYAH_IOEVENTFD_DATAMATCH is set
 * @addr: Address in guest memory
 * @len: Length of access
 * @fd: When ioeventfd is matched, this eventfd is written
 * @flags: See &enum gunyah_ioeventfd_flags
 * @padding: padding bytes
 *
 * Create this function with &GUNYAH_VM_ADD_FUNCTION using type &GUNYAH_FN_IOEVENTFD.
 *
 * Attaches an ioeventfd to a legal mmio address within the guest. A guest write
 * in the registered address will signal the provided event instead of triggering
 * an exit on the GUNYAH_VCPU_RUN ioctl.
 */
struct gunyah_fn_ioeventfd_arg {
	__u64 datamatch;
	__u64 addr;        /* legal mmio address */
	__u32 len;         /* 1, 2, 4, or 8 bytes; or 0 to ignore length */
	__s32 fd;
	__u32 flags;
	__u32 padding;
};

/**
 * struct gunyah_fn_desc - Arguments to create a VM function
 * @type: Type of the function. See &enum gunyah_fn_type.
 * @arg_size: Size of argument to pass to the function. arg_size <= GUNYAH_FN_MAX_ARG_SIZE
 * @arg: Pointer to argument given to the function. See &enum gunyah_fn_type for expected
 *       arguments for a function type.
 */
struct gunyah_fn_desc {
	__u32 type;
	__u32 arg_size;
	__u64 arg;
};

#define GUNYAH_VM_ADD_FUNCTION	_IOW(GUNYAH_IOCTL_TYPE, 0x4, struct gunyah_fn_desc)
#define GUNYAH_VM_REMOVE_FUNCTION	_IOW(GUNYAH_IOCTL_TYPE, 0x7, struct gunyah_fn_desc)

/**
 * enum gunyah_map_flags- Possible flags on &struct gunyah_map_mem_args
 * @GUNYAH_MEM_DEFAULT_SHARE: Use default host access for the VM type
 * @GUNYAH_MEM_FORCE_LEND: Force unmapping the memory once the guest starts to use
 * @GUNYAH_MEM_FORCE_SHARE: Allow host to continue accessing memory when guest starts to use
 * @GUNYAH_MEM_ALLOW_READ: Allow guest to read memory
 * @GUNYAH_MEM_ALLOW_WRITE: Allow guest to write to the memory
 * @GUNYAH_MEM_ALLOW_EXEC: Allow guest to execute instructions in the memory
 */
enum gunyah_map_flags {
	GUNYAH_MEM_ALLOW_READ = 1UL << 0,
	GUNYAH_MEM_ALLOW_WRITE = 1UL << 1,
	GUNYAH_MEM_ALLOW_EXEC = 1UL << 2,
	GUNYAH_MEM_ALLOW_RWX =
		(GUNYAH_MEM_ALLOW_READ | GUNYAH_MEM_ALLOW_WRITE | GUNYAH_MEM_ALLOW_EXEC),
	GUNYAH_MEM_DEFAULT_ACCESS = 0x00,
	GUNYAH_MEM_FORCE_LEND = 0x10,
	GUNYAH_MEM_FORCE_SHARE = 0x20,
#define GUNYAH_MEM_ACCESS_MASK 0x70

	GUNYAH_MEM_UNMAP = 1UL << 8,
};

/**
 * struct gunyah_map_mem_args - Description to provide guest memory into a VM
 * @guest_addr: Location in guest address space to place the memory
 * @flags: See &enum gunyah_map_flags.
 * @guest_mem_fd: File descriptor created by GUNYAH_CREATE_GUEST_MEM
 * @offset: Offset into the guest memory file
 */
struct gunyah_map_mem_args {
	__u64 guest_addr;
	__u32 flags;
	__u32 guest_mem_fd;
	__u64 offset;
	__u64 size;
};

#define GUNYAH_VM_MAP_MEM _IOW(GUNYAH_IOCTL_TYPE, 0x9, struct gunyah_map_mem_args)

enum gunyah_vm_boot_context_reg {
	REG_SET_X		= 0,
	REG_SET_PC		= 1,
	REG_SET_SP		= 2,
};

#define GUNYAH_VM_BOOT_CONTEXT_REG_SHIFT	8
#define GUNYAH_VM_BOOT_CONTEXT_REG(reg, idx) (((reg & 0xff) << GUNYAH_VM_BOOT_CONTEXT_REG_SHIFT) |\
					      (idx & 0xff))

/**
 * struct gunyah_vm_boot_context - Set an initial register for the VM
 * @reg: Register to set. See GUNYAH_VM_BOOT_CONTEXT_REG_* macros
 * @reserved: reserved for alignment
 * @value: value to fill in the register
 */
struct gunyah_vm_boot_context {
	__u32 reg;
	__u32 reserved;
	__u64 value;
};
#define GUNYAH_VM_SET_BOOT_CONTEXT	_IOW(GUNYAH_IOCTL_TYPE, 0xa, struct gunyah_vm_boot_context)

/*
 * ioctls for vCPU fds
 */

/**
 * enum gunyah_vm_status - Stores status reason why VM is not runnable (exited).
 * @GUNYAH_VM_STATUS_LOAD_FAILED: VM didn't start because it couldn't be loaded.
 * @GUNYAH_VM_STATUS_EXITED: VM requested shutdown/reboot.
 *                       Use &struct gunyah_vm_exit_info.reason for further details.
 * @GUNYAH_VM_STATUS_CRASHED: VM state is unknown and has crashed.
 */
enum gunyah_vm_status {
	GUNYAH_VM_STATUS_LOAD_FAILED	= 1,
	GUNYAH_VM_STATUS_EXITED		= 2,
	GUNYAH_VM_STATUS_CRASHED		= 3,
};

/*
 * Gunyah presently sends max 4 bytes of exit_reason.
 * If that changes, this macro can be safely increased without breaking
 * userspace so long as struct gunyah_vcpu_run < PAGE_SIZE.
 */
#define GUNYAH_VM_MAX_EXIT_REASON_SIZE	8u

/**
 * struct gunyah_vm_exit_info - Reason for VM exit as reported by Gunyah
 * See Gunyah documentation for values.
 * @type: Describes how VM exited
 * @padding: padding bytes
 * @reason_size: Number of bytes valid for `reason`
 * @reason: See Gunyah documentation for interpretation. Note: these values are
 *          not interpreted by Linux and need to be converted from little-endian
 *          as applicable.
 */
struct gunyah_vm_exit_info {
	__u16 type;
	__u16 padding;
	__u32 reason_size;
	__u8 reason[GUNYAH_VM_MAX_EXIT_REASON_SIZE];
};

/**
 * enum gunyah_vcpu_exit - Stores reason why &GUNYAH_VCPU_RUN ioctl recently exited with status 0
 * @GUNYAH_VCPU_EXIT_UNKNOWN: Not used, status != 0
 * @GUNYAH_VCPU_EXIT_MMIO: vCPU performed a read or write that could not be handled
 *                     by hypervisor or Linux. Use @struct gunyah_vcpu_run.mmio for
 *                     details of the read/write.
 * @GUNYAH_VCPU_EXIT_STATUS: vCPU not able to run because the VM has exited.
 *                       Use @struct gunyah_vcpu_run.status for why VM has exited.
 * @GUNYAH_VCPU_EXIT_PAGE_FAULT: vCPU tried to execute an instruction at an address
 *                               for which memory hasn't been provided. Use
 *                               @struct gunyah_vcpu_run.page_fault for details.
 */
enum gunyah_vcpu_exit {
	GUNYAH_VCPU_EXIT_UNKNOWN,
	GUNYAH_VCPU_EXIT_MMIO,
	GUNYAH_VCPU_EXIT_STATUS,
	GUNYAH_VCPU_EXIT_PAGE_FAULT,
};

/**
 * enum gunyah_vcpu_resume_action - Provide resume action after an MMIO or page fault
 * @GUNYAH_VCPU_RESUME_HANDLED: The mmio or page fault has been handled, continue
 *                              normal operation of vCPU
 * @GUNYAH_VCPU_RESUME_FAULT: The mmio or page fault could not be satisfied and
 *                            inject the original fault back to the guest.
 * @GUNYAH_VCPU_RESUME_RETRY: Retry the faulting instruction. Perhaps you added
 *                            memory binding to satisfy the request.
 */
enum gunyah_vcpu_resume_action {
	GUNYAH_VCPU_RESUME_HANDLED = 0,
	GUNYAH_VCPU_RESUME_FAULT,
	GUNYAH_VCPU_RESUME_RETRY,
};

/**
 * struct gunyah_vcpu_run - Application code obtains a pointer to the gunyah_vcpu_run
 *                      structure by mmap()ing a vcpu fd.
 * @immediate_exit: polled when scheduling the vcpu. If set, immediately returns -EINTR.
 * @padding: padding bytes
 * @exit_reason: Set when GUNYAH_VCPU_RUN returns successfully and gives reason why
 *               GUNYAH_VCPU_RUN has stopped running the vCPU. See &enum gunyah_vcpu_exit.
 * @mmio: Used when exit_reason == GUNYAH_VCPU_EXIT_MMIO
 *        The guest has faulted on an memory-mapped I/O that
 *        couldn't be satisfied by gunyah.
 * @mmio.phys_addr: Address guest tried to access
 * @mmio.data: the value that was written if `is_write == 1`. Filled by
 *        user for reads (`is_write == 0`).
 * @mmio.len: Length of write. Only the first `len` bytes of `data`
 *       are considered by Gunyah.
 * @mmio.is_write: 1 if VM tried to perform a write, 0 for a read
 * @mmio.resume_action: See &enum gunyah_vcpu_resume_action
 * @status: Used when exit_reason == GUNYAH_VCPU_EXIT_STATUS.
 *          The guest VM is no longer runnable. This struct informs why.
 * @status.status: See &enum gunyah_vm_status for possible values
 * @status.exit_info: Used when status == GUNYAH_VM_STATUS_EXITED
 * @page_fault: Used when EXIT_REASON == GUNYAH_VCPU_EXIT_PAGE_FAULT
 *              The guest has faulted on a region that can only be provided
 *              by mapping memory at phys_addr.
 * @page_fault.phys_addr: Address guest tried to access.
 * @page_fault.attempt: Error code why Linux wasn't able to handle fault itself
 *                      Typically, if no memory was mapped: -ENOENT,
 *                      If permission bits weren't what the VM wanted: -EPERM
 * @page_fault.resume_action: See &enum gunyah_vcpu_resume_action
 */
struct gunyah_vcpu_run {
	/* in */
	__u8 immediate_exit;
	__u8 padding[7];

	/* out */
	__u32 exit_reason;

	union {
		struct {
			__u64 phys_addr;
			__u8  data[8];
			__u32 len;
			__u8  is_write;
			__u8  resume_action;
		} mmio;

		struct {
			enum gunyah_vm_status status;
			struct gunyah_vm_exit_info exit_info;
		} status;

		struct {
			__u64 phys_addr;
			__s32 attempt;
			__u8  resume_action;
		} page_fault;
	};
};

#define GUNYAH_VCPU_RUN		_IO(GUNYAH_IOCTL_TYPE, 0x5)
#define GUNYAH_VCPU_MMAP_SIZE	_IO(GUNYAH_IOCTL_TYPE, 0x6)

/**
 * struct gunyah_userspace_memory_region - Userspace memory descripion for GH_VM_SET_USER_MEM_REGION
 * @label: Identifer to the region which is unique to the VM.
 * @flags: Flags for memory parcel behavior. See &enum gh_mem_flags.
 * @guest_phys_addr: Location of the memory region in guest's memory space (page-aligned)
 * @memory_size: Size of the region (page-aligned)
 * @userspace_addr: Location of the memory region in caller (userspace)'s memory
 *
 * See Documentation/virt/gunyah/vm-manager.rst for further details.
 */
struct gunyah_userspace_memory_region {
	__u32 label;
	__u32 flags;
	__u64 guest_phys_addr;
	__u64 memory_size;
	__u64 userspace_addr;
};

#define GH_VM_SET_USER_MEM_REGION	_IOW(GUNYAH_IOCTL_TYPE, 0x1, \
						struct gunyah_userspace_memory_region)
#define GH_ANDROID_IOCTL_TYPE		'A'

#define GH_VM_ANDROID_LEND_USER_MEM	_IOW(GH_ANDROID_IOCTL_TYPE, 0x11, \
						struct gunyah_userspace_memory_region)

struct gunyah_vm_firmware_config {
	__u64 guest_phys_addr;
	__u64 size;
};

#define GH_VM_ANDROID_SET_FW_CONFIG	_IOW(GH_ANDROID_IOCTL_TYPE, 0x12, \
						struct gunyah_vm_firmware_config)

struct gunyah_address_range {
	__u64 guest_phys_addr;
	__u64 size;
};

#define GH_VM_RECLAIM_REGION		_IOW(GH_ANDROID_IOCTL_TYPE, 0x13, \
						struct gunyah_address_range)


/**
 * Obtain an fd for the CMA backed file.
 */
#define GH_ANDROID_CREATE_CMA_MEM_FD _IO(GH_ANDROID_IOCTL_TYPE, 0x14)

/**
 * struct gunyah_map_cma_mem_args - Description to provide CMA based guest memory into a VM
 * @guest_addr: Location in guest address space to place the memory
 * @flags: See &enum gunyah_map_flags.
 * @guest_mem_fd: File descriptor created by GH_ANDROID_CREATE_CMA_MEM_FD
 * @offset: Offset into the guest memory file
 * @size: Size of the region to be mapped.
 */
struct gunyah_map_cma_mem_args {
	__u32 label;
	__u64 guest_addr;
	__u32 flags;
	__u32 guest_mem_fd;
	__u64 offset;
	__u64 size;
};

#define GH_VM_ANDROID_MAP_CMA_MEM _IOW(GH_ANDROID_IOCTL_TYPE, 0x15, struct gunyah_map_cma_mem_args)

/**
 * enum gunyah_auth_type - Valid types of authentication supported by Gunyah
 */
enum gunyah_auth_type {
	GUNYAH_QCOM_TRUSTED_VM_TYPE = 1,
};

/**
 * struct gunyah_qtvm_auth_arg - Argument to QTVM auth vm manager
 * @vm_id: The VM ID that Gunyah associates with the QTVM.
 * @peripheral_id: Also called pas_id which firmware needs for authenticating the QTVM.
 * @guest_phys_addr: Address where the VM image should be mapped to.
 * @size: Size of the VM image
 */
struct gunyah_qtvm_auth_arg {
	__u16 vm_id;
	__u32 peripheral_id;
	__u64 guest_phys_addr;
	__u64 size;
};

/**
 * struct gunyah_auth_desc - Arguments to match with an auth vm manager
 * @type: Type of the authentication mechanism. See &enum gunyah_auth_type.
 * @arg_size: Size of argument to pass to the auth vm mgr. arg_size <= GUNYAH_FN_MAX_ARG_SIZE
 * @arg: Pointer to argument given to the auth vm mgr. See &enum gunyah_auth_type for expected
 *       arguments for a function type.
 */
struct gunyah_auth_desc {
	__u32 type;
	__u32 arg_size;
	__u64 arg;
};
#define GH_VM_ANDROID_SET_AUTH_TYPE		_IOW(GH_ANDROID_IOCTL_TYPE, 0x16, \
							struct gunyah_auth_desc)
#endif
