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

Commit 6ec2f42b authored by Carl van Schaik's avatar Carl van Schaik Committed by Murali Nalajala
Browse files

drivers misc: add misc okl4 hypervisor drivers



Add miscellaneous drivers which serve as examples for
using okl4 hypervisor APIs.

Change-Id: I597f2d77bc5af2b7b05279364041b37c16b86b8b
Signed-off-by: default avatarCarl van Schaik <carl@cog.systems>
Git-commit: 312e4c5d63828fb7283bbea4416fabeb5979db36
Git-repo: https://github.com/CogSystems/linux-msm/commits/msm-4.9-hyp


[mnalajal@codeaurora: Resolve trivial merge conflicts]
Signed-off-by: default avatarMurali Nalajala <mnalajal@codeaurora.org>
parent 4b54d9aa
Loading
Loading
Loading
Loading
+43 −0
Original line number Diff line number Diff line
@@ -553,6 +553,40 @@ config QPNP_MISC
	  and the driver provides an API to check if this interrupt
	  is available on the current PMIC chip.

config OKL4_USER_VIRQ
	tristate "User space accessible OKL4 virtual interrupts"
	depends on OKL4_GUEST
	default n
	help
	  Say Y here if you want to enable the driver that exposes OKL4
	  virtual interrupts and virtual interrupt sources which enable
          inter-VM notifications.

config OKL4_RINGBUF
	tristate "Test driver for OKL4 inter-cell communication"
	depends on OKL4_GUEST
	default n
	help
	  Say Y here if you want to test communication between OKL4 guests
	  using virtual interrupts and shared memory.

config TEST_IRQ_REQUESTER
	tristate "Receive interrupt notifications in user-space"
	depends on DEBUG_FS
	default n
	help
	  This module allows interrupts to be registered from user-space by
	  writing to a debugfs file. If an interrupt registered by this
	  module triggers then a log message will be displayed. Note: This
	  driver is intented only for basic testing of interrupts. It not
	  designed to be a generic solution for providing interrupts to
	  user-space.

	  This module can be used to test client side vServices vGPIO
	  interrupt handling.

	  If in doubt, say N.

source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
@@ -568,6 +602,15 @@ source "drivers/misc/cxl/Kconfig"
source "drivers/misc/fpr_FingerprintCard/Kconfig"
endmenu

config OKL4_USER_VIPC
	bool "OKL4 Virtual Platform IPC Driver"
	depends on OKL4_GUEST
	default n
	help
	  Say Y here if you want to enable the driver which exposes the OKL4
	  Shared Buffer IPC mechanism for accessing and initializing buffers
	  and vIRQs associated with IPC between two cells.

config OKL4_LINK_SHBUF
	    tristate "OKL4 link with shared buffer transport"
	    default y
+6 −0
Original line number Diff line number Diff line
@@ -65,6 +65,10 @@ obj-$(CONFIG_UID_SYS_STATS) += uid_sys_stats.o
obj-$(CONFIG_MEMORY_STATE_TIME)	+= memory_state_time.o
obj-$(CONFIG_QPNP_MISC) 	+= qpnp-misc.o

obj-$(CONFIG_OKL4_USER_VIRQ)	+= okl4-virq.o
obj-$(CONFIG_OKL4_RINGBUF)	+= okl4-ringbuf.o
obj-$(CONFIG_TEST_IRQ_REQUESTER) += irq_requester.o

lkdtm-$(CONFIG_LKDTM)		+= lkdtm_core.o
lkdtm-$(CONFIG_LKDTM)		+= lkdtm_bugs.o
lkdtm-$(CONFIG_LKDTM)		+= lkdtm_heap.o
@@ -84,4 +88,6 @@ targets += lkdtm_rodata.o lkdtm_rodata_objcopy.o
$(obj)/lkdtm_rodata_objcopy.o: $(obj)/lkdtm_rodata.o FORCE
	$(call if_changed,objcopy)

obj-$(CONFIG_OKL4_USER_VIPC)    += okl4-vipc.o
obj-$(CONFIG_OKL4_GUEST)        += okl4-panic.o
obj-$(CONFIG_OKL4_LINK_SHBUF)    += okl4-link-shbuf.o
+37 −0
Original line number Diff line number Diff line
/*
 * OKL4 Debug panic handler
 *
 * Copyright (c) 2017 Cog Systems Pty Ltd.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published by
 * the Free Software Foundation.
 *
 * This registers a notifier that can trigger a KDB entry on panic, if enabled
 * on the kernel command line and running under a debug OKL4 kernel.
 */

#include <linux/kernel.h>
#include <linux/notifier.h>
#include <linux/init.h>

#include <microvisor/microvisor.h>

static int panic_kdb_interact(struct notifier_block *this, unsigned long event,
	 void *ptr)
{
	_okl4_sys_kdb_interact();

	return NOTIFY_DONE;
}

static struct notifier_block panic_block = {
	.notifier_call	= panic_kdb_interact,
};

static int __init setup_okl4_panic_kdb(char *buf)
{
	atomic_notifier_chain_register(&panic_notifier_list, &panic_block);
	return 0;
}
early_param("okl4_panic_kdb", setup_okl4_panic_kdb);
+629 −0
Original line number Diff line number Diff line
/*
 * Test driver for OKL4 Microvisor inter-cell communication
 *
 * Copyright (c) 2012-2014 General Dynamics
 * Copyright (c) 2014 Open Kernel Labs, Inc.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published by
 * the Free Software Foundation.
 */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/sysfs.h>
#include <linux/ioport.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/version.h>
#include <microvisor/microvisor.h>

/* Linux version compatibility */
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0)
#define DEVICE_ATTR_RO(_name) \
	struct device_attribute dev_attr_##_name = __ATTR_RO(_name)
#define OLD_ATTRS
#endif

struct ringbuf_data {
	struct platform_device *pdev;
	unsigned int irq;
	u32 virqline;
	struct mutex lock;

	struct resource buffer_res;
	void *buffer_map;

	u32 *head;
	u32 *tail;
	char *base;
	size_t size;

	size_t count;

	union {
		wait_queue_head_t tx_wq;
		struct work_struct rx_work;
	};

	struct cdev cdev;
};

dev_t ringbuf_tx_dev, ringbuf_rx_dev;

/* VIRQ payload flags */
#define VIRQ_FLAG_RESET 1UL /* Head has been set to 0 */
#define VIRQ_FLAG_DATA  2UL /* Head or tail has been advanced */

/*
 * Common probe / remove code
 */
static int ringbuf_probe(struct platform_device *pdev, const char *shbuf_name,
		const char *virqline_name, struct ringbuf_data **ringbuf_data)
{
	struct device_node *node = pdev->dev.of_node;
	struct device_node *virqline_node = NULL;
	struct ringbuf_data *priv;
	int r = 0;

	if (!node)
		return -ENODEV;
	if (of_property_match_string(node, "label", shbuf_name) < 0)
		return -ENODEV;

	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
	if (!priv)
		return -ENOMEM;

	priv->pdev = pdev;
	mutex_init(&priv->lock);

	r = of_address_to_resource(node, 0, &priv->buffer_res);
	if (r < 0)
		return r;

	/*
	 * For a useful ring buffer, we need enough space for the head
	 * and tail pointers, plus at least two bytes of data.
	 */
	if (resource_size(&priv->buffer_res) < (2 * sizeof(u32) + 2))
		return -ENODEV;

	/*
	 * Locate the virqline. Since we don't have a microvisor bus yet,
	 * we have to do this by searching the DT manually.
	 */
	/* FIXME: Jira ticket SDK-5565 - philipd. */
	for_each_compatible_node(virqline_node, NULL,
			"okl,microvisor-interrupt-line")
		if (of_property_match_string(virqline_node, "label",
					virqline_name) >= 0)
			break;
	if (!virqline_node)
		return -ENODEV;
	if (of_property_read_u32(virqline_node, "reg", &priv->virqline) < 0)
		return -ENODEV;

	/* Map the shared buffer's memory. */
	if (!devm_request_mem_region(&pdev->dev, priv->buffer_res.start,
			resource_size(&priv->buffer_res),
			dev_name(&pdev->dev)))
		return -EBUSY;

	dev_dbg(&pdev->dev, "Ring buffer @ 0x%llx, size 0x%llx\n",
			priv->buffer_res.start,
			resource_size(&priv->buffer_res));

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) || \
			defined(CONFIG_NO_DEPRECATED_IOREMAP)
        priv->buffer_map = ioremap_cache(priv->buffer_res.start,
			resource_size(&priv->buffer_res));
#else
        priv->buffer_map = ioremap_cached(priv->buffer_res.start,
			resource_size(&priv->buffer_res));
#endif
	if (priv->buffer_map == NULL)
		return -EIO;

	priv->head = (u32 *)priv->buffer_map;
	priv->tail = (u32 *)priv->buffer_map + 1;
	priv->base = (char *)priv->buffer_map + (2 * sizeof(u32));
	priv->size = resource_size(&priv->buffer_res) - (2 * sizeof(u32));
	priv->count = 0;

	dev_set_drvdata(&pdev->dev, priv);

	*ringbuf_data = priv;

	return 0;
}

static int ringbuf_remove(struct platform_device *pdev)
{
	struct ringbuf_data *priv = dev_get_drvdata(&pdev->dev);

	iounmap(priv->buffer_map);

	return 0;
}

/*
 * Sending side
 *
 * This registers a simple character device to accept writes from userland.
 * There is also a sysfs interface to ask the kernel to send arbitrary data.
 */
static size_t ringbuf_write_room(struct ringbuf_data *priv)
{
	return (priv->size + *priv->tail - *priv->head - 1) % priv->size;
}

static int ringbuf_tx_write(struct file *file, const char __user *buffer,
		size_t count, loff_t *ppos)
{
	struct inode *inode = file_inode(file);
	struct cdev *cdev = inode->i_cdev;
	struct ringbuf_data *priv;
	int r;
	u32 head, new_head;
	size_t to_send;

	priv = container_of(cdev, struct ringbuf_data, cdev);

	/* Truncate the message if it's too big. */
	count = min(count, priv->size - 1);

	/* Wait until there is space to write the data. */
	mutex_lock(&priv->lock);
	r = wait_event_interruptible(priv->tx_wq,
			ringbuf_write_room(priv) >= count);
	if (r < 0) {
		mutex_unlock(&priv->lock);
		return r;
	}

	/*
	 * Note: wait_event_* has an implicit memory barrier, which prevents the
	 * copy below beginning before the wait completes. This matches the
	 * mb() before the cmpxchg() at the end of the read.
	 */

	head = *priv->head;
	to_send = count;
	new_head = (head + to_send) % priv->size;

	if (new_head < head) {
		if (copy_from_user(&priv->base[head], buffer,
				priv->size - head)) {
			mutex_unlock(&priv->lock);
			return -EFAULT;
		}
		buffer += priv->size - head;
		to_send -= priv->size - head;
		head = 0;
	}

	if (to_send > 0) {
		if (copy_from_user(&priv->base[head], buffer, to_send)) {
			mutex_unlock(&priv->lock);
			return -EFAULT;
		}
	}

	/*
	 * Ensure that the other side sees the copy before the head update.
	 * This matches the rmb() at the start of the read.
	 */
	wmb();

	*priv->head = new_head;
	mutex_unlock(&priv->lock);

	_okl4_sys_vinterrupt_raise(priv->virqline, VIRQ_FLAG_DATA);

	return count;
}

static int ringbuf_tx_fsync(struct file *file, loff_t start, loff_t end,
		int datasync)
{
	struct inode *inode = file_inode(file);
	struct cdev *cdev = inode->i_cdev;
	struct ringbuf_data *priv;

	priv = container_of(cdev, struct ringbuf_data, cdev);

	/* Wait until the ring buffer has emptied. */
	return wait_event_interruptible(priv->tx_wq,
			*priv->head == *priv->tail);
}

static const struct file_operations ringbuf_tx_fops = {
	.owner	= THIS_MODULE,
	.write	= ringbuf_tx_write,
	.fsync	= ringbuf_tx_fsync,
};

static int ringbuf_tx_send_zeros(struct ringbuf_data *priv, size_t count)
{
	int r;
	u32 head, new_head;
	size_t to_send;

	/* Truncate the message if it's too big. */
	count = min(count, priv->size - 1);

	/* Wait until there is space to write the data. */
	mutex_lock(&priv->lock);
	r = wait_event_interruptible(priv->tx_wq,
			ringbuf_write_room(priv) >= count);
	if (r < 0) {
		mutex_unlock(&priv->lock);
		return r;
	}

	/*
	 * Note: wait_event_* has an implicit memory barrier, which prevents the
	 * copy below beginning before the wait completes. This matches the
	 * mb() before the cmpxchg() at the end of the read.
	 */

	head = *priv->head;
	to_send = count;
	new_head = (head + to_send) % priv->size;

	if (new_head < head) {
		memset(&priv->base[head], 0, priv->size - head);
		to_send -= priv->size - head;
		head = 0;
	}

	if (to_send > 0)
		memset(&priv->base[head], 0, to_send);

	/*
	 * Ensure that the other side sees the copy before the head update.
	 * This matches the rmb() at the start of the read.
	 */
	wmb();

	*priv->head = new_head;
	mutex_unlock(&priv->lock);

	_okl4_sys_vinterrupt_raise(priv->virqline, VIRQ_FLAG_DATA);

	return count;
}

static irqreturn_t ringbuf_tx_irq_handler(int irq, void *data)
{
	struct ringbuf_data *priv = data;

	wake_up_interruptible(&priv->tx_wq);

	return IRQ_HANDLED;
}

static ssize_t send_zeros_store(struct device *dev,
		struct device_attribute *attr, const char *buffer, size_t count)
{
	unsigned long val;
	struct ringbuf_data *priv = dev_get_drvdata(dev);
	int r;

	r = kstrtoul(buffer, 0, &val);
	if (r < 0)
		return -EINVAL;

	r = ringbuf_tx_send_zeros(priv, val);
	if (r < 0)
		return r;

	return count;
}

static ssize_t flush_store(struct device *dev,
		struct device_attribute *attr, const char *buffer, size_t count)
{
	struct ringbuf_data *priv = dev_get_drvdata(dev);

	/* Wait until the ring buffer has emptied. */
	return wait_event_interruptible(priv->tx_wq,
			*priv->head == *priv->tail);
}

#ifndef OLD_ATTRS
static DEVICE_ATTR_WO(send_zeros);
static DEVICE_ATTR_WO(flush);

static struct attribute *ringbuf_tx_test_attrs[] = {
	&dev_attr_send_zeros.attr,
	&dev_attr_flush.attr,
	NULL
};
ATTRIBUTE_GROUPS(ringbuf_tx_test);
#else
static struct device_attribute ringbuf_tx_test_attrs[] = {
	__ATTR(send_zeros, S_IWUGO, NULL, send_zeros_store),
	__ATTR(flush, S_IWUGO, NULL, flush_store),
	__ATTR_NULL
};
#endif

static struct class *ringbuf_tx_class;

static int ringbuf_tx_probe(struct platform_device *pdev)
{
	struct ringbuf_data *priv;
	int r;
	okl4_error_t err;
	struct device *child;

	r = ringbuf_probe(pdev, "ringbuf", "ringbuf_tx", &priv);
	if (r < 0)
		return r;

	/*
	 * Make the tail temporarily invalid to prevent the receiver getting
	 * confused when we clear head and tail, in case it's still reading data
	 * sent by a previous instance of the driver.
	 */
	*priv->tail = ~0;
	wmb();

	/*
	 * Zero the head and tail, and ensure that the writes are visible
	 * remotely (and in that order) before any new data is written.
	 */
	*priv->head = 0;
	wmb();
	*priv->tail = 0;
	wmb();

	init_waitqueue_head(&priv->tx_wq);

	cdev_init(&priv->cdev, &ringbuf_tx_fops);
	r = cdev_add(&priv->cdev, ringbuf_tx_dev, 1);
	if (r < 0)
		goto fail_cdev;

#ifndef OLD_ATTRS
	child = device_create_with_groups(ringbuf_tx_class, &pdev->dev,
                        ringbuf_tx_dev, priv, ringbuf_tx_test_groups, "ringbuf");
#else
	child = device_create(ringbuf_tx_class, &pdev->dev, ringbuf_tx_dev,
			priv, "ringbuf");
#endif
	if (IS_ERR(child)) {
		r = PTR_ERR(child);
		goto fail_device_create;
	}

	/* Hard-coded IRQ number; also hard-coded in system XML */
	/* FIXME: Jira ticket SDK-5566 - philipd. */
	priv->irq = 128;
	r = devm_request_irq(&pdev->dev, priv->irq, ringbuf_tx_irq_handler, 0,
			dev_name(&pdev->dev), priv);
	if (r < 0)
		goto fail_request_irq;

	/*
	 * Signal to the receiver that the head and tail have been reset.
	 * Note that the rx driver below doesn't actually make use of this.
	 */
	err = _okl4_sys_vinterrupt_raise(priv->virqline, VIRQ_FLAG_RESET);
	if (err != OKL4_OK) {
		r = -EIO;
		goto fail_raise;
	}

	return 0;

fail_request_irq:
	device_destroy(ringbuf_tx_class, ringbuf_tx_dev);
fail_device_create:
fail_raise:
	cdev_del(&priv->cdev);
fail_cdev:
	ringbuf_remove(pdev);

	return r;
}

static int ringbuf_tx_remove(struct platform_device *pdev)
{
	struct ringbuf_data *priv = dev_get_drvdata(&pdev->dev);

	device_destroy(ringbuf_tx_class, ringbuf_tx_dev);
	cdev_del(&priv->cdev);
	ringbuf_remove(pdev);

	return 0;
}

static const struct of_device_id of_platform_shared_buffer_table[] = {
	{ .compatible = "okl,ringbuffer" },
	{ /* end */ },
};
MODULE_DEVICE_TABLE(of, of_platform_shared_buffer_table);

static struct platform_driver of_platform_ringbuf_tx_driver = {
	.driver = {
		.name = "okl4_ringbuf_tx",
		.owner = THIS_MODULE,
		.of_match_table = of_platform_shared_buffer_table,
	},
	.probe = ringbuf_tx_probe,
	.remove = ringbuf_tx_remove,
};

static irqreturn_t ringbuf_rx_irq_handler(int irq, void *data)
{
	struct ringbuf_data *priv = data;

	schedule_work(&priv->rx_work);

	return IRQ_HANDLED;
}

/*
 * Read the incoming data, in order to measure the cost of doing so, and then
 * discard it.
 */
static void read_and_discard(const char *buf, size_t count)
{
	while (count && ((uintptr_t)buf & (sizeof(u32) - 1))) {
		const char *data = buf++;
		(void)ACCESS_ONCE(data);
		count--;
	}

	while (count > sizeof(u32)) {
		const u32 *data = (const u32 *)buf;
		(void)ACCESS_ONCE(data);
		count -= sizeof(u32);
		buf += sizeof(u32);
	}

	while (count) {
		const char *data = buf++;
		(void)ACCESS_ONCE(data);
		count--;
	}
}

static void ringbuf_rx_work(struct work_struct *work)
{
	struct ringbuf_data *priv = container_of(work, struct ringbuf_data,
			rx_work);
	u32 head, tail, old_tail;

	head = *priv->head;
	old_tail = tail = *priv->tail;

	/* If the tail is invalid, stop here */
	if (tail == ~0UL)
		return;

	/* Ensure we can see all data written before the head was updated */
	rmb();

	if (head < tail) {
		read_and_discard(&priv->base[tail], priv->size - tail);
		tail = 0;
	}

	if (head != tail) {
		read_and_discard(&priv->base[tail], head - tail);
		tail = head;
	}

	/* The tail update must occur after the data has been read. */
	mb();

	/*
	 * We must use cmpxchg to update the tail in case it was reset to 0
	 * by the sending side.
	 */
	if (cmpxchg(priv->tail, old_tail, tail) == old_tail) {
		_okl4_sys_vinterrupt_raise(priv->virqline, VIRQ_FLAG_DATA);
		priv->count += (priv->size + tail - old_tail) % priv->size;
	} else {
		dev_err(&priv->pdev->dev, "Read stale data!\n");
	}
}

static int ringbuf_rx_probe(struct platform_device *pdev)
{
	struct ringbuf_data *priv;
	int r;

	r = ringbuf_probe(pdev, "ringbuf_remote", "ringbuf_rx", &priv);
	if (r < 0)
		return r;

	INIT_WORK(&priv->rx_work, ringbuf_rx_work);

	/* Hard-coded IRQ number; also hard-coded in system XML */
	/* FIXME: Jira ticket SDK-5566 - philipd. */
	priv->irq = 129;
	r = devm_request_irq(&pdev->dev, priv->irq, ringbuf_rx_irq_handler, 0,
			dev_name(&pdev->dev), priv);
	if (r < 0) {
		ringbuf_remove(pdev);
		return r;
	}

	return 0;
}

static struct platform_driver of_platform_ringbuf_rx_driver = {
	.driver = {
		.name = "okl4_ringbuf_rx",
		.owner = THIS_MODULE,
		.of_match_table = of_platform_shared_buffer_table,
	},
	.probe = ringbuf_rx_probe,
	.remove = ringbuf_remove,
};

static int __init ringbuf_init(void)
{
	int err = 0;
	dev_t dev;

	err = alloc_chrdev_region(&dev, 0, 2, "ringbuf");
	if (err)
		goto fail_chrdev_region;

	ringbuf_tx_dev = dev;
	ringbuf_rx_dev = MKDEV(MAJOR(dev), MINOR(dev) + 1);

	ringbuf_tx_class = class_create(THIS_MODULE, "ringbuf_tx");
	if (!ringbuf_tx_class)
		goto fail_class_create;
#ifdef OLD_ATTRS
	ringbuf_tx_class->dev_attrs = ringbuf_tx_test_attrs;
#endif
	err = platform_driver_register(&of_platform_ringbuf_tx_driver);
	if (err)
		goto fail_tx_driver;

	err = platform_driver_register(&of_platform_ringbuf_rx_driver);
	if (err)
		goto fail_rx_driver;

	return 0;

fail_rx_driver:
	platform_driver_unregister(&of_platform_ringbuf_tx_driver);
fail_tx_driver:
	class_destroy(ringbuf_tx_class);
fail_class_create:
	unregister_chrdev_region(ringbuf_tx_dev, 2);
fail_chrdev_region:

	return err;
}
module_init(ringbuf_init);

static void __exit ringbuf_exit(void)
{
	platform_driver_unregister(&of_platform_ringbuf_rx_driver);
	platform_driver_unregister(&of_platform_ringbuf_tx_driver);
	class_destroy(ringbuf_tx_class);
	unregister_chrdev_region(ringbuf_tx_dev, 2);
}
module_exit(ringbuf_exit);

MODULE_AUTHOR("Philip Derrin <philipd@ok-labs.com>");
MODULE_DESCRIPTION("Test driver for OKL4 Microvisor inter-cell communication");
+651 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading