484 lines
15 KiB
C
Executable File
484 lines
15 KiB
C
Executable File
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
/*
|
|
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
|
|
*/
|
|
|
|
#ifndef _LINUX_GUNYAH_H
|
|
#define _LINUX_GUNYAH_H
|
|
|
|
#include <linux/bitfield.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/limits.h>
|
|
#include <linux/list.h>
|
|
#include <linux/mod_devicetable.h>
|
|
#include <linux/types.h>
|
|
|
|
#include <uapi/linux/gunyah.h>
|
|
|
|
struct gunyah_vm;
|
|
|
|
int __must_check gunyah_vm_get(struct gunyah_vm *ghvm);
|
|
void gunyah_vm_put(struct gunyah_vm *ghvm);
|
|
|
|
struct gunyah_vm_function_instance;
|
|
/**
|
|
* struct gunyah_vm_function - Represents a function type
|
|
* @type: value from &enum gunyah_fn_type
|
|
* @name: friendly name for debug purposes
|
|
* @mod: owner of the function type
|
|
* @bind: Called when a new function of this type has been allocated.
|
|
* @unbind: Called when the function instance is being destroyed.
|
|
* @compare: Compare function instance @f's argument to the provided arg.
|
|
* Return true if they are equivalent. Used on GUNYAH_VM_REMOVE_FUNCTION.
|
|
*/
|
|
struct gunyah_vm_function {
|
|
u32 type;
|
|
const char *name;
|
|
struct module *mod;
|
|
long (*bind)(struct gunyah_vm_function_instance *f);
|
|
void (*unbind)(struct gunyah_vm_function_instance *f);
|
|
bool (*compare)(const struct gunyah_vm_function_instance *f,
|
|
const void *arg, size_t size);
|
|
};
|
|
|
|
/**
|
|
* struct gunyah_vm_function_instance - Represents one function instance
|
|
* @arg_size: size of user argument
|
|
* @argp: pointer to user argument
|
|
* @ghvm: Pointer to VM instance
|
|
* @rm: Pointer to resource manager for the VM instance
|
|
* @fn: The ops for the function
|
|
* @data: Private data for function
|
|
* @vm_list: for gunyah_vm's functions list
|
|
*/
|
|
struct gunyah_vm_function_instance {
|
|
size_t arg_size;
|
|
void *argp;
|
|
struct gunyah_vm *ghvm;
|
|
struct gunyah_rm *rm;
|
|
struct gunyah_vm_function *fn;
|
|
void *data;
|
|
struct list_head vm_list;
|
|
};
|
|
|
|
int gunyah_vm_function_register(struct gunyah_vm_function *f);
|
|
void gunyah_vm_function_unregister(struct gunyah_vm_function *f);
|
|
|
|
/* Since the function identifiers were setup in a uapi header as an
|
|
* enum and we do no want to change that, the user must supply the expanded
|
|
* constant as well and the compiler checks they are the same.
|
|
* See also MODULE_ALIAS_RDMA_NETLINK.
|
|
*/
|
|
#define MODULE_ALIAS_GUNYAH_VM_FUNCTION(_type, _idx) \
|
|
static inline void __maybe_unused __chk##_idx(void) \
|
|
{ \
|
|
BUILD_BUG_ON(_type != _idx); \
|
|
} \
|
|
MODULE_ALIAS("ghfunc:" __stringify(_idx))
|
|
|
|
#define DECLARE_GUNYAH_VM_FUNCTION(_name, _type, _bind, _unbind, _compare) \
|
|
static struct gunyah_vm_function _name = { \
|
|
.type = _type, \
|
|
.name = __stringify(_name), \
|
|
.mod = THIS_MODULE, \
|
|
.bind = _bind, \
|
|
.unbind = _unbind, \
|
|
.compare = _compare, \
|
|
}
|
|
|
|
#define module_gunyah_vm_function(__gf) \
|
|
module_driver(__gf, gunyah_vm_function_register, \
|
|
gunyah_vm_function_unregister)
|
|
|
|
#define DECLARE_GUNYAH_VM_FUNCTION_INIT(_name, _type, _idx, _bind, _unbind, \
|
|
_compare) \
|
|
DECLARE_GUNYAH_VM_FUNCTION(_name, _type, _bind, _unbind, _compare); \
|
|
module_gunyah_vm_function(_name); \
|
|
MODULE_ALIAS_GUNYAH_VM_FUNCTION(_type, _idx)
|
|
|
|
/* Matches resource manager's resource types for VM_GET_HYP_RESOURCES RPC */
|
|
enum gunyah_resource_type {
|
|
/* clang-format off */
|
|
GUNYAH_RESOURCE_TYPE_BELL_TX = 0,
|
|
GUNYAH_RESOURCE_TYPE_BELL_RX = 1,
|
|
GUNYAH_RESOURCE_TYPE_MSGQ_TX = 2,
|
|
GUNYAH_RESOURCE_TYPE_MSGQ_RX = 3,
|
|
GUNYAH_RESOURCE_TYPE_VCPU = 4,
|
|
GUNYAH_RESOURCE_TYPE_MEM_EXTENT = 9,
|
|
GUNYAH_RESOURCE_TYPE_ADDR_SPACE = 10,
|
|
/* clang-format on */
|
|
};
|
|
|
|
struct gunyah_resource {
|
|
enum gunyah_resource_type type;
|
|
u64 capid;
|
|
unsigned int irq;
|
|
|
|
struct list_head list;
|
|
u32 rm_label;
|
|
};
|
|
|
|
/**
|
|
* struct gunyah_vm_resource_ticket - Represents a ticket to reserve access to VM resource(s)
|
|
* @vm_list: for @gunyah_vm->resource_tickets
|
|
* @resources: List of resource(s) associated with this ticket
|
|
* (members are from @gunyah_resource->list)
|
|
* @resource_type: Type of resource this ticket reserves
|
|
* @label: Label of the resource from resource manager this ticket reserves.
|
|
* @owner: owner of the ticket
|
|
* @populate: callback provided by the ticket owner and called when a resource is found that
|
|
* matches @resource_type and @label. Note that this callback could be called
|
|
* multiple times if userspace created mutliple resources with the same type/label.
|
|
* This callback may also have significant delay after gunyah_vm_add_resource_ticket()
|
|
* since gunyah_vm_add_resource_ticket() could be called before the VM starts.
|
|
* @unpopulate: callback provided by the ticket owner and called when the ticket owner should no
|
|
* longer use the resource provided in the argument. When unpopulate() returns,
|
|
* the ticket owner should not be able to use the resource any more as the resource
|
|
* might being freed.
|
|
*/
|
|
struct gunyah_vm_resource_ticket {
|
|
struct list_head vm_list;
|
|
struct list_head resources;
|
|
enum gunyah_resource_type resource_type;
|
|
u32 label;
|
|
|
|
struct module *owner;
|
|
bool (*populate)(struct gunyah_vm_resource_ticket *ticket,
|
|
struct gunyah_resource *ghrsc);
|
|
void (*unpopulate)(struct gunyah_vm_resource_ticket *ticket,
|
|
struct gunyah_resource *ghrsc);
|
|
};
|
|
|
|
int gunyah_vm_add_resource_ticket(struct gunyah_vm *ghvm,
|
|
struct gunyah_vm_resource_ticket *ticket);
|
|
void gunyah_vm_remove_resource_ticket(struct gunyah_vm *ghvm,
|
|
struct gunyah_vm_resource_ticket *ticket);
|
|
|
|
/*
|
|
* gunyah_vm_io_handler contains the info about an io device and its associated
|
|
* addr and the ops associated with the io device.
|
|
*/
|
|
struct gunyah_vm_io_handler {
|
|
struct rb_node node;
|
|
u64 addr;
|
|
|
|
bool datamatch;
|
|
u8 len;
|
|
u64 data;
|
|
struct gunyah_vm_io_handler_ops *ops;
|
|
};
|
|
|
|
/*
|
|
* gunyah_vm_io_handler_ops contains function pointers associated with an iodevice.
|
|
*/
|
|
struct gunyah_vm_io_handler_ops {
|
|
int (*read)(struct gunyah_vm_io_handler *io_dev, u64 addr, u32 len,
|
|
u64 data);
|
|
int (*write)(struct gunyah_vm_io_handler *io_dev, u64 addr, u32 len,
|
|
u64 data);
|
|
};
|
|
|
|
int gunyah_vm_add_io_handler(struct gunyah_vm *ghvm,
|
|
struct gunyah_vm_io_handler *io_dev);
|
|
void gunyah_vm_remove_io_handler(struct gunyah_vm *ghvm,
|
|
struct gunyah_vm_io_handler *io_dev);
|
|
|
|
#define GUNYAH_RM_ACL_X BIT(0)
|
|
#define GUNYAH_RM_ACL_W BIT(1)
|
|
#define GUNYAH_RM_ACL_R BIT(2)
|
|
|
|
struct gunyah_rm_mem_acl_entry {
|
|
__le16 vmid;
|
|
u8 perms;
|
|
u8 reserved;
|
|
} __packed;
|
|
|
|
struct gunyah_rm_mem_entry {
|
|
__le64 phys_addr;
|
|
__le64 size;
|
|
} __packed;
|
|
|
|
enum gunyah_rm_mem_type {
|
|
GUNYAH_RM_MEM_TYPE_NORMAL = 0,
|
|
GUNYAH_RM_MEM_TYPE_IO = 1,
|
|
};
|
|
|
|
/*
|
|
* struct gunyah_rm_mem_parcel - Info about memory to be lent/shared/donated/reclaimed
|
|
* @mem_type: The type of memory: normal (DDR) or IO
|
|
* @label: An client-specified identifier which can be used by the other VMs to identify the purpose
|
|
* of the memory parcel.
|
|
* @n_acl_entries: Count of the number of entries in the @acl_entries array.
|
|
* @acl_entries: An array of access control entries. Each entry specifies a VM and what access
|
|
* is allowed for the memory parcel.
|
|
* @n_mem_entries: Count of the number of entries in the @mem_entries array.
|
|
* @mem_entries: An array of regions to be associated with the memory parcel. Addresses should be
|
|
* (intermediate) physical addresses from Linux's perspective.
|
|
* @mem_handle: On success, filled with memory handle that RM allocates for this memory parcel
|
|
*/
|
|
struct gunyah_rm_mem_parcel {
|
|
enum gunyah_rm_mem_type mem_type;
|
|
u32 label;
|
|
size_t n_acl_entries;
|
|
struct gunyah_rm_mem_acl_entry *acl_entries;
|
|
size_t n_mem_entries;
|
|
struct gunyah_rm_mem_entry *mem_entries;
|
|
u32 mem_handle;
|
|
};
|
|
|
|
enum gunyah_pagetable_access {
|
|
/* clang-format off */
|
|
GUNYAH_PAGETABLE_ACCESS_NONE = 0,
|
|
GUNYAH_PAGETABLE_ACCESS_X = 1,
|
|
GUNYAH_PAGETABLE_ACCESS_W = 2,
|
|
GUNYAH_PAGETABLE_ACCESS_R = 4,
|
|
GUNYAH_PAGETABLE_ACCESS_RX = 5,
|
|
GUNYAH_PAGETABLE_ACCESS_RW = 6,
|
|
GUNYAH_PAGETABLE_ACCESS_RWX = 7,
|
|
/* clang-format on */
|
|
};
|
|
|
|
struct gunyah_rm_platform_ops {
|
|
int (*pre_mem_share)(struct gunyah_rm *rm,
|
|
struct gunyah_rm_mem_parcel *mem_parcel);
|
|
int (*post_mem_reclaim)(struct gunyah_rm *rm,
|
|
struct gunyah_rm_mem_parcel *mem_parcel);
|
|
|
|
int (*pre_demand_page)(struct gunyah_rm *rm, u16 vmid,
|
|
enum gunyah_pagetable_access access,
|
|
struct folio *folio);
|
|
int (*release_demand_page)(struct gunyah_rm *rm, u16 vmid,
|
|
enum gunyah_pagetable_access access,
|
|
struct folio *folio);
|
|
};
|
|
|
|
#if IS_ENABLED(CONFIG_GUNYAH_PLATFORM_HOOKS)
|
|
int gunyah_rm_register_platform_ops(
|
|
const struct gunyah_rm_platform_ops *platform_ops);
|
|
void gunyah_rm_unregister_platform_ops(
|
|
const struct gunyah_rm_platform_ops *platform_ops);
|
|
int devm_gunyah_rm_register_platform_ops(
|
|
struct device *dev, const struct gunyah_rm_platform_ops *ops);
|
|
#else
|
|
static inline int gunyah_rm_register_platform_ops(
|
|
const struct gunyah_rm_platform_ops *platform_ops)
|
|
{
|
|
return 0;
|
|
}
|
|
static inline void gunyah_rm_unregister_platform_ops(
|
|
const struct gunyah_rm_platform_ops *platform_ops)
|
|
{
|
|
}
|
|
static inline int
|
|
devm_gunyah_rm_register_platform_ops(struct device *dev,
|
|
const struct gunyah_rm_platform_ops *ops)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/******************************************************************************/
|
|
/* Common arch-independent definitions for Gunyah hypercalls */
|
|
#define GUNYAH_CAPID_INVAL U64_MAX
|
|
#define GUNYAH_VMID_ROOT_VM 0xff
|
|
|
|
enum gunyah_error {
|
|
/* clang-format off */
|
|
GUNYAH_ERROR_OK = 0,
|
|
GUNYAH_ERROR_UNIMPLEMENTED = -1,
|
|
GUNYAH_ERROR_RETRY = -2,
|
|
|
|
GUNYAH_ERROR_ARG_INVAL = 1,
|
|
GUNYAH_ERROR_ARG_SIZE = 2,
|
|
GUNYAH_ERROR_ARG_ALIGN = 3,
|
|
|
|
GUNYAH_ERROR_NOMEM = 10,
|
|
|
|
GUNYAH_ERROR_ADDR_OVFL = 20,
|
|
GUNYAH_ERROR_ADDR_UNFL = 21,
|
|
GUNYAH_ERROR_ADDR_INVAL = 22,
|
|
|
|
GUNYAH_ERROR_DENIED = 30,
|
|
GUNYAH_ERROR_BUSY = 31,
|
|
GUNYAH_ERROR_IDLE = 32,
|
|
|
|
GUNYAH_ERROR_IRQ_BOUND = 40,
|
|
GUNYAH_ERROR_IRQ_UNBOUND = 41,
|
|
|
|
GUNYAH_ERROR_CSPACE_CAP_NULL = 50,
|
|
GUNYAH_ERROR_CSPACE_CAP_REVOKED = 51,
|
|
GUNYAH_ERROR_CSPACE_WRONG_OBJ_TYPE = 52,
|
|
GUNYAH_ERROR_CSPACE_INSUF_RIGHTS = 53,
|
|
GUNYAH_ERROR_CSPACE_FULL = 54,
|
|
|
|
GUNYAH_ERROR_MSGQUEUE_EMPTY = 60,
|
|
GUNYAH_ERROR_MSGQUEUE_FULL = 61,
|
|
/* clang-format on */
|
|
};
|
|
|
|
/**
|
|
* gunyah_error_remap() - Remap Gunyah hypervisor errors into a Linux error code
|
|
* @gunyah_error: Gunyah hypercall return value
|
|
*/
|
|
static inline int gunyah_error_remap(enum gunyah_error gunyah_error)
|
|
{
|
|
switch (gunyah_error) {
|
|
case GUNYAH_ERROR_OK:
|
|
return 0;
|
|
case GUNYAH_ERROR_NOMEM:
|
|
return -ENOMEM;
|
|
case GUNYAH_ERROR_DENIED:
|
|
case GUNYAH_ERROR_CSPACE_CAP_NULL:
|
|
case GUNYAH_ERROR_CSPACE_CAP_REVOKED:
|
|
case GUNYAH_ERROR_CSPACE_WRONG_OBJ_TYPE:
|
|
case GUNYAH_ERROR_CSPACE_INSUF_RIGHTS:
|
|
return -EACCES;
|
|
case GUNYAH_ERROR_CSPACE_FULL:
|
|
case GUNYAH_ERROR_BUSY:
|
|
case GUNYAH_ERROR_IDLE:
|
|
return -EBUSY;
|
|
case GUNYAH_ERROR_IRQ_BOUND:
|
|
case GUNYAH_ERROR_IRQ_UNBOUND:
|
|
case GUNYAH_ERROR_MSGQUEUE_FULL:
|
|
case GUNYAH_ERROR_MSGQUEUE_EMPTY:
|
|
return -EIO;
|
|
case GUNYAH_ERROR_UNIMPLEMENTED:
|
|
return -EOPNOTSUPP;
|
|
case GUNYAH_ERROR_RETRY:
|
|
return -EAGAIN;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
enum gunyah_api_feature {
|
|
/* clang-format off */
|
|
GUNYAH_FEATURE_DOORBELL = 1,
|
|
GUNYAH_FEATURE_MSGQUEUE = 2,
|
|
GUNYAH_FEATURE_VCPU = 5,
|
|
GUNYAH_FEATURE_MEMEXTENT = 6,
|
|
/* clang-format on */
|
|
};
|
|
|
|
bool arch_is_gunyah_guest(void);
|
|
|
|
#define GUNYAH_API_V1 1
|
|
|
|
/* Other bits reserved for future use and will be zero */
|
|
/* clang-format off */
|
|
#define GUNYAH_API_INFO_API_VERSION_MASK GENMASK_ULL(13, 0)
|
|
#define GUNYAH_API_INFO_BIG_ENDIAN BIT_ULL(14)
|
|
#define GUNYAH_API_INFO_IS_64BIT BIT_ULL(15)
|
|
#define GUNYAH_API_INFO_VARIANT_MASK GENMASK_ULL(63, 56)
|
|
/* clang-format on */
|
|
|
|
struct gunyah_hypercall_hyp_identify_resp {
|
|
u64 api_info;
|
|
u64 flags[3];
|
|
};
|
|
|
|
static inline u16
|
|
gunyah_api_version(const struct gunyah_hypercall_hyp_identify_resp *gunyah_api)
|
|
{
|
|
return FIELD_GET(GUNYAH_API_INFO_API_VERSION_MASK,
|
|
gunyah_api->api_info);
|
|
}
|
|
|
|
void gunyah_hypercall_hyp_identify(
|
|
struct gunyah_hypercall_hyp_identify_resp *hyp_identity);
|
|
|
|
enum gunyah_error gunyah_hypercall_bell_send(u64 capid, u64 new_flags,
|
|
u64 *old_flags);
|
|
enum gunyah_error gunyah_hypercall_bell_set_mask(u64 capid, u64 enable_mask,
|
|
u64 ack_mask);
|
|
|
|
/* Immediately raise RX vIRQ on receiver VM */
|
|
#define GUNYAH_HYPERCALL_MSGQ_TX_FLAGS_PUSH BIT(0)
|
|
|
|
enum gunyah_error gunyah_hypercall_msgq_send(u64 capid, size_t size, void *buff,
|
|
u64 tx_flags, bool *ready);
|
|
enum gunyah_error gunyah_hypercall_msgq_recv(u64 capid, void *buff, size_t size,
|
|
size_t *recv_size, bool *ready);
|
|
|
|
#define GUNYAH_ADDRSPACE_SELF_CAP 0
|
|
|
|
/* clang-format off */
|
|
#define GUNYAH_MEMEXTENT_MAPPING_USER_ACCESS GENMASK_ULL(2, 0)
|
|
#define GUNYAH_MEMEXTENT_MAPPING_KERNEL_ACCESS GENMASK_ULL(6, 4)
|
|
#define GUNYAH_MEMEXTENT_MAPPING_TYPE GENMASK_ULL(23, 16)
|
|
/* clang-format on */
|
|
|
|
enum gunyah_memextent_donate_type {
|
|
/* clang-format off */
|
|
GUNYAH_MEMEXTENT_DONATE_TO_CHILD = 0,
|
|
GUNYAH_MEMEXTENT_DONATE_TO_PARENT = 1,
|
|
GUNYAH_MEMEXTENT_DONATE_TO_SIBLING = 2,
|
|
GUNYAH_MEMEXTENT_DONATE_TO_PROTECTED = 3,
|
|
GUNYAH_MEMEXTENT_DONATE_FROM_PROTECTED = 4,
|
|
/* clang-format on */
|
|
};
|
|
|
|
enum gunyah_addrspace_map_flag_bits {
|
|
/* clang-format off */
|
|
GUNYAH_ADDRSPACE_MAP_FLAG_PARTIAL = 0,
|
|
GUNYAH_ADDRSPACE_MAP_FLAG_PRIVATE = 1,
|
|
GUNYAH_ADDRSPACE_MAP_FLAG_VMMIO = 2,
|
|
GUNYAH_ADDRSPACE_MAP_FLAG_NOSYNC = 31,
|
|
/* clang-format on */
|
|
};
|
|
|
|
enum gunyah_error gunyah_hypercall_addrspace_map(u64 capid, u64 extent_capid,
|
|
u64 vbase, u32 extent_attrs,
|
|
u32 flags, u64 offset,
|
|
u64 size);
|
|
enum gunyah_error gunyah_hypercall_addrspace_unmap(u64 capid, u64 extent_capid,
|
|
u64 vbase, u32 flags,
|
|
u64 offset, u64 size);
|
|
|
|
/* clang-format off */
|
|
#define GUNYAH_MEMEXTENT_OPTION_TYPE_MASK GENMASK_ULL(7, 0)
|
|
#define GUNYAH_MEMEXTENT_OPTION_NOSYNC BIT(31)
|
|
/* clang-format on */
|
|
|
|
enum gunyah_error gunyah_hypercall_memextent_donate(u32 options, u64 from_capid,
|
|
u64 to_capid, u64 offset,
|
|
u64 size);
|
|
|
|
struct gunyah_hypercall_vcpu_run_resp {
|
|
union {
|
|
enum {
|
|
/* clang-format off */
|
|
/* VCPU is ready to run */
|
|
GUNYAH_VCPU_STATE_READY = 0,
|
|
/* VCPU is sleeping until an interrupt arrives */
|
|
GUNYAH_VCPU_STATE_EXPECTS_WAKEUP = 1,
|
|
/* VCPU is powered off */
|
|
GUNYAH_VCPU_STATE_POWERED_OFF = 2,
|
|
/* VCPU is blocked in EL2 for unspecified reason */
|
|
GUNYAH_VCPU_STATE_BLOCKED = 3,
|
|
/* VCPU has returned for MMIO READ */
|
|
GUNYAH_VCPU_ADDRSPACE_VMMIO_READ = 4,
|
|
/* VCPU has returned for MMIO WRITE */
|
|
GUNYAH_VCPU_ADDRSPACE_VMMIO_WRITE = 5,
|
|
/* VCPU blocked on fault where we can demand page */
|
|
GUNYAH_VCPU_ADDRSPACE_PAGE_FAULT = 7,
|
|
/* clang-format on */
|
|
} state;
|
|
u64 sized_state;
|
|
};
|
|
u64 state_data[3];
|
|
};
|
|
|
|
enum {
|
|
GUNYAH_ADDRSPACE_VMMIO_ACTION_EMULATE = 0,
|
|
GUNYAH_ADDRSPACE_VMMIO_ACTION_RETRY = 1,
|
|
GUNYAH_ADDRSPACE_VMMIO_ACTION_FAULT = 2,
|
|
};
|
|
|
|
enum gunyah_error
|
|
gunyah_hypercall_vcpu_run(u64 capid, unsigned long *resume_data,
|
|
struct gunyah_hypercall_vcpu_run_resp *resp);
|
|
|
|
#endif
|