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

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

soc: qcom: hab: code refactor to support OS and Hypervisor variants better



HAB(Hypervisor ABstraction) driver supports inter-OS communication with the
underlying Hypervisor's mechanism in some virtualization platforms.

There are different OSes and Hypervisors where HAB can work on. The change
refactors the code to move those code different among different OSes and/or
Hypervisors into some new files.

Change-Id: I63637577adf49e5d01bc56442b43d79330c3e872
Signed-off-by: default avatarYong Ding <yongding@codeaurora.org>
parent b6fbaa1d
Loading
Loading
Loading
Loading
+11 −4
Original line number Diff line number Diff line
@@ -6,16 +6,21 @@ msm_hab-objs = \
	hab_pchan.o \
	hab_open.o \
	hab_mimex.o \
	hab_mem_linux.o \
	hab_pipe.o \
	hab_parser.o \
	khab_test.o \
	hab_stat.o

msm_hab_linux-objs = \
	hab_linux.o \
	hab_mem_linux.o

ifdef CONFIG_GHS_VMM
msm_hab_hyp-objs = \
	ghs_comm.o \
	hab_ghs.o
	ghs_comm_linux.o \
	hab_ghs.o \
	hab_ghs_linux.o

ifndef CONFIG_MSM_AGL
ccflags-y += -DHABMM_HC_VMID
@@ -25,7 +30,9 @@ else
ifdef CONFIG_QTI_GVM_QUIN
msm_hab_hyp-objs = \
	qvm_comm.o \
	hab_qvm.o
	qvm_comm_linux.o \
	hab_qvm.o \
	hab_qvm_linux.o
else
msm_hab_hyp-objs = \
	hab_comm.o \
@@ -33,4 +40,4 @@ msm_hab_hyp-objs = \
endif
endif

obj-$(CONFIG_MSM_HAB) += msm_hab.o msm_hab_hyp.o
obj-$(CONFIG_MSM_HAB) += msm_hab.o msm_hab_linux.o msm_hab_hyp.o
+28 −40
Original line number Diff line number Diff line
@@ -47,6 +47,13 @@ int physical_channel_send(struct physical_channel *pchan,

	hab_spin_lock(&dev->io_lock, irqs_disabled);

	result = hab_gipc_wait_to_send(dev->endpoint);
	if (result != GIPC_Success) {
		hab_spin_unlock(&dev->io_lock, irqs_disabled);
		pr_err("failed to wait to send %d\n", result);
		return -EBUSY;
	}

	result = GIPC_PrepareMessage(dev->endpoint, sizebytes+sizeof(*header),
		(void **)&msg);
	if (result == GIPC_Full) {
@@ -89,30 +96,15 @@ int physical_channel_send(struct physical_channel *pchan,
	return 0;
}

void physical_channel_rx_dispatch(unsigned long physical_channel)
void physical_channel_rx_dispatch_common(unsigned long physical_channel)
{
	struct hab_header header;
	struct physical_channel *pchan =
		(struct physical_channel *)physical_channel;
	struct ghs_vdev *dev = (struct ghs_vdev *)pchan->hyp_data;
	GIPC_Result result;

	uint32_t events;
	unsigned long flags;
	int irqs_disabled = irqs_disabled();

	spin_lock_irqsave(&pchan->rxbuf_lock, flags);
	events = kgipc_dequeue_events(dev->endpoint);
	spin_unlock_irqrestore(&pchan->rxbuf_lock, flags);

	if (events & (GIPC_EVENT_RESET))
		pr_err("hab gipc %s remote vmid %d RESET\n",
				dev->name, pchan->vmid_remote);
	if (events & (GIPC_EVENT_RESETINPROGRESS))
		pr_err("hab gipc %s remote vmid %d RESETINPROGRESS\n",
				dev->name, pchan->vmid_remote);

	if (events & (GIPC_EVENT_RECEIVEREADY)) {
	hab_spin_lock(&pchan->rxbuf_lock, irqs_disabled);
	while (1) {
		dev->read_size = 0;
@@ -137,7 +129,3 @@ void physical_channel_rx_dispatch(unsigned long physical_channel)
	}
	hab_spin_unlock(&pchan->rxbuf_lock, irqs_disabled);
}

	if (events & (GIPC_EVENT_SENDREADY))
		pr_debug("kgipc send ready\n");
}
+49 −0
Original line number Diff line number Diff line
/* Copyright (c) 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"
#include "hab_ghs.h"

inline int hab_gipc_wait_to_send(GIPC_Endpoint endpoint)
{
	(void)endpoint;

	return GIPC_Success;
}

void physical_channel_rx_dispatch(unsigned long physical_channel)
{
	struct physical_channel *pchan =
		(struct physical_channel *)physical_channel;
	struct ghs_vdev *dev = (struct ghs_vdev *)pchan->hyp_data;

	uint32_t events;
	unsigned long flags;

	spin_lock_irqsave(&pchan->rxbuf_lock, flags);
	events = kgipc_dequeue_events(dev->endpoint);
	spin_unlock_irqrestore(&pchan->rxbuf_lock, flags);

	if (events & (GIPC_EVENT_RESET))
		pr_err("hab gipc %s remote vmid %d RESET\n",
				dev->name, pchan->vmid_remote);
	if (events & (GIPC_EVENT_RESETINPROGRESS))
		pr_err("hab gipc %s remote vmid %d RESETINPROGRESS\n",
				dev->name, pchan->vmid_remote);

	if (events & (GIPC_EVENT_RECEIVEREADY))
		physical_channel_rx_dispatch_common(physical_channel);

	if (events & (GIPC_EVENT_SENDREADY))
		pr_debug("kgipc send ready\n");
}
+1 −367
Original line number Diff line number Diff line
/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
/* 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
@@ -999,11 +999,6 @@ int do_hab_parse(void)
	return result;
}

unsigned int get_refcnt(struct kref ref)
{
	return kref_read(&ref);
}

void hab_hypervisor_unregister_common(void)
{
	int status, i;
@@ -1040,364 +1035,3 @@ void hab_hypervisor_unregister_common(void)
	}
	spin_unlock_bh(&hab_driver.drvlock);
}

static int hab_open(struct inode *inodep, struct file *filep)
{
	int result = 0;
	struct uhab_context *ctx;

	ctx = hab_ctx_alloc(0);

	if (!ctx) {
		pr_err("hab_ctx_alloc failed\n");
		filep->private_data = NULL;
		return -ENOMEM;
	}

	ctx->owner = task_pid_nr(current);
	filep->private_data = ctx;
	pr_debug("ctx owner %d refcnt %d\n", ctx->owner,
			get_refcnt(ctx->refcount));

	return result;
}

static int hab_release(struct inode *inodep, struct file *filep)
{
	struct uhab_context *ctx = filep->private_data;
	struct virtual_channel *vchan, *tmp;
	struct hab_open_node *node;

	if (!ctx)
		return 0;

	pr_debug("inode %pK, filep %pK ctx %pK\n", inodep, filep, ctx);

	write_lock(&ctx->ctx_lock);
	/* notify remote side on vchan closing */
	list_for_each_entry_safe(vchan, tmp, &ctx->vchannels, node) {
		/* local close starts */
		vchan->closed = 1;

		list_del(&vchan->node); /* vchan is not in this ctx anymore */
		ctx->vcnt--;

		write_unlock(&ctx->ctx_lock);
		hab_vchan_stop_notify(vchan);
		hab_vchan_put(vchan); /* there is a lock inside */
		write_lock(&ctx->ctx_lock);
	}

	/* notify remote side on pending open */
	list_for_each_entry(node, &ctx->pending_open, node) {
		/* no touch to the list itself. it is allocated on the stack */
		if (hab_open_cancel_notify(&node->request))
			pr_err("failed to send open cancel vcid %x subid %d openid %d pchan %s\n",
					node->request.xdata.vchan_id,
					node->request.xdata.sub_id,
					node->request.xdata.open_id,
					node->request.pchan->habdev->name);
	}
	write_unlock(&ctx->ctx_lock);

	hab_ctx_put(ctx);
	filep->private_data = NULL;

	return 0;
}

static long hab_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
{
	struct uhab_context *ctx = (struct uhab_context *)filep->private_data;
	struct hab_open *open_param;
	struct hab_close *close_param;
	struct hab_recv *recv_param;
	struct hab_send *send_param;
	struct hab_info *info_param;
	struct hab_message *msg = NULL;
	void *send_data;
	unsigned char data[256] = { 0 };
	long ret = 0;
	char names[30];

	if (_IOC_SIZE(cmd) && (cmd & IOC_IN)) {
		if (_IOC_SIZE(cmd) > sizeof(data))
			return -EINVAL;

		if (copy_from_user(data, (void __user *)arg, _IOC_SIZE(cmd))) {
			pr_err("copy_from_user failed cmd=%x size=%d\n",
				cmd, _IOC_SIZE(cmd));
			return -EFAULT;
		}
	}

	switch (cmd) {
	case IOCTL_HAB_VC_OPEN:
		open_param = (struct hab_open *)data;
		ret = hab_vchan_open(ctx, open_param->mmid,
			&open_param->vcid,
			open_param->timeout,
			open_param->flags);
		break;
	case IOCTL_HAB_VC_CLOSE:
		close_param = (struct hab_close *)data;
		hab_vchan_close(ctx, close_param->vcid);
		break;
	case IOCTL_HAB_SEND:
		send_param = (struct hab_send *)data;
		if (send_param->sizebytes > HAB_HEADER_SIZE_MASK) {
			ret = -EINVAL;
			break;
		}

		send_data = kzalloc(send_param->sizebytes, GFP_KERNEL);
		if (!send_data) {
			ret = -ENOMEM;
			break;
		}

		if (copy_from_user(send_data, (void __user *)send_param->data,
				send_param->sizebytes)) {
			ret = -EFAULT;
		} else {
			ret = hab_vchan_send(ctx, send_param->vcid,
						send_param->sizebytes,
						send_data,
						send_param->flags);
		}
		kfree(send_data);
		break;
	case IOCTL_HAB_RECV:
		recv_param = (struct hab_recv *)data;
		if (!recv_param->data) {
			ret = -EINVAL;
			break;
		}

		ret = hab_vchan_recv(ctx, &msg, recv_param->vcid,
				&recv_param->sizebytes, recv_param->flags);

		if (ret == 0 && msg) {
			if (copy_to_user((void __user *)recv_param->data,
					msg->data,
					msg->sizebytes)) {
				pr_err("copy_to_user failed: vc=%x size=%d\n",
				   recv_param->vcid, (int)msg->sizebytes);
				recv_param->sizebytes = 0;
				ret = -EFAULT;
			}
		} else if (ret && msg) {
			pr_warn("vcid %X recv failed %d and msg is still of %zd bytes\n",
				recv_param->vcid, (int)ret, msg->sizebytes);
		}

		if (msg)
			hab_msg_free(msg);
		break;
	case IOCTL_HAB_VC_EXPORT:
		ret = hab_mem_export(ctx, (struct hab_export *)data, 0);
		break;
	case IOCTL_HAB_VC_IMPORT:
		ret = hab_mem_import(ctx, (struct hab_import *)data, 0);
		break;
	case IOCTL_HAB_VC_UNEXPORT:
		ret = hab_mem_unexport(ctx, (struct hab_unexport *)data, 0);
		break;
	case IOCTL_HAB_VC_UNIMPORT:
		ret = hab_mem_unimport(ctx, (struct hab_unimport *)data, 0);
		break;
	case IOCTL_HAB_VC_QUERY:
		info_param = (struct hab_info *)data;
		if (!info_param->names || !info_param->namesize ||
			info_param->namesize > sizeof(names)) {
			pr_err("wrong param for vm info vcid %X, names %llX, sz %d\n",
					info_param->vcid, info_param->names,
					info_param->namesize);
			ret = -EINVAL;
			break;
		}
		ret = hab_vchan_query(ctx, info_param->vcid,
				(uint64_t *)&info_param->ids,
				 names, info_param->namesize, 0);
		if (!ret) {
			if (copy_to_user((void __user *)info_param->names,
						 names,
						 info_param->namesize)) {
				pr_err("copy_to_user failed: vc=%x size=%d\n",
						info_param->vcid,
						info_param->namesize*2);
				info_param->namesize = 0;
				ret = -EFAULT;
			}
		}
		break;
	default:
		ret = -ENOIOCTLCMD;
	}

	if (_IOC_SIZE(cmd) && (cmd & IOC_OUT))
		if (copy_to_user((void __user *) arg, data, _IOC_SIZE(cmd))) {
			pr_err("copy_to_user failed: cmd=%x\n", cmd);
			ret = -EFAULT;
		}

	return ret;
}

static long hab_compat_ioctl(struct file *filep, unsigned int cmd,
	unsigned long arg)
{
	return hab_ioctl(filep, cmd, arg);
}

static const struct file_operations hab_fops = {
	.owner = THIS_MODULE,
	.open = hab_open,
	.release = hab_release,
	.mmap = habmem_imp_hyp_mmap,
	.unlocked_ioctl = hab_ioctl,
	.compat_ioctl = hab_compat_ioctl
};

/*
 * These map sg functions are pass through because the memory backing the
 * sg list is already accessible to the kernel as they come from a the
 * dedicated shared vm pool
 */

static int hab_map_sg(struct device *dev, struct scatterlist *sgl,
	int nelems, enum dma_data_direction dir,
	unsigned long attrs)
{
	/* return nelems directly */
	return nelems;
}

static void hab_unmap_sg(struct device *dev,
	struct scatterlist *sgl, int nelems,
	enum dma_data_direction dir,
	unsigned long attrs)
{
	/*Do nothing */
}

static const struct dma_map_ops hab_dma_ops = {
	.map_sg		= hab_map_sg,
	.unmap_sg	= hab_unmap_sg,
};

static int hab_power_down_callback(
		struct notifier_block *nfb, unsigned long action, void *data)
{

	switch (action) {
	case SYS_DOWN:
	case SYS_HALT:
	case SYS_POWER_OFF:
		pr_debug("reboot called %ld\n", action);
		hab_hypervisor_unregister(); /* only for single VM guest */
		break;
	}
	pr_debug("reboot called %ld done\n", action);
	return NOTIFY_DONE;
}

static struct notifier_block hab_reboot_notifier = {
	.notifier_call = hab_power_down_callback,
};

static int __init hab_init(void)
{
	int result;
	dev_t dev;

	result = alloc_chrdev_region(&hab_driver.major, 0, 1, "hab");

	if (result < 0) {
		pr_err("alloc_chrdev_region failed: %d\n", result);
		return result;
	}

	cdev_init(&hab_driver.cdev, &hab_fops);
	hab_driver.cdev.owner = THIS_MODULE;
	hab_driver.cdev.ops = &hab_fops;
	dev = MKDEV(MAJOR(hab_driver.major), 0);

	result = cdev_add(&hab_driver.cdev, dev, 1);

	if (result < 0) {
		unregister_chrdev_region(dev, 1);
		pr_err("cdev_add failed: %d\n", result);
		return result;
	}

	hab_driver.class = class_create(THIS_MODULE, "hab");

	if (IS_ERR(hab_driver.class)) {
		result = PTR_ERR(hab_driver.class);
		pr_err("class_create failed: %d\n", result);
		goto err;
	}

	hab_driver.dev = device_create(hab_driver.class, NULL,
					dev, &hab_driver, "hab");

	if (IS_ERR(hab_driver.dev)) {
		result = PTR_ERR(hab_driver.dev);
		pr_err("device_create failed: %d\n", result);
		goto err;
	}

	result = register_reboot_notifier(&hab_reboot_notifier);
	if (result)
		pr_err("failed to register reboot notifier %d\n", result);

	/* read in hab config, then configure pchans */
	result = do_hab_parse();

	if (!result) {
		hab_driver.kctx = hab_ctx_alloc(1);
		if (!hab_driver.kctx) {
			pr_err("hab_ctx_alloc failed");
			result = -ENOMEM;
			hab_hypervisor_unregister();
			goto err;
		} else
			set_dma_ops(hab_driver.dev, &hab_dma_ops);
	}
	hab_stat_init(&hab_driver);
	return result;

err:
	if (!IS_ERR_OR_NULL(hab_driver.dev))
		device_destroy(hab_driver.class, dev);
	if (!IS_ERR_OR_NULL(hab_driver.class))
		class_destroy(hab_driver.class);
	cdev_del(&hab_driver.cdev);
	unregister_chrdev_region(dev, 1);

	pr_err("Error in hab init, result %d\n", result);
	return result;
}

static void __exit hab_exit(void)
{
	dev_t dev;

	hab_hypervisor_unregister();
	hab_stat_deinit(&hab_driver);
	hab_ctx_put(hab_driver.kctx);
	dev = MKDEV(MAJOR(hab_driver.major), 0);
	device_destroy(hab_driver.class, dev);
	class_destroy(hab_driver.class);
	cdev_del(&hab_driver.cdev);
	unregister_chrdev_region(dev, 1);
	unregister_reboot_notifier(&hab_reboot_notifier);
	pr_debug("hab exit called\n");
}

subsys_initcall(hab_init);
module_exit(hab_exit);

MODULE_DESCRIPTION("Hypervisor abstraction layer");
MODULE_LICENSE("GPL v2");
+6 −33
Original line number Diff line number Diff line
@@ -13,39 +13,7 @@
#ifndef __HAB_H
#define __HAB_H

#ifdef pr_fmt
#undef pr_fmt
#endif
#define pr_fmt(fmt) "hab:%s:%d " fmt, __func__, __LINE__

#include <linux/types.h>

#include <linux/habmm.h>
#include <linux/hab_ioctl.h>

#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/cdev.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/rbtree.h>
#include <linux/idr.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include <linux/dma-direction.h>
#include <linux/dma-mapping.h>
#include <linux/jiffies.h>
#include <linux/reboot.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/delay.h>
#include "hab_os.h"	/* OS-specific part in the core header file */

enum hab_payload_type {
	HAB_PAYLOAD_TYPE_MSG = 0x0,
@@ -520,12 +488,16 @@ static inline void hab_ctx_put(struct uhab_context *ctx)
}

void hab_send_close_msg(struct virtual_channel *vchan);

int hab_hypervisor_register(void);
int hab_hypervisor_register_os(void);
void hab_hypervisor_unregister(void);
void hab_hypervisor_unregister_common(void);
int habhyp_commdev_alloc(void **commdev, int is_be, char *name,
		int vmid_remote, struct hab_device *mmid_device);
int habhyp_commdev_dealloc(void *commdev);
void habhyp_commdev_dealloc_os(void *commdev);
int habhyp_commdev_create_dispatcher(struct physical_channel *pchan);

int physical_channel_read(struct physical_channel *pchan,
		void *payload,
@@ -536,6 +508,7 @@ int physical_channel_send(struct physical_channel *pchan,
		void *payload);

void physical_channel_rx_dispatch(unsigned long physical_channel);
void physical_channel_rx_dispatch_common(unsigned long physical_channel);

int loopback_pchan_create(struct hab_device *dev, char *pchan_name);

Loading