Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cb31bb4d authored by Yong Ding's avatar Yong Ding
Browse files

soc: qcom: hab: add a dummy hypervisor



With such a dummy hypervisor, HAB driver can still be compiled
when no real hypervisor relevant files are available.

Moreover, it supports the connection creation between two local
processes over HAB socket pair and the message transaction over
such connection.

Change-Id: Ic9102ac3ce067ae8981b185c23bfadef3b4a020c
Signed-off-by: default avatarYong Ding <yongding@codeaurora.org>
parent 208f9b46
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -26,6 +26,10 @@ ifdef CONFIG_QTI_GVM_QUIN
msm_hab_hyp-objs = \
	qvm_comm.o \
	hab_qvm.o
else
msm_hab_hyp-objs = \
	hab_comm.o \
	hyp_stub.o
endif
endif

+270 −0
Original line number Diff line number Diff line
/* 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;
}
+25 −0
Original line number Diff line number Diff line
/* 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();
}