[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Minios-devel] [UNIKRAFT PATCH v2 2/2] plat/kvm: Add pci driver support for kvm



Signed-off-by: Simon Kuenzer <simon.kuenzer@xxxxxxxxx>
Signed-off-by: Sharan Santhanam <sharan.santhanam@xxxxxxxxx>
---
 plat/common/include/pci/pci_bus.h | 186 ++++++++++++++++++++++++
 plat/common/pci_bus.c             | 288 ++++++++++++++++++++++++++++++++++++++
 plat/kvm/Config.uk                |   7 +
 plat/kvm/Makefile.uk              |   4 +
 4 files changed, 485 insertions(+)
 create mode 100644 plat/common/include/pci/pci_bus.h
 create mode 100644 plat/common/pci_bus.c

diff --git a/plat/common/include/pci/pci_bus.h 
b/plat/common/include/pci/pci_bus.h
new file mode 100644
index 0000000..cb38afc
--- /dev/null
+++ b/plat/common/include/pci/pci_bus.h
@@ -0,0 +1,186 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Authors: Simon Kuenzer <simon.kuenzer@xxxxxxxxx>
+ *
+ * Copyright (c) 2018, NEC Europe Ltd., NEC Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY.
+ */
+
+#ifndef __UKPLAT_COMMON_PCI_H__
+#define __UKPLAT_COMMON_PCI_H__
+
+#include <uk/bus.h>
+#include <uk/alloc.h>
+
+/* FROM DPDK: lib/librte_pci/rte_pci.h */
+
+/**
+ * A structure describing an ID for a PCI driver. Each driver provides a
+ * table of these IDs for each device that it supports.
+ */
+struct pci_device_id {
+       /**< Class ID or PCI_CLASS_ANY_ID. */
+       uint32_t class_id;
+       /**< Vendor ID or PCI_ANY_ID. */
+       uint16_t vendor_id;
+       /**< Device ID or PCI_ANY_ID. */
+       uint16_t device_id;
+       /**< Subsystem vendor ID or PCI_ANY_ID. */
+       uint16_t subsystem_vendor_id;
+       /**< Subsystem device ID or PCI_ANY_ID. */
+       uint16_t subsystem_device_id;
+};
+
+/** Any PCI device identifier (vendor, device, ...) */
+#define PCI_ANY_ID       (0xffff)
+#define PCI_CLASS_ANY_ID (0xffffff)
+
+/** Macro used to help building up tables of device IDs */
+#define PCI_DEVICE_ID(vend, dev)           \
+       .class_id = PCI_CLASS_ANY_ID,      \
+       .vendor_id = (vend),               \
+       .device_id = (dev),                \
+       .subsystem_vendor_id = PCI_ANY_ID, \
+       .subsystem_device_id = PCI_ANY_ID
+
+#define PCI_ANY_DEVICE_ID                  \
+       .class_id = PCI_CLASS_ANY_ID,      \
+       .vendor_id = PCI_ANY_ID,           \
+       .device_id = PCI_ANY_ID,           \
+       .subsystem_vendor_id = PCI_ANY_ID, \
+       .subsystem_device_id = PCI_ANY_ID
+
+/**
+ * A structure describing the location of a PCI device.
+ */
+struct pci_address {
+       /**< Device domain */
+       uint32_t domain;
+       /**< Device bus */
+       uint8_t bus;
+       /**< Device ID */
+       uint8_t devid;
+       /**< Device function. */
+       uint8_t function;
+};
+
+/*********** DPDK END *****************************/
+struct pci_device;
+
+typedef int (*pci_driver_add_func_t)(struct pci_device *);
+typedef int (*pci_driver_init_func_t)(struct uk_alloc *a);
+
+struct pci_driver {
+       UK_TAILQ_ENTRY(struct pci_driver) next;
+       /**< ANY-ID terminated list of device IDs that the driver handles */
+       const struct pci_device_id *device_ids;
+       pci_driver_init_func_t init; /* optional */
+       pci_driver_add_func_t add_dev;
+};
+UK_TAILQ_HEAD(pci_driver_list, struct pci_driver);
+
+enum pci_device_state {
+       PCI_DEVICE_STATE_RESET = 0,
+       PCI_DEVICE_STATE_RUNNING
+};
+
+struct pci_device {
+       UK_TAILQ_ENTRY(struct pci_device) next; /**< in use by pci_bus_handler 
*/
+       struct pci_device_id  id;
+       struct pci_address    addr;
+       struct pci_driver     *drv;
+       enum pci_device_state state;
+
+       uint16_t base;
+       uint8_t irq;
+};
+UK_TAILQ_HEAD(pci_device_list, struct pci_device);
+
+#define PCI_LIBNAME(x, y)      x##y
+#define PCI_REGISTER_DRIVER(b)                  \
+       _PCI_REGISTER_DRIVER(__LIBNAME__, b)
+
+#define _PCI_REGISTER_DRIVER(libname, b) \
+       static void __constructor_prio(103) PCI_LIBNAME(libname, 
_pci_register_driver)(void) \
+       {\
+               _pci_register_driver((b));\
+       }
+void _pci_register_driver(struct pci_driver *drv); /* use PCI_REGISTER_DRIVER 
*/
+
+
+/* TODO:
+ * Replace
+ */
+static inline uint8_t inb(uint16_t port)
+{
+       uint8_t v;
+
+       __asm__ __volatile__("inb %1,%0" : "=a" (v) : "dN" (port));
+       return v;
+}
+static inline uint16_t inw(uint16_t port)
+{
+       uint16_t v;
+
+       __asm__ __volatile__("inw %1,%0" : "=a" (v) : "dN" (port));
+       return v;
+}
+static inline uint32_t inl(uint16_t port)
+{
+       uint32_t v;
+
+       __asm__ __volatile__("inl %1,%0" : "=a" (v) : "dN" (port));
+       return v;
+}
+
+static inline uint64_t inq(uint16_t port_lo)
+{
+       uint16_t port_hi = port_lo + 4;
+       uint32_t lo, hi;
+
+       __asm__ __volatile__("inl %1,%0" : "=a" (lo) : "dN" (port_lo));
+       __asm__ __volatile__("inl %1,%0" : "=a" (hi) : "dN" (port_hi));
+
+       return ((uint64_t)lo) | ((uint64_t)hi << 32);
+}
+
+static inline void outb(uint16_t port, uint8_t v)
+{
+       __asm__ __volatile__("outb %0,%1" : : "a" (v), "dN" (port));
+}
+static inline void outw(uint16_t port, uint16_t v)
+{
+       __asm__ __volatile__("outw %0,%1" : : "a" (v), "dN" (port));
+}
+static inline void outl(uint16_t port, uint32_t v)
+{
+       __asm__ __volatile__("outl %0,%1" : : "a" (v), "dN" (port));
+}
+
+#endif /* __UKPLAT_COMMON_PCI_H__ */
diff --git a/plat/common/pci_bus.c b/plat/common/pci_bus.c
new file mode 100644
index 0000000..7417678
--- /dev/null
+++ b/plat/common/pci_bus.c
@@ -0,0 +1,288 @@
+/*
+ * Copyright (c) 2015-2017 Contributors as noted in the AUTHORS file
+ *
+ * This file is part of Solo5, a unikernel base layer.
+ *
+ * Permission to use, copy, modify, and/or distribute this software
+ * for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear
+ * in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <string.h>
+#include <uk/print.h>
+#include <pci_bus.h>
+
+#define  PCI_BUS_CTOR_PRIO      (102)
+
+struct pci_bus_handler {
+       struct uk_bus b;
+       struct uk_alloc *a;
+       struct pci_driver_list drv_list;  /**< List of PCI drivers */
+       int drv_list_initialized;
+       struct pci_device_list dev_list;  /**< List of PCI devices */
+};
+static struct pci_bus_handler ph;
+
+#define FOREACH_DRIVER(drv) \
+       UK_TAILQ_FOREACH(drv, &ph.drv_list, next)
+
+#define FOREACH_DRIVER_SAFE(drv, drv_next) \
+       UK_TAILQ_FOREACH_SAFE(drv, &ph.drv_list, next, drv_next)
+
+#define FOREACH_DEVICE(dev) \
+       UK_TAILQ_FOREACH(dev, &ph.dev_list, ph_next)
+
+#define PCI_INVALID_ID              (0xFFFF)
+#define PCI_DEVICE_ID_MASK          (0xFFFF)
+
+#define PCI_CONFIG_ADDR             (0xCF8)
+#define PCI_CONFIG_DATA             (0xCFC)
+
+/* 8 bits for bus number, 5 bits for devices */
+#define PCI_MAX_BUSES               (1 << 8)
+#define PCI_MAX_DEVICES             (1 << 5)
+
+#define PCI_BUS_SHIFT               (16)
+#define PCI_DEVICE_SHIFT            (11)
+#define PCI_ENABLE_BIT              (1 << 31)
+
+#define PCI_CONF_SUBSYS_ID          (0x2c)
+#define PCI_CONF_SUBSYS_ID_SHFT     (16)
+#define PCI_CONF_SUBSYS_ID_MASK     (0xFFFF)
+
+#define PCI_CONF_IRQ                (0X3C)
+#define PCI_CONF_IRQ_SHFT           (0x0)
+#define PCI_CONF_IRQ_MASK           (0XFF)
+
+#define PCI_CONF_IOBAR              (0x10)
+#define PCI_CONF_IOBAR_SHFT         (0x0)
+#define PCI_CONF_IOBAR_MASK         (~0x3)
+
+#define PCI_CONF_READ(type, ret, a, s) do {                          \
+       uint32_t _conf_data;                                             \
+       outl(PCI_CONFIG_ADDR, (a) | PCI_CONF_##s);                       \
+       _conf_data = ((inl(PCI_CONFIG_DATA) >> PCI_CONF_##s##_SHFT)      \
+                       & PCI_CONF_##s##_MASK);                          \
+       *(ret) = (type) _conf_data;                                      \
+       } while (0)
+
+static inline int pci_device_id_match(const struct pci_device_id *id0,
+                                       const struct pci_device_id *id1)
+{
+       if ((id0->class_id != PCI_CLASS_ANY_ID) &&
+           (id1->class_id != PCI_CLASS_ANY_ID) &&
+           (id0->class_id != id1->class_id)) {
+               return 0;
+       }
+       if ((id0->vendor_id != PCI_ANY_ID) &&
+           (id1->vendor_id != PCI_ANY_ID) &&
+           (id0->vendor_id != id1->vendor_id)) {
+               return 0;
+       }
+       if ((id0->device_id != PCI_ANY_ID) &&
+           (id1->device_id != PCI_ANY_ID) &&
+           (id0->device_id != id1->device_id)) {
+               return 0;
+       }
+       if ((id0->subsystem_vendor_id != PCI_ANY_ID) &&
+           (id1->subsystem_vendor_id != PCI_ANY_ID) &&
+           (id0->subsystem_vendor_id != id1->subsystem_vendor_id)) {
+               return 0;
+       }
+       if ((id0->subsystem_device_id != PCI_ANY_ID) &&
+           (id1->subsystem_device_id != PCI_ANY_ID) &&
+           (id0->subsystem_device_id != id1->subsystem_device_id)) {
+               return 0;
+       }
+       return 1;
+}
+
+static inline int pci_device_id_is_any(const struct pci_device_id *id)
+{
+       if ((id->class_id == PCI_CLASS_ANY_ID) &&
+           (id->vendor_id == PCI_ANY_ID) &&
+           (id->device_id == PCI_ANY_ID) &&
+           (id->subsystem_vendor_id == PCI_ANY_ID) &&
+           (id->subsystem_device_id == PCI_ANY_ID)) {
+               return 1;
+       }
+       return 0;
+}
+
+static inline struct pci_driver *pci_find_driver(struct pci_device_id *id)
+{
+       struct pci_driver *drv;
+       const struct pci_device_id *drv_id;
+
+       FOREACH_DRIVER(drv) {
+               for (drv_id = drv->device_ids;
+                   !pci_device_id_is_any(drv_id);
+                    drv_id++) {
+                       if (pci_device_id_match(id, drv_id))
+                               return drv;
+               }
+       }
+       return NULL; /* no driver found */
+}
+
+static inline int pci_driver_add_device(struct pci_driver *drv,
+                                       struct pci_address *addr,
+                                       struct pci_device_id *devid)
+{
+       struct pci_device *dev;
+       uint32_t config_addr;
+       int ret;
+
+       UK_ASSERT(drv != NULL);
+       UK_ASSERT(drv->add_dev != NULL);
+       UK_ASSERT(addr != NULL);
+       UK_ASSERT(devid != NULL);
+
+       dev = (struct pci_device *) uk_calloc(ph.a, 1, sizeof(*dev));
+       if (!dev) {
+               uk_printd(DLVL_ERR,
+                               "PCI %02x:%02x.%02x: Failed to initialize: Out 
of memory!\n",
+                               (int) addr->bus,
+                               (int) addr->devid,
+                               (int) addr->function);
+               return -ENOMEM;
+       }
+
+       memcpy(&dev->id,   devid, sizeof(dev->id));
+       memcpy(&dev->addr, addr,  sizeof(dev->addr));
+       dev->drv = drv;
+
+       config_addr = (PCI_ENABLE_BIT)
+                       | (addr->bus << PCI_BUS_SHIFT)
+                       | (addr->devid << PCI_DEVICE_SHIFT);
+       PCI_CONF_READ(uint16_t, &dev->base, config_addr, IOBAR);
+       PCI_CONF_READ(uint8_t, &dev->irq, config_addr, IRQ);
+
+       ret = drv->add_dev(dev);
+       if (ret < 0) {
+               uk_printd(DLVL_ERR,
+                         "PCI %02x:%02x.%02x: Failed to initialize driver\n",
+                       (int) addr->bus,
+                       (int) addr->devid,
+                       (int) addr->function);
+               uk_free(ph.a, dev);
+       }
+       return 0;
+}
+
+static int pci_probe(void)
+{
+       struct pci_address addr;
+       struct pci_device_id devid;
+       struct pci_driver *drv;
+       uint32_t config_addr, config_data;
+       uint32_t bus;
+       uint8_t dev;
+
+       uk_printd(DLVL_EXTRA, "Probe PCI\n");
+
+       for (bus = 0; bus < PCI_MAX_BUSES; ++bus) {
+               for (dev = 0; dev < PCI_MAX_DEVICES; ++dev) {
+                       config_addr = (PCI_ENABLE_BIT)
+                                       | (bus << PCI_BUS_SHIFT)
+                                       | (dev << PCI_DEVICE_SHIFT);
+
+                       outl(PCI_CONFIG_ADDR, config_addr);
+                       config_data = inl(PCI_CONFIG_DATA);
+
+                       /* TODO: Retrieve the device identfier */
+                       addr.domain   = 0x0;
+                       addr.bus      = bus;
+                       addr.devid    = dev;
+                        /* TODO: Retrieve the function */
+                       addr.function = 0x0;
+
+                       devid.vendor_id = config_data & PCI_DEVICE_ID_MASK;
+                       if (devid.vendor_id == PCI_INVALID_ID) {
+                               /* Device doesn't exist */
+                               continue;
+                       }
+
+                       /* TODO: Implement a way to fetch information from the 
device  */
+                       devid.class_id            = PCI_CLASS_ANY_ID;
+                       /* TODO: Implement a way to fetch information from the 
device  */
+                       devid.device_id           = PCI_ANY_ID;
+                       /* TODO: Implement a way to fetch information from the 
device  */
+                       devid.subsystem_vendor_id = PCI_ANY_ID;
+                       PCI_CONF_READ(uint16_t, &devid.subsystem_device_id,
+                                       config_addr, SUBSYS_ID);
+
+                       uk_printd(DLVL_INFO, "PCI %02x:%02x.%02x (%04x 
%04x:%04x): ",
+                               (int) addr.bus,
+                               (int) addr.devid,
+                               (int) addr.function,
+                               (int) devid.class_id,
+                               (int) devid.vendor_id,
+                               (int) devid.device_id);
+                       drv = pci_find_driver(&devid);
+                       if (!drv) {
+                               uk_printd(DLVL_INFO, "<no driver>\n");
+                               continue;
+                       }
+                       uk_printd(DLVL_INFO, "driver %p\n", drv);
+                       pci_driver_add_device(drv, &addr, &devid);
+               }
+       }
+       return 0;
+}
+
+
+static int pci_init(struct uk_alloc *a)
+{
+       struct pci_driver *drv, *drv_next;
+       int ret = 0;
+
+       UK_ASSERT(a != NULL);
+
+       ph.a = a;
+
+       if (!ph.drv_list_initialized) {
+               UK_TAILQ_INIT(&ph.drv_list);
+               ph.drv_list_initialized = 1;
+       }
+       UK_TAILQ_INIT(&ph.dev_list);
+
+       FOREACH_DRIVER_SAFE(drv, drv_next) {
+               if (drv->init) {
+                       ret = drv->init(a);
+                       if (0 == ret) {
+                               continue;
+                       }
+                       uk_printd(DLVL_ERR, "Failed to initialize the driver 
%d\n", ret);
+                       UK_TAILQ_REMOVE(&ph.drv_list, drv, next);
+               }
+       }
+       return 0;
+}
+
+void _pci_register_driver(struct pci_driver *drv)
+{
+       UK_ASSERT(drv != NULL);
+
+       if (!ph.drv_list_initialized) {
+               UK_TAILQ_INIT(&ph.drv_list);
+               ph.drv_list_initialized = 1;
+       }
+       UK_TAILQ_INSERT_TAIL(&ph.drv_list, drv, next);
+}
+
+static struct pci_bus_handler ph = {
+       .b.init = pci_init,
+       .b.probe = pci_probe
+};
+UK_BUS_REGISTER(&ph.b);
diff --git a/plat/kvm/Config.uk b/plat/kvm/Config.uk
index 622c4eb..47d55f4 100644
--- a/plat/kvm/Config.uk
+++ b/plat/kvm/Config.uk
@@ -10,4 +10,11 @@ menuconfig PLAT_KVM
                 Create a Unikraft image that runs as a KVM guest
 
 #if (PLAT_KVM)
+menuconfig KVM_PCI
+       bool "PCI Bus Driver"
+       default n
+       depends on (ARCH_X86_64)
+       select LIBUKBUS
+       help
+                Register an PCI bus driver as uk_bus
 #endif
diff --git a/plat/kvm/Makefile.uk b/plat/kvm/Makefile.uk
index b840c2f..b24bef6 100644
--- a/plat/kvm/Makefile.uk
+++ b/plat/kvm/Makefile.uk
@@ -7,14 +7,17 @@ $(eval $(call addplat_s,kvm,$(CONFIG_PLAT_KVM)))
 ## KVM platform library registration
 ##
 $(eval $(call addplatlib,kvm,libkvmplat))
+$(eval $(call addplatlib_s,kvm,libkvmpci,$(CONFIG_KVM_PCI)))
 
 ##
 ## Platform library definitions
 ##
 LIBKVMPLAT_ASINCLUDES-y        += -I$(LIBKVMPLAT_BASE)/include
 LIBKVMPLAT_ASINCLUDES-y        += -I$(UK_PLAT_COMMON_BASE)/include
+LIBKVMPCI_ASINCLUDES-$(CONFIG_ARCH_X86_64)  += 
-I$(UK_PLAT_COMMON_BASE)/include/pci
 LIBKVMPLAT_CINCLUDES-y         += -I$(LIBKVMPLAT_BASE)/include
 LIBKVMPLAT_CINCLUDES-y         += -I$(UK_PLAT_COMMON_BASE)/include
+LIBKVMPCI_CINCLUDES-$(CONFIG_ARCH_X86_64)   += 
-I$(UK_PLAT_COMMON_BASE)/include/pci
 
 LIBKVMPLAT_SRCS-$(CONFIG_ARCH_X86_64) += 
$(UK_PLAT_COMMON_BASE)/x86/trace.c|common
 LIBKVMPLAT_SRCS-$(CONFIG_ARCH_X86_64) += 
$(UK_PLAT_COMMON_BASE)/x86/traps.c|common
@@ -37,3 +40,4 @@ LIBKVMPLAT_SRCS-y              += $(LIBKVMPLAT_BASE)/irq.c
 LIBKVMPLAT_SRCS-y              += $(LIBKVMPLAT_BASE)/time.c
 LIBKVMPLAT_SRCS-y              += $(LIBKVMPLAT_BASE)/tscclock.c
 LIBKVMPLAT_SRCS-y              += $(UK_PLAT_COMMON_BASE)/lcpu.c|common
+LIBKVMPCI_SRCS-$(CONFIG_ARCH_X86_64)  += 
$(UK_PLAT_COMMON_BASE)/pci_bus.c|common
-- 
2.7.4


_______________________________________________
Minios-devel mailing list
Minios-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/minios-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.