[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Minios-devel] [UNIKRAFT PATCH 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 | 183 ++++++++++++++++++++++++ plat/common/pci_bus.c | 288 ++++++++++++++++++++++++++++++++++++++ plat/kvm/Config.uk | 7 + plat/kvm/Makefile.uk | 4 + 4 files changed, 482 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..1eb2352 --- /dev/null +++ b/plat/common/include/pci/pci_bus.h @@ -0,0 +1,183 @@ +/* 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_REGISTER_DRIVER(b, priority) \ + _PCI_REGISTER_DRIVER(b, priority, __LIBNAME__) + +#define _PCI_REGISTER_DRIVER(b, priority, libname) \ + UK_CTOR_REGISTER(pci_register_driver, priority, libname, 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..449a6f9 --- /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, PCI_BUS_CTOR_PRIO); 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
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |