aboutsummaryrefslogtreecommitdiffstats
path: root/src/hw/pci.c
diff options
context:
space:
mode:
authorGerd Hoffmann <kraxel@redhat.com>2020-03-23 15:59:11 +0100
committerGerd Hoffmann <kraxel@redhat.com>2020-03-27 08:32:45 +0100
commit6a3b59ab9c7dc00331c21346052dfa6a0df45aa3 (patch)
treed969a64fc2528152c593f0a874d9ee5d9011a908 /src/hw/pci.c
parent63a44aff7a6a2303ff1c03b6bfcfa6477943e60d (diff)
downloadseabios-6a3b59ab9c7dc00331c21346052dfa6a0df45aa3.tar.gz
pci: add mmconfig support
Add support for pci config space access via mmconfig bar. Enable for qemu q35 chipset. Main advantage is that we need only one instead of two io operations per config space access, which translates to one instead of two vmexits for virtualization. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> Message-id: 20200323145911.22319-3-kraxel@redhat.com
Diffstat (limited to 'src/hw/pci.c')
-rw-r--r--src/hw/pci.c64
1 files changed, 52 insertions, 12 deletions
diff --git a/src/hw/pci.c b/src/hw/pci.c
index 7aca1e6b..d9dbf313 100644
--- a/src/hw/pci.c
+++ b/src/hw/pci.c
@@ -14,6 +14,13 @@
#define PORT_PCI_CMD 0x0cf8
#define PORT_PCI_DATA 0x0cfc
+static u32 mmconfig;
+
+static void *mmconfig_addr(u16 bdf, u32 addr)
+{
+ return (void*)(mmconfig + ((u32)bdf << 12) + addr);
+}
+
static u32 ioconfig_cmd(u16 bdf, u32 addr)
{
return 0x80000000 | (bdf << 8) | (addr & 0xfc);
@@ -21,38 +28,62 @@ static u32 ioconfig_cmd(u16 bdf, u32 addr)
void pci_config_writel(u16 bdf, u32 addr, u32 val)
{
- outl(ioconfig_cmd(bdf, addr), PORT_PCI_CMD);
- outl(val, PORT_PCI_DATA);
+ if (MODESEGMENT && mmconfig) {
+ writel(mmconfig_addr(bdf, addr), val);
+ } else {
+ outl(ioconfig_cmd(bdf, addr), PORT_PCI_CMD);
+ outl(val, PORT_PCI_DATA);
+ }
}
void pci_config_writew(u16 bdf, u32 addr, u16 val)
{
- outl(ioconfig_cmd(bdf, addr), PORT_PCI_CMD);
- outw(val, PORT_PCI_DATA + (addr & 2));
+ if (MODESEGMENT && mmconfig) {
+ writew(mmconfig_addr(bdf, addr), val);
+ } else {
+ outl(ioconfig_cmd(bdf, addr), PORT_PCI_CMD);
+ outw(val, PORT_PCI_DATA + (addr & 2));
+ }
}
void pci_config_writeb(u16 bdf, u32 addr, u8 val)
{
- outl(ioconfig_cmd(bdf, addr), PORT_PCI_CMD);
- outb(val, PORT_PCI_DATA + (addr & 3));
+ if (MODESEGMENT && mmconfig) {
+ writeb(mmconfig_addr(bdf, addr), val);
+ } else {
+ outl(ioconfig_cmd(bdf, addr), PORT_PCI_CMD);
+ outb(val, PORT_PCI_DATA + (addr & 3));
+ }
}
u32 pci_config_readl(u16 bdf, u32 addr)
{
- outl(ioconfig_cmd(bdf, addr), PORT_PCI_CMD);
- return inl(PORT_PCI_DATA);
+ if (MODESEGMENT && mmconfig) {
+ return readl(mmconfig_addr(bdf, addr));
+ } else {
+ outl(ioconfig_cmd(bdf, addr), PORT_PCI_CMD);
+ return inl(PORT_PCI_DATA);
+ }
}
u16 pci_config_readw(u16 bdf, u32 addr)
{
- outl(ioconfig_cmd(bdf, addr), PORT_PCI_CMD);
- return inw(PORT_PCI_DATA + (addr & 2));
+ if (MODESEGMENT && mmconfig) {
+ return readw(mmconfig_addr(bdf, addr));
+ } else {
+ outl(ioconfig_cmd(bdf, addr), PORT_PCI_CMD);
+ return inw(PORT_PCI_DATA + (addr & 2));
+ }
}
u8 pci_config_readb(u16 bdf, u32 addr)
{
- outl(ioconfig_cmd(bdf, addr), PORT_PCI_CMD);
- return inb(PORT_PCI_DATA + (addr & 3));
+ if (MODESEGMENT && mmconfig) {
+ return readb(mmconfig_addr(bdf, addr));
+ } else {
+ outl(ioconfig_cmd(bdf, addr), PORT_PCI_CMD);
+ return inb(PORT_PCI_DATA + (addr & 3));
+ }
}
void
@@ -63,6 +94,15 @@ pci_config_maskw(u16 bdf, u32 addr, u16 off, u16 on)
pci_config_writew(bdf, addr, val);
}
+void
+pci_enable_mmconfig(u64 addr, const char *name)
+{
+ if (addr >= 0x100000000ll)
+ return;
+ dprintf(1, "PCIe: using %s mmconfig at 0x%llx\n", name, addr);
+ mmconfig = addr;
+}
+
u8 pci_find_capability(u16 bdf, u8 cap_id, u8 cap)
{
int i;