Merge "soc: qcom: hab: add a dummy hypervisor"
diff --git a/drivers/soc/qcom/hab/Makefile b/drivers/soc/qcom/hab/Makefile
index 09f1447..235d303 100644
--- a/drivers/soc/qcom/hab/Makefile
+++ b/drivers/soc/qcom/hab/Makefile
@@ -26,6 +26,10 @@
msm_hab_hyp-objs = \
qvm_comm.o \
hab_qvm.o
+else
+msm_hab_hyp-objs = \
+ hab_comm.o \
+ hyp_stub.o
endif
endif
diff --git a/drivers/soc/qcom/hab/hab_comm.c b/drivers/soc/qcom/hab/hab_comm.c
new file mode 100644
index 0000000..689254f
--- /dev/null
+++ b/drivers/soc/qcom/hab/hab_comm.c
@@ -0,0 +1,270 @@
+/* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include "hab.h"
+
+struct loopback_msg {
+ struct list_head node;
+ int payload_size;
+ struct hab_header header;
+ char payload[];
+};
+
+struct lb_thread_struct {
+ int stop; /* set by creator */
+ int bexited; /* set by thread */
+ void *data; /* thread private data */
+};
+
+struct loopback_dev {
+ spinlock_t io_lock;
+ struct list_head msg_list;
+ int msg_cnt;
+ struct task_struct *kthread; /* creator's thread handle */
+ struct lb_thread_struct thread_data; /* thread private data */
+ wait_queue_head_t thread_queue;
+ struct loopback_msg *current_msg;
+};
+
+static int lb_thread_queue_empty(struct loopback_dev *dev)
+{
+ int ret;
+
+ spin_lock_bh(&dev->io_lock);
+ ret = list_empty(&dev->msg_list);
+ spin_unlock_bh(&dev->io_lock);
+ return ret;
+}
+
+
+int lb_kthread(void *d)
+{
+ struct lb_thread_struct *p = (struct lb_thread_struct *)d;
+ struct physical_channel *pchan = (struct physical_channel *)p->data;
+ struct loopback_dev *dev = pchan->hyp_data;
+ int ret = 0;
+
+ while (!p->stop) {
+ schedule();
+ ret = wait_event_interruptible(dev->thread_queue,
+ !lb_thread_queue_empty(dev) ||
+ p->stop);
+
+ spin_lock_bh(&dev->io_lock);
+
+ while (!list_empty(&dev->msg_list)) {
+ struct loopback_msg *msg = NULL;
+
+ msg = list_first_entry(&dev->msg_list,
+ struct loopback_msg, node);
+ dev->current_msg = msg;
+ list_del(&msg->node);
+ dev->msg_cnt--;
+
+ ret = hab_msg_recv(pchan, &msg->header);
+ if (ret) {
+ pr_err("failed %d msg handling sz %d header %d %d %d, %d %X %d, total %d\n",
+ ret, msg->payload_size,
+ HAB_HEADER_GET_ID(msg->header),
+ HAB_HEADER_GET_TYPE(msg->header),
+ HAB_HEADER_GET_SIZE(msg->header),
+ msg->header.session_id,
+ msg->header.signature,
+ msg->header.sequence, dev->msg_cnt);
+ }
+
+ kfree(msg);
+ dev->current_msg = NULL;
+ }
+
+ spin_unlock_bh(&dev->io_lock);
+ }
+ p->bexited = 1;
+ pr_debug("exit kthread\n");
+ return 0;
+}
+
+int physical_channel_send(struct physical_channel *pchan,
+ struct hab_header *header,
+ void *payload)
+{
+ int size = HAB_HEADER_GET_SIZE(*header); /* payload size */
+ struct timeval tv;
+ struct loopback_msg *msg = NULL;
+ struct loopback_dev *dev = pchan->hyp_data;
+
+ msg = kmalloc(size + sizeof(*msg), GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ memcpy(&msg->header, header, sizeof(*header));
+ msg->payload_size = size; /* payload size could be zero */
+
+ if (size && payload) {
+ if (HAB_HEADER_GET_TYPE(*header) == HAB_PAYLOAD_TYPE_PROFILE) {
+ struct habmm_xing_vm_stat *pstat =
+ (struct habmm_xing_vm_stat *)payload;
+
+ do_gettimeofday(&tv);
+ pstat->tx_sec = tv.tv_sec;
+ pstat->tx_usec = tv.tv_usec;
+ }
+
+ memcpy(msg->payload, payload, size);
+ }
+
+ spin_lock_bh(&dev->io_lock);
+ list_add_tail(&msg->node, &dev->msg_list);
+ dev->msg_cnt++;
+ spin_unlock_bh(&dev->io_lock);
+
+ wake_up_interruptible(&dev->thread_queue);
+ return 0;
+}
+
+/* loopback read is only used during open */
+int physical_channel_read(struct physical_channel *pchan,
+ void *payload,
+ size_t read_size)
+{
+ struct loopback_dev *dev = pchan->hyp_data;
+ struct loopback_msg *msg = dev->current_msg;
+
+ if (read_size) {
+ if (read_size != msg->payload_size) {
+ pr_err("read size mismatch requested %zd, received %d\n",
+ read_size, msg->payload_size);
+ memcpy(payload, msg->payload, min(((int)read_size),
+ msg->payload_size));
+ } else {
+ memcpy(payload, msg->payload, read_size);
+ }
+ } else {
+ read_size = 0;
+ }
+ return read_size;
+}
+
+/* pchan is directly added into the hab_device */
+int loopback_pchan_create(struct hab_device *dev, char *pchan_name)
+{
+ int result;
+ struct physical_channel *pchan = NULL;
+ struct loopback_dev *lb_dev = NULL;
+
+ pchan = hab_pchan_alloc(dev, LOOPBACK_DOM);
+ if (!pchan) {
+ result = -ENOMEM;
+ goto err;
+ }
+
+ pchan->closed = 0;
+ strlcpy(pchan->name, pchan_name, sizeof(pchan->name));
+
+ lb_dev = kzalloc(sizeof(*lb_dev), GFP_KERNEL);
+ if (!lb_dev) {
+ result = -ENOMEM;
+ goto err;
+ }
+
+ spin_lock_init(&lb_dev->io_lock);
+ INIT_LIST_HEAD(&lb_dev->msg_list);
+
+ init_waitqueue_head(&lb_dev->thread_queue);
+
+ lb_dev->thread_data.data = pchan;
+ lb_dev->kthread = kthread_run(lb_kthread, &lb_dev->thread_data,
+ pchan->name);
+ if (IS_ERR(lb_dev->kthread)) {
+ result = PTR_ERR(lb_dev->kthread);
+ pr_err("failed to create kthread for %s, ret %d\n",
+ pchan->name, result);
+ goto err;
+ }
+
+ pchan->hyp_data = lb_dev;
+
+ return 0;
+err:
+ kfree(lb_dev);
+ kfree(pchan);
+
+ return result;
+}
+
+void physical_channel_rx_dispatch(unsigned long data)
+{
+}
+
+int habhyp_commdev_alloc(void **commdev, int is_be, char *name,
+ int vmid_remote, struct hab_device *mmid_device)
+{
+ struct physical_channel *pchan;
+
+ int ret = loopback_pchan_create(mmid_device, name);
+
+ if (ret) {
+ pr_err("failed to create %s pchan in mmid device %s, ret %d, pchan cnt %d\n",
+ name, mmid_device->name, ret, mmid_device->pchan_cnt);
+ *commdev = NULL;
+ } else {
+ pr_debug("loopback physical channel on %s return %d, loopback mode(%d), total pchan %d\n",
+ name, ret, hab_driver.b_loopback,
+ mmid_device->pchan_cnt);
+ pchan = hab_pchan_find_domid(mmid_device,
+ HABCFG_VMID_DONT_CARE);
+ *commdev = pchan;
+ hab_pchan_put(pchan);
+
+ pr_debug("pchan %s vchans %d refcnt %d\n",
+ pchan->name, pchan->vcnt, get_refcnt(pchan->refcount));
+ }
+
+ return ret;
+}
+
+int habhyp_commdev_dealloc(void *commdev)
+{
+ struct physical_channel *pchan = commdev;
+ struct loopback_dev *dev = pchan->hyp_data;
+ struct loopback_msg *msg, *tmp;
+ int ret;
+
+ spin_lock_bh(&dev->io_lock);
+ if (!list_empty(&dev->msg_list) || dev->msg_cnt) {
+ pr_err("pchan %s msg leak cnt %d\n", pchan->name, dev->msg_cnt);
+
+ list_for_each_entry_safe(msg, tmp, &dev->msg_list, node) {
+ list_del(&msg->node);
+ dev->msg_cnt--;
+ kfree(msg);
+ }
+ pr_debug("pchan %s msg cnt %d now\n",
+ pchan->name, dev->msg_cnt);
+ }
+ spin_unlock_bh(&dev->io_lock);
+
+ dev->thread_data.stop = 1;
+ ret = kthread_stop(dev->kthread);
+ while (!dev->thread_data.bexited)
+ schedule();
+ dev->kthread = NULL;
+
+ /* hyp_data is freed in pchan */
+ if (get_refcnt(pchan->refcount) > 1) {
+ pr_warn("potential leak pchan %s vchans %d refcnt %d\n",
+ pchan->name, pchan->vcnt, get_refcnt(pchan->refcount));
+ }
+ hab_pchan_put((struct physical_channel *)commdev);
+
+ return 0;
+}
diff --git a/drivers/soc/qcom/hab/hyp_stub.c b/drivers/soc/qcom/hab/hyp_stub.c
new file mode 100644
index 0000000..c25bbe3
--- /dev/null
+++ b/drivers/soc/qcom/hab/hyp_stub.c
@@ -0,0 +1,25 @@
+/* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include "hab.h"
+
+int hab_hypervisor_register(void)
+{
+ hab_driver.b_loopback = 1;
+
+ return 0;
+}
+
+void hab_hypervisor_unregister(void)
+{
+ hab_hypervisor_unregister_common();
+}