aboutsummaryrefslogtreecommitdiff
path: root/kvm/drivers/hypercall.c
diff options
context:
space:
mode:
Diffstat (limited to 'kvm/drivers/hypercall.c')
-rw-r--r--kvm/drivers/hypercall.c304
1 files changed, 294 insertions, 10 deletions
diff --git a/kvm/drivers/hypercall.c b/kvm/drivers/hypercall.c
index 9c9462f66..e5f4c8b56 100644
--- a/kvm/drivers/hypercall.c
+++ b/kvm/drivers/hypercall.c
@@ -6,11 +6,12 @@
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/completion.h>
+#include <linux/interrupt.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
-#define HYPERCALL_DRIVER_NAME "Qumranet hypercall driver"
+#define HYPERCALL_DRIVER_NAME "Qumranet_hypercall_driver"
#define HYPERCALL_DRIVER_VERSION "1"
#define PCI_VENDOR_ID_HYPERCALL 0x5002
#define PCI_DEVICE_ID_HYPERCALL 0x2258
@@ -43,26 +44,68 @@ static struct pci_device_id hypercall_pci_tbl[] = {
};
MODULE_DEVICE_TABLE (pci, hypercall_pci_tbl);
+
+
+/****** Hypercall device definitions ***************/
+/* To be moved into a shared file with user space */
+#define HP_CMD 0x00 // The command register WR
+#define HP_ISRSTATUS 0x04 // Interrupt status reg RD
+#define HP_TXSIZE 0x08
+#define HP_TXBUFF 0x0c
+#define HP_RXSIZE 0x10
+#define HP_RXBUFF 0x14
+
+// HP_CMD register commands
+#define HP_CMD_DI 1 // disable interrupts
+#define HP_CMD_EI 2 // enable interrupts
+#define HP_CMD_INIT 4 // reset device
+#define HP_CMD_RESET (HP_CMD_INIT|HP_CMD_DI)
+
+/* Bits in HP_ISR - Interrupt status register */
+#define HPISR_RX 0x01 // Data is ready to be read
+
+#define HP_MEM_SIZE 0xE0
+/******* End of Hypercall device definitions */
+
+/* read PIO/MMIO register */
+#define HIO_READ8(reg, ioaddr) ioread8(ioaddr + (reg))
+#define HIO_READ16(reg, ioaddr) ioread16(ioaddr + (reg))
+#define HIO_READ32(reg, ioaddr) ioread32(ioaddr + (reg))
+
+/* write PIO/MMIO register */
+#define HIO_WRITE8(reg, val8, ioaddr) iowrite8((val8), ioaddr + (reg))
+#define HIO_WRITE16(reg, val16, ioaddr) iowrite16((val16), ioaddr + (reg))
+#define HIO_WRITE32(reg, val32, ioaddr) iowrite32((val32), ioaddr + (reg))
+
+
struct hypercall_dev {
struct pci_dev *pci_dev;
+ struct kobject kobject;
u32 state;
spinlock_t lock;
u8 name[128];
u16 irq;
u32 regs_len;
- void __iomem *mmio_addr;
+ void __iomem *io_addr;
unsigned long base_addr; /* device I/O address */
+ unsigned long cmd;
};
-
+static int hypercall_close(struct hypercall_dev* dev);
+static int hypercall_open(struct hypercall_dev *dev);
static void hypercall_cleanup_dev(struct hypercall_dev *dev);
+static irqreturn_t hypercall_interrupt(int irq, void *dev_instance,
+ struct pt_regs *regs);
+
+static void __exit hypercall_sysfs_remove(struct hypercall_dev *dev);
+static int hypercall_sysfs_add(struct hypercall_dev *dev);
static int __devinit hypercall_init_board(struct pci_dev *pdev,
struct hypercall_dev **dev_out)
{
- unsigned long *ioaddr;
+ unsigned long ioaddr;
struct hypercall_dev *dev;
int rc;
u32 disable_dev_on_err = 0;
@@ -101,17 +144,17 @@ static int __devinit hypercall_init_board(struct pci_dev *pdev,
if (rc)
goto err_out;
- pci_set_master (pdev);
-
#define USE_IO_OPS 1
#ifdef USE_IO_OPS
- ioaddr = pci_iomap(pdev, 0, 0);
+ ioaddr = (unsigned long)pci_iomap(pdev, 0, 0);
+ //ioaddr = ioport_map(pio_start, pio_len);
if (!ioaddr) {
printk(KERN_ERR "%s: cannot map PIO, aborting\n", pci_name(pdev));
rc = -EIO;
goto err_out;
}
- dev->base_addr = (unsigned long)ioaddr;
+ dev->base_addr = (unsigned long)pio_start;
+ dev->io_addr = (void*)ioaddr;
dev->regs_len = pio_len;
#else
ioaddr = pci_iomap(pdev, 1, 0);
@@ -121,6 +164,7 @@ static int __devinit hypercall_init_board(struct pci_dev *pdev,
goto err_out;
}
dev->base_addr = ioaddr;
+ dev->io_addr = (void*)ioaddr;
dev->regs_len = mmio_len;
#endif /* USE_IO_OPS */
@@ -161,7 +205,13 @@ static int __devinit hypercall_init_one(struct pci_dev *pdev,
spin_lock_init(&dev->lock);
pci_set_drvdata(pdev, dev);
- printk (KERN_INFO "%s: 0x%lx, IRQ %d\n", dev->name, dev->base_addr, dev->irq);
+ printk (KERN_INFO "name=%s: base_addr=0x%lx, io_addr=0x%lx, IRQ=%d\n",
+ dev->name, dev->base_addr, (unsigned long)dev->io_addr, dev->irq);
+ hypercall_open(dev);
+
+ if (hypercall_sysfs_add(dev) != 0)
+ return -1;
+
return 0;
}
@@ -171,10 +221,111 @@ static void __devexit hypercall_remove_one(struct pci_dev *pdev)
assert(dev != NULL);
+ hypercall_close(dev);
+ hypercall_sysfs_remove(dev);
hypercall_cleanup_dev(dev);
pci_disable_device(pdev);
}
+static int hypercall_tx(struct hypercall_dev *dev, unsigned char *buf, size_t len)
+{
+ void __iomem *ioaddr = (void __iomem*)dev->io_addr;
+ int i;
+
+ if (len > HP_MEM_SIZE)
+ return -EINVAL;
+
+ spin_lock(&dev->lock);
+ HIO_WRITE8(HP_TXSIZE, len, ioaddr);
+ for (i=0; i< len; i++)
+ HIO_WRITE8(HP_TXBUFF, buf[i], ioaddr);
+ spin_unlock(&dev->lock);
+
+ return 0;
+}
+
+/*
+ * The interrupt handler does all of the rx work and cleans up
+ * after the tx
+ */
+static irqreturn_t hypercall_interrupt(int irq, void *dev_instance,
+ struct pt_regs *regs)
+{
+ struct hypercall_dev *dev = (struct hypercall_dev *)dev_instance;
+ void __iomem *ioaddr = (void __iomem*)dev->io_addr;
+ u32 status;
+ int irq_handled = IRQ_NONE;
+ int rx_buf_size;
+ int i;
+ u8 buffer[HP_MEM_SIZE];
+ u8 *pbuf;
+
+ DPRINTK("base addr is 0x%lx, io_addr=0x%lx\n", dev->base_addr, (long)dev->io_addr);
+
+ spin_lock(&dev->lock);
+ status = HIO_READ8(HP_ISRSTATUS, ioaddr);
+ DPRINTK("irq status is 0x%x\n", status);
+
+ /* shared irq? */
+ if (unlikely((status & HPISR_RX) == 0)) {
+ DPRINTK("not handeling irq, not ours\n");
+ goto out;
+ }
+
+ /* Disable device interrupts */
+ HIO_WRITE8(HP_CMD, HP_CMD_DI, ioaddr);
+ DPRINTK("disable device interrupts\n");
+
+ rx_buf_size = HIO_READ8(HP_RXSIZE, ioaddr);
+ DPRINTK("Rx buffer size is %d\n", rx_buf_size);
+
+ if (rx_buf_size > HP_MEM_SIZE)
+ rx_buf_size = HP_MEM_SIZE;
+
+ for (i=0, pbuf=buffer; i<rx_buf_size; i++, pbuf++) {
+ *pbuf = HIO_READ8(HP_RXBUFF, ioaddr + i);
+ DPRINTK("Read 0x%x as dword %d\n", *pbuf, i);
+ }
+ *pbuf = '\0';
+ DPRINTK("Read buffer %s", (char*)buffer);
+
+ HIO_WRITE8(HP_CMD, HP_CMD_EI, ioaddr);
+ DPRINTK("Enable interrupt\n");
+ irq_handled = IRQ_HANDLED;
+ out:
+ spin_unlock(&dev->lock);
+
+
+ hypercall_tx(dev, "hello host", sizeof("hello host"));
+ return irq_handled;
+}
+
+
+static int hypercall_open(struct hypercall_dev *dev)
+{
+ int rc;
+
+ rc = request_irq(dev->irq, &hypercall_interrupt,
+ SA_SHIRQ, dev->name, dev);
+ if (rc) {
+ printk(KERN_ERR "%s failed to request an irq\n", __FUNCTION__);
+ return rc;
+ }
+
+ //hypercall_thread_start(dev);
+
+ return 0;
+}
+
+static int hypercall_close(struct hypercall_dev* dev)
+{
+ //hypercall_thread_stop(dev);
+ synchronize_irq(dev->irq);
+ free_irq(dev->irq, dev);
+
+ return 0;
+}
+
#ifdef CONFIG_PM
static int hypercall_suspend(struct pci_dev *pdev, pm_message_t state)
@@ -201,7 +352,8 @@ static void hypercall_cleanup_dev(struct hypercall_dev *dev)
{
DPRINTK("cleaning up\n");
pci_release_regions(dev->pci_dev);
- pci_iounmap(dev->pci_dev, (void*)dev->base_addr);
+ pci_iounmap(dev->pci_dev, (void*)dev->io_addr);
+ pci_set_drvdata (dev->pci_dev, NULL);
kfree(dev);
}
@@ -227,5 +379,137 @@ static void __exit hypercall_cleanup_module(void)
pci_unregister_driver(&hypercall_pci_driver);
}
+/*
+ * sysfs support
+ */
+
+struct hypercall_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct hypercall_dev*, char *buf);
+ ssize_t (*store)(struct hypercall_dev*, unsigned long val);
+};
+
+static ssize_t hypercall_attribute_show(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ struct hypercall_attribute *hypercall_attr;
+ struct hypercall_dev *hdev;
+
+ hypercall_attr = container_of(attr, struct hypercall_attribute, attr);
+ hdev = container_of(kobj, struct hypercall_dev, kobject);
+
+ if (!hypercall_attr->show)
+ return -EIO;
+
+ return hypercall_attr->show(hdev, buf);
+}
+
+static ssize_t hypercall_attribute_store(struct kobject *kobj,
+ struct attribute *attr, const char *buf, size_t count)
+{
+ struct hypercall_attribute *hypercall_attr;
+ struct hypercall_dev *hdev;
+ char *endp;
+ unsigned long val;
+ int rc;
+
+ val = simple_strtoul(buf, &endp, 0);
+
+ hypercall_attr = container_of(attr, struct hypercall_attribute, attr);
+ hdev = container_of(kobj, struct hypercall_dev, kobject);
+
+ if (!hypercall_attr->store)
+ return -EIO;
+
+ rc = hypercall_attr->store(hdev, val);
+ if (!rc)
+ rc = count;
+ return rc;
+}
+
+#define MAKE_HYPERCALL_R_ATTR(_name) \
+static ssize_t _name##_show(struct hypercall_dev *hdev, char *buf) \
+{ \
+ return sprintf(buf, "%lu\n", (unsigned long)hdev->_name); \
+} \
+struct hypercall_attribute hypercall_attr_##_name = __ATTR_RO(_name)
+
+#define MAKE_HYPERCALL_WR_ATTR(_name) \
+static int _name##_store(struct hypercall_dev *hdev, unsigned long val) \
+{ \
+ hdev->_name = (typeof(hdev->_name))val; \
+ return 0; \
+} \
+static ssize_t _name##_show(struct hypercall_dev *hdev, char *buf) \
+{ \
+ return sprintf(buf, "%lu\n", (unsigned long)hdev->_name); \
+} \
+struct hypercall_attribute hypercall_attr_##_name = \
+ __ATTR(_name,S_IRUGO|S_IWUGO,_name##_show,_name##_store)
+
+MAKE_HYPERCALL_R_ATTR(base_addr);
+MAKE_HYPERCALL_R_ATTR(irq);
+MAKE_HYPERCALL_WR_ATTR(cmd);
+
+#define GET_HYPERCALL_ATTR(_name) (&hypercall_attr_##_name.attr)
+
+static struct attribute *hypercall_default_attrs[] = {
+ GET_HYPERCALL_ATTR(base_addr),
+ GET_HYPERCALL_ATTR(irq),
+ GET_HYPERCALL_ATTR(cmd),
+ NULL
+};
+
+static struct sysfs_ops hypercall_sysfs_ops = {
+ .show = hypercall_attribute_show,
+ .store = hypercall_attribute_store,
+};
+
+static void hypercall_sysfs_release(struct kobject *kobj)
+{
+ DPRINTK(" called for obj name %s\n", kobj->name);
+}
+
+static struct kobj_type hypercall_ktype = {
+ .release = hypercall_sysfs_release,
+ .sysfs_ops = &hypercall_sysfs_ops,
+ .default_attrs = hypercall_default_attrs
+};
+
+
+static int hypercall_sysfs_add(struct hypercall_dev *dev)
+{
+ int rc;
+
+ kobject_init(&dev->kobject);
+ dev->kobject.ktype = &hypercall_ktype;
+ rc = kobject_set_name(&dev->kobject, "%s", HYPERCALL_DRIVER_NAME);
+ if (rc != 0) {
+ printk("%s: kobject_set_name failed, err=%d\n", __FUNCTION__, rc);
+ return rc;
+ }
+
+ rc = kobject_add(&dev->kobject);
+ if (rc != 0) {
+ printk("%s: kobject_add failed, err=%d\n", __FUNCTION__, rc);
+ return rc;
+ }
+
+ rc = sysfs_create_link(&dev->pci_dev->dev.kobj, &dev->kobject,
+ HYPERCALL_DRIVER_NAME);
+ if (rc != 0) {
+ printk("%s: sysfs_create_link failed, err=%d\n", __FUNCTION__, rc);
+ kobject_del(&dev->kobject);
+ }
+
+ return rc;
+}
+
+static void hypercall_sysfs_remove(struct hypercall_dev *dev)
+{
+ sysfs_remove_link(&dev->pci_dev->dev.kobj, HYPERCALL_DRIVER_NAME);
+ kobject_del(&dev->kobject);
+}
+
module_init(hypercall_init_module);
module_exit(hypercall_cleanup_module);