Commit 68f5d3f3 authored by Johannes Berg's avatar Johannes Berg Committed by Richard Weinberger

um: add PCI over virtio emulation driver

To support testing of PCI/PCIe drivers in UML, add a PCI bus
support driver. This driver uses virtio, which in UML is really
just vhost-user, to talk to devices, and adds the devices to
the virtual PCI bus in the system.

Since virtio already allows DMA/bus mastering this really isn't
all that hard, of course we need the logic_iomem infrastructure
that was added by a previous patch.

The protocol to talk to the device is has a few fairly simple
messages for reading to/writing from config and IO spaces, and
messages for the device to send the various interrupts (INT#,
MSI/MSI-X and while suspended PME#).

Note that currently no offical virtio device ID is assigned for
this protocol, as a consequence this patch requires defining it
in the Kconfig, with a default that makes the driver refuse to
work at all.

Finally, in order to add support for MSI/MSI-X interrupts, some
small changes are needed in the UML IRQ code, it needs to have
more interrupts, changing NR_IRQS from 64 to 128 if this driver
is enabled, but not actually use them for anything so that the
generic IRQ domain/MSI infrastructure can allocate IRQ numbers.
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarRichard Weinberger <richard@nod.at>
parent a5ab7c84
...@@ -15,7 +15,7 @@ config UML ...@@ -15,7 +15,7 @@ config UML
select HAVE_FUTEX_CMPXCHG if FUTEX select HAVE_FUTEX_CMPXCHG if FUTEX
select HAVE_DEBUG_KMEMLEAK select HAVE_DEBUG_KMEMLEAK
select HAVE_DEBUG_BUGVERBOSE select HAVE_DEBUG_BUGVERBOSE
select NO_DMA select NO_DMA if !UML_DMA_EMULATION
select GENERIC_IRQ_SHOW select GENERIC_IRQ_SHOW
select GENERIC_CPU_DEVICES select GENERIC_CPU_DEVICES
select HAVE_GCC_PLUGINS select HAVE_GCC_PLUGINS
...@@ -26,10 +26,21 @@ config MMU ...@@ -26,10 +26,21 @@ config MMU
bool bool
default y default y
config UML_DMA_EMULATION
bool
config NO_IOMEM config NO_IOMEM
bool "disable IOMEM" if EXPERT bool "disable IOMEM" if EXPERT
depends on !INDIRECT_IOMEM
default y default y
config UML_IOMEM_EMULATION
bool
select INDIRECT_IOMEM
select GENERIC_PCI_IOMAP
select GENERIC_IOMAP
select NO_GENERIC_PCI_IOPORT_MAP
config NO_IOPORT_MAP config NO_IOPORT_MAP
def_bool y def_bool y
......
...@@ -357,3 +357,23 @@ config UML_RTC ...@@ -357,3 +357,23 @@ config UML_RTC
rtcwake, especially in time-travel mode. This driver enables that rtcwake, especially in time-travel mode. This driver enables that
by providing a fake RTC clock that causes a wakeup at the right by providing a fake RTC clock that causes a wakeup at the right
time. time.
config UML_PCI_OVER_VIRTIO
bool "Enable PCI over VIRTIO device simulation"
# in theory, just VIRTIO is enough, but that causes recursion
depends on VIRTIO_UML
select FORCE_PCI
select UML_IOMEM_EMULATION
select UML_DMA_EMULATION
select PCI_MSI
select PCI_MSI_IRQ_DOMAIN
select PCI_LOCKLESS_CONFIG
config UML_PCI_OVER_VIRTIO_DEVICE_ID
int "set the virtio device ID for PCI emulation"
default -1
depends on UML_PCI_OVER_VIRTIO
help
There's no official device ID assigned (yet), set the one you
wish to use for experimentation here. The default of -1 is
not valid and will cause the driver to fail at probe.
...@@ -64,6 +64,7 @@ obj-$(CONFIG_BLK_DEV_COW_COMMON) += cow_user.o ...@@ -64,6 +64,7 @@ obj-$(CONFIG_BLK_DEV_COW_COMMON) += cow_user.o
obj-$(CONFIG_UML_RANDOM) += random.o obj-$(CONFIG_UML_RANDOM) += random.o
obj-$(CONFIG_VIRTIO_UML) += virtio_uml.o obj-$(CONFIG_VIRTIO_UML) += virtio_uml.o
obj-$(CONFIG_UML_RTC) += rtc.o obj-$(CONFIG_UML_RTC) += rtc.o
obj-$(CONFIG_UML_PCI_OVER_VIRTIO) += virt-pci.o
# pcap_user.o must be added explicitly. # pcap_user.o must be added explicitly.
USER_OBJS := fd.o null.o pty.o tty.o xterm.o slip_common.o pcap_user.o vde_user.o vector_user.o USER_OBJS := fd.o null.o pty.o tty.o xterm.o slip_common.o pcap_user.o vde_user.o vector_user.o
......
This diff is collapsed.
...@@ -18,7 +18,6 @@ generic-y += mcs_spinlock.h ...@@ -18,7 +18,6 @@ generic-y += mcs_spinlock.h
generic-y += mmiowb.h generic-y += mmiowb.h
generic-y += module.lds.h generic-y += module.lds.h
generic-y += param.h generic-y += param.h
generic-y += pci.h
generic-y += percpu.h generic-y += percpu.h
generic-y += preempt.h generic-y += preempt.h
generic-y += softirq_stack.h generic-y += softirq_stack.h
......
...@@ -3,16 +3,23 @@ ...@@ -3,16 +3,23 @@
#define _ASM_UM_IO_H #define _ASM_UM_IO_H
#include <linux/types.h> #include <linux/types.h>
/* get emulated iomem (if desired) */
#include <asm-generic/logic_io.h>
#ifndef ioremap
#define ioremap ioremap #define ioremap ioremap
static inline void __iomem *ioremap(phys_addr_t offset, size_t size) static inline void __iomem *ioremap(phys_addr_t offset, size_t size)
{ {
return NULL; return NULL;
} }
#endif /* ioremap */
#ifndef iounmap
#define iounmap iounmap #define iounmap iounmap
static inline void iounmap(void __iomem *addr) static inline void iounmap(void __iomem *addr)
{ {
} }
#endif /* iounmap */
#include <asm-generic/io.h> #include <asm-generic/io.h>
......
...@@ -31,7 +31,13 @@ ...@@ -31,7 +31,13 @@
#endif #endif
#define NR_IRQS 64 #define UM_LAST_SIGNAL_IRQ 64
/* If we have (simulated) PCI MSI, allow 64 more interrupt numbers for it */
#ifdef CONFIG_PCI_MSI
#define NR_IRQS (UM_LAST_SIGNAL_IRQ + 64)
#else
#define NR_IRQS UM_LAST_SIGNAL_IRQ
#endif /* CONFIG_PCI_MSI */
#include <asm-generic/irq.h> #include <asm-generic/irq.h>
#endif #endif
#include <asm-generic/msi.h>
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __ASM_UM_PCI_H
#define __ASM_UM_PCI_H
#include <linux/types.h>
#include <asm/io.h>
#define PCIBIOS_MIN_IO 0
#define PCIBIOS_MIN_MEM 0
#define pcibios_assign_all_busses() 1
extern int isa_dma_bridge_buggy;
#ifdef CONFIG_PCI
static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel)
{
/* no legacy IRQs */
return -ENODEV;
}
#endif
#ifdef CONFIG_PCI_DOMAINS
static inline int pci_proc_domain(struct pci_bus *bus)
{
/* always show the domain in /proc */
return 1;
}
#endif /* CONFIG_PCI */
#ifdef CONFIG_PCI_MSI_IRQ_DOMAIN
/*
* This is a bit of an annoying hack, and it assumes we only have
* the virt-pci (if anything). Which is true, but still.
*/
void *pci_root_bus_fwnode(struct pci_bus *bus);
#define pci_root_bus_fwnode pci_root_bus_fwnode
#endif
#endif /* __ASM_UM_PCI_H */
...@@ -23,6 +23,7 @@ obj-$(CONFIG_BLK_DEV_INITRD) += initrd.o ...@@ -23,6 +23,7 @@ obj-$(CONFIG_BLK_DEV_INITRD) += initrd.o
obj-$(CONFIG_GPROF) += gprof_syms.o obj-$(CONFIG_GPROF) += gprof_syms.o
obj-$(CONFIG_EARLY_PRINTK) += early_printk.o obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-$(CONFIG_STACKTRACE) += stacktrace.o
obj-$(CONFIG_GENERIC_PCI_IOMAP) += ioport.o
USER_OBJS := config.o USER_OBJS := config.o
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2021 Intel Corporation
* Author: Johannes Berg <johannes@sipsolutions.net>
*/
#include <asm/iomap.h>
#include <asm-generic/pci_iomap.h>
void __iomem *__pci_ioport_map(struct pci_dev *dev, unsigned long port,
unsigned int nr)
{
return NULL;
}
...@@ -56,7 +56,7 @@ struct irq_entry { ...@@ -56,7 +56,7 @@ struct irq_entry {
static DEFINE_SPINLOCK(irq_lock); static DEFINE_SPINLOCK(irq_lock);
static LIST_HEAD(active_fds); static LIST_HEAD(active_fds);
static DECLARE_BITMAP(irqs_allocated, NR_IRQS); static DECLARE_BITMAP(irqs_allocated, UM_LAST_SIGNAL_IRQ);
static bool irqs_suspended; static bool irqs_suspended;
static void irq_io_loop(struct irq_reg *irq, struct uml_pt_regs *regs) static void irq_io_loop(struct irq_reg *irq, struct uml_pt_regs *regs)
...@@ -419,7 +419,8 @@ unsigned int do_IRQ(int irq, struct uml_pt_regs *regs) ...@@ -419,7 +419,8 @@ unsigned int do_IRQ(int irq, struct uml_pt_regs *regs)
void um_free_irq(int irq, void *dev) void um_free_irq(int irq, void *dev)
{ {
if (WARN(irq < 0 || irq > NR_IRQS, "freeing invalid irq %d", irq)) if (WARN(irq < 0 || irq > UM_LAST_SIGNAL_IRQ,
"freeing invalid irq %d", irq))
return; return;
free_irq_by_irq_and_dev(irq, dev); free_irq_by_irq_and_dev(irq, dev);
...@@ -648,7 +649,7 @@ void __init init_IRQ(void) ...@@ -648,7 +649,7 @@ void __init init_IRQ(void)
irq_set_chip_and_handler(TIMER_IRQ, &alarm_irq_type, handle_edge_irq); irq_set_chip_and_handler(TIMER_IRQ, &alarm_irq_type, handle_edge_irq);
for (i = 1; i < NR_IRQS; i++) for (i = 1; i < UM_LAST_SIGNAL_IRQ; i++)
irq_set_chip_and_handler(i, &normal_irq_type, handle_edge_irq); irq_set_chip_and_handler(i, &normal_irq_type, handle_edge_irq);
/* Initialize EPOLL Loop */ /* Initialize EPOLL Loop */
os_setup_epoll(); os_setup_epoll();
......
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
/*
* Copyright (C) 2021 Intel Corporation
* Author: Johannes Berg <johannes@sipsolutions.net>
*/
#ifndef _UAPI_LINUX_VIRTIO_PCIDEV_H
#define _UAPI_LINUX_VIRTIO_PCIDEV_H
#include <linux/types.h>
/**
* enum virtio_pcidev_ops - virtual PCI device operations
* @VIRTIO_PCIDEV_OP_CFG_READ: read config space, size is 1, 2, 4 or 8;
* the @data field should be filled in by the device (in little endian).
* @VIRTIO_PCIDEV_OP_CFG_WRITE: write config space, size is 1, 2, 4 or 8;
* the @data field contains the data to write (in little endian).
* @VIRTIO_PCIDEV_OP_BAR_READ: read BAR mem/pio, size can be variable;
* the @data field should be filled in by the device (in little endian).
* @VIRTIO_PCIDEV_OP_BAR_WRITE: write BAR mem/pio, size can be variable;
* the @data field contains the data to write (in little endian).
* @VIRTIO_PCIDEV_OP_MMIO_MEMSET: memset MMIO, size is variable but
* the @data field only has one byte (unlike @VIRTIO_PCIDEV_OP_MMIO_WRITE)
* @VIRTIO_PCIDEV_OP_INT: legacy INTx# pin interrupt, the addr field is 1-4 for
* the number
* @VIRTIO_PCIDEV_OP_MSI: MSI(-X) interrupt, this message basically transports
* the 16- or 32-bit write that would otherwise be done into memory,
* analogous to the write messages (@VIRTIO_PCIDEV_OP_MMIO_WRITE) above
* @VIRTIO_PCIDEV_OP_PME: Dummy message whose content is ignored (and should be
* all zeroes) to signal the PME# pin.
*/
enum virtio_pcidev_ops {
VIRTIO_PCIDEV_OP_RESERVED = 0,
VIRTIO_PCIDEV_OP_CFG_READ,
VIRTIO_PCIDEV_OP_CFG_WRITE,
VIRTIO_PCIDEV_OP_MMIO_READ,
VIRTIO_PCIDEV_OP_MMIO_WRITE,
VIRTIO_PCIDEV_OP_MMIO_MEMSET,
VIRTIO_PCIDEV_OP_INT,
VIRTIO_PCIDEV_OP_MSI,
VIRTIO_PCIDEV_OP_PME,
};
/**
* struct virtio_pcidev_msg - virtio PCI device operation
* @op: the operation to do
* @bar: the bar (only with BAR read/write messages)
* @reserved: reserved
* @size: the size of the read/write (in bytes)
* @addr: the address to read/write
* @data: the data, normally @size long, but just one byte for
* %VIRTIO_PCIDEV_OP_MMIO_MEMSET
*
* Note: the fields are all in native (CPU) endian, however, the
* @data values will often be in little endian (see the ops above.)
*/
struct virtio_pcidev_msg {
__u8 op;
__u8 bar;
__u16 reserved;
__u32 size;
__u64 addr;
__u8 data[];
};
#endif /* _UAPI_LINUX_VIRTIO_PCIDEV_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment