mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-09 13:49:24 -04:00
haven: Add Haven Resource Manager Driver
Add Haven Resource Manager driver to talk to the Resource Manager Virtual Machine for using the services such as resource (IRQ/device) sharing, obtaining info about IPC/signalling mechanisms, getting notifications about other VM's health, and so on. Change-Id: Ibff0fa3a2fef12276c1e9d7037b8bec3eb2efab1 Signed-off-by: Raghavendra Rao Ananta <rananta@codeaurora.org> Signed-off-by: Elliot Berman <eberman@codeaurora.org>
This commit is contained in:
parent
0ae181fd30
commit
7af086a1d3
6 changed files with 1207 additions and 0 deletions
|
|
@ -24,4 +24,17 @@ config HH_MSGQ
|
|||
the services offered by the drivers is simply to send and receive
|
||||
messages in a blocking manner.
|
||||
|
||||
config HH_RM_DRV
|
||||
tristate "Haven Resource Manager driver"
|
||||
help
|
||||
The Haven Resource Manager driver is used to communicate with the
|
||||
Resource Manager Virtual Machine (RM-VM). The RM-VM acts as a mediator
|
||||
and provides numerous services to the other VMs running in the system,
|
||||
such as notifying when a particular VM is up, resource (IRQ/device)
|
||||
sharing between VMs, information about the IPC mechanisms, and so on.
|
||||
|
||||
The Resource Manager driver runs on the Virtual Machine and acts as an
|
||||
interface to other driver in order to obtain the services provided by
|
||||
the RM-VM.
|
||||
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -1,2 +1,4 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-$(CONFIG_HH_MSGQ) += hh_msgq.o
|
||||
obj-$(CONFIG_HH_RM_DRV) += hh_rm_drv.o
|
||||
hh_rm_drv-y := hh_rm_core.o hh_rm_iface.o
|
||||
|
|
|
|||
807
drivers/virt/haven/hh_rm_core.c
Normal file
807
drivers/virt/haven/hh_rm_core.c
Normal file
|
|
@ -0,0 +1,807 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/of.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
#include <linux/haven/hh_msgq.h>
|
||||
#include <linux/haven/hh_errno.h>
|
||||
#include <linux/haven/hh_common.h>
|
||||
|
||||
#include "hh_rm_drv_private.h"
|
||||
|
||||
#define HH_RM_MAX_NUM_FRAGMENTS 62
|
||||
|
||||
#define GIC_V3_SPI_MAX 1019
|
||||
|
||||
#define HH_RM_NO_IRQ_ALLOC -1
|
||||
|
||||
#define HH_RM_MAX_MSG_SIZE_BYTES \
|
||||
(HH_MSGQ_MAX_MSG_SIZE_BYTES - sizeof(struct hh_rm_rpc_hdr))
|
||||
|
||||
struct hh_rm_connection {
|
||||
u16 seq;
|
||||
void *recv_buff;
|
||||
u32 reply_err_code;
|
||||
size_t recv_buff_size;
|
||||
|
||||
struct completion seq_done;
|
||||
|
||||
u8 num_fragments;
|
||||
u8 fragments_received;
|
||||
void *current_recv_buff;
|
||||
};
|
||||
|
||||
static struct task_struct *hh_rm_drv_recv_task;
|
||||
static struct hh_msgq_desc *hh_rm_msgq_desc;
|
||||
|
||||
static DEFINE_MUTEX(hh_rm_call_idr_lock);
|
||||
static DEFINE_IDR(hh_rm_call_idr);
|
||||
|
||||
static DEFINE_IDA(hh_rm_free_virq_ida);
|
||||
static struct device_node *hh_rm_intc;
|
||||
static u32 hh_rm_base_virq;
|
||||
|
||||
SRCU_NOTIFIER_HEAD_STATIC(hh_rm_notifier);
|
||||
|
||||
static void hh_rm_get_svm_res_work_fn(struct work_struct *work);
|
||||
static DECLARE_WORK(hh_rm_get_svm_res_work, hh_rm_get_svm_res_work_fn);
|
||||
|
||||
static int hh_rm_populate_hyp_res(void);
|
||||
|
||||
static struct hh_rm_connection *hh_rm_alloc_connection(int seq)
|
||||
{
|
||||
struct hh_rm_connection *connection;
|
||||
|
||||
connection = kzalloc(sizeof(*connection), GFP_KERNEL);
|
||||
if (!connection)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init_completion(&connection->seq_done);
|
||||
|
||||
if (seq > 0) {
|
||||
connection->seq = seq;
|
||||
return connection;
|
||||
}
|
||||
|
||||
/* Allocate a new seq number for this connection */
|
||||
if (mutex_lock_interruptible(&hh_rm_call_idr_lock)) {
|
||||
kfree(connection);
|
||||
return ERR_PTR(-ERESTARTSYS);
|
||||
}
|
||||
|
||||
connection->seq = idr_alloc_cyclic(&hh_rm_call_idr, connection,
|
||||
0, U16_MAX, GFP_KERNEL);
|
||||
mutex_unlock(&hh_rm_call_idr_lock);
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
static int
|
||||
hh_rm_init_connection_buff(struct hh_rm_connection *connection,
|
||||
void *recv_buff, size_t hdr_size,
|
||||
size_t payload_size)
|
||||
{
|
||||
struct hh_rm_rpc_hdr *hdr = recv_buff;
|
||||
|
||||
/* Some of the 'reply' types doesn't contain any payload */
|
||||
if (!payload_size)
|
||||
return 0;
|
||||
|
||||
/* If the data is split into multiple fragments, allocate a large
|
||||
* enough buffer to hold the payloads for all the fragments.
|
||||
*/
|
||||
connection->recv_buff = connection->current_recv_buff =
|
||||
kzalloc((HH_MSGQ_MAX_MSG_SIZE_BYTES - hdr_size) *
|
||||
(hdr->fragments + 1),
|
||||
GFP_KERNEL);
|
||||
if (!connection->recv_buff)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(connection->recv_buff, recv_buff + hdr_size, payload_size);
|
||||
connection->current_recv_buff += payload_size;
|
||||
connection->recv_buff_size = payload_size;
|
||||
connection->num_fragments = hdr->fragments;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hh_rm_register_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return srcu_notifier_chain_register(&hh_rm_notifier, nb);
|
||||
}
|
||||
EXPORT_SYMBOL(hh_rm_register_notifier);
|
||||
|
||||
int hh_rm_unregister_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return srcu_notifier_chain_unregister(&hh_rm_notifier, nb);
|
||||
}
|
||||
EXPORT_SYMBOL(hh_rm_unregister_notifier);
|
||||
|
||||
static int hh_rm_process_notif_vm_status(void *recv_buff, size_t recv_buff_size)
|
||||
{
|
||||
struct hh_rm_notif_vm_status_payload *vm_status_payload;
|
||||
|
||||
vm_status_payload = recv_buff + sizeof(struct hh_rm_rpc_hdr);
|
||||
|
||||
/* The VM is now booting. Collect it's info and
|
||||
* populate to other entities such as MessageQ and DBL
|
||||
*/
|
||||
if (vm_status_payload->vm_status == HH_RM_OS_STATUS_BOOT)
|
||||
return hh_rm_populate_hyp_res();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct hh_rm_connection *
|
||||
hh_rm_wait_for_notif_fragments(void *recv_buff, size_t recv_buff_size)
|
||||
{
|
||||
struct hh_rm_rpc_hdr *hdr = recv_buff;
|
||||
struct hh_rm_connection *connection;
|
||||
size_t payload_size;
|
||||
int ret = 0;
|
||||
|
||||
connection = hh_rm_alloc_connection(hdr->seq);
|
||||
if (IS_ERR_OR_NULL(connection))
|
||||
return connection;
|
||||
|
||||
payload_size = recv_buff_size - sizeof(*hdr);
|
||||
|
||||
ret = hh_rm_init_connection_buff(connection, recv_buff,
|
||||
sizeof(*hdr), payload_size);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if (wait_for_completion_interruptible(&connection->seq_done)) {
|
||||
ret = -ERESTARTSYS;
|
||||
goto out;
|
||||
}
|
||||
|
||||
return connection;
|
||||
|
||||
out:
|
||||
kfree(connection);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static int hh_rm_process_notif(void *recv_buff, size_t recv_buff_size)
|
||||
{
|
||||
struct hh_rm_connection *connection = NULL;
|
||||
struct hh_rm_rpc_hdr *hdr = recv_buff;
|
||||
u32 notification = hdr->msg_id;
|
||||
void *payload = NULL;
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("Notification received from RM-VM: %x\n", notification);
|
||||
|
||||
if (recv_buff_size > sizeof(*hdr))
|
||||
payload = recv_buff + sizeof(*hdr);
|
||||
|
||||
/* If the notification payload is split-up into
|
||||
* fragments, wait until all them arrive.
|
||||
*/
|
||||
if (hdr->fragments) {
|
||||
connection = hh_rm_wait_for_notif_fragments(recv_buff,
|
||||
recv_buff_size);
|
||||
if (IS_ERR_OR_NULL(connection))
|
||||
return PTR_ERR(connection);
|
||||
|
||||
/* In the case of fragments, the complete payload
|
||||
* is contained under connection->recv_buff
|
||||
*/
|
||||
payload = connection->recv_buff;
|
||||
recv_buff_size = connection->recv_buff_size;
|
||||
}
|
||||
|
||||
switch (notification) {
|
||||
case HH_RM_NOTIF_VM_STATUS:
|
||||
if (recv_buff_size != sizeof(*hdr) +
|
||||
sizeof(struct hh_rm_notif_vm_status_payload)) {
|
||||
pr_err("%s: Invalid size for VM_STATUS notif: %u\n",
|
||||
__func__, recv_buff_size - sizeof(*hdr));
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = hh_rm_process_notif_vm_status(recv_buff, recv_buff_size);
|
||||
break;
|
||||
case HH_RM_NOTIF_VM_IRQ_LENT:
|
||||
if (recv_buff_size != sizeof(*hdr) +
|
||||
sizeof(struct hh_rm_notif_vm_irq_lent_payload)) {
|
||||
pr_err("%s: Invalid size for VM_IRQ_LENT notif: %u\n",
|
||||
__func__, recv_buff_size - sizeof(*hdr));
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
break;
|
||||
case HH_RM_NOTIF_VM_IRQ_RELEASED:
|
||||
if (recv_buff_size != sizeof(*hdr) +
|
||||
sizeof(struct hh_rm_notif_vm_irq_released_payload)) {
|
||||
pr_err("%s: Invalid size for VM_IRQ_REL notif: %u\n",
|
||||
__func__, recv_buff_size - sizeof(*hdr));
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
break;
|
||||
case HH_RM_NOTIF_MEM_SHARED:
|
||||
if (recv_buff_size < sizeof(*hdr) +
|
||||
sizeof(struct hh_rm_notif_mem_shared_payload)) {
|
||||
pr_err("%s: Invalid size for MEM_SHARED notif: %u\n",
|
||||
__func__, recv_buff_size - sizeof(*hdr));
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
break;
|
||||
case HH_RM_NOTIF_MEM_RELEASED:
|
||||
if (recv_buff_size != sizeof(*hdr) +
|
||||
sizeof(struct hh_rm_notif_mem_released_payload)) {
|
||||
pr_err("%s: Invalid size for MEM_RELEASED notif: %u\n",
|
||||
__func__, recv_buff_size - sizeof(*hdr));
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
break;
|
||||
case HH_RM_NOTIF_VM_CONSOLE_CHARS:
|
||||
if (recv_buff_size < sizeof(*hdr) +
|
||||
sizeof(struct hh_rm_notif_vm_console_chars)) {
|
||||
struct hh_rm_notif_vm_console_chars *console_chars;
|
||||
u16 num_bytes;
|
||||
|
||||
console_chars = recv_buff + sizeof(*hdr);
|
||||
num_bytes = console_chars->num_bytes;
|
||||
|
||||
if (sizeof(*hdr) + sizeof(*console_chars) + num_bytes !=
|
||||
recv_buff_size) {
|
||||
pr_err("%s: Invalid size for VM_CONSOLE_CHARS notify %u\n",
|
||||
__func__, recv_buff_size - sizeof(*hdr));
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pr_err("%s: Unknown notification received: %u\n", __func__,
|
||||
notification);
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
srcu_notifier_call_chain(&hh_rm_notifier, notification, payload);
|
||||
|
||||
err:
|
||||
if (connection) {
|
||||
kfree(connection->recv_buff);
|
||||
kfree(connection);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hh_rm_process_rply(void *recv_buff, size_t recv_buff_size)
|
||||
{
|
||||
struct hh_rm_rpc_reply_hdr *reply_hdr = recv_buff;
|
||||
struct hh_rm_rpc_hdr *hdr = recv_buff;
|
||||
struct hh_rm_connection *connection;
|
||||
size_t payload_size;
|
||||
int ret = 0;
|
||||
|
||||
if (mutex_lock_interruptible(&hh_rm_call_idr_lock)) {
|
||||
ret = -ERESTARTSYS;
|
||||
goto out;
|
||||
}
|
||||
|
||||
connection = idr_find(&hh_rm_call_idr, hdr->seq);
|
||||
mutex_unlock(&hh_rm_call_idr_lock);
|
||||
|
||||
if (!connection || connection->seq != hdr->seq) {
|
||||
pr_err("%s: Failed to get the connection info for seq: %d\n",
|
||||
__func__, hdr->seq);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
payload_size = recv_buff_size - sizeof(*reply_hdr);
|
||||
|
||||
ret = hh_rm_init_connection_buff(connection, recv_buff,
|
||||
sizeof(*reply_hdr), payload_size);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
connection->reply_err_code = reply_hdr->err_code;
|
||||
|
||||
/*
|
||||
* If the data is composed of a single message, wakeup the
|
||||
* receiver immediately.
|
||||
*
|
||||
* Else, if the data is split into multiple fragments, fill
|
||||
* this buffer as and when the fragments arrive, and finally
|
||||
* wakeup the receiver upon reception of the last fragment.
|
||||
*/
|
||||
if (!hdr->fragments)
|
||||
complete(&connection->seq_done);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hh_rm_process_cont(void *recv_buff, size_t recv_buff_size)
|
||||
{
|
||||
struct hh_rm_rpc_hdr *hdr = recv_buff;
|
||||
struct hh_rm_connection *connection;
|
||||
size_t payload_size;
|
||||
|
||||
if (mutex_lock_interruptible(&hh_rm_call_idr_lock))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
connection = idr_find(&hh_rm_call_idr, hdr->seq);
|
||||
mutex_unlock(&hh_rm_call_idr_lock);
|
||||
|
||||
/* The seq number should be the same for all the fragments */
|
||||
if (!connection || connection->seq != hdr->seq) {
|
||||
pr_err("%s: Failed to get the connection info for seq: %d\n",
|
||||
__func__, hdr->seq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* hdr->fragments preserves the value from the first 'reply/notif'
|
||||
* message. For the sake of sanity, check if it's still intact.
|
||||
*/
|
||||
if (connection->num_fragments != hdr->fragments) {
|
||||
pr_err("%s: Number of fragments mismatch for seq: %d\n",
|
||||
__func__, hdr->seq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
payload_size = recv_buff_size - sizeof(*hdr);
|
||||
|
||||
/* Keep appending the data to the previous fragment's end */
|
||||
memcpy(connection->current_recv_buff,
|
||||
recv_buff + sizeof(*hdr), payload_size);
|
||||
connection->current_recv_buff += payload_size;
|
||||
connection->recv_buff_size += payload_size;
|
||||
|
||||
connection->fragments_received++;
|
||||
if (connection->fragments_received == connection->num_fragments)
|
||||
complete(&connection->seq_done);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct hh_rm_msgq_data {
|
||||
void *recv_buff;
|
||||
size_t recv_buff_size;
|
||||
struct work_struct recv_work;
|
||||
};
|
||||
|
||||
static void hh_rm_process_recv_work(struct work_struct *work)
|
||||
{
|
||||
struct hh_rm_msgq_data *msgq_data;
|
||||
struct hh_rm_rpc_hdr *hdr;
|
||||
void *recv_buff;
|
||||
|
||||
msgq_data = container_of(work, struct hh_rm_msgq_data, recv_work);
|
||||
recv_buff = hdr = msgq_data->recv_buff;
|
||||
|
||||
switch (hdr->type) {
|
||||
case HH_RM_RPC_TYPE_NOTIF:
|
||||
hh_rm_process_notif(recv_buff, msgq_data->recv_buff_size);
|
||||
break;
|
||||
case HH_RM_RPC_TYPE_RPLY:
|
||||
hh_rm_process_rply(recv_buff, msgq_data->recv_buff_size);
|
||||
break;
|
||||
case HH_RM_RPC_TYPE_CONT:
|
||||
hh_rm_process_cont(recv_buff, msgq_data->recv_buff_size);
|
||||
break;
|
||||
default:
|
||||
pr_err("%s: Invalid message type (%d) received\n",
|
||||
__func__, hdr->type);
|
||||
}
|
||||
|
||||
/* All the processing functions would have trimmed-off the header
|
||||
* and copied the data to connection->recv_buff. Hence, it's okay
|
||||
* to release the original packet that arrived.
|
||||
*/
|
||||
kfree(recv_buff);
|
||||
}
|
||||
|
||||
static int hh_rm_recv_task_fn(void *data)
|
||||
{
|
||||
struct hh_rm_msgq_data *msgq_data;
|
||||
size_t recv_buff_size;
|
||||
void *recv_buff;
|
||||
int ret;
|
||||
|
||||
while (!kthread_should_stop()) {
|
||||
/* Block until a new message is received */
|
||||
ret = hh_msgq_recv(hh_rm_msgq_desc, &recv_buff,
|
||||
&recv_buff_size, 0);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: Failed to receive the message: %d\n",
|
||||
__func__, ret);
|
||||
continue;
|
||||
} else if (recv_buff_size <= sizeof(struct hh_rm_rpc_hdr)) {
|
||||
pr_err("%s: Invalid message size received\n", __func__);
|
||||
kfree(recv_buff);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Process this message in the background, while leaving
|
||||
* this thread to receive the next message.
|
||||
*/
|
||||
msgq_data = kzalloc(sizeof(*msgq_data), GFP_KERNEL);
|
||||
if (!msgq_data) {
|
||||
kfree(recv_buff);
|
||||
continue;
|
||||
}
|
||||
|
||||
msgq_data->recv_buff = recv_buff;
|
||||
msgq_data->recv_buff_size = recv_buff_size;
|
||||
INIT_WORK(&msgq_data->recv_work, hh_rm_process_recv_work);
|
||||
|
||||
schedule_work(&msgq_data->recv_work);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hh_rm_send_request(u32 message_id,
|
||||
const void *req_buff, size_t req_buff_size,
|
||||
struct hh_rm_connection *connection)
|
||||
{
|
||||
size_t buff_size_remaining = req_buff_size;
|
||||
const void *req_buff_curr = req_buff;
|
||||
struct hh_rm_rpc_hdr *hdr;
|
||||
unsigned long tx_flags;
|
||||
u8 num_fragments = 0;
|
||||
size_t payload_size;
|
||||
void *send_buff;
|
||||
int i, ret;
|
||||
|
||||
num_fragments = (req_buff_size + HH_RM_MAX_MSG_SIZE_BYTES - 1) /
|
||||
HH_RM_MAX_MSG_SIZE_BYTES;
|
||||
|
||||
/* The above calculation also includes the count
|
||||
* for the 'request' packet. Exclude it as the
|
||||
* header needs to fill the num. of fragments to follow.
|
||||
*/
|
||||
num_fragments--;
|
||||
|
||||
if (num_fragments > HH_RM_MAX_NUM_FRAGMENTS) {
|
||||
pr_err("%s: Limit exceeded for the number of fragments: %u\n",
|
||||
__func__, num_fragments);
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
/* Consider also the 'request' packet for the loop count */
|
||||
for (i = 0; i <= num_fragments; i++) {
|
||||
if (buff_size_remaining > HH_RM_MAX_MSG_SIZE_BYTES) {
|
||||
payload_size = HH_RM_MAX_MSG_SIZE_BYTES;
|
||||
buff_size_remaining -= payload_size;
|
||||
} else {
|
||||
payload_size = buff_size_remaining;
|
||||
}
|
||||
|
||||
send_buff = kzalloc(sizeof(*hdr) + payload_size, GFP_KERNEL);
|
||||
if (!send_buff)
|
||||
return -ENOMEM;
|
||||
|
||||
hdr = send_buff;
|
||||
hdr->version = HH_RM_RPC_HDR_VERSION_ONE;
|
||||
hdr->hdr_words = HH_RM_RPC_HDR_WORDS;
|
||||
hdr->type = i == 0 ? HH_RM_RPC_TYPE_REQ : HH_RM_RPC_TYPE_CONT;
|
||||
hdr->fragments = num_fragments;
|
||||
hdr->seq = connection->seq;
|
||||
hdr->msg_id = message_id;
|
||||
|
||||
memcpy(send_buff + sizeof(*hdr), req_buff_curr, payload_size);
|
||||
req_buff_curr += payload_size;
|
||||
|
||||
/* Force the last fragment (or the request type)
|
||||
* to be sent immediately to the receiver
|
||||
*/
|
||||
tx_flags = (i == num_fragments) ? HH_MSGQ_TX_PUSH : 0;
|
||||
|
||||
ret = hh_msgq_send(hh_rm_msgq_desc, send_buff,
|
||||
sizeof(*hdr) + payload_size, tx_flags);
|
||||
if (ret) {
|
||||
kfree(send_buff);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* hh_rm_call: Achieve request-response type communication with RPC
|
||||
* @message_id: The RM RPC message-id
|
||||
* @req_buff: Request buffer that contains the payload
|
||||
* @req_buff_size: Total size of the payload
|
||||
* @resp_buff_size: Size of the response buffer
|
||||
* @reply_err_code: Returns Haven standard error code for the response
|
||||
*
|
||||
* Make a request to the RM-VM and expect a reply back. For a successful
|
||||
* response, the function returns the payload and its size for the response.
|
||||
* Some of the reply types doesn't contain any payload, in which case, the
|
||||
* caller would see a NULL returned. Hence, it's recommended that the caller
|
||||
* first read the error code and then dereference the returned payload
|
||||
* (if applicable). Also, the caller should kfree the returned pointer
|
||||
* when done.
|
||||
*/
|
||||
void *hh_rm_call(hh_rm_msgid_t message_id,
|
||||
void *req_buff, size_t req_buff_size,
|
||||
size_t *resp_buff_size, int *reply_err_code)
|
||||
{
|
||||
struct hh_rm_connection *connection;
|
||||
int req_ret;
|
||||
void *ret;
|
||||
|
||||
if (!message_id || !req_buff || !resp_buff_size || !reply_err_code)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
connection = hh_rm_alloc_connection(-1);
|
||||
if (IS_ERR_OR_NULL(connection))
|
||||
return connection;
|
||||
|
||||
/* Send the request to the Resource Manager VM */
|
||||
req_ret = hh_rm_send_request(message_id,
|
||||
req_buff, req_buff_size,
|
||||
connection);
|
||||
if (req_ret < 0) {
|
||||
ret = ERR_PTR(req_ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Wait for response */
|
||||
if (wait_for_completion_interruptible(&connection->seq_done)) {
|
||||
ret = ERR_PTR(-ERESTARTSYS);
|
||||
goto out;
|
||||
}
|
||||
|
||||
*reply_err_code = connection->reply_err_code;
|
||||
if (connection->reply_err_code) {
|
||||
pr_err("%s: Reply for seq:%d failed with err: %d\n",
|
||||
__func__, connection->seq, connection->reply_err_code);
|
||||
ret = ERR_PTR(hh_remap_error(connection->reply_err_code));
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = connection->recv_buff;
|
||||
*resp_buff_size = connection->recv_buff_size;
|
||||
|
||||
out:
|
||||
kfree(connection);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hh_rm_virq_to_linux_irq(u32 virq, u32 type, u32 trigger)
|
||||
{
|
||||
struct irq_fwspec fwspec = {};
|
||||
|
||||
fwspec.fwnode = of_node_to_fwnode(hh_rm_intc);
|
||||
fwspec.param_count = 3;
|
||||
fwspec.param[0] = type;
|
||||
fwspec.param[1] = virq;
|
||||
fwspec.param[2] = trigger;
|
||||
|
||||
return irq_create_fwspec_mapping(&fwspec);
|
||||
}
|
||||
|
||||
static int hh_rm_get_irq(struct hh_vm_get_hyp_res_resp_entry *res_entry)
|
||||
{
|
||||
int ret, virq = res_entry->virq;
|
||||
|
||||
/* For resources, such as DBL source, there's no IRQ. The virq_handle
|
||||
* wouldn't be defined for such cases. Hence ignore such cases
|
||||
*/
|
||||
if (!res_entry->virq_handle)
|
||||
return 0;
|
||||
|
||||
/* Allocate and bind a new IRQ if RM-VM hasn't already done already */
|
||||
if (virq == HH_RM_NO_IRQ_ALLOC) {
|
||||
/* Get the next free vIRQ.
|
||||
* Subtract 32 from the base virq to get the base SPI.
|
||||
*/
|
||||
ret = virq = ida_alloc_range(&hh_rm_free_virq_ida,
|
||||
hh_rm_base_virq - 32,
|
||||
GIC_V3_SPI_MAX, GFP_KERNEL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Bind the vIRQ */
|
||||
ret = hh_rm_vm_irq_accept(res_entry->virq_handle, virq);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
}
|
||||
|
||||
return hh_rm_virq_to_linux_irq(virq, GIC_SPI, IRQ_TYPE_LEVEL_HIGH);
|
||||
|
||||
err:
|
||||
ida_free(&hh_rm_free_virq_ida, virq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hh_rm_populate_hyp_res(void)
|
||||
{
|
||||
struct hh_vm_get_hyp_res_resp_entry *res_entries = NULL;
|
||||
int linux_irq, ret = 0;
|
||||
hh_capid_t cap_id;
|
||||
hh_label_t label;
|
||||
u32 n_res, i;
|
||||
|
||||
res_entries = hh_rm_vm_get_hyp_res(0, &n_res);
|
||||
if (IS_ERR_OR_NULL(res_entries))
|
||||
return PTR_ERR(res_entries);
|
||||
|
||||
for (i = 0; i < n_res; i++) {
|
||||
ret = linux_irq = hh_rm_get_irq(&res_entries[i]);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
cap_id = (u64) res_entries[i].cap_id_high << 32 |
|
||||
res_entries[i].cap_id_low;
|
||||
label = res_entries[i].resource_label;
|
||||
|
||||
/* Populate MessageQ & DBL's cap tables */
|
||||
/* TODO: Handle DBL */
|
||||
switch (res_entries[i].res_type) {
|
||||
case HH_RM_RES_TYPE_MQ_TX:
|
||||
ret = hh_msgq_populate_cap_info(label, cap_id,
|
||||
HH_MSGQ_DIRECTION_TX, linux_irq);
|
||||
break;
|
||||
case HH_RM_RES_TYPE_MQ_RX:
|
||||
ret = hh_msgq_populate_cap_info(label, cap_id,
|
||||
HH_MSGQ_DIRECTION_RX, linux_irq);
|
||||
break;
|
||||
case HH_RM_RES_TYPE_DB_TX:
|
||||
break;
|
||||
case HH_RM_RES_TYPE_DB_RX:
|
||||
break;
|
||||
default:
|
||||
pr_err("%s: Unknown resource type: %u\n",
|
||||
__func__, res_entries[i].res_type);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
kfree(res_entries);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void hh_rm_get_svm_res_work_fn(struct work_struct *work)
|
||||
{
|
||||
hh_rm_populate_hyp_res();
|
||||
}
|
||||
|
||||
static const struct of_device_id hh_rm_drv_of_match[] = {
|
||||
{ .compatible = "qcom,haven-resource-manager-1-0" },
|
||||
{ }
|
||||
};
|
||||
|
||||
static int hh_rm_drv_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *rm_tx_res, *rm_rx_res;
|
||||
int tx_irq, rx_irq;
|
||||
u32 owner_vmid;
|
||||
int ret;
|
||||
|
||||
rm_tx_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"rm-tx-cap-id");
|
||||
if (!rm_tx_res) {
|
||||
dev_err(&pdev->dev, "Failed to get the Tx cap-id\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rm_rx_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"rm-rx-cap-id");
|
||||
if (!rm_rx_res) {
|
||||
dev_err(&pdev->dev, "Failed to get the Rx cap-id\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tx_irq = platform_get_irq_byname(pdev, "rm-tx-irq");
|
||||
if (tx_irq < 0) {
|
||||
dev_err(&pdev->dev, "Failed to get the Tx IRQ. ret: %d\n",
|
||||
tx_irq);
|
||||
return tx_irq;
|
||||
}
|
||||
|
||||
rx_irq = platform_get_irq_byname(pdev, "rm-rx-irq");
|
||||
if (rx_irq < 0) {
|
||||
dev_err(&pdev->dev, "Failed to get the Rx IRQ. ret: %d\n",
|
||||
rx_irq);
|
||||
return rx_irq;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(pdev->dev.of_node, "qcom,virq-base",
|
||||
&hh_rm_base_virq)) {
|
||||
dev_err(&pdev->dev, "Failed to get the vIRQ base\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
hh_rm_intc = of_irq_find_parent(pdev->dev.of_node);
|
||||
if (!hh_rm_intc) {
|
||||
dev_err(&pdev->dev, "Failed to get the IRQ parent node\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
ret = hh_msgq_populate_cap_info(HH_MSGQ_LABEL_RM, rm_tx_res->start,
|
||||
HH_MSGQ_DIRECTION_TX, tx_irq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = hh_msgq_populate_cap_info(HH_MSGQ_LABEL_RM, rm_rx_res->start,
|
||||
HH_MSGQ_DIRECTION_RX, rx_irq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
hh_rm_msgq_desc = hh_msgq_register(HH_MSGQ_LABEL_RM);
|
||||
if (IS_ERR_OR_NULL(hh_rm_msgq_desc))
|
||||
return PTR_ERR(hh_rm_msgq_desc);
|
||||
|
||||
/* As we don't have a callback for message reception yet,
|
||||
* spawn a kthread and always listen to incoming messages.
|
||||
*/
|
||||
hh_rm_drv_recv_task = kthread_run(hh_rm_recv_task_fn,
|
||||
NULL, "hh_rm_recv_task");
|
||||
if (IS_ERR_OR_NULL(hh_rm_drv_recv_task)) {
|
||||
ret = PTR_ERR(hh_rm_drv_recv_task);
|
||||
goto err_recv_task;
|
||||
}
|
||||
|
||||
/* If the node has a "qcom,owner-vmid" property, it means that it's
|
||||
* a secondary-VM. Gain the info about it's resources here as there
|
||||
* won't be any explicit notification from the primary-VM.
|
||||
*/
|
||||
if (!of_property_read_u32(pdev->dev.of_node, "qcom,owner-vmid",
|
||||
&owner_vmid)) {
|
||||
schedule_work(&hh_rm_get_svm_res_work);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_recv_task:
|
||||
hh_msgq_unregister(hh_rm_msgq_desc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hh_rm_drv_remove(struct platform_device *pdev)
|
||||
{
|
||||
kthread_stop(hh_rm_drv_recv_task);
|
||||
hh_msgq_unregister(hh_rm_msgq_desc);
|
||||
idr_destroy(&hh_rm_call_idr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver hh_rm_driver = {
|
||||
.probe = hh_rm_drv_probe,
|
||||
.remove = hh_rm_drv_remove,
|
||||
.driver = {
|
||||
.name = "hh_rm_driver",
|
||||
.of_match_table = hh_rm_drv_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(hh_rm_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Haven Resource Mgr. Driver");
|
||||
139
drivers/virt/haven/hh_rm_drv_private.h
Normal file
139
drivers/virt/haven/hh_rm_drv_private.h
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __HH_RM_DRV_PRIVATE_H
|
||||
#define __HH_RM_DRV_PRIVATE_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <linux/haven/hh_rm_drv.h>
|
||||
#include <linux/haven/hh_common.h>
|
||||
|
||||
/* Resource Manager Header */
|
||||
struct hh_rm_rpc_hdr {
|
||||
u8 version:4,
|
||||
hdr_words:4;
|
||||
u8 type:2,
|
||||
fragments:6;
|
||||
u16 seq;
|
||||
u32 msg_id;
|
||||
} __packed;
|
||||
|
||||
/* Standard reply header */
|
||||
struct hh_rm_rpc_reply_hdr {
|
||||
struct hh_rm_rpc_hdr rpc_hdr;
|
||||
u32 err_code;
|
||||
} __packed;
|
||||
|
||||
/* RPC Header versions */
|
||||
#define HH_RM_RPC_HDR_VERSION_ONE 0x1
|
||||
|
||||
/* RPC Header words */
|
||||
#define HH_RM_RPC_HDR_WORDS 0x2
|
||||
|
||||
/* RPC Message types */
|
||||
#define HH_RM_RPC_TYPE_CONT 0x0
|
||||
#define HH_RM_RPC_TYPE_REQ 0x1
|
||||
#define HH_RM_RPC_TYPE_RPLY 0x2
|
||||
#define HH_RM_RPC_TYPE_NOTIF 0x3
|
||||
|
||||
/* RPC Message IDs */
|
||||
/* Call type Message IDs that has a request/reply pattern */
|
||||
/* Message IDs: Informative */
|
||||
#define HH_RM_RPC_MSG_ID_CALL_GET_IDENT 0x00000001
|
||||
#define HH_RM_RPC_MSG_ID_CALL_GET_FEATURES 0x00000002
|
||||
|
||||
/* Message IDs: Memory management */
|
||||
#define HH_RM_RPC_MSG_ID_CALL_MEM_ACCEPT 0x51000011
|
||||
#define HH_RM_RPC_MSG_ID_CALL_MEM_LEND 0x51000012
|
||||
#define HH_RM_RPC_MSG_ID_CALL_MEM_SHARE 0x51000013
|
||||
#define HH_RM_RPC_MSG_ID_CALL_MEM_RELEASE 0x51000014
|
||||
#define HH_RM_RPC_MSG_ID_CALL_MEM_RECLAIM 0x51000015
|
||||
#define HH_RM_RPC_MSG_ID_CALL_MEM_NOTIFY 0x51000017
|
||||
|
||||
/* Message IDs: extensions for hyp-assign */
|
||||
#define HH_RM_RPC_MSG_ID_CALL_MEM_QCOM_LOOKUP_SGL 0x5100001A
|
||||
|
||||
/* Message IDs: VM Management */
|
||||
#define HH_RM_RPC_MSG_ID_CALL_VM_ALLOCATE 0x56000001
|
||||
#define HH_RM_RPC_MSG_ID_CALL_VM_START 0x56000004
|
||||
|
||||
/* Message IDs: VM Query */
|
||||
#define HH_RM_RPC_MSG_ID_CALL_VM_GET_STATE 0x56000017
|
||||
#define HH_RM_RPC_MSG_ID_CALL_VM_GET_HYP_RESOURCES 0x56000020
|
||||
#define HH_RM_RPC_MSG_ID_CALL_VM_LOOKUP_HYP_CAPIDS 0x56000021
|
||||
#define HH_RM_RPC_MSG_ID_CALL_VM_LOOKUP_HYP_IRQS 0X56000022
|
||||
|
||||
/* Message IDs: VM Configuration */
|
||||
#define HH_RM_RPC_MSG_ID_CALL_VM_IRQ_ACCEPT 0x56000050
|
||||
#define HH_RM_RPC_MSG_ID_CALL_VM_IRQ_LEND 0x56000051
|
||||
#define HH_RM_RPC_MSG_ID_CALL_VM_IRQ_RELEASE 0x56000052
|
||||
#define HH_RM_RPC_MSG_ID_CALL_VM_IRQ_RECLAIM 0x56000053
|
||||
#define HH_RM_RPC_MSG_ID_CALL_VM_IRQ_NOTIFY 0x56000054
|
||||
#define HH_RM_RPC_MSG_ID_CALL_VM_IRQ_UNMAP 0x56000055
|
||||
|
||||
/* Message IDs: VM Services */
|
||||
#define HH_RM_RPC_MSG_ID_CALL_VM_SET_STATUS 0x56000080
|
||||
#define HH_RM_RPC_MSG_ID_CALL_VM_CONSOLE_OPEN 0x56000081
|
||||
#define HH_RM_RPC_MSG_ID_CALL_VM_CONSOLE_CLOSE 0x56000082
|
||||
#define HH_RM_RPC_MSG_ID_CALL_VM_CONSOLE_WRITE 0x56000083
|
||||
#define HH_RM_RPC_MSG_ID_CALL_VM_CONSOLE_FLUSH 0x56000084
|
||||
|
||||
/* Message IDs: VM-Host Query */
|
||||
#define HH_RM_RPC_MSG_ID_CALL_VM_HOST_GET_TYPE 0x560000A0
|
||||
|
||||
/* End Call type Message IDs */
|
||||
/* End RPC Message IDs */
|
||||
|
||||
/* Message ID headers */
|
||||
/* Call: VM_GET_HYP_RESOURCES */
|
||||
#define HH_RM_RES_TYPE_DB_TX 0
|
||||
#define HH_RM_RES_TYPE_DB_RX 1
|
||||
#define HH_RM_RES_TYPE_MQ_TX 2
|
||||
#define HH_RM_RES_TYPE_MQ_RX 3
|
||||
|
||||
struct hh_vm_get_hyp_res_req_payload {
|
||||
u16 vmid;
|
||||
u16 reserved;
|
||||
} __packed;
|
||||
|
||||
struct hh_vm_get_hyp_res_resp_entry {
|
||||
u8 res_type;
|
||||
u8 reserved;
|
||||
u16 partner_vmid;
|
||||
u32 resource_handle;
|
||||
u32 resource_label;
|
||||
u32 cap_id_low;
|
||||
u32 cap_id_high;
|
||||
u32 virq_handle;
|
||||
u32 virq;
|
||||
} __packed;
|
||||
|
||||
struct hh_vm_get_hyp_res_resp_payload {
|
||||
u32 n_resource_entries;
|
||||
struct hh_vm_get_hyp_res_resp_entry resp_entries[];
|
||||
} __packed;
|
||||
|
||||
/* Call: VM_IRQ_ACCEPT */
|
||||
struct hh_vm_irq_accept_req_payload {
|
||||
hh_virq_handle_t virq_handle;
|
||||
s32 virq;
|
||||
} __packed;
|
||||
|
||||
struct hh_vm_irq_accept_resp_payload {
|
||||
s32 virq;
|
||||
} __packed;
|
||||
|
||||
/* End Message ID headers */
|
||||
|
||||
/* Common function declerations */
|
||||
void *hh_rm_call(hh_rm_msgid_t message_id,
|
||||
void *req_buff, size_t req_buff_size,
|
||||
size_t *resp_buff_size, int *reply_err_code);
|
||||
struct hh_vm_get_hyp_res_resp_entry *
|
||||
hh_rm_vm_get_hyp_res(hh_vmid_t vmid, u32 *out_n_entries);
|
||||
int hh_msgq_populate_cap_info(enum hh_msgq_label label, u64 cap_id,
|
||||
int direction, int irq);
|
||||
#endif /* __HH_RM_DRV_PRIVATE_H */
|
||||
127
drivers/virt/haven/hh_rm_iface.c
Normal file
127
drivers/virt/haven/hh_rm_iface.c
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/haven/hh_msgq.h>
|
||||
#include <linux/haven/hh_common.h>
|
||||
|
||||
#include "hh_rm_drv_private.h"
|
||||
|
||||
/**
|
||||
* hh_rm_vm_get_hyp_res: Get info about a series of resources for this VM
|
||||
* @vmid: vmid whose info is needed. Pass 0 for self
|
||||
* @n_entries: The number of the resource entries that's returned to the caller
|
||||
*
|
||||
* The function returns an array of type 'struct hh_vm_get_hyp_res_resp_entry',
|
||||
* in which each entry specifies info about a particular resource. The number
|
||||
* of entries in the array is returned by 'n_entries'. The caller must kfree
|
||||
* the returned pointer when done.
|
||||
*
|
||||
* The function encodes the error codes via ERR_PTR. Hence, the caller is
|
||||
* responsible to check it with IS_ERR_OR_NULL().
|
||||
*/
|
||||
struct hh_vm_get_hyp_res_resp_entry *
|
||||
hh_rm_vm_get_hyp_res(hh_vmid_t vmid, u32 *n_entries)
|
||||
{
|
||||
struct hh_vm_get_hyp_res_resp_payload *resp_payload;
|
||||
struct hh_vm_get_hyp_res_req_payload req_payload = {
|
||||
.vmid = vmid
|
||||
};
|
||||
struct hh_vm_get_hyp_res_resp_entry *resp_entries;
|
||||
size_t resp_payload_size, resp_entries_size;
|
||||
int err, reply_err_code;
|
||||
|
||||
if (!n_entries)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
resp_payload = hh_rm_call(HH_RM_RPC_MSG_ID_CALL_VM_GET_HYP_RESOURCES,
|
||||
&req_payload, sizeof(req_payload),
|
||||
&resp_payload_size, &reply_err_code);
|
||||
if (reply_err_code || IS_ERR_OR_NULL(resp_payload)) {
|
||||
err = PTR_ERR(resp_payload);
|
||||
pr_err("%s: GET_HYP_RESOURCES failed with err: %d\n",
|
||||
__func__, err);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
/* The response payload should contain all the resource entries */
|
||||
if (resp_payload_size < sizeof(*n_entries) ||
|
||||
resp_payload_size != sizeof(*n_entries) +
|
||||
(resp_payload->n_resource_entries * sizeof(*resp_entries))) {
|
||||
pr_err("%s: Invalid size received for GET_HYP_RESOURCES: %u\n",
|
||||
__func__, resp_payload_size);
|
||||
resp_entries = ERR_PTR(-EINVAL);
|
||||
goto out;
|
||||
}
|
||||
|
||||
resp_entries_size = sizeof(*resp_entries) *
|
||||
resp_payload->n_resource_entries;
|
||||
resp_entries = kmemdup(resp_payload->resp_entries, resp_entries_size,
|
||||
GFP_KERNEL);
|
||||
if (!resp_entries) {
|
||||
resp_entries = ERR_PTR(-ENOMEM);
|
||||
goto out;
|
||||
}
|
||||
|
||||
*n_entries = resp_payload->n_resource_entries;
|
||||
|
||||
out:
|
||||
kfree(resp_payload);
|
||||
return resp_entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* hh_rm_vm_irq_accept: Bind the virq number to the supplied virq_handle
|
||||
* @virq_handle: The virtual IRQ handle (for example, obtained via
|
||||
* call to hh_rm_get_hyp_resources())
|
||||
* @virq: The virtual IRQ number to bind to. Note that this is the virtual
|
||||
* GIC IRQ number and not the linux IRQ number. Pass -1 here if the
|
||||
* caller wants the Resource Manager VM to allocate a number
|
||||
*
|
||||
* If provided -1 for virq, the function returns the new IRQ number, else
|
||||
* the one that was already provided.
|
||||
*
|
||||
* The function encodes the error codes via ERR_PTR. Hence, the caller is
|
||||
* responsible to check it with IS_ERR_OR_NULL().
|
||||
*/
|
||||
int hh_rm_vm_irq_accept(hh_virq_handle_t virq_handle, int virq)
|
||||
{
|
||||
struct hh_vm_irq_accept_resp_payload *resp_payload;
|
||||
struct hh_vm_irq_accept_req_payload req_payload;
|
||||
size_t resp_payload_size;
|
||||
int ret, reply_err_code;
|
||||
|
||||
/* -1 is valid for virq if requesting for a new number */
|
||||
if (virq < -1)
|
||||
return -EINVAL;
|
||||
|
||||
req_payload.virq_handle = virq_handle;
|
||||
req_payload.virq = virq;
|
||||
|
||||
resp_payload = hh_rm_call(HH_RM_RPC_MSG_ID_CALL_VM_IRQ_ACCEPT,
|
||||
&req_payload, sizeof(req_payload),
|
||||
&resp_payload_size, &reply_err_code);
|
||||
if (reply_err_code || IS_ERR_OR_NULL(resp_payload)) {
|
||||
ret = PTR_ERR(resp_payload);
|
||||
pr_err("%s: VM_IRQ_ACCEPT failed with err: %d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (virq == -1 && resp_payload_size != sizeof(*resp_payload)) {
|
||||
pr_err("%s: Invalid size received for VM_IRQ_ACCEPT: %u\n",
|
||||
__func__, resp_payload_size);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = virq == -1 ? resp_payload->virq : virq;
|
||||
out:
|
||||
kfree(resp_payload);
|
||||
return ret;
|
||||
}
|
||||
119
include/linux/haven/hh_rm_drv.h
Normal file
119
include/linux/haven/hh_rm_drv.h
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __HH_RM_DRV_H
|
||||
#define __HH_RM_DRV_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "hh_common.h"
|
||||
|
||||
/* Notification type Message IDs */
|
||||
/* Memory APIs */
|
||||
#define HH_RM_NOTIF_MEM_SHARED 0x51100011
|
||||
#define HH_RM_NOTIF_MEM_RELEASED 0x51100012
|
||||
|
||||
#define HH_RM_MEM_TYPE_NORMAL 0
|
||||
#define HH_RM_MEM_TYPE_IO 1
|
||||
|
||||
#define HH_RM_TRANS_TYPE_DONATE 0
|
||||
#define HH_RM_TRANS_TYPE_LEND 1
|
||||
#define HH_RM_TRANS_TYPE_SHARE 2
|
||||
|
||||
#define HH_RM_ACL_X BIT(0)
|
||||
#define HH_RM_ACL_W BIT(1)
|
||||
#define HH_RM_ACL_R BIT(2)
|
||||
|
||||
struct hh_rm_mem_shared_acl_entry;
|
||||
struct hh_rm_mem_shared_sgl_entry;
|
||||
struct hh_rm_mem_shared_attr_entry;
|
||||
|
||||
struct hh_rm_notif_mem_shared_payload {
|
||||
u32 mem_handle;
|
||||
u8 mem_type;
|
||||
u8 trans_type;
|
||||
u8 flags;
|
||||
u8 reserved1;
|
||||
u16 owner_vmid;
|
||||
u16 reserved2;
|
||||
u32 label;
|
||||
/* TODO: How to arrange multiple variable length struct arrays? */
|
||||
} __packed;
|
||||
|
||||
struct hh_rm_mem_shared_acl_entry {
|
||||
u16 acl_vmid;
|
||||
u8 acl_rights;
|
||||
u8 reserved;
|
||||
} __packed;
|
||||
|
||||
struct hh_rm_mem_shared_sgl_entry {
|
||||
u32 sgl_size_low;
|
||||
u32 sgl_size_high;
|
||||
} __packed;
|
||||
|
||||
struct hh_rm_mem_shared_attr_entry {
|
||||
u16 attributes;
|
||||
u16 attributes_vmid;
|
||||
} __packed;
|
||||
|
||||
struct hh_rm_notif_mem_released_payload {
|
||||
u32 mem_handle;
|
||||
u16 participant_vmid;
|
||||
u16 reserved;
|
||||
} __packed;
|
||||
|
||||
/* VM APIs */
|
||||
#define HH_RM_NOTIF_VM_STATUS 0x56100008
|
||||
#define HH_RM_NOTIF_VM_IRQ_LENT 0x56100011
|
||||
#define HH_RM_NOTIF_VM_IRQ_RELEASED 0x56100012
|
||||
|
||||
#define HH_RM_VM_STATUS_NO_STATE 0
|
||||
#define HH_RM_VM_STATUS_RUNNING 1
|
||||
#define HH_RM_VM_STATUS_PAUSED 2
|
||||
#define HH_RM_VM_STATUS_SHUTDOWN 3
|
||||
#define HH_RM_VM_STATUS_SHUTOFF 4
|
||||
#define HH_RM_VM_STATUS_CRASHED 5
|
||||
|
||||
#define HH_RM_OS_STATUS_NONE 0
|
||||
#define HH_RM_OS_STATUS_EARLY_BOOT 1
|
||||
#define HH_RM_OS_STATUS_BOOT 2
|
||||
#define HH_RM_OS_STATUS_INIT 3
|
||||
#define HH_RM_OS_STATUS_RUN 4
|
||||
|
||||
struct hh_rm_notif_vm_status_payload {
|
||||
u16 vmid;
|
||||
u16 reserved;
|
||||
u8 vm_status;
|
||||
u8 os_status;
|
||||
u16 app_status;
|
||||
} __packed;
|
||||
|
||||
struct hh_rm_notif_vm_irq_lent_payload {
|
||||
u16 owner_vmid;
|
||||
u32 virq_handle;
|
||||
u32 virq_label;
|
||||
} __packed;
|
||||
|
||||
struct hh_rm_notif_vm_irq_released_payload {
|
||||
u32 virq_handle;
|
||||
} __packed;
|
||||
|
||||
/* VM Services */
|
||||
#define HH_RM_NOTIF_VM_CONSOLE_CHARS 0X56100080
|
||||
|
||||
struct hh_rm_notif_vm_console_chars {
|
||||
u16 vmid;
|
||||
u16 num_bytes;
|
||||
u8 bytes[0];
|
||||
} __packed;
|
||||
|
||||
/* End Notification type APIs */
|
||||
|
||||
int hh_rm_register_notifier(struct notifier_block *nb);
|
||||
int hh_rm_unregister_notifier(struct notifier_block *nb);
|
||||
int hh_rm_vm_irq_accept(hh_virq_handle_t virq_handle, int virq);
|
||||
|
||||
#endif
|
||||
Loading…
Reference in a new issue