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

Commit 3e91991e authored by qctecmdr Service's avatar qctecmdr Service Committed by Gerrit - the friendly Code Review server
Browse files

Merge "soc: qcom: hab: add a dummy hypervisor"

parents 341f4114 cb31bb4d
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();
}