diff --git a/arch/arm64/include/asm/haven/hcall.h b/arch/arm64/include/asm/haven/hcall.h new file mode 100644 index 000000000000..3bf89c558041 --- /dev/null +++ b/arch/arm64/include/asm/haven/hcall.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ +#ifndef __ASM_HH_HCALL_H +#define __ASM_HH_HCALL_H + +#include + +#include + +/** + * _hh_hcall: Performs an AArch64-specific call into hypervisor using Haven ABI + * @hcall_num: Hypercall function ID to invoke + * @args: Hypercall argument registers + * @resp: Pointer to location to store response + */ +static inline int _hh_hcall(const hh_hcall_fnid_t hcall_num, + const struct hh_hcall_args args, + struct hh_hcall_resp *resp) +{ + uint64_t _x18; + + register uint64_t _x0 asm("x0") = args.arg0; + register uint64_t _x1 asm("x1") = args.arg1; + register uint64_t _x2 asm("x2") = args.arg2; + register uint64_t _x3 asm("x3") = args.arg3; + register uint64_t _x4 asm("x4") = args.arg4; + register uint64_t _x5 asm("x5") = args.arg5; + register uint64_t _x6 asm("x6") = args.arg6; + register uint64_t _x7 asm("x7") = args.arg7; + + asm volatile ( + "str x18, [%[_x18]]\n" + "hvc %[num]\n" + "ldr x18, [%[_x18]]\n" + "str xzr, [%[_x18]]\n" + : "+r"(_x0), "+r"(_x1), "+r"(_x2), "+r"(_x3), "+r"(_x4), + "+r"(_x5), "+r"(_x6), "+r"(_x7) + : [num] "i" (hcall_num), [_x18] "r"(&_x18) + : "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", + "memory" + ); + + resp->resp0 = _x0; + resp->resp1 = _x1; + resp->resp2 = _x2; + resp->resp3 = _x3; + resp->resp4 = _x4; + resp->resp5 = _x5; + resp->resp6 = _x6; + resp->resp7 = _x7; + + return _x0; +} + +#endif diff --git a/drivers/virt/Kconfig b/drivers/virt/Kconfig index 363af2eaf2ba..37dca6d85c98 100644 --- a/drivers/virt/Kconfig +++ b/drivers/virt/Kconfig @@ -32,4 +32,5 @@ config FSL_HV_MANAGER partition shuts down. source "drivers/virt/vboxguest/Kconfig" +source "drivers/virt/haven/Kconfig" endif diff --git a/drivers/virt/Makefile b/drivers/virt/Makefile index fd331247c27a..740e4088d934 100644 --- a/drivers/virt/Makefile +++ b/drivers/virt/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_FSL_HV_MANAGER) += fsl_hypervisor.o obj-y += vboxguest/ +obj-y += haven/ diff --git a/drivers/virt/haven/Kconfig b/drivers/virt/haven/Kconfig new file mode 100644 index 000000000000..9dc44f3d1037 --- /dev/null +++ b/drivers/virt/haven/Kconfig @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: GPL-2.0-only + +menuconfig HAVEN_DRIVERS + bool "Haven Virtualization drivers" + help + The Haven drivers are the helper interfaces that runs on the + virtual machines that provides support such as memory/device + sharing, IRQ sharing, IPC/signalling mechanisms, and so on. + + Say Y here to enable the drivers needed to work on Haven + virtualization environment. + + If you say N, all options in this submenu will be skipped and disabled. + +if HAVEN_DRIVERS + +config HH_MSGQ + tristate "Haven Message Queue driver" + help + Haven offers message-queues as one of the IPC mechanisms to + communicate among the Virtual Machines. The message queue drivers + runs on the Virtual machines to provide an interface to the clients + who wish to communicate to other clients on a different VM. Currently, + the services offered by the drivers is simply to send and receive + messages in a blocking manner. + +endif diff --git a/drivers/virt/haven/Makefile b/drivers/virt/haven/Makefile new file mode 100644 index 000000000000..68ab1b7f0809 --- /dev/null +++ b/drivers/virt/haven/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_HH_MSGQ) += hh_msgq.o diff --git a/drivers/virt/haven/hh_msgq.c b/drivers/virt/haven/hh_msgq.c new file mode 100644 index 000000000000..67fb1b56579b --- /dev/null +++ b/drivers/virt/haven/hh_msgq.c @@ -0,0 +1,523 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* HVC call specific mask: 0 to 31 */ +#define HH_MSGQ_HVC_FLAGS_MASK GENMASK_ULL(31, 0) + +struct hh_msgq_desc { + enum hh_msgq_label label; +}; + +struct hh_msgq_cap_table { + struct hh_msgq_desc *client_desc; + struct mutex cap_entry_lock; + + hh_capid_t tx_cap_id; + hh_capid_t rx_cap_id; + int tx_irq; + int rx_irq; + const char *tx_irq_name; + const char *rx_irq_name; + spinlock_t tx_lock; + spinlock_t rx_lock; + + bool tx_full; + bool rx_empty; + wait_queue_head_t tx_wq; + wait_queue_head_t rx_wq; +}; + +static struct hh_msgq_cap_table hh_msgq_cap_table[HH_MSGQ_LABEL_MAX]; + +static irqreturn_t hh_msgq_rx_isr(int irq, void *dev) +{ + struct hh_msgq_cap_table *cap_table_entry = dev; + + spin_lock(&cap_table_entry->rx_lock); + cap_table_entry->rx_empty = false; + spin_unlock(&cap_table_entry->rx_lock); + + wake_up_interruptible(&cap_table_entry->rx_wq); + + return IRQ_HANDLED; +} + +static irqreturn_t hh_msgq_tx_isr(int irq, void *dev) +{ + struct hh_msgq_cap_table *cap_table_entry = dev; + + spin_lock(&cap_table_entry->tx_lock); + cap_table_entry->tx_full = false; + spin_unlock(&cap_table_entry->tx_lock); + + wake_up_interruptible(&cap_table_entry->tx_wq); + + return IRQ_HANDLED; +} + +static int __hh_msgq_recv(struct hh_msgq_cap_table *cap_table_entry, + void *buff, size_t *size, u64 rx_flags) +{ + struct hh_hcall_msgq_recv_resp resp = {}; + unsigned long flags; + int hh_ret; + int ret = 0; + + /* Discard the driver specific flags, and keep only HVC specifics */ + rx_flags &= HH_MSGQ_HVC_FLAGS_MASK; + + spin_lock_irqsave(&cap_table_entry->rx_lock, flags); + hh_ret = hh_hcall_msgq_recv(cap_table_entry->rx_cap_id, buff, + HH_MSGQ_MAX_MSG_SIZE_BYTES, &resp); + + switch (hh_ret) { + case HH_ERROR_OK: + *size = resp.recv_size; + ret = 0; + break; + case HH_ERROR_MSGQUEUE_EMPTY: + cap_table_entry->rx_empty = true; + ret = -EAGAIN; + break; + default: + ret = hh_remap_error(hh_ret); + } + + spin_unlock_irqrestore(&cap_table_entry->rx_lock, flags); + + if (ret != 0 && ret != -EAGAIN) + pr_err("%s: Failed to recv the message. Error: %d\n", + __func__, hh_ret); + + return ret; +} + +/** + * hh_msgq_recv: Receive a message from the client running on a different VM + * @client_desc: The client descriptor that was obtained via hh_msgq_register() + * @buff: Pointer to the buffer where the received data must be placed. Note + * that the caller is responsible to free the data contained in buff + * @size: The size of the buffer received + * @flags: Optional flags to pass to receive the data. For the list of flags, + * see linux/haven/hh_msgq.h + * + * The function returns -EINVAL if the caller passes invalid arguments, -EAGAIN + * if the message queue is not yet ready to communicate, and -EPERM if the + * caller doesn't have permissions to receive the data. 0 is the data is + * successfully received. + */ +int hh_msgq_recv(void *msgq_client_desc, + void **buff, size_t *size, unsigned long flags) +{ + struct hh_msgq_desc *client_desc = msgq_client_desc; + struct hh_msgq_cap_table *cap_table_entry; + int ret; + + if (!client_desc || !(*buff) || !size) + return -EINVAL; + + cap_table_entry = &hh_msgq_cap_table[client_desc->label]; + + if (mutex_lock_interruptible(&cap_table_entry->cap_entry_lock)) + return -ERESTARTSYS; + + if (cap_table_entry->client_desc != client_desc) { + pr_err("%s: Invalid client descriptor\n", __func__); + ret = -EINVAL; + goto err; + } + + if (!cap_table_entry->rx_cap_id) { + pr_err("%s: Recv info for label %d not yet initialized\n", + __func__, client_desc->label); + ret = -EAGAIN; + goto err; + } + + if (!cap_table_entry->rx_irq) { + pr_err("%s: Rx IRQ for label %d not yet setup\n", + __func__, client_desc->label); + ret = -EAGAIN; + goto err; + } + + mutex_unlock(&cap_table_entry->cap_entry_lock); + + *buff = kzalloc(HH_MSGQ_MAX_MSG_SIZE_BYTES, GFP_KERNEL); + if (!(*buff)) + return -ENOMEM; + + do { + if (cap_table_entry->rx_empty && (flags & HH_MSGQ_NONBLOCK)) { + ret = -EAGAIN; + goto buff_free; + } + + if (wait_event_interruptible(cap_table_entry->rx_wq, + !cap_table_entry->rx_empty)) { + ret = -ERESTARTSYS; + goto buff_free; + } + + ret = __hh_msgq_recv(cap_table_entry, *buff, size, flags); + } while (ret == -EAGAIN); + +buff_free: + if (ret < 0) { + kfree(*buff); + *buff = NULL; + } + + return ret; + +err: + mutex_unlock(&cap_table_entry->cap_entry_lock); + return ret; +} +EXPORT_SYMBOL(hh_msgq_recv); + +static int __hh_msgq_send(struct hh_msgq_cap_table *cap_table_entry, + void *buff, size_t size, u64 tx_flags) +{ + struct hh_hcall_msgq_send_resp resp = {}; + unsigned long flags; + int hh_ret; + int ret = 0; + + /* Discard the driver specific flags, and keep only HVC specifics */ + tx_flags &= HH_MSGQ_HVC_FLAGS_MASK; + + spin_lock_irqsave(&cap_table_entry->tx_lock, flags); + hh_ret = hh_hcall_msgq_send(cap_table_entry->tx_cap_id, + size, buff, tx_flags, &resp); + + switch (hh_ret) { + case HH_ERROR_MSGQUEUE_FULL: + cap_table_entry->tx_full = true; + ret = -EAGAIN; + break; + default: + ret = hh_remap_error(hh_ret); + } + + spin_unlock_irqrestore(&cap_table_entry->tx_lock, flags); + + if (ret != 0 && ret != -EAGAIN) + pr_err("%s: Failed to send the message. Error: %d\n", + __func__, hh_ret); + + return ret; +} + +/** + * hh_msgq_send: Send a message to the client on a different VM + * @client_desc: The client descriptor that was obtained via hh_msgq_register() + * @buff: Pointer to the buffer that needs to be sent. The buffer should be + * dynamically allocated via kmalloc/kzalloc. + * @size: The size of the buffer + * @flags: Optional flags to pass to send the data. For the list of flags, + * see linux/haven/hh_msgq.h + * + * The function would free the buffer upon success, and returns 0. The caller + * should not be referencing the buffer anymore. + * On the other hand, it returns -EINVAL if the caller passes invalid arguments, + * -EAGAIN if the message queue is not yet ready to communicate, and -EPERM if + * the caller doesn't have permissions to send the data. + * + */ +int hh_msgq_send(void *msgq_client_desc, + void *buff, size_t size, unsigned long flags) +{ + struct hh_msgq_desc *client_desc = msgq_client_desc; + struct hh_msgq_cap_table *cap_table_entry; + int ret; + + if (!client_desc || !buff || !size) + return -EINVAL; + + if (size > HH_MSGQ_MAX_MSG_SIZE_BYTES) + return -E2BIG; + + cap_table_entry = &hh_msgq_cap_table[client_desc->label]; + + if (mutex_lock_interruptible(&cap_table_entry->cap_entry_lock)) + return -ERESTARTSYS; + + if (cap_table_entry->client_desc != client_desc) { + pr_err("%s: Invalid client descriptor\n", __func__); + ret = -EINVAL; + goto err; + } + + if (!cap_table_entry->tx_cap_id) { + pr_err("%s: Send info for label %d not yet initialized\n", + __func__, client_desc->label); + ret = -EAGAIN; + goto err; + } + + if (!cap_table_entry->tx_irq) { + pr_err("%s: Tx IRQ for label %d not yet setup\n", + __func__, client_desc->label); + ret = -EAGAIN; + goto err; + } + + mutex_unlock(&cap_table_entry->cap_entry_lock); + + do { + if (cap_table_entry->tx_full && (flags & HH_MSGQ_NONBLOCK)) { + ret = -EAGAIN; + goto err; + } + + if (wait_event_interruptible(cap_table_entry->tx_wq, + !cap_table_entry->tx_full)) + return -ERESTARTSYS; + + ret = __hh_msgq_send(cap_table_entry, buff, size, flags); + } while (ret == -EAGAIN); + + /* If the send is success, hypervisor should not be holding any + * references to 'buff', and hence, can be released. + */ + if (!ret) + kfree(buff); + + return ret; +err: + mutex_unlock(&cap_table_entry->cap_entry_lock); + return ret; +} +EXPORT_SYMBOL(hh_msgq_send); + +/** + * hh_msgq_register: Register as a client to the use the message queue + * @label: The label associated to the message queue that the client wants + * to communicate + * + * The function returns a descriptor for the clients to send and receive the + * messages. Else, returns -EBUSY if some other client is already regitsered + * to this label, and -EINVAL for invalid arguments. The caller should check + * the return value using IS_ERR_OR_NULL() and PTR_ERR() to extract the error + * code. + */ +void *hh_msgq_register(enum hh_msgq_label label) +{ + struct hh_msgq_cap_table *cap_table_entry; + struct hh_msgq_desc *client_desc; + int ret; + + if (label < 0 || label >= HH_MSGQ_LABEL_MAX) + return ERR_PTR(-EINVAL); + + cap_table_entry = &hh_msgq_cap_table[label]; + + if (mutex_lock_interruptible(&cap_table_entry->cap_entry_lock)) + return ERR_PTR(-ERESTARTSYS); + + /* Multiple clients cannot register to the same label (msgq) */ + if (cap_table_entry->client_desc) { + ret = -EBUSY; + goto err; + } + + client_desc = kzalloc(sizeof(*client_desc), GFP_KERNEL); + if (!client_desc) { + ret = ENOMEM; + goto err; + } + + client_desc->label = label; + + cap_table_entry->client_desc = client_desc; + + pr_info("hh_msgq: Registered client for label: %d\n", label); + + mutex_unlock(&cap_table_entry->cap_entry_lock); + return client_desc; + +err: + mutex_unlock(&cap_table_entry->cap_entry_lock); + return ERR_PTR(ret); +} +EXPORT_SYMBOL(hh_msgq_register); + +/** + * hh_msgq_unregister: Unregister as a client to the use the message queue + * @client_desc: The descriptor that was passed via hh_msgq_register() + * + * The function returns 0 is the client was unregistered successfully. Else, + * -EINVAL for invalid arguments. + */ +int hh_msgq_unregister(void *msgq_client_desc) +{ + struct hh_msgq_desc *client_desc = msgq_client_desc; + struct hh_msgq_cap_table *cap_table_entry; + + if (!client_desc) + return -EINVAL; + + cap_table_entry = &hh_msgq_cap_table[client_desc->label]; + + if (mutex_lock_interruptible(&cap_table_entry->cap_entry_lock)) + return -ERESTARTSYS; + + /* Is the client trying to free someone else's msgq? */ + if (cap_table_entry->client_desc != client_desc) { + pr_err("%s: Trying to free invalid client descriptor!\n", + __func__); + mutex_unlock(&cap_table_entry->cap_entry_lock); + return -EINVAL; + } + + cap_table_entry->client_desc = NULL; + + mutex_unlock(&cap_table_entry->cap_entry_lock); + + pr_info("%s: Unregistered client for label: %d\n", + __func__, client_desc->label); + + kfree(client_desc); + + return 0; +} +EXPORT_SYMBOL(hh_msgq_unregister); + +int hh_msgq_populate_cap_info(enum hh_msgq_label label, u64 cap_id, + int direction, int irq) +{ + struct hh_msgq_cap_table *cap_table_entry; + int ret; + + if (label < 0 || label >= HH_MSGQ_LABEL_MAX) { + pr_err("%s: Invalid label passed\n", __func__); + return -EINVAL; + } + + if (irq < 0) { + pr_err("%s: Invalid IRQ number passed\n", __func__); + return -ENXIO; + } + + cap_table_entry = &hh_msgq_cap_table[label]; + + if (mutex_lock_interruptible(&cap_table_entry->cap_entry_lock)) + return -ERESTARTSYS; + + if (direction == HH_MSGQ_DIRECTION_TX) { + ret = request_irq(irq, hh_msgq_tx_isr, 0, + cap_table_entry->tx_irq_name, cap_table_entry); + if (ret < 0) + goto err; + + cap_table_entry->tx_irq = irq; + cap_table_entry->tx_cap_id = cap_id; + } else if (direction == HH_MSGQ_DIRECTION_RX) { + ret = request_irq(irq, hh_msgq_rx_isr, 0, + cap_table_entry->rx_irq_name, cap_table_entry); + if (ret < 0) + goto err; + + cap_table_entry->rx_cap_id = cap_id; + cap_table_entry->rx_irq = irq; + } else { + pr_err("%s: Invalid direction passed\n", __func__); + ret = -EINVAL; + goto err; + } + + mutex_unlock(&cap_table_entry->cap_entry_lock); + + pr_debug( + "%s: label: %d; cap_id: %llu; dir: %d; irq: %d\n", + __func__, label, cap_id, direction, irq); + + return 0; + +err: + mutex_unlock(&cap_table_entry->cap_entry_lock); + return ret; +} +EXPORT_SYMBOL(hh_msgq_populate_cap_info); + +static void hh_msgq_cleanup(int begin_idx) +{ + struct hh_msgq_cap_table *cap_table_entry; + int i; + + if (begin_idx >= HH_MSGQ_LABEL_MAX) + begin_idx = HH_MSGQ_LABEL_MAX - 1; + + for (i = begin_idx; i >= 0; i--) { + cap_table_entry = &hh_msgq_cap_table[i]; + + kfree(cap_table_entry->tx_irq_name); + kfree(cap_table_entry->rx_irq_name); + + mutex_destroy(&cap_table_entry->cap_entry_lock); + } +} + +static int __init hh_msgq_init(void) +{ + struct hh_msgq_cap_table *cap_table_entry; + int ret; + int i; + + for (i = 0; i < HH_MSGQ_LABEL_MAX; i++) { + cap_table_entry = &hh_msgq_cap_table[i]; + + cap_table_entry->tx_full = false; + cap_table_entry->rx_empty = true; + init_waitqueue_head(&cap_table_entry->tx_wq); + init_waitqueue_head(&cap_table_entry->rx_wq); + spin_lock_init(&cap_table_entry->tx_lock); + spin_lock_init(&cap_table_entry->rx_lock); + mutex_init(&cap_table_entry->cap_entry_lock); + + cap_table_entry->tx_irq_name = kasprintf(GFP_KERNEL, + "hh_msgq_tx_%d", i); + if (!cap_table_entry->tx_irq_name) { + ret = -ENOMEM; + goto err; + } + + cap_table_entry->rx_irq_name = kasprintf(GFP_KERNEL, + "hh_msgq_rx_%d", i); + if (!cap_table_entry->rx_irq_name) { + ret = -ENOMEM; + goto err; + } + } + + return 0; + +err: + hh_msgq_cleanup(i); + return ret; +} +module_init(hh_msgq_init); + +static void __exit hh_msgq_exit(void) +{ + hh_msgq_cleanup(HH_MSGQ_LABEL_MAX - 1); +} +module_exit(hh_msgq_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Haven Message Queue Driver"); diff --git a/include/linux/haven/hcall.h b/include/linux/haven/hcall.h new file mode 100644 index 000000000000..c39dd9d81893 --- /dev/null +++ b/include/linux/haven/hcall.h @@ -0,0 +1,273 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ +#ifndef __HH_HCALL_H +#define __HH_HCALL_H + +#include +#include + +#include +#include +#include + +struct hh_hcall_hyp_identify_resp { + u64 api_info; + u64 flags[3]; +}; + +static inline int hh_hcall_hyp_identify(struct hh_hcall_hyp_identify_resp *resp) +{ + int ret; + struct hh_hcall_resp _resp = {0}; + + ret = _hh_hcall(0x6000, + (struct hh_hcall_args){ 0 }, + &_resp); + + if (!ret && resp) { + resp->api_info = _resp.resp0; + resp->flags[0] = _resp.resp1; + resp->flags[1] = _resp.resp2; + resp->flags[2] = _resp.resp3; + } + + return ret < 0 ? ret : 0; +} + +static inline int hh_hcall_dbl_bind(hh_capid_t dbl_capid, hh_capid_t vic_capid, + hh_virq_handle_t virq_info) +{ + int ret; + struct hh_hcall_resp _resp = {0}; + + ret = _hh_hcall(0x6010, + (struct hh_hcall_args){ dbl_capid, vic_capid, virq_info }, + &_resp); + + return ret; +} + +static inline int hh_hcall_dbl_unbind(hh_capid_t dbl_capid) +{ + int ret; + struct hh_hcall_resp _resp = {0}; + + ret = _hh_hcall(0x6011, + (struct hh_hcall_args){ dbl_capid }, + &_resp); + + return ret; +} + +struct hh_hcall_dbl_send_resp { + u64 old_flags; +}; + +static inline int hh_hcall_dbl_send(hh_capid_t dbl_capid, + hh_dbl_flags_t new_flags, + struct hh_hcall_dbl_send_resp *resp) +{ + int ret; + struct hh_hcall_resp _resp = {0}; + + ret = _hh_hcall(0x6012, + (struct hh_hcall_args){ dbl_capid, new_flags }, + &_resp); + + if (!ret && resp) + resp->old_flags = _resp.resp1; + + return ret; +} + +struct hh_hcall_dbl_recv_resp { + u64 old_flags; +}; + +static inline int hh_hcall_dbl_recv(hh_capid_t dbl_capid, + hh_dbl_flags_t clear_flags, + struct hh_hcall_dbl_recv_resp *resp) +{ + int ret; + struct hh_hcall_resp _resp = {0}; + + ret = _hh_hcall(0x6013, + (struct hh_hcall_args){ dbl_capid, clear_flags }, + &_resp); + + if (!ret && resp) + resp->old_flags = _resp.resp1; + + return ret; +} + +static inline int hh_hcall_dbl_reset(hh_capid_t dbl_capid) +{ + int ret; + struct hh_hcall_resp _resp = {0}; + + ret = _hh_hcall(0x6014, + (struct hh_hcall_args){ dbl_capid }, + &_resp); + + return ret; +} + +static inline int hh_hcall_dbl_mask(hh_capid_t dbl_capid, + hh_dbl_flags_t enable_mask, + hh_dbl_flags_t ack_mask) +{ + int ret; + struct hh_hcall_resp _resp = {0}; + + ret = _hh_hcall(0x6015, + (struct hh_hcall_args){ dbl_capid, enable_mask, ack_mask }, + &_resp); + + return ret; +} + +static inline int hh_hcall_msgq_bind_send(hh_capid_t msgq_capid, + hh_capid_t vic_capid, + hh_virq_handle_t virq_info) +{ + int ret; + struct hh_hcall_resp _resp = {0}; + + ret = _hh_hcall(0x6017, + (struct hh_hcall_args){ msgq_capid, vic_capid, virq_info }, + &_resp); + + return ret; +} + +static inline int hh_hcall_msgq_bind_recv(hh_capid_t msgq_capid, + hh_capid_t vic_capid, + hh_virq_handle_t virq_info) +{ + int ret; + struct hh_hcall_resp _resp = {0}; + + ret = _hh_hcall(0x6018, + (struct hh_hcall_args){ msgq_capid, vic_capid, virq_info }, + &_resp); + + return ret; +} + +static inline int hh_hcall_msgq_unbind_send(hh_capid_t msgq_capid) +{ + int ret; + struct hh_hcall_resp _resp = {0}; + + ret = _hh_hcall(0x6019, + (struct hh_hcall_args){ msgq_capid }, + &_resp); + + return ret; +} + +static inline int hh_hcall_msgq_unbind_recv(hh_capid_t msgq_capid) +{ + int ret; + struct hh_hcall_resp _resp = {0}; + + ret = _hh_hcall(0x601A, + (struct hh_hcall_args){ msgq_capid }, + &_resp); + + return ret; +} + +struct hh_hcall_msgq_send_resp { + bool not_full; +}; + +static inline int hh_hcall_msgq_send(hh_capid_t msgq_capid, size_t size, + void *data, u64 send_flags, + struct hh_hcall_msgq_send_resp *resp) +{ + int ret; + struct hh_hcall_resp _resp = {0}; + + ret = _hh_hcall(0x601B, + (struct hh_hcall_args){ msgq_capid, size, (unsigned long)data, + send_flags }, + &_resp); + + if (resp) + resp->not_full = _resp.resp1; + + return ret; +} + +struct hh_hcall_msgq_recv_resp { + size_t recv_size; + bool not_empty; +}; + +static inline int hh_hcall_msgq_recv(hh_capid_t msgq_capid, void *buffer, + size_t max_size, + struct hh_hcall_msgq_recv_resp *resp) +{ + int ret; + struct hh_hcall_resp _resp = {0}; + + ret = _hh_hcall(0x601C, + (struct hh_hcall_args){ msgq_capid, (unsigned long)buffer, + max_size }, + &_resp); + + if (!ret && resp) { + resp->recv_size = _resp.resp1; + resp->not_empty = _resp.resp2; + } + + return ret; +} + +static inline int hh_hcall_msgq_flush(hh_capid_t msgq_capid) +{ + int ret; + struct hh_hcall_resp _resp = {0}; + + ret = _hh_hcall(0x601D, + (struct hh_hcall_args){ msgq_capid }, + &_resp); + + return ret; +} + +static inline int hh_hcall_msgq_configure_send(hh_capid_t msgq_capid, + long not_full_threshold, + long not_full_delay) +{ + int ret; + struct hh_hcall_resp _resp = {0}; + + ret = _hh_hcall(0x601F, + (struct hh_hcall_args){ msgq_capid, not_full_threshold, + not_full_delay, -1 }, + &_resp); + + return ret; +} + +static inline int hh_hcall_msgq_configure_recv(hh_capid_t msgq_capid, + long not_empty_threshold, + long not_empty_delay) +{ + int ret; + struct hh_hcall_resp _resp = {0}; + + ret = _hh_hcall(0x6020, + (struct hh_hcall_args){ msgq_capid, not_empty_threshold, + not_empty_delay, -1 }, + &_resp); + + return ret; +} + +#endif diff --git a/include/linux/haven/hcall_common.h b/include/linux/haven/hcall_common.h new file mode 100644 index 000000000000..f6ada3eabd0d --- /dev/null +++ b/include/linux/haven/hcall_common.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ +#ifndef __HH_HCALL_COMMON_H +#define __HH_HCALL_COMMON_H + +struct hh_hcall_args { + unsigned long arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7; +}; + +struct hh_hcall_resp { + unsigned long resp0, resp1, resp2, resp3, resp4, resp5, resp6, resp7; +}; + +typedef u16 hh_hcall_fnid_t; + +#endif diff --git a/include/linux/haven/hh_common.h b/include/linux/haven/hh_common.h new file mode 100644 index 000000000000..90e8ea72fed1 --- /dev/null +++ b/include/linux/haven/hh_common.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + */ + +#ifndef __HH_COMMON_H +#define __HH_COMMON_H + +/* Common Haven types */ +typedef u16 hh_vmid_t; +typedef u32 hh_rm_msgid_t; +typedef u32 hh_virq_handle_t; +typedef u32 hh_label_t; +typedef u64 hh_capid_t; +typedef u64 hh_dbl_flags_t; + +#endif diff --git a/include/linux/haven/hh_errno.h b/include/linux/haven/hh_errno.h new file mode 100644 index 000000000000..3ac33b991d13 --- /dev/null +++ b/include/linux/haven/hh_errno.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + */ + +#ifndef __HH_ERRNO_H +#define __HH_ERRNO_H + +#include + +#define HH_ERROR_OK 0 +#define HH_ERROR_UNIMPLEMENTED -1 + +#define HH_ERROR_ARG_INVAL 1 +#define HH_ERROR_ARG_SIZE 2 +#define HH_ERROR_ARG_ALIGN 3 + +#define HH_ERROR_NOMEM 10 + +#define HH_ERROR_ADDR_OVFL 20 +#define HH_ERROR_ADDR_UNFL 21 +#define HH_ERROR_ADDR_INVAL 22 + +#define HH_ERROR_DENIED 30 +#define HH_ERROR_BUSY 31 +#define HH_ERROR_IDLE 32 + +#define HH_ERROR_IRQ_BOUND 40 +#define HH_ERROR_IRQ_UNBOUND 41 + +#define HH_ERROR_CSPACE_CAP_NULL 50 +#define HH_ERROR_CSPACE_CAP_REVOKED 51 +#define HH_ERROR_CSPACE_WRONG_OBJ_TYPE 52 +#define HH_ERROR_CSPACE_INSUF_RIGHTS 53 +#define HH_ERROR_CSPACE_FULL 54 + +/* TODO: Get the correct values for the MSGQ error codes */ +#define HH_ERROR_MSGQUEUE_FULL 60 +#define HH_ERROR_MSGQUEUE_EMPTY 61 + +static inline int hh_remap_error(int hh_error) +{ + switch (hh_error) { + case HH_ERROR_OK: + return 0; + case HH_ERROR_NOMEM: + return -ENOMEM; + case HH_ERROR_DENIED: + case HH_ERROR_CSPACE_CAP_NULL: + case HH_ERROR_CSPACE_CAP_REVOKED: + case HH_ERROR_CSPACE_WRONG_OBJ_TYPE: + case HH_ERROR_CSPACE_INSUF_RIGHTS: + case HH_ERROR_CSPACE_FULL: + return -EACCES; + case HH_ERROR_BUSY: + case HH_ERROR_IDLE: + return -EBUSY; + case HH_ERROR_IRQ_BOUND: + case HH_ERROR_IRQ_UNBOUND: + case HH_ERROR_MSGQUEUE_FULL: + case HH_ERROR_MSGQUEUE_EMPTY: + return -EPERM; + case HH_ERROR_UNIMPLEMENTED: + case HH_ERROR_ARG_INVAL: + case HH_ERROR_ARG_SIZE: + case HH_ERROR_ARG_ALIGN: + case HH_ERROR_ADDR_OVFL: + case HH_ERROR_ADDR_UNFL: + case HH_ERROR_ADDR_INVAL: + default: + return -EINVAL; + } +} + +#endif diff --git a/include/linux/haven/hh_msgq.h b/include/linux/haven/hh_msgq.h new file mode 100644 index 000000000000..a179c773a3d7 --- /dev/null +++ b/include/linux/haven/hh_msgq.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + */ + +#ifndef __HH_MSGQ_H +#define __HH_MSGQ_H + +#include + +#include "hh_common.h" + +enum hh_msgq_label { + HH_MSGQ_LABEL_RM, + HH_MSGQ_LABEL_MAX +}; + +#define HH_MSGQ_MAX_MSG_SIZE_BYTES 240 + +#define HH_MSGQ_DIRECTION_TX 0 +#define HH_MSGQ_DIRECTION_RX 1 + +/* Possible flags to pass for Tx or Rx */ +#define HH_MSGQ_TX_PUSH BIT(0) +#define HH_MSGQ_NONBLOCK BIT(32) + +void *hh_msgq_register(enum hh_msgq_label label); +int hh_msgq_unregister(void *msgq_client_desc); +int hh_msgq_send(void *msgq_client_desc, + void *buff, size_t size, unsigned long flags); +int hh_msgq_recv(void *msgq_client_desc, + void **buff, size_t *size, unsigned long flags); + +int hh_msgq_populate_cap_info(enum hh_msgq_label label, u64 cap_id, + int direction, int irq); +#endif