From 315a1350c4a386065a73aad2ded1a11d77bf7771 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Wed, 12 Dec 2012 13:32:14 +0200 Subject: pci: move pci core code to hw/pci Move files and modify makefiles to pick them at the new location. Signed-off-by: Michael S. Tsirkin --- hw/Makefile.objs | 11 +- hw/i386/Makefile.objs | 2 +- hw/msi.c | 395 --------- hw/msi.h | 50 -- hw/msix.c | 562 ------------- hw/msix.h | 41 - hw/pci-hotplug.c | 292 ------- hw/pci-stub.c | 47 -- hw/pci.c | 2168 ------------------------------------------------ hw/pci.h | 684 --------------- hw/pci/Makefile.objs | 7 + hw/pci/msi.c | 395 +++++++++ hw/pci/msi.h | 50 ++ hw/pci/msix.c | 562 +++++++++++++ hw/pci/msix.h | 41 + hw/pci/pci-hotplug.c | 292 +++++++ hw/pci/pci-stub.c | 47 ++ hw/pci/pci.c | 2168 ++++++++++++++++++++++++++++++++++++++++++++++++ hw/pci/pci.h | 684 +++++++++++++++ hw/pci/pci_bridge.c | 363 ++++++++ hw/pci/pci_bridge.h | 66 ++ hw/pci/pci_host.c | 180 ++++ hw/pci/pci_host.h | 62 ++ hw/pci/pci_ids.h | 147 ++++ hw/pci/pci_internals.h | 78 ++ hw/pci/pci_regs.h | 717 ++++++++++++++++ hw/pci/pcie.c | 555 +++++++++++++ hw/pci/pcie.h | 142 ++++ hw/pci/pcie_aer.c | 1032 +++++++++++++++++++++++ hw/pci/pcie_aer.h | 106 +++ hw/pci/pcie_host.c | 161 ++++ hw/pci/pcie_host.h | 54 ++ hw/pci/pcie_port.c | 114 +++ hw/pci/pcie_port.h | 51 ++ hw/pci/pcie_regs.h | 156 ++++ hw/pci/shpc.c | 681 +++++++++++++++ hw/pci/shpc.h | 48 ++ hw/pci/slotid_cap.c | 44 + hw/pci/slotid_cap.h | 11 + hw/pci_bridge.c | 363 -------- hw/pci_bridge.h | 66 -- hw/pci_host.c | 180 ---- hw/pci_host.h | 62 -- hw/pci_ids.h | 147 ---- hw/pci_internals.h | 78 -- hw/pci_regs.h | 717 ---------------- hw/pcie.c | 555 ------------- hw/pcie.h | 142 ---- hw/pcie_aer.c | 1032 ----------------------- hw/pcie_aer.h | 106 --- hw/pcie_host.c | 161 ---- hw/pcie_host.h | 54 -- hw/pcie_port.c | 114 --- hw/pcie_port.h | 51 -- hw/pcie_regs.h | 156 ---- hw/ppc/Makefile.objs | 2 +- hw/shpc.c | 681 --------------- hw/shpc.h | 48 -- hw/slotid_cap.c | 44 - hw/slotid_cap.h | 11 - 60 files changed, 9018 insertions(+), 9018 deletions(-) delete mode 100644 hw/msi.c delete mode 100644 hw/msi.h delete mode 100644 hw/msix.c delete mode 100644 hw/msix.h delete mode 100644 hw/pci-hotplug.c delete mode 100644 hw/pci-stub.c delete mode 100644 hw/pci.c delete mode 100644 hw/pci.h create mode 100644 hw/pci/Makefile.objs create mode 100644 hw/pci/msi.c create mode 100644 hw/pci/msi.h create mode 100644 hw/pci/msix.c create mode 100644 hw/pci/msix.h create mode 100644 hw/pci/pci-hotplug.c create mode 100644 hw/pci/pci-stub.c create mode 100644 hw/pci/pci.c create mode 100644 hw/pci/pci.h create mode 100644 hw/pci/pci_bridge.c create mode 100644 hw/pci/pci_bridge.h create mode 100644 hw/pci/pci_host.c create mode 100644 hw/pci/pci_host.h create mode 100644 hw/pci/pci_ids.h create mode 100644 hw/pci/pci_internals.h create mode 100644 hw/pci/pci_regs.h create mode 100644 hw/pci/pcie.c create mode 100644 hw/pci/pcie.h create mode 100644 hw/pci/pcie_aer.c create mode 100644 hw/pci/pcie_aer.h create mode 100644 hw/pci/pcie_host.c create mode 100644 hw/pci/pcie_host.h create mode 100644 hw/pci/pcie_port.c create mode 100644 hw/pci/pcie_port.h create mode 100644 hw/pci/pcie_regs.h create mode 100644 hw/pci/shpc.c create mode 100644 hw/pci/shpc.h create mode 100644 hw/pci/slotid_cap.c create mode 100644 hw/pci/slotid_cap.h delete mode 100644 hw/pci_bridge.c delete mode 100644 hw/pci_bridge.h delete mode 100644 hw/pci_host.c delete mode 100644 hw/pci_host.h delete mode 100644 hw/pci_ids.h delete mode 100644 hw/pci_internals.h delete mode 100644 hw/pci_regs.h delete mode 100644 hw/pcie.c delete mode 100644 hw/pcie.h delete mode 100644 hw/pcie_aer.c delete mode 100644 hw/pcie_aer.h delete mode 100644 hw/pcie_host.c delete mode 100644 hw/pcie_host.h delete mode 100644 hw/pcie_port.c delete mode 100644 hw/pcie_port.h delete mode 100644 hw/pcie_regs.h delete mode 100644 hw/shpc.c delete mode 100644 hw/shpc.h delete mode 100644 hw/slotid_cap.c delete mode 100644 hw/slotid_cap.h diff --git a/hw/Makefile.objs b/hw/Makefile.objs index d581d8d6d6..9d33b189e6 100644 --- a/hw/Makefile.objs +++ b/hw/Makefile.objs @@ -1,14 +1,10 @@ -common-obj-y = usb/ ide/ +common-obj-y = usb/ ide/ pci/ common-obj-y += loader.o common-obj-$(CONFIG_VIRTIO) += virtio-console.o common-obj-$(CONFIG_VIRTIO) += virtio-rng.o common-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o common-obj-y += fw_cfg.o -common-obj-$(CONFIG_PCI) += pci.o pci_bridge.o pci_bridge_dev.o -common-obj-$(CONFIG_PCI) += msix.o msi.o -common-obj-$(CONFIG_PCI) += shpc.o -common-obj-$(CONFIG_PCI) += slotid_cap.o -common-obj-$(CONFIG_PCI) += pci_host.o pcie_host.o +common-obj-$(CONFIG_PCI) += pci_bridge_dev.o common-obj-$(CONFIG_PCI) += ioh3420.o xio3130_upstream.o xio3130_downstream.o common-obj-$(CONFIG_PCI) += i82801b11.o common-obj-y += watchdog.o @@ -102,8 +98,6 @@ common-obj-$(CONFIG_XGMAC) += xgmac.o # PCI watchdog devices common-obj-$(CONFIG_PCI) += wdt_i6300esb.o -common-obj-$(CONFIG_PCI) += pcie.o pcie_aer.o pcie_port.o - # PCI network cards common-obj-$(CONFIG_NE2000_PCI) += ne2000.o common-obj-$(CONFIG_EEPRO100_PCI) += eepro100.o @@ -199,7 +193,6 @@ obj-$(CONFIG_VIRTIO) += virtio-serial-bus.o virtio-scsi.o obj-$(CONFIG_SOFTMMU) += vhost_net.o obj-$(CONFIG_VHOST_NET) += vhost.o obj-$(CONFIG_REALLY_VIRTFS) += 9pfs/ -obj-$(CONFIG_NO_PCI) += pci-stub.o obj-$(CONFIG_VGA) += vga.o obj-$(CONFIG_SOFTMMU) += device-hotplug.o obj-$(CONFIG_XEN) += xen_domainbuild.o xen_machine_pv.o diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs index 0d3f6a8e84..257f3c1a91 100644 --- a/hw/i386/Makefile.objs +++ b/hw/i386/Makefile.objs @@ -2,7 +2,7 @@ obj-y += mc146818rtc.o pc.o obj-y += apic_common.o apic.o kvmvapic.o obj-y += sga.o ioapic_common.o ioapic.o piix_pci.o obj-y += vmport.o -obj-y += pci-hotplug.o smbios.o wdt_ib700.o +obj-y += pci/pci-hotplug.o smbios.o wdt_ib700.o obj-y += debugcon.o multiboot.o obj-y += pc_piix.o obj-y += pc_sysfw.o diff --git a/hw/msi.c b/hw/msi.c deleted file mode 100644 index 33037a80e9..0000000000 --- a/hw/msi.c +++ /dev/null @@ -1,395 +0,0 @@ -/* - * msi.c - * - * Copyright (c) 2010 Isaku Yamahata - * VA Linux Systems Japan K.K. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "msi.h" -#include "range.h" - -/* Eventually those constants should go to Linux pci_regs.h */ -#define PCI_MSI_PENDING_32 0x10 -#define PCI_MSI_PENDING_64 0x14 - -/* PCI_MSI_ADDRESS_LO */ -#define PCI_MSI_ADDRESS_LO_MASK (~0x3) - -/* If we get rid of cap allocator, we won't need those. */ -#define PCI_MSI_32_SIZEOF 0x0a -#define PCI_MSI_64_SIZEOF 0x0e -#define PCI_MSI_32M_SIZEOF 0x14 -#define PCI_MSI_64M_SIZEOF 0x18 - -#define PCI_MSI_VECTORS_MAX 32 - -/* Flag for interrupt controller to declare MSI/MSI-X support */ -bool msi_supported; - -/* If we get rid of cap allocator, we won't need this. */ -static inline uint8_t msi_cap_sizeof(uint16_t flags) -{ - switch (flags & (PCI_MSI_FLAGS_MASKBIT | PCI_MSI_FLAGS_64BIT)) { - case PCI_MSI_FLAGS_MASKBIT | PCI_MSI_FLAGS_64BIT: - return PCI_MSI_64M_SIZEOF; - case PCI_MSI_FLAGS_64BIT: - return PCI_MSI_64_SIZEOF; - case PCI_MSI_FLAGS_MASKBIT: - return PCI_MSI_32M_SIZEOF; - case 0: - return PCI_MSI_32_SIZEOF; - default: - abort(); - break; - } - return 0; -} - -//#define MSI_DEBUG - -#ifdef MSI_DEBUG -# define MSI_DPRINTF(fmt, ...) \ - fprintf(stderr, "%s:%d " fmt, __func__, __LINE__, ## __VA_ARGS__) -#else -# define MSI_DPRINTF(fmt, ...) do { } while (0) -#endif -#define MSI_DEV_PRINTF(dev, fmt, ...) \ - MSI_DPRINTF("%s:%x " fmt, (dev)->name, (dev)->devfn, ## __VA_ARGS__) - -static inline unsigned int msi_nr_vectors(uint16_t flags) -{ - return 1U << - ((flags & PCI_MSI_FLAGS_QSIZE) >> (ffs(PCI_MSI_FLAGS_QSIZE) - 1)); -} - -static inline uint8_t msi_flags_off(const PCIDevice* dev) -{ - return dev->msi_cap + PCI_MSI_FLAGS; -} - -static inline uint8_t msi_address_lo_off(const PCIDevice* dev) -{ - return dev->msi_cap + PCI_MSI_ADDRESS_LO; -} - -static inline uint8_t msi_address_hi_off(const PCIDevice* dev) -{ - return dev->msi_cap + PCI_MSI_ADDRESS_HI; -} - -static inline uint8_t msi_data_off(const PCIDevice* dev, bool msi64bit) -{ - return dev->msi_cap + (msi64bit ? PCI_MSI_DATA_64 : PCI_MSI_DATA_32); -} - -static inline uint8_t msi_mask_off(const PCIDevice* dev, bool msi64bit) -{ - return dev->msi_cap + (msi64bit ? PCI_MSI_MASK_64 : PCI_MSI_MASK_32); -} - -static inline uint8_t msi_pending_off(const PCIDevice* dev, bool msi64bit) -{ - return dev->msi_cap + (msi64bit ? PCI_MSI_PENDING_64 : PCI_MSI_PENDING_32); -} - -/* - * Special API for POWER to configure the vectors through - * a side channel. Should never be used by devices. - */ -void msi_set_message(PCIDevice *dev, MSIMessage msg) -{ - uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); - bool msi64bit = flags & PCI_MSI_FLAGS_64BIT; - - if (msi64bit) { - pci_set_quad(dev->config + msi_address_lo_off(dev), msg.address); - } else { - pci_set_long(dev->config + msi_address_lo_off(dev), msg.address); - } - pci_set_word(dev->config + msi_data_off(dev, msi64bit), msg.data); -} - -MSIMessage msi_get_message(PCIDevice *dev, unsigned int vector) -{ - uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); - bool msi64bit = flags & PCI_MSI_FLAGS_64BIT; - unsigned int nr_vectors = msi_nr_vectors(flags); - MSIMessage msg; - - assert(vector < nr_vectors); - - if (msi64bit) { - msg.address = pci_get_quad(dev->config + msi_address_lo_off(dev)); - } else { - msg.address = pci_get_long(dev->config + msi_address_lo_off(dev)); - } - - /* upper bit 31:16 is zero */ - msg.data = pci_get_word(dev->config + msi_data_off(dev, msi64bit)); - if (nr_vectors > 1) { - msg.data &= ~(nr_vectors - 1); - msg.data |= vector; - } - - return msg; -} - -bool msi_enabled(const PCIDevice *dev) -{ - return msi_present(dev) && - (pci_get_word(dev->config + msi_flags_off(dev)) & - PCI_MSI_FLAGS_ENABLE); -} - -int msi_init(struct PCIDevice *dev, uint8_t offset, - unsigned int nr_vectors, bool msi64bit, bool msi_per_vector_mask) -{ - unsigned int vectors_order; - uint16_t flags; - uint8_t cap_size; - int config_offset; - - if (!msi_supported) { - return -ENOTSUP; - } - - MSI_DEV_PRINTF(dev, - "init offset: 0x%"PRIx8" vector: %"PRId8 - " 64bit %d mask %d\n", - offset, nr_vectors, msi64bit, msi_per_vector_mask); - - assert(!(nr_vectors & (nr_vectors - 1))); /* power of 2 */ - assert(nr_vectors > 0); - assert(nr_vectors <= PCI_MSI_VECTORS_MAX); - /* the nr of MSI vectors is up to 32 */ - vectors_order = ffs(nr_vectors) - 1; - - flags = vectors_order << (ffs(PCI_MSI_FLAGS_QMASK) - 1); - if (msi64bit) { - flags |= PCI_MSI_FLAGS_64BIT; - } - if (msi_per_vector_mask) { - flags |= PCI_MSI_FLAGS_MASKBIT; - } - - cap_size = msi_cap_sizeof(flags); - config_offset = pci_add_capability(dev, PCI_CAP_ID_MSI, offset, cap_size); - if (config_offset < 0) { - return config_offset; - } - - dev->msi_cap = config_offset; - dev->cap_present |= QEMU_PCI_CAP_MSI; - - pci_set_word(dev->config + msi_flags_off(dev), flags); - pci_set_word(dev->wmask + msi_flags_off(dev), - PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE); - pci_set_long(dev->wmask + msi_address_lo_off(dev), - PCI_MSI_ADDRESS_LO_MASK); - if (msi64bit) { - pci_set_long(dev->wmask + msi_address_hi_off(dev), 0xffffffff); - } - pci_set_word(dev->wmask + msi_data_off(dev, msi64bit), 0xffff); - - if (msi_per_vector_mask) { - /* Make mask bits 0 to nr_vectors - 1 writable. */ - pci_set_long(dev->wmask + msi_mask_off(dev, msi64bit), - 0xffffffff >> (PCI_MSI_VECTORS_MAX - nr_vectors)); - } - return config_offset; -} - -void msi_uninit(struct PCIDevice *dev) -{ - uint16_t flags; - uint8_t cap_size; - - if (!msi_present(dev)) { - return; - } - flags = pci_get_word(dev->config + msi_flags_off(dev)); - cap_size = msi_cap_sizeof(flags); - pci_del_capability(dev, PCI_CAP_ID_MSI, cap_size); - dev->cap_present &= ~QEMU_PCI_CAP_MSI; - - MSI_DEV_PRINTF(dev, "uninit\n"); -} - -void msi_reset(PCIDevice *dev) -{ - uint16_t flags; - bool msi64bit; - - if (!msi_present(dev)) { - return; - } - - flags = pci_get_word(dev->config + msi_flags_off(dev)); - flags &= ~(PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE); - msi64bit = flags & PCI_MSI_FLAGS_64BIT; - - pci_set_word(dev->config + msi_flags_off(dev), flags); - pci_set_long(dev->config + msi_address_lo_off(dev), 0); - if (msi64bit) { - pci_set_long(dev->config + msi_address_hi_off(dev), 0); - } - pci_set_word(dev->config + msi_data_off(dev, msi64bit), 0); - if (flags & PCI_MSI_FLAGS_MASKBIT) { - pci_set_long(dev->config + msi_mask_off(dev, msi64bit), 0); - pci_set_long(dev->config + msi_pending_off(dev, msi64bit), 0); - } - MSI_DEV_PRINTF(dev, "reset\n"); -} - -static bool msi_is_masked(const PCIDevice *dev, unsigned int vector) -{ - uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); - uint32_t mask; - assert(vector < PCI_MSI_VECTORS_MAX); - - if (!(flags & PCI_MSI_FLAGS_MASKBIT)) { - return false; - } - - mask = pci_get_long(dev->config + - msi_mask_off(dev, flags & PCI_MSI_FLAGS_64BIT)); - return mask & (1U << vector); -} - -void msi_notify(PCIDevice *dev, unsigned int vector) -{ - uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); - bool msi64bit = flags & PCI_MSI_FLAGS_64BIT; - unsigned int nr_vectors = msi_nr_vectors(flags); - MSIMessage msg; - - assert(vector < nr_vectors); - if (msi_is_masked(dev, vector)) { - assert(flags & PCI_MSI_FLAGS_MASKBIT); - pci_long_test_and_set_mask( - dev->config + msi_pending_off(dev, msi64bit), 1U << vector); - MSI_DEV_PRINTF(dev, "pending vector 0x%x\n", vector); - return; - } - - msg = msi_get_message(dev, vector); - - MSI_DEV_PRINTF(dev, - "notify vector 0x%x" - " address: 0x%"PRIx64" data: 0x%"PRIx32"\n", - vector, msg.address, msg.data); - stl_le_phys(msg.address, msg.data); -} - -/* Normally called by pci_default_write_config(). */ -void msi_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len) -{ - uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); - bool msi64bit = flags & PCI_MSI_FLAGS_64BIT; - bool msi_per_vector_mask = flags & PCI_MSI_FLAGS_MASKBIT; - unsigned int nr_vectors; - uint8_t log_num_vecs; - uint8_t log_max_vecs; - unsigned int vector; - uint32_t pending; - - if (!msi_present(dev) || - !ranges_overlap(addr, len, dev->msi_cap, msi_cap_sizeof(flags))) { - return; - } - -#ifdef MSI_DEBUG - MSI_DEV_PRINTF(dev, "addr 0x%"PRIx32" val 0x%"PRIx32" len %d\n", - addr, val, len); - MSI_DEV_PRINTF(dev, "ctrl: 0x%"PRIx16" address: 0x%"PRIx32, - flags, - pci_get_long(dev->config + msi_address_lo_off(dev))); - if (msi64bit) { - fprintf(stderr, " address-hi: 0x%"PRIx32, - pci_get_long(dev->config + msi_address_hi_off(dev))); - } - fprintf(stderr, " data: 0x%"PRIx16, - pci_get_word(dev->config + msi_data_off(dev, msi64bit))); - if (flags & PCI_MSI_FLAGS_MASKBIT) { - fprintf(stderr, " mask 0x%"PRIx32" pending 0x%"PRIx32, - pci_get_long(dev->config + msi_mask_off(dev, msi64bit)), - pci_get_long(dev->config + msi_pending_off(dev, msi64bit))); - } - fprintf(stderr, "\n"); -#endif - - if (!(flags & PCI_MSI_FLAGS_ENABLE)) { - return; - } - - /* - * Now MSI is enabled, clear INTx# interrupts. - * the driver is prohibited from writing enable bit to mask - * a service request. But the guest OS could do this. - * So we just discard the interrupts as moderate fallback. - * - * 6.8.3.3. Enabling Operation - * While enabled for MSI or MSI-X operation, a function is prohibited - * from using its INTx# pin (if implemented) to request - * service (MSI, MSI-X, and INTx# are mutually exclusive). - */ - pci_device_deassert_intx(dev); - - /* - * nr_vectors might be set bigger than capable. So clamp it. - * This is not legal by spec, so we can do anything we like, - * just don't crash the host - */ - log_num_vecs = - (flags & PCI_MSI_FLAGS_QSIZE) >> (ffs(PCI_MSI_FLAGS_QSIZE) - 1); - log_max_vecs = - (flags & PCI_MSI_FLAGS_QMASK) >> (ffs(PCI_MSI_FLAGS_QMASK) - 1); - if (log_num_vecs > log_max_vecs) { - flags &= ~PCI_MSI_FLAGS_QSIZE; - flags |= log_max_vecs << (ffs(PCI_MSI_FLAGS_QSIZE) - 1); - pci_set_word(dev->config + msi_flags_off(dev), flags); - } - - if (!msi_per_vector_mask) { - /* if per vector masking isn't supported, - there is no pending interrupt. */ - return; - } - - nr_vectors = msi_nr_vectors(flags); - - /* This will discard pending interrupts, if any. */ - pending = pci_get_long(dev->config + msi_pending_off(dev, msi64bit)); - pending &= 0xffffffff >> (PCI_MSI_VECTORS_MAX - nr_vectors); - pci_set_long(dev->config + msi_pending_off(dev, msi64bit), pending); - - /* deliver pending interrupts which are unmasked */ - for (vector = 0; vector < nr_vectors; ++vector) { - if (msi_is_masked(dev, vector) || !(pending & (1U << vector))) { - continue; - } - - pci_long_test_and_clear_mask( - dev->config + msi_pending_off(dev, msi64bit), 1U << vector); - msi_notify(dev, vector); - } -} - -unsigned int msi_nr_vectors_allocated(const PCIDevice *dev) -{ - uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); - return msi_nr_vectors(flags); -} diff --git a/hw/msi.h b/hw/msi.h deleted file mode 100644 index 150b09a19d..0000000000 --- a/hw/msi.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * msi.h - * - * Copyright (c) 2010 Isaku Yamahata - * VA Linux Systems Japan K.K. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#ifndef QEMU_MSI_H -#define QEMU_MSI_H - -#include "qemu-common.h" -#include "pci.h" - -struct MSIMessage { - uint64_t address; - uint32_t data; -}; - -extern bool msi_supported; - -void msi_set_message(PCIDevice *dev, MSIMessage msg); -MSIMessage msi_get_message(PCIDevice *dev, unsigned int vector); -bool msi_enabled(const PCIDevice *dev); -int msi_init(struct PCIDevice *dev, uint8_t offset, - unsigned int nr_vectors, bool msi64bit, bool msi_per_vector_mask); -void msi_uninit(struct PCIDevice *dev); -void msi_reset(PCIDevice *dev); -void msi_notify(PCIDevice *dev, unsigned int vector); -void msi_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len); -unsigned int msi_nr_vectors_allocated(const PCIDevice *dev); - -static inline bool msi_present(const PCIDevice *dev) -{ - return dev->cap_present & QEMU_PCI_CAP_MSI; -} - -#endif /* QEMU_MSI_H */ diff --git a/hw/msix.c b/hw/msix.c deleted file mode 100644 index 136ef09373..0000000000 --- a/hw/msix.c +++ /dev/null @@ -1,562 +0,0 @@ -/* - * MSI-X device support - * - * This module includes support for MSI-X in pci devices. - * - * Author: Michael S. Tsirkin - * - * Copyright (c) 2009, Red Hat Inc, Michael S. Tsirkin (mst@redhat.com) - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ - -#include "hw.h" -#include "msi.h" -#include "msix.h" -#include "pci.h" -#include "range.h" - -#define MSIX_CAP_LENGTH 12 - -/* MSI enable bit and maskall bit are in byte 1 in FLAGS register */ -#define MSIX_CONTROL_OFFSET (PCI_MSIX_FLAGS + 1) -#define MSIX_ENABLE_MASK (PCI_MSIX_FLAGS_ENABLE >> 8) -#define MSIX_MASKALL_MASK (PCI_MSIX_FLAGS_MASKALL >> 8) - -static MSIMessage msix_get_message(PCIDevice *dev, unsigned vector) -{ - uint8_t *table_entry = dev->msix_table + vector * PCI_MSIX_ENTRY_SIZE; - MSIMessage msg; - - msg.address = pci_get_quad(table_entry + PCI_MSIX_ENTRY_LOWER_ADDR); - msg.data = pci_get_long(table_entry + PCI_MSIX_ENTRY_DATA); - return msg; -} - -/* - * Special API for POWER to configure the vectors through - * a side channel. Should never be used by devices. - */ -void msix_set_message(PCIDevice *dev, int vector, struct MSIMessage msg) -{ - uint8_t *table_entry = dev->msix_table + vector * PCI_MSIX_ENTRY_SIZE; - - pci_set_quad(table_entry + PCI_MSIX_ENTRY_LOWER_ADDR, msg.address); - pci_set_long(table_entry + PCI_MSIX_ENTRY_DATA, msg.data); - table_entry[PCI_MSIX_ENTRY_VECTOR_CTRL] &= ~PCI_MSIX_ENTRY_CTRL_MASKBIT; -} - -static uint8_t msix_pending_mask(int vector) -{ - return 1 << (vector % 8); -} - -static uint8_t *msix_pending_byte(PCIDevice *dev, int vector) -{ - return dev->msix_pba + vector / 8; -} - -static int msix_is_pending(PCIDevice *dev, int vector) -{ - return *msix_pending_byte(dev, vector) & msix_pending_mask(vector); -} - -static void msix_set_pending(PCIDevice *dev, int vector) -{ - *msix_pending_byte(dev, vector) |= msix_pending_mask(vector); -} - -static void msix_clr_pending(PCIDevice *dev, int vector) -{ - *msix_pending_byte(dev, vector) &= ~msix_pending_mask(vector); -} - -static bool msix_vector_masked(PCIDevice *dev, int vector, bool fmask) -{ - unsigned offset = vector * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL; - return fmask || dev->msix_table[offset] & PCI_MSIX_ENTRY_CTRL_MASKBIT; -} - -static bool msix_is_masked(PCIDevice *dev, int vector) -{ - return msix_vector_masked(dev, vector, dev->msix_function_masked); -} - -static void msix_fire_vector_notifier(PCIDevice *dev, - unsigned int vector, bool is_masked) -{ - MSIMessage msg; - int ret; - - if (!dev->msix_vector_use_notifier) { - return; - } - if (is_masked) { - dev->msix_vector_release_notifier(dev, vector); - } else { - msg = msix_get_message(dev, vector); - ret = dev->msix_vector_use_notifier(dev, vector, msg); - assert(ret >= 0); - } -} - -static void msix_handle_mask_update(PCIDevice *dev, int vector, bool was_masked) -{ - bool is_masked = msix_is_masked(dev, vector); - - if (is_masked == was_masked) { - return; - } - - msix_fire_vector_notifier(dev, vector, is_masked); - - if (!is_masked && msix_is_pending(dev, vector)) { - msix_clr_pending(dev, vector); - msix_notify(dev, vector); - } -} - -static void msix_update_function_masked(PCIDevice *dev) -{ - dev->msix_function_masked = !msix_enabled(dev) || - (dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] & MSIX_MASKALL_MASK); -} - -/* Handle MSI-X capability config write. */ -void msix_write_config(PCIDevice *dev, uint32_t addr, - uint32_t val, int len) -{ - unsigned enable_pos = dev->msix_cap + MSIX_CONTROL_OFFSET; - int vector; - bool was_masked; - - if (!msix_present(dev) || !range_covers_byte(addr, len, enable_pos)) { - return; - } - - was_masked = dev->msix_function_masked; - msix_update_function_masked(dev); - - if (!msix_enabled(dev)) { - return; - } - - pci_device_deassert_intx(dev); - - if (dev->msix_function_masked == was_masked) { - return; - } - - for (vector = 0; vector < dev->msix_entries_nr; ++vector) { - msix_handle_mask_update(dev, vector, - msix_vector_masked(dev, vector, was_masked)); - } -} - -static uint64_t msix_table_mmio_read(void *opaque, hwaddr addr, - unsigned size) -{ - PCIDevice *dev = opaque; - - return pci_get_long(dev->msix_table + addr); -} - -static void msix_table_mmio_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - PCIDevice *dev = opaque; - int vector = addr / PCI_MSIX_ENTRY_SIZE; - bool was_masked; - - was_masked = msix_is_masked(dev, vector); - pci_set_long(dev->msix_table + addr, val); - msix_handle_mask_update(dev, vector, was_masked); -} - -static const MemoryRegionOps msix_table_mmio_ops = { - .read = msix_table_mmio_read, - .write = msix_table_mmio_write, - /* TODO: MSIX should be LITTLE_ENDIAN. */ - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static uint64_t msix_pba_mmio_read(void *opaque, hwaddr addr, - unsigned size) -{ - PCIDevice *dev = opaque; - - return pci_get_long(dev->msix_pba + addr); -} - -static const MemoryRegionOps msix_pba_mmio_ops = { - .read = msix_pba_mmio_read, - /* TODO: MSIX should be LITTLE_ENDIAN. */ - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static void msix_mask_all(struct PCIDevice *dev, unsigned nentries) -{ - int vector; - - for (vector = 0; vector < nentries; ++vector) { - unsigned offset = - vector * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL; - bool was_masked = msix_is_masked(dev, vector); - - dev->msix_table[offset] |= PCI_MSIX_ENTRY_CTRL_MASKBIT; - msix_handle_mask_update(dev, vector, was_masked); - } -} - -/* Initialize the MSI-X structures */ -int msix_init(struct PCIDevice *dev, unsigned short nentries, - MemoryRegion *table_bar, uint8_t table_bar_nr, - unsigned table_offset, MemoryRegion *pba_bar, - uint8_t pba_bar_nr, unsigned pba_offset, uint8_t cap_pos) -{ - int cap; - unsigned table_size, pba_size; - uint8_t *config; - - /* Nothing to do if MSI is not supported by interrupt controller */ - if (!msi_supported) { - return -ENOTSUP; - } - - if (nentries < 1 || nentries > PCI_MSIX_FLAGS_QSIZE + 1) { - return -EINVAL; - } - - table_size = nentries * PCI_MSIX_ENTRY_SIZE; - pba_size = QEMU_ALIGN_UP(nentries, 64) / 8; - - /* Sanity test: table & pba don't overlap, fit within BARs, min aligned */ - if ((table_bar_nr == pba_bar_nr && - ranges_overlap(table_offset, table_size, pba_offset, pba_size)) || - table_offset + table_size > memory_region_size(table_bar) || - pba_offset + pba_size > memory_region_size(pba_bar) || - (table_offset | pba_offset) & PCI_MSIX_FLAGS_BIRMASK) { - return -EINVAL; - } - - cap = pci_add_capability(dev, PCI_CAP_ID_MSIX, cap_pos, MSIX_CAP_LENGTH); - if (cap < 0) { - return cap; - } - - dev->msix_cap = cap; - dev->cap_present |= QEMU_PCI_CAP_MSIX; - config = dev->config + cap; - - pci_set_word(config + PCI_MSIX_FLAGS, nentries - 1); - dev->msix_entries_nr = nentries; - dev->msix_function_masked = true; - - pci_set_long(config + PCI_MSIX_TABLE, table_offset | table_bar_nr); - pci_set_long(config + PCI_MSIX_PBA, pba_offset | pba_bar_nr); - - /* Make flags bit writable. */ - dev->wmask[cap + MSIX_CONTROL_OFFSET] |= MSIX_ENABLE_MASK | - MSIX_MASKALL_MASK; - - dev->msix_table = g_malloc0(table_size); - dev->msix_pba = g_malloc0(pba_size); - dev->msix_entry_used = g_malloc0(nentries * sizeof *dev->msix_entry_used); - - msix_mask_all(dev, nentries); - - memory_region_init_io(&dev->msix_table_mmio, &msix_table_mmio_ops, dev, - "msix-table", table_size); - memory_region_add_subregion(table_bar, table_offset, &dev->msix_table_mmio); - memory_region_init_io(&dev->msix_pba_mmio, &msix_pba_mmio_ops, dev, - "msix-pba", pba_size); - memory_region_add_subregion(pba_bar, pba_offset, &dev->msix_pba_mmio); - - return 0; -} - -int msix_init_exclusive_bar(PCIDevice *dev, unsigned short nentries, - uint8_t bar_nr) -{ - int ret; - char *name; - - /* - * Migration compatibility dictates that this remains a 4k - * BAR with the vector table in the lower half and PBA in - * the upper half. Do not use these elsewhere! - */ -#define MSIX_EXCLUSIVE_BAR_SIZE 4096 -#define MSIX_EXCLUSIVE_BAR_TABLE_OFFSET 0 -#define MSIX_EXCLUSIVE_BAR_PBA_OFFSET (MSIX_EXCLUSIVE_BAR_SIZE / 2) -#define MSIX_EXCLUSIVE_CAP_OFFSET 0 - - if (nentries * PCI_MSIX_ENTRY_SIZE > MSIX_EXCLUSIVE_BAR_PBA_OFFSET) { - return -EINVAL; - } - - name = g_strdup_printf("%s-msix", dev->name); - memory_region_init(&dev->msix_exclusive_bar, name, MSIX_EXCLUSIVE_BAR_SIZE); - g_free(name); - - ret = msix_init(dev, nentries, &dev->msix_exclusive_bar, bar_nr, - MSIX_EXCLUSIVE_BAR_TABLE_OFFSET, &dev->msix_exclusive_bar, - bar_nr, MSIX_EXCLUSIVE_BAR_PBA_OFFSET, - MSIX_EXCLUSIVE_CAP_OFFSET); - if (ret) { - memory_region_destroy(&dev->msix_exclusive_bar); - return ret; - } - - pci_register_bar(dev, bar_nr, PCI_BASE_ADDRESS_SPACE_MEMORY, - &dev->msix_exclusive_bar); - - return 0; -} - -static void msix_free_irq_entries(PCIDevice *dev) -{ - int vector; - - for (vector = 0; vector < dev->msix_entries_nr; ++vector) { - dev->msix_entry_used[vector] = 0; - msix_clr_pending(dev, vector); - } -} - -static void msix_clear_all_vectors(PCIDevice *dev) -{ - int vector; - - for (vector = 0; vector < dev->msix_entries_nr; ++vector) { - msix_clr_pending(dev, vector); - } -} - -/* Clean up resources for the device. */ -void msix_uninit(PCIDevice *dev, MemoryRegion *table_bar, MemoryRegion *pba_bar) -{ - if (!msix_present(dev)) { - return; - } - pci_del_capability(dev, PCI_CAP_ID_MSIX, MSIX_CAP_LENGTH); - dev->msix_cap = 0; - msix_free_irq_entries(dev); - dev->msix_entries_nr = 0; - memory_region_del_subregion(pba_bar, &dev->msix_pba_mmio); - memory_region_destroy(&dev->msix_pba_mmio); - g_free(dev->msix_pba); - dev->msix_pba = NULL; - memory_region_del_subregion(table_bar, &dev->msix_table_mmio); - memory_region_destroy(&dev->msix_table_mmio); - g_free(dev->msix_table); - dev->msix_table = NULL; - g_free(dev->msix_entry_used); - dev->msix_entry_used = NULL; - dev->cap_present &= ~QEMU_PCI_CAP_MSIX; -} - -void msix_uninit_exclusive_bar(PCIDevice *dev) -{ - if (msix_present(dev)) { - msix_uninit(dev, &dev->msix_exclusive_bar, &dev->msix_exclusive_bar); - memory_region_destroy(&dev->msix_exclusive_bar); - } -} - -void msix_save(PCIDevice *dev, QEMUFile *f) -{ - unsigned n = dev->msix_entries_nr; - - if (!msix_present(dev)) { - return; - } - - qemu_put_buffer(f, dev->msix_table, n * PCI_MSIX_ENTRY_SIZE); - qemu_put_buffer(f, dev->msix_pba, (n + 7) / 8); -} - -/* Should be called after restoring the config space. */ -void msix_load(PCIDevice *dev, QEMUFile *f) -{ - unsigned n = dev->msix_entries_nr; - unsigned int vector; - - if (!msix_present(dev)) { - return; - } - - msix_clear_all_vectors(dev); - qemu_get_buffer(f, dev->msix_table, n * PCI_MSIX_ENTRY_SIZE); - qemu_get_buffer(f, dev->msix_pba, (n + 7) / 8); - msix_update_function_masked(dev); - - for (vector = 0; vector < n; vector++) { - msix_handle_mask_update(dev, vector, true); - } -} - -/* Does device support MSI-X? */ -int msix_present(PCIDevice *dev) -{ - return dev->cap_present & QEMU_PCI_CAP_MSIX; -} - -/* Is MSI-X enabled? */ -int msix_enabled(PCIDevice *dev) -{ - return (dev->cap_present & QEMU_PCI_CAP_MSIX) && - (dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] & - MSIX_ENABLE_MASK); -} - -/* Send an MSI-X message */ -void msix_notify(PCIDevice *dev, unsigned vector) -{ - MSIMessage msg; - - if (vector >= dev->msix_entries_nr || !dev->msix_entry_used[vector]) - return; - if (msix_is_masked(dev, vector)) { - msix_set_pending(dev, vector); - return; - } - - msg = msix_get_message(dev, vector); - - stl_le_phys(msg.address, msg.data); -} - -void msix_reset(PCIDevice *dev) -{ - if (!msix_present(dev)) { - return; - } - msix_clear_all_vectors(dev); - dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] &= - ~dev->wmask[dev->msix_cap + MSIX_CONTROL_OFFSET]; - memset(dev->msix_table, 0, dev->msix_entries_nr * PCI_MSIX_ENTRY_SIZE); - memset(dev->msix_pba, 0, QEMU_ALIGN_UP(dev->msix_entries_nr, 64) / 8); - msix_mask_all(dev, dev->msix_entries_nr); -} - -/* PCI spec suggests that devices make it possible for software to configure - * less vectors than supported by the device, but does not specify a standard - * mechanism for devices to do so. - * - * We support this by asking devices to declare vectors software is going to - * actually use, and checking this on the notification path. Devices that - * don't want to follow the spec suggestion can declare all vectors as used. */ - -/* Mark vector as used. */ -int msix_vector_use(PCIDevice *dev, unsigned vector) -{ - if (vector >= dev->msix_entries_nr) - return -EINVAL; - dev->msix_entry_used[vector]++; - return 0; -} - -/* Mark vector as unused. */ -void msix_vector_unuse(PCIDevice *dev, unsigned vector) -{ - if (vector >= dev->msix_entries_nr || !dev->msix_entry_used[vector]) { - return; - } - if (--dev->msix_entry_used[vector]) { - return; - } - msix_clr_pending(dev, vector); -} - -void msix_unuse_all_vectors(PCIDevice *dev) -{ - if (!msix_present(dev)) { - return; - } - msix_free_irq_entries(dev); -} - -unsigned int msix_nr_vectors_allocated(const PCIDevice *dev) -{ - return dev->msix_entries_nr; -} - -static int msix_set_notifier_for_vector(PCIDevice *dev, unsigned int vector) -{ - MSIMessage msg; - - if (msix_is_masked(dev, vector)) { - return 0; - } - msg = msix_get_message(dev, vector); - return dev->msix_vector_use_notifier(dev, vector, msg); -} - -static void msix_unset_notifier_for_vector(PCIDevice *dev, unsigned int vector) -{ - if (msix_is_masked(dev, vector)) { - return; - } - dev->msix_vector_release_notifier(dev, vector); -} - -int msix_set_vector_notifiers(PCIDevice *dev, - MSIVectorUseNotifier use_notifier, - MSIVectorReleaseNotifier release_notifier) -{ - int vector, ret; - - assert(use_notifier && release_notifier); - - dev->msix_vector_use_notifier = use_notifier; - dev->msix_vector_release_notifier = release_notifier; - - if ((dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] & - (MSIX_ENABLE_MASK | MSIX_MASKALL_MASK)) == MSIX_ENABLE_MASK) { - for (vector = 0; vector < dev->msix_entries_nr; vector++) { - ret = msix_set_notifier_for_vector(dev, vector); - if (ret < 0) { - goto undo; - } - } - } - return 0; - -undo: - while (--vector >= 0) { - msix_unset_notifier_for_vector(dev, vector); - } - dev->msix_vector_use_notifier = NULL; - dev->msix_vector_release_notifier = NULL; - return ret; -} - -void msix_unset_vector_notifiers(PCIDevice *dev) -{ - int vector; - - assert(dev->msix_vector_use_notifier && - dev->msix_vector_release_notifier); - - if ((dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] & - (MSIX_ENABLE_MASK | MSIX_MASKALL_MASK)) == MSIX_ENABLE_MASK) { - for (vector = 0; vector < dev->msix_entries_nr; vector++) { - msix_unset_notifier_for_vector(dev, vector); - } - } - dev->msix_vector_use_notifier = NULL; - dev->msix_vector_release_notifier = NULL; -} diff --git a/hw/msix.h b/hw/msix.h deleted file mode 100644 index 15211cb592..0000000000 --- a/hw/msix.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef QEMU_MSIX_H -#define QEMU_MSIX_H - -#include "qemu-common.h" -#include "pci.h" - -void msix_set_message(PCIDevice *dev, int vector, MSIMessage msg); -int msix_init(PCIDevice *dev, unsigned short nentries, - MemoryRegion *table_bar, uint8_t table_bar_nr, - unsigned table_offset, MemoryRegion *pba_bar, - uint8_t pba_bar_nr, unsigned pba_offset, uint8_t cap_pos); -int msix_init_exclusive_bar(PCIDevice *dev, unsigned short nentries, - uint8_t bar_nr); - -void msix_write_config(PCIDevice *dev, uint32_t address, uint32_t val, int len); - -void msix_uninit(PCIDevice *dev, MemoryRegion *table_bar, - MemoryRegion *pba_bar); -void msix_uninit_exclusive_bar(PCIDevice *dev); - -unsigned int msix_nr_vectors_allocated(const PCIDevice *dev); - -void msix_save(PCIDevice *dev, QEMUFile *f); -void msix_load(PCIDevice *dev, QEMUFile *f); - -int msix_enabled(PCIDevice *dev); -int msix_present(PCIDevice *dev); - -int msix_vector_use(PCIDevice *dev, unsigned vector); -void msix_vector_unuse(PCIDevice *dev, unsigned vector); -void msix_unuse_all_vectors(PCIDevice *dev); - -void msix_notify(PCIDevice *dev, unsigned vector); - -void msix_reset(PCIDevice *dev); - -int msix_set_vector_notifiers(PCIDevice *dev, - MSIVectorUseNotifier use_notifier, - MSIVectorReleaseNotifier release_notifier); -void msix_unset_vector_notifiers(PCIDevice *dev); -#endif diff --git a/hw/pci-hotplug.c b/hw/pci-hotplug.c deleted file mode 100644 index 3bcfdcc1a6..0000000000 --- a/hw/pci-hotplug.c +++ /dev/null @@ -1,292 +0,0 @@ -/* - * QEMU PCI hotplug support - * - * Copyright (c) 2004 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "hw.h" -#include "boards.h" -#include "pci.h" -#include "net.h" -#include "pc.h" -#include "monitor.h" -#include "scsi.h" -#include "virtio-blk.h" -#include "qemu-config.h" -#include "blockdev.h" -#include "error.h" - -#if defined(TARGET_I386) -static PCIDevice *qemu_pci_hot_add_nic(Monitor *mon, - const char *devaddr, - const char *opts_str) -{ - Error *local_err = NULL; - QemuOpts *opts; - PCIBus *bus; - int ret, devfn; - - bus = pci_get_bus_devfn(&devfn, devaddr); - if (!bus) { - monitor_printf(mon, "Invalid PCI device address %s\n", devaddr); - return NULL; - } - if (!((BusState*)bus)->allow_hotplug) { - monitor_printf(mon, "PCI bus doesn't support hotplug\n"); - return NULL; - } - - opts = qemu_opts_parse(qemu_find_opts("net"), opts_str ? opts_str : "", 0); - if (!opts) { - return NULL; - } - - qemu_opt_set(opts, "type", "nic"); - - ret = net_client_init(opts, 0, &local_err); - if (error_is_set(&local_err)) { - qerror_report_err(local_err); - error_free(local_err); - return NULL; - } - if (nd_table[ret].devaddr) { - monitor_printf(mon, "Parameter addr not supported\n"); - return NULL; - } - return pci_nic_init(&nd_table[ret], "rtl8139", devaddr); -} - -static int scsi_hot_add(Monitor *mon, DeviceState *adapter, - DriveInfo *dinfo, int printinfo) -{ - SCSIBus *scsibus; - SCSIDevice *scsidev; - - scsibus = (SCSIBus *) - object_dynamic_cast(OBJECT(QLIST_FIRST(&adapter->child_bus)), - TYPE_SCSI_BUS); - if (!scsibus) { - error_report("Device is not a SCSI adapter"); - return -1; - } - - /* - * drive_init() tries to find a default for dinfo->unit. Doesn't - * work at all for hotplug though as we assign the device to a - * specific bus instead of the first bus with spare scsi ids. - * - * Ditch the calculated value and reload from option string (if - * specified). - */ - dinfo->unit = qemu_opt_get_number(dinfo->opts, "unit", -1); - dinfo->bus = scsibus->busnr; - scsidev = scsi_bus_legacy_add_drive(scsibus, dinfo->bdrv, dinfo->unit, - false, -1); - if (!scsidev) { - return -1; - } - dinfo->unit = scsidev->id; - - if (printinfo) - monitor_printf(mon, "OK bus %d, unit %d\n", - scsibus->busnr, scsidev->id); - return 0; -} - -int pci_drive_hot_add(Monitor *mon, const QDict *qdict, DriveInfo *dinfo) -{ - int dom, pci_bus; - unsigned slot; - PCIDevice *dev; - const char *pci_addr = qdict_get_str(qdict, "pci_addr"); - - switch (dinfo->type) { - case IF_SCSI: - if (pci_read_devaddr(mon, pci_addr, &dom, &pci_bus, &slot)) { - goto err; - } - dev = pci_find_device(pci_find_root_bus(dom), pci_bus, - PCI_DEVFN(slot, 0)); - if (!dev) { - monitor_printf(mon, "no pci device with address %s\n", pci_addr); - goto err; - } - if (scsi_hot_add(mon, &dev->qdev, dinfo, 1) != 0) { - goto err; - } - break; - default: - monitor_printf(mon, "Can't hot-add drive to type %d\n", dinfo->type); - goto err; - } - - return 0; -err: - return -1; -} - -static PCIDevice *qemu_pci_hot_add_storage(Monitor *mon, - const char *devaddr, - const char *opts) -{ - PCIDevice *dev; - DriveInfo *dinfo = NULL; - int type = -1; - char buf[128]; - PCIBus *bus; - int devfn; - - if (get_param_value(buf, sizeof(buf), "if", opts)) { - if (!strcmp(buf, "scsi")) - type = IF_SCSI; - else if (!strcmp(buf, "virtio")) { - type = IF_VIRTIO; - } else { - monitor_printf(mon, "type %s not a hotpluggable PCI device.\n", buf); - return NULL; - } - } else { - monitor_printf(mon, "no if= specified\n"); - return NULL; - } - - if (get_param_value(buf, sizeof(buf), "file", opts)) { - dinfo = add_init_drive(opts); - if (!dinfo) - return NULL; - if (dinfo->devaddr) { - monitor_printf(mon, "Parameter addr not supported\n"); - return NULL; - } - } else { - dinfo = NULL; - } - - bus = pci_get_bus_devfn(&devfn, devaddr); - if (!bus) { - monitor_printf(mon, "Invalid PCI device address %s\n", devaddr); - return NULL; - } - if (!((BusState*)bus)->allow_hotplug) { - monitor_printf(mon, "PCI bus doesn't support hotplug\n"); - return NULL; - } - - switch (type) { - case IF_SCSI: - dev = pci_create(bus, devfn, "lsi53c895a"); - if (qdev_init(&dev->qdev) < 0) - dev = NULL; - if (dev && dinfo) { - if (scsi_hot_add(mon, &dev->qdev, dinfo, 0) != 0) { - qdev_unplug(&dev->qdev, NULL); - dev = NULL; - } - } - break; - case IF_VIRTIO: - if (!dinfo) { - monitor_printf(mon, "virtio requires a backing file/device.\n"); - return NULL; - } - dev = pci_create(bus, devfn, "virtio-blk-pci"); - if (qdev_prop_set_drive(&dev->qdev, "drive", dinfo->bdrv) < 0) { - qdev_free(&dev->qdev); - dev = NULL; - break; - } - if (qdev_init(&dev->qdev) < 0) - dev = NULL; - break; - default: - dev = NULL; - } - return dev; -} - -void pci_device_hot_add(Monitor *mon, const QDict *qdict) -{ - PCIDevice *dev = NULL; - const char *pci_addr = qdict_get_str(qdict, "pci_addr"); - const char *type = qdict_get_str(qdict, "type"); - const char *opts = qdict_get_try_str(qdict, "opts"); - - /* strip legacy tag */ - if (!strncmp(pci_addr, "pci_addr=", 9)) { - pci_addr += 9; - } - - if (!opts) { - opts = ""; - } - - if (!strcmp(pci_addr, "auto")) - pci_addr = NULL; - - if (strcmp(type, "nic") == 0) { - dev = qemu_pci_hot_add_nic(mon, pci_addr, opts); - } else if (strcmp(type, "storage") == 0) { - dev = qemu_pci_hot_add_storage(mon, pci_addr, opts); - } else { - monitor_printf(mon, "invalid type: %s\n", type); - } - - if (dev) { - monitor_printf(mon, "OK domain %d, bus %d, slot %d, function %d\n", - pci_find_domain(dev->bus), - pci_bus_num(dev->bus), PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn)); - } else - monitor_printf(mon, "failed to add %s\n", opts); -} -#endif - -static int pci_device_hot_remove(Monitor *mon, const char *pci_addr) -{ - PCIDevice *d; - int dom, bus; - unsigned slot; - Error *local_err = NULL; - - if (pci_read_devaddr(mon, pci_addr, &dom, &bus, &slot)) { - return -1; - } - - d = pci_find_device(pci_find_root_bus(dom), bus, PCI_DEVFN(slot, 0)); - if (!d) { - monitor_printf(mon, "slot %d empty\n", slot); - return -1; - } - - qdev_unplug(&d->qdev, &local_err); - if (error_is_set(&local_err)) { - monitor_printf(mon, "%s\n", error_get_pretty(local_err)); - error_free(local_err); - return -1; - } - - return 0; -} - -void do_pci_device_hot_remove(Monitor *mon, const QDict *qdict) -{ - pci_device_hot_remove(mon, qdict_get_str(qdict, "pci_addr")); -} diff --git a/hw/pci-stub.c b/hw/pci-stub.c deleted file mode 100644 index 134c4484b6..0000000000 --- a/hw/pci-stub.c +++ /dev/null @@ -1,47 +0,0 @@ -/* - * PCI stubs for platforms that don't support pci bus. - * - * Copyright (c) 2010 Isaku Yamahata - * VA Linux Systems Japan K.K. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "sysemu.h" -#include "monitor.h" -#include "pci.h" -#include "qmp-commands.h" - -PciInfoList *qmp_query_pci(Error **errp) -{ - error_set(errp, QERR_UNSUPPORTED); - return NULL; -} - -static void pci_error_message(Monitor *mon) -{ - monitor_printf(mon, "PCI devices not supported\n"); -} - -int do_pcie_aer_inject_error(Monitor *mon, - const QDict *qdict, QObject **ret_data) -{ - pci_error_message(mon); - return -ENOSYS; -} - -void pcie_aer_inject_error_print(Monitor *mon, const QObject *data) -{ - pci_error_message(mon); -} diff --git a/hw/pci.c b/hw/pci.c deleted file mode 100644 index 97a0cd77c1..0000000000 --- a/hw/pci.c +++ /dev/null @@ -1,2168 +0,0 @@ -/* - * QEMU PCI bus manager - * - * Copyright (c) 2004 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "hw.h" -#include "pci.h" -#include "pci_bridge.h" -#include "pci_internals.h" -#include "monitor.h" -#include "net.h" -#include "sysemu.h" -#include "loader.h" -#include "range.h" -#include "qmp-commands.h" -#include "msi.h" -#include "msix.h" -#include "exec-memory.h" - -//#define DEBUG_PCI -#ifdef DEBUG_PCI -# define PCI_DPRINTF(format, ...) printf(format, ## __VA_ARGS__) -#else -# define PCI_DPRINTF(format, ...) do { } while (0) -#endif - -static void pcibus_dev_print(Monitor *mon, DeviceState *dev, int indent); -static char *pcibus_get_dev_path(DeviceState *dev); -static char *pcibus_get_fw_dev_path(DeviceState *dev); -static int pcibus_reset(BusState *qbus); - -static Property pci_props[] = { - DEFINE_PROP_PCI_DEVFN("addr", PCIDevice, devfn, -1), - DEFINE_PROP_STRING("romfile", PCIDevice, romfile), - DEFINE_PROP_UINT32("rombar", PCIDevice, rom_bar, 1), - DEFINE_PROP_BIT("multifunction", PCIDevice, cap_present, - QEMU_PCI_CAP_MULTIFUNCTION_BITNR, false), - DEFINE_PROP_BIT("command_serr_enable", PCIDevice, cap_present, - QEMU_PCI_CAP_SERR_BITNR, true), - DEFINE_PROP_END_OF_LIST() -}; - -static void pci_bus_class_init(ObjectClass *klass, void *data) -{ - BusClass *k = BUS_CLASS(klass); - - k->print_dev = pcibus_dev_print; - k->get_dev_path = pcibus_get_dev_path; - k->get_fw_dev_path = pcibus_get_fw_dev_path; - k->reset = pcibus_reset; -} - -static const TypeInfo pci_bus_info = { - .name = TYPE_PCI_BUS, - .parent = TYPE_BUS, - .instance_size = sizeof(PCIBus), - .class_init = pci_bus_class_init, -}; - -static PCIBus *pci_find_bus_nr(PCIBus *bus, int bus_num); -static void pci_update_mappings(PCIDevice *d); -static void pci_set_irq(void *opaque, int irq_num, int level); -static int pci_add_option_rom(PCIDevice *pdev, bool is_default_rom); -static void pci_del_option_rom(PCIDevice *pdev); - -static uint16_t pci_default_sub_vendor_id = PCI_SUBVENDOR_ID_REDHAT_QUMRANET; -static uint16_t pci_default_sub_device_id = PCI_SUBDEVICE_ID_QEMU; - -struct PCIHostBus { - int domain; - struct PCIBus *bus; - QLIST_ENTRY(PCIHostBus) next; -}; -static QLIST_HEAD(, PCIHostBus) host_buses; - -static const VMStateDescription vmstate_pcibus = { - .name = "PCIBUS", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField []) { - VMSTATE_INT32_EQUAL(nirq, PCIBus), - VMSTATE_VARRAY_INT32(irq_count, PCIBus, nirq, 0, vmstate_info_int32, int32_t), - VMSTATE_END_OF_LIST() - } -}; -static int pci_bar(PCIDevice *d, int reg) -{ - uint8_t type; - - if (reg != PCI_ROM_SLOT) - return PCI_BASE_ADDRESS_0 + reg * 4; - - type = d->config[PCI_HEADER_TYPE] & ~PCI_HEADER_TYPE_MULTI_FUNCTION; - return type == PCI_HEADER_TYPE_BRIDGE ? PCI_ROM_ADDRESS1 : PCI_ROM_ADDRESS; -} - -static inline int pci_irq_state(PCIDevice *d, int irq_num) -{ - return (d->irq_state >> irq_num) & 0x1; -} - -static inline void pci_set_irq_state(PCIDevice *d, int irq_num, int level) -{ - d->irq_state &= ~(0x1 << irq_num); - d->irq_state |= level << irq_num; -} - -static void pci_change_irq_level(PCIDevice *pci_dev, int irq_num, int change) -{ - PCIBus *bus; - for (;;) { - bus = pci_dev->bus; - irq_num = bus->map_irq(pci_dev, irq_num); - if (bus->set_irq) - break; - pci_dev = bus->parent_dev; - } - bus->irq_count[irq_num] += change; - bus->set_irq(bus->irq_opaque, irq_num, bus->irq_count[irq_num] != 0); -} - -int pci_bus_get_irq_level(PCIBus *bus, int irq_num) -{ - assert(irq_num >= 0); - assert(irq_num < bus->nirq); - return !!bus->irq_count[irq_num]; -} - -/* Update interrupt status bit in config space on interrupt - * state change. */ -static void pci_update_irq_status(PCIDevice *dev) -{ - if (dev->irq_state) { - dev->config[PCI_STATUS] |= PCI_STATUS_INTERRUPT; - } else { - dev->config[PCI_STATUS] &= ~PCI_STATUS_INTERRUPT; - } -} - -void pci_device_deassert_intx(PCIDevice *dev) -{ - int i; - for (i = 0; i < PCI_NUM_PINS; ++i) { - qemu_set_irq(dev->irq[i], 0); - } -} - -/* - * This function is called on #RST and FLR. - * FLR if PCI_EXP_DEVCTL_BCR_FLR is set - */ -void pci_device_reset(PCIDevice *dev) -{ - int r; - - qdev_reset_all(&dev->qdev); - - dev->irq_state = 0; - pci_update_irq_status(dev); - pci_device_deassert_intx(dev); - /* Clear all writable bits */ - pci_word_test_and_clear_mask(dev->config + PCI_COMMAND, - pci_get_word(dev->wmask + PCI_COMMAND) | - pci_get_word(dev->w1cmask + PCI_COMMAND)); - pci_word_test_and_clear_mask(dev->config + PCI_STATUS, - pci_get_word(dev->wmask + PCI_STATUS) | - pci_get_word(dev->w1cmask + PCI_STATUS)); - dev->config[PCI_CACHE_LINE_SIZE] = 0x0; - dev->config[PCI_INTERRUPT_LINE] = 0x0; - for (r = 0; r < PCI_NUM_REGIONS; ++r) { - PCIIORegion *region = &dev->io_regions[r]; - if (!region->size) { - continue; - } - - if (!(region->type & PCI_BASE_ADDRESS_SPACE_IO) && - region->type & PCI_BASE_ADDRESS_MEM_TYPE_64) { - pci_set_quad(dev->config + pci_bar(dev, r), region->type); - } else { - pci_set_long(dev->config + pci_bar(dev, r), region->type); - } - } - pci_update_mappings(dev); - - msi_reset(dev); - msix_reset(dev); -} - -/* - * Trigger pci bus reset under a given bus. - * To be called on RST# assert. - */ -void pci_bus_reset(PCIBus *bus) -{ - int i; - - for (i = 0; i < bus->nirq; i++) { - bus->irq_count[i] = 0; - } - for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) { - if (bus->devices[i]) { - pci_device_reset(bus->devices[i]); - } - } -} - -static int pcibus_reset(BusState *qbus) -{ - pci_bus_reset(DO_UPCAST(PCIBus, qbus, qbus)); - - /* topology traverse is done by pci_bus_reset(). - Tell qbus/qdev walker not to traverse the tree */ - return 1; -} - -static void pci_host_bus_register(int domain, PCIBus *bus) -{ - struct PCIHostBus *host; - host = g_malloc0(sizeof(*host)); - host->domain = domain; - host->bus = bus; - QLIST_INSERT_HEAD(&host_buses, host, next); -} - -PCIBus *pci_find_root_bus(int domain) -{ - struct PCIHostBus *host; - - QLIST_FOREACH(host, &host_buses, next) { - if (host->domain == domain) { - return host->bus; - } - } - - return NULL; -} - -int pci_find_domain(const PCIBus *bus) -{ - PCIDevice *d; - struct PCIHostBus *host; - - /* obtain root bus */ - while ((d = bus->parent_dev) != NULL) { - bus = d->bus; - } - - QLIST_FOREACH(host, &host_buses, next) { - if (host->bus == bus) { - return host->domain; - } - } - - abort(); /* should not be reached */ - return -1; -} - -void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent, - const char *name, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io, - uint8_t devfn_min) -{ - qbus_create_inplace(&bus->qbus, TYPE_PCI_BUS, parent, name); - assert(PCI_FUNC(devfn_min) == 0); - bus->devfn_min = devfn_min; - bus->address_space_mem = address_space_mem; - bus->address_space_io = address_space_io; - - /* host bridge */ - QLIST_INIT(&bus->child); - pci_host_bus_register(0, bus); /* for now only pci domain 0 is supported */ - - vmstate_register(NULL, -1, &vmstate_pcibus, bus); -} - -PCIBus *pci_bus_new(DeviceState *parent, const char *name, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io, - uint8_t devfn_min) -{ - PCIBus *bus; - - bus = g_malloc0(sizeof(*bus)); - pci_bus_new_inplace(bus, parent, name, address_space_mem, - address_space_io, devfn_min); - OBJECT(bus)->free = g_free; - return bus; -} - -void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, - void *irq_opaque, int nirq) -{ - bus->set_irq = set_irq; - bus->map_irq = map_irq; - bus->irq_opaque = irq_opaque; - bus->nirq = nirq; - bus->irq_count = g_malloc0(nirq * sizeof(bus->irq_count[0])); -} - -void pci_bus_hotplug(PCIBus *bus, pci_hotplug_fn hotplug, DeviceState *qdev) -{ - bus->qbus.allow_hotplug = 1; - bus->hotplug = hotplug; - bus->hotplug_qdev = qdev; -} - -PCIBus *pci_register_bus(DeviceState *parent, const char *name, - pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, - void *irq_opaque, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io, - uint8_t devfn_min, int nirq) -{ - PCIBus *bus; - - bus = pci_bus_new(parent, name, address_space_mem, - address_space_io, devfn_min); - pci_bus_irqs(bus, set_irq, map_irq, irq_opaque, nirq); - return bus; -} - -int pci_bus_num(PCIBus *s) -{ - if (!s->parent_dev) - return 0; /* pci host bridge */ - return s->parent_dev->config[PCI_SECONDARY_BUS]; -} - -static int get_pci_config_device(QEMUFile *f, void *pv, size_t size) -{ - PCIDevice *s = container_of(pv, PCIDevice, config); - uint8_t *config; - int i; - - assert(size == pci_config_size(s)); - config = g_malloc(size); - - qemu_get_buffer(f, config, size); - for (i = 0; i < size; ++i) { - if ((config[i] ^ s->config[i]) & - s->cmask[i] & ~s->wmask[i] & ~s->w1cmask[i]) { - g_free(config); - return -EINVAL; - } - } - memcpy(s->config, config, size); - - pci_update_mappings(s); - - memory_region_set_enabled(&s->bus_master_enable_region, - pci_get_word(s->config + PCI_COMMAND) - & PCI_COMMAND_MASTER); - - g_free(config); - return 0; -} - -/* just put buffer */ -static void put_pci_config_device(QEMUFile *f, void *pv, size_t size) -{ - const uint8_t **v = pv; - assert(size == pci_config_size(container_of(pv, PCIDevice, config))); - qemu_put_buffer(f, *v, size); -} - -static VMStateInfo vmstate_info_pci_config = { - .name = "pci config", - .get = get_pci_config_device, - .put = put_pci_config_device, -}; - -static int get_pci_irq_state(QEMUFile *f, void *pv, size_t size) -{ - PCIDevice *s = container_of(pv, PCIDevice, irq_state); - uint32_t irq_state[PCI_NUM_PINS]; - int i; - for (i = 0; i < PCI_NUM_PINS; ++i) { - irq_state[i] = qemu_get_be32(f); - if (irq_state[i] != 0x1 && irq_state[i] != 0) { - fprintf(stderr, "irq state %d: must be 0 or 1.\n", - irq_state[i]); - return -EINVAL; - } - } - - for (i = 0; i < PCI_NUM_PINS; ++i) { - pci_set_irq_state(s, i, irq_state[i]); - } - - return 0; -} - -static void put_pci_irq_state(QEMUFile *f, void *pv, size_t size) -{ - int i; - PCIDevice *s = container_of(pv, PCIDevice, irq_state); - - for (i = 0; i < PCI_NUM_PINS; ++i) { - qemu_put_be32(f, pci_irq_state(s, i)); - } -} - -static VMStateInfo vmstate_info_pci_irq_state = { - .name = "pci irq state", - .get = get_pci_irq_state, - .put = put_pci_irq_state, -}; - -const VMStateDescription vmstate_pci_device = { - .name = "PCIDevice", - .version_id = 2, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField []) { - VMSTATE_INT32_LE(version_id, PCIDevice), - VMSTATE_BUFFER_UNSAFE_INFO(config, PCIDevice, 0, - vmstate_info_pci_config, - PCI_CONFIG_SPACE_SIZE), - VMSTATE_BUFFER_UNSAFE_INFO(irq_state, PCIDevice, 2, - vmstate_info_pci_irq_state, - PCI_NUM_PINS * sizeof(int32_t)), - VMSTATE_END_OF_LIST() - } -}; - -const VMStateDescription vmstate_pcie_device = { - .name = "PCIEDevice", - .version_id = 2, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField []) { - VMSTATE_INT32_LE(version_id, PCIDevice), - VMSTATE_BUFFER_UNSAFE_INFO(config, PCIDevice, 0, - vmstate_info_pci_config, - PCIE_CONFIG_SPACE_SIZE), - VMSTATE_BUFFER_UNSAFE_INFO(irq_state, PCIDevice, 2, - vmstate_info_pci_irq_state, - PCI_NUM_PINS * sizeof(int32_t)), - VMSTATE_END_OF_LIST() - } -}; - -static inline const VMStateDescription *pci_get_vmstate(PCIDevice *s) -{ - return pci_is_express(s) ? &vmstate_pcie_device : &vmstate_pci_device; -} - -void pci_device_save(PCIDevice *s, QEMUFile *f) -{ - /* Clear interrupt status bit: it is implicit - * in irq_state which we are saving. - * This makes us compatible with old devices - * which never set or clear this bit. */ - s->config[PCI_STATUS] &= ~PCI_STATUS_INTERRUPT; - vmstate_save_state(f, pci_get_vmstate(s), s); - /* Restore the interrupt status bit. */ - pci_update_irq_status(s); -} - -int pci_device_load(PCIDevice *s, QEMUFile *f) -{ - int ret; - ret = vmstate_load_state(f, pci_get_vmstate(s), s, s->version_id); - /* Restore the interrupt status bit. */ - pci_update_irq_status(s); - return ret; -} - -static void pci_set_default_subsystem_id(PCIDevice *pci_dev) -{ - pci_set_word(pci_dev->config + PCI_SUBSYSTEM_VENDOR_ID, - pci_default_sub_vendor_id); - pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID, - pci_default_sub_device_id); -} - -/* - * Parse [[:]:], return -1 on error if funcp == NULL - * [[:]:]., return -1 on error - */ -static int pci_parse_devaddr(const char *addr, int *domp, int *busp, - unsigned int *slotp, unsigned int *funcp) -{ - const char *p; - char *e; - unsigned long val; - unsigned long dom = 0, bus = 0; - unsigned int slot = 0; - unsigned int func = 0; - - p = addr; - val = strtoul(p, &e, 16); - if (e == p) - return -1; - if (*e == ':') { - bus = val; - p = e + 1; - val = strtoul(p, &e, 16); - if (e == p) - return -1; - if (*e == ':') { - dom = bus; - bus = val; - p = e + 1; - val = strtoul(p, &e, 16); - if (e == p) - return -1; - } - } - - slot = val; - - if (funcp != NULL) { - if (*e != '.') - return -1; - - p = e + 1; - val = strtoul(p, &e, 16); - if (e == p) - return -1; - - func = val; - } - - /* if funcp == NULL func is 0 */ - if (dom > 0xffff || bus > 0xff || slot > 0x1f || func > 7) - return -1; - - if (*e) - return -1; - - *domp = dom; - *busp = bus; - *slotp = slot; - if (funcp != NULL) - *funcp = func; - return 0; -} - -int pci_read_devaddr(Monitor *mon, const char *addr, int *domp, int *busp, - unsigned *slotp) -{ - /* strip legacy tag */ - if (!strncmp(addr, "pci_addr=", 9)) { - addr += 9; - } - if (pci_parse_devaddr(addr, domp, busp, slotp, NULL)) { - monitor_printf(mon, "Invalid pci address\n"); - return -1; - } - return 0; -} - -PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr) -{ - int dom, bus; - unsigned slot; - - if (!devaddr) { - *devfnp = -1; - return pci_find_bus_nr(pci_find_root_bus(0), 0); - } - - if (pci_parse_devaddr(devaddr, &dom, &bus, &slot, NULL) < 0) { - return NULL; - } - - *devfnp = PCI_DEVFN(slot, 0); - return pci_find_bus_nr(pci_find_root_bus(dom), bus); -} - -static void pci_init_cmask(PCIDevice *dev) -{ - pci_set_word(dev->cmask + PCI_VENDOR_ID, 0xffff); - pci_set_word(dev->cmask + PCI_DEVICE_ID, 0xffff); - dev->cmask[PCI_STATUS] = PCI_STATUS_CAP_LIST; - dev->cmask[PCI_REVISION_ID] = 0xff; - dev->cmask[PCI_CLASS_PROG] = 0xff; - pci_set_word(dev->cmask + PCI_CLASS_DEVICE, 0xffff); - dev->cmask[PCI_HEADER_TYPE] = 0xff; - dev->cmask[PCI_CAPABILITY_LIST] = 0xff; -} - -static void pci_init_wmask(PCIDevice *dev) -{ - int config_size = pci_config_size(dev); - - dev->wmask[PCI_CACHE_LINE_SIZE] = 0xff; - dev->wmask[PCI_INTERRUPT_LINE] = 0xff; - pci_set_word(dev->wmask + PCI_COMMAND, - PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | - PCI_COMMAND_INTX_DISABLE); - if (dev->cap_present & QEMU_PCI_CAP_SERR) { - pci_word_test_and_set_mask(dev->wmask + PCI_COMMAND, PCI_COMMAND_SERR); - } - - memset(dev->wmask + PCI_CONFIG_HEADER_SIZE, 0xff, - config_size - PCI_CONFIG_HEADER_SIZE); -} - -static void pci_init_w1cmask(PCIDevice *dev) -{ - /* - * Note: It's okay to set w1cmask even for readonly bits as - * long as their value is hardwired to 0. - */ - pci_set_word(dev->w1cmask + PCI_STATUS, - PCI_STATUS_PARITY | PCI_STATUS_SIG_TARGET_ABORT | - PCI_STATUS_REC_TARGET_ABORT | PCI_STATUS_REC_MASTER_ABORT | - PCI_STATUS_SIG_SYSTEM_ERROR | PCI_STATUS_DETECTED_PARITY); -} - -static void pci_init_mask_bridge(PCIDevice *d) -{ - /* PCI_PRIMARY_BUS, PCI_SECONDARY_BUS, PCI_SUBORDINATE_BUS and - PCI_SEC_LETENCY_TIMER */ - memset(d->wmask + PCI_PRIMARY_BUS, 0xff, 4); - - /* base and limit */ - d->wmask[PCI_IO_BASE] = PCI_IO_RANGE_MASK & 0xff; - d->wmask[PCI_IO_LIMIT] = PCI_IO_RANGE_MASK & 0xff; - pci_set_word(d->wmask + PCI_MEMORY_BASE, - PCI_MEMORY_RANGE_MASK & 0xffff); - pci_set_word(d->wmask + PCI_MEMORY_LIMIT, - PCI_MEMORY_RANGE_MASK & 0xffff); - pci_set_word(d->wmask + PCI_PREF_MEMORY_BASE, - PCI_PREF_RANGE_MASK & 0xffff); - pci_set_word(d->wmask + PCI_PREF_MEMORY_LIMIT, - PCI_PREF_RANGE_MASK & 0xffff); - - /* PCI_PREF_BASE_UPPER32 and PCI_PREF_LIMIT_UPPER32 */ - memset(d->wmask + PCI_PREF_BASE_UPPER32, 0xff, 8); - - /* Supported memory and i/o types */ - d->config[PCI_IO_BASE] |= PCI_IO_RANGE_TYPE_16; - d->config[PCI_IO_LIMIT] |= PCI_IO_RANGE_TYPE_16; - pci_word_test_and_set_mask(d->config + PCI_PREF_MEMORY_BASE, - PCI_PREF_RANGE_TYPE_64); - pci_word_test_and_set_mask(d->config + PCI_PREF_MEMORY_LIMIT, - PCI_PREF_RANGE_TYPE_64); - -/* TODO: add this define to pci_regs.h in linux and then in qemu. */ -#define PCI_BRIDGE_CTL_VGA_16BIT 0x10 /* VGA 16-bit decode */ -#define PCI_BRIDGE_CTL_DISCARD 0x100 /* Primary discard timer */ -#define PCI_BRIDGE_CTL_SEC_DISCARD 0x200 /* Secondary discard timer */ -#define PCI_BRIDGE_CTL_DISCARD_STATUS 0x400 /* Discard timer status */ -#define PCI_BRIDGE_CTL_DISCARD_SERR 0x800 /* Discard timer SERR# enable */ - pci_set_word(d->wmask + PCI_BRIDGE_CONTROL, - PCI_BRIDGE_CTL_PARITY | - PCI_BRIDGE_CTL_SERR | - PCI_BRIDGE_CTL_ISA | - PCI_BRIDGE_CTL_VGA | - PCI_BRIDGE_CTL_VGA_16BIT | - PCI_BRIDGE_CTL_MASTER_ABORT | - PCI_BRIDGE_CTL_BUS_RESET | - PCI_BRIDGE_CTL_FAST_BACK | - PCI_BRIDGE_CTL_DISCARD | - PCI_BRIDGE_CTL_SEC_DISCARD | - PCI_BRIDGE_CTL_DISCARD_SERR); - /* Below does not do anything as we never set this bit, put here for - * completeness. */ - pci_set_word(d->w1cmask + PCI_BRIDGE_CONTROL, - PCI_BRIDGE_CTL_DISCARD_STATUS); - d->cmask[PCI_IO_BASE] |= PCI_IO_RANGE_TYPE_MASK; - d->cmask[PCI_IO_LIMIT] |= PCI_IO_RANGE_TYPE_MASK; - pci_word_test_and_set_mask(d->cmask + PCI_PREF_MEMORY_BASE, - PCI_PREF_RANGE_TYPE_MASK); - pci_word_test_and_set_mask(d->cmask + PCI_PREF_MEMORY_LIMIT, - PCI_PREF_RANGE_TYPE_MASK); -} - -static int pci_init_multifunction(PCIBus *bus, PCIDevice *dev) -{ - uint8_t slot = PCI_SLOT(dev->devfn); - uint8_t func; - - if (dev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { - dev->config[PCI_HEADER_TYPE] |= PCI_HEADER_TYPE_MULTI_FUNCTION; - } - - /* - * multifunction bit is interpreted in two ways as follows. - * - all functions must set the bit to 1. - * Example: Intel X53 - * - function 0 must set the bit, but the rest function (> 0) - * is allowed to leave the bit to 0. - * Example: PIIX3(also in qemu), PIIX4(also in qemu), ICH10, - * - * So OS (at least Linux) checks the bit of only function 0, - * and doesn't see the bit of function > 0. - * - * The below check allows both interpretation. - */ - if (PCI_FUNC(dev->devfn)) { - PCIDevice *f0 = bus->devices[PCI_DEVFN(slot, 0)]; - if (f0 && !(f0->cap_present & QEMU_PCI_CAP_MULTIFUNCTION)) { - /* function 0 should set multifunction bit */ - error_report("PCI: single function device can't be populated " - "in function %x.%x", slot, PCI_FUNC(dev->devfn)); - return -1; - } - return 0; - } - - if (dev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { - return 0; - } - /* function 0 indicates single function, so function > 0 must be NULL */ - for (func = 1; func < PCI_FUNC_MAX; ++func) { - if (bus->devices[PCI_DEVFN(slot, func)]) { - error_report("PCI: %x.0 indicates single function, " - "but %x.%x is already populated.", - slot, slot, func); - return -1; - } - } - return 0; -} - -static void pci_config_alloc(PCIDevice *pci_dev) -{ - int config_size = pci_config_size(pci_dev); - - pci_dev->config = g_malloc0(config_size); - pci_dev->cmask = g_malloc0(config_size); - pci_dev->wmask = g_malloc0(config_size); - pci_dev->w1cmask = g_malloc0(config_size); - pci_dev->used = g_malloc0(config_size); -} - -static void pci_config_free(PCIDevice *pci_dev) -{ - g_free(pci_dev->config); - g_free(pci_dev->cmask); - g_free(pci_dev->wmask); - g_free(pci_dev->w1cmask); - g_free(pci_dev->used); -} - -/* -1 for devfn means auto assign */ -static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, - const char *name, int devfn) -{ - PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(pci_dev); - PCIConfigReadFunc *config_read = pc->config_read; - PCIConfigWriteFunc *config_write = pc->config_write; - - if (devfn < 0) { - for(devfn = bus->devfn_min ; devfn < ARRAY_SIZE(bus->devices); - devfn += PCI_FUNC_MAX) { - if (!bus->devices[devfn]) - goto found; - } - error_report("PCI: no slot/function available for %s, all in use", name); - return NULL; - found: ; - } else if (bus->devices[devfn]) { - error_report("PCI: slot %d function %d not available for %s, in use by %s", - PCI_SLOT(devfn), PCI_FUNC(devfn), name, bus->devices[devfn]->name); - return NULL; - } - pci_dev->bus = bus; - if (bus->dma_context_fn) { - pci_dev->dma = bus->dma_context_fn(bus, bus->dma_context_opaque, devfn); - } else { - /* FIXME: Make dma_context_fn use MemoryRegions instead, so this path is - * taken unconditionally */ - /* FIXME: inherit memory region from bus creator */ - memory_region_init_alias(&pci_dev->bus_master_enable_region, "bus master", - get_system_memory(), 0, - memory_region_size(get_system_memory())); - memory_region_set_enabled(&pci_dev->bus_master_enable_region, false); - address_space_init(&pci_dev->bus_master_as, &pci_dev->bus_master_enable_region); - pci_dev->dma = g_new(DMAContext, 1); - dma_context_init(pci_dev->dma, &pci_dev->bus_master_as, NULL, NULL, NULL); - } - pci_dev->devfn = devfn; - pstrcpy(pci_dev->name, sizeof(pci_dev->name), name); - pci_dev->irq_state = 0; - pci_config_alloc(pci_dev); - - pci_config_set_vendor_id(pci_dev->config, pc->vendor_id); - pci_config_set_device_id(pci_dev->config, pc->device_id); - pci_config_set_revision(pci_dev->config, pc->revision); - pci_config_set_class(pci_dev->config, pc->class_id); - - if (!pc->is_bridge) { - if (pc->subsystem_vendor_id || pc->subsystem_id) { - pci_set_word(pci_dev->config + PCI_SUBSYSTEM_VENDOR_ID, - pc->subsystem_vendor_id); - pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID, - pc->subsystem_id); - } else { - pci_set_default_subsystem_id(pci_dev); - } - } else { - /* subsystem_vendor_id/subsystem_id are only for header type 0 */ - assert(!pc->subsystem_vendor_id); - assert(!pc->subsystem_id); - } - pci_init_cmask(pci_dev); - pci_init_wmask(pci_dev); - pci_init_w1cmask(pci_dev); - if (pc->is_bridge) { - pci_init_mask_bridge(pci_dev); - } - if (pci_init_multifunction(bus, pci_dev)) { - pci_config_free(pci_dev); - return NULL; - } - - if (!config_read) - config_read = pci_default_read_config; - if (!config_write) - config_write = pci_default_write_config; - pci_dev->config_read = config_read; - pci_dev->config_write = config_write; - bus->devices[devfn] = pci_dev; - pci_dev->irq = qemu_allocate_irqs(pci_set_irq, pci_dev, PCI_NUM_PINS); - pci_dev->version_id = 2; /* Current pci device vmstate version */ - return pci_dev; -} - -static void do_pci_unregister_device(PCIDevice *pci_dev) -{ - qemu_free_irqs(pci_dev->irq); - pci_dev->bus->devices[pci_dev->devfn] = NULL; - pci_config_free(pci_dev); - - if (!pci_dev->bus->dma_context_fn) { - address_space_destroy(&pci_dev->bus_master_as); - memory_region_destroy(&pci_dev->bus_master_enable_region); - g_free(pci_dev->dma); - pci_dev->dma = NULL; - } -} - -static void pci_unregister_io_regions(PCIDevice *pci_dev) -{ - PCIIORegion *r; - int i; - - for(i = 0; i < PCI_NUM_REGIONS; i++) { - r = &pci_dev->io_regions[i]; - if (!r->size || r->addr == PCI_BAR_UNMAPPED) - continue; - memory_region_del_subregion(r->address_space, r->memory); - } -} - -static int pci_unregister_device(DeviceState *dev) -{ - PCIDevice *pci_dev = PCI_DEVICE(dev); - PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(pci_dev); - - pci_unregister_io_regions(pci_dev); - pci_del_option_rom(pci_dev); - - if (pc->exit) { - pc->exit(pci_dev); - } - - do_pci_unregister_device(pci_dev); - return 0; -} - -void pci_register_bar(PCIDevice *pci_dev, int region_num, - uint8_t type, MemoryRegion *memory) -{ - PCIIORegion *r; - uint32_t addr; - uint64_t wmask; - pcibus_t size = memory_region_size(memory); - - assert(region_num >= 0); - assert(region_num < PCI_NUM_REGIONS); - if (size & (size-1)) { - fprintf(stderr, "ERROR: PCI region size must be pow2 " - "type=0x%x, size=0x%"FMT_PCIBUS"\n", type, size); - exit(1); - } - - r = &pci_dev->io_regions[region_num]; - r->addr = PCI_BAR_UNMAPPED; - r->size = size; - r->type = type; - r->memory = NULL; - - wmask = ~(size - 1); - addr = pci_bar(pci_dev, region_num); - if (region_num == PCI_ROM_SLOT) { - /* ROM enable bit is writable */ - wmask |= PCI_ROM_ADDRESS_ENABLE; - } - pci_set_long(pci_dev->config + addr, type); - if (!(r->type & PCI_BASE_ADDRESS_SPACE_IO) && - r->type & PCI_BASE_ADDRESS_MEM_TYPE_64) { - pci_set_quad(pci_dev->wmask + addr, wmask); - pci_set_quad(pci_dev->cmask + addr, ~0ULL); - } else { - pci_set_long(pci_dev->wmask + addr, wmask & 0xffffffff); - pci_set_long(pci_dev->cmask + addr, 0xffffffff); - } - pci_dev->io_regions[region_num].memory = memory; - pci_dev->io_regions[region_num].address_space - = type & PCI_BASE_ADDRESS_SPACE_IO - ? pci_dev->bus->address_space_io - : pci_dev->bus->address_space_mem; -} - -pcibus_t pci_get_bar_addr(PCIDevice *pci_dev, int region_num) -{ - return pci_dev->io_regions[region_num].addr; -} - -static pcibus_t pci_bar_address(PCIDevice *d, - int reg, uint8_t type, pcibus_t size) -{ - pcibus_t new_addr, last_addr; - int bar = pci_bar(d, reg); - uint16_t cmd = pci_get_word(d->config + PCI_COMMAND); - - if (type & PCI_BASE_ADDRESS_SPACE_IO) { - if (!(cmd & PCI_COMMAND_IO)) { - return PCI_BAR_UNMAPPED; - } - new_addr = pci_get_long(d->config + bar) & ~(size - 1); - last_addr = new_addr + size - 1; - /* NOTE: we have only 64K ioports on PC */ - if (last_addr <= new_addr || new_addr == 0 || last_addr > UINT16_MAX) { - return PCI_BAR_UNMAPPED; - } - return new_addr; - } - - if (!(cmd & PCI_COMMAND_MEMORY)) { - return PCI_BAR_UNMAPPED; - } - if (type & PCI_BASE_ADDRESS_MEM_TYPE_64) { - new_addr = pci_get_quad(d->config + bar); - } else { - new_addr = pci_get_long(d->config + bar); - } - /* the ROM slot has a specific enable bit */ - if (reg == PCI_ROM_SLOT && !(new_addr & PCI_ROM_ADDRESS_ENABLE)) { - return PCI_BAR_UNMAPPED; - } - new_addr &= ~(size - 1); - last_addr = new_addr + size - 1; - /* NOTE: we do not support wrapping */ - /* XXX: as we cannot support really dynamic - mappings, we handle specific values as invalid - mappings. */ - if (last_addr <= new_addr || new_addr == 0 || - last_addr == PCI_BAR_UNMAPPED) { - return PCI_BAR_UNMAPPED; - } - - /* Now pcibus_t is 64bit. - * Check if 32 bit BAR wraps around explicitly. - * Without this, PC ide doesn't work well. - * TODO: remove this work around. - */ - if (!(type & PCI_BASE_ADDRESS_MEM_TYPE_64) && last_addr >= UINT32_MAX) { - return PCI_BAR_UNMAPPED; - } - - /* - * OS is allowed to set BAR beyond its addressable - * bits. For example, 32 bit OS can set 64bit bar - * to >4G. Check it. TODO: we might need to support - * it in the future for e.g. PAE. - */ - if (last_addr >= HWADDR_MAX) { - return PCI_BAR_UNMAPPED; - } - - return new_addr; -} - -static void pci_update_mappings(PCIDevice *d) -{ - PCIIORegion *r; - int i; - pcibus_t new_addr; - - for(i = 0; i < PCI_NUM_REGIONS; i++) { - r = &d->io_regions[i]; - - /* this region isn't registered */ - if (!r->size) - continue; - - new_addr = pci_bar_address(d, i, r->type, r->size); - - /* This bar isn't changed */ - if (new_addr == r->addr) - continue; - - /* now do the real mapping */ - if (r->addr != PCI_BAR_UNMAPPED) { - memory_region_del_subregion(r->address_space, r->memory); - } - r->addr = new_addr; - if (r->addr != PCI_BAR_UNMAPPED) { - memory_region_add_subregion_overlap(r->address_space, - r->addr, r->memory, 1); - } - } -} - -static inline int pci_irq_disabled(PCIDevice *d) -{ - return pci_get_word(d->config + PCI_COMMAND) & PCI_COMMAND_INTX_DISABLE; -} - -/* Called after interrupt disabled field update in config space, - * assert/deassert interrupts if necessary. - * Gets original interrupt disable bit value (before update). */ -static void pci_update_irq_disabled(PCIDevice *d, int was_irq_disabled) -{ - int i, disabled = pci_irq_disabled(d); - if (disabled == was_irq_disabled) - return; - for (i = 0; i < PCI_NUM_PINS; ++i) { - int state = pci_irq_state(d, i); - pci_change_irq_level(d, i, disabled ? -state : state); - } -} - -uint32_t pci_default_read_config(PCIDevice *d, - uint32_t address, int len) -{ - uint32_t val = 0; - - memcpy(&val, d->config + address, len); - return le32_to_cpu(val); -} - -void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l) -{ - int i, was_irq_disabled = pci_irq_disabled(d); - - for (i = 0; i < l; val >>= 8, ++i) { - uint8_t wmask = d->wmask[addr + i]; - uint8_t w1cmask = d->w1cmask[addr + i]; - assert(!(wmask & w1cmask)); - d->config[addr + i] = (d->config[addr + i] & ~wmask) | (val & wmask); - d->config[addr + i] &= ~(val & w1cmask); /* W1C: Write 1 to Clear */ - } - if (ranges_overlap(addr, l, PCI_BASE_ADDRESS_0, 24) || - ranges_overlap(addr, l, PCI_ROM_ADDRESS, 4) || - ranges_overlap(addr, l, PCI_ROM_ADDRESS1, 4) || - range_covers_byte(addr, l, PCI_COMMAND)) - pci_update_mappings(d); - - if (range_covers_byte(addr, l, PCI_COMMAND)) { - pci_update_irq_disabled(d, was_irq_disabled); - memory_region_set_enabled(&d->bus_master_enable_region, - pci_get_word(d->config + PCI_COMMAND) - & PCI_COMMAND_MASTER); - } - - msi_write_config(d, addr, val, l); - msix_write_config(d, addr, val, l); -} - -/***********************************************************/ -/* generic PCI irq support */ - -/* 0 <= irq_num <= 3. level must be 0 or 1 */ -static void pci_set_irq(void *opaque, int irq_num, int level) -{ - PCIDevice *pci_dev = opaque; - int change; - - change = level - pci_irq_state(pci_dev, irq_num); - if (!change) - return; - - pci_set_irq_state(pci_dev, irq_num, level); - pci_update_irq_status(pci_dev); - if (pci_irq_disabled(pci_dev)) - return; - pci_change_irq_level(pci_dev, irq_num, change); -} - -/* Special hooks used by device assignment */ -void pci_bus_set_route_irq_fn(PCIBus *bus, pci_route_irq_fn route_intx_to_irq) -{ - assert(!bus->parent_dev); - bus->route_intx_to_irq = route_intx_to_irq; -} - -PCIINTxRoute pci_device_route_intx_to_irq(PCIDevice *dev, int pin) -{ - PCIBus *bus; - - do { - bus = dev->bus; - pin = bus->map_irq(dev, pin); - dev = bus->parent_dev; - } while (dev); - - if (!bus->route_intx_to_irq) { - error_report("PCI: Bug - unimplemented PCI INTx routing (%s)\n", - object_get_typename(OBJECT(bus->qbus.parent))); - return (PCIINTxRoute) { PCI_INTX_DISABLED, -1 }; - } - - return bus->route_intx_to_irq(bus->irq_opaque, pin); -} - -bool pci_intx_route_changed(PCIINTxRoute *old, PCIINTxRoute *new) -{ - return old->mode != new->mode || old->irq != new->irq; -} - -void pci_bus_fire_intx_routing_notifier(PCIBus *bus) -{ - PCIDevice *dev; - PCIBus *sec; - int i; - - for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) { - dev = bus->devices[i]; - if (dev && dev->intx_routing_notifier) { - dev->intx_routing_notifier(dev); - } - QLIST_FOREACH(sec, &bus->child, sibling) { - pci_bus_fire_intx_routing_notifier(sec); - } - } -} - -void pci_device_set_intx_routing_notifier(PCIDevice *dev, - PCIINTxRoutingNotifier notifier) -{ - dev->intx_routing_notifier = notifier; -} - -/* - * PCI-to-PCI bridge specification - * 9.1: Interrupt routing. Table 9-1 - * - * the PCI Express Base Specification, Revision 2.1 - * 2.2.8.1: INTx interrutp signaling - Rules - * the Implementation Note - * Table 2-20 - */ -/* - * 0 <= pin <= 3 0 = INTA, 1 = INTB, 2 = INTC, 3 = INTD - * 0-origin unlike PCI interrupt pin register. - */ -int pci_swizzle_map_irq_fn(PCIDevice *pci_dev, int pin) -{ - return (pin + PCI_SLOT(pci_dev->devfn)) % PCI_NUM_PINS; -} - -/***********************************************************/ -/* monitor info on PCI */ - -typedef struct { - uint16_t class; - const char *desc; - const char *fw_name; - uint16_t fw_ign_bits; -} pci_class_desc; - -static const pci_class_desc pci_class_descriptions[] = -{ - { 0x0001, "VGA controller", "display"}, - { 0x0100, "SCSI controller", "scsi"}, - { 0x0101, "IDE controller", "ide"}, - { 0x0102, "Floppy controller", "fdc"}, - { 0x0103, "IPI controller", "ipi"}, - { 0x0104, "RAID controller", "raid"}, - { 0x0106, "SATA controller"}, - { 0x0107, "SAS controller"}, - { 0x0180, "Storage controller"}, - { 0x0200, "Ethernet controller", "ethernet"}, - { 0x0201, "Token Ring controller", "token-ring"}, - { 0x0202, "FDDI controller", "fddi"}, - { 0x0203, "ATM controller", "atm"}, - { 0x0280, "Network controller"}, - { 0x0300, "VGA controller", "display", 0x00ff}, - { 0x0301, "XGA controller"}, - { 0x0302, "3D controller"}, - { 0x0380, "Display controller"}, - { 0x0400, "Video controller", "video"}, - { 0x0401, "Audio controller", "sound"}, - { 0x0402, "Phone"}, - { 0x0403, "Audio controller", "sound"}, - { 0x0480, "Multimedia controller"}, - { 0x0500, "RAM controller", "memory"}, - { 0x0501, "Flash controller", "flash"}, - { 0x0580, "Memory controller"}, - { 0x0600, "Host bridge", "host"}, - { 0x0601, "ISA bridge", "isa"}, - { 0x0602, "EISA bridge", "eisa"}, - { 0x0603, "MC bridge", "mca"}, - { 0x0604, "PCI bridge", "pci"}, - { 0x0605, "PCMCIA bridge", "pcmcia"}, - { 0x0606, "NUBUS bridge", "nubus"}, - { 0x0607, "CARDBUS bridge", "cardbus"}, - { 0x0608, "RACEWAY bridge"}, - { 0x0680, "Bridge"}, - { 0x0700, "Serial port", "serial"}, - { 0x0701, "Parallel port", "parallel"}, - { 0x0800, "Interrupt controller", "interrupt-controller"}, - { 0x0801, "DMA controller", "dma-controller"}, - { 0x0802, "Timer", "timer"}, - { 0x0803, "RTC", "rtc"}, - { 0x0900, "Keyboard", "keyboard"}, - { 0x0901, "Pen", "pen"}, - { 0x0902, "Mouse", "mouse"}, - { 0x0A00, "Dock station", "dock", 0x00ff}, - { 0x0B00, "i386 cpu", "cpu", 0x00ff}, - { 0x0c00, "Fireware contorller", "fireware"}, - { 0x0c01, "Access bus controller", "access-bus"}, - { 0x0c02, "SSA controller", "ssa"}, - { 0x0c03, "USB controller", "usb"}, - { 0x0c04, "Fibre channel controller", "fibre-channel"}, - { 0x0c05, "SMBus"}, - { 0, NULL} -}; - -static void pci_for_each_device_under_bus(PCIBus *bus, - void (*fn)(PCIBus *b, PCIDevice *d, - void *opaque), - void *opaque) -{ - PCIDevice *d; - int devfn; - - for(devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) { - d = bus->devices[devfn]; - if (d) { - fn(bus, d, opaque); - } - } -} - -void pci_for_each_device(PCIBus *bus, int bus_num, - void (*fn)(PCIBus *b, PCIDevice *d, void *opaque), - void *opaque) -{ - bus = pci_find_bus_nr(bus, bus_num); - - if (bus) { - pci_for_each_device_under_bus(bus, fn, opaque); - } -} - -static const pci_class_desc *get_class_desc(int class) -{ - const pci_class_desc *desc; - - desc = pci_class_descriptions; - while (desc->desc && class != desc->class) { - desc++; - } - - return desc; -} - -static PciDeviceInfoList *qmp_query_pci_devices(PCIBus *bus, int bus_num); - -static PciMemoryRegionList *qmp_query_pci_regions(const PCIDevice *dev) -{ - PciMemoryRegionList *head = NULL, *cur_item = NULL; - int i; - - for (i = 0; i < PCI_NUM_REGIONS; i++) { - const PCIIORegion *r = &dev->io_regions[i]; - PciMemoryRegionList *region; - - if (!r->size) { - continue; - } - - region = g_malloc0(sizeof(*region)); - region->value = g_malloc0(sizeof(*region->value)); - - if (r->type & PCI_BASE_ADDRESS_SPACE_IO) { - region->value->type = g_strdup("io"); - } else { - region->value->type = g_strdup("memory"); - region->value->has_prefetch = true; - region->value->prefetch = !!(r->type & PCI_BASE_ADDRESS_MEM_PREFETCH); - region->value->has_mem_type_64 = true; - region->value->mem_type_64 = !!(r->type & PCI_BASE_ADDRESS_MEM_TYPE_64); - } - - region->value->bar = i; - region->value->address = r->addr; - region->value->size = r->size; - - /* XXX: waiting for the qapi to support GSList */ - if (!cur_item) { - head = cur_item = region; - } else { - cur_item->next = region; - cur_item = region; - } - } - - return head; -} - -static PciBridgeInfo *qmp_query_pci_bridge(PCIDevice *dev, PCIBus *bus, - int bus_num) -{ - PciBridgeInfo *info; - - info = g_malloc0(sizeof(*info)); - - info->bus.number = dev->config[PCI_PRIMARY_BUS]; - info->bus.secondary = dev->config[PCI_SECONDARY_BUS]; - info->bus.subordinate = dev->config[PCI_SUBORDINATE_BUS]; - - info->bus.io_range = g_malloc0(sizeof(*info->bus.io_range)); - info->bus.io_range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_IO); - info->bus.io_range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_IO); - - info->bus.memory_range = g_malloc0(sizeof(*info->bus.memory_range)); - info->bus.memory_range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY); - info->bus.memory_range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY); - - info->bus.prefetchable_range = g_malloc0(sizeof(*info->bus.prefetchable_range)); - info->bus.prefetchable_range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); - info->bus.prefetchable_range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); - - if (dev->config[PCI_SECONDARY_BUS] != 0) { - PCIBus *child_bus = pci_find_bus_nr(bus, dev->config[PCI_SECONDARY_BUS]); - if (child_bus) { - info->has_devices = true; - info->devices = qmp_query_pci_devices(child_bus, dev->config[PCI_SECONDARY_BUS]); - } - } - - return info; -} - -static PciDeviceInfo *qmp_query_pci_device(PCIDevice *dev, PCIBus *bus, - int bus_num) -{ - const pci_class_desc *desc; - PciDeviceInfo *info; - uint8_t type; - int class; - - info = g_malloc0(sizeof(*info)); - info->bus = bus_num; - info->slot = PCI_SLOT(dev->devfn); - info->function = PCI_FUNC(dev->devfn); - - class = pci_get_word(dev->config + PCI_CLASS_DEVICE); - info->class_info.class = class; - desc = get_class_desc(class); - if (desc->desc) { - info->class_info.has_desc = true; - info->class_info.desc = g_strdup(desc->desc); - } - - info->id.vendor = pci_get_word(dev->config + PCI_VENDOR_ID); - info->id.device = pci_get_word(dev->config + PCI_DEVICE_ID); - info->regions = qmp_query_pci_regions(dev); - info->qdev_id = g_strdup(dev->qdev.id ? dev->qdev.id : ""); - - if (dev->config[PCI_INTERRUPT_PIN] != 0) { - info->has_irq = true; - info->irq = dev->config[PCI_INTERRUPT_LINE]; - } - - type = dev->config[PCI_HEADER_TYPE] & ~PCI_HEADER_TYPE_MULTI_FUNCTION; - if (type == PCI_HEADER_TYPE_BRIDGE) { - info->has_pci_bridge = true; - info->pci_bridge = qmp_query_pci_bridge(dev, bus, bus_num); - } - - return info; -} - -static PciDeviceInfoList *qmp_query_pci_devices(PCIBus *bus, int bus_num) -{ - PciDeviceInfoList *info, *head = NULL, *cur_item = NULL; - PCIDevice *dev; - int devfn; - - for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) { - dev = bus->devices[devfn]; - if (dev) { - info = g_malloc0(sizeof(*info)); - info->value = qmp_query_pci_device(dev, bus, bus_num); - - /* XXX: waiting for the qapi to support GSList */ - if (!cur_item) { - head = cur_item = info; - } else { - cur_item->next = info; - cur_item = info; - } - } - } - - return head; -} - -static PciInfo *qmp_query_pci_bus(PCIBus *bus, int bus_num) -{ - PciInfo *info = NULL; - - bus = pci_find_bus_nr(bus, bus_num); - if (bus) { - info = g_malloc0(sizeof(*info)); - info->bus = bus_num; - info->devices = qmp_query_pci_devices(bus, bus_num); - } - - return info; -} - -PciInfoList *qmp_query_pci(Error **errp) -{ - PciInfoList *info, *head = NULL, *cur_item = NULL; - struct PCIHostBus *host; - - QLIST_FOREACH(host, &host_buses, next) { - info = g_malloc0(sizeof(*info)); - info->value = qmp_query_pci_bus(host->bus, 0); - - /* XXX: waiting for the qapi to support GSList */ - if (!cur_item) { - head = cur_item = info; - } else { - cur_item->next = info; - cur_item = info; - } - } - - return head; -} - -static const char * const pci_nic_models[] = { - "ne2k_pci", - "i82551", - "i82557b", - "i82559er", - "rtl8139", - "e1000", - "pcnet", - "virtio", - NULL -}; - -static const char * const pci_nic_names[] = { - "ne2k_pci", - "i82551", - "i82557b", - "i82559er", - "rtl8139", - "e1000", - "pcnet", - "virtio-net-pci", - NULL -}; - -/* Initialize a PCI NIC. */ -/* FIXME callers should check for failure, but don't */ -PCIDevice *pci_nic_init(NICInfo *nd, const char *default_model, - const char *default_devaddr) -{ - const char *devaddr = nd->devaddr ? nd->devaddr : default_devaddr; - PCIBus *bus; - int devfn; - PCIDevice *pci_dev; - DeviceState *dev; - int i; - - i = qemu_find_nic_model(nd, pci_nic_models, default_model); - if (i < 0) - return NULL; - - bus = pci_get_bus_devfn(&devfn, devaddr); - if (!bus) { - error_report("Invalid PCI device address %s for device %s", - devaddr, pci_nic_names[i]); - return NULL; - } - - pci_dev = pci_create(bus, devfn, pci_nic_names[i]); - dev = &pci_dev->qdev; - qdev_set_nic_properties(dev, nd); - if (qdev_init(dev) < 0) - return NULL; - return pci_dev; -} - -PCIDevice *pci_nic_init_nofail(NICInfo *nd, const char *default_model, - const char *default_devaddr) -{ - PCIDevice *res; - - if (qemu_show_nic_models(nd->model, pci_nic_models)) - exit(0); - - res = pci_nic_init(nd, default_model, default_devaddr); - if (!res) - exit(1); - return res; -} - -PCIDevice *pci_vga_init(PCIBus *bus) -{ - switch (vga_interface_type) { - case VGA_CIRRUS: - return pci_create_simple(bus, -1, "cirrus-vga"); - case VGA_QXL: - return pci_create_simple(bus, -1, "qxl-vga"); - case VGA_STD: - return pci_create_simple(bus, -1, "VGA"); - case VGA_VMWARE: - return pci_create_simple(bus, -1, "vmware-svga"); - case VGA_NONE: - default: /* Other non-PCI types. Checking for unsupported types is already - done in vl.c. */ - return NULL; - } -} - -/* Whether a given bus number is in range of the secondary - * bus of the given bridge device. */ -static bool pci_secondary_bus_in_range(PCIDevice *dev, int bus_num) -{ - return !(pci_get_word(dev->config + PCI_BRIDGE_CONTROL) & - PCI_BRIDGE_CTL_BUS_RESET) /* Don't walk the bus if it's reset. */ && - dev->config[PCI_SECONDARY_BUS] < bus_num && - bus_num <= dev->config[PCI_SUBORDINATE_BUS]; -} - -static PCIBus *pci_find_bus_nr(PCIBus *bus, int bus_num) -{ - PCIBus *sec; - - if (!bus) { - return NULL; - } - - if (pci_bus_num(bus) == bus_num) { - return bus; - } - - /* Consider all bus numbers in range for the host pci bridge. */ - if (bus->parent_dev && - !pci_secondary_bus_in_range(bus->parent_dev, bus_num)) { - return NULL; - } - - /* try child bus */ - for (; bus; bus = sec) { - QLIST_FOREACH(sec, &bus->child, sibling) { - assert(sec->parent_dev); - if (sec->parent_dev->config[PCI_SECONDARY_BUS] == bus_num) { - return sec; - } - if (pci_secondary_bus_in_range(sec->parent_dev, bus_num)) { - break; - } - } - } - - return NULL; -} - -PCIDevice *pci_find_device(PCIBus *bus, int bus_num, uint8_t devfn) -{ - bus = pci_find_bus_nr(bus, bus_num); - - if (!bus) - return NULL; - - return bus->devices[devfn]; -} - -static int pci_qdev_init(DeviceState *qdev) -{ - PCIDevice *pci_dev = (PCIDevice *)qdev; - PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(pci_dev); - PCIBus *bus; - int rc; - bool is_default_rom; - - /* initialize cap_present for pci_is_express() and pci_config_size() */ - if (pc->is_express) { - pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS; - } - - bus = FROM_QBUS(PCIBus, qdev_get_parent_bus(qdev)); - pci_dev = do_pci_register_device(pci_dev, bus, - object_get_typename(OBJECT(qdev)), - pci_dev->devfn); - if (pci_dev == NULL) - return -1; - if (qdev->hotplugged && pc->no_hotplug) { - qerror_report(QERR_DEVICE_NO_HOTPLUG, object_get_typename(OBJECT(pci_dev))); - do_pci_unregister_device(pci_dev); - return -1; - } - if (pc->init) { - rc = pc->init(pci_dev); - if (rc != 0) { - do_pci_unregister_device(pci_dev); - return rc; - } - } - - /* rom loading */ - is_default_rom = false; - if (pci_dev->romfile == NULL && pc->romfile != NULL) { - pci_dev->romfile = g_strdup(pc->romfile); - is_default_rom = true; - } - pci_add_option_rom(pci_dev, is_default_rom); - - if (bus->hotplug) { - /* Let buses differentiate between hotplug and when device is - * enabled during qemu machine creation. */ - rc = bus->hotplug(bus->hotplug_qdev, pci_dev, - qdev->hotplugged ? PCI_HOTPLUG_ENABLED: - PCI_COLDPLUG_ENABLED); - if (rc != 0) { - int r = pci_unregister_device(&pci_dev->qdev); - assert(!r); - return rc; - } - } - return 0; -} - -static int pci_unplug_device(DeviceState *qdev) -{ - PCIDevice *dev = PCI_DEVICE(qdev); - PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); - - if (pc->no_hotplug) { - qerror_report(QERR_DEVICE_NO_HOTPLUG, object_get_typename(OBJECT(dev))); - return -1; - } - return dev->bus->hotplug(dev->bus->hotplug_qdev, dev, - PCI_HOTPLUG_DISABLED); -} - -PCIDevice *pci_create_multifunction(PCIBus *bus, int devfn, bool multifunction, - const char *name) -{ - DeviceState *dev; - - dev = qdev_create(&bus->qbus, name); - qdev_prop_set_int32(dev, "addr", devfn); - qdev_prop_set_bit(dev, "multifunction", multifunction); - return PCI_DEVICE(dev); -} - -PCIDevice *pci_create_simple_multifunction(PCIBus *bus, int devfn, - bool multifunction, - const char *name) -{ - PCIDevice *dev = pci_create_multifunction(bus, devfn, multifunction, name); - qdev_init_nofail(&dev->qdev); - return dev; -} - -PCIDevice *pci_create(PCIBus *bus, int devfn, const char *name) -{ - return pci_create_multifunction(bus, devfn, false, name); -} - -PCIDevice *pci_create_simple(PCIBus *bus, int devfn, const char *name) -{ - return pci_create_simple_multifunction(bus, devfn, false, name); -} - -static uint8_t pci_find_space(PCIDevice *pdev, uint8_t size) -{ - int offset = PCI_CONFIG_HEADER_SIZE; - int i; - for (i = PCI_CONFIG_HEADER_SIZE; i < PCI_CONFIG_SPACE_SIZE; ++i) { - if (pdev->used[i]) - offset = i + 1; - else if (i - offset + 1 == size) - return offset; - } - return 0; -} - -static uint8_t pci_find_capability_list(PCIDevice *pdev, uint8_t cap_id, - uint8_t *prev_p) -{ - uint8_t next, prev; - - if (!(pdev->config[PCI_STATUS] & PCI_STATUS_CAP_LIST)) - return 0; - - for (prev = PCI_CAPABILITY_LIST; (next = pdev->config[prev]); - prev = next + PCI_CAP_LIST_NEXT) - if (pdev->config[next + PCI_CAP_LIST_ID] == cap_id) - break; - - if (prev_p) - *prev_p = prev; - return next; -} - -static uint8_t pci_find_capability_at_offset(PCIDevice *pdev, uint8_t offset) -{ - uint8_t next, prev, found = 0; - - if (!(pdev->used[offset])) { - return 0; - } - - assert(pdev->config[PCI_STATUS] & PCI_STATUS_CAP_LIST); - - for (prev = PCI_CAPABILITY_LIST; (next = pdev->config[prev]); - prev = next + PCI_CAP_LIST_NEXT) { - if (next <= offset && next > found) { - found = next; - } - } - return found; -} - -/* Patch the PCI vendor and device ids in a PCI rom image if necessary. - This is needed for an option rom which is used for more than one device. */ -static void pci_patch_ids(PCIDevice *pdev, uint8_t *ptr, int size) -{ - uint16_t vendor_id; - uint16_t device_id; - uint16_t rom_vendor_id; - uint16_t rom_device_id; - uint16_t rom_magic; - uint16_t pcir_offset; - uint8_t checksum; - - /* Words in rom data are little endian (like in PCI configuration), - so they can be read / written with pci_get_word / pci_set_word. */ - - /* Only a valid rom will be patched. */ - rom_magic = pci_get_word(ptr); - if (rom_magic != 0xaa55) { - PCI_DPRINTF("Bad ROM magic %04x\n", rom_magic); - return; - } - pcir_offset = pci_get_word(ptr + 0x18); - if (pcir_offset + 8 >= size || memcmp(ptr + pcir_offset, "PCIR", 4)) { - PCI_DPRINTF("Bad PCIR offset 0x%x or signature\n", pcir_offset); - return; - } - - vendor_id = pci_get_word(pdev->config + PCI_VENDOR_ID); - device_id = pci_get_word(pdev->config + PCI_DEVICE_ID); - rom_vendor_id = pci_get_word(ptr + pcir_offset + 4); - rom_device_id = pci_get_word(ptr + pcir_offset + 6); - - PCI_DPRINTF("%s: ROM id %04x%04x / PCI id %04x%04x\n", pdev->romfile, - vendor_id, device_id, rom_vendor_id, rom_device_id); - - checksum = ptr[6]; - - if (vendor_id != rom_vendor_id) { - /* Patch vendor id and checksum (at offset 6 for etherboot roms). */ - checksum += (uint8_t)rom_vendor_id + (uint8_t)(rom_vendor_id >> 8); - checksum -= (uint8_t)vendor_id + (uint8_t)(vendor_id >> 8); - PCI_DPRINTF("ROM checksum %02x / %02x\n", ptr[6], checksum); - ptr[6] = checksum; - pci_set_word(ptr + pcir_offset + 4, vendor_id); - } - - if (device_id != rom_device_id) { - /* Patch device id and checksum (at offset 6 for etherboot roms). */ - checksum += (uint8_t)rom_device_id + (uint8_t)(rom_device_id >> 8); - checksum -= (uint8_t)device_id + (uint8_t)(device_id >> 8); - PCI_DPRINTF("ROM checksum %02x / %02x\n", ptr[6], checksum); - ptr[6] = checksum; - pci_set_word(ptr + pcir_offset + 6, device_id); - } -} - -/* Add an option rom for the device */ -static int pci_add_option_rom(PCIDevice *pdev, bool is_default_rom) -{ - int size; - char *path; - void *ptr; - char name[32]; - const VMStateDescription *vmsd; - - if (!pdev->romfile) - return 0; - if (strlen(pdev->romfile) == 0) - return 0; - - if (!pdev->rom_bar) { - /* - * Load rom via fw_cfg instead of creating a rom bar, - * for 0.11 compatibility. - */ - int class = pci_get_word(pdev->config + PCI_CLASS_DEVICE); - if (class == 0x0300) { - rom_add_vga(pdev->romfile); - } else { - rom_add_option(pdev->romfile, -1); - } - return 0; - } - - path = qemu_find_file(QEMU_FILE_TYPE_BIOS, pdev->romfile); - if (path == NULL) { - path = g_strdup(pdev->romfile); - } - - size = get_image_size(path); - if (size < 0) { - error_report("%s: failed to find romfile \"%s\"", - __FUNCTION__, pdev->romfile); - g_free(path); - return -1; - } - if (size & (size - 1)) { - size = 1 << qemu_fls(size); - } - - vmsd = qdev_get_vmsd(DEVICE(pdev)); - - if (vmsd) { - snprintf(name, sizeof(name), "%s.rom", vmsd->name); - } else { - snprintf(name, sizeof(name), "%s.rom", object_get_typename(OBJECT(pdev))); - } - pdev->has_rom = true; - memory_region_init_ram(&pdev->rom, name, size); - vmstate_register_ram(&pdev->rom, &pdev->qdev); - ptr = memory_region_get_ram_ptr(&pdev->rom); - load_image(path, ptr); - g_free(path); - - if (is_default_rom) { - /* Only the default rom images will be patched (if needed). */ - pci_patch_ids(pdev, ptr, size); - } - - qemu_put_ram_ptr(ptr); - - pci_register_bar(pdev, PCI_ROM_SLOT, 0, &pdev->rom); - - return 0; -} - -static void pci_del_option_rom(PCIDevice *pdev) -{ - if (!pdev->has_rom) - return; - - vmstate_unregister_ram(&pdev->rom, &pdev->qdev); - memory_region_destroy(&pdev->rom); - pdev->has_rom = false; -} - -/* - * if !offset - * Reserve space and add capability to the linked list in pci config space - * - * if offset = 0, - * Find and reserve space and add capability to the linked list - * in pci config space */ -int pci_add_capability(PCIDevice *pdev, uint8_t cap_id, - uint8_t offset, uint8_t size) -{ - uint8_t *config; - int i, overlapping_cap; - - if (!offset) { - offset = pci_find_space(pdev, size); - if (!offset) { - return -ENOSPC; - } - } else { - /* Verify that capabilities don't overlap. Note: device assignment - * depends on this check to verify that the device is not broken. - * Should never trigger for emulated devices, but it's helpful - * for debugging these. */ - for (i = offset; i < offset + size; i++) { - overlapping_cap = pci_find_capability_at_offset(pdev, i); - if (overlapping_cap) { - fprintf(stderr, "ERROR: %04x:%02x:%02x.%x " - "Attempt to add PCI capability %x at offset " - "%x overlaps existing capability %x at offset %x\n", - pci_find_domain(pdev->bus), pci_bus_num(pdev->bus), - PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), - cap_id, offset, overlapping_cap, i); - return -EINVAL; - } - } - } - - config = pdev->config + offset; - config[PCI_CAP_LIST_ID] = cap_id; - config[PCI_CAP_LIST_NEXT] = pdev->config[PCI_CAPABILITY_LIST]; - pdev->config[PCI_CAPABILITY_LIST] = offset; - pdev->config[PCI_STATUS] |= PCI_STATUS_CAP_LIST; - memset(pdev->used + offset, 0xFF, QEMU_ALIGN_UP(size, 4)); - /* Make capability read-only by default */ - memset(pdev->wmask + offset, 0, size); - /* Check capability by default */ - memset(pdev->cmask + offset, 0xFF, size); - return offset; -} - -/* Unlink capability from the pci config space. */ -void pci_del_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size) -{ - uint8_t prev, offset = pci_find_capability_list(pdev, cap_id, &prev); - if (!offset) - return; - pdev->config[prev] = pdev->config[offset + PCI_CAP_LIST_NEXT]; - /* Make capability writable again */ - memset(pdev->wmask + offset, 0xff, size); - memset(pdev->w1cmask + offset, 0, size); - /* Clear cmask as device-specific registers can't be checked */ - memset(pdev->cmask + offset, 0, size); - memset(pdev->used + offset, 0, QEMU_ALIGN_UP(size, 4)); - - if (!pdev->config[PCI_CAPABILITY_LIST]) - pdev->config[PCI_STATUS] &= ~PCI_STATUS_CAP_LIST; -} - -uint8_t pci_find_capability(PCIDevice *pdev, uint8_t cap_id) -{ - return pci_find_capability_list(pdev, cap_id, NULL); -} - -static void pcibus_dev_print(Monitor *mon, DeviceState *dev, int indent) -{ - PCIDevice *d = (PCIDevice *)dev; - const pci_class_desc *desc; - char ctxt[64]; - PCIIORegion *r; - int i, class; - - class = pci_get_word(d->config + PCI_CLASS_DEVICE); - desc = pci_class_descriptions; - while (desc->desc && class != desc->class) - desc++; - if (desc->desc) { - snprintf(ctxt, sizeof(ctxt), "%s", desc->desc); - } else { - snprintf(ctxt, sizeof(ctxt), "Class %04x", class); - } - - monitor_printf(mon, "%*sclass %s, addr %02x:%02x.%x, " - "pci id %04x:%04x (sub %04x:%04x)\n", - indent, "", ctxt, pci_bus_num(d->bus), - PCI_SLOT(d->devfn), PCI_FUNC(d->devfn), - pci_get_word(d->config + PCI_VENDOR_ID), - pci_get_word(d->config + PCI_DEVICE_ID), - pci_get_word(d->config + PCI_SUBSYSTEM_VENDOR_ID), - pci_get_word(d->config + PCI_SUBSYSTEM_ID)); - for (i = 0; i < PCI_NUM_REGIONS; i++) { - r = &d->io_regions[i]; - if (!r->size) - continue; - monitor_printf(mon, "%*sbar %d: %s at 0x%"FMT_PCIBUS - " [0x%"FMT_PCIBUS"]\n", - indent, "", - i, r->type & PCI_BASE_ADDRESS_SPACE_IO ? "i/o" : "mem", - r->addr, r->addr + r->size - 1); - } -} - -static char *pci_dev_fw_name(DeviceState *dev, char *buf, int len) -{ - PCIDevice *d = (PCIDevice *)dev; - const char *name = NULL; - const pci_class_desc *desc = pci_class_descriptions; - int class = pci_get_word(d->config + PCI_CLASS_DEVICE); - - while (desc->desc && - (class & ~desc->fw_ign_bits) != - (desc->class & ~desc->fw_ign_bits)) { - desc++; - } - - if (desc->desc) { - name = desc->fw_name; - } - - if (name) { - pstrcpy(buf, len, name); - } else { - snprintf(buf, len, "pci%04x,%04x", - pci_get_word(d->config + PCI_VENDOR_ID), - pci_get_word(d->config + PCI_DEVICE_ID)); - } - - return buf; -} - -static char *pcibus_get_fw_dev_path(DeviceState *dev) -{ - PCIDevice *d = (PCIDevice *)dev; - char path[50], name[33]; - int off; - - off = snprintf(path, sizeof(path), "%s@%x", - pci_dev_fw_name(dev, name, sizeof name), - PCI_SLOT(d->devfn)); - if (PCI_FUNC(d->devfn)) - snprintf(path + off, sizeof(path) + off, ",%x", PCI_FUNC(d->devfn)); - return g_strdup(path); -} - -static char *pcibus_get_dev_path(DeviceState *dev) -{ - PCIDevice *d = container_of(dev, PCIDevice, qdev); - PCIDevice *t; - int slot_depth; - /* Path format: Domain:00:Slot.Function:Slot.Function....:Slot.Function. - * 00 is added here to make this format compatible with - * domain:Bus:Slot.Func for systems without nested PCI bridges. - * Slot.Function list specifies the slot and function numbers for all - * devices on the path from root to the specific device. */ - char domain[] = "DDDD:00"; - char slot[] = ":SS.F"; - int domain_len = sizeof domain - 1 /* For '\0' */; - int slot_len = sizeof slot - 1 /* For '\0' */; - int path_len; - char *path, *p; - int s; - - /* Calculate # of slots on path between device and root. */; - slot_depth = 0; - for (t = d; t; t = t->bus->parent_dev) { - ++slot_depth; - } - - path_len = domain_len + slot_len * slot_depth; - - /* Allocate memory, fill in the terminating null byte. */ - path = g_malloc(path_len + 1 /* For '\0' */); - path[path_len] = '\0'; - - /* First field is the domain. */ - s = snprintf(domain, sizeof domain, "%04x:00", pci_find_domain(d->bus)); - assert(s == domain_len); - memcpy(path, domain, domain_len); - - /* Fill in slot numbers. We walk up from device to root, so need to print - * them in the reverse order, last to first. */ - p = path + path_len; - for (t = d; t; t = t->bus->parent_dev) { - p -= slot_len; - s = snprintf(slot, sizeof slot, ":%02x.%x", - PCI_SLOT(t->devfn), PCI_FUNC(t->devfn)); - assert(s == slot_len); - memcpy(p, slot, slot_len); - } - - return path; -} - -static int pci_qdev_find_recursive(PCIBus *bus, - const char *id, PCIDevice **pdev) -{ - DeviceState *qdev = qdev_find_recursive(&bus->qbus, id); - if (!qdev) { - return -ENODEV; - } - - /* roughly check if given qdev is pci device */ - if (object_dynamic_cast(OBJECT(qdev), TYPE_PCI_DEVICE)) { - *pdev = PCI_DEVICE(qdev); - return 0; - } - return -EINVAL; -} - -int pci_qdev_find_device(const char *id, PCIDevice **pdev) -{ - struct PCIHostBus *host; - int rc = -ENODEV; - - QLIST_FOREACH(host, &host_buses, next) { - int tmp = pci_qdev_find_recursive(host->bus, id, pdev); - if (!tmp) { - rc = 0; - break; - } - if (tmp != -ENODEV) { - rc = tmp; - } - } - - return rc; -} - -MemoryRegion *pci_address_space(PCIDevice *dev) -{ - return dev->bus->address_space_mem; -} - -MemoryRegion *pci_address_space_io(PCIDevice *dev) -{ - return dev->bus->address_space_io; -} - -static void pci_device_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *k = DEVICE_CLASS(klass); - k->init = pci_qdev_init; - k->unplug = pci_unplug_device; - k->exit = pci_unregister_device; - k->bus_type = TYPE_PCI_BUS; - k->props = pci_props; -} - -void pci_setup_iommu(PCIBus *bus, PCIDMAContextFunc fn, void *opaque) -{ - bus->dma_context_fn = fn; - bus->dma_context_opaque = opaque; -} - -static TypeInfo pci_device_type_info = { - .name = TYPE_PCI_DEVICE, - .parent = TYPE_DEVICE, - .instance_size = sizeof(PCIDevice), - .abstract = true, - .class_size = sizeof(PCIDeviceClass), - .class_init = pci_device_class_init, -}; - -static void pci_register_types(void) -{ - type_register_static(&pci_bus_info); - type_register_static(&pci_device_type_info); -} - -type_init(pci_register_types) diff --git a/hw/pci.h b/hw/pci.h deleted file mode 100644 index 4da0c2a4c9..0000000000 --- a/hw/pci.h +++ /dev/null @@ -1,684 +0,0 @@ -#ifndef QEMU_PCI_H -#define QEMU_PCI_H - -#include "qemu-common.h" - -#include "qdev.h" -#include "memory.h" -#include "dma.h" - -/* PCI includes legacy ISA access. */ -#include "isa.h" - -#include "pcie.h" - -/* PCI bus */ - -#define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) -#define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f) -#define PCI_FUNC(devfn) ((devfn) & 0x07) -#define PCI_SLOT_MAX 32 -#define PCI_FUNC_MAX 8 - -/* Class, Vendor and Device IDs from Linux's pci_ids.h */ -#include "pci_ids.h" - -/* QEMU-specific Vendor and Device ID definitions */ - -/* IBM (0x1014) */ -#define PCI_DEVICE_ID_IBM_440GX 0x027f -#define PCI_DEVICE_ID_IBM_OPENPIC2 0xffff - -/* Hitachi (0x1054) */ -#define PCI_VENDOR_ID_HITACHI 0x1054 -#define PCI_DEVICE_ID_HITACHI_SH7751R 0x350e - -/* Apple (0x106b) */ -#define PCI_DEVICE_ID_APPLE_343S1201 0x0010 -#define PCI_DEVICE_ID_APPLE_UNI_N_I_PCI 0x001e -#define PCI_DEVICE_ID_APPLE_UNI_N_PCI 0x001f -#define PCI_DEVICE_ID_APPLE_UNI_N_KEYL 0x0022 -#define PCI_DEVICE_ID_APPLE_IPID_USB 0x003f - -/* Realtek (0x10ec) */ -#define PCI_DEVICE_ID_REALTEK_8029 0x8029 - -/* Xilinx (0x10ee) */ -#define PCI_DEVICE_ID_XILINX_XC2VP30 0x0300 - -/* Marvell (0x11ab) */ -#define PCI_DEVICE_ID_MARVELL_GT6412X 0x4620 - -/* QEMU/Bochs VGA (0x1234) */ -#define PCI_VENDOR_ID_QEMU 0x1234 -#define PCI_DEVICE_ID_QEMU_VGA 0x1111 - -/* VMWare (0x15ad) */ -#define PCI_VENDOR_ID_VMWARE 0x15ad -#define PCI_DEVICE_ID_VMWARE_SVGA2 0x0405 -#define PCI_DEVICE_ID_VMWARE_SVGA 0x0710 -#define PCI_DEVICE_ID_VMWARE_NET 0x0720 -#define PCI_DEVICE_ID_VMWARE_SCSI 0x0730 -#define PCI_DEVICE_ID_VMWARE_IDE 0x1729 - -/* Intel (0x8086) */ -#define PCI_DEVICE_ID_INTEL_82551IT 0x1209 -#define PCI_DEVICE_ID_INTEL_82557 0x1229 -#define PCI_DEVICE_ID_INTEL_82801IR 0x2922 - -/* Red Hat / Qumranet (for QEMU) -- see pci-ids.txt */ -#define PCI_VENDOR_ID_REDHAT_QUMRANET 0x1af4 -#define PCI_SUBVENDOR_ID_REDHAT_QUMRANET 0x1af4 -#define PCI_SUBDEVICE_ID_QEMU 0x1100 - -#define PCI_DEVICE_ID_VIRTIO_NET 0x1000 -#define PCI_DEVICE_ID_VIRTIO_BLOCK 0x1001 -#define PCI_DEVICE_ID_VIRTIO_BALLOON 0x1002 -#define PCI_DEVICE_ID_VIRTIO_CONSOLE 0x1003 -#define PCI_DEVICE_ID_VIRTIO_SCSI 0x1004 -#define PCI_DEVICE_ID_VIRTIO_RNG 0x1005 - -#define FMT_PCIBUS PRIx64 - -typedef void PCIConfigWriteFunc(PCIDevice *pci_dev, - uint32_t address, uint32_t data, int len); -typedef uint32_t PCIConfigReadFunc(PCIDevice *pci_dev, - uint32_t address, int len); -typedef void PCIMapIORegionFunc(PCIDevice *pci_dev, int region_num, - pcibus_t addr, pcibus_t size, int type); -typedef void PCIUnregisterFunc(PCIDevice *pci_dev); - -typedef struct PCIIORegion { - pcibus_t addr; /* current PCI mapping address. -1 means not mapped */ -#define PCI_BAR_UNMAPPED (~(pcibus_t)0) - pcibus_t size; - uint8_t type; - MemoryRegion *memory; - MemoryRegion *address_space; -} PCIIORegion; - -#define PCI_ROM_SLOT 6 -#define PCI_NUM_REGIONS 7 - -#include "pci_regs.h" - -/* PCI HEADER_TYPE */ -#define PCI_HEADER_TYPE_MULTI_FUNCTION 0x80 - -/* Size of the standard PCI config header */ -#define PCI_CONFIG_HEADER_SIZE 0x40 -/* Size of the standard PCI config space */ -#define PCI_CONFIG_SPACE_SIZE 0x100 -/* Size of the standart PCIe config space: 4KB */ -#define PCIE_CONFIG_SPACE_SIZE 0x1000 - -#define PCI_NUM_PINS 4 /* A-D */ - -/* Bits in cap_present field. */ -enum { - QEMU_PCI_CAP_MSI = 0x1, - QEMU_PCI_CAP_MSIX = 0x2, - QEMU_PCI_CAP_EXPRESS = 0x4, - - /* multifunction capable device */ -#define QEMU_PCI_CAP_MULTIFUNCTION_BITNR 3 - QEMU_PCI_CAP_MULTIFUNCTION = (1 << QEMU_PCI_CAP_MULTIFUNCTION_BITNR), - - /* command register SERR bit enabled */ -#define QEMU_PCI_CAP_SERR_BITNR 4 - QEMU_PCI_CAP_SERR = (1 << QEMU_PCI_CAP_SERR_BITNR), - /* Standard hot plug controller. */ -#define QEMU_PCI_SHPC_BITNR 5 - QEMU_PCI_CAP_SHPC = (1 << QEMU_PCI_SHPC_BITNR), -#define QEMU_PCI_SLOTID_BITNR 6 - QEMU_PCI_CAP_SLOTID = (1 << QEMU_PCI_SLOTID_BITNR), -}; - -#define TYPE_PCI_DEVICE "pci-device" -#define PCI_DEVICE(obj) \ - OBJECT_CHECK(PCIDevice, (obj), TYPE_PCI_DEVICE) -#define PCI_DEVICE_CLASS(klass) \ - OBJECT_CLASS_CHECK(PCIDeviceClass, (klass), TYPE_PCI_DEVICE) -#define PCI_DEVICE_GET_CLASS(obj) \ - OBJECT_GET_CLASS(PCIDeviceClass, (obj), TYPE_PCI_DEVICE) - -typedef struct PCIINTxRoute { - enum { - PCI_INTX_ENABLED, - PCI_INTX_INVERTED, - PCI_INTX_DISABLED, - } mode; - int irq; -} PCIINTxRoute; - -typedef struct PCIDeviceClass { - DeviceClass parent_class; - - int (*init)(PCIDevice *dev); - PCIUnregisterFunc *exit; - PCIConfigReadFunc *config_read; - PCIConfigWriteFunc *config_write; - - uint16_t vendor_id; - uint16_t device_id; - uint8_t revision; - uint16_t class_id; - uint16_t subsystem_vendor_id; /* only for header type = 0 */ - uint16_t subsystem_id; /* only for header type = 0 */ - - /* - * pci-to-pci bridge or normal device. - * This doesn't mean pci host switch. - * When card bus bridge is supported, this would be enhanced. - */ - int is_bridge; - - /* pcie stuff */ - int is_express; /* is this device pci express? */ - - /* device isn't hot-pluggable */ - int no_hotplug; - - /* rom bar */ - const char *romfile; -} PCIDeviceClass; - -typedef void (*PCIINTxRoutingNotifier)(PCIDevice *dev); -typedef int (*MSIVectorUseNotifier)(PCIDevice *dev, unsigned int vector, - MSIMessage msg); -typedef void (*MSIVectorReleaseNotifier)(PCIDevice *dev, unsigned int vector); - -struct PCIDevice { - DeviceState qdev; - - /* PCI config space */ - uint8_t *config; - - /* Used to enable config checks on load. Note that writable bits are - * never checked even if set in cmask. */ - uint8_t *cmask; - - /* Used to implement R/W bytes */ - uint8_t *wmask; - - /* Used to implement RW1C(Write 1 to Clear) bytes */ - uint8_t *w1cmask; - - /* Used to allocate config space for capabilities. */ - uint8_t *used; - - /* the following fields are read only */ - PCIBus *bus; - int32_t devfn; - char name[64]; - PCIIORegion io_regions[PCI_NUM_REGIONS]; - AddressSpace bus_master_as; - MemoryRegion bus_master_enable_region; - DMAContext *dma; - - /* do not access the following fields */ - PCIConfigReadFunc *config_read; - PCIConfigWriteFunc *config_write; - - /* IRQ objects for the INTA-INTD pins. */ - qemu_irq *irq; - - /* Current IRQ levels. Used internally by the generic PCI code. */ - uint8_t irq_state; - - /* Capability bits */ - uint32_t cap_present; - - /* Offset of MSI-X capability in config space */ - uint8_t msix_cap; - - /* MSI-X entries */ - int msix_entries_nr; - - /* Space to store MSIX table & pending bit array */ - uint8_t *msix_table; - uint8_t *msix_pba; - /* MemoryRegion container for msix exclusive BAR setup */ - MemoryRegion msix_exclusive_bar; - /* Memory Regions for MSIX table and pending bit entries. */ - MemoryRegion msix_table_mmio; - MemoryRegion msix_pba_mmio; - /* Reference-count for entries actually in use by driver. */ - unsigned *msix_entry_used; - /* MSIX function mask set or MSIX disabled */ - bool msix_function_masked; - /* Version id needed for VMState */ - int32_t version_id; - - /* Offset of MSI capability in config space */ - uint8_t msi_cap; - - /* PCI Express */ - PCIExpressDevice exp; - - /* SHPC */ - SHPCDevice *shpc; - - /* Location of option rom */ - char *romfile; - bool has_rom; - MemoryRegion rom; - uint32_t rom_bar; - - /* INTx routing notifier */ - PCIINTxRoutingNotifier intx_routing_notifier; - - /* MSI-X notifiers */ - MSIVectorUseNotifier msix_vector_use_notifier; - MSIVectorReleaseNotifier msix_vector_release_notifier; -}; - -void pci_register_bar(PCIDevice *pci_dev, int region_num, - uint8_t attr, MemoryRegion *memory); -pcibus_t pci_get_bar_addr(PCIDevice *pci_dev, int region_num); - -int pci_add_capability(PCIDevice *pdev, uint8_t cap_id, - uint8_t offset, uint8_t size); - -void pci_del_capability(PCIDevice *pci_dev, uint8_t cap_id, uint8_t cap_size); - -uint8_t pci_find_capability(PCIDevice *pci_dev, uint8_t cap_id); - - -uint32_t pci_default_read_config(PCIDevice *d, - uint32_t address, int len); -void pci_default_write_config(PCIDevice *d, - uint32_t address, uint32_t val, int len); -void pci_device_save(PCIDevice *s, QEMUFile *f); -int pci_device_load(PCIDevice *s, QEMUFile *f); -MemoryRegion *pci_address_space(PCIDevice *dev); -MemoryRegion *pci_address_space_io(PCIDevice *dev); - -typedef void (*pci_set_irq_fn)(void *opaque, int irq_num, int level); -typedef int (*pci_map_irq_fn)(PCIDevice *pci_dev, int irq_num); -typedef PCIINTxRoute (*pci_route_irq_fn)(void *opaque, int pin); - -typedef enum { - PCI_HOTPLUG_DISABLED, - PCI_HOTPLUG_ENABLED, - PCI_COLDPLUG_ENABLED, -} PCIHotplugState; - -typedef int (*pci_hotplug_fn)(DeviceState *qdev, PCIDevice *pci_dev, - PCIHotplugState state); -void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent, - const char *name, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io, - uint8_t devfn_min); -PCIBus *pci_bus_new(DeviceState *parent, const char *name, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io, - uint8_t devfn_min); -void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, - void *irq_opaque, int nirq); -int pci_bus_get_irq_level(PCIBus *bus, int irq_num); -void pci_bus_hotplug(PCIBus *bus, pci_hotplug_fn hotplug, DeviceState *dev); -/* 0 <= pin <= 3 0 = INTA, 1 = INTB, 2 = INTC, 3 = INTD */ -int pci_swizzle_map_irq_fn(PCIDevice *pci_dev, int pin); -PCIBus *pci_register_bus(DeviceState *parent, const char *name, - pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, - void *irq_opaque, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io, - uint8_t devfn_min, int nirq); -void pci_bus_set_route_irq_fn(PCIBus *, pci_route_irq_fn); -PCIINTxRoute pci_device_route_intx_to_irq(PCIDevice *dev, int pin); -bool pci_intx_route_changed(PCIINTxRoute *old, PCIINTxRoute *new); -void pci_bus_fire_intx_routing_notifier(PCIBus *bus); -void pci_device_set_intx_routing_notifier(PCIDevice *dev, - PCIINTxRoutingNotifier notifier); -void pci_device_reset(PCIDevice *dev); -void pci_bus_reset(PCIBus *bus); - -PCIDevice *pci_nic_init(NICInfo *nd, const char *default_model, - const char *default_devaddr); -PCIDevice *pci_nic_init_nofail(NICInfo *nd, const char *default_model, - const char *default_devaddr); - -PCIDevice *pci_vga_init(PCIBus *bus); - -int pci_bus_num(PCIBus *s); -void pci_for_each_device(PCIBus *bus, int bus_num, - void (*fn)(PCIBus *bus, PCIDevice *d, void *opaque), - void *opaque); -PCIBus *pci_find_root_bus(int domain); -int pci_find_domain(const PCIBus *bus); -PCIDevice *pci_find_device(PCIBus *bus, int bus_num, uint8_t devfn); -int pci_qdev_find_device(const char *id, PCIDevice **pdev); -PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr); - -int pci_read_devaddr(Monitor *mon, const char *addr, int *domp, int *busp, - unsigned *slotp); - -void pci_device_deassert_intx(PCIDevice *dev); - -typedef DMAContext *(*PCIDMAContextFunc)(PCIBus *, void *, int); - -void pci_setup_iommu(PCIBus *bus, PCIDMAContextFunc fn, void *opaque); - -static inline void -pci_set_byte(uint8_t *config, uint8_t val) -{ - *config = val; -} - -static inline uint8_t -pci_get_byte(const uint8_t *config) -{ - return *config; -} - -static inline void -pci_set_word(uint8_t *config, uint16_t val) -{ - cpu_to_le16wu((uint16_t *)config, val); -} - -static inline uint16_t -pci_get_word(const uint8_t *config) -{ - return le16_to_cpupu((const uint16_t *)config); -} - -static inline void -pci_set_long(uint8_t *config, uint32_t val) -{ - cpu_to_le32wu((uint32_t *)config, val); -} - -static inline uint32_t -pci_get_long(const uint8_t *config) -{ - return le32_to_cpupu((const uint32_t *)config); -} - -static inline void -pci_set_quad(uint8_t *config, uint64_t val) -{ - cpu_to_le64w((uint64_t *)config, val); -} - -static inline uint64_t -pci_get_quad(const uint8_t *config) -{ - return le64_to_cpup((const uint64_t *)config); -} - -static inline void -pci_config_set_vendor_id(uint8_t *pci_config, uint16_t val) -{ - pci_set_word(&pci_config[PCI_VENDOR_ID], val); -} - -static inline void -pci_config_set_device_id(uint8_t *pci_config, uint16_t val) -{ - pci_set_word(&pci_config[PCI_DEVICE_ID], val); -} - -static inline void -pci_config_set_revision(uint8_t *pci_config, uint8_t val) -{ - pci_set_byte(&pci_config[PCI_REVISION_ID], val); -} - -static inline void -pci_config_set_class(uint8_t *pci_config, uint16_t val) -{ - pci_set_word(&pci_config[PCI_CLASS_DEVICE], val); -} - -static inline void -pci_config_set_prog_interface(uint8_t *pci_config, uint8_t val) -{ - pci_set_byte(&pci_config[PCI_CLASS_PROG], val); -} - -static inline void -pci_config_set_interrupt_pin(uint8_t *pci_config, uint8_t val) -{ - pci_set_byte(&pci_config[PCI_INTERRUPT_PIN], val); -} - -/* - * helper functions to do bit mask operation on configuration space. - * Just to set bit, use test-and-set and discard returned value. - * Just to clear bit, use test-and-clear and discard returned value. - * NOTE: They aren't atomic. - */ -static inline uint8_t -pci_byte_test_and_clear_mask(uint8_t *config, uint8_t mask) -{ - uint8_t val = pci_get_byte(config); - pci_set_byte(config, val & ~mask); - return val & mask; -} - -static inline uint8_t -pci_byte_test_and_set_mask(uint8_t *config, uint8_t mask) -{ - uint8_t val = pci_get_byte(config); - pci_set_byte(config, val | mask); - return val & mask; -} - -static inline uint16_t -pci_word_test_and_clear_mask(uint8_t *config, uint16_t mask) -{ - uint16_t val = pci_get_word(config); - pci_set_word(config, val & ~mask); - return val & mask; -} - -static inline uint16_t -pci_word_test_and_set_mask(uint8_t *config, uint16_t mask) -{ - uint16_t val = pci_get_word(config); - pci_set_word(config, val | mask); - return val & mask; -} - -static inline uint32_t -pci_long_test_and_clear_mask(uint8_t *config, uint32_t mask) -{ - uint32_t val = pci_get_long(config); - pci_set_long(config, val & ~mask); - return val & mask; -} - -static inline uint32_t -pci_long_test_and_set_mask(uint8_t *config, uint32_t mask) -{ - uint32_t val = pci_get_long(config); - pci_set_long(config, val | mask); - return val & mask; -} - -static inline uint64_t -pci_quad_test_and_clear_mask(uint8_t *config, uint64_t mask) -{ - uint64_t val = pci_get_quad(config); - pci_set_quad(config, val & ~mask); - return val & mask; -} - -static inline uint64_t -pci_quad_test_and_set_mask(uint8_t *config, uint64_t mask) -{ - uint64_t val = pci_get_quad(config); - pci_set_quad(config, val | mask); - return val & mask; -} - -/* Access a register specified by a mask */ -static inline void -pci_set_byte_by_mask(uint8_t *config, uint8_t mask, uint8_t reg) -{ - uint8_t val = pci_get_byte(config); - uint8_t rval = reg << (ffs(mask) - 1); - pci_set_byte(config, (~mask & val) | (mask & rval)); -} - -static inline uint8_t -pci_get_byte_by_mask(uint8_t *config, uint8_t mask) -{ - uint8_t val = pci_get_byte(config); - return (val & mask) >> (ffs(mask) - 1); -} - -static inline void -pci_set_word_by_mask(uint8_t *config, uint16_t mask, uint16_t reg) -{ - uint16_t val = pci_get_word(config); - uint16_t rval = reg << (ffs(mask) - 1); - pci_set_word(config, (~mask & val) | (mask & rval)); -} - -static inline uint16_t -pci_get_word_by_mask(uint8_t *config, uint16_t mask) -{ - uint16_t val = pci_get_word(config); - return (val & mask) >> (ffs(mask) - 1); -} - -static inline void -pci_set_long_by_mask(uint8_t *config, uint32_t mask, uint32_t reg) -{ - uint32_t val = pci_get_long(config); - uint32_t rval = reg << (ffs(mask) - 1); - pci_set_long(config, (~mask & val) | (mask & rval)); -} - -static inline uint32_t -pci_get_long_by_mask(uint8_t *config, uint32_t mask) -{ - uint32_t val = pci_get_long(config); - return (val & mask) >> (ffs(mask) - 1); -} - -static inline void -pci_set_quad_by_mask(uint8_t *config, uint64_t mask, uint64_t reg) -{ - uint64_t val = pci_get_quad(config); - uint64_t rval = reg << (ffs(mask) - 1); - pci_set_quad(config, (~mask & val) | (mask & rval)); -} - -static inline uint64_t -pci_get_quad_by_mask(uint8_t *config, uint64_t mask) -{ - uint64_t val = pci_get_quad(config); - return (val & mask) >> (ffs(mask) - 1); -} - -PCIDevice *pci_create_multifunction(PCIBus *bus, int devfn, bool multifunction, - const char *name); -PCIDevice *pci_create_simple_multifunction(PCIBus *bus, int devfn, - bool multifunction, - const char *name); -PCIDevice *pci_create(PCIBus *bus, int devfn, const char *name); -PCIDevice *pci_create_simple(PCIBus *bus, int devfn, const char *name); - -static inline int pci_is_express(const PCIDevice *d) -{ - return d->cap_present & QEMU_PCI_CAP_EXPRESS; -} - -static inline uint32_t pci_config_size(const PCIDevice *d) -{ - return pci_is_express(d) ? PCIE_CONFIG_SPACE_SIZE : PCI_CONFIG_SPACE_SIZE; -} - -/* DMA access functions */ -static inline DMAContext *pci_dma_context(PCIDevice *dev) -{ - return dev->dma; -} - -static inline int pci_dma_rw(PCIDevice *dev, dma_addr_t addr, - void *buf, dma_addr_t len, DMADirection dir) -{ - dma_memory_rw(pci_dma_context(dev), addr, buf, len, dir); - return 0; -} - -static inline int pci_dma_read(PCIDevice *dev, dma_addr_t addr, - void *buf, dma_addr_t len) -{ - return pci_dma_rw(dev, addr, buf, len, DMA_DIRECTION_TO_DEVICE); -} - -static inline int pci_dma_write(PCIDevice *dev, dma_addr_t addr, - const void *buf, dma_addr_t len) -{ - return pci_dma_rw(dev, addr, (void *) buf, len, DMA_DIRECTION_FROM_DEVICE); -} - -#define PCI_DMA_DEFINE_LDST(_l, _s, _bits) \ - static inline uint##_bits##_t ld##_l##_pci_dma(PCIDevice *dev, \ - dma_addr_t addr) \ - { \ - return ld##_l##_dma(pci_dma_context(dev), addr); \ - } \ - static inline void st##_s##_pci_dma(PCIDevice *dev, \ - dma_addr_t addr, uint##_bits##_t val) \ - { \ - st##_s##_dma(pci_dma_context(dev), addr, val); \ - } - -PCI_DMA_DEFINE_LDST(ub, b, 8); -PCI_DMA_DEFINE_LDST(uw_le, w_le, 16) -PCI_DMA_DEFINE_LDST(l_le, l_le, 32); -PCI_DMA_DEFINE_LDST(q_le, q_le, 64); -PCI_DMA_DEFINE_LDST(uw_be, w_be, 16) -PCI_DMA_DEFINE_LDST(l_be, l_be, 32); -PCI_DMA_DEFINE_LDST(q_be, q_be, 64); - -#undef PCI_DMA_DEFINE_LDST - -static inline void *pci_dma_map(PCIDevice *dev, dma_addr_t addr, - dma_addr_t *plen, DMADirection dir) -{ - void *buf; - - buf = dma_memory_map(pci_dma_context(dev), addr, plen, dir); - return buf; -} - -static inline void pci_dma_unmap(PCIDevice *dev, void *buffer, dma_addr_t len, - DMADirection dir, dma_addr_t access_len) -{ - dma_memory_unmap(pci_dma_context(dev), buffer, len, dir, access_len); -} - -static inline void pci_dma_sglist_init(QEMUSGList *qsg, PCIDevice *dev, - int alloc_hint) -{ - qemu_sglist_init(qsg, alloc_hint, pci_dma_context(dev)); -} - -extern const VMStateDescription vmstate_pci_device; - -#define VMSTATE_PCI_DEVICE(_field, _state) { \ - .name = (stringify(_field)), \ - .size = sizeof(PCIDevice), \ - .vmsd = &vmstate_pci_device, \ - .flags = VMS_STRUCT, \ - .offset = vmstate_offset_value(_state, _field, PCIDevice), \ -} - -#define VMSTATE_PCI_DEVICE_POINTER(_field, _state) { \ - .name = (stringify(_field)), \ - .size = sizeof(PCIDevice), \ - .vmsd = &vmstate_pci_device, \ - .flags = VMS_STRUCT|VMS_POINTER, \ - .offset = vmstate_offset_pointer(_state, _field, PCIDevice), \ -} - -#endif diff --git a/hw/pci/Makefile.objs b/hw/pci/Makefile.objs new file mode 100644 index 0000000000..aa7a0e84b5 --- /dev/null +++ b/hw/pci/Makefile.objs @@ -0,0 +1,7 @@ +common-obj-$(CONFIG_PCI) += pci.o pci_bridge.o +common-obj-$(CONFIG_PCI) += msix.o msi.o +common-obj-$(CONFIG_PCI) += shpc.o +common-obj-$(CONFIG_PCI) += slotid_cap.o +common-obj-$(CONFIG_PCI) += pci_host.o pcie_host.o +common-obj-$(CONFIG_PCI) += pcie.o pcie_aer.o pcie_port.o +common-obj-$(CONFIG_NO_PCI) += pci-stub.o diff --git a/hw/pci/msi.c b/hw/pci/msi.c new file mode 100644 index 0000000000..33037a80e9 --- /dev/null +++ b/hw/pci/msi.c @@ -0,0 +1,395 @@ +/* + * msi.c + * + * Copyright (c) 2010 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "msi.h" +#include "range.h" + +/* Eventually those constants should go to Linux pci_regs.h */ +#define PCI_MSI_PENDING_32 0x10 +#define PCI_MSI_PENDING_64 0x14 + +/* PCI_MSI_ADDRESS_LO */ +#define PCI_MSI_ADDRESS_LO_MASK (~0x3) + +/* If we get rid of cap allocator, we won't need those. */ +#define PCI_MSI_32_SIZEOF 0x0a +#define PCI_MSI_64_SIZEOF 0x0e +#define PCI_MSI_32M_SIZEOF 0x14 +#define PCI_MSI_64M_SIZEOF 0x18 + +#define PCI_MSI_VECTORS_MAX 32 + +/* Flag for interrupt controller to declare MSI/MSI-X support */ +bool msi_supported; + +/* If we get rid of cap allocator, we won't need this. */ +static inline uint8_t msi_cap_sizeof(uint16_t flags) +{ + switch (flags & (PCI_MSI_FLAGS_MASKBIT | PCI_MSI_FLAGS_64BIT)) { + case PCI_MSI_FLAGS_MASKBIT | PCI_MSI_FLAGS_64BIT: + return PCI_MSI_64M_SIZEOF; + case PCI_MSI_FLAGS_64BIT: + return PCI_MSI_64_SIZEOF; + case PCI_MSI_FLAGS_MASKBIT: + return PCI_MSI_32M_SIZEOF; + case 0: + return PCI_MSI_32_SIZEOF; + default: + abort(); + break; + } + return 0; +} + +//#define MSI_DEBUG + +#ifdef MSI_DEBUG +# define MSI_DPRINTF(fmt, ...) \ + fprintf(stderr, "%s:%d " fmt, __func__, __LINE__, ## __VA_ARGS__) +#else +# define MSI_DPRINTF(fmt, ...) do { } while (0) +#endif +#define MSI_DEV_PRINTF(dev, fmt, ...) \ + MSI_DPRINTF("%s:%x " fmt, (dev)->name, (dev)->devfn, ## __VA_ARGS__) + +static inline unsigned int msi_nr_vectors(uint16_t flags) +{ + return 1U << + ((flags & PCI_MSI_FLAGS_QSIZE) >> (ffs(PCI_MSI_FLAGS_QSIZE) - 1)); +} + +static inline uint8_t msi_flags_off(const PCIDevice* dev) +{ + return dev->msi_cap + PCI_MSI_FLAGS; +} + +static inline uint8_t msi_address_lo_off(const PCIDevice* dev) +{ + return dev->msi_cap + PCI_MSI_ADDRESS_LO; +} + +static inline uint8_t msi_address_hi_off(const PCIDevice* dev) +{ + return dev->msi_cap + PCI_MSI_ADDRESS_HI; +} + +static inline uint8_t msi_data_off(const PCIDevice* dev, bool msi64bit) +{ + return dev->msi_cap + (msi64bit ? PCI_MSI_DATA_64 : PCI_MSI_DATA_32); +} + +static inline uint8_t msi_mask_off(const PCIDevice* dev, bool msi64bit) +{ + return dev->msi_cap + (msi64bit ? PCI_MSI_MASK_64 : PCI_MSI_MASK_32); +} + +static inline uint8_t msi_pending_off(const PCIDevice* dev, bool msi64bit) +{ + return dev->msi_cap + (msi64bit ? PCI_MSI_PENDING_64 : PCI_MSI_PENDING_32); +} + +/* + * Special API for POWER to configure the vectors through + * a side channel. Should never be used by devices. + */ +void msi_set_message(PCIDevice *dev, MSIMessage msg) +{ + uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); + bool msi64bit = flags & PCI_MSI_FLAGS_64BIT; + + if (msi64bit) { + pci_set_quad(dev->config + msi_address_lo_off(dev), msg.address); + } else { + pci_set_long(dev->config + msi_address_lo_off(dev), msg.address); + } + pci_set_word(dev->config + msi_data_off(dev, msi64bit), msg.data); +} + +MSIMessage msi_get_message(PCIDevice *dev, unsigned int vector) +{ + uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); + bool msi64bit = flags & PCI_MSI_FLAGS_64BIT; + unsigned int nr_vectors = msi_nr_vectors(flags); + MSIMessage msg; + + assert(vector < nr_vectors); + + if (msi64bit) { + msg.address = pci_get_quad(dev->config + msi_address_lo_off(dev)); + } else { + msg.address = pci_get_long(dev->config + msi_address_lo_off(dev)); + } + + /* upper bit 31:16 is zero */ + msg.data = pci_get_word(dev->config + msi_data_off(dev, msi64bit)); + if (nr_vectors > 1) { + msg.data &= ~(nr_vectors - 1); + msg.data |= vector; + } + + return msg; +} + +bool msi_enabled(const PCIDevice *dev) +{ + return msi_present(dev) && + (pci_get_word(dev->config + msi_flags_off(dev)) & + PCI_MSI_FLAGS_ENABLE); +} + +int msi_init(struct PCIDevice *dev, uint8_t offset, + unsigned int nr_vectors, bool msi64bit, bool msi_per_vector_mask) +{ + unsigned int vectors_order; + uint16_t flags; + uint8_t cap_size; + int config_offset; + + if (!msi_supported) { + return -ENOTSUP; + } + + MSI_DEV_PRINTF(dev, + "init offset: 0x%"PRIx8" vector: %"PRId8 + " 64bit %d mask %d\n", + offset, nr_vectors, msi64bit, msi_per_vector_mask); + + assert(!(nr_vectors & (nr_vectors - 1))); /* power of 2 */ + assert(nr_vectors > 0); + assert(nr_vectors <= PCI_MSI_VECTORS_MAX); + /* the nr of MSI vectors is up to 32 */ + vectors_order = ffs(nr_vectors) - 1; + + flags = vectors_order << (ffs(PCI_MSI_FLAGS_QMASK) - 1); + if (msi64bit) { + flags |= PCI_MSI_FLAGS_64BIT; + } + if (msi_per_vector_mask) { + flags |= PCI_MSI_FLAGS_MASKBIT; + } + + cap_size = msi_cap_sizeof(flags); + config_offset = pci_add_capability(dev, PCI_CAP_ID_MSI, offset, cap_size); + if (config_offset < 0) { + return config_offset; + } + + dev->msi_cap = config_offset; + dev->cap_present |= QEMU_PCI_CAP_MSI; + + pci_set_word(dev->config + msi_flags_off(dev), flags); + pci_set_word(dev->wmask + msi_flags_off(dev), + PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE); + pci_set_long(dev->wmask + msi_address_lo_off(dev), + PCI_MSI_ADDRESS_LO_MASK); + if (msi64bit) { + pci_set_long(dev->wmask + msi_address_hi_off(dev), 0xffffffff); + } + pci_set_word(dev->wmask + msi_data_off(dev, msi64bit), 0xffff); + + if (msi_per_vector_mask) { + /* Make mask bits 0 to nr_vectors - 1 writable. */ + pci_set_long(dev->wmask + msi_mask_off(dev, msi64bit), + 0xffffffff >> (PCI_MSI_VECTORS_MAX - nr_vectors)); + } + return config_offset; +} + +void msi_uninit(struct PCIDevice *dev) +{ + uint16_t flags; + uint8_t cap_size; + + if (!msi_present(dev)) { + return; + } + flags = pci_get_word(dev->config + msi_flags_off(dev)); + cap_size = msi_cap_sizeof(flags); + pci_del_capability(dev, PCI_CAP_ID_MSI, cap_size); + dev->cap_present &= ~QEMU_PCI_CAP_MSI; + + MSI_DEV_PRINTF(dev, "uninit\n"); +} + +void msi_reset(PCIDevice *dev) +{ + uint16_t flags; + bool msi64bit; + + if (!msi_present(dev)) { + return; + } + + flags = pci_get_word(dev->config + msi_flags_off(dev)); + flags &= ~(PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE); + msi64bit = flags & PCI_MSI_FLAGS_64BIT; + + pci_set_word(dev->config + msi_flags_off(dev), flags); + pci_set_long(dev->config + msi_address_lo_off(dev), 0); + if (msi64bit) { + pci_set_long(dev->config + msi_address_hi_off(dev), 0); + } + pci_set_word(dev->config + msi_data_off(dev, msi64bit), 0); + if (flags & PCI_MSI_FLAGS_MASKBIT) { + pci_set_long(dev->config + msi_mask_off(dev, msi64bit), 0); + pci_set_long(dev->config + msi_pending_off(dev, msi64bit), 0); + } + MSI_DEV_PRINTF(dev, "reset\n"); +} + +static bool msi_is_masked(const PCIDevice *dev, unsigned int vector) +{ + uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); + uint32_t mask; + assert(vector < PCI_MSI_VECTORS_MAX); + + if (!(flags & PCI_MSI_FLAGS_MASKBIT)) { + return false; + } + + mask = pci_get_long(dev->config + + msi_mask_off(dev, flags & PCI_MSI_FLAGS_64BIT)); + return mask & (1U << vector); +} + +void msi_notify(PCIDevice *dev, unsigned int vector) +{ + uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); + bool msi64bit = flags & PCI_MSI_FLAGS_64BIT; + unsigned int nr_vectors = msi_nr_vectors(flags); + MSIMessage msg; + + assert(vector < nr_vectors); + if (msi_is_masked(dev, vector)) { + assert(flags & PCI_MSI_FLAGS_MASKBIT); + pci_long_test_and_set_mask( + dev->config + msi_pending_off(dev, msi64bit), 1U << vector); + MSI_DEV_PRINTF(dev, "pending vector 0x%x\n", vector); + return; + } + + msg = msi_get_message(dev, vector); + + MSI_DEV_PRINTF(dev, + "notify vector 0x%x" + " address: 0x%"PRIx64" data: 0x%"PRIx32"\n", + vector, msg.address, msg.data); + stl_le_phys(msg.address, msg.data); +} + +/* Normally called by pci_default_write_config(). */ +void msi_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len) +{ + uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); + bool msi64bit = flags & PCI_MSI_FLAGS_64BIT; + bool msi_per_vector_mask = flags & PCI_MSI_FLAGS_MASKBIT; + unsigned int nr_vectors; + uint8_t log_num_vecs; + uint8_t log_max_vecs; + unsigned int vector; + uint32_t pending; + + if (!msi_present(dev) || + !ranges_overlap(addr, len, dev->msi_cap, msi_cap_sizeof(flags))) { + return; + } + +#ifdef MSI_DEBUG + MSI_DEV_PRINTF(dev, "addr 0x%"PRIx32" val 0x%"PRIx32" len %d\n", + addr, val, len); + MSI_DEV_PRINTF(dev, "ctrl: 0x%"PRIx16" address: 0x%"PRIx32, + flags, + pci_get_long(dev->config + msi_address_lo_off(dev))); + if (msi64bit) { + fprintf(stderr, " address-hi: 0x%"PRIx32, + pci_get_long(dev->config + msi_address_hi_off(dev))); + } + fprintf(stderr, " data: 0x%"PRIx16, + pci_get_word(dev->config + msi_data_off(dev, msi64bit))); + if (flags & PCI_MSI_FLAGS_MASKBIT) { + fprintf(stderr, " mask 0x%"PRIx32" pending 0x%"PRIx32, + pci_get_long(dev->config + msi_mask_off(dev, msi64bit)), + pci_get_long(dev->config + msi_pending_off(dev, msi64bit))); + } + fprintf(stderr, "\n"); +#endif + + if (!(flags & PCI_MSI_FLAGS_ENABLE)) { + return; + } + + /* + * Now MSI is enabled, clear INTx# interrupts. + * the driver is prohibited from writing enable bit to mask + * a service request. But the guest OS could do this. + * So we just discard the interrupts as moderate fallback. + * + * 6.8.3.3. Enabling Operation + * While enabled for MSI or MSI-X operation, a function is prohibited + * from using its INTx# pin (if implemented) to request + * service (MSI, MSI-X, and INTx# are mutually exclusive). + */ + pci_device_deassert_intx(dev); + + /* + * nr_vectors might be set bigger than capable. So clamp it. + * This is not legal by spec, so we can do anything we like, + * just don't crash the host + */ + log_num_vecs = + (flags & PCI_MSI_FLAGS_QSIZE) >> (ffs(PCI_MSI_FLAGS_QSIZE) - 1); + log_max_vecs = + (flags & PCI_MSI_FLAGS_QMASK) >> (ffs(PCI_MSI_FLAGS_QMASK) - 1); + if (log_num_vecs > log_max_vecs) { + flags &= ~PCI_MSI_FLAGS_QSIZE; + flags |= log_max_vecs << (ffs(PCI_MSI_FLAGS_QSIZE) - 1); + pci_set_word(dev->config + msi_flags_off(dev), flags); + } + + if (!msi_per_vector_mask) { + /* if per vector masking isn't supported, + there is no pending interrupt. */ + return; + } + + nr_vectors = msi_nr_vectors(flags); + + /* This will discard pending interrupts, if any. */ + pending = pci_get_long(dev->config + msi_pending_off(dev, msi64bit)); + pending &= 0xffffffff >> (PCI_MSI_VECTORS_MAX - nr_vectors); + pci_set_long(dev->config + msi_pending_off(dev, msi64bit), pending); + + /* deliver pending interrupts which are unmasked */ + for (vector = 0; vector < nr_vectors; ++vector) { + if (msi_is_masked(dev, vector) || !(pending & (1U << vector))) { + continue; + } + + pci_long_test_and_clear_mask( + dev->config + msi_pending_off(dev, msi64bit), 1U << vector); + msi_notify(dev, vector); + } +} + +unsigned int msi_nr_vectors_allocated(const PCIDevice *dev) +{ + uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); + return msi_nr_vectors(flags); +} diff --git a/hw/pci/msi.h b/hw/pci/msi.h new file mode 100644 index 0000000000..150b09a19d --- /dev/null +++ b/hw/pci/msi.h @@ -0,0 +1,50 @@ +/* + * msi.h + * + * Copyright (c) 2010 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#ifndef QEMU_MSI_H +#define QEMU_MSI_H + +#include "qemu-common.h" +#include "pci.h" + +struct MSIMessage { + uint64_t address; + uint32_t data; +}; + +extern bool msi_supported; + +void msi_set_message(PCIDevice *dev, MSIMessage msg); +MSIMessage msi_get_message(PCIDevice *dev, unsigned int vector); +bool msi_enabled(const PCIDevice *dev); +int msi_init(struct PCIDevice *dev, uint8_t offset, + unsigned int nr_vectors, bool msi64bit, bool msi_per_vector_mask); +void msi_uninit(struct PCIDevice *dev); +void msi_reset(PCIDevice *dev); +void msi_notify(PCIDevice *dev, unsigned int vector); +void msi_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len); +unsigned int msi_nr_vectors_allocated(const PCIDevice *dev); + +static inline bool msi_present(const PCIDevice *dev) +{ + return dev->cap_present & QEMU_PCI_CAP_MSI; +} + +#endif /* QEMU_MSI_H */ diff --git a/hw/pci/msix.c b/hw/pci/msix.c new file mode 100644 index 0000000000..136ef09373 --- /dev/null +++ b/hw/pci/msix.c @@ -0,0 +1,562 @@ +/* + * MSI-X device support + * + * This module includes support for MSI-X in pci devices. + * + * Author: Michael S. Tsirkin + * + * Copyright (c) 2009, Red Hat Inc, Michael S. Tsirkin (mst@redhat.com) + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#include "hw.h" +#include "msi.h" +#include "msix.h" +#include "pci.h" +#include "range.h" + +#define MSIX_CAP_LENGTH 12 + +/* MSI enable bit and maskall bit are in byte 1 in FLAGS register */ +#define MSIX_CONTROL_OFFSET (PCI_MSIX_FLAGS + 1) +#define MSIX_ENABLE_MASK (PCI_MSIX_FLAGS_ENABLE >> 8) +#define MSIX_MASKALL_MASK (PCI_MSIX_FLAGS_MASKALL >> 8) + +static MSIMessage msix_get_message(PCIDevice *dev, unsigned vector) +{ + uint8_t *table_entry = dev->msix_table + vector * PCI_MSIX_ENTRY_SIZE; + MSIMessage msg; + + msg.address = pci_get_quad(table_entry + PCI_MSIX_ENTRY_LOWER_ADDR); + msg.data = pci_get_long(table_entry + PCI_MSIX_ENTRY_DATA); + return msg; +} + +/* + * Special API for POWER to configure the vectors through + * a side channel. Should never be used by devices. + */ +void msix_set_message(PCIDevice *dev, int vector, struct MSIMessage msg) +{ + uint8_t *table_entry = dev->msix_table + vector * PCI_MSIX_ENTRY_SIZE; + + pci_set_quad(table_entry + PCI_MSIX_ENTRY_LOWER_ADDR, msg.address); + pci_set_long(table_entry + PCI_MSIX_ENTRY_DATA, msg.data); + table_entry[PCI_MSIX_ENTRY_VECTOR_CTRL] &= ~PCI_MSIX_ENTRY_CTRL_MASKBIT; +} + +static uint8_t msix_pending_mask(int vector) +{ + return 1 << (vector % 8); +} + +static uint8_t *msix_pending_byte(PCIDevice *dev, int vector) +{ + return dev->msix_pba + vector / 8; +} + +static int msix_is_pending(PCIDevice *dev, int vector) +{ + return *msix_pending_byte(dev, vector) & msix_pending_mask(vector); +} + +static void msix_set_pending(PCIDevice *dev, int vector) +{ + *msix_pending_byte(dev, vector) |= msix_pending_mask(vector); +} + +static void msix_clr_pending(PCIDevice *dev, int vector) +{ + *msix_pending_byte(dev, vector) &= ~msix_pending_mask(vector); +} + +static bool msix_vector_masked(PCIDevice *dev, int vector, bool fmask) +{ + unsigned offset = vector * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL; + return fmask || dev->msix_table[offset] & PCI_MSIX_ENTRY_CTRL_MASKBIT; +} + +static bool msix_is_masked(PCIDevice *dev, int vector) +{ + return msix_vector_masked(dev, vector, dev->msix_function_masked); +} + +static void msix_fire_vector_notifier(PCIDevice *dev, + unsigned int vector, bool is_masked) +{ + MSIMessage msg; + int ret; + + if (!dev->msix_vector_use_notifier) { + return; + } + if (is_masked) { + dev->msix_vector_release_notifier(dev, vector); + } else { + msg = msix_get_message(dev, vector); + ret = dev->msix_vector_use_notifier(dev, vector, msg); + assert(ret >= 0); + } +} + +static void msix_handle_mask_update(PCIDevice *dev, int vector, bool was_masked) +{ + bool is_masked = msix_is_masked(dev, vector); + + if (is_masked == was_masked) { + return; + } + + msix_fire_vector_notifier(dev, vector, is_masked); + + if (!is_masked && msix_is_pending(dev, vector)) { + msix_clr_pending(dev, vector); + msix_notify(dev, vector); + } +} + +static void msix_update_function_masked(PCIDevice *dev) +{ + dev->msix_function_masked = !msix_enabled(dev) || + (dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] & MSIX_MASKALL_MASK); +} + +/* Handle MSI-X capability config write. */ +void msix_write_config(PCIDevice *dev, uint32_t addr, + uint32_t val, int len) +{ + unsigned enable_pos = dev->msix_cap + MSIX_CONTROL_OFFSET; + int vector; + bool was_masked; + + if (!msix_present(dev) || !range_covers_byte(addr, len, enable_pos)) { + return; + } + + was_masked = dev->msix_function_masked; + msix_update_function_masked(dev); + + if (!msix_enabled(dev)) { + return; + } + + pci_device_deassert_intx(dev); + + if (dev->msix_function_masked == was_masked) { + return; + } + + for (vector = 0; vector < dev->msix_entries_nr; ++vector) { + msix_handle_mask_update(dev, vector, + msix_vector_masked(dev, vector, was_masked)); + } +} + +static uint64_t msix_table_mmio_read(void *opaque, hwaddr addr, + unsigned size) +{ + PCIDevice *dev = opaque; + + return pci_get_long(dev->msix_table + addr); +} + +static void msix_table_mmio_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PCIDevice *dev = opaque; + int vector = addr / PCI_MSIX_ENTRY_SIZE; + bool was_masked; + + was_masked = msix_is_masked(dev, vector); + pci_set_long(dev->msix_table + addr, val); + msix_handle_mask_update(dev, vector, was_masked); +} + +static const MemoryRegionOps msix_table_mmio_ops = { + .read = msix_table_mmio_read, + .write = msix_table_mmio_write, + /* TODO: MSIX should be LITTLE_ENDIAN. */ + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static uint64_t msix_pba_mmio_read(void *opaque, hwaddr addr, + unsigned size) +{ + PCIDevice *dev = opaque; + + return pci_get_long(dev->msix_pba + addr); +} + +static const MemoryRegionOps msix_pba_mmio_ops = { + .read = msix_pba_mmio_read, + /* TODO: MSIX should be LITTLE_ENDIAN. */ + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void msix_mask_all(struct PCIDevice *dev, unsigned nentries) +{ + int vector; + + for (vector = 0; vector < nentries; ++vector) { + unsigned offset = + vector * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL; + bool was_masked = msix_is_masked(dev, vector); + + dev->msix_table[offset] |= PCI_MSIX_ENTRY_CTRL_MASKBIT; + msix_handle_mask_update(dev, vector, was_masked); + } +} + +/* Initialize the MSI-X structures */ +int msix_init(struct PCIDevice *dev, unsigned short nentries, + MemoryRegion *table_bar, uint8_t table_bar_nr, + unsigned table_offset, MemoryRegion *pba_bar, + uint8_t pba_bar_nr, unsigned pba_offset, uint8_t cap_pos) +{ + int cap; + unsigned table_size, pba_size; + uint8_t *config; + + /* Nothing to do if MSI is not supported by interrupt controller */ + if (!msi_supported) { + return -ENOTSUP; + } + + if (nentries < 1 || nentries > PCI_MSIX_FLAGS_QSIZE + 1) { + return -EINVAL; + } + + table_size = nentries * PCI_MSIX_ENTRY_SIZE; + pba_size = QEMU_ALIGN_UP(nentries, 64) / 8; + + /* Sanity test: table & pba don't overlap, fit within BARs, min aligned */ + if ((table_bar_nr == pba_bar_nr && + ranges_overlap(table_offset, table_size, pba_offset, pba_size)) || + table_offset + table_size > memory_region_size(table_bar) || + pba_offset + pba_size > memory_region_size(pba_bar) || + (table_offset | pba_offset) & PCI_MSIX_FLAGS_BIRMASK) { + return -EINVAL; + } + + cap = pci_add_capability(dev, PCI_CAP_ID_MSIX, cap_pos, MSIX_CAP_LENGTH); + if (cap < 0) { + return cap; + } + + dev->msix_cap = cap; + dev->cap_present |= QEMU_PCI_CAP_MSIX; + config = dev->config + cap; + + pci_set_word(config + PCI_MSIX_FLAGS, nentries - 1); + dev->msix_entries_nr = nentries; + dev->msix_function_masked = true; + + pci_set_long(config + PCI_MSIX_TABLE, table_offset | table_bar_nr); + pci_set_long(config + PCI_MSIX_PBA, pba_offset | pba_bar_nr); + + /* Make flags bit writable. */ + dev->wmask[cap + MSIX_CONTROL_OFFSET] |= MSIX_ENABLE_MASK | + MSIX_MASKALL_MASK; + + dev->msix_table = g_malloc0(table_size); + dev->msix_pba = g_malloc0(pba_size); + dev->msix_entry_used = g_malloc0(nentries * sizeof *dev->msix_entry_used); + + msix_mask_all(dev, nentries); + + memory_region_init_io(&dev->msix_table_mmio, &msix_table_mmio_ops, dev, + "msix-table", table_size); + memory_region_add_subregion(table_bar, table_offset, &dev->msix_table_mmio); + memory_region_init_io(&dev->msix_pba_mmio, &msix_pba_mmio_ops, dev, + "msix-pba", pba_size); + memory_region_add_subregion(pba_bar, pba_offset, &dev->msix_pba_mmio); + + return 0; +} + +int msix_init_exclusive_bar(PCIDevice *dev, unsigned short nentries, + uint8_t bar_nr) +{ + int ret; + char *name; + + /* + * Migration compatibility dictates that this remains a 4k + * BAR with the vector table in the lower half and PBA in + * the upper half. Do not use these elsewhere! + */ +#define MSIX_EXCLUSIVE_BAR_SIZE 4096 +#define MSIX_EXCLUSIVE_BAR_TABLE_OFFSET 0 +#define MSIX_EXCLUSIVE_BAR_PBA_OFFSET (MSIX_EXCLUSIVE_BAR_SIZE / 2) +#define MSIX_EXCLUSIVE_CAP_OFFSET 0 + + if (nentries * PCI_MSIX_ENTRY_SIZE > MSIX_EXCLUSIVE_BAR_PBA_OFFSET) { + return -EINVAL; + } + + name = g_strdup_printf("%s-msix", dev->name); + memory_region_init(&dev->msix_exclusive_bar, name, MSIX_EXCLUSIVE_BAR_SIZE); + g_free(name); + + ret = msix_init(dev, nentries, &dev->msix_exclusive_bar, bar_nr, + MSIX_EXCLUSIVE_BAR_TABLE_OFFSET, &dev->msix_exclusive_bar, + bar_nr, MSIX_EXCLUSIVE_BAR_PBA_OFFSET, + MSIX_EXCLUSIVE_CAP_OFFSET); + if (ret) { + memory_region_destroy(&dev->msix_exclusive_bar); + return ret; + } + + pci_register_bar(dev, bar_nr, PCI_BASE_ADDRESS_SPACE_MEMORY, + &dev->msix_exclusive_bar); + + return 0; +} + +static void msix_free_irq_entries(PCIDevice *dev) +{ + int vector; + + for (vector = 0; vector < dev->msix_entries_nr; ++vector) { + dev->msix_entry_used[vector] = 0; + msix_clr_pending(dev, vector); + } +} + +static void msix_clear_all_vectors(PCIDevice *dev) +{ + int vector; + + for (vector = 0; vector < dev->msix_entries_nr; ++vector) { + msix_clr_pending(dev, vector); + } +} + +/* Clean up resources for the device. */ +void msix_uninit(PCIDevice *dev, MemoryRegion *table_bar, MemoryRegion *pba_bar) +{ + if (!msix_present(dev)) { + return; + } + pci_del_capability(dev, PCI_CAP_ID_MSIX, MSIX_CAP_LENGTH); + dev->msix_cap = 0; + msix_free_irq_entries(dev); + dev->msix_entries_nr = 0; + memory_region_del_subregion(pba_bar, &dev->msix_pba_mmio); + memory_region_destroy(&dev->msix_pba_mmio); + g_free(dev->msix_pba); + dev->msix_pba = NULL; + memory_region_del_subregion(table_bar, &dev->msix_table_mmio); + memory_region_destroy(&dev->msix_table_mmio); + g_free(dev->msix_table); + dev->msix_table = NULL; + g_free(dev->msix_entry_used); + dev->msix_entry_used = NULL; + dev->cap_present &= ~QEMU_PCI_CAP_MSIX; +} + +void msix_uninit_exclusive_bar(PCIDevice *dev) +{ + if (msix_present(dev)) { + msix_uninit(dev, &dev->msix_exclusive_bar, &dev->msix_exclusive_bar); + memory_region_destroy(&dev->msix_exclusive_bar); + } +} + +void msix_save(PCIDevice *dev, QEMUFile *f) +{ + unsigned n = dev->msix_entries_nr; + + if (!msix_present(dev)) { + return; + } + + qemu_put_buffer(f, dev->msix_table, n * PCI_MSIX_ENTRY_SIZE); + qemu_put_buffer(f, dev->msix_pba, (n + 7) / 8); +} + +/* Should be called after restoring the config space. */ +void msix_load(PCIDevice *dev, QEMUFile *f) +{ + unsigned n = dev->msix_entries_nr; + unsigned int vector; + + if (!msix_present(dev)) { + return; + } + + msix_clear_all_vectors(dev); + qemu_get_buffer(f, dev->msix_table, n * PCI_MSIX_ENTRY_SIZE); + qemu_get_buffer(f, dev->msix_pba, (n + 7) / 8); + msix_update_function_masked(dev); + + for (vector = 0; vector < n; vector++) { + msix_handle_mask_update(dev, vector, true); + } +} + +/* Does device support MSI-X? */ +int msix_present(PCIDevice *dev) +{ + return dev->cap_present & QEMU_PCI_CAP_MSIX; +} + +/* Is MSI-X enabled? */ +int msix_enabled(PCIDevice *dev) +{ + return (dev->cap_present & QEMU_PCI_CAP_MSIX) && + (dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] & + MSIX_ENABLE_MASK); +} + +/* Send an MSI-X message */ +void msix_notify(PCIDevice *dev, unsigned vector) +{ + MSIMessage msg; + + if (vector >= dev->msix_entries_nr || !dev->msix_entry_used[vector]) + return; + if (msix_is_masked(dev, vector)) { + msix_set_pending(dev, vector); + return; + } + + msg = msix_get_message(dev, vector); + + stl_le_phys(msg.address, msg.data); +} + +void msix_reset(PCIDevice *dev) +{ + if (!msix_present(dev)) { + return; + } + msix_clear_all_vectors(dev); + dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] &= + ~dev->wmask[dev->msix_cap + MSIX_CONTROL_OFFSET]; + memset(dev->msix_table, 0, dev->msix_entries_nr * PCI_MSIX_ENTRY_SIZE); + memset(dev->msix_pba, 0, QEMU_ALIGN_UP(dev->msix_entries_nr, 64) / 8); + msix_mask_all(dev, dev->msix_entries_nr); +} + +/* PCI spec suggests that devices make it possible for software to configure + * less vectors than supported by the device, but does not specify a standard + * mechanism for devices to do so. + * + * We support this by asking devices to declare vectors software is going to + * actually use, and checking this on the notification path. Devices that + * don't want to follow the spec suggestion can declare all vectors as used. */ + +/* Mark vector as used. */ +int msix_vector_use(PCIDevice *dev, unsigned vector) +{ + if (vector >= dev->msix_entries_nr) + return -EINVAL; + dev->msix_entry_used[vector]++; + return 0; +} + +/* Mark vector as unused. */ +void msix_vector_unuse(PCIDevice *dev, unsigned vector) +{ + if (vector >= dev->msix_entries_nr || !dev->msix_entry_used[vector]) { + return; + } + if (--dev->msix_entry_used[vector]) { + return; + } + msix_clr_pending(dev, vector); +} + +void msix_unuse_all_vectors(PCIDevice *dev) +{ + if (!msix_present(dev)) { + return; + } + msix_free_irq_entries(dev); +} + +unsigned int msix_nr_vectors_allocated(const PCIDevice *dev) +{ + return dev->msix_entries_nr; +} + +static int msix_set_notifier_for_vector(PCIDevice *dev, unsigned int vector) +{ + MSIMessage msg; + + if (msix_is_masked(dev, vector)) { + return 0; + } + msg = msix_get_message(dev, vector); + return dev->msix_vector_use_notifier(dev, vector, msg); +} + +static void msix_unset_notifier_for_vector(PCIDevice *dev, unsigned int vector) +{ + if (msix_is_masked(dev, vector)) { + return; + } + dev->msix_vector_release_notifier(dev, vector); +} + +int msix_set_vector_notifiers(PCIDevice *dev, + MSIVectorUseNotifier use_notifier, + MSIVectorReleaseNotifier release_notifier) +{ + int vector, ret; + + assert(use_notifier && release_notifier); + + dev->msix_vector_use_notifier = use_notifier; + dev->msix_vector_release_notifier = release_notifier; + + if ((dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] & + (MSIX_ENABLE_MASK | MSIX_MASKALL_MASK)) == MSIX_ENABLE_MASK) { + for (vector = 0; vector < dev->msix_entries_nr; vector++) { + ret = msix_set_notifier_for_vector(dev, vector); + if (ret < 0) { + goto undo; + } + } + } + return 0; + +undo: + while (--vector >= 0) { + msix_unset_notifier_for_vector(dev, vector); + } + dev->msix_vector_use_notifier = NULL; + dev->msix_vector_release_notifier = NULL; + return ret; +} + +void msix_unset_vector_notifiers(PCIDevice *dev) +{ + int vector; + + assert(dev->msix_vector_use_notifier && + dev->msix_vector_release_notifier); + + if ((dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] & + (MSIX_ENABLE_MASK | MSIX_MASKALL_MASK)) == MSIX_ENABLE_MASK) { + for (vector = 0; vector < dev->msix_entries_nr; vector++) { + msix_unset_notifier_for_vector(dev, vector); + } + } + dev->msix_vector_use_notifier = NULL; + dev->msix_vector_release_notifier = NULL; +} diff --git a/hw/pci/msix.h b/hw/pci/msix.h new file mode 100644 index 0000000000..15211cb592 --- /dev/null +++ b/hw/pci/msix.h @@ -0,0 +1,41 @@ +#ifndef QEMU_MSIX_H +#define QEMU_MSIX_H + +#include "qemu-common.h" +#include "pci.h" + +void msix_set_message(PCIDevice *dev, int vector, MSIMessage msg); +int msix_init(PCIDevice *dev, unsigned short nentries, + MemoryRegion *table_bar, uint8_t table_bar_nr, + unsigned table_offset, MemoryRegion *pba_bar, + uint8_t pba_bar_nr, unsigned pba_offset, uint8_t cap_pos); +int msix_init_exclusive_bar(PCIDevice *dev, unsigned short nentries, + uint8_t bar_nr); + +void msix_write_config(PCIDevice *dev, uint32_t address, uint32_t val, int len); + +void msix_uninit(PCIDevice *dev, MemoryRegion *table_bar, + MemoryRegion *pba_bar); +void msix_uninit_exclusive_bar(PCIDevice *dev); + +unsigned int msix_nr_vectors_allocated(const PCIDevice *dev); + +void msix_save(PCIDevice *dev, QEMUFile *f); +void msix_load(PCIDevice *dev, QEMUFile *f); + +int msix_enabled(PCIDevice *dev); +int msix_present(PCIDevice *dev); + +int msix_vector_use(PCIDevice *dev, unsigned vector); +void msix_vector_unuse(PCIDevice *dev, unsigned vector); +void msix_unuse_all_vectors(PCIDevice *dev); + +void msix_notify(PCIDevice *dev, unsigned vector); + +void msix_reset(PCIDevice *dev); + +int msix_set_vector_notifiers(PCIDevice *dev, + MSIVectorUseNotifier use_notifier, + MSIVectorReleaseNotifier release_notifier); +void msix_unset_vector_notifiers(PCIDevice *dev); +#endif diff --git a/hw/pci/pci-hotplug.c b/hw/pci/pci-hotplug.c new file mode 100644 index 0000000000..3bcfdcc1a6 --- /dev/null +++ b/hw/pci/pci-hotplug.c @@ -0,0 +1,292 @@ +/* + * QEMU PCI hotplug support + * + * Copyright (c) 2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "hw.h" +#include "boards.h" +#include "pci.h" +#include "net.h" +#include "pc.h" +#include "monitor.h" +#include "scsi.h" +#include "virtio-blk.h" +#include "qemu-config.h" +#include "blockdev.h" +#include "error.h" + +#if defined(TARGET_I386) +static PCIDevice *qemu_pci_hot_add_nic(Monitor *mon, + const char *devaddr, + const char *opts_str) +{ + Error *local_err = NULL; + QemuOpts *opts; + PCIBus *bus; + int ret, devfn; + + bus = pci_get_bus_devfn(&devfn, devaddr); + if (!bus) { + monitor_printf(mon, "Invalid PCI device address %s\n", devaddr); + return NULL; + } + if (!((BusState*)bus)->allow_hotplug) { + monitor_printf(mon, "PCI bus doesn't support hotplug\n"); + return NULL; + } + + opts = qemu_opts_parse(qemu_find_opts("net"), opts_str ? opts_str : "", 0); + if (!opts) { + return NULL; + } + + qemu_opt_set(opts, "type", "nic"); + + ret = net_client_init(opts, 0, &local_err); + if (error_is_set(&local_err)) { + qerror_report_err(local_err); + error_free(local_err); + return NULL; + } + if (nd_table[ret].devaddr) { + monitor_printf(mon, "Parameter addr not supported\n"); + return NULL; + } + return pci_nic_init(&nd_table[ret], "rtl8139", devaddr); +} + +static int scsi_hot_add(Monitor *mon, DeviceState *adapter, + DriveInfo *dinfo, int printinfo) +{ + SCSIBus *scsibus; + SCSIDevice *scsidev; + + scsibus = (SCSIBus *) + object_dynamic_cast(OBJECT(QLIST_FIRST(&adapter->child_bus)), + TYPE_SCSI_BUS); + if (!scsibus) { + error_report("Device is not a SCSI adapter"); + return -1; + } + + /* + * drive_init() tries to find a default for dinfo->unit. Doesn't + * work at all for hotplug though as we assign the device to a + * specific bus instead of the first bus with spare scsi ids. + * + * Ditch the calculated value and reload from option string (if + * specified). + */ + dinfo->unit = qemu_opt_get_number(dinfo->opts, "unit", -1); + dinfo->bus = scsibus->busnr; + scsidev = scsi_bus_legacy_add_drive(scsibus, dinfo->bdrv, dinfo->unit, + false, -1); + if (!scsidev) { + return -1; + } + dinfo->unit = scsidev->id; + + if (printinfo) + monitor_printf(mon, "OK bus %d, unit %d\n", + scsibus->busnr, scsidev->id); + return 0; +} + +int pci_drive_hot_add(Monitor *mon, const QDict *qdict, DriveInfo *dinfo) +{ + int dom, pci_bus; + unsigned slot; + PCIDevice *dev; + const char *pci_addr = qdict_get_str(qdict, "pci_addr"); + + switch (dinfo->type) { + case IF_SCSI: + if (pci_read_devaddr(mon, pci_addr, &dom, &pci_bus, &slot)) { + goto err; + } + dev = pci_find_device(pci_find_root_bus(dom), pci_bus, + PCI_DEVFN(slot, 0)); + if (!dev) { + monitor_printf(mon, "no pci device with address %s\n", pci_addr); + goto err; + } + if (scsi_hot_add(mon, &dev->qdev, dinfo, 1) != 0) { + goto err; + } + break; + default: + monitor_printf(mon, "Can't hot-add drive to type %d\n", dinfo->type); + goto err; + } + + return 0; +err: + return -1; +} + +static PCIDevice *qemu_pci_hot_add_storage(Monitor *mon, + const char *devaddr, + const char *opts) +{ + PCIDevice *dev; + DriveInfo *dinfo = NULL; + int type = -1; + char buf[128]; + PCIBus *bus; + int devfn; + + if (get_param_value(buf, sizeof(buf), "if", opts)) { + if (!strcmp(buf, "scsi")) + type = IF_SCSI; + else if (!strcmp(buf, "virtio")) { + type = IF_VIRTIO; + } else { + monitor_printf(mon, "type %s not a hotpluggable PCI device.\n", buf); + return NULL; + } + } else { + monitor_printf(mon, "no if= specified\n"); + return NULL; + } + + if (get_param_value(buf, sizeof(buf), "file", opts)) { + dinfo = add_init_drive(opts); + if (!dinfo) + return NULL; + if (dinfo->devaddr) { + monitor_printf(mon, "Parameter addr not supported\n"); + return NULL; + } + } else { + dinfo = NULL; + } + + bus = pci_get_bus_devfn(&devfn, devaddr); + if (!bus) { + monitor_printf(mon, "Invalid PCI device address %s\n", devaddr); + return NULL; + } + if (!((BusState*)bus)->allow_hotplug) { + monitor_printf(mon, "PCI bus doesn't support hotplug\n"); + return NULL; + } + + switch (type) { + case IF_SCSI: + dev = pci_create(bus, devfn, "lsi53c895a"); + if (qdev_init(&dev->qdev) < 0) + dev = NULL; + if (dev && dinfo) { + if (scsi_hot_add(mon, &dev->qdev, dinfo, 0) != 0) { + qdev_unplug(&dev->qdev, NULL); + dev = NULL; + } + } + break; + case IF_VIRTIO: + if (!dinfo) { + monitor_printf(mon, "virtio requires a backing file/device.\n"); + return NULL; + } + dev = pci_create(bus, devfn, "virtio-blk-pci"); + if (qdev_prop_set_drive(&dev->qdev, "drive", dinfo->bdrv) < 0) { + qdev_free(&dev->qdev); + dev = NULL; + break; + } + if (qdev_init(&dev->qdev) < 0) + dev = NULL; + break; + default: + dev = NULL; + } + return dev; +} + +void pci_device_hot_add(Monitor *mon, const QDict *qdict) +{ + PCIDevice *dev = NULL; + const char *pci_addr = qdict_get_str(qdict, "pci_addr"); + const char *type = qdict_get_str(qdict, "type"); + const char *opts = qdict_get_try_str(qdict, "opts"); + + /* strip legacy tag */ + if (!strncmp(pci_addr, "pci_addr=", 9)) { + pci_addr += 9; + } + + if (!opts) { + opts = ""; + } + + if (!strcmp(pci_addr, "auto")) + pci_addr = NULL; + + if (strcmp(type, "nic") == 0) { + dev = qemu_pci_hot_add_nic(mon, pci_addr, opts); + } else if (strcmp(type, "storage") == 0) { + dev = qemu_pci_hot_add_storage(mon, pci_addr, opts); + } else { + monitor_printf(mon, "invalid type: %s\n", type); + } + + if (dev) { + monitor_printf(mon, "OK domain %d, bus %d, slot %d, function %d\n", + pci_find_domain(dev->bus), + pci_bus_num(dev->bus), PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn)); + } else + monitor_printf(mon, "failed to add %s\n", opts); +} +#endif + +static int pci_device_hot_remove(Monitor *mon, const char *pci_addr) +{ + PCIDevice *d; + int dom, bus; + unsigned slot; + Error *local_err = NULL; + + if (pci_read_devaddr(mon, pci_addr, &dom, &bus, &slot)) { + return -1; + } + + d = pci_find_device(pci_find_root_bus(dom), bus, PCI_DEVFN(slot, 0)); + if (!d) { + monitor_printf(mon, "slot %d empty\n", slot); + return -1; + } + + qdev_unplug(&d->qdev, &local_err); + if (error_is_set(&local_err)) { + monitor_printf(mon, "%s\n", error_get_pretty(local_err)); + error_free(local_err); + return -1; + } + + return 0; +} + +void do_pci_device_hot_remove(Monitor *mon, const QDict *qdict) +{ + pci_device_hot_remove(mon, qdict_get_str(qdict, "pci_addr")); +} diff --git a/hw/pci/pci-stub.c b/hw/pci/pci-stub.c new file mode 100644 index 0000000000..134c4484b6 --- /dev/null +++ b/hw/pci/pci-stub.c @@ -0,0 +1,47 @@ +/* + * PCI stubs for platforms that don't support pci bus. + * + * Copyright (c) 2010 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "sysemu.h" +#include "monitor.h" +#include "pci.h" +#include "qmp-commands.h" + +PciInfoList *qmp_query_pci(Error **errp) +{ + error_set(errp, QERR_UNSUPPORTED); + return NULL; +} + +static void pci_error_message(Monitor *mon) +{ + monitor_printf(mon, "PCI devices not supported\n"); +} + +int do_pcie_aer_inject_error(Monitor *mon, + const QDict *qdict, QObject **ret_data) +{ + pci_error_message(mon); + return -ENOSYS; +} + +void pcie_aer_inject_error_print(Monitor *mon, const QObject *data) +{ + pci_error_message(mon); +} diff --git a/hw/pci/pci.c b/hw/pci/pci.c new file mode 100644 index 0000000000..97a0cd77c1 --- /dev/null +++ b/hw/pci/pci.c @@ -0,0 +1,2168 @@ +/* + * QEMU PCI bus manager + * + * Copyright (c) 2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "hw.h" +#include "pci.h" +#include "pci_bridge.h" +#include "pci_internals.h" +#include "monitor.h" +#include "net.h" +#include "sysemu.h" +#include "loader.h" +#include "range.h" +#include "qmp-commands.h" +#include "msi.h" +#include "msix.h" +#include "exec-memory.h" + +//#define DEBUG_PCI +#ifdef DEBUG_PCI +# define PCI_DPRINTF(format, ...) printf(format, ## __VA_ARGS__) +#else +# define PCI_DPRINTF(format, ...) do { } while (0) +#endif + +static void pcibus_dev_print(Monitor *mon, DeviceState *dev, int indent); +static char *pcibus_get_dev_path(DeviceState *dev); +static char *pcibus_get_fw_dev_path(DeviceState *dev); +static int pcibus_reset(BusState *qbus); + +static Property pci_props[] = { + DEFINE_PROP_PCI_DEVFN("addr", PCIDevice, devfn, -1), + DEFINE_PROP_STRING("romfile", PCIDevice, romfile), + DEFINE_PROP_UINT32("rombar", PCIDevice, rom_bar, 1), + DEFINE_PROP_BIT("multifunction", PCIDevice, cap_present, + QEMU_PCI_CAP_MULTIFUNCTION_BITNR, false), + DEFINE_PROP_BIT("command_serr_enable", PCIDevice, cap_present, + QEMU_PCI_CAP_SERR_BITNR, true), + DEFINE_PROP_END_OF_LIST() +}; + +static void pci_bus_class_init(ObjectClass *klass, void *data) +{ + BusClass *k = BUS_CLASS(klass); + + k->print_dev = pcibus_dev_print; + k->get_dev_path = pcibus_get_dev_path; + k->get_fw_dev_path = pcibus_get_fw_dev_path; + k->reset = pcibus_reset; +} + +static const TypeInfo pci_bus_info = { + .name = TYPE_PCI_BUS, + .parent = TYPE_BUS, + .instance_size = sizeof(PCIBus), + .class_init = pci_bus_class_init, +}; + +static PCIBus *pci_find_bus_nr(PCIBus *bus, int bus_num); +static void pci_update_mappings(PCIDevice *d); +static void pci_set_irq(void *opaque, int irq_num, int level); +static int pci_add_option_rom(PCIDevice *pdev, bool is_default_rom); +static void pci_del_option_rom(PCIDevice *pdev); + +static uint16_t pci_default_sub_vendor_id = PCI_SUBVENDOR_ID_REDHAT_QUMRANET; +static uint16_t pci_default_sub_device_id = PCI_SUBDEVICE_ID_QEMU; + +struct PCIHostBus { + int domain; + struct PCIBus *bus; + QLIST_ENTRY(PCIHostBus) next; +}; +static QLIST_HEAD(, PCIHostBus) host_buses; + +static const VMStateDescription vmstate_pcibus = { + .name = "PCIBUS", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField []) { + VMSTATE_INT32_EQUAL(nirq, PCIBus), + VMSTATE_VARRAY_INT32(irq_count, PCIBus, nirq, 0, vmstate_info_int32, int32_t), + VMSTATE_END_OF_LIST() + } +}; +static int pci_bar(PCIDevice *d, int reg) +{ + uint8_t type; + + if (reg != PCI_ROM_SLOT) + return PCI_BASE_ADDRESS_0 + reg * 4; + + type = d->config[PCI_HEADER_TYPE] & ~PCI_HEADER_TYPE_MULTI_FUNCTION; + return type == PCI_HEADER_TYPE_BRIDGE ? PCI_ROM_ADDRESS1 : PCI_ROM_ADDRESS; +} + +static inline int pci_irq_state(PCIDevice *d, int irq_num) +{ + return (d->irq_state >> irq_num) & 0x1; +} + +static inline void pci_set_irq_state(PCIDevice *d, int irq_num, int level) +{ + d->irq_state &= ~(0x1 << irq_num); + d->irq_state |= level << irq_num; +} + +static void pci_change_irq_level(PCIDevice *pci_dev, int irq_num, int change) +{ + PCIBus *bus; + for (;;) { + bus = pci_dev->bus; + irq_num = bus->map_irq(pci_dev, irq_num); + if (bus->set_irq) + break; + pci_dev = bus->parent_dev; + } + bus->irq_count[irq_num] += change; + bus->set_irq(bus->irq_opaque, irq_num, bus->irq_count[irq_num] != 0); +} + +int pci_bus_get_irq_level(PCIBus *bus, int irq_num) +{ + assert(irq_num >= 0); + assert(irq_num < bus->nirq); + return !!bus->irq_count[irq_num]; +} + +/* Update interrupt status bit in config space on interrupt + * state change. */ +static void pci_update_irq_status(PCIDevice *dev) +{ + if (dev->irq_state) { + dev->config[PCI_STATUS] |= PCI_STATUS_INTERRUPT; + } else { + dev->config[PCI_STATUS] &= ~PCI_STATUS_INTERRUPT; + } +} + +void pci_device_deassert_intx(PCIDevice *dev) +{ + int i; + for (i = 0; i < PCI_NUM_PINS; ++i) { + qemu_set_irq(dev->irq[i], 0); + } +} + +/* + * This function is called on #RST and FLR. + * FLR if PCI_EXP_DEVCTL_BCR_FLR is set + */ +void pci_device_reset(PCIDevice *dev) +{ + int r; + + qdev_reset_all(&dev->qdev); + + dev->irq_state = 0; + pci_update_irq_status(dev); + pci_device_deassert_intx(dev); + /* Clear all writable bits */ + pci_word_test_and_clear_mask(dev->config + PCI_COMMAND, + pci_get_word(dev->wmask + PCI_COMMAND) | + pci_get_word(dev->w1cmask + PCI_COMMAND)); + pci_word_test_and_clear_mask(dev->config + PCI_STATUS, + pci_get_word(dev->wmask + PCI_STATUS) | + pci_get_word(dev->w1cmask + PCI_STATUS)); + dev->config[PCI_CACHE_LINE_SIZE] = 0x0; + dev->config[PCI_INTERRUPT_LINE] = 0x0; + for (r = 0; r < PCI_NUM_REGIONS; ++r) { + PCIIORegion *region = &dev->io_regions[r]; + if (!region->size) { + continue; + } + + if (!(region->type & PCI_BASE_ADDRESS_SPACE_IO) && + region->type & PCI_BASE_ADDRESS_MEM_TYPE_64) { + pci_set_quad(dev->config + pci_bar(dev, r), region->type); + } else { + pci_set_long(dev->config + pci_bar(dev, r), region->type); + } + } + pci_update_mappings(dev); + + msi_reset(dev); + msix_reset(dev); +} + +/* + * Trigger pci bus reset under a given bus. + * To be called on RST# assert. + */ +void pci_bus_reset(PCIBus *bus) +{ + int i; + + for (i = 0; i < bus->nirq; i++) { + bus->irq_count[i] = 0; + } + for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) { + if (bus->devices[i]) { + pci_device_reset(bus->devices[i]); + } + } +} + +static int pcibus_reset(BusState *qbus) +{ + pci_bus_reset(DO_UPCAST(PCIBus, qbus, qbus)); + + /* topology traverse is done by pci_bus_reset(). + Tell qbus/qdev walker not to traverse the tree */ + return 1; +} + +static void pci_host_bus_register(int domain, PCIBus *bus) +{ + struct PCIHostBus *host; + host = g_malloc0(sizeof(*host)); + host->domain = domain; + host->bus = bus; + QLIST_INSERT_HEAD(&host_buses, host, next); +} + +PCIBus *pci_find_root_bus(int domain) +{ + struct PCIHostBus *host; + + QLIST_FOREACH(host, &host_buses, next) { + if (host->domain == domain) { + return host->bus; + } + } + + return NULL; +} + +int pci_find_domain(const PCIBus *bus) +{ + PCIDevice *d; + struct PCIHostBus *host; + + /* obtain root bus */ + while ((d = bus->parent_dev) != NULL) { + bus = d->bus; + } + + QLIST_FOREACH(host, &host_buses, next) { + if (host->bus == bus) { + return host->domain; + } + } + + abort(); /* should not be reached */ + return -1; +} + +void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent, + const char *name, + MemoryRegion *address_space_mem, + MemoryRegion *address_space_io, + uint8_t devfn_min) +{ + qbus_create_inplace(&bus->qbus, TYPE_PCI_BUS, parent, name); + assert(PCI_FUNC(devfn_min) == 0); + bus->devfn_min = devfn_min; + bus->address_space_mem = address_space_mem; + bus->address_space_io = address_space_io; + + /* host bridge */ + QLIST_INIT(&bus->child); + pci_host_bus_register(0, bus); /* for now only pci domain 0 is supported */ + + vmstate_register(NULL, -1, &vmstate_pcibus, bus); +} + +PCIBus *pci_bus_new(DeviceState *parent, const char *name, + MemoryRegion *address_space_mem, + MemoryRegion *address_space_io, + uint8_t devfn_min) +{ + PCIBus *bus; + + bus = g_malloc0(sizeof(*bus)); + pci_bus_new_inplace(bus, parent, name, address_space_mem, + address_space_io, devfn_min); + OBJECT(bus)->free = g_free; + return bus; +} + +void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, + void *irq_opaque, int nirq) +{ + bus->set_irq = set_irq; + bus->map_irq = map_irq; + bus->irq_opaque = irq_opaque; + bus->nirq = nirq; + bus->irq_count = g_malloc0(nirq * sizeof(bus->irq_count[0])); +} + +void pci_bus_hotplug(PCIBus *bus, pci_hotplug_fn hotplug, DeviceState *qdev) +{ + bus->qbus.allow_hotplug = 1; + bus->hotplug = hotplug; + bus->hotplug_qdev = qdev; +} + +PCIBus *pci_register_bus(DeviceState *parent, const char *name, + pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, + void *irq_opaque, + MemoryRegion *address_space_mem, + MemoryRegion *address_space_io, + uint8_t devfn_min, int nirq) +{ + PCIBus *bus; + + bus = pci_bus_new(parent, name, address_space_mem, + address_space_io, devfn_min); + pci_bus_irqs(bus, set_irq, map_irq, irq_opaque, nirq); + return bus; +} + +int pci_bus_num(PCIBus *s) +{ + if (!s->parent_dev) + return 0; /* pci host bridge */ + return s->parent_dev->config[PCI_SECONDARY_BUS]; +} + +static int get_pci_config_device(QEMUFile *f, void *pv, size_t size) +{ + PCIDevice *s = container_of(pv, PCIDevice, config); + uint8_t *config; + int i; + + assert(size == pci_config_size(s)); + config = g_malloc(size); + + qemu_get_buffer(f, config, size); + for (i = 0; i < size; ++i) { + if ((config[i] ^ s->config[i]) & + s->cmask[i] & ~s->wmask[i] & ~s->w1cmask[i]) { + g_free(config); + return -EINVAL; + } + } + memcpy(s->config, config, size); + + pci_update_mappings(s); + + memory_region_set_enabled(&s->bus_master_enable_region, + pci_get_word(s->config + PCI_COMMAND) + & PCI_COMMAND_MASTER); + + g_free(config); + return 0; +} + +/* just put buffer */ +static void put_pci_config_device(QEMUFile *f, void *pv, size_t size) +{ + const uint8_t **v = pv; + assert(size == pci_config_size(container_of(pv, PCIDevice, config))); + qemu_put_buffer(f, *v, size); +} + +static VMStateInfo vmstate_info_pci_config = { + .name = "pci config", + .get = get_pci_config_device, + .put = put_pci_config_device, +}; + +static int get_pci_irq_state(QEMUFile *f, void *pv, size_t size) +{ + PCIDevice *s = container_of(pv, PCIDevice, irq_state); + uint32_t irq_state[PCI_NUM_PINS]; + int i; + for (i = 0; i < PCI_NUM_PINS; ++i) { + irq_state[i] = qemu_get_be32(f); + if (irq_state[i] != 0x1 && irq_state[i] != 0) { + fprintf(stderr, "irq state %d: must be 0 or 1.\n", + irq_state[i]); + return -EINVAL; + } + } + + for (i = 0; i < PCI_NUM_PINS; ++i) { + pci_set_irq_state(s, i, irq_state[i]); + } + + return 0; +} + +static void put_pci_irq_state(QEMUFile *f, void *pv, size_t size) +{ + int i; + PCIDevice *s = container_of(pv, PCIDevice, irq_state); + + for (i = 0; i < PCI_NUM_PINS; ++i) { + qemu_put_be32(f, pci_irq_state(s, i)); + } +} + +static VMStateInfo vmstate_info_pci_irq_state = { + .name = "pci irq state", + .get = get_pci_irq_state, + .put = put_pci_irq_state, +}; + +const VMStateDescription vmstate_pci_device = { + .name = "PCIDevice", + .version_id = 2, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField []) { + VMSTATE_INT32_LE(version_id, PCIDevice), + VMSTATE_BUFFER_UNSAFE_INFO(config, PCIDevice, 0, + vmstate_info_pci_config, + PCI_CONFIG_SPACE_SIZE), + VMSTATE_BUFFER_UNSAFE_INFO(irq_state, PCIDevice, 2, + vmstate_info_pci_irq_state, + PCI_NUM_PINS * sizeof(int32_t)), + VMSTATE_END_OF_LIST() + } +}; + +const VMStateDescription vmstate_pcie_device = { + .name = "PCIEDevice", + .version_id = 2, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField []) { + VMSTATE_INT32_LE(version_id, PCIDevice), + VMSTATE_BUFFER_UNSAFE_INFO(config, PCIDevice, 0, + vmstate_info_pci_config, + PCIE_CONFIG_SPACE_SIZE), + VMSTATE_BUFFER_UNSAFE_INFO(irq_state, PCIDevice, 2, + vmstate_info_pci_irq_state, + PCI_NUM_PINS * sizeof(int32_t)), + VMSTATE_END_OF_LIST() + } +}; + +static inline const VMStateDescription *pci_get_vmstate(PCIDevice *s) +{ + return pci_is_express(s) ? &vmstate_pcie_device : &vmstate_pci_device; +} + +void pci_device_save(PCIDevice *s, QEMUFile *f) +{ + /* Clear interrupt status bit: it is implicit + * in irq_state which we are saving. + * This makes us compatible with old devices + * which never set or clear this bit. */ + s->config[PCI_STATUS] &= ~PCI_STATUS_INTERRUPT; + vmstate_save_state(f, pci_get_vmstate(s), s); + /* Restore the interrupt status bit. */ + pci_update_irq_status(s); +} + +int pci_device_load(PCIDevice *s, QEMUFile *f) +{ + int ret; + ret = vmstate_load_state(f, pci_get_vmstate(s), s, s->version_id); + /* Restore the interrupt status bit. */ + pci_update_irq_status(s); + return ret; +} + +static void pci_set_default_subsystem_id(PCIDevice *pci_dev) +{ + pci_set_word(pci_dev->config + PCI_SUBSYSTEM_VENDOR_ID, + pci_default_sub_vendor_id); + pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID, + pci_default_sub_device_id); +} + +/* + * Parse [[:]:], return -1 on error if funcp == NULL + * [[:]:]., return -1 on error + */ +static int pci_parse_devaddr(const char *addr, int *domp, int *busp, + unsigned int *slotp, unsigned int *funcp) +{ + const char *p; + char *e; + unsigned long val; + unsigned long dom = 0, bus = 0; + unsigned int slot = 0; + unsigned int func = 0; + + p = addr; + val = strtoul(p, &e, 16); + if (e == p) + return -1; + if (*e == ':') { + bus = val; + p = e + 1; + val = strtoul(p, &e, 16); + if (e == p) + return -1; + if (*e == ':') { + dom = bus; + bus = val; + p = e + 1; + val = strtoul(p, &e, 16); + if (e == p) + return -1; + } + } + + slot = val; + + if (funcp != NULL) { + if (*e != '.') + return -1; + + p = e + 1; + val = strtoul(p, &e, 16); + if (e == p) + return -1; + + func = val; + } + + /* if funcp == NULL func is 0 */ + if (dom > 0xffff || bus > 0xff || slot > 0x1f || func > 7) + return -1; + + if (*e) + return -1; + + *domp = dom; + *busp = bus; + *slotp = slot; + if (funcp != NULL) + *funcp = func; + return 0; +} + +int pci_read_devaddr(Monitor *mon, const char *addr, int *domp, int *busp, + unsigned *slotp) +{ + /* strip legacy tag */ + if (!strncmp(addr, "pci_addr=", 9)) { + addr += 9; + } + if (pci_parse_devaddr(addr, domp, busp, slotp, NULL)) { + monitor_printf(mon, "Invalid pci address\n"); + return -1; + } + return 0; +} + +PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr) +{ + int dom, bus; + unsigned slot; + + if (!devaddr) { + *devfnp = -1; + return pci_find_bus_nr(pci_find_root_bus(0), 0); + } + + if (pci_parse_devaddr(devaddr, &dom, &bus, &slot, NULL) < 0) { + return NULL; + } + + *devfnp = PCI_DEVFN(slot, 0); + return pci_find_bus_nr(pci_find_root_bus(dom), bus); +} + +static void pci_init_cmask(PCIDevice *dev) +{ + pci_set_word(dev->cmask + PCI_VENDOR_ID, 0xffff); + pci_set_word(dev->cmask + PCI_DEVICE_ID, 0xffff); + dev->cmask[PCI_STATUS] = PCI_STATUS_CAP_LIST; + dev->cmask[PCI_REVISION_ID] = 0xff; + dev->cmask[PCI_CLASS_PROG] = 0xff; + pci_set_word(dev->cmask + PCI_CLASS_DEVICE, 0xffff); + dev->cmask[PCI_HEADER_TYPE] = 0xff; + dev->cmask[PCI_CAPABILITY_LIST] = 0xff; +} + +static void pci_init_wmask(PCIDevice *dev) +{ + int config_size = pci_config_size(dev); + + dev->wmask[PCI_CACHE_LINE_SIZE] = 0xff; + dev->wmask[PCI_INTERRUPT_LINE] = 0xff; + pci_set_word(dev->wmask + PCI_COMMAND, + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | + PCI_COMMAND_INTX_DISABLE); + if (dev->cap_present & QEMU_PCI_CAP_SERR) { + pci_word_test_and_set_mask(dev->wmask + PCI_COMMAND, PCI_COMMAND_SERR); + } + + memset(dev->wmask + PCI_CONFIG_HEADER_SIZE, 0xff, + config_size - PCI_CONFIG_HEADER_SIZE); +} + +static void pci_init_w1cmask(PCIDevice *dev) +{ + /* + * Note: It's okay to set w1cmask even for readonly bits as + * long as their value is hardwired to 0. + */ + pci_set_word(dev->w1cmask + PCI_STATUS, + PCI_STATUS_PARITY | PCI_STATUS_SIG_TARGET_ABORT | + PCI_STATUS_REC_TARGET_ABORT | PCI_STATUS_REC_MASTER_ABORT | + PCI_STATUS_SIG_SYSTEM_ERROR | PCI_STATUS_DETECTED_PARITY); +} + +static void pci_init_mask_bridge(PCIDevice *d) +{ + /* PCI_PRIMARY_BUS, PCI_SECONDARY_BUS, PCI_SUBORDINATE_BUS and + PCI_SEC_LETENCY_TIMER */ + memset(d->wmask + PCI_PRIMARY_BUS, 0xff, 4); + + /* base and limit */ + d->wmask[PCI_IO_BASE] = PCI_IO_RANGE_MASK & 0xff; + d->wmask[PCI_IO_LIMIT] = PCI_IO_RANGE_MASK & 0xff; + pci_set_word(d->wmask + PCI_MEMORY_BASE, + PCI_MEMORY_RANGE_MASK & 0xffff); + pci_set_word(d->wmask + PCI_MEMORY_LIMIT, + PCI_MEMORY_RANGE_MASK & 0xffff); + pci_set_word(d->wmask + PCI_PREF_MEMORY_BASE, + PCI_PREF_RANGE_MASK & 0xffff); + pci_set_word(d->wmask + PCI_PREF_MEMORY_LIMIT, + PCI_PREF_RANGE_MASK & 0xffff); + + /* PCI_PREF_BASE_UPPER32 and PCI_PREF_LIMIT_UPPER32 */ + memset(d->wmask + PCI_PREF_BASE_UPPER32, 0xff, 8); + + /* Supported memory and i/o types */ + d->config[PCI_IO_BASE] |= PCI_IO_RANGE_TYPE_16; + d->config[PCI_IO_LIMIT] |= PCI_IO_RANGE_TYPE_16; + pci_word_test_and_set_mask(d->config + PCI_PREF_MEMORY_BASE, + PCI_PREF_RANGE_TYPE_64); + pci_word_test_and_set_mask(d->config + PCI_PREF_MEMORY_LIMIT, + PCI_PREF_RANGE_TYPE_64); + +/* TODO: add this define to pci_regs.h in linux and then in qemu. */ +#define PCI_BRIDGE_CTL_VGA_16BIT 0x10 /* VGA 16-bit decode */ +#define PCI_BRIDGE_CTL_DISCARD 0x100 /* Primary discard timer */ +#define PCI_BRIDGE_CTL_SEC_DISCARD 0x200 /* Secondary discard timer */ +#define PCI_BRIDGE_CTL_DISCARD_STATUS 0x400 /* Discard timer status */ +#define PCI_BRIDGE_CTL_DISCARD_SERR 0x800 /* Discard timer SERR# enable */ + pci_set_word(d->wmask + PCI_BRIDGE_CONTROL, + PCI_BRIDGE_CTL_PARITY | + PCI_BRIDGE_CTL_SERR | + PCI_BRIDGE_CTL_ISA | + PCI_BRIDGE_CTL_VGA | + PCI_BRIDGE_CTL_VGA_16BIT | + PCI_BRIDGE_CTL_MASTER_ABORT | + PCI_BRIDGE_CTL_BUS_RESET | + PCI_BRIDGE_CTL_FAST_BACK | + PCI_BRIDGE_CTL_DISCARD | + PCI_BRIDGE_CTL_SEC_DISCARD | + PCI_BRIDGE_CTL_DISCARD_SERR); + /* Below does not do anything as we never set this bit, put here for + * completeness. */ + pci_set_word(d->w1cmask + PCI_BRIDGE_CONTROL, + PCI_BRIDGE_CTL_DISCARD_STATUS); + d->cmask[PCI_IO_BASE] |= PCI_IO_RANGE_TYPE_MASK; + d->cmask[PCI_IO_LIMIT] |= PCI_IO_RANGE_TYPE_MASK; + pci_word_test_and_set_mask(d->cmask + PCI_PREF_MEMORY_BASE, + PCI_PREF_RANGE_TYPE_MASK); + pci_word_test_and_set_mask(d->cmask + PCI_PREF_MEMORY_LIMIT, + PCI_PREF_RANGE_TYPE_MASK); +} + +static int pci_init_multifunction(PCIBus *bus, PCIDevice *dev) +{ + uint8_t slot = PCI_SLOT(dev->devfn); + uint8_t func; + + if (dev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { + dev->config[PCI_HEADER_TYPE] |= PCI_HEADER_TYPE_MULTI_FUNCTION; + } + + /* + * multifunction bit is interpreted in two ways as follows. + * - all functions must set the bit to 1. + * Example: Intel X53 + * - function 0 must set the bit, but the rest function (> 0) + * is allowed to leave the bit to 0. + * Example: PIIX3(also in qemu), PIIX4(also in qemu), ICH10, + * + * So OS (at least Linux) checks the bit of only function 0, + * and doesn't see the bit of function > 0. + * + * The below check allows both interpretation. + */ + if (PCI_FUNC(dev->devfn)) { + PCIDevice *f0 = bus->devices[PCI_DEVFN(slot, 0)]; + if (f0 && !(f0->cap_present & QEMU_PCI_CAP_MULTIFUNCTION)) { + /* function 0 should set multifunction bit */ + error_report("PCI: single function device can't be populated " + "in function %x.%x", slot, PCI_FUNC(dev->devfn)); + return -1; + } + return 0; + } + + if (dev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { + return 0; + } + /* function 0 indicates single function, so function > 0 must be NULL */ + for (func = 1; func < PCI_FUNC_MAX; ++func) { + if (bus->devices[PCI_DEVFN(slot, func)]) { + error_report("PCI: %x.0 indicates single function, " + "but %x.%x is already populated.", + slot, slot, func); + return -1; + } + } + return 0; +} + +static void pci_config_alloc(PCIDevice *pci_dev) +{ + int config_size = pci_config_size(pci_dev); + + pci_dev->config = g_malloc0(config_size); + pci_dev->cmask = g_malloc0(config_size); + pci_dev->wmask = g_malloc0(config_size); + pci_dev->w1cmask = g_malloc0(config_size); + pci_dev->used = g_malloc0(config_size); +} + +static void pci_config_free(PCIDevice *pci_dev) +{ + g_free(pci_dev->config); + g_free(pci_dev->cmask); + g_free(pci_dev->wmask); + g_free(pci_dev->w1cmask); + g_free(pci_dev->used); +} + +/* -1 for devfn means auto assign */ +static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, + const char *name, int devfn) +{ + PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(pci_dev); + PCIConfigReadFunc *config_read = pc->config_read; + PCIConfigWriteFunc *config_write = pc->config_write; + + if (devfn < 0) { + for(devfn = bus->devfn_min ; devfn < ARRAY_SIZE(bus->devices); + devfn += PCI_FUNC_MAX) { + if (!bus->devices[devfn]) + goto found; + } + error_report("PCI: no slot/function available for %s, all in use", name); + return NULL; + found: ; + } else if (bus->devices[devfn]) { + error_report("PCI: slot %d function %d not available for %s, in use by %s", + PCI_SLOT(devfn), PCI_FUNC(devfn), name, bus->devices[devfn]->name); + return NULL; + } + pci_dev->bus = bus; + if (bus->dma_context_fn) { + pci_dev->dma = bus->dma_context_fn(bus, bus->dma_context_opaque, devfn); + } else { + /* FIXME: Make dma_context_fn use MemoryRegions instead, so this path is + * taken unconditionally */ + /* FIXME: inherit memory region from bus creator */ + memory_region_init_alias(&pci_dev->bus_master_enable_region, "bus master", + get_system_memory(), 0, + memory_region_size(get_system_memory())); + memory_region_set_enabled(&pci_dev->bus_master_enable_region, false); + address_space_init(&pci_dev->bus_master_as, &pci_dev->bus_master_enable_region); + pci_dev->dma = g_new(DMAContext, 1); + dma_context_init(pci_dev->dma, &pci_dev->bus_master_as, NULL, NULL, NULL); + } + pci_dev->devfn = devfn; + pstrcpy(pci_dev->name, sizeof(pci_dev->name), name); + pci_dev->irq_state = 0; + pci_config_alloc(pci_dev); + + pci_config_set_vendor_id(pci_dev->config, pc->vendor_id); + pci_config_set_device_id(pci_dev->config, pc->device_id); + pci_config_set_revision(pci_dev->config, pc->revision); + pci_config_set_class(pci_dev->config, pc->class_id); + + if (!pc->is_bridge) { + if (pc->subsystem_vendor_id || pc->subsystem_id) { + pci_set_word(pci_dev->config + PCI_SUBSYSTEM_VENDOR_ID, + pc->subsystem_vendor_id); + pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID, + pc->subsystem_id); + } else { + pci_set_default_subsystem_id(pci_dev); + } + } else { + /* subsystem_vendor_id/subsystem_id are only for header type 0 */ + assert(!pc->subsystem_vendor_id); + assert(!pc->subsystem_id); + } + pci_init_cmask(pci_dev); + pci_init_wmask(pci_dev); + pci_init_w1cmask(pci_dev); + if (pc->is_bridge) { + pci_init_mask_bridge(pci_dev); + } + if (pci_init_multifunction(bus, pci_dev)) { + pci_config_free(pci_dev); + return NULL; + } + + if (!config_read) + config_read = pci_default_read_config; + if (!config_write) + config_write = pci_default_write_config; + pci_dev->config_read = config_read; + pci_dev->config_write = config_write; + bus->devices[devfn] = pci_dev; + pci_dev->irq = qemu_allocate_irqs(pci_set_irq, pci_dev, PCI_NUM_PINS); + pci_dev->version_id = 2; /* Current pci device vmstate version */ + return pci_dev; +} + +static void do_pci_unregister_device(PCIDevice *pci_dev) +{ + qemu_free_irqs(pci_dev->irq); + pci_dev->bus->devices[pci_dev->devfn] = NULL; + pci_config_free(pci_dev); + + if (!pci_dev->bus->dma_context_fn) { + address_space_destroy(&pci_dev->bus_master_as); + memory_region_destroy(&pci_dev->bus_master_enable_region); + g_free(pci_dev->dma); + pci_dev->dma = NULL; + } +} + +static void pci_unregister_io_regions(PCIDevice *pci_dev) +{ + PCIIORegion *r; + int i; + + for(i = 0; i < PCI_NUM_REGIONS; i++) { + r = &pci_dev->io_regions[i]; + if (!r->size || r->addr == PCI_BAR_UNMAPPED) + continue; + memory_region_del_subregion(r->address_space, r->memory); + } +} + +static int pci_unregister_device(DeviceState *dev) +{ + PCIDevice *pci_dev = PCI_DEVICE(dev); + PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(pci_dev); + + pci_unregister_io_regions(pci_dev); + pci_del_option_rom(pci_dev); + + if (pc->exit) { + pc->exit(pci_dev); + } + + do_pci_unregister_device(pci_dev); + return 0; +} + +void pci_register_bar(PCIDevice *pci_dev, int region_num, + uint8_t type, MemoryRegion *memory) +{ + PCIIORegion *r; + uint32_t addr; + uint64_t wmask; + pcibus_t size = memory_region_size(memory); + + assert(region_num >= 0); + assert(region_num < PCI_NUM_REGIONS); + if (size & (size-1)) { + fprintf(stderr, "ERROR: PCI region size must be pow2 " + "type=0x%x, size=0x%"FMT_PCIBUS"\n", type, size); + exit(1); + } + + r = &pci_dev->io_regions[region_num]; + r->addr = PCI_BAR_UNMAPPED; + r->size = size; + r->type = type; + r->memory = NULL; + + wmask = ~(size - 1); + addr = pci_bar(pci_dev, region_num); + if (region_num == PCI_ROM_SLOT) { + /* ROM enable bit is writable */ + wmask |= PCI_ROM_ADDRESS_ENABLE; + } + pci_set_long(pci_dev->config + addr, type); + if (!(r->type & PCI_BASE_ADDRESS_SPACE_IO) && + r->type & PCI_BASE_ADDRESS_MEM_TYPE_64) { + pci_set_quad(pci_dev->wmask + addr, wmask); + pci_set_quad(pci_dev->cmask + addr, ~0ULL); + } else { + pci_set_long(pci_dev->wmask + addr, wmask & 0xffffffff); + pci_set_long(pci_dev->cmask + addr, 0xffffffff); + } + pci_dev->io_regions[region_num].memory = memory; + pci_dev->io_regions[region_num].address_space + = type & PCI_BASE_ADDRESS_SPACE_IO + ? pci_dev->bus->address_space_io + : pci_dev->bus->address_space_mem; +} + +pcibus_t pci_get_bar_addr(PCIDevice *pci_dev, int region_num) +{ + return pci_dev->io_regions[region_num].addr; +} + +static pcibus_t pci_bar_address(PCIDevice *d, + int reg, uint8_t type, pcibus_t size) +{ + pcibus_t new_addr, last_addr; + int bar = pci_bar(d, reg); + uint16_t cmd = pci_get_word(d->config + PCI_COMMAND); + + if (type & PCI_BASE_ADDRESS_SPACE_IO) { + if (!(cmd & PCI_COMMAND_IO)) { + return PCI_BAR_UNMAPPED; + } + new_addr = pci_get_long(d->config + bar) & ~(size - 1); + last_addr = new_addr + size - 1; + /* NOTE: we have only 64K ioports on PC */ + if (last_addr <= new_addr || new_addr == 0 || last_addr > UINT16_MAX) { + return PCI_BAR_UNMAPPED; + } + return new_addr; + } + + if (!(cmd & PCI_COMMAND_MEMORY)) { + return PCI_BAR_UNMAPPED; + } + if (type & PCI_BASE_ADDRESS_MEM_TYPE_64) { + new_addr = pci_get_quad(d->config + bar); + } else { + new_addr = pci_get_long(d->config + bar); + } + /* the ROM slot has a specific enable bit */ + if (reg == PCI_ROM_SLOT && !(new_addr & PCI_ROM_ADDRESS_ENABLE)) { + return PCI_BAR_UNMAPPED; + } + new_addr &= ~(size - 1); + last_addr = new_addr + size - 1; + /* NOTE: we do not support wrapping */ + /* XXX: as we cannot support really dynamic + mappings, we handle specific values as invalid + mappings. */ + if (last_addr <= new_addr || new_addr == 0 || + last_addr == PCI_BAR_UNMAPPED) { + return PCI_BAR_UNMAPPED; + } + + /* Now pcibus_t is 64bit. + * Check if 32 bit BAR wraps around explicitly. + * Without this, PC ide doesn't work well. + * TODO: remove this work around. + */ + if (!(type & PCI_BASE_ADDRESS_MEM_TYPE_64) && last_addr >= UINT32_MAX) { + return PCI_BAR_UNMAPPED; + } + + /* + * OS is allowed to set BAR beyond its addressable + * bits. For example, 32 bit OS can set 64bit bar + * to >4G. Check it. TODO: we might need to support + * it in the future for e.g. PAE. + */ + if (last_addr >= HWADDR_MAX) { + return PCI_BAR_UNMAPPED; + } + + return new_addr; +} + +static void pci_update_mappings(PCIDevice *d) +{ + PCIIORegion *r; + int i; + pcibus_t new_addr; + + for(i = 0; i < PCI_NUM_REGIONS; i++) { + r = &d->io_regions[i]; + + /* this region isn't registered */ + if (!r->size) + continue; + + new_addr = pci_bar_address(d, i, r->type, r->size); + + /* This bar isn't changed */ + if (new_addr == r->addr) + continue; + + /* now do the real mapping */ + if (r->addr != PCI_BAR_UNMAPPED) { + memory_region_del_subregion(r->address_space, r->memory); + } + r->addr = new_addr; + if (r->addr != PCI_BAR_UNMAPPED) { + memory_region_add_subregion_overlap(r->address_space, + r->addr, r->memory, 1); + } + } +} + +static inline int pci_irq_disabled(PCIDevice *d) +{ + return pci_get_word(d->config + PCI_COMMAND) & PCI_COMMAND_INTX_DISABLE; +} + +/* Called after interrupt disabled field update in config space, + * assert/deassert interrupts if necessary. + * Gets original interrupt disable bit value (before update). */ +static void pci_update_irq_disabled(PCIDevice *d, int was_irq_disabled) +{ + int i, disabled = pci_irq_disabled(d); + if (disabled == was_irq_disabled) + return; + for (i = 0; i < PCI_NUM_PINS; ++i) { + int state = pci_irq_state(d, i); + pci_change_irq_level(d, i, disabled ? -state : state); + } +} + +uint32_t pci_default_read_config(PCIDevice *d, + uint32_t address, int len) +{ + uint32_t val = 0; + + memcpy(&val, d->config + address, len); + return le32_to_cpu(val); +} + +void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l) +{ + int i, was_irq_disabled = pci_irq_disabled(d); + + for (i = 0; i < l; val >>= 8, ++i) { + uint8_t wmask = d->wmask[addr + i]; + uint8_t w1cmask = d->w1cmask[addr + i]; + assert(!(wmask & w1cmask)); + d->config[addr + i] = (d->config[addr + i] & ~wmask) | (val & wmask); + d->config[addr + i] &= ~(val & w1cmask); /* W1C: Write 1 to Clear */ + } + if (ranges_overlap(addr, l, PCI_BASE_ADDRESS_0, 24) || + ranges_overlap(addr, l, PCI_ROM_ADDRESS, 4) || + ranges_overlap(addr, l, PCI_ROM_ADDRESS1, 4) || + range_covers_byte(addr, l, PCI_COMMAND)) + pci_update_mappings(d); + + if (range_covers_byte(addr, l, PCI_COMMAND)) { + pci_update_irq_disabled(d, was_irq_disabled); + memory_region_set_enabled(&d->bus_master_enable_region, + pci_get_word(d->config + PCI_COMMAND) + & PCI_COMMAND_MASTER); + } + + msi_write_config(d, addr, val, l); + msix_write_config(d, addr, val, l); +} + +/***********************************************************/ +/* generic PCI irq support */ + +/* 0 <= irq_num <= 3. level must be 0 or 1 */ +static void pci_set_irq(void *opaque, int irq_num, int level) +{ + PCIDevice *pci_dev = opaque; + int change; + + change = level - pci_irq_state(pci_dev, irq_num); + if (!change) + return; + + pci_set_irq_state(pci_dev, irq_num, level); + pci_update_irq_status(pci_dev); + if (pci_irq_disabled(pci_dev)) + return; + pci_change_irq_level(pci_dev, irq_num, change); +} + +/* Special hooks used by device assignment */ +void pci_bus_set_route_irq_fn(PCIBus *bus, pci_route_irq_fn route_intx_to_irq) +{ + assert(!bus->parent_dev); + bus->route_intx_to_irq = route_intx_to_irq; +} + +PCIINTxRoute pci_device_route_intx_to_irq(PCIDevice *dev, int pin) +{ + PCIBus *bus; + + do { + bus = dev->bus; + pin = bus->map_irq(dev, pin); + dev = bus->parent_dev; + } while (dev); + + if (!bus->route_intx_to_irq) { + error_report("PCI: Bug - unimplemented PCI INTx routing (%s)\n", + object_get_typename(OBJECT(bus->qbus.parent))); + return (PCIINTxRoute) { PCI_INTX_DISABLED, -1 }; + } + + return bus->route_intx_to_irq(bus->irq_opaque, pin); +} + +bool pci_intx_route_changed(PCIINTxRoute *old, PCIINTxRoute *new) +{ + return old->mode != new->mode || old->irq != new->irq; +} + +void pci_bus_fire_intx_routing_notifier(PCIBus *bus) +{ + PCIDevice *dev; + PCIBus *sec; + int i; + + for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) { + dev = bus->devices[i]; + if (dev && dev->intx_routing_notifier) { + dev->intx_routing_notifier(dev); + } + QLIST_FOREACH(sec, &bus->child, sibling) { + pci_bus_fire_intx_routing_notifier(sec); + } + } +} + +void pci_device_set_intx_routing_notifier(PCIDevice *dev, + PCIINTxRoutingNotifier notifier) +{ + dev->intx_routing_notifier = notifier; +} + +/* + * PCI-to-PCI bridge specification + * 9.1: Interrupt routing. Table 9-1 + * + * the PCI Express Base Specification, Revision 2.1 + * 2.2.8.1: INTx interrutp signaling - Rules + * the Implementation Note + * Table 2-20 + */ +/* + * 0 <= pin <= 3 0 = INTA, 1 = INTB, 2 = INTC, 3 = INTD + * 0-origin unlike PCI interrupt pin register. + */ +int pci_swizzle_map_irq_fn(PCIDevice *pci_dev, int pin) +{ + return (pin + PCI_SLOT(pci_dev->devfn)) % PCI_NUM_PINS; +} + +/***********************************************************/ +/* monitor info on PCI */ + +typedef struct { + uint16_t class; + const char *desc; + const char *fw_name; + uint16_t fw_ign_bits; +} pci_class_desc; + +static const pci_class_desc pci_class_descriptions[] = +{ + { 0x0001, "VGA controller", "display"}, + { 0x0100, "SCSI controller", "scsi"}, + { 0x0101, "IDE controller", "ide"}, + { 0x0102, "Floppy controller", "fdc"}, + { 0x0103, "IPI controller", "ipi"}, + { 0x0104, "RAID controller", "raid"}, + { 0x0106, "SATA controller"}, + { 0x0107, "SAS controller"}, + { 0x0180, "Storage controller"}, + { 0x0200, "Ethernet controller", "ethernet"}, + { 0x0201, "Token Ring controller", "token-ring"}, + { 0x0202, "FDDI controller", "fddi"}, + { 0x0203, "ATM controller", "atm"}, + { 0x0280, "Network controller"}, + { 0x0300, "VGA controller", "display", 0x00ff}, + { 0x0301, "XGA controller"}, + { 0x0302, "3D controller"}, + { 0x0380, "Display controller"}, + { 0x0400, "Video controller", "video"}, + { 0x0401, "Audio controller", "sound"}, + { 0x0402, "Phone"}, + { 0x0403, "Audio controller", "sound"}, + { 0x0480, "Multimedia controller"}, + { 0x0500, "RAM controller", "memory"}, + { 0x0501, "Flash controller", "flash"}, + { 0x0580, "Memory controller"}, + { 0x0600, "Host bridge", "host"}, + { 0x0601, "ISA bridge", "isa"}, + { 0x0602, "EISA bridge", "eisa"}, + { 0x0603, "MC bridge", "mca"}, + { 0x0604, "PCI bridge", "pci"}, + { 0x0605, "PCMCIA bridge", "pcmcia"}, + { 0x0606, "NUBUS bridge", "nubus"}, + { 0x0607, "CARDBUS bridge", "cardbus"}, + { 0x0608, "RACEWAY bridge"}, + { 0x0680, "Bridge"}, + { 0x0700, "Serial port", "serial"}, + { 0x0701, "Parallel port", "parallel"}, + { 0x0800, "Interrupt controller", "interrupt-controller"}, + { 0x0801, "DMA controller", "dma-controller"}, + { 0x0802, "Timer", "timer"}, + { 0x0803, "RTC", "rtc"}, + { 0x0900, "Keyboard", "keyboard"}, + { 0x0901, "Pen", "pen"}, + { 0x0902, "Mouse", "mouse"}, + { 0x0A00, "Dock station", "dock", 0x00ff}, + { 0x0B00, "i386 cpu", "cpu", 0x00ff}, + { 0x0c00, "Fireware contorller", "fireware"}, + { 0x0c01, "Access bus controller", "access-bus"}, + { 0x0c02, "SSA controller", "ssa"}, + { 0x0c03, "USB controller", "usb"}, + { 0x0c04, "Fibre channel controller", "fibre-channel"}, + { 0x0c05, "SMBus"}, + { 0, NULL} +}; + +static void pci_for_each_device_under_bus(PCIBus *bus, + void (*fn)(PCIBus *b, PCIDevice *d, + void *opaque), + void *opaque) +{ + PCIDevice *d; + int devfn; + + for(devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) { + d = bus->devices[devfn]; + if (d) { + fn(bus, d, opaque); + } + } +} + +void pci_for_each_device(PCIBus *bus, int bus_num, + void (*fn)(PCIBus *b, PCIDevice *d, void *opaque), + void *opaque) +{ + bus = pci_find_bus_nr(bus, bus_num); + + if (bus) { + pci_for_each_device_under_bus(bus, fn, opaque); + } +} + +static const pci_class_desc *get_class_desc(int class) +{ + const pci_class_desc *desc; + + desc = pci_class_descriptions; + while (desc->desc && class != desc->class) { + desc++; + } + + return desc; +} + +static PciDeviceInfoList *qmp_query_pci_devices(PCIBus *bus, int bus_num); + +static PciMemoryRegionList *qmp_query_pci_regions(const PCIDevice *dev) +{ + PciMemoryRegionList *head = NULL, *cur_item = NULL; + int i; + + for (i = 0; i < PCI_NUM_REGIONS; i++) { + const PCIIORegion *r = &dev->io_regions[i]; + PciMemoryRegionList *region; + + if (!r->size) { + continue; + } + + region = g_malloc0(sizeof(*region)); + region->value = g_malloc0(sizeof(*region->value)); + + if (r->type & PCI_BASE_ADDRESS_SPACE_IO) { + region->value->type = g_strdup("io"); + } else { + region->value->type = g_strdup("memory"); + region->value->has_prefetch = true; + region->value->prefetch = !!(r->type & PCI_BASE_ADDRESS_MEM_PREFETCH); + region->value->has_mem_type_64 = true; + region->value->mem_type_64 = !!(r->type & PCI_BASE_ADDRESS_MEM_TYPE_64); + } + + region->value->bar = i; + region->value->address = r->addr; + region->value->size = r->size; + + /* XXX: waiting for the qapi to support GSList */ + if (!cur_item) { + head = cur_item = region; + } else { + cur_item->next = region; + cur_item = region; + } + } + + return head; +} + +static PciBridgeInfo *qmp_query_pci_bridge(PCIDevice *dev, PCIBus *bus, + int bus_num) +{ + PciBridgeInfo *info; + + info = g_malloc0(sizeof(*info)); + + info->bus.number = dev->config[PCI_PRIMARY_BUS]; + info->bus.secondary = dev->config[PCI_SECONDARY_BUS]; + info->bus.subordinate = dev->config[PCI_SUBORDINATE_BUS]; + + info->bus.io_range = g_malloc0(sizeof(*info->bus.io_range)); + info->bus.io_range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_IO); + info->bus.io_range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_IO); + + info->bus.memory_range = g_malloc0(sizeof(*info->bus.memory_range)); + info->bus.memory_range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY); + info->bus.memory_range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY); + + info->bus.prefetchable_range = g_malloc0(sizeof(*info->bus.prefetchable_range)); + info->bus.prefetchable_range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); + info->bus.prefetchable_range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); + + if (dev->config[PCI_SECONDARY_BUS] != 0) { + PCIBus *child_bus = pci_find_bus_nr(bus, dev->config[PCI_SECONDARY_BUS]); + if (child_bus) { + info->has_devices = true; + info->devices = qmp_query_pci_devices(child_bus, dev->config[PCI_SECONDARY_BUS]); + } + } + + return info; +} + +static PciDeviceInfo *qmp_query_pci_device(PCIDevice *dev, PCIBus *bus, + int bus_num) +{ + const pci_class_desc *desc; + PciDeviceInfo *info; + uint8_t type; + int class; + + info = g_malloc0(sizeof(*info)); + info->bus = bus_num; + info->slot = PCI_SLOT(dev->devfn); + info->function = PCI_FUNC(dev->devfn); + + class = pci_get_word(dev->config + PCI_CLASS_DEVICE); + info->class_info.class = class; + desc = get_class_desc(class); + if (desc->desc) { + info->class_info.has_desc = true; + info->class_info.desc = g_strdup(desc->desc); + } + + info->id.vendor = pci_get_word(dev->config + PCI_VENDOR_ID); + info->id.device = pci_get_word(dev->config + PCI_DEVICE_ID); + info->regions = qmp_query_pci_regions(dev); + info->qdev_id = g_strdup(dev->qdev.id ? dev->qdev.id : ""); + + if (dev->config[PCI_INTERRUPT_PIN] != 0) { + info->has_irq = true; + info->irq = dev->config[PCI_INTERRUPT_LINE]; + } + + type = dev->config[PCI_HEADER_TYPE] & ~PCI_HEADER_TYPE_MULTI_FUNCTION; + if (type == PCI_HEADER_TYPE_BRIDGE) { + info->has_pci_bridge = true; + info->pci_bridge = qmp_query_pci_bridge(dev, bus, bus_num); + } + + return info; +} + +static PciDeviceInfoList *qmp_query_pci_devices(PCIBus *bus, int bus_num) +{ + PciDeviceInfoList *info, *head = NULL, *cur_item = NULL; + PCIDevice *dev; + int devfn; + + for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) { + dev = bus->devices[devfn]; + if (dev) { + info = g_malloc0(sizeof(*info)); + info->value = qmp_query_pci_device(dev, bus, bus_num); + + /* XXX: waiting for the qapi to support GSList */ + if (!cur_item) { + head = cur_item = info; + } else { + cur_item->next = info; + cur_item = info; + } + } + } + + return head; +} + +static PciInfo *qmp_query_pci_bus(PCIBus *bus, int bus_num) +{ + PciInfo *info = NULL; + + bus = pci_find_bus_nr(bus, bus_num); + if (bus) { + info = g_malloc0(sizeof(*info)); + info->bus = bus_num; + info->devices = qmp_query_pci_devices(bus, bus_num); + } + + return info; +} + +PciInfoList *qmp_query_pci(Error **errp) +{ + PciInfoList *info, *head = NULL, *cur_item = NULL; + struct PCIHostBus *host; + + QLIST_FOREACH(host, &host_buses, next) { + info = g_malloc0(sizeof(*info)); + info->value = qmp_query_pci_bus(host->bus, 0); + + /* XXX: waiting for the qapi to support GSList */ + if (!cur_item) { + head = cur_item = info; + } else { + cur_item->next = info; + cur_item = info; + } + } + + return head; +} + +static const char * const pci_nic_models[] = { + "ne2k_pci", + "i82551", + "i82557b", + "i82559er", + "rtl8139", + "e1000", + "pcnet", + "virtio", + NULL +}; + +static const char * const pci_nic_names[] = { + "ne2k_pci", + "i82551", + "i82557b", + "i82559er", + "rtl8139", + "e1000", + "pcnet", + "virtio-net-pci", + NULL +}; + +/* Initialize a PCI NIC. */ +/* FIXME callers should check for failure, but don't */ +PCIDevice *pci_nic_init(NICInfo *nd, const char *default_model, + const char *default_devaddr) +{ + const char *devaddr = nd->devaddr ? nd->devaddr : default_devaddr; + PCIBus *bus; + int devfn; + PCIDevice *pci_dev; + DeviceState *dev; + int i; + + i = qemu_find_nic_model(nd, pci_nic_models, default_model); + if (i < 0) + return NULL; + + bus = pci_get_bus_devfn(&devfn, devaddr); + if (!bus) { + error_report("Invalid PCI device address %s for device %s", + devaddr, pci_nic_names[i]); + return NULL; + } + + pci_dev = pci_create(bus, devfn, pci_nic_names[i]); + dev = &pci_dev->qdev; + qdev_set_nic_properties(dev, nd); + if (qdev_init(dev) < 0) + return NULL; + return pci_dev; +} + +PCIDevice *pci_nic_init_nofail(NICInfo *nd, const char *default_model, + const char *default_devaddr) +{ + PCIDevice *res; + + if (qemu_show_nic_models(nd->model, pci_nic_models)) + exit(0); + + res = pci_nic_init(nd, default_model, default_devaddr); + if (!res) + exit(1); + return res; +} + +PCIDevice *pci_vga_init(PCIBus *bus) +{ + switch (vga_interface_type) { + case VGA_CIRRUS: + return pci_create_simple(bus, -1, "cirrus-vga"); + case VGA_QXL: + return pci_create_simple(bus, -1, "qxl-vga"); + case VGA_STD: + return pci_create_simple(bus, -1, "VGA"); + case VGA_VMWARE: + return pci_create_simple(bus, -1, "vmware-svga"); + case VGA_NONE: + default: /* Other non-PCI types. Checking for unsupported types is already + done in vl.c. */ + return NULL; + } +} + +/* Whether a given bus number is in range of the secondary + * bus of the given bridge device. */ +static bool pci_secondary_bus_in_range(PCIDevice *dev, int bus_num) +{ + return !(pci_get_word(dev->config + PCI_BRIDGE_CONTROL) & + PCI_BRIDGE_CTL_BUS_RESET) /* Don't walk the bus if it's reset. */ && + dev->config[PCI_SECONDARY_BUS] < bus_num && + bus_num <= dev->config[PCI_SUBORDINATE_BUS]; +} + +static PCIBus *pci_find_bus_nr(PCIBus *bus, int bus_num) +{ + PCIBus *sec; + + if (!bus) { + return NULL; + } + + if (pci_bus_num(bus) == bus_num) { + return bus; + } + + /* Consider all bus numbers in range for the host pci bridge. */ + if (bus->parent_dev && + !pci_secondary_bus_in_range(bus->parent_dev, bus_num)) { + return NULL; + } + + /* try child bus */ + for (; bus; bus = sec) { + QLIST_FOREACH(sec, &bus->child, sibling) { + assert(sec->parent_dev); + if (sec->parent_dev->config[PCI_SECONDARY_BUS] == bus_num) { + return sec; + } + if (pci_secondary_bus_in_range(sec->parent_dev, bus_num)) { + break; + } + } + } + + return NULL; +} + +PCIDevice *pci_find_device(PCIBus *bus, int bus_num, uint8_t devfn) +{ + bus = pci_find_bus_nr(bus, bus_num); + + if (!bus) + return NULL; + + return bus->devices[devfn]; +} + +static int pci_qdev_init(DeviceState *qdev) +{ + PCIDevice *pci_dev = (PCIDevice *)qdev; + PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(pci_dev); + PCIBus *bus; + int rc; + bool is_default_rom; + + /* initialize cap_present for pci_is_express() and pci_config_size() */ + if (pc->is_express) { + pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS; + } + + bus = FROM_QBUS(PCIBus, qdev_get_parent_bus(qdev)); + pci_dev = do_pci_register_device(pci_dev, bus, + object_get_typename(OBJECT(qdev)), + pci_dev->devfn); + if (pci_dev == NULL) + return -1; + if (qdev->hotplugged && pc->no_hotplug) { + qerror_report(QERR_DEVICE_NO_HOTPLUG, object_get_typename(OBJECT(pci_dev))); + do_pci_unregister_device(pci_dev); + return -1; + } + if (pc->init) { + rc = pc->init(pci_dev); + if (rc != 0) { + do_pci_unregister_device(pci_dev); + return rc; + } + } + + /* rom loading */ + is_default_rom = false; + if (pci_dev->romfile == NULL && pc->romfile != NULL) { + pci_dev->romfile = g_strdup(pc->romfile); + is_default_rom = true; + } + pci_add_option_rom(pci_dev, is_default_rom); + + if (bus->hotplug) { + /* Let buses differentiate between hotplug and when device is + * enabled during qemu machine creation. */ + rc = bus->hotplug(bus->hotplug_qdev, pci_dev, + qdev->hotplugged ? PCI_HOTPLUG_ENABLED: + PCI_COLDPLUG_ENABLED); + if (rc != 0) { + int r = pci_unregister_device(&pci_dev->qdev); + assert(!r); + return rc; + } + } + return 0; +} + +static int pci_unplug_device(DeviceState *qdev) +{ + PCIDevice *dev = PCI_DEVICE(qdev); + PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); + + if (pc->no_hotplug) { + qerror_report(QERR_DEVICE_NO_HOTPLUG, object_get_typename(OBJECT(dev))); + return -1; + } + return dev->bus->hotplug(dev->bus->hotplug_qdev, dev, + PCI_HOTPLUG_DISABLED); +} + +PCIDevice *pci_create_multifunction(PCIBus *bus, int devfn, bool multifunction, + const char *name) +{ + DeviceState *dev; + + dev = qdev_create(&bus->qbus, name); + qdev_prop_set_int32(dev, "addr", devfn); + qdev_prop_set_bit(dev, "multifunction", multifunction); + return PCI_DEVICE(dev); +} + +PCIDevice *pci_create_simple_multifunction(PCIBus *bus, int devfn, + bool multifunction, + const char *name) +{ + PCIDevice *dev = pci_create_multifunction(bus, devfn, multifunction, name); + qdev_init_nofail(&dev->qdev); + return dev; +} + +PCIDevice *pci_create(PCIBus *bus, int devfn, const char *name) +{ + return pci_create_multifunction(bus, devfn, false, name); +} + +PCIDevice *pci_create_simple(PCIBus *bus, int devfn, const char *name) +{ + return pci_create_simple_multifunction(bus, devfn, false, name); +} + +static uint8_t pci_find_space(PCIDevice *pdev, uint8_t size) +{ + int offset = PCI_CONFIG_HEADER_SIZE; + int i; + for (i = PCI_CONFIG_HEADER_SIZE; i < PCI_CONFIG_SPACE_SIZE; ++i) { + if (pdev->used[i]) + offset = i + 1; + else if (i - offset + 1 == size) + return offset; + } + return 0; +} + +static uint8_t pci_find_capability_list(PCIDevice *pdev, uint8_t cap_id, + uint8_t *prev_p) +{ + uint8_t next, prev; + + if (!(pdev->config[PCI_STATUS] & PCI_STATUS_CAP_LIST)) + return 0; + + for (prev = PCI_CAPABILITY_LIST; (next = pdev->config[prev]); + prev = next + PCI_CAP_LIST_NEXT) + if (pdev->config[next + PCI_CAP_LIST_ID] == cap_id) + break; + + if (prev_p) + *prev_p = prev; + return next; +} + +static uint8_t pci_find_capability_at_offset(PCIDevice *pdev, uint8_t offset) +{ + uint8_t next, prev, found = 0; + + if (!(pdev->used[offset])) { + return 0; + } + + assert(pdev->config[PCI_STATUS] & PCI_STATUS_CAP_LIST); + + for (prev = PCI_CAPABILITY_LIST; (next = pdev->config[prev]); + prev = next + PCI_CAP_LIST_NEXT) { + if (next <= offset && next > found) { + found = next; + } + } + return found; +} + +/* Patch the PCI vendor and device ids in a PCI rom image if necessary. + This is needed for an option rom which is used for more than one device. */ +static void pci_patch_ids(PCIDevice *pdev, uint8_t *ptr, int size) +{ + uint16_t vendor_id; + uint16_t device_id; + uint16_t rom_vendor_id; + uint16_t rom_device_id; + uint16_t rom_magic; + uint16_t pcir_offset; + uint8_t checksum; + + /* Words in rom data are little endian (like in PCI configuration), + so they can be read / written with pci_get_word / pci_set_word. */ + + /* Only a valid rom will be patched. */ + rom_magic = pci_get_word(ptr); + if (rom_magic != 0xaa55) { + PCI_DPRINTF("Bad ROM magic %04x\n", rom_magic); + return; + } + pcir_offset = pci_get_word(ptr + 0x18); + if (pcir_offset + 8 >= size || memcmp(ptr + pcir_offset, "PCIR", 4)) { + PCI_DPRINTF("Bad PCIR offset 0x%x or signature\n", pcir_offset); + return; + } + + vendor_id = pci_get_word(pdev->config + PCI_VENDOR_ID); + device_id = pci_get_word(pdev->config + PCI_DEVICE_ID); + rom_vendor_id = pci_get_word(ptr + pcir_offset + 4); + rom_device_id = pci_get_word(ptr + pcir_offset + 6); + + PCI_DPRINTF("%s: ROM id %04x%04x / PCI id %04x%04x\n", pdev->romfile, + vendor_id, device_id, rom_vendor_id, rom_device_id); + + checksum = ptr[6]; + + if (vendor_id != rom_vendor_id) { + /* Patch vendor id and checksum (at offset 6 for etherboot roms). */ + checksum += (uint8_t)rom_vendor_id + (uint8_t)(rom_vendor_id >> 8); + checksum -= (uint8_t)vendor_id + (uint8_t)(vendor_id >> 8); + PCI_DPRINTF("ROM checksum %02x / %02x\n", ptr[6], checksum); + ptr[6] = checksum; + pci_set_word(ptr + pcir_offset + 4, vendor_id); + } + + if (device_id != rom_device_id) { + /* Patch device id and checksum (at offset 6 for etherboot roms). */ + checksum += (uint8_t)rom_device_id + (uint8_t)(rom_device_id >> 8); + checksum -= (uint8_t)device_id + (uint8_t)(device_id >> 8); + PCI_DPRINTF("ROM checksum %02x / %02x\n", ptr[6], checksum); + ptr[6] = checksum; + pci_set_word(ptr + pcir_offset + 6, device_id); + } +} + +/* Add an option rom for the device */ +static int pci_add_option_rom(PCIDevice *pdev, bool is_default_rom) +{ + int size; + char *path; + void *ptr; + char name[32]; + const VMStateDescription *vmsd; + + if (!pdev->romfile) + return 0; + if (strlen(pdev->romfile) == 0) + return 0; + + if (!pdev->rom_bar) { + /* + * Load rom via fw_cfg instead of creating a rom bar, + * for 0.11 compatibility. + */ + int class = pci_get_word(pdev->config + PCI_CLASS_DEVICE); + if (class == 0x0300) { + rom_add_vga(pdev->romfile); + } else { + rom_add_option(pdev->romfile, -1); + } + return 0; + } + + path = qemu_find_file(QEMU_FILE_TYPE_BIOS, pdev->romfile); + if (path == NULL) { + path = g_strdup(pdev->romfile); + } + + size = get_image_size(path); + if (size < 0) { + error_report("%s: failed to find romfile \"%s\"", + __FUNCTION__, pdev->romfile); + g_free(path); + return -1; + } + if (size & (size - 1)) { + size = 1 << qemu_fls(size); + } + + vmsd = qdev_get_vmsd(DEVICE(pdev)); + + if (vmsd) { + snprintf(name, sizeof(name), "%s.rom", vmsd->name); + } else { + snprintf(name, sizeof(name), "%s.rom", object_get_typename(OBJECT(pdev))); + } + pdev->has_rom = true; + memory_region_init_ram(&pdev->rom, name, size); + vmstate_register_ram(&pdev->rom, &pdev->qdev); + ptr = memory_region_get_ram_ptr(&pdev->rom); + load_image(path, ptr); + g_free(path); + + if (is_default_rom) { + /* Only the default rom images will be patched (if needed). */ + pci_patch_ids(pdev, ptr, size); + } + + qemu_put_ram_ptr(ptr); + + pci_register_bar(pdev, PCI_ROM_SLOT, 0, &pdev->rom); + + return 0; +} + +static void pci_del_option_rom(PCIDevice *pdev) +{ + if (!pdev->has_rom) + return; + + vmstate_unregister_ram(&pdev->rom, &pdev->qdev); + memory_region_destroy(&pdev->rom); + pdev->has_rom = false; +} + +/* + * if !offset + * Reserve space and add capability to the linked list in pci config space + * + * if offset = 0, + * Find and reserve space and add capability to the linked list + * in pci config space */ +int pci_add_capability(PCIDevice *pdev, uint8_t cap_id, + uint8_t offset, uint8_t size) +{ + uint8_t *config; + int i, overlapping_cap; + + if (!offset) { + offset = pci_find_space(pdev, size); + if (!offset) { + return -ENOSPC; + } + } else { + /* Verify that capabilities don't overlap. Note: device assignment + * depends on this check to verify that the device is not broken. + * Should never trigger for emulated devices, but it's helpful + * for debugging these. */ + for (i = offset; i < offset + size; i++) { + overlapping_cap = pci_find_capability_at_offset(pdev, i); + if (overlapping_cap) { + fprintf(stderr, "ERROR: %04x:%02x:%02x.%x " + "Attempt to add PCI capability %x at offset " + "%x overlaps existing capability %x at offset %x\n", + pci_find_domain(pdev->bus), pci_bus_num(pdev->bus), + PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), + cap_id, offset, overlapping_cap, i); + return -EINVAL; + } + } + } + + config = pdev->config + offset; + config[PCI_CAP_LIST_ID] = cap_id; + config[PCI_CAP_LIST_NEXT] = pdev->config[PCI_CAPABILITY_LIST]; + pdev->config[PCI_CAPABILITY_LIST] = offset; + pdev->config[PCI_STATUS] |= PCI_STATUS_CAP_LIST; + memset(pdev->used + offset, 0xFF, QEMU_ALIGN_UP(size, 4)); + /* Make capability read-only by default */ + memset(pdev->wmask + offset, 0, size); + /* Check capability by default */ + memset(pdev->cmask + offset, 0xFF, size); + return offset; +} + +/* Unlink capability from the pci config space. */ +void pci_del_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size) +{ + uint8_t prev, offset = pci_find_capability_list(pdev, cap_id, &prev); + if (!offset) + return; + pdev->config[prev] = pdev->config[offset + PCI_CAP_LIST_NEXT]; + /* Make capability writable again */ + memset(pdev->wmask + offset, 0xff, size); + memset(pdev->w1cmask + offset, 0, size); + /* Clear cmask as device-specific registers can't be checked */ + memset(pdev->cmask + offset, 0, size); + memset(pdev->used + offset, 0, QEMU_ALIGN_UP(size, 4)); + + if (!pdev->config[PCI_CAPABILITY_LIST]) + pdev->config[PCI_STATUS] &= ~PCI_STATUS_CAP_LIST; +} + +uint8_t pci_find_capability(PCIDevice *pdev, uint8_t cap_id) +{ + return pci_find_capability_list(pdev, cap_id, NULL); +} + +static void pcibus_dev_print(Monitor *mon, DeviceState *dev, int indent) +{ + PCIDevice *d = (PCIDevice *)dev; + const pci_class_desc *desc; + char ctxt[64]; + PCIIORegion *r; + int i, class; + + class = pci_get_word(d->config + PCI_CLASS_DEVICE); + desc = pci_class_descriptions; + while (desc->desc && class != desc->class) + desc++; + if (desc->desc) { + snprintf(ctxt, sizeof(ctxt), "%s", desc->desc); + } else { + snprintf(ctxt, sizeof(ctxt), "Class %04x", class); + } + + monitor_printf(mon, "%*sclass %s, addr %02x:%02x.%x, " + "pci id %04x:%04x (sub %04x:%04x)\n", + indent, "", ctxt, pci_bus_num(d->bus), + PCI_SLOT(d->devfn), PCI_FUNC(d->devfn), + pci_get_word(d->config + PCI_VENDOR_ID), + pci_get_word(d->config + PCI_DEVICE_ID), + pci_get_word(d->config + PCI_SUBSYSTEM_VENDOR_ID), + pci_get_word(d->config + PCI_SUBSYSTEM_ID)); + for (i = 0; i < PCI_NUM_REGIONS; i++) { + r = &d->io_regions[i]; + if (!r->size) + continue; + monitor_printf(mon, "%*sbar %d: %s at 0x%"FMT_PCIBUS + " [0x%"FMT_PCIBUS"]\n", + indent, "", + i, r->type & PCI_BASE_ADDRESS_SPACE_IO ? "i/o" : "mem", + r->addr, r->addr + r->size - 1); + } +} + +static char *pci_dev_fw_name(DeviceState *dev, char *buf, int len) +{ + PCIDevice *d = (PCIDevice *)dev; + const char *name = NULL; + const pci_class_desc *desc = pci_class_descriptions; + int class = pci_get_word(d->config + PCI_CLASS_DEVICE); + + while (desc->desc && + (class & ~desc->fw_ign_bits) != + (desc->class & ~desc->fw_ign_bits)) { + desc++; + } + + if (desc->desc) { + name = desc->fw_name; + } + + if (name) { + pstrcpy(buf, len, name); + } else { + snprintf(buf, len, "pci%04x,%04x", + pci_get_word(d->config + PCI_VENDOR_ID), + pci_get_word(d->config + PCI_DEVICE_ID)); + } + + return buf; +} + +static char *pcibus_get_fw_dev_path(DeviceState *dev) +{ + PCIDevice *d = (PCIDevice *)dev; + char path[50], name[33]; + int off; + + off = snprintf(path, sizeof(path), "%s@%x", + pci_dev_fw_name(dev, name, sizeof name), + PCI_SLOT(d->devfn)); + if (PCI_FUNC(d->devfn)) + snprintf(path + off, sizeof(path) + off, ",%x", PCI_FUNC(d->devfn)); + return g_strdup(path); +} + +static char *pcibus_get_dev_path(DeviceState *dev) +{ + PCIDevice *d = container_of(dev, PCIDevice, qdev); + PCIDevice *t; + int slot_depth; + /* Path format: Domain:00:Slot.Function:Slot.Function....:Slot.Function. + * 00 is added here to make this format compatible with + * domain:Bus:Slot.Func for systems without nested PCI bridges. + * Slot.Function list specifies the slot and function numbers for all + * devices on the path from root to the specific device. */ + char domain[] = "DDDD:00"; + char slot[] = ":SS.F"; + int domain_len = sizeof domain - 1 /* For '\0' */; + int slot_len = sizeof slot - 1 /* For '\0' */; + int path_len; + char *path, *p; + int s; + + /* Calculate # of slots on path between device and root. */; + slot_depth = 0; + for (t = d; t; t = t->bus->parent_dev) { + ++slot_depth; + } + + path_len = domain_len + slot_len * slot_depth; + + /* Allocate memory, fill in the terminating null byte. */ + path = g_malloc(path_len + 1 /* For '\0' */); + path[path_len] = '\0'; + + /* First field is the domain. */ + s = snprintf(domain, sizeof domain, "%04x:00", pci_find_domain(d->bus)); + assert(s == domain_len); + memcpy(path, domain, domain_len); + + /* Fill in slot numbers. We walk up from device to root, so need to print + * them in the reverse order, last to first. */ + p = path + path_len; + for (t = d; t; t = t->bus->parent_dev) { + p -= slot_len; + s = snprintf(slot, sizeof slot, ":%02x.%x", + PCI_SLOT(t->devfn), PCI_FUNC(t->devfn)); + assert(s == slot_len); + memcpy(p, slot, slot_len); + } + + return path; +} + +static int pci_qdev_find_recursive(PCIBus *bus, + const char *id, PCIDevice **pdev) +{ + DeviceState *qdev = qdev_find_recursive(&bus->qbus, id); + if (!qdev) { + return -ENODEV; + } + + /* roughly check if given qdev is pci device */ + if (object_dynamic_cast(OBJECT(qdev), TYPE_PCI_DEVICE)) { + *pdev = PCI_DEVICE(qdev); + return 0; + } + return -EINVAL; +} + +int pci_qdev_find_device(const char *id, PCIDevice **pdev) +{ + struct PCIHostBus *host; + int rc = -ENODEV; + + QLIST_FOREACH(host, &host_buses, next) { + int tmp = pci_qdev_find_recursive(host->bus, id, pdev); + if (!tmp) { + rc = 0; + break; + } + if (tmp != -ENODEV) { + rc = tmp; + } + } + + return rc; +} + +MemoryRegion *pci_address_space(PCIDevice *dev) +{ + return dev->bus->address_space_mem; +} + +MemoryRegion *pci_address_space_io(PCIDevice *dev) +{ + return dev->bus->address_space_io; +} + +static void pci_device_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *k = DEVICE_CLASS(klass); + k->init = pci_qdev_init; + k->unplug = pci_unplug_device; + k->exit = pci_unregister_device; + k->bus_type = TYPE_PCI_BUS; + k->props = pci_props; +} + +void pci_setup_iommu(PCIBus *bus, PCIDMAContextFunc fn, void *opaque) +{ + bus->dma_context_fn = fn; + bus->dma_context_opaque = opaque; +} + +static TypeInfo pci_device_type_info = { + .name = TYPE_PCI_DEVICE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(PCIDevice), + .abstract = true, + .class_size = sizeof(PCIDeviceClass), + .class_init = pci_device_class_init, +}; + +static void pci_register_types(void) +{ + type_register_static(&pci_bus_info); + type_register_static(&pci_device_type_info); +} + +type_init(pci_register_types) diff --git a/hw/pci/pci.h b/hw/pci/pci.h new file mode 100644 index 0000000000..4da0c2a4c9 --- /dev/null +++ b/hw/pci/pci.h @@ -0,0 +1,684 @@ +#ifndef QEMU_PCI_H +#define QEMU_PCI_H + +#include "qemu-common.h" + +#include "qdev.h" +#include "memory.h" +#include "dma.h" + +/* PCI includes legacy ISA access. */ +#include "isa.h" + +#include "pcie.h" + +/* PCI bus */ + +#define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) +#define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f) +#define PCI_FUNC(devfn) ((devfn) & 0x07) +#define PCI_SLOT_MAX 32 +#define PCI_FUNC_MAX 8 + +/* Class, Vendor and Device IDs from Linux's pci_ids.h */ +#include "pci_ids.h" + +/* QEMU-specific Vendor and Device ID definitions */ + +/* IBM (0x1014) */ +#define PCI_DEVICE_ID_IBM_440GX 0x027f +#define PCI_DEVICE_ID_IBM_OPENPIC2 0xffff + +/* Hitachi (0x1054) */ +#define PCI_VENDOR_ID_HITACHI 0x1054 +#define PCI_DEVICE_ID_HITACHI_SH7751R 0x350e + +/* Apple (0x106b) */ +#define PCI_DEVICE_ID_APPLE_343S1201 0x0010 +#define PCI_DEVICE_ID_APPLE_UNI_N_I_PCI 0x001e +#define PCI_DEVICE_ID_APPLE_UNI_N_PCI 0x001f +#define PCI_DEVICE_ID_APPLE_UNI_N_KEYL 0x0022 +#define PCI_DEVICE_ID_APPLE_IPID_USB 0x003f + +/* Realtek (0x10ec) */ +#define PCI_DEVICE_ID_REALTEK_8029 0x8029 + +/* Xilinx (0x10ee) */ +#define PCI_DEVICE_ID_XILINX_XC2VP30 0x0300 + +/* Marvell (0x11ab) */ +#define PCI_DEVICE_ID_MARVELL_GT6412X 0x4620 + +/* QEMU/Bochs VGA (0x1234) */ +#define PCI_VENDOR_ID_QEMU 0x1234 +#define PCI_DEVICE_ID_QEMU_VGA 0x1111 + +/* VMWare (0x15ad) */ +#define PCI_VENDOR_ID_VMWARE 0x15ad +#define PCI_DEVICE_ID_VMWARE_SVGA2 0x0405 +#define PCI_DEVICE_ID_VMWARE_SVGA 0x0710 +#define PCI_DEVICE_ID_VMWARE_NET 0x0720 +#define PCI_DEVICE_ID_VMWARE_SCSI 0x0730 +#define PCI_DEVICE_ID_VMWARE_IDE 0x1729 + +/* Intel (0x8086) */ +#define PCI_DEVICE_ID_INTEL_82551IT 0x1209 +#define PCI_DEVICE_ID_INTEL_82557 0x1229 +#define PCI_DEVICE_ID_INTEL_82801IR 0x2922 + +/* Red Hat / Qumranet (for QEMU) -- see pci-ids.txt */ +#define PCI_VENDOR_ID_REDHAT_QUMRANET 0x1af4 +#define PCI_SUBVENDOR_ID_REDHAT_QUMRANET 0x1af4 +#define PCI_SUBDEVICE_ID_QEMU 0x1100 + +#define PCI_DEVICE_ID_VIRTIO_NET 0x1000 +#define PCI_DEVICE_ID_VIRTIO_BLOCK 0x1001 +#define PCI_DEVICE_ID_VIRTIO_BALLOON 0x1002 +#define PCI_DEVICE_ID_VIRTIO_CONSOLE 0x1003 +#define PCI_DEVICE_ID_VIRTIO_SCSI 0x1004 +#define PCI_DEVICE_ID_VIRTIO_RNG 0x1005 + +#define FMT_PCIBUS PRIx64 + +typedef void PCIConfigWriteFunc(PCIDevice *pci_dev, + uint32_t address, uint32_t data, int len); +typedef uint32_t PCIConfigReadFunc(PCIDevice *pci_dev, + uint32_t address, int len); +typedef void PCIMapIORegionFunc(PCIDevice *pci_dev, int region_num, + pcibus_t addr, pcibus_t size, int type); +typedef void PCIUnregisterFunc(PCIDevice *pci_dev); + +typedef struct PCIIORegion { + pcibus_t addr; /* current PCI mapping address. -1 means not mapped */ +#define PCI_BAR_UNMAPPED (~(pcibus_t)0) + pcibus_t size; + uint8_t type; + MemoryRegion *memory; + MemoryRegion *address_space; +} PCIIORegion; + +#define PCI_ROM_SLOT 6 +#define PCI_NUM_REGIONS 7 + +#include "pci_regs.h" + +/* PCI HEADER_TYPE */ +#define PCI_HEADER_TYPE_MULTI_FUNCTION 0x80 + +/* Size of the standard PCI config header */ +#define PCI_CONFIG_HEADER_SIZE 0x40 +/* Size of the standard PCI config space */ +#define PCI_CONFIG_SPACE_SIZE 0x100 +/* Size of the standart PCIe config space: 4KB */ +#define PCIE_CONFIG_SPACE_SIZE 0x1000 + +#define PCI_NUM_PINS 4 /* A-D */ + +/* Bits in cap_present field. */ +enum { + QEMU_PCI_CAP_MSI = 0x1, + QEMU_PCI_CAP_MSIX = 0x2, + QEMU_PCI_CAP_EXPRESS = 0x4, + + /* multifunction capable device */ +#define QEMU_PCI_CAP_MULTIFUNCTION_BITNR 3 + QEMU_PCI_CAP_MULTIFUNCTION = (1 << QEMU_PCI_CAP_MULTIFUNCTION_BITNR), + + /* command register SERR bit enabled */ +#define QEMU_PCI_CAP_SERR_BITNR 4 + QEMU_PCI_CAP_SERR = (1 << QEMU_PCI_CAP_SERR_BITNR), + /* Standard hot plug controller. */ +#define QEMU_PCI_SHPC_BITNR 5 + QEMU_PCI_CAP_SHPC = (1 << QEMU_PCI_SHPC_BITNR), +#define QEMU_PCI_SLOTID_BITNR 6 + QEMU_PCI_CAP_SLOTID = (1 << QEMU_PCI_SLOTID_BITNR), +}; + +#define TYPE_PCI_DEVICE "pci-device" +#define PCI_DEVICE(obj) \ + OBJECT_CHECK(PCIDevice, (obj), TYPE_PCI_DEVICE) +#define PCI_DEVICE_CLASS(klass) \ + OBJECT_CLASS_CHECK(PCIDeviceClass, (klass), TYPE_PCI_DEVICE) +#define PCI_DEVICE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(PCIDeviceClass, (obj), TYPE_PCI_DEVICE) + +typedef struct PCIINTxRoute { + enum { + PCI_INTX_ENABLED, + PCI_INTX_INVERTED, + PCI_INTX_DISABLED, + } mode; + int irq; +} PCIINTxRoute; + +typedef struct PCIDeviceClass { + DeviceClass parent_class; + + int (*init)(PCIDevice *dev); + PCIUnregisterFunc *exit; + PCIConfigReadFunc *config_read; + PCIConfigWriteFunc *config_write; + + uint16_t vendor_id; + uint16_t device_id; + uint8_t revision; + uint16_t class_id; + uint16_t subsystem_vendor_id; /* only for header type = 0 */ + uint16_t subsystem_id; /* only for header type = 0 */ + + /* + * pci-to-pci bridge or normal device. + * This doesn't mean pci host switch. + * When card bus bridge is supported, this would be enhanced. + */ + int is_bridge; + + /* pcie stuff */ + int is_express; /* is this device pci express? */ + + /* device isn't hot-pluggable */ + int no_hotplug; + + /* rom bar */ + const char *romfile; +} PCIDeviceClass; + +typedef void (*PCIINTxRoutingNotifier)(PCIDevice *dev); +typedef int (*MSIVectorUseNotifier)(PCIDevice *dev, unsigned int vector, + MSIMessage msg); +typedef void (*MSIVectorReleaseNotifier)(PCIDevice *dev, unsigned int vector); + +struct PCIDevice { + DeviceState qdev; + + /* PCI config space */ + uint8_t *config; + + /* Used to enable config checks on load. Note that writable bits are + * never checked even if set in cmask. */ + uint8_t *cmask; + + /* Used to implement R/W bytes */ + uint8_t *wmask; + + /* Used to implement RW1C(Write 1 to Clear) bytes */ + uint8_t *w1cmask; + + /* Used to allocate config space for capabilities. */ + uint8_t *used; + + /* the following fields are read only */ + PCIBus *bus; + int32_t devfn; + char name[64]; + PCIIORegion io_regions[PCI_NUM_REGIONS]; + AddressSpace bus_master_as; + MemoryRegion bus_master_enable_region; + DMAContext *dma; + + /* do not access the following fields */ + PCIConfigReadFunc *config_read; + PCIConfigWriteFunc *config_write; + + /* IRQ objects for the INTA-INTD pins. */ + qemu_irq *irq; + + /* Current IRQ levels. Used internally by the generic PCI code. */ + uint8_t irq_state; + + /* Capability bits */ + uint32_t cap_present; + + /* Offset of MSI-X capability in config space */ + uint8_t msix_cap; + + /* MSI-X entries */ + int msix_entries_nr; + + /* Space to store MSIX table & pending bit array */ + uint8_t *msix_table; + uint8_t *msix_pba; + /* MemoryRegion container for msix exclusive BAR setup */ + MemoryRegion msix_exclusive_bar; + /* Memory Regions for MSIX table and pending bit entries. */ + MemoryRegion msix_table_mmio; + MemoryRegion msix_pba_mmio; + /* Reference-count for entries actually in use by driver. */ + unsigned *msix_entry_used; + /* MSIX function mask set or MSIX disabled */ + bool msix_function_masked; + /* Version id needed for VMState */ + int32_t version_id; + + /* Offset of MSI capability in config space */ + uint8_t msi_cap; + + /* PCI Express */ + PCIExpressDevice exp; + + /* SHPC */ + SHPCDevice *shpc; + + /* Location of option rom */ + char *romfile; + bool has_rom; + MemoryRegion rom; + uint32_t rom_bar; + + /* INTx routing notifier */ + PCIINTxRoutingNotifier intx_routing_notifier; + + /* MSI-X notifiers */ + MSIVectorUseNotifier msix_vector_use_notifier; + MSIVectorReleaseNotifier msix_vector_release_notifier; +}; + +void pci_register_bar(PCIDevice *pci_dev, int region_num, + uint8_t attr, MemoryRegion *memory); +pcibus_t pci_get_bar_addr(PCIDevice *pci_dev, int region_num); + +int pci_add_capability(PCIDevice *pdev, uint8_t cap_id, + uint8_t offset, uint8_t size); + +void pci_del_capability(PCIDevice *pci_dev, uint8_t cap_id, uint8_t cap_size); + +uint8_t pci_find_capability(PCIDevice *pci_dev, uint8_t cap_id); + + +uint32_t pci_default_read_config(PCIDevice *d, + uint32_t address, int len); +void pci_default_write_config(PCIDevice *d, + uint32_t address, uint32_t val, int len); +void pci_device_save(PCIDevice *s, QEMUFile *f); +int pci_device_load(PCIDevice *s, QEMUFile *f); +MemoryRegion *pci_address_space(PCIDevice *dev); +MemoryRegion *pci_address_space_io(PCIDevice *dev); + +typedef void (*pci_set_irq_fn)(void *opaque, int irq_num, int level); +typedef int (*pci_map_irq_fn)(PCIDevice *pci_dev, int irq_num); +typedef PCIINTxRoute (*pci_route_irq_fn)(void *opaque, int pin); + +typedef enum { + PCI_HOTPLUG_DISABLED, + PCI_HOTPLUG_ENABLED, + PCI_COLDPLUG_ENABLED, +} PCIHotplugState; + +typedef int (*pci_hotplug_fn)(DeviceState *qdev, PCIDevice *pci_dev, + PCIHotplugState state); +void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent, + const char *name, + MemoryRegion *address_space_mem, + MemoryRegion *address_space_io, + uint8_t devfn_min); +PCIBus *pci_bus_new(DeviceState *parent, const char *name, + MemoryRegion *address_space_mem, + MemoryRegion *address_space_io, + uint8_t devfn_min); +void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, + void *irq_opaque, int nirq); +int pci_bus_get_irq_level(PCIBus *bus, int irq_num); +void pci_bus_hotplug(PCIBus *bus, pci_hotplug_fn hotplug, DeviceState *dev); +/* 0 <= pin <= 3 0 = INTA, 1 = INTB, 2 = INTC, 3 = INTD */ +int pci_swizzle_map_irq_fn(PCIDevice *pci_dev, int pin); +PCIBus *pci_register_bus(DeviceState *parent, const char *name, + pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, + void *irq_opaque, + MemoryRegion *address_space_mem, + MemoryRegion *address_space_io, + uint8_t devfn_min, int nirq); +void pci_bus_set_route_irq_fn(PCIBus *, pci_route_irq_fn); +PCIINTxRoute pci_device_route_intx_to_irq(PCIDevice *dev, int pin); +bool pci_intx_route_changed(PCIINTxRoute *old, PCIINTxRoute *new); +void pci_bus_fire_intx_routing_notifier(PCIBus *bus); +void pci_device_set_intx_routing_notifier(PCIDevice *dev, + PCIINTxRoutingNotifier notifier); +void pci_device_reset(PCIDevice *dev); +void pci_bus_reset(PCIBus *bus); + +PCIDevice *pci_nic_init(NICInfo *nd, const char *default_model, + const char *default_devaddr); +PCIDevice *pci_nic_init_nofail(NICInfo *nd, const char *default_model, + const char *default_devaddr); + +PCIDevice *pci_vga_init(PCIBus *bus); + +int pci_bus_num(PCIBus *s); +void pci_for_each_device(PCIBus *bus, int bus_num, + void (*fn)(PCIBus *bus, PCIDevice *d, void *opaque), + void *opaque); +PCIBus *pci_find_root_bus(int domain); +int pci_find_domain(const PCIBus *bus); +PCIDevice *pci_find_device(PCIBus *bus, int bus_num, uint8_t devfn); +int pci_qdev_find_device(const char *id, PCIDevice **pdev); +PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr); + +int pci_read_devaddr(Monitor *mon, const char *addr, int *domp, int *busp, + unsigned *slotp); + +void pci_device_deassert_intx(PCIDevice *dev); + +typedef DMAContext *(*PCIDMAContextFunc)(PCIBus *, void *, int); + +void pci_setup_iommu(PCIBus *bus, PCIDMAContextFunc fn, void *opaque); + +static inline void +pci_set_byte(uint8_t *config, uint8_t val) +{ + *config = val; +} + +static inline uint8_t +pci_get_byte(const uint8_t *config) +{ + return *config; +} + +static inline void +pci_set_word(uint8_t *config, uint16_t val) +{ + cpu_to_le16wu((uint16_t *)config, val); +} + +static inline uint16_t +pci_get_word(const uint8_t *config) +{ + return le16_to_cpupu((const uint16_t *)config); +} + +static inline void +pci_set_long(uint8_t *config, uint32_t val) +{ + cpu_to_le32wu((uint32_t *)config, val); +} + +static inline uint32_t +pci_get_long(const uint8_t *config) +{ + return le32_to_cpupu((const uint32_t *)config); +} + +static inline void +pci_set_quad(uint8_t *config, uint64_t val) +{ + cpu_to_le64w((uint64_t *)config, val); +} + +static inline uint64_t +pci_get_quad(const uint8_t *config) +{ + return le64_to_cpup((const uint64_t *)config); +} + +static inline void +pci_config_set_vendor_id(uint8_t *pci_config, uint16_t val) +{ + pci_set_word(&pci_config[PCI_VENDOR_ID], val); +} + +static inline void +pci_config_set_device_id(uint8_t *pci_config, uint16_t val) +{ + pci_set_word(&pci_config[PCI_DEVICE_ID], val); +} + +static inline void +pci_config_set_revision(uint8_t *pci_config, uint8_t val) +{ + pci_set_byte(&pci_config[PCI_REVISION_ID], val); +} + +static inline void +pci_config_set_class(uint8_t *pci_config, uint16_t val) +{ + pci_set_word(&pci_config[PCI_CLASS_DEVICE], val); +} + +static inline void +pci_config_set_prog_interface(uint8_t *pci_config, uint8_t val) +{ + pci_set_byte(&pci_config[PCI_CLASS_PROG], val); +} + +static inline void +pci_config_set_interrupt_pin(uint8_t *pci_config, uint8_t val) +{ + pci_set_byte(&pci_config[PCI_INTERRUPT_PIN], val); +} + +/* + * helper functions to do bit mask operation on configuration space. + * Just to set bit, use test-and-set and discard returned value. + * Just to clear bit, use test-and-clear and discard returned value. + * NOTE: They aren't atomic. + */ +static inline uint8_t +pci_byte_test_and_clear_mask(uint8_t *config, uint8_t mask) +{ + uint8_t val = pci_get_byte(config); + pci_set_byte(config, val & ~mask); + return val & mask; +} + +static inline uint8_t +pci_byte_test_and_set_mask(uint8_t *config, uint8_t mask) +{ + uint8_t val = pci_get_byte(config); + pci_set_byte(config, val | mask); + return val & mask; +} + +static inline uint16_t +pci_word_test_and_clear_mask(uint8_t *config, uint16_t mask) +{ + uint16_t val = pci_get_word(config); + pci_set_word(config, val & ~mask); + return val & mask; +} + +static inline uint16_t +pci_word_test_and_set_mask(uint8_t *config, uint16_t mask) +{ + uint16_t val = pci_get_word(config); + pci_set_word(config, val | mask); + return val & mask; +} + +static inline uint32_t +pci_long_test_and_clear_mask(uint8_t *config, uint32_t mask) +{ + uint32_t val = pci_get_long(config); + pci_set_long(config, val & ~mask); + return val & mask; +} + +static inline uint32_t +pci_long_test_and_set_mask(uint8_t *config, uint32_t mask) +{ + uint32_t val = pci_get_long(config); + pci_set_long(config, val | mask); + return val & mask; +} + +static inline uint64_t +pci_quad_test_and_clear_mask(uint8_t *config, uint64_t mask) +{ + uint64_t val = pci_get_quad(config); + pci_set_quad(config, val & ~mask); + return val & mask; +} + +static inline uint64_t +pci_quad_test_and_set_mask(uint8_t *config, uint64_t mask) +{ + uint64_t val = pci_get_quad(config); + pci_set_quad(config, val | mask); + return val & mask; +} + +/* Access a register specified by a mask */ +static inline void +pci_set_byte_by_mask(uint8_t *config, uint8_t mask, uint8_t reg) +{ + uint8_t val = pci_get_byte(config); + uint8_t rval = reg << (ffs(mask) - 1); + pci_set_byte(config, (~mask & val) | (mask & rval)); +} + +static inline uint8_t +pci_get_byte_by_mask(uint8_t *config, uint8_t mask) +{ + uint8_t val = pci_get_byte(config); + return (val & mask) >> (ffs(mask) - 1); +} + +static inline void +pci_set_word_by_mask(uint8_t *config, uint16_t mask, uint16_t reg) +{ + uint16_t val = pci_get_word(config); + uint16_t rval = reg << (ffs(mask) - 1); + pci_set_word(config, (~mask & val) | (mask & rval)); +} + +static inline uint16_t +pci_get_word_by_mask(uint8_t *config, uint16_t mask) +{ + uint16_t val = pci_get_word(config); + return (val & mask) >> (ffs(mask) - 1); +} + +static inline void +pci_set_long_by_mask(uint8_t *config, uint32_t mask, uint32_t reg) +{ + uint32_t val = pci_get_long(config); + uint32_t rval = reg << (ffs(mask) - 1); + pci_set_long(config, (~mask & val) | (mask & rval)); +} + +static inline uint32_t +pci_get_long_by_mask(uint8_t *config, uint32_t mask) +{ + uint32_t val = pci_get_long(config); + return (val & mask) >> (ffs(mask) - 1); +} + +static inline void +pci_set_quad_by_mask(uint8_t *config, uint64_t mask, uint64_t reg) +{ + uint64_t val = pci_get_quad(config); + uint64_t rval = reg << (ffs(mask) - 1); + pci_set_quad(config, (~mask & val) | (mask & rval)); +} + +static inline uint64_t +pci_get_quad_by_mask(uint8_t *config, uint64_t mask) +{ + uint64_t val = pci_get_quad(config); + return (val & mask) >> (ffs(mask) - 1); +} + +PCIDevice *pci_create_multifunction(PCIBus *bus, int devfn, bool multifunction, + const char *name); +PCIDevice *pci_create_simple_multifunction(PCIBus *bus, int devfn, + bool multifunction, + const char *name); +PCIDevice *pci_create(PCIBus *bus, int devfn, const char *name); +PCIDevice *pci_create_simple(PCIBus *bus, int devfn, const char *name); + +static inline int pci_is_express(const PCIDevice *d) +{ + return d->cap_present & QEMU_PCI_CAP_EXPRESS; +} + +static inline uint32_t pci_config_size(const PCIDevice *d) +{ + return pci_is_express(d) ? PCIE_CONFIG_SPACE_SIZE : PCI_CONFIG_SPACE_SIZE; +} + +/* DMA access functions */ +static inline DMAContext *pci_dma_context(PCIDevice *dev) +{ + return dev->dma; +} + +static inline int pci_dma_rw(PCIDevice *dev, dma_addr_t addr, + void *buf, dma_addr_t len, DMADirection dir) +{ + dma_memory_rw(pci_dma_context(dev), addr, buf, len, dir); + return 0; +} + +static inline int pci_dma_read(PCIDevice *dev, dma_addr_t addr, + void *buf, dma_addr_t len) +{ + return pci_dma_rw(dev, addr, buf, len, DMA_DIRECTION_TO_DEVICE); +} + +static inline int pci_dma_write(PCIDevice *dev, dma_addr_t addr, + const void *buf, dma_addr_t len) +{ + return pci_dma_rw(dev, addr, (void *) buf, len, DMA_DIRECTION_FROM_DEVICE); +} + +#define PCI_DMA_DEFINE_LDST(_l, _s, _bits) \ + static inline uint##_bits##_t ld##_l##_pci_dma(PCIDevice *dev, \ + dma_addr_t addr) \ + { \ + return ld##_l##_dma(pci_dma_context(dev), addr); \ + } \ + static inline void st##_s##_pci_dma(PCIDevice *dev, \ + dma_addr_t addr, uint##_bits##_t val) \ + { \ + st##_s##_dma(pci_dma_context(dev), addr, val); \ + } + +PCI_DMA_DEFINE_LDST(ub, b, 8); +PCI_DMA_DEFINE_LDST(uw_le, w_le, 16) +PCI_DMA_DEFINE_LDST(l_le, l_le, 32); +PCI_DMA_DEFINE_LDST(q_le, q_le, 64); +PCI_DMA_DEFINE_LDST(uw_be, w_be, 16) +PCI_DMA_DEFINE_LDST(l_be, l_be, 32); +PCI_DMA_DEFINE_LDST(q_be, q_be, 64); + +#undef PCI_DMA_DEFINE_LDST + +static inline void *pci_dma_map(PCIDevice *dev, dma_addr_t addr, + dma_addr_t *plen, DMADirection dir) +{ + void *buf; + + buf = dma_memory_map(pci_dma_context(dev), addr, plen, dir); + return buf; +} + +static inline void pci_dma_unmap(PCIDevice *dev, void *buffer, dma_addr_t len, + DMADirection dir, dma_addr_t access_len) +{ + dma_memory_unmap(pci_dma_context(dev), buffer, len, dir, access_len); +} + +static inline void pci_dma_sglist_init(QEMUSGList *qsg, PCIDevice *dev, + int alloc_hint) +{ + qemu_sglist_init(qsg, alloc_hint, pci_dma_context(dev)); +} + +extern const VMStateDescription vmstate_pci_device; + +#define VMSTATE_PCI_DEVICE(_field, _state) { \ + .name = (stringify(_field)), \ + .size = sizeof(PCIDevice), \ + .vmsd = &vmstate_pci_device, \ + .flags = VMS_STRUCT, \ + .offset = vmstate_offset_value(_state, _field, PCIDevice), \ +} + +#define VMSTATE_PCI_DEVICE_POINTER(_field, _state) { \ + .name = (stringify(_field)), \ + .size = sizeof(PCIDevice), \ + .vmsd = &vmstate_pci_device, \ + .flags = VMS_STRUCT|VMS_POINTER, \ + .offset = vmstate_offset_pointer(_state, _field, PCIDevice), \ +} + +#endif diff --git a/hw/pci/pci_bridge.c b/hw/pci/pci_bridge.c new file mode 100644 index 0000000000..4680501e4e --- /dev/null +++ b/hw/pci/pci_bridge.c @@ -0,0 +1,363 @@ +/* + * QEMU PCI bus manager + * + * Copyright (c) 2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to dea + + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM + + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +/* + * split out from pci.c + * Copyright (c) 2010 Isaku Yamahata + * VA Linux Systems Japan K.K. + */ + +#include "pci_bridge.h" +#include "pci_internals.h" +#include "range.h" + +/* PCI bridge subsystem vendor ID helper functions */ +#define PCI_SSVID_SIZEOF 8 +#define PCI_SSVID_SVID 4 +#define PCI_SSVID_SSID 6 + +int pci_bridge_ssvid_init(PCIDevice *dev, uint8_t offset, + uint16_t svid, uint16_t ssid) +{ + int pos; + pos = pci_add_capability(dev, PCI_CAP_ID_SSVID, offset, PCI_SSVID_SIZEOF); + if (pos < 0) { + return pos; + } + + pci_set_word(dev->config + pos + PCI_SSVID_SVID, svid); + pci_set_word(dev->config + pos + PCI_SSVID_SSID, ssid); + return pos; +} + +/* Accessor function to get parent bridge device from pci bus. */ +PCIDevice *pci_bridge_get_device(PCIBus *bus) +{ + return bus->parent_dev; +} + +/* Accessor function to get secondary bus from pci-to-pci bridge device */ +PCIBus *pci_bridge_get_sec_bus(PCIBridge *br) +{ + return &br->sec_bus; +} + +static uint32_t pci_config_get_io_base(const PCIDevice *d, + uint32_t base, uint32_t base_upper16) +{ + uint32_t val; + + val = ((uint32_t)d->config[base] & PCI_IO_RANGE_MASK) << 8; + if (d->config[base] & PCI_IO_RANGE_TYPE_32) { + val |= (uint32_t)pci_get_word(d->config + base_upper16) << 16; + } + return val; +} + +static pcibus_t pci_config_get_memory_base(const PCIDevice *d, uint32_t base) +{ + return ((pcibus_t)pci_get_word(d->config + base) & PCI_MEMORY_RANGE_MASK) + << 16; +} + +static pcibus_t pci_config_get_pref_base(const PCIDevice *d, + uint32_t base, uint32_t upper) +{ + pcibus_t tmp; + pcibus_t val; + + tmp = (pcibus_t)pci_get_word(d->config + base); + val = (tmp & PCI_PREF_RANGE_MASK) << 16; + if (tmp & PCI_PREF_RANGE_TYPE_64) { + val |= (pcibus_t)pci_get_long(d->config + upper) << 32; + } + return val; +} + +/* accessor function to get bridge filtering base address */ +pcibus_t pci_bridge_get_base(const PCIDevice *bridge, uint8_t type) +{ + pcibus_t base; + if (type & PCI_BASE_ADDRESS_SPACE_IO) { + base = pci_config_get_io_base(bridge, + PCI_IO_BASE, PCI_IO_BASE_UPPER16); + } else { + if (type & PCI_BASE_ADDRESS_MEM_PREFETCH) { + base = pci_config_get_pref_base( + bridge, PCI_PREF_MEMORY_BASE, PCI_PREF_BASE_UPPER32); + } else { + base = pci_config_get_memory_base(bridge, PCI_MEMORY_BASE); + } + } + + return base; +} + +/* accessor funciton to get bridge filtering limit */ +pcibus_t pci_bridge_get_limit(const PCIDevice *bridge, uint8_t type) +{ + pcibus_t limit; + if (type & PCI_BASE_ADDRESS_SPACE_IO) { + limit = pci_config_get_io_base(bridge, + PCI_IO_LIMIT, PCI_IO_LIMIT_UPPER16); + limit |= 0xfff; /* PCI bridge spec 3.2.5.6. */ + } else { + if (type & PCI_BASE_ADDRESS_MEM_PREFETCH) { + limit = pci_config_get_pref_base( + bridge, PCI_PREF_MEMORY_LIMIT, PCI_PREF_LIMIT_UPPER32); + } else { + limit = pci_config_get_memory_base(bridge, PCI_MEMORY_LIMIT); + } + limit |= 0xfffff; /* PCI bridge spec 3.2.5.{1, 8}. */ + } + return limit; +} + +static void pci_bridge_init_alias(PCIBridge *bridge, MemoryRegion *alias, + uint8_t type, const char *name, + MemoryRegion *space, + MemoryRegion *parent_space, + bool enabled) +{ + pcibus_t base = pci_bridge_get_base(&bridge->dev, type); + pcibus_t limit = pci_bridge_get_limit(&bridge->dev, type); + /* TODO: this doesn't handle base = 0 limit = 2^64 - 1 correctly. + * Apparently no way to do this with existing memory APIs. */ + pcibus_t size = enabled && limit >= base ? limit + 1 - base : 0; + + memory_region_init_alias(alias, name, space, base, size); + memory_region_add_subregion_overlap(parent_space, base, alias, 1); +} + +static PCIBridgeWindows *pci_bridge_region_init(PCIBridge *br) +{ + PCIBus *parent = br->dev.bus; + PCIBridgeWindows *w = g_new(PCIBridgeWindows, 1); + uint16_t cmd = pci_get_word(br->dev.config + PCI_COMMAND); + + pci_bridge_init_alias(br, &w->alias_pref_mem, + PCI_BASE_ADDRESS_MEM_PREFETCH, + "pci_bridge_pref_mem", + &br->address_space_mem, + parent->address_space_mem, + cmd & PCI_COMMAND_MEMORY); + pci_bridge_init_alias(br, &w->alias_mem, + PCI_BASE_ADDRESS_SPACE_MEMORY, + "pci_bridge_mem", + &br->address_space_mem, + parent->address_space_mem, + cmd & PCI_COMMAND_MEMORY); + pci_bridge_init_alias(br, &w->alias_io, + PCI_BASE_ADDRESS_SPACE_IO, + "pci_bridge_io", + &br->address_space_io, + parent->address_space_io, + cmd & PCI_COMMAND_IO); + /* TODO: optinal VGA and VGA palette snooping support. */ + + return w; +} + +static void pci_bridge_region_del(PCIBridge *br, PCIBridgeWindows *w) +{ + PCIBus *parent = br->dev.bus; + + memory_region_del_subregion(parent->address_space_io, &w->alias_io); + memory_region_del_subregion(parent->address_space_mem, &w->alias_mem); + memory_region_del_subregion(parent->address_space_mem, &w->alias_pref_mem); +} + +static void pci_bridge_region_cleanup(PCIBridge *br, PCIBridgeWindows *w) +{ + memory_region_destroy(&w->alias_io); + memory_region_destroy(&w->alias_mem); + memory_region_destroy(&w->alias_pref_mem); + g_free(w); +} + +static void pci_bridge_update_mappings(PCIBridge *br) +{ + PCIBridgeWindows *w = br->windows; + + /* Make updates atomic to: handle the case of one VCPU updating the bridge + * while another accesses an unaffected region. */ + memory_region_transaction_begin(); + pci_bridge_region_del(br, br->windows); + br->windows = pci_bridge_region_init(br); + memory_region_transaction_commit(); + pci_bridge_region_cleanup(br, w); +} + +/* default write_config function for PCI-to-PCI bridge */ +void pci_bridge_write_config(PCIDevice *d, + uint32_t address, uint32_t val, int len) +{ + PCIBridge *s = container_of(d, PCIBridge, dev); + uint16_t oldctl = pci_get_word(d->config + PCI_BRIDGE_CONTROL); + uint16_t newctl; + + pci_default_write_config(d, address, val, len); + + if (ranges_overlap(address, len, PCI_COMMAND, 2) || + + /* io base/limit */ + ranges_overlap(address, len, PCI_IO_BASE, 2) || + + /* memory base/limit, prefetchable base/limit and + io base/limit upper 16 */ + ranges_overlap(address, len, PCI_MEMORY_BASE, 20)) { + pci_bridge_update_mappings(s); + } + + newctl = pci_get_word(d->config + PCI_BRIDGE_CONTROL); + if (~oldctl & newctl & PCI_BRIDGE_CTL_BUS_RESET) { + /* Trigger hot reset on 0->1 transition. */ + pci_bus_reset(&s->sec_bus); + } +} + +void pci_bridge_disable_base_limit(PCIDevice *dev) +{ + uint8_t *conf = dev->config; + + pci_byte_test_and_set_mask(conf + PCI_IO_BASE, + PCI_IO_RANGE_MASK & 0xff); + pci_byte_test_and_clear_mask(conf + PCI_IO_LIMIT, + PCI_IO_RANGE_MASK & 0xff); + pci_word_test_and_set_mask(conf + PCI_MEMORY_BASE, + PCI_MEMORY_RANGE_MASK & 0xffff); + pci_word_test_and_clear_mask(conf + PCI_MEMORY_LIMIT, + PCI_MEMORY_RANGE_MASK & 0xffff); + pci_word_test_and_set_mask(conf + PCI_PREF_MEMORY_BASE, + PCI_PREF_RANGE_MASK & 0xffff); + pci_word_test_and_clear_mask(conf + PCI_PREF_MEMORY_LIMIT, + PCI_PREF_RANGE_MASK & 0xffff); + pci_set_long(conf + PCI_PREF_BASE_UPPER32, 0); + pci_set_long(conf + PCI_PREF_LIMIT_UPPER32, 0); +} + +/* reset bridge specific configuration registers */ +void pci_bridge_reset(DeviceState *qdev) +{ + PCIDevice *dev = PCI_DEVICE(qdev); + uint8_t *conf = dev->config; + + conf[PCI_PRIMARY_BUS] = 0; + conf[PCI_SECONDARY_BUS] = 0; + conf[PCI_SUBORDINATE_BUS] = 0; + conf[PCI_SEC_LATENCY_TIMER] = 0; + + /* + * the default values for base/limit registers aren't specified + * in the PCI-to-PCI-bridge spec. So we don't thouch them here. + * Each implementation can override it. + * typical implementation does + * zero base/limit registers or + * disable forwarding: pci_bridge_disable_base_limit() + * If disable forwarding is wanted, call pci_bridge_disable_base_limit() + * after this function. + */ + pci_byte_test_and_clear_mask(conf + PCI_IO_BASE, + PCI_IO_RANGE_MASK & 0xff); + pci_byte_test_and_clear_mask(conf + PCI_IO_LIMIT, + PCI_IO_RANGE_MASK & 0xff); + pci_word_test_and_clear_mask(conf + PCI_MEMORY_BASE, + PCI_MEMORY_RANGE_MASK & 0xffff); + pci_word_test_and_clear_mask(conf + PCI_MEMORY_LIMIT, + PCI_MEMORY_RANGE_MASK & 0xffff); + pci_word_test_and_clear_mask(conf + PCI_PREF_MEMORY_BASE, + PCI_PREF_RANGE_MASK & 0xffff); + pci_word_test_and_clear_mask(conf + PCI_PREF_MEMORY_LIMIT, + PCI_PREF_RANGE_MASK & 0xffff); + pci_set_long(conf + PCI_PREF_BASE_UPPER32, 0); + pci_set_long(conf + PCI_PREF_LIMIT_UPPER32, 0); + + pci_set_word(conf + PCI_BRIDGE_CONTROL, 0); +} + +/* default qdev initialization function for PCI-to-PCI bridge */ +int pci_bridge_initfn(PCIDevice *dev) +{ + PCIBus *parent = dev->bus; + PCIBridge *br = DO_UPCAST(PCIBridge, dev, dev); + PCIBus *sec_bus = &br->sec_bus; + + pci_word_test_and_set_mask(dev->config + PCI_STATUS, + PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK); + pci_config_set_class(dev->config, PCI_CLASS_BRIDGE_PCI); + dev->config[PCI_HEADER_TYPE] = + (dev->config[PCI_HEADER_TYPE] & PCI_HEADER_TYPE_MULTI_FUNCTION) | + PCI_HEADER_TYPE_BRIDGE; + pci_set_word(dev->config + PCI_SEC_STATUS, + PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK); + + /* + * If we don't specify the name, the bus will be addressed as .0, where + * id is the device id. + * Since PCI Bridge devices have a single bus each, we don't need the index: + * let users address the bus using the device name. + */ + if (!br->bus_name && dev->qdev.id && *dev->qdev.id) { + br->bus_name = dev->qdev.id; + } + + qbus_create_inplace(&sec_bus->qbus, TYPE_PCI_BUS, &dev->qdev, + br->bus_name); + sec_bus->parent_dev = dev; + sec_bus->map_irq = br->map_irq; + sec_bus->address_space_mem = &br->address_space_mem; + memory_region_init(&br->address_space_mem, "pci_bridge_pci", INT64_MAX); + sec_bus->address_space_io = &br->address_space_io; + memory_region_init(&br->address_space_io, "pci_bridge_io", 65536); + br->windows = pci_bridge_region_init(br); + QLIST_INIT(&sec_bus->child); + QLIST_INSERT_HEAD(&parent->child, sec_bus, sibling); + return 0; +} + +/* default qdev clean up function for PCI-to-PCI bridge */ +void pci_bridge_exitfn(PCIDevice *pci_dev) +{ + PCIBridge *s = DO_UPCAST(PCIBridge, dev, pci_dev); + assert(QLIST_EMPTY(&s->sec_bus.child)); + QLIST_REMOVE(&s->sec_bus, sibling); + pci_bridge_region_del(s, s->windows); + pci_bridge_region_cleanup(s, s->windows); + memory_region_destroy(&s->address_space_mem); + memory_region_destroy(&s->address_space_io); + /* qbus_free() is called automatically by qdev_free() */ +} + +/* + * before qdev initialization(qdev_init()), this function sets bus_name and + * map_irq callback which are necessry for pci_bridge_initfn() to + * initialize bus. + */ +void pci_bridge_map_irq(PCIBridge *br, const char* bus_name, + pci_map_irq_fn map_irq) +{ + br->map_irq = map_irq; + br->bus_name = bus_name; +} diff --git a/hw/pci/pci_bridge.h b/hw/pci/pci_bridge.h new file mode 100644 index 0000000000..a00accc172 --- /dev/null +++ b/hw/pci/pci_bridge.h @@ -0,0 +1,66 @@ +/* + * QEMU PCI bridge + * + * Copyright (c) 2004 Fabrice Bellard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * split out pci bus specific stuff from pci.[hc] to pci_bridge.[hc] + * Copyright (c) 2009 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + */ + +#ifndef QEMU_PCI_BRIDGE_H +#define QEMU_PCI_BRIDGE_H + +#include "pci.h" + +int pci_bridge_ssvid_init(PCIDevice *dev, uint8_t offset, + uint16_t svid, uint16_t ssid); + +PCIDevice *pci_bridge_get_device(PCIBus *bus); +PCIBus *pci_bridge_get_sec_bus(PCIBridge *br); + +pcibus_t pci_bridge_get_base(const PCIDevice *bridge, uint8_t type); +pcibus_t pci_bridge_get_limit(const PCIDevice *bridge, uint8_t type); + +void pci_bridge_write_config(PCIDevice *d, + uint32_t address, uint32_t val, int len); +void pci_bridge_disable_base_limit(PCIDevice *dev); +void pci_bridge_reset_reg(PCIDevice *dev); +void pci_bridge_reset(DeviceState *qdev); + +int pci_bridge_initfn(PCIDevice *pci_dev); +void pci_bridge_exitfn(PCIDevice *pci_dev); + + +/* + * before qdev initialization(qdev_init()), this function sets bus_name and + * map_irq callback which are necessry for pci_bridge_initfn() to + * initialize bus. + */ +void pci_bridge_map_irq(PCIBridge *br, const char* bus_name, + pci_map_irq_fn map_irq); + +#endif /* QEMU_PCI_BRIDGE_H */ +/* + * Local variables: + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 8 + * indent-tab-mode: nil + * End: + */ diff --git a/hw/pci/pci_host.c b/hw/pci/pci_host.c new file mode 100644 index 0000000000..68e328cd23 --- /dev/null +++ b/hw/pci/pci_host.c @@ -0,0 +1,180 @@ +/* + * pci_host.c + * + * Copyright (c) 2009 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "pci.h" +#include "pci_host.h" + +/* debug PCI */ +//#define DEBUG_PCI + +#ifdef DEBUG_PCI +#define PCI_DPRINTF(fmt, ...) \ +do { printf("pci_host_data: " fmt , ## __VA_ARGS__); } while (0) +#else +#define PCI_DPRINTF(fmt, ...) +#endif + +/* + * PCI address + * bit 16 - 24: bus number + * bit 8 - 15: devfun number + * bit 0 - 7: offset in configuration space of a given pci device + */ + +/* the helper functio to get a PCIDeice* for a given pci address */ +static inline PCIDevice *pci_dev_find_by_addr(PCIBus *bus, uint32_t addr) +{ + uint8_t bus_num = addr >> 16; + uint8_t devfn = addr >> 8; + + return pci_find_device(bus, bus_num, devfn); +} + +void pci_host_config_write_common(PCIDevice *pci_dev, uint32_t addr, + uint32_t limit, uint32_t val, uint32_t len) +{ + assert(len <= 4); + pci_dev->config_write(pci_dev, addr, val, MIN(len, limit - addr)); +} + +uint32_t pci_host_config_read_common(PCIDevice *pci_dev, uint32_t addr, + uint32_t limit, uint32_t len) +{ + assert(len <= 4); + return pci_dev->config_read(pci_dev, addr, MIN(len, limit - addr)); +} + +void pci_data_write(PCIBus *s, uint32_t addr, uint32_t val, int len) +{ + PCIDevice *pci_dev = pci_dev_find_by_addr(s, addr); + uint32_t config_addr = addr & (PCI_CONFIG_SPACE_SIZE - 1); + + if (!pci_dev) { + return; + } + + PCI_DPRINTF("%s: %s: addr=%02" PRIx32 " val=%08" PRIx32 " len=%d\n", + __func__, pci_dev->name, config_addr, val, len); + pci_host_config_write_common(pci_dev, config_addr, PCI_CONFIG_SPACE_SIZE, + val, len); +} + +uint32_t pci_data_read(PCIBus *s, uint32_t addr, int len) +{ + PCIDevice *pci_dev = pci_dev_find_by_addr(s, addr); + uint32_t config_addr = addr & (PCI_CONFIG_SPACE_SIZE - 1); + uint32_t val; + + if (!pci_dev) { + return ~0x0; + } + + val = pci_host_config_read_common(pci_dev, config_addr, + PCI_CONFIG_SPACE_SIZE, len); + PCI_DPRINTF("%s: %s: addr=%02"PRIx32" val=%08"PRIx32" len=%d\n", + __func__, pci_dev->name, config_addr, val, len); + + return val; +} + +static void pci_host_config_write(void *opaque, hwaddr addr, + uint64_t val, unsigned len) +{ + PCIHostState *s = opaque; + + PCI_DPRINTF("%s addr " TARGET_FMT_plx " len %d val %"PRIx64"\n", + __func__, addr, len, val); + if (addr != 0 || len != 4) { + return; + } + s->config_reg = val; +} + +static uint64_t pci_host_config_read(void *opaque, hwaddr addr, + unsigned len) +{ + PCIHostState *s = opaque; + uint32_t val = s->config_reg; + + PCI_DPRINTF("%s addr " TARGET_FMT_plx " len %d val %"PRIx32"\n", + __func__, addr, len, val); + return val; +} + +static void pci_host_data_write(void *opaque, hwaddr addr, + uint64_t val, unsigned len) +{ + PCIHostState *s = opaque; + PCI_DPRINTF("write addr " TARGET_FMT_plx " len %d val %x\n", + addr, len, (unsigned)val); + if (s->config_reg & (1u << 31)) + pci_data_write(s->bus, s->config_reg | (addr & 3), val, len); +} + +static uint64_t pci_host_data_read(void *opaque, + hwaddr addr, unsigned len) +{ + PCIHostState *s = opaque; + uint32_t val; + if (!(s->config_reg & (1 << 31))) + return 0xffffffff; + val = pci_data_read(s->bus, s->config_reg | (addr & 3), len); + PCI_DPRINTF("read addr " TARGET_FMT_plx " len %d val %x\n", + addr, len, val); + return val; +} + +const MemoryRegionOps pci_host_conf_le_ops = { + .read = pci_host_config_read, + .write = pci_host_config_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +const MemoryRegionOps pci_host_conf_be_ops = { + .read = pci_host_config_read, + .write = pci_host_config_write, + .endianness = DEVICE_BIG_ENDIAN, +}; + +const MemoryRegionOps pci_host_data_le_ops = { + .read = pci_host_data_read, + .write = pci_host_data_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +const MemoryRegionOps pci_host_data_be_ops = { + .read = pci_host_data_read, + .write = pci_host_data_write, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static const TypeInfo pci_host_type_info = { + .name = TYPE_PCI_HOST_BRIDGE, + .parent = TYPE_SYS_BUS_DEVICE, + .abstract = true, + .instance_size = sizeof(PCIHostState), +}; + +static void pci_host_register_types(void) +{ + type_register_static(&pci_host_type_info); +} + +type_init(pci_host_register_types) diff --git a/hw/pci/pci_host.h b/hw/pci/pci_host.h new file mode 100644 index 0000000000..4b9c300fcf --- /dev/null +++ b/hw/pci/pci_host.h @@ -0,0 +1,62 @@ +/* + * QEMU Common PCI Host bridge configuration data space access routines. + * + * Copyright (c) 2006 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Worker routines for a PCI host controller that uses an {address,data} + register pair to access PCI configuration space. */ + +#ifndef PCI_HOST_H +#define PCI_HOST_H + +#include "sysbus.h" + +#define TYPE_PCI_HOST_BRIDGE "pci-host-bridge" +#define PCI_HOST_BRIDGE(obj) \ + OBJECT_CHECK(PCIHostState, (obj), TYPE_PCI_HOST_BRIDGE) + +struct PCIHostState { + SysBusDevice busdev; + + MemoryRegion conf_mem; + MemoryRegion data_mem; + MemoryRegion mmcfg; + MemoryRegion *address_space; + uint32_t config_reg; + PCIBus *bus; +}; + +/* common internal helpers for PCI/PCIe hosts, cut off overflows */ +void pci_host_config_write_common(PCIDevice *pci_dev, uint32_t addr, + uint32_t limit, uint32_t val, uint32_t len); +uint32_t pci_host_config_read_common(PCIDevice *pci_dev, uint32_t addr, + uint32_t limit, uint32_t len); + +void pci_data_write(PCIBus *s, uint32_t addr, uint32_t val, int len); +uint32_t pci_data_read(PCIBus *s, uint32_t addr, int len); + +extern const MemoryRegionOps pci_host_conf_le_ops; +extern const MemoryRegionOps pci_host_conf_be_ops; +extern const MemoryRegionOps pci_host_data_le_ops; +extern const MemoryRegionOps pci_host_data_be_ops; + +#endif /* PCI_HOST_H */ diff --git a/hw/pci/pci_ids.h b/hw/pci/pci_ids.h new file mode 100644 index 0000000000..5df7245349 --- /dev/null +++ b/hw/pci/pci_ids.h @@ -0,0 +1,147 @@ +/* + * PCI Class, Vendor and Device IDs + * + * Please keep sorted. + * + * Abbreviated version of linux/pci_ids.h + * + * QEMU-specific definitions belong in pci.h + */ + +/* Device classes and subclasses */ + +#define PCI_BASE_CLASS_STORAGE 0x01 +#define PCI_BASE_CLASS_NETWORK 0x02 + +#define PCI_CLASS_STORAGE_SCSI 0x0100 +#define PCI_CLASS_STORAGE_IDE 0x0101 +#define PCI_CLASS_STORAGE_RAID 0x0104 +#define PCI_CLASS_STORAGE_SATA 0x0106 +#define PCI_CLASS_STORAGE_OTHER 0x0180 + +#define PCI_CLASS_NETWORK_ETHERNET 0x0200 + +#define PCI_CLASS_DISPLAY_VGA 0x0300 +#define PCI_CLASS_DISPLAY_OTHER 0x0380 + +#define PCI_CLASS_MULTIMEDIA_AUDIO 0x0401 + +#define PCI_CLASS_MEMORY_RAM 0x0500 + +#define PCI_CLASS_SYSTEM_OTHER 0x0880 + +#define PCI_CLASS_SERIAL_USB 0x0c03 +#define PCI_CLASS_SERIAL_SMBUS 0x0c05 + +#define PCI_CLASS_BRIDGE_HOST 0x0600 +#define PCI_CLASS_BRIDGE_ISA 0x0601 +#define PCI_CLASS_BRIDGE_PCI 0x0604 +#define PCI_CLASS_BRDIGE_PCI_INF_SUB 0x01 +#define PCI_CLASS_BRIDGE_OTHER 0x0680 + +#define PCI_CLASS_COMMUNICATION_SERIAL 0x0700 +#define PCI_CLASS_COMMUNICATION_OTHER 0x0780 + +#define PCI_CLASS_PROCESSOR_CO 0x0b40 +#define PCI_CLASS_PROCESSOR_POWERPC 0x0b20 + +#define PCI_CLASS_OTHERS 0xff + +/* Vendors and devices. Sort key: vendor first, device next. */ + +#define PCI_VENDOR_ID_LSI_LOGIC 0x1000 +#define PCI_DEVICE_ID_LSI_53C895A 0x0012 +#define PCI_DEVICE_ID_LSI_SAS1078 0x0060 + +#define PCI_VENDOR_ID_DEC 0x1011 +#define PCI_DEVICE_ID_DEC_21154 0x0026 + +#define PCI_VENDOR_ID_CIRRUS 0x1013 + +#define PCI_VENDOR_ID_IBM 0x1014 + +#define PCI_VENDOR_ID_AMD 0x1022 +#define PCI_DEVICE_ID_AMD_LANCE 0x2000 +#define PCI_DEVICE_ID_AMD_SCSI 0x2020 + +#define PCI_VENDOR_ID_TI 0x104c + +#define PCI_VENDOR_ID_MOTOROLA 0x1057 +#define PCI_DEVICE_ID_MOTOROLA_MPC106 0x0002 +#define PCI_DEVICE_ID_MOTOROLA_RAVEN 0x4801 + +#define PCI_VENDOR_ID_APPLE 0x106b +#define PCI_DEVICE_ID_APPLE_UNI_N_AGP 0x0020 +#define PCI_DEVICE_ID_APPLE_U3_AGP 0x004b + +#define PCI_VENDOR_ID_SUN 0x108e +#define PCI_DEVICE_ID_SUN_EBUS 0x1000 +#define PCI_DEVICE_ID_SUN_SIMBA 0x5000 +#define PCI_DEVICE_ID_SUN_SABRE 0xa000 + +#define PCI_VENDOR_ID_CMD 0x1095 +#define PCI_DEVICE_ID_CMD_646 0x0646 + +#define PCI_VENDOR_ID_REALTEK 0x10ec +#define PCI_DEVICE_ID_REALTEK_8139 0x8139 + +#define PCI_VENDOR_ID_XILINX 0x10ee + +#define PCI_VENDOR_ID_VIA 0x1106 +#define PCI_DEVICE_ID_VIA_ISA_BRIDGE 0x0686 +#define PCI_DEVICE_ID_VIA_IDE 0x0571 +#define PCI_DEVICE_ID_VIA_UHCI 0x3038 +#define PCI_DEVICE_ID_VIA_ACPI 0x3057 +#define PCI_DEVICE_ID_VIA_AC97 0x3058 +#define PCI_DEVICE_ID_VIA_MC97 0x3068 + +#define PCI_VENDOR_ID_MARVELL 0x11ab + +#define PCI_VENDOR_ID_ENSONIQ 0x1274 +#define PCI_DEVICE_ID_ENSONIQ_ES1370 0x5000 + +#define PCI_VENDOR_ID_FREESCALE 0x1957 +#define PCI_DEVICE_ID_MPC8533E 0x0030 + +#define PCI_VENDOR_ID_INTEL 0x8086 +#define PCI_DEVICE_ID_INTEL_82378 0x0484 +#define PCI_DEVICE_ID_INTEL_82441 0x1237 +#define PCI_DEVICE_ID_INTEL_82801AA_5 0x2415 +#define PCI_DEVICE_ID_INTEL_82801BA_11 0x244e +#define PCI_DEVICE_ID_INTEL_82801D 0x24CD +#define PCI_DEVICE_ID_INTEL_ESB_9 0x25ab +#define PCI_DEVICE_ID_INTEL_82371SB_0 0x7000 +#define PCI_DEVICE_ID_INTEL_82371SB_1 0x7010 +#define PCI_DEVICE_ID_INTEL_82371SB_2 0x7020 +#define PCI_DEVICE_ID_INTEL_82371AB_0 0x7110 +#define PCI_DEVICE_ID_INTEL_82371AB 0x7111 +#define PCI_DEVICE_ID_INTEL_82371AB_2 0x7112 +#define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113 + +#define PCI_DEVICE_ID_INTEL_ICH9_0 0x2910 +#define PCI_DEVICE_ID_INTEL_ICH9_1 0x2917 +#define PCI_DEVICE_ID_INTEL_ICH9_2 0x2912 +#define PCI_DEVICE_ID_INTEL_ICH9_3 0x2913 +#define PCI_DEVICE_ID_INTEL_ICH9_4 0x2914 +#define PCI_DEVICE_ID_INTEL_ICH9_5 0x2919 +#define PCI_DEVICE_ID_INTEL_ICH9_6 0x2930 +#define PCI_DEVICE_ID_INTEL_ICH9_7 0x2916 +#define PCI_DEVICE_ID_INTEL_ICH9_8 0x2918 + +#define PCI_DEVICE_ID_INTEL_82801I_UHCI1 0x2934 +#define PCI_DEVICE_ID_INTEL_82801I_UHCI2 0x2935 +#define PCI_DEVICE_ID_INTEL_82801I_UHCI3 0x2936 +#define PCI_DEVICE_ID_INTEL_82801I_UHCI4 0x2937 +#define PCI_DEVICE_ID_INTEL_82801I_UHCI5 0x2938 +#define PCI_DEVICE_ID_INTEL_82801I_UHCI6 0x2939 +#define PCI_DEVICE_ID_INTEL_82801I_EHCI1 0x293a +#define PCI_DEVICE_ID_INTEL_82801I_EHCI2 0x293c +#define PCI_DEVICE_ID_INTEL_82599_SFP_VF 0x10ed + +#define PCI_DEVICE_ID_INTEL_Q35_MCH 0x29c0 + +#define PCI_VENDOR_ID_XEN 0x5853 +#define PCI_DEVICE_ID_XEN_PLATFORM 0x0001 + +#define PCI_VENDOR_ID_NEC 0x1033 +#define PCI_DEVICE_ID_NEC_UPD720200 0x0194 diff --git a/hw/pci/pci_internals.h b/hw/pci/pci_internals.h new file mode 100644 index 0000000000..21d0ce6973 --- /dev/null +++ b/hw/pci/pci_internals.h @@ -0,0 +1,78 @@ +#ifndef QEMU_PCI_INTERNALS_H +#define QEMU_PCI_INTERNALS_H + +/* + * This header files is private to pci.c and pci_bridge.c + * So following structures are opaque to others and shouldn't be + * accessed. + * + * For pci-to-pci bridge needs to include this header file to embed + * PCIBridge in its structure or to get sizeof(PCIBridge), + * However, they shouldn't access those following members directly. + * Use accessor function in pci.h, pci_bridge.h + */ + +#define TYPE_PCI_BUS "PCI" +#define PCI_BUS(obj) OBJECT_CHECK(PCIBus, (obj), TYPE_PCI_BUS) + +struct PCIBus { + BusState qbus; + PCIDMAContextFunc dma_context_fn; + void *dma_context_opaque; + uint8_t devfn_min; + pci_set_irq_fn set_irq; + pci_map_irq_fn map_irq; + pci_route_irq_fn route_intx_to_irq; + pci_hotplug_fn hotplug; + DeviceState *hotplug_qdev; + void *irq_opaque; + PCIDevice *devices[PCI_SLOT_MAX * PCI_FUNC_MAX]; + PCIDevice *parent_dev; + MemoryRegion *address_space_mem; + MemoryRegion *address_space_io; + + QLIST_HEAD(, PCIBus) child; /* this will be replaced by qdev later */ + QLIST_ENTRY(PCIBus) sibling;/* this will be replaced by qdev later */ + + /* The bus IRQ state is the logical OR of the connected devices. + Keep a count of the number of devices with raised IRQs. */ + int nirq; + int *irq_count; +}; + +typedef struct PCIBridgeWindows PCIBridgeWindows; + +/* + * Aliases for each of the address space windows that the bridge + * can forward. Mapped into the bridge's parent's address space, + * as subregions. + */ +struct PCIBridgeWindows { + MemoryRegion alias_pref_mem; + MemoryRegion alias_mem; + MemoryRegion alias_io; +}; + +struct PCIBridge { + PCIDevice dev; + + /* private member */ + PCIBus sec_bus; + /* + * Memory regions for the bridge's address spaces. These regions are not + * directly added to system_memory/system_io or its descendants. + * Bridge's secondary bus points to these, so that devices + * under the bridge see these regions as its address spaces. + * The regions are as large as the entire address space - + * they don't take into account any windows. + */ + MemoryRegion address_space_mem; + MemoryRegion address_space_io; + + PCIBridgeWindows *windows; + + pci_map_irq_fn map_irq; + const char *bus_name; +}; + +#endif /* QEMU_PCI_INTERNALS_H */ diff --git a/hw/pci/pci_regs.h b/hw/pci/pci_regs.h new file mode 100644 index 0000000000..56a404be6e --- /dev/null +++ b/hw/pci/pci_regs.h @@ -0,0 +1,717 @@ +/* + * pci_regs.h + * + * PCI standard defines + * Copyright 1994, Drew Eckhardt + * Copyright 1997--1999 Martin Mares + * + * For more information, please consult the following manuals (look at + * http://www.pcisig.com/ for how to get them): + * + * PCI BIOS Specification + * PCI Local Bus Specification + * PCI to PCI Bridge Specification + * PCI System Design Guide + * + * For hypertransport information, please consult the following manuals + * from http://www.hypertransport.org + * + * The Hypertransport I/O Link Specification + */ + +#ifndef LINUX_PCI_REGS_H +#define LINUX_PCI_REGS_H + +/* + * Under PCI, each device has 256 bytes of configuration address space, + * of which the first 64 bytes are standardized as follows: + */ +#define PCI_VENDOR_ID 0x00 /* 16 bits */ +#define PCI_DEVICE_ID 0x02 /* 16 bits */ +#define PCI_COMMAND 0x04 /* 16 bits */ +#define PCI_COMMAND_IO 0x1 /* Enable response in I/O space */ +#define PCI_COMMAND_MEMORY 0x2 /* Enable response in Memory space */ +#define PCI_COMMAND_MASTER 0x4 /* Enable bus mastering */ +#define PCI_COMMAND_SPECIAL 0x8 /* Enable response to special cycles */ +#define PCI_COMMAND_INVALIDATE 0x10 /* Use memory write and invalidate */ +#define PCI_COMMAND_VGA_PALETTE 0x20 /* Enable palette snooping */ +#define PCI_COMMAND_PARITY 0x40 /* Enable parity checking */ +#define PCI_COMMAND_WAIT 0x80 /* Enable address/data stepping */ +#define PCI_COMMAND_SERR 0x100 /* Enable SERR */ +#define PCI_COMMAND_FAST_BACK 0x200 /* Enable back-to-back writes */ +#define PCI_COMMAND_INTX_DISABLE 0x400 /* INTx Emulation Disable */ + +#define PCI_STATUS 0x06 /* 16 bits */ +#define PCI_STATUS_INTERRUPT 0x08 /* Interrupt status */ +#define PCI_STATUS_CAP_LIST 0x10 /* Support Capability List */ +#define PCI_STATUS_66MHZ 0x20 /* Support 66 Mhz PCI 2.1 bus */ +#define PCI_STATUS_UDF 0x40 /* Support User Definable Features [obsolete] */ +#define PCI_STATUS_FAST_BACK 0x80 /* Accept fast-back to back */ +#define PCI_STATUS_PARITY 0x100 /* Detected parity error */ +#define PCI_STATUS_DEVSEL_MASK 0x600 /* DEVSEL timing */ +#define PCI_STATUS_DEVSEL_FAST 0x000 +#define PCI_STATUS_DEVSEL_MEDIUM 0x200 +#define PCI_STATUS_DEVSEL_SLOW 0x400 +#define PCI_STATUS_SIG_TARGET_ABORT 0x800 /* Set on target abort */ +#define PCI_STATUS_REC_TARGET_ABORT 0x1000 /* Master ack of " */ +#define PCI_STATUS_REC_MASTER_ABORT 0x2000 /* Set on master abort */ +#define PCI_STATUS_SIG_SYSTEM_ERROR 0x4000 /* Set when we drive SERR */ +#define PCI_STATUS_DETECTED_PARITY 0x8000 /* Set on parity error */ + +#define PCI_CLASS_REVISION 0x08 /* High 24 bits are class, low 8 revision */ +#define PCI_REVISION_ID 0x08 /* Revision ID */ +#define PCI_CLASS_PROG 0x09 /* Reg. Level Programming Interface */ +#define PCI_CLASS_DEVICE 0x0a /* Device class */ + +#define PCI_CACHE_LINE_SIZE 0x0c /* 8 bits */ +#define PCI_LATENCY_TIMER 0x0d /* 8 bits */ +#define PCI_HEADER_TYPE 0x0e /* 8 bits */ +#define PCI_HEADER_TYPE_NORMAL 0 +#define PCI_HEADER_TYPE_BRIDGE 1 +#define PCI_HEADER_TYPE_CARDBUS 2 + +#define PCI_BIST 0x0f /* 8 bits */ +#define PCI_BIST_CODE_MASK 0x0f /* Return result */ +#define PCI_BIST_START 0x40 /* 1 to start BIST, 2 secs or less */ +#define PCI_BIST_CAPABLE 0x80 /* 1 if BIST capable */ + +/* + * Base addresses specify locations in memory or I/O space. + * Decoded size can be determined by writing a value of + * 0xffffffff to the register, and reading it back. Only + * 1 bits are decoded. + */ +#define PCI_BASE_ADDRESS_0 0x10 /* 32 bits */ +#define PCI_BASE_ADDRESS_1 0x14 /* 32 bits [htype 0,1 only] */ +#define PCI_BASE_ADDRESS_2 0x18 /* 32 bits [htype 0 only] */ +#define PCI_BASE_ADDRESS_3 0x1c /* 32 bits */ +#define PCI_BASE_ADDRESS_4 0x20 /* 32 bits */ +#define PCI_BASE_ADDRESS_5 0x24 /* 32 bits */ +#define PCI_BASE_ADDRESS_SPACE 0x01 /* 0 = memory, 1 = I/O */ +#define PCI_BASE_ADDRESS_SPACE_IO 0x01 +#define PCI_BASE_ADDRESS_SPACE_MEMORY 0x00 +#define PCI_BASE_ADDRESS_MEM_TYPE_MASK 0x06 +#define PCI_BASE_ADDRESS_MEM_TYPE_32 0x00 /* 32 bit address */ +#define PCI_BASE_ADDRESS_MEM_TYPE_1M 0x02 /* Below 1M [obsolete] */ +#define PCI_BASE_ADDRESS_MEM_TYPE_64 0x04 /* 64 bit address */ +#define PCI_BASE_ADDRESS_MEM_PREFETCH 0x08 /* prefetchable? */ +#define PCI_BASE_ADDRESS_MEM_MASK (~0x0fUL) +#define PCI_BASE_ADDRESS_IO_MASK (~0x03UL) +/* bit 1 is reserved if address_space = 1 */ + +/* Header type 0 (normal devices) */ +#define PCI_CARDBUS_CIS 0x28 +#define PCI_SUBSYSTEM_VENDOR_ID 0x2c +#define PCI_SUBSYSTEM_ID 0x2e +#define PCI_ROM_ADDRESS 0x30 /* Bits 31..11 are address, 10..1 reserved */ +#define PCI_ROM_ADDRESS_ENABLE 0x01 +#define PCI_ROM_ADDRESS_MASK (~0x7ffUL) + +#define PCI_CAPABILITY_LIST 0x34 /* Offset of first capability list entry */ + +/* 0x35-0x3b are reserved */ +#define PCI_INTERRUPT_LINE 0x3c /* 8 bits */ +#define PCI_INTERRUPT_PIN 0x3d /* 8 bits */ +#define PCI_MIN_GNT 0x3e /* 8 bits */ +#define PCI_MAX_LAT 0x3f /* 8 bits */ + +/* Header type 1 (PCI-to-PCI bridges) */ +#define PCI_PRIMARY_BUS 0x18 /* Primary bus number */ +#define PCI_SECONDARY_BUS 0x19 /* Secondary bus number */ +#define PCI_SUBORDINATE_BUS 0x1a /* Highest bus number behind the bridge */ +#define PCI_SEC_LATENCY_TIMER 0x1b /* Latency timer for secondary interface */ +#define PCI_IO_BASE 0x1c /* I/O range behind the bridge */ +#define PCI_IO_LIMIT 0x1d +#define PCI_IO_RANGE_TYPE_MASK 0x0fUL /* I/O bridging type */ +#define PCI_IO_RANGE_TYPE_16 0x00 +#define PCI_IO_RANGE_TYPE_32 0x01 +#define PCI_IO_RANGE_MASK (~0x0fUL) +#define PCI_SEC_STATUS 0x1e /* Secondary status register, only bit 14 used */ +#define PCI_MEMORY_BASE 0x20 /* Memory range behind */ +#define PCI_MEMORY_LIMIT 0x22 +#define PCI_MEMORY_RANGE_TYPE_MASK 0x0fUL +#define PCI_MEMORY_RANGE_MASK (~0x0fUL) +#define PCI_PREF_MEMORY_BASE 0x24 /* Prefetchable memory range behind */ +#define PCI_PREF_MEMORY_LIMIT 0x26 +#define PCI_PREF_RANGE_TYPE_MASK 0x0fUL +#define PCI_PREF_RANGE_TYPE_32 0x00 +#define PCI_PREF_RANGE_TYPE_64 0x01 +#define PCI_PREF_RANGE_MASK (~0x0fUL) +#define PCI_PREF_BASE_UPPER32 0x28 /* Upper half of prefetchable memory range */ +#define PCI_PREF_LIMIT_UPPER32 0x2c +#define PCI_IO_BASE_UPPER16 0x30 /* Upper half of I/O addresses */ +#define PCI_IO_LIMIT_UPPER16 0x32 +/* 0x34 same as for htype 0 */ +/* 0x35-0x3b is reserved */ +#define PCI_ROM_ADDRESS1 0x38 /* Same as PCI_ROM_ADDRESS, but for htype 1 */ +/* 0x3c-0x3d are same as for htype 0 */ +#define PCI_BRIDGE_CONTROL 0x3e +#define PCI_BRIDGE_CTL_PARITY 0x01 /* Enable parity detection on secondary interface */ +#define PCI_BRIDGE_CTL_SERR 0x02 /* The same for SERR forwarding */ +#define PCI_BRIDGE_CTL_ISA 0x04 /* Enable ISA mode */ +#define PCI_BRIDGE_CTL_VGA 0x08 /* Forward VGA addresses */ +#define PCI_BRIDGE_CTL_MASTER_ABORT 0x20 /* Report master aborts */ +#define PCI_BRIDGE_CTL_BUS_RESET 0x40 /* Secondary bus reset */ +#define PCI_BRIDGE_CTL_FAST_BACK 0x80 /* Fast Back2Back enabled on secondary interface */ + +/* Header type 2 (CardBus bridges) */ +#define PCI_CB_CAPABILITY_LIST 0x14 +/* 0x15 reserved */ +#define PCI_CB_SEC_STATUS 0x16 /* Secondary status */ +#define PCI_CB_PRIMARY_BUS 0x18 /* PCI bus number */ +#define PCI_CB_CARD_BUS 0x19 /* CardBus bus number */ +#define PCI_CB_SUBORDINATE_BUS 0x1a /* Subordinate bus number */ +#define PCI_CB_LATENCY_TIMER 0x1b /* CardBus latency timer */ +#define PCI_CB_MEMORY_BASE_0 0x1c +#define PCI_CB_MEMORY_LIMIT_0 0x20 +#define PCI_CB_MEMORY_BASE_1 0x24 +#define PCI_CB_MEMORY_LIMIT_1 0x28 +#define PCI_CB_IO_BASE_0 0x2c +#define PCI_CB_IO_BASE_0_HI 0x2e +#define PCI_CB_IO_LIMIT_0 0x30 +#define PCI_CB_IO_LIMIT_0_HI 0x32 +#define PCI_CB_IO_BASE_1 0x34 +#define PCI_CB_IO_BASE_1_HI 0x36 +#define PCI_CB_IO_LIMIT_1 0x38 +#define PCI_CB_IO_LIMIT_1_HI 0x3a +#define PCI_CB_IO_RANGE_MASK (~0x03UL) +/* 0x3c-0x3d are same as for htype 0 */ +#define PCI_CB_BRIDGE_CONTROL 0x3e +#define PCI_CB_BRIDGE_CTL_PARITY 0x01 /* Similar to standard bridge control register */ +#define PCI_CB_BRIDGE_CTL_SERR 0x02 +#define PCI_CB_BRIDGE_CTL_ISA 0x04 +#define PCI_CB_BRIDGE_CTL_VGA 0x08 +#define PCI_CB_BRIDGE_CTL_MASTER_ABORT 0x20 +#define PCI_CB_BRIDGE_CTL_CB_RESET 0x40 /* CardBus reset */ +#define PCI_CB_BRIDGE_CTL_16BIT_INT 0x80 /* Enable interrupt for 16-bit cards */ +#define PCI_CB_BRIDGE_CTL_PREFETCH_MEM0 0x100 /* Prefetch enable for both memory regions */ +#define PCI_CB_BRIDGE_CTL_PREFETCH_MEM1 0x200 +#define PCI_CB_BRIDGE_CTL_POST_WRITES 0x400 +#define PCI_CB_SUBSYSTEM_VENDOR_ID 0x40 +#define PCI_CB_SUBSYSTEM_ID 0x42 +#define PCI_CB_LEGACY_MODE_BASE 0x44 /* 16-bit PC Card legacy mode base address (ExCa) */ +/* 0x48-0x7f reserved */ + +/* Capability lists */ + +#define PCI_CAP_LIST_ID 0 /* Capability ID */ +#define PCI_CAP_ID_PM 0x01 /* Power Management */ +#define PCI_CAP_ID_AGP 0x02 /* Accelerated Graphics Port */ +#define PCI_CAP_ID_VPD 0x03 /* Vital Product Data */ +#define PCI_CAP_ID_SLOTID 0x04 /* Slot Identification */ +#define PCI_CAP_ID_MSI 0x05 /* Message Signalled Interrupts */ +#define PCI_CAP_ID_CHSWP 0x06 /* CompactPCI HotSwap */ +#define PCI_CAP_ID_PCIX 0x07 /* PCI-X */ +#define PCI_CAP_ID_HT 0x08 /* HyperTransport */ +#define PCI_CAP_ID_VNDR 0x09 /* Vendor specific */ +#define PCI_CAP_ID_DBG 0x0A /* Debug port */ +#define PCI_CAP_ID_CCRC 0x0B /* CompactPCI Central Resource Control */ +#define PCI_CAP_ID_SHPC 0x0C /* PCI Standard Hot-Plug Controller */ +#define PCI_CAP_ID_SSVID 0x0D /* Bridge subsystem vendor/device ID */ +#define PCI_CAP_ID_AGP3 0x0E /* AGP Target PCI-PCI bridge */ +#define PCI_CAP_ID_EXP 0x10 /* PCI Express */ +#define PCI_CAP_ID_MSIX 0x11 /* MSI-X */ +#define PCI_CAP_ID_SATA 0x12 /* Serial ATA */ +#define PCI_CAP_ID_AF 0x13 /* PCI Advanced Features */ +#define PCI_CAP_LIST_NEXT 1 /* Next capability in the list */ +#define PCI_CAP_FLAGS 2 /* Capability defined flags (16 bits) */ +#define PCI_CAP_SIZEOF 4 + +/* Power Management Registers */ + +#define PCI_PM_PMC 2 /* PM Capabilities Register */ +#define PCI_PM_CAP_VER_MASK 0x0007 /* Version */ +#define PCI_PM_CAP_PME_CLOCK 0x0008 /* PME clock required */ +#define PCI_PM_CAP_RESERVED 0x0010 /* Reserved field */ +#define PCI_PM_CAP_DSI 0x0020 /* Device specific initialization */ +#define PCI_PM_CAP_AUX_POWER 0x01C0 /* Auxiliary power support mask */ +#define PCI_PM_CAP_D1 0x0200 /* D1 power state support */ +#define PCI_PM_CAP_D2 0x0400 /* D2 power state support */ +#define PCI_PM_CAP_PME 0x0800 /* PME pin supported */ +#define PCI_PM_CAP_PME_MASK 0xF800 /* PME Mask of all supported states */ +#define PCI_PM_CAP_PME_D0 0x0800 /* PME# from D0 */ +#define PCI_PM_CAP_PME_D1 0x1000 /* PME# from D1 */ +#define PCI_PM_CAP_PME_D2 0x2000 /* PME# from D2 */ +#define PCI_PM_CAP_PME_D3 0x4000 /* PME# from D3 (hot) */ +#define PCI_PM_CAP_PME_D3cold 0x8000 /* PME# from D3 (cold) */ +#define PCI_PM_CAP_PME_SHIFT 11 /* Start of the PME Mask in PMC */ +#define PCI_PM_CTRL 4 /* PM control and status register */ +#define PCI_PM_CTRL_STATE_MASK 0x0003 /* Current power state (D0 to D3) */ +#define PCI_PM_CTRL_NO_SOFT_RESET 0x0008 /* No reset for D3hot->D0 */ +#define PCI_PM_CTRL_PME_ENABLE 0x0100 /* PME pin enable */ +#define PCI_PM_CTRL_DATA_SEL_MASK 0x1e00 /* Data select (??) */ +#define PCI_PM_CTRL_DATA_SCALE_MASK 0x6000 /* Data scale (??) */ +#define PCI_PM_CTRL_PME_STATUS 0x8000 /* PME pin status */ +#define PCI_PM_PPB_EXTENSIONS 6 /* PPB support extensions (??) */ +#define PCI_PM_PPB_B2_B3 0x40 /* Stop clock when in D3hot (??) */ +#define PCI_PM_BPCC_ENABLE 0x80 /* Bus power/clock control enable (??) */ +#define PCI_PM_DATA_REGISTER 7 /* (??) */ +#define PCI_PM_SIZEOF 8 + +/* AGP registers */ + +#define PCI_AGP_VERSION 2 /* BCD version number */ +#define PCI_AGP_RFU 3 /* Rest of capability flags */ +#define PCI_AGP_STATUS 4 /* Status register */ +#define PCI_AGP_STATUS_RQ_MASK 0xff000000 /* Maximum number of requests - 1 */ +#define PCI_AGP_STATUS_SBA 0x0200 /* Sideband addressing supported */ +#define PCI_AGP_STATUS_64BIT 0x0020 /* 64-bit addressing supported */ +#define PCI_AGP_STATUS_FW 0x0010 /* FW transfers supported */ +#define PCI_AGP_STATUS_RATE4 0x0004 /* 4x transfer rate supported */ +#define PCI_AGP_STATUS_RATE2 0x0002 /* 2x transfer rate supported */ +#define PCI_AGP_STATUS_RATE1 0x0001 /* 1x transfer rate supported */ +#define PCI_AGP_COMMAND 8 /* Control register */ +#define PCI_AGP_COMMAND_RQ_MASK 0xff000000 /* Master: Maximum number of requests */ +#define PCI_AGP_COMMAND_SBA 0x0200 /* Sideband addressing enabled */ +#define PCI_AGP_COMMAND_AGP 0x0100 /* Allow processing of AGP transactions */ +#define PCI_AGP_COMMAND_64BIT 0x0020 /* Allow processing of 64-bit addresses */ +#define PCI_AGP_COMMAND_FW 0x0010 /* Force FW transfers */ +#define PCI_AGP_COMMAND_RATE4 0x0004 /* Use 4x rate */ +#define PCI_AGP_COMMAND_RATE2 0x0002 /* Use 2x rate */ +#define PCI_AGP_COMMAND_RATE1 0x0001 /* Use 1x rate */ +#define PCI_AGP_SIZEOF 12 + +/* Vital Product Data */ + +#define PCI_VPD_ADDR 2 /* Address to access (15 bits!) */ +#define PCI_VPD_ADDR_MASK 0x7fff /* Address mask */ +#define PCI_VPD_ADDR_F 0x8000 /* Write 0, 1 indicates completion */ +#define PCI_VPD_DATA 4 /* 32-bits of data returned here */ + +/* Slot Identification */ + +#define PCI_SID_ESR 2 /* Expansion Slot Register */ +#define PCI_SID_ESR_NSLOTS 0x1f /* Number of expansion slots available */ +#define PCI_SID_ESR_FIC 0x20 /* First In Chassis Flag */ +#define PCI_SID_CHASSIS_NR 3 /* Chassis Number */ + +/* Message Signalled Interrupts registers */ + +#define PCI_MSI_FLAGS 2 /* Various flags */ +#define PCI_MSI_FLAGS_64BIT 0x80 /* 64-bit addresses allowed */ +#define PCI_MSI_FLAGS_QSIZE 0x70 /* Message queue size configured */ +#define PCI_MSI_FLAGS_QMASK 0x0e /* Maximum queue size available */ +#define PCI_MSI_FLAGS_ENABLE 0x01 /* MSI feature enabled */ +#define PCI_MSI_FLAGS_MASKBIT 0x100 /* 64-bit mask bits allowed */ +#define PCI_MSI_RFU 3 /* Rest of capability flags */ +#define PCI_MSI_ADDRESS_LO 4 /* Lower 32 bits */ +#define PCI_MSI_ADDRESS_HI 8 /* Upper 32 bits (if PCI_MSI_FLAGS_64BIT set) */ +#define PCI_MSI_DATA_32 8 /* 16 bits of data for 32-bit devices */ +#define PCI_MSI_MASK_32 12 /* Mask bits register for 32-bit devices */ +#define PCI_MSI_DATA_64 12 /* 16 bits of data for 64-bit devices */ +#define PCI_MSI_MASK_64 16 /* Mask bits register for 64-bit devices */ + +/* MSI-X registers */ +#define PCI_MSIX_FLAGS 2 +#define PCI_MSIX_FLAGS_QSIZE 0x7FF +#define PCI_MSIX_FLAGS_ENABLE (1 << 15) +#define PCI_MSIX_FLAGS_MASKALL (1 << 14) +#define PCI_MSIX_TABLE 4 +#define PCI_MSIX_PBA 8 +#define PCI_MSIX_FLAGS_BIRMASK (7 << 0) + +/* MSI-X entry's format */ +#define PCI_MSIX_ENTRY_SIZE 16 +#define PCI_MSIX_ENTRY_LOWER_ADDR 0 +#define PCI_MSIX_ENTRY_UPPER_ADDR 4 +#define PCI_MSIX_ENTRY_DATA 8 +#define PCI_MSIX_ENTRY_VECTOR_CTRL 12 +#define PCI_MSIX_ENTRY_CTRL_MASKBIT 1 + +/* CompactPCI Hotswap Register */ + +#define PCI_CHSWP_CSR 2 /* Control and Status Register */ +#define PCI_CHSWP_DHA 0x01 /* Device Hiding Arm */ +#define PCI_CHSWP_EIM 0x02 /* ENUM# Signal Mask */ +#define PCI_CHSWP_PIE 0x04 /* Pending Insert or Extract */ +#define PCI_CHSWP_LOO 0x08 /* LED On / Off */ +#define PCI_CHSWP_PI 0x30 /* Programming Interface */ +#define PCI_CHSWP_EXT 0x40 /* ENUM# status - extraction */ +#define PCI_CHSWP_INS 0x80 /* ENUM# status - insertion */ + +/* PCI Advanced Feature registers */ + +#define PCI_AF_LENGTH 2 +#define PCI_AF_CAP 3 +#define PCI_AF_CAP_TP 0x01 +#define PCI_AF_CAP_FLR 0x02 +#define PCI_AF_CTRL 4 +#define PCI_AF_CTRL_FLR 0x01 +#define PCI_AF_STATUS 5 +#define PCI_AF_STATUS_TP 0x01 + +/* PCI-X registers */ + +#define PCI_X_CMD 2 /* Modes & Features */ +#define PCI_X_CMD_DPERR_E 0x0001 /* Data Parity Error Recovery Enable */ +#define PCI_X_CMD_ERO 0x0002 /* Enable Relaxed Ordering */ +#define PCI_X_CMD_READ_512 0x0000 /* 512 byte maximum read byte count */ +#define PCI_X_CMD_READ_1K 0x0004 /* 1Kbyte maximum read byte count */ +#define PCI_X_CMD_READ_2K 0x0008 /* 2Kbyte maximum read byte count */ +#define PCI_X_CMD_READ_4K 0x000c /* 4Kbyte maximum read byte count */ +#define PCI_X_CMD_MAX_READ 0x000c /* Max Memory Read Byte Count */ + /* Max # of outstanding split transactions */ +#define PCI_X_CMD_SPLIT_1 0x0000 /* Max 1 */ +#define PCI_X_CMD_SPLIT_2 0x0010 /* Max 2 */ +#define PCI_X_CMD_SPLIT_3 0x0020 /* Max 3 */ +#define PCI_X_CMD_SPLIT_4 0x0030 /* Max 4 */ +#define PCI_X_CMD_SPLIT_8 0x0040 /* Max 8 */ +#define PCI_X_CMD_SPLIT_12 0x0050 /* Max 12 */ +#define PCI_X_CMD_SPLIT_16 0x0060 /* Max 16 */ +#define PCI_X_CMD_SPLIT_32 0x0070 /* Max 32 */ +#define PCI_X_CMD_MAX_SPLIT 0x0070 /* Max Outstanding Split Transactions */ +#define PCI_X_CMD_VERSION(x) (((x) >> 12) & 3) /* Version */ +#define PCI_X_STATUS 4 /* PCI-X capabilities */ +#define PCI_X_STATUS_DEVFN 0x000000ff /* A copy of devfn */ +#define PCI_X_STATUS_BUS 0x0000ff00 /* A copy of bus nr */ +#define PCI_X_STATUS_64BIT 0x00010000 /* 64-bit device */ +#define PCI_X_STATUS_133MHZ 0x00020000 /* 133 MHz capable */ +#define PCI_X_STATUS_SPL_DISC 0x00040000 /* Split Completion Discarded */ +#define PCI_X_STATUS_UNX_SPL 0x00080000 /* Unexpected Split Completion */ +#define PCI_X_STATUS_COMPLEX 0x00100000 /* Device Complexity */ +#define PCI_X_STATUS_MAX_READ 0x00600000 /* Designed Max Memory Read Count */ +#define PCI_X_STATUS_MAX_SPLIT 0x03800000 /* Designed Max Outstanding Split Transactions */ +#define PCI_X_STATUS_MAX_CUM 0x1c000000 /* Designed Max Cumulative Read Size */ +#define PCI_X_STATUS_SPL_ERR 0x20000000 /* Rcvd Split Completion Error Msg */ +#define PCI_X_STATUS_266MHZ 0x40000000 /* 266 MHz capable */ +#define PCI_X_STATUS_533MHZ 0x80000000 /* 533 MHz capable */ + +/* PCI Bridge Subsystem ID registers */ + +#define PCI_SSVID_VENDOR_ID 4 /* PCI-Bridge subsystem vendor id register */ +#define PCI_SSVID_DEVICE_ID 6 /* PCI-Bridge subsystem device id register */ + +/* PCI Express capability registers */ + +#define PCI_EXP_FLAGS 2 /* Capabilities register */ +#define PCI_EXP_FLAGS_VERS 0x000f /* Capability version */ +#define PCI_EXP_FLAGS_TYPE 0x00f0 /* Device/Port type */ +#define PCI_EXP_TYPE_ENDPOINT 0x0 /* Express Endpoint */ +#define PCI_EXP_TYPE_LEG_END 0x1 /* Legacy Endpoint */ +#define PCI_EXP_TYPE_ROOT_PORT 0x4 /* Root Port */ +#define PCI_EXP_TYPE_UPSTREAM 0x5 /* Upstream Port */ +#define PCI_EXP_TYPE_DOWNSTREAM 0x6 /* Downstream Port */ +#define PCI_EXP_TYPE_PCI_BRIDGE 0x7 /* PCI/PCI-X Bridge */ +#define PCI_EXP_TYPE_PCIE_BRIDGE 0x8 /* PCI/PCI-X to PCIE Bridge */ +#define PCI_EXP_TYPE_RC_END 0x9 /* Root Complex Integrated Endpoint */ +#define PCI_EXP_TYPE_RC_EC 0xa /* Root Complex Event Collector */ +#define PCI_EXP_FLAGS_SLOT 0x0100 /* Slot implemented */ +#define PCI_EXP_FLAGS_IRQ 0x3e00 /* Interrupt message number */ +#define PCI_EXP_DEVCAP 4 /* Device capabilities */ +#define PCI_EXP_DEVCAP_PAYLOAD 0x07 /* Max_Payload_Size */ +#define PCI_EXP_DEVCAP_PHANTOM 0x18 /* Phantom functions */ +#define PCI_EXP_DEVCAP_EXT_TAG 0x20 /* Extended tags */ +#define PCI_EXP_DEVCAP_L0S 0x1c0 /* L0s Acceptable Latency */ +#define PCI_EXP_DEVCAP_L1 0xe00 /* L1 Acceptable Latency */ +#define PCI_EXP_DEVCAP_ATN_BUT 0x1000 /* Attention Button Present */ +#define PCI_EXP_DEVCAP_ATN_IND 0x2000 /* Attention Indicator Present */ +#define PCI_EXP_DEVCAP_PWR_IND 0x4000 /* Power Indicator Present */ +#define PCI_EXP_DEVCAP_RBER 0x8000 /* Role-Based Error Reporting */ +#define PCI_EXP_DEVCAP_PWR_VAL 0x3fc0000 /* Slot Power Limit Value */ +#define PCI_EXP_DEVCAP_PWR_SCL 0xc000000 /* Slot Power Limit Scale */ +#define PCI_EXP_DEVCAP_FLR 0x10000000 /* Function Level Reset */ +#define PCI_EXP_DEVCTL 8 /* Device Control */ +#define PCI_EXP_DEVCTL_CERE 0x0001 /* Correctable Error Reporting En. */ +#define PCI_EXP_DEVCTL_NFERE 0x0002 /* Non-Fatal Error Reporting Enable */ +#define PCI_EXP_DEVCTL_FERE 0x0004 /* Fatal Error Reporting Enable */ +#define PCI_EXP_DEVCTL_URRE 0x0008 /* Unsupported Request Reporting En. */ +#define PCI_EXP_DEVCTL_RELAX_EN 0x0010 /* Enable relaxed ordering */ +#define PCI_EXP_DEVCTL_PAYLOAD 0x00e0 /* Max_Payload_Size */ +#define PCI_EXP_DEVCTL_EXT_TAG 0x0100 /* Extended Tag Field Enable */ +#define PCI_EXP_DEVCTL_PHANTOM 0x0200 /* Phantom Functions Enable */ +#define PCI_EXP_DEVCTL_AUX_PME 0x0400 /* Auxiliary Power PM Enable */ +#define PCI_EXP_DEVCTL_NOSNOOP_EN 0x0800 /* Enable No Snoop */ +#define PCI_EXP_DEVCTL_READRQ 0x7000 /* Max_Read_Request_Size */ +#define PCI_EXP_DEVCTL_BCR_FLR 0x8000 /* Bridge Configuration Retry / FLR */ +#define PCI_EXP_DEVSTA 10 /* Device Status */ +#define PCI_EXP_DEVSTA_CED 0x01 /* Correctable Error Detected */ +#define PCI_EXP_DEVSTA_NFED 0x02 /* Non-Fatal Error Detected */ +#define PCI_EXP_DEVSTA_FED 0x04 /* Fatal Error Detected */ +#define PCI_EXP_DEVSTA_URD 0x08 /* Unsupported Request Detected */ +#define PCI_EXP_DEVSTA_AUXPD 0x10 /* AUX Power Detected */ +#define PCI_EXP_DEVSTA_TRPND 0x20 /* Transactions Pending */ +#define PCI_EXP_LNKCAP 12 /* Link Capabilities */ +#define PCI_EXP_LNKCAP_SLS 0x0000000f /* Supported Link Speeds */ +#define PCI_EXP_LNKCAP_MLW 0x000003f0 /* Maximum Link Width */ +#define PCI_EXP_LNKCAP_ASPMS 0x00000c00 /* ASPM Support */ +#define PCI_EXP_LNKCAP_L0SEL 0x00007000 /* L0s Exit Latency */ +#define PCI_EXP_LNKCAP_L1EL 0x00038000 /* L1 Exit Latency */ +#define PCI_EXP_LNKCAP_CLKPM 0x00040000 /* L1 Clock Power Management */ +#define PCI_EXP_LNKCAP_SDERC 0x00080000 /* Surprise Down Error Reporting Capable */ +#define PCI_EXP_LNKCAP_DLLLARC 0x00100000 /* Data Link Layer Link Active Reporting Capable */ +#define PCI_EXP_LNKCAP_LBNC 0x00200000 /* Link Bandwidth Notification Capability */ +#define PCI_EXP_LNKCAP_PN 0xff000000 /* Port Number */ +#define PCI_EXP_LNKCTL 16 /* Link Control */ +#define PCI_EXP_LNKCTL_ASPMC 0x0003 /* ASPM Control */ +#define PCI_EXP_LNKCTL_RCB 0x0008 /* Read Completion Boundary */ +#define PCI_EXP_LNKCTL_LD 0x0010 /* Link Disable */ +#define PCI_EXP_LNKCTL_RL 0x0020 /* Retrain Link */ +#define PCI_EXP_LNKCTL_CCC 0x0040 /* Common Clock Configuration */ +#define PCI_EXP_LNKCTL_ES 0x0080 /* Extended Synch */ +#define PCI_EXP_LNKCTL_CLKREQ_EN 0x100 /* Enable clkreq */ +#define PCI_EXP_LNKCTL_HAWD 0x0200 /* Hardware Autonomous Width Disable */ +#define PCI_EXP_LNKCTL_LBMIE 0x0400 /* Link Bandwidth Management Interrupt Enable */ +#define PCI_EXP_LNKCTL_LABIE 0x0800 /* Lnk Autonomous Bandwidth Interrupt Enable */ +#define PCI_EXP_LNKSTA 18 /* Link Status */ +#define PCI_EXP_LNKSTA_CLS 0x000f /* Current Link Speed */ +#define PCI_EXP_LNKSTA_CLS_2_5GB 0x01 /* Current Link Speed 2.5GT/s */ +#define PCI_EXP_LNKSTA_CLS_5_0GB 0x02 /* Current Link Speed 5.0GT/s */ +#define PCI_EXP_LNKSTA_NLW 0x03f0 /* Nogotiated Link Width */ +#define PCI_EXP_LNKSTA_NLW_SHIFT 4 /* start of NLW mask in link status */ +#define PCI_EXP_LNKSTA_LT 0x0800 /* Link Training */ +#define PCI_EXP_LNKSTA_SLC 0x1000 /* Slot Clock Configuration */ +#define PCI_EXP_LNKSTA_DLLLA 0x2000 /* Data Link Layer Link Active */ +#define PCI_EXP_LNKSTA_LBMS 0x4000 /* Link Bandwidth Management Status */ +#define PCI_EXP_LNKSTA_LABS 0x8000 /* Link Autonomous Bandwidth Status */ +#define PCI_EXP_SLTCAP 20 /* Slot Capabilities */ +#define PCI_EXP_SLTCAP_ABP 0x00000001 /* Attention Button Present */ +#define PCI_EXP_SLTCAP_PCP 0x00000002 /* Power Controller Present */ +#define PCI_EXP_SLTCAP_MRLSP 0x00000004 /* MRL Sensor Present */ +#define PCI_EXP_SLTCAP_AIP 0x00000008 /* Attention Indicator Present */ +#define PCI_EXP_SLTCAP_PIP 0x00000010 /* Power Indicator Present */ +#define PCI_EXP_SLTCAP_HPS 0x00000020 /* Hot-Plug Surprise */ +#define PCI_EXP_SLTCAP_HPC 0x00000040 /* Hot-Plug Capable */ +#define PCI_EXP_SLTCAP_SPLV 0x00007f80 /* Slot Power Limit Value */ +#define PCI_EXP_SLTCAP_SPLS 0x00018000 /* Slot Power Limit Scale */ +#define PCI_EXP_SLTCAP_EIP 0x00020000 /* Electromechanical Interlock Present */ +#define PCI_EXP_SLTCAP_NCCS 0x00040000 /* No Command Completed Support */ +#define PCI_EXP_SLTCAP_PSN 0xfff80000 /* Physical Slot Number */ +#define PCI_EXP_SLTCTL 24 /* Slot Control */ +#define PCI_EXP_SLTCTL_ABPE 0x0001 /* Attention Button Pressed Enable */ +#define PCI_EXP_SLTCTL_PFDE 0x0002 /* Power Fault Detected Enable */ +#define PCI_EXP_SLTCTL_MRLSCE 0x0004 /* MRL Sensor Changed Enable */ +#define PCI_EXP_SLTCTL_PDCE 0x0008 /* Presence Detect Changed Enable */ +#define PCI_EXP_SLTCTL_CCIE 0x0010 /* Command Completed Interrupt Enable */ +#define PCI_EXP_SLTCTL_HPIE 0x0020 /* Hot-Plug Interrupt Enable */ +#define PCI_EXP_SLTCTL_AIC 0x00c0 /* Attention Indicator Control */ +#define PCI_EXP_SLTCTL_PIC 0x0300 /* Power Indicator Control */ +#define PCI_EXP_SLTCTL_PCC 0x0400 /* Power Controller Control */ +#define PCI_EXP_SLTCTL_EIC 0x0800 /* Electromechanical Interlock Control */ +#define PCI_EXP_SLTCTL_DLLSCE 0x1000 /* Data Link Layer State Changed Enable */ +#define PCI_EXP_SLTSTA 26 /* Slot Status */ +#define PCI_EXP_SLTSTA_ABP 0x0001 /* Attention Button Pressed */ +#define PCI_EXP_SLTSTA_PFD 0x0002 /* Power Fault Detected */ +#define PCI_EXP_SLTSTA_MRLSC 0x0004 /* MRL Sensor Changed */ +#define PCI_EXP_SLTSTA_PDC 0x0008 /* Presence Detect Changed */ +#define PCI_EXP_SLTSTA_CC 0x0010 /* Command Completed */ +#define PCI_EXP_SLTSTA_MRLSS 0x0020 /* MRL Sensor State */ +#define PCI_EXP_SLTSTA_PDS 0x0040 /* Presence Detect State */ +#define PCI_EXP_SLTSTA_EIS 0x0080 /* Electromechanical Interlock Status */ +#define PCI_EXP_SLTSTA_DLLSC 0x0100 /* Data Link Layer State Changed */ +#define PCI_EXP_RTCTL 28 /* Root Control */ +#define PCI_EXP_RTCTL_SECEE 0x01 /* System Error on Correctable Error */ +#define PCI_EXP_RTCTL_SENFEE 0x02 /* System Error on Non-Fatal Error */ +#define PCI_EXP_RTCTL_SEFEE 0x04 /* System Error on Fatal Error */ +#define PCI_EXP_RTCTL_PMEIE 0x08 /* PME Interrupt Enable */ +#define PCI_EXP_RTCTL_CRSSVE 0x10 /* CRS Software Visibility Enable */ +#define PCI_EXP_RTCAP 30 /* Root Capabilities */ +#define PCI_EXP_RTSTA 32 /* Root Status */ +#define PCI_EXP_RTSTA_PME 0x10000 /* PME status */ +#define PCI_EXP_RTSTA_PENDING 0x20000 /* PME pending */ +#define PCI_EXP_DEVCAP2 36 /* Device Capabilities 2 */ +#define PCI_EXP_DEVCAP2_ARI 0x20 /* Alternative Routing-ID */ +#define PCI_EXP_DEVCAP2_LTR 0x800 /* Latency tolerance reporting */ +#define PCI_EXP_OBFF_MASK 0xc0000 /* OBFF support mechanism */ +#define PCI_EXP_OBFF_MSG 0x40000 /* New message signaling */ +#define PCI_EXP_OBFF_WAKE 0x80000 /* Re-use WAKE# for OBFF */ +#define PCI_EXP_DEVCTL2 40 /* Device Control 2 */ +#define PCI_EXP_DEVCTL2_ARI 0x20 /* Alternative Routing-ID */ +#define PCI_EXP_IDO_REQ_EN 0x100 /* ID-based ordering request enable */ +#define PCI_EXP_IDO_CMP_EN 0x200 /* ID-based ordering completion enable */ +#define PCI_EXP_LTR_EN 0x400 /* Latency tolerance reporting */ +#define PCI_EXP_OBFF_MSGA_EN 0x2000 /* OBFF enable with Message type A */ +#define PCI_EXP_OBFF_MSGB_EN 0x4000 /* OBFF enable with Message type B */ +#define PCI_EXP_OBFF_WAKE_EN 0x6000 /* OBFF using WAKE# signaling */ +#define PCI_EXP_LNKCTL2 48 /* Link Control 2 */ +#define PCI_EXP_SLTCTL2 56 /* Slot Control 2 */ + +/* Extended Capabilities (PCI-X 2.0 and Express) */ +#define PCI_EXT_CAP_ID(header) (header & 0x0000ffff) +#define PCI_EXT_CAP_VER(header) ((header >> 16) & 0xf) +#define PCI_EXT_CAP_NEXT(header) ((header >> 20) & 0xffc) + +#define PCI_EXT_CAP_ID_ERR 1 +#define PCI_EXT_CAP_ID_VC 2 +#define PCI_EXT_CAP_ID_DSN 3 +#define PCI_EXT_CAP_ID_PWR 4 +#define PCI_EXT_CAP_ID_VNDR 11 +#define PCI_EXT_CAP_ID_ACS 13 +#define PCI_EXT_CAP_ID_ARI 14 +#define PCI_EXT_CAP_ID_ATS 15 +#define PCI_EXT_CAP_ID_SRIOV 16 +#define PCI_EXT_CAP_ID_LTR 24 + +/* Advanced Error Reporting */ +#define PCI_ERR_UNCOR_STATUS 4 /* Uncorrectable Error Status */ +#define PCI_ERR_UNC_TRAIN 0x00000001 /* Training */ +#define PCI_ERR_UNC_DLP 0x00000010 /* Data Link Protocol */ +#define PCI_ERR_UNC_POISON_TLP 0x00001000 /* Poisoned TLP */ +#define PCI_ERR_UNC_FCP 0x00002000 /* Flow Control Protocol */ +#define PCI_ERR_UNC_COMP_TIME 0x00004000 /* Completion Timeout */ +#define PCI_ERR_UNC_COMP_ABORT 0x00008000 /* Completer Abort */ +#define PCI_ERR_UNC_UNX_COMP 0x00010000 /* Unexpected Completion */ +#define PCI_ERR_UNC_RX_OVER 0x00020000 /* Receiver Overflow */ +#define PCI_ERR_UNC_MALF_TLP 0x00040000 /* Malformed TLP */ +#define PCI_ERR_UNC_ECRC 0x00080000 /* ECRC Error Status */ +#define PCI_ERR_UNC_UNSUP 0x00100000 /* Unsupported Request */ +#define PCI_ERR_UNCOR_MASK 8 /* Uncorrectable Error Mask */ + /* Same bits as above */ +#define PCI_ERR_UNCOR_SEVER 12 /* Uncorrectable Error Severity */ + /* Same bits as above */ +#define PCI_ERR_COR_STATUS 16 /* Correctable Error Status */ +#define PCI_ERR_COR_RCVR 0x00000001 /* Receiver Error Status */ +#define PCI_ERR_COR_BAD_TLP 0x00000040 /* Bad TLP Status */ +#define PCI_ERR_COR_BAD_DLLP 0x00000080 /* Bad DLLP Status */ +#define PCI_ERR_COR_REP_ROLL 0x00000100 /* REPLAY_NUM Rollover */ +#define PCI_ERR_COR_REP_TIMER 0x00001000 /* Replay Timer Timeout */ +#define PCI_ERR_COR_MASK 20 /* Correctable Error Mask */ + /* Same bits as above */ +#define PCI_ERR_CAP 24 /* Advanced Error Capabilities */ +#define PCI_ERR_CAP_FEP(x) ((x) & 31) /* First Error Pointer */ +#define PCI_ERR_CAP_ECRC_GENC 0x00000020 /* ECRC Generation Capable */ +#define PCI_ERR_CAP_ECRC_GENE 0x00000040 /* ECRC Generation Enable */ +#define PCI_ERR_CAP_ECRC_CHKC 0x00000080 /* ECRC Check Capable */ +#define PCI_ERR_CAP_ECRC_CHKE 0x00000100 /* ECRC Check Enable */ +#define PCI_ERR_HEADER_LOG 28 /* Header Log Register (16 bytes) */ +#define PCI_ERR_ROOT_COMMAND 44 /* Root Error Command */ +/* Correctable Err Reporting Enable */ +#define PCI_ERR_ROOT_CMD_COR_EN 0x00000001 +/* Non-fatal Err Reporting Enable */ +#define PCI_ERR_ROOT_CMD_NONFATAL_EN 0x00000002 +/* Fatal Err Reporting Enable */ +#define PCI_ERR_ROOT_CMD_FATAL_EN 0x00000004 +#define PCI_ERR_ROOT_STATUS 48 +#define PCI_ERR_ROOT_COR_RCV 0x00000001 /* ERR_COR Received */ +/* Multi ERR_COR Received */ +#define PCI_ERR_ROOT_MULTI_COR_RCV 0x00000002 +/* ERR_FATAL/NONFATAL Recevied */ +#define PCI_ERR_ROOT_UNCOR_RCV 0x00000004 +/* Multi ERR_FATAL/NONFATAL Recevied */ +#define PCI_ERR_ROOT_MULTI_UNCOR_RCV 0x00000008 +#define PCI_ERR_ROOT_FIRST_FATAL 0x00000010 /* First Fatal */ +#define PCI_ERR_ROOT_NONFATAL_RCV 0x00000020 /* Non-Fatal Received */ +#define PCI_ERR_ROOT_FATAL_RCV 0x00000040 /* Fatal Received */ +#define PCI_ERR_ROOT_ERR_SRC 52 /* Error Source Identification */ + +/* Virtual Channel */ +#define PCI_VC_PORT_REG1 4 +#define PCI_VC_PORT_REG2 8 +#define PCI_VC_PORT_CTRL 12 +#define PCI_VC_PORT_STATUS 14 +#define PCI_VC_RES_CAP 16 +#define PCI_VC_RES_CTRL 20 +#define PCI_VC_RES_STATUS 26 + +/* Power Budgeting */ +#define PCI_PWR_DSR 4 /* Data Select Register */ +#define PCI_PWR_DATA 8 /* Data Register */ +#define PCI_PWR_DATA_BASE(x) ((x) & 0xff) /* Base Power */ +#define PCI_PWR_DATA_SCALE(x) (((x) >> 8) & 3) /* Data Scale */ +#define PCI_PWR_DATA_PM_SUB(x) (((x) >> 10) & 7) /* PM Sub State */ +#define PCI_PWR_DATA_PM_STATE(x) (((x) >> 13) & 3) /* PM State */ +#define PCI_PWR_DATA_TYPE(x) (((x) >> 15) & 7) /* Type */ +#define PCI_PWR_DATA_RAIL(x) (((x) >> 18) & 7) /* Power Rail */ +#define PCI_PWR_CAP 12 /* Capability */ +#define PCI_PWR_CAP_BUDGET(x) ((x) & 1) /* Included in system budget */ + +/* + * Hypertransport sub capability types + * + * Unfortunately there are both 3 bit and 5 bit capability types defined + * in the HT spec, catering for that is a little messy. You probably don't + * want to use these directly, just use pci_find_ht_capability() and it + * will do the right thing for you. + */ +#define HT_3BIT_CAP_MASK 0xE0 +#define HT_CAPTYPE_SLAVE 0x00 /* Slave/Primary link configuration */ +#define HT_CAPTYPE_HOST 0x20 /* Host/Secondary link configuration */ + +#define HT_5BIT_CAP_MASK 0xF8 +#define HT_CAPTYPE_IRQ 0x80 /* IRQ Configuration */ +#define HT_CAPTYPE_REMAPPING_40 0xA0 /* 40 bit address remapping */ +#define HT_CAPTYPE_REMAPPING_64 0xA2 /* 64 bit address remapping */ +#define HT_CAPTYPE_UNITID_CLUMP 0x90 /* Unit ID clumping */ +#define HT_CAPTYPE_EXTCONF 0x98 /* Extended Configuration Space Access */ +#define HT_CAPTYPE_MSI_MAPPING 0xA8 /* MSI Mapping Capability */ +#define HT_MSI_FLAGS 0x02 /* Offset to flags */ +#define HT_MSI_FLAGS_ENABLE 0x1 /* Mapping enable */ +#define HT_MSI_FLAGS_FIXED 0x2 /* Fixed mapping only */ +#define HT_MSI_FIXED_ADDR 0x00000000FEE00000ULL /* Fixed addr */ +#define HT_MSI_ADDR_LO 0x04 /* Offset to low addr bits */ +#define HT_MSI_ADDR_LO_MASK 0xFFF00000 /* Low address bit mask */ +#define HT_MSI_ADDR_HI 0x08 /* Offset to high addr bits */ +#define HT_CAPTYPE_DIRECT_ROUTE 0xB0 /* Direct routing configuration */ +#define HT_CAPTYPE_VCSET 0xB8 /* Virtual Channel configuration */ +#define HT_CAPTYPE_ERROR_RETRY 0xC0 /* Retry on error configuration */ +#define HT_CAPTYPE_GEN3 0xD0 /* Generation 3 hypertransport configuration */ +#define HT_CAPTYPE_PM 0xE0 /* Hypertransport powermanagement configuration */ + +/* Alternative Routing-ID Interpretation */ +#define PCI_ARI_CAP 0x04 /* ARI Capability Register */ +#define PCI_ARI_CAP_MFVC 0x0001 /* MFVC Function Groups Capability */ +#define PCI_ARI_CAP_ACS 0x0002 /* ACS Function Groups Capability */ +#define PCI_ARI_CAP_NFN(x) (((x) >> 8) & 0xff) /* Next Function Number */ +#define PCI_ARI_CTRL 0x06 /* ARI Control Register */ +#define PCI_ARI_CTRL_MFVC 0x0001 /* MFVC Function Groups Enable */ +#define PCI_ARI_CTRL_ACS 0x0002 /* ACS Function Groups Enable */ +#define PCI_ARI_CTRL_FG(x) (((x) >> 4) & 7) /* Function Group */ + +/* Address Translation Service */ +#define PCI_ATS_CAP 0x04 /* ATS Capability Register */ +#define PCI_ATS_CAP_QDEP(x) ((x) & 0x1f) /* Invalidate Queue Depth */ +#define PCI_ATS_MAX_QDEP 32 /* Max Invalidate Queue Depth */ +#define PCI_ATS_CTRL 0x06 /* ATS Control Register */ +#define PCI_ATS_CTRL_ENABLE 0x8000 /* ATS Enable */ +#define PCI_ATS_CTRL_STU(x) ((x) & 0x1f) /* Smallest Translation Unit */ +#define PCI_ATS_MIN_STU 12 /* shift of minimum STU block */ + +/* Single Root I/O Virtualization */ +#define PCI_SRIOV_CAP 0x04 /* SR-IOV Capabilities */ +#define PCI_SRIOV_CAP_VFM 0x01 /* VF Migration Capable */ +#define PCI_SRIOV_CAP_INTR(x) ((x) >> 21) /* Interrupt Message Number */ +#define PCI_SRIOV_CTRL 0x08 /* SR-IOV Control */ +#define PCI_SRIOV_CTRL_VFE 0x01 /* VF Enable */ +#define PCI_SRIOV_CTRL_VFM 0x02 /* VF Migration Enable */ +#define PCI_SRIOV_CTRL_INTR 0x04 /* VF Migration Interrupt Enable */ +#define PCI_SRIOV_CTRL_MSE 0x08 /* VF Memory Space Enable */ +#define PCI_SRIOV_CTRL_ARI 0x10 /* ARI Capable Hierarchy */ +#define PCI_SRIOV_STATUS 0x0a /* SR-IOV Status */ +#define PCI_SRIOV_STATUS_VFM 0x01 /* VF Migration Status */ +#define PCI_SRIOV_INITIAL_VF 0x0c /* Initial VFs */ +#define PCI_SRIOV_TOTAL_VF 0x0e /* Total VFs */ +#define PCI_SRIOV_NUM_VF 0x10 /* Number of VFs */ +#define PCI_SRIOV_FUNC_LINK 0x12 /* Function Dependency Link */ +#define PCI_SRIOV_VF_OFFSET 0x14 /* First VF Offset */ +#define PCI_SRIOV_VF_STRIDE 0x16 /* Following VF Stride */ +#define PCI_SRIOV_VF_DID 0x1a /* VF Device ID */ +#define PCI_SRIOV_SUP_PGSIZE 0x1c /* Supported Page Sizes */ +#define PCI_SRIOV_SYS_PGSIZE 0x20 /* System Page Size */ +#define PCI_SRIOV_BAR 0x24 /* VF BAR0 */ +#define PCI_SRIOV_NUM_BARS 6 /* Number of VF BARs */ +#define PCI_SRIOV_VFM 0x3c /* VF Migration State Array Offset*/ +#define PCI_SRIOV_VFM_BIR(x) ((x) & 7) /* State BIR */ +#define PCI_SRIOV_VFM_OFFSET(x) ((x) & ~7) /* State Offset */ +#define PCI_SRIOV_VFM_UA 0x0 /* Inactive.Unavailable */ +#define PCI_SRIOV_VFM_MI 0x1 /* Dormant.MigrateIn */ +#define PCI_SRIOV_VFM_MO 0x2 /* Active.MigrateOut */ +#define PCI_SRIOV_VFM_AV 0x3 /* Active.Available */ + +#define PCI_LTR_MAX_SNOOP_LAT 0x4 +#define PCI_LTR_MAX_NOSNOOP_LAT 0x6 +#define PCI_LTR_VALUE_MASK 0x000003ff +#define PCI_LTR_SCALE_MASK 0x00001c00 +#define PCI_LTR_SCALE_SHIFT 10 + +/* Access Control Service */ +#define PCI_ACS_CAP 0x04 /* ACS Capability Register */ +#define PCI_ACS_SV 0x01 /* Source Validation */ +#define PCI_ACS_TB 0x02 /* Translation Blocking */ +#define PCI_ACS_RR 0x04 /* P2P Request Redirect */ +#define PCI_ACS_CR 0x08 /* P2P Completion Redirect */ +#define PCI_ACS_UF 0x10 /* Upstream Forwarding */ +#define PCI_ACS_EC 0x20 /* P2P Egress Control */ +#define PCI_ACS_DT 0x40 /* Direct Translated P2P */ +#define PCI_ACS_CTRL 0x06 /* ACS Control Register */ +#define PCI_ACS_EGRESS_CTL_V 0x08 /* ACS Egress Control Vector */ + +#endif /* LINUX_PCI_REGS_H */ diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c new file mode 100644 index 0000000000..7c92f193e7 --- /dev/null +++ b/hw/pci/pcie.c @@ -0,0 +1,555 @@ +/* + * pcie.c + * + * Copyright (c) 2010 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "qemu-common.h" +#include "pci_bridge.h" +#include "pcie.h" +#include "msix.h" +#include "msi.h" +#include "pci_internals.h" +#include "pcie_regs.h" +#include "range.h" + +//#define DEBUG_PCIE +#ifdef DEBUG_PCIE +# define PCIE_DPRINTF(fmt, ...) \ + fprintf(stderr, "%s:%d " fmt, __func__, __LINE__, ## __VA_ARGS__) +#else +# define PCIE_DPRINTF(fmt, ...) do {} while (0) +#endif +#define PCIE_DEV_PRINTF(dev, fmt, ...) \ + PCIE_DPRINTF("%s:%x "fmt, (dev)->name, (dev)->devfn, ## __VA_ARGS__) + + +/*************************************************************************** + * pci express capability helper functions + */ +int pcie_cap_init(PCIDevice *dev, uint8_t offset, uint8_t type, uint8_t port) +{ + int pos; + uint8_t *exp_cap; + + assert(pci_is_express(dev)); + + pos = pci_add_capability(dev, PCI_CAP_ID_EXP, offset, + PCI_EXP_VER2_SIZEOF); + if (pos < 0) { + return pos; + } + dev->exp.exp_cap = pos; + exp_cap = dev->config + pos; + + /* capability register + interrupt message number defaults to 0 */ + pci_set_word(exp_cap + PCI_EXP_FLAGS, + ((type << PCI_EXP_FLAGS_TYPE_SHIFT) & PCI_EXP_FLAGS_TYPE) | + PCI_EXP_FLAGS_VER2); + + /* device capability register + * table 7-12: + * roll based error reporting bit must be set by all + * Functions conforming to the ECN, PCI Express Base + * Specification, Revision 1.1., or subsequent PCI Express Base + * Specification revisions. + */ + pci_set_long(exp_cap + PCI_EXP_DEVCAP, PCI_EXP_DEVCAP_RBER); + + pci_set_long(exp_cap + PCI_EXP_LNKCAP, + (port << PCI_EXP_LNKCAP_PN_SHIFT) | + PCI_EXP_LNKCAP_ASPMS_0S | + PCI_EXP_LNK_MLW_1 | + PCI_EXP_LNK_LS_25); + + pci_set_word(exp_cap + PCI_EXP_LNKSTA, + PCI_EXP_LNK_MLW_1 | PCI_EXP_LNK_LS_25); + + pci_set_long(exp_cap + PCI_EXP_DEVCAP2, + PCI_EXP_DEVCAP2_EFF | PCI_EXP_DEVCAP2_EETLPP); + + pci_set_word(dev->wmask + pos, PCI_EXP_DEVCTL2_EETLPPB); + return pos; +} + +void pcie_cap_exit(PCIDevice *dev) +{ + pci_del_capability(dev, PCI_CAP_ID_EXP, PCI_EXP_VER2_SIZEOF); +} + +uint8_t pcie_cap_get_type(const PCIDevice *dev) +{ + uint32_t pos = dev->exp.exp_cap; + assert(pos > 0); + return (pci_get_word(dev->config + pos + PCI_EXP_FLAGS) & + PCI_EXP_FLAGS_TYPE) >> PCI_EXP_FLAGS_TYPE_SHIFT; +} + +/* MSI/MSI-X */ +/* pci express interrupt message number */ +/* 7.8.2 PCI Express Capabilities Register: Interrupt Message Number */ +void pcie_cap_flags_set_vector(PCIDevice *dev, uint8_t vector) +{ + uint8_t *exp_cap = dev->config + dev->exp.exp_cap; + assert(vector < 32); + pci_word_test_and_clear_mask(exp_cap + PCI_EXP_FLAGS, PCI_EXP_FLAGS_IRQ); + pci_word_test_and_set_mask(exp_cap + PCI_EXP_FLAGS, + vector << PCI_EXP_FLAGS_IRQ_SHIFT); +} + +uint8_t pcie_cap_flags_get_vector(PCIDevice *dev) +{ + return (pci_get_word(dev->config + dev->exp.exp_cap + PCI_EXP_FLAGS) & + PCI_EXP_FLAGS_IRQ) >> PCI_EXP_FLAGS_IRQ_SHIFT; +} + +void pcie_cap_deverr_init(PCIDevice *dev) +{ + uint32_t pos = dev->exp.exp_cap; + pci_long_test_and_set_mask(dev->config + pos + PCI_EXP_DEVCAP, + PCI_EXP_DEVCAP_RBER); + pci_long_test_and_set_mask(dev->wmask + pos + PCI_EXP_DEVCTL, + PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | + PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE); + pci_long_test_and_set_mask(dev->w1cmask + pos + PCI_EXP_DEVSTA, + PCI_EXP_DEVSTA_CED | PCI_EXP_DEVSTA_NFED | + PCI_EXP_DEVSTA_URD | PCI_EXP_DEVSTA_URD); +} + +void pcie_cap_deverr_reset(PCIDevice *dev) +{ + uint8_t *devctl = dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL; + pci_long_test_and_clear_mask(devctl, + PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | + PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE); +} + +static void hotplug_event_update_event_status(PCIDevice *dev) +{ + uint32_t pos = dev->exp.exp_cap; + uint8_t *exp_cap = dev->config + pos; + uint16_t sltctl = pci_get_word(exp_cap + PCI_EXP_SLTCTL); + uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA); + + dev->exp.hpev_notified = (sltctl & PCI_EXP_SLTCTL_HPIE) && + (sltsta & sltctl & PCI_EXP_HP_EV_SUPPORTED); +} + +static void hotplug_event_notify(PCIDevice *dev) +{ + bool prev = dev->exp.hpev_notified; + + hotplug_event_update_event_status(dev); + + if (prev == dev->exp.hpev_notified) { + return; + } + + /* Note: the logic above does not take into account whether interrupts + * are masked. The result is that interrupt will be sent when it is + * subsequently unmasked. This appears to be legal: Section 6.7.3.4: + * The Port may optionally send an MSI when there are hot-plug events that + * occur while interrupt generation is disabled, and interrupt generation is + * subsequently enabled. */ + if (msix_enabled(dev)) { + msix_notify(dev, pcie_cap_flags_get_vector(dev)); + } else if (msi_enabled(dev)) { + msi_notify(dev, pcie_cap_flags_get_vector(dev)); + } else { + qemu_set_irq(dev->irq[dev->exp.hpev_intx], dev->exp.hpev_notified); + } +} + +static void hotplug_event_clear(PCIDevice *dev) +{ + hotplug_event_update_event_status(dev); + if (!msix_enabled(dev) && !msi_enabled(dev) && !dev->exp.hpev_notified) { + qemu_set_irq(dev->irq[dev->exp.hpev_intx], 0); + } +} + +/* + * A PCI Express Hot-Plug Event has occurred, so update slot status register + * and notify OS of the event if necessary. + * + * 6.7.3 PCI Express Hot-Plug Events + * 6.7.3.4 Software Notification of Hot-Plug Events + */ +static void pcie_cap_slot_event(PCIDevice *dev, PCIExpressHotPlugEvent event) +{ + /* Minor optimization: if nothing changed - no event is needed. */ + if (pci_word_test_and_set_mask(dev->config + dev->exp.exp_cap + + PCI_EXP_SLTSTA, event)) { + return; + } + hotplug_event_notify(dev); +} + +static int pcie_cap_slot_hotplug(DeviceState *qdev, + PCIDevice *pci_dev, PCIHotplugState state) +{ + PCIDevice *d = PCI_DEVICE(qdev); + uint8_t *exp_cap = d->config + d->exp.exp_cap; + uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA); + + /* Don't send event when device is enabled during qemu machine creation: + * it is present on boot, no hotplug event is necessary. We do send an + * event when the device is disabled later. */ + if (state == PCI_COLDPLUG_ENABLED) { + pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_PDS); + return 0; + } + + PCIE_DEV_PRINTF(pci_dev, "hotplug state: %d\n", state); + if (sltsta & PCI_EXP_SLTSTA_EIS) { + /* the slot is electromechanically locked. + * This error is propagated up to qdev and then to HMP/QMP. + */ + return -EBUSY; + } + + /* TODO: multifunction hot-plug. + * Right now, only a device of function = 0 is allowed to be + * hot plugged/unplugged. + */ + assert(PCI_FUNC(pci_dev->devfn) == 0); + + if (state == PCI_HOTPLUG_ENABLED) { + pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_PDS); + pcie_cap_slot_event(d, PCI_EXP_HP_EV_PDC); + } else { + qdev_free(&pci_dev->qdev); + pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_PDS); + pcie_cap_slot_event(d, PCI_EXP_HP_EV_PDC); + } + return 0; +} + +/* pci express slot for pci express root/downstream port + PCI express capability slot registers */ +void pcie_cap_slot_init(PCIDevice *dev, uint16_t slot) +{ + uint32_t pos = dev->exp.exp_cap; + + pci_word_test_and_set_mask(dev->config + pos + PCI_EXP_FLAGS, + PCI_EXP_FLAGS_SLOT); + + pci_long_test_and_clear_mask(dev->config + pos + PCI_EXP_SLTCAP, + ~PCI_EXP_SLTCAP_PSN); + pci_long_test_and_set_mask(dev->config + pos + PCI_EXP_SLTCAP, + (slot << PCI_EXP_SLTCAP_PSN_SHIFT) | + PCI_EXP_SLTCAP_EIP | + PCI_EXP_SLTCAP_HPS | + PCI_EXP_SLTCAP_HPC | + PCI_EXP_SLTCAP_PIP | + PCI_EXP_SLTCAP_AIP | + PCI_EXP_SLTCAP_ABP); + + pci_word_test_and_clear_mask(dev->config + pos + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_PIC | + PCI_EXP_SLTCTL_AIC); + pci_word_test_and_set_mask(dev->config + pos + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_PIC_OFF | + PCI_EXP_SLTCTL_AIC_OFF); + pci_word_test_and_set_mask(dev->wmask + pos + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_PIC | + PCI_EXP_SLTCTL_AIC | + PCI_EXP_SLTCTL_HPIE | + PCI_EXP_SLTCTL_CCIE | + PCI_EXP_SLTCTL_PDCE | + PCI_EXP_SLTCTL_ABPE); + /* Although reading PCI_EXP_SLTCTL_EIC returns always 0, + * make the bit writable here in order to detect 1b is written. + * pcie_cap_slot_write_config() test-and-clear the bit, so + * this bit always returns 0 to the guest. + */ + pci_word_test_and_set_mask(dev->wmask + pos + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_EIC); + + pci_word_test_and_set_mask(dev->w1cmask + pos + PCI_EXP_SLTSTA, + PCI_EXP_HP_EV_SUPPORTED); + + dev->exp.hpev_notified = false; + + pci_bus_hotplug(pci_bridge_get_sec_bus(DO_UPCAST(PCIBridge, dev, dev)), + pcie_cap_slot_hotplug, &dev->qdev); +} + +void pcie_cap_slot_reset(PCIDevice *dev) +{ + uint8_t *exp_cap = dev->config + dev->exp.exp_cap; + + PCIE_DEV_PRINTF(dev, "reset\n"); + + pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_EIC | + PCI_EXP_SLTCTL_PIC | + PCI_EXP_SLTCTL_AIC | + PCI_EXP_SLTCTL_HPIE | + PCI_EXP_SLTCTL_CCIE | + PCI_EXP_SLTCTL_PDCE | + PCI_EXP_SLTCTL_ABPE); + pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_PIC_OFF | + PCI_EXP_SLTCTL_AIC_OFF); + + pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_EIS |/* on reset, + the lock is released */ + PCI_EXP_SLTSTA_CC | + PCI_EXP_SLTSTA_PDC | + PCI_EXP_SLTSTA_ABP); + + hotplug_event_update_event_status(dev); +} + +void pcie_cap_slot_write_config(PCIDevice *dev, + uint32_t addr, uint32_t val, int len) +{ + uint32_t pos = dev->exp.exp_cap; + uint8_t *exp_cap = dev->config + pos; + uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA); + + if (ranges_overlap(addr, len, pos + PCI_EXP_SLTSTA, 2)) { + hotplug_event_clear(dev); + } + + if (!ranges_overlap(addr, len, pos + PCI_EXP_SLTCTL, 2)) { + return; + } + + if (pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_EIC)) { + sltsta ^= PCI_EXP_SLTSTA_EIS; /* toggle PCI_EXP_SLTSTA_EIS bit */ + pci_set_word(exp_cap + PCI_EXP_SLTSTA, sltsta); + PCIE_DEV_PRINTF(dev, "PCI_EXP_SLTCTL_EIC: " + "sltsta -> 0x%02"PRIx16"\n", + sltsta); + } + + hotplug_event_notify(dev); + + /* + * 6.7.3.2 Command Completed Events + * + * Software issues a command to a hot-plug capable Downstream Port by + * issuing a write transaction that targets any portion of the Port’s Slot + * Control register. A single write to the Slot Control register is + * considered to be a single command, even if the write affects more than + * one field in the Slot Control register. In response to this transaction, + * the Port must carry out the requested actions and then set the + * associated status field for the command completed event. */ + + /* Real hardware might take a while to complete requested command because + * physical movement would be involved like locking the electromechanical + * lock. However in our case, command is completed instantaneously above, + * so send a command completion event right now. + */ + pcie_cap_slot_event(dev, PCI_EXP_HP_EV_CCI); +} + +int pcie_cap_slot_post_load(void *opaque, int version_id) +{ + PCIDevice *dev = opaque; + hotplug_event_update_event_status(dev); + return 0; +} + +void pcie_cap_slot_push_attention_button(PCIDevice *dev) +{ + pcie_cap_slot_event(dev, PCI_EXP_HP_EV_ABP); +} + +/* root control/capabilities/status. PME isn't emulated for now */ +void pcie_cap_root_init(PCIDevice *dev) +{ + pci_set_word(dev->wmask + dev->exp.exp_cap + PCI_EXP_RTCTL, + PCI_EXP_RTCTL_SECEE | PCI_EXP_RTCTL_SENFEE | + PCI_EXP_RTCTL_SEFEE); +} + +void pcie_cap_root_reset(PCIDevice *dev) +{ + pci_set_word(dev->config + dev->exp.exp_cap + PCI_EXP_RTCTL, 0); +} + +/* function level reset(FLR) */ +void pcie_cap_flr_init(PCIDevice *dev) +{ + pci_long_test_and_set_mask(dev->config + dev->exp.exp_cap + PCI_EXP_DEVCAP, + PCI_EXP_DEVCAP_FLR); + + /* Although reading BCR_FLR returns always 0, + * the bit is made writable here in order to detect the 1b is written + * pcie_cap_flr_write_config() test-and-clear the bit, so + * this bit always returns 0 to the guest. + */ + pci_word_test_and_set_mask(dev->wmask + dev->exp.exp_cap + PCI_EXP_DEVCTL, + PCI_EXP_DEVCTL_BCR_FLR); +} + +void pcie_cap_flr_write_config(PCIDevice *dev, + uint32_t addr, uint32_t val, int len) +{ + uint8_t *devctl = dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL; + if (pci_get_word(devctl) & PCI_EXP_DEVCTL_BCR_FLR) { + /* Clear PCI_EXP_DEVCTL_BCR_FLR after invoking the reset handler + so the handler can detect FLR by looking at this bit. */ + pci_device_reset(dev); + pci_word_test_and_clear_mask(devctl, PCI_EXP_DEVCTL_BCR_FLR); + } +} + +/* Alternative Routing-ID Interpretation (ARI) */ +/* ari forwarding support for down stream port */ +void pcie_cap_ari_init(PCIDevice *dev) +{ + uint32_t pos = dev->exp.exp_cap; + pci_long_test_and_set_mask(dev->config + pos + PCI_EXP_DEVCAP2, + PCI_EXP_DEVCAP2_ARI); + pci_long_test_and_set_mask(dev->wmask + pos + PCI_EXP_DEVCTL2, + PCI_EXP_DEVCTL2_ARI); +} + +void pcie_cap_ari_reset(PCIDevice *dev) +{ + uint8_t *devctl2 = dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL2; + pci_long_test_and_clear_mask(devctl2, PCI_EXP_DEVCTL2_ARI); +} + +bool pcie_cap_is_ari_enabled(const PCIDevice *dev) +{ + if (!pci_is_express(dev)) { + return false; + } + if (!dev->exp.exp_cap) { + return false; + } + + return pci_get_long(dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL2) & + PCI_EXP_DEVCTL2_ARI; +} + +/************************************************************************** + * pci express extended capability allocation functions + * uint16_t ext_cap_id (16 bit) + * uint8_t cap_ver (4 bit) + * uint16_t cap_offset (12 bit) + * uint16_t ext_cap_size + */ + +static uint16_t pcie_find_capability_list(PCIDevice *dev, uint16_t cap_id, + uint16_t *prev_p) +{ + uint16_t prev = 0; + uint16_t next; + uint32_t header = pci_get_long(dev->config + PCI_CONFIG_SPACE_SIZE); + + if (!header) { + /* no extended capability */ + next = 0; + goto out; + } + for (next = PCI_CONFIG_SPACE_SIZE; next; + prev = next, next = PCI_EXT_CAP_NEXT(header)) { + + assert(next >= PCI_CONFIG_SPACE_SIZE); + assert(next <= PCIE_CONFIG_SPACE_SIZE - 8); + + header = pci_get_long(dev->config + next); + if (PCI_EXT_CAP_ID(header) == cap_id) { + break; + } + } + +out: + if (prev_p) { + *prev_p = prev; + } + return next; +} + +uint16_t pcie_find_capability(PCIDevice *dev, uint16_t cap_id) +{ + return pcie_find_capability_list(dev, cap_id, NULL); +} + +static void pcie_ext_cap_set_next(PCIDevice *dev, uint16_t pos, uint16_t next) +{ + uint16_t header = pci_get_long(dev->config + pos); + assert(!(next & (PCI_EXT_CAP_ALIGN - 1))); + header = (header & ~PCI_EXT_CAP_NEXT_MASK) | + ((next << PCI_EXT_CAP_NEXT_SHIFT) & PCI_EXT_CAP_NEXT_MASK); + pci_set_long(dev->config + pos, header); +} + +/* + * caller must supply valid (offset, size) * such that the range shouldn't + * overlap with other capability or other registers. + * This function doesn't check it. + */ +void pcie_add_capability(PCIDevice *dev, + uint16_t cap_id, uint8_t cap_ver, + uint16_t offset, uint16_t size) +{ + uint32_t header; + uint16_t next; + + assert(offset >= PCI_CONFIG_SPACE_SIZE); + assert(offset < offset + size); + assert(offset + size < PCIE_CONFIG_SPACE_SIZE); + assert(size >= 8); + assert(pci_is_express(dev)); + + if (offset == PCI_CONFIG_SPACE_SIZE) { + header = pci_get_long(dev->config + offset); + next = PCI_EXT_CAP_NEXT(header); + } else { + uint16_t prev; + + /* 0 is reserved cap id. use internally to find the last capability + in the linked list */ + next = pcie_find_capability_list(dev, 0, &prev); + + assert(prev >= PCI_CONFIG_SPACE_SIZE); + assert(next == 0); + pcie_ext_cap_set_next(dev, prev, offset); + } + pci_set_long(dev->config + offset, PCI_EXT_CAP(cap_id, cap_ver, next)); + + /* Make capability read-only by default */ + memset(dev->wmask + offset, 0, size); + memset(dev->w1cmask + offset, 0, size); + /* Check capability by default */ + memset(dev->cmask + offset, 0xFF, size); +} + +/************************************************************************** + * pci express extended capability helper functions + */ + +/* ARI */ +void pcie_ari_init(PCIDevice *dev, uint16_t offset, uint16_t nextfn) +{ + pcie_add_capability(dev, PCI_EXT_CAP_ID_ARI, PCI_ARI_VER, + offset, PCI_ARI_SIZEOF); + pci_set_long(dev->config + offset + PCI_ARI_CAP, PCI_ARI_CAP_NFN(nextfn)); +} diff --git a/hw/pci/pcie.h b/hw/pci/pcie.h new file mode 100644 index 0000000000..4889194ae6 --- /dev/null +++ b/hw/pci/pcie.h @@ -0,0 +1,142 @@ +/* + * pcie.h + * + * Copyright (c) 2010 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#ifndef QEMU_PCIE_H +#define QEMU_PCIE_H + +#include "hw.h" +#include "pci_regs.h" +#include "pcie_regs.h" +#include "pcie_aer.h" + +typedef enum { + /* for attention and power indicator */ + PCI_EXP_HP_IND_RESERVED = PCI_EXP_SLTCTL_IND_RESERVED, + PCI_EXP_HP_IND_ON = PCI_EXP_SLTCTL_IND_ON, + PCI_EXP_HP_IND_BLINK = PCI_EXP_SLTCTL_IND_BLINK, + PCI_EXP_HP_IND_OFF = PCI_EXP_SLTCTL_IND_OFF, +} PCIExpressIndicator; + +typedef enum { + /* these bits must match the bits in Slot Control/Status registers. + * PCI_EXP_HP_EV_xxx = PCI_EXP_SLTCTL_xxxE = PCI_EXP_SLTSTA_xxx + * + * Not all the bits of slot control register match with the ones of + * slot status. Not some bits of slot status register is used to + * show status, not to report event occurrence. + * So such bits must be masked out when checking the software + * notification condition. + */ + PCI_EXP_HP_EV_ABP = PCI_EXP_SLTCTL_ABPE, + /* attention button pressed */ + PCI_EXP_HP_EV_PDC = PCI_EXP_SLTCTL_PDCE, + /* presence detect changed */ + PCI_EXP_HP_EV_CCI = PCI_EXP_SLTCTL_CCIE, + /* command completed */ + + PCI_EXP_HP_EV_SUPPORTED = PCI_EXP_HP_EV_ABP | + PCI_EXP_HP_EV_PDC | + PCI_EXP_HP_EV_CCI, + /* supported event mask */ + + /* events not listed aren't supported */ +} PCIExpressHotPlugEvent; + +struct PCIExpressDevice { + /* Offset of express capability in config space */ + uint8_t exp_cap; + + /* SLOT */ + unsigned int hpev_intx; /* INTx for hot plug event (0-3:INT[A-D]#) + * default is 0 = INTA# + * If the chip wants to use other interrupt + * line, initialize this member with the + * desired number. + * If the chip dynamically changes this member, + * also initialize it when loaded as + * appropreately. + */ + bool hpev_notified; /* Logical AND of conditions for hot plug event. + Following 6.7.3.4: + Software Notification of Hot-Plug Events, an interrupt + is sent whenever the logical and of these conditions + transitions from false to true. */ + + /* AER */ + uint16_t aer_cap; + PCIEAERLog aer_log; + unsigned int aer_intx; /* INTx for error reporting + * default is 0 = INTA# + * If the chip wants to use other interrupt + * line, initialize this member with the + * desired number. + * If the chip dynamically changes this member, + * also initialize it when loaded as + * appropreately. + */ +}; + +/* PCI express capability helper functions */ +int pcie_cap_init(PCIDevice *dev, uint8_t offset, uint8_t type, uint8_t port); +void pcie_cap_exit(PCIDevice *dev); +uint8_t pcie_cap_get_type(const PCIDevice *dev); +void pcie_cap_flags_set_vector(PCIDevice *dev, uint8_t vector); +uint8_t pcie_cap_flags_get_vector(PCIDevice *dev); + +void pcie_cap_deverr_init(PCIDevice *dev); +void pcie_cap_deverr_reset(PCIDevice *dev); + +void pcie_cap_slot_init(PCIDevice *dev, uint16_t slot); +void pcie_cap_slot_reset(PCIDevice *dev); +void pcie_cap_slot_write_config(PCIDevice *dev, + uint32_t addr, uint32_t val, int len); +int pcie_cap_slot_post_load(void *opaque, int version_id); +void pcie_cap_slot_push_attention_button(PCIDevice *dev); + +void pcie_cap_root_init(PCIDevice *dev); +void pcie_cap_root_reset(PCIDevice *dev); + +void pcie_cap_flr_init(PCIDevice *dev); +void pcie_cap_flr_write_config(PCIDevice *dev, + uint32_t addr, uint32_t val, int len); + +void pcie_cap_ari_init(PCIDevice *dev); +void pcie_cap_ari_reset(PCIDevice *dev); +bool pcie_cap_is_ari_enabled(const PCIDevice *dev); + +/* PCI express extended capability helper functions */ +uint16_t pcie_find_capability(PCIDevice *dev, uint16_t cap_id); +void pcie_add_capability(PCIDevice *dev, + uint16_t cap_id, uint8_t cap_ver, + uint16_t offset, uint16_t size); + +void pcie_ari_init(PCIDevice *dev, uint16_t offset, uint16_t nextfn); + +extern const VMStateDescription vmstate_pcie_device; + +#define VMSTATE_PCIE_DEVICE(_field, _state) { \ + .name = (stringify(_field)), \ + .size = sizeof(PCIDevice), \ + .vmsd = &vmstate_pcie_device, \ + .flags = VMS_STRUCT, \ + .offset = vmstate_offset_value(_state, _field, PCIDevice), \ +} + +#endif /* QEMU_PCIE_H */ diff --git a/hw/pci/pcie_aer.c b/hw/pci/pcie_aer.c new file mode 100644 index 0000000000..b04c164e22 --- /dev/null +++ b/hw/pci/pcie_aer.c @@ -0,0 +1,1032 @@ +/* + * pcie_aer.c + * + * Copyright (c) 2010 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "sysemu.h" +#include "qemu-objects.h" +#include "monitor.h" +#include "pci_bridge.h" +#include "pcie.h" +#include "msix.h" +#include "msi.h" +#include "pci_internals.h" +#include "pcie_regs.h" + +//#define DEBUG_PCIE +#ifdef DEBUG_PCIE +# define PCIE_DPRINTF(fmt, ...) \ + fprintf(stderr, "%s:%d " fmt, __func__, __LINE__, ## __VA_ARGS__) +#else +# define PCIE_DPRINTF(fmt, ...) do {} while (0) +#endif +#define PCIE_DEV_PRINTF(dev, fmt, ...) \ + PCIE_DPRINTF("%s:%x "fmt, (dev)->name, (dev)->devfn, ## __VA_ARGS__) + +#define PCI_ERR_SRC_COR_OFFS 0 +#define PCI_ERR_SRC_UNCOR_OFFS 2 + +/* From 6.2.7 Error Listing and Rules. Table 6-2, 6-3 and 6-4 */ +static uint32_t pcie_aer_uncor_default_severity(uint32_t status) +{ + switch (status) { + case PCI_ERR_UNC_INTN: + case PCI_ERR_UNC_DLP: + case PCI_ERR_UNC_SDN: + case PCI_ERR_UNC_RX_OVER: + case PCI_ERR_UNC_FCP: + case PCI_ERR_UNC_MALF_TLP: + return PCI_ERR_ROOT_CMD_FATAL_EN; + case PCI_ERR_UNC_POISON_TLP: + case PCI_ERR_UNC_ECRC: + case PCI_ERR_UNC_UNSUP: + case PCI_ERR_UNC_COMP_TIME: + case PCI_ERR_UNC_COMP_ABORT: + case PCI_ERR_UNC_UNX_COMP: + case PCI_ERR_UNC_ACSV: + case PCI_ERR_UNC_MCBTLP: + case PCI_ERR_UNC_ATOP_EBLOCKED: + case PCI_ERR_UNC_TLP_PRF_BLOCKED: + return PCI_ERR_ROOT_CMD_NONFATAL_EN; + default: + abort(); + break; + } + return PCI_ERR_ROOT_CMD_FATAL_EN; +} + +static int aer_log_add_err(PCIEAERLog *aer_log, const PCIEAERErr *err) +{ + if (aer_log->log_num == aer_log->log_max) { + return -1; + } + memcpy(&aer_log->log[aer_log->log_num], err, sizeof *err); + aer_log->log_num++; + return 0; +} + +static void aer_log_del_err(PCIEAERLog *aer_log, PCIEAERErr *err) +{ + assert(aer_log->log_num); + *err = aer_log->log[0]; + aer_log->log_num--; + memmove(&aer_log->log[0], &aer_log->log[1], + aer_log->log_num * sizeof *err); +} + +static void aer_log_clear_all_err(PCIEAERLog *aer_log) +{ + aer_log->log_num = 0; +} + +int pcie_aer_init(PCIDevice *dev, uint16_t offset) +{ + PCIExpressDevice *exp; + + pcie_add_capability(dev, PCI_EXT_CAP_ID_ERR, PCI_ERR_VER, + offset, PCI_ERR_SIZEOF); + exp = &dev->exp; + exp->aer_cap = offset; + + /* log_max is property */ + if (dev->exp.aer_log.log_max == PCIE_AER_LOG_MAX_UNSET) { + dev->exp.aer_log.log_max = PCIE_AER_LOG_MAX_DEFAULT; + } + /* clip down the value to avoid unreasobale memory usage */ + if (dev->exp.aer_log.log_max > PCIE_AER_LOG_MAX_LIMIT) { + return -EINVAL; + } + dev->exp.aer_log.log = g_malloc0(sizeof dev->exp.aer_log.log[0] * + dev->exp.aer_log.log_max); + + pci_set_long(dev->w1cmask + offset + PCI_ERR_UNCOR_STATUS, + PCI_ERR_UNC_SUPPORTED); + + pci_set_long(dev->config + offset + PCI_ERR_UNCOR_SEVER, + PCI_ERR_UNC_SEVERITY_DEFAULT); + pci_set_long(dev->wmask + offset + PCI_ERR_UNCOR_SEVER, + PCI_ERR_UNC_SUPPORTED); + + pci_long_test_and_set_mask(dev->w1cmask + offset + PCI_ERR_COR_STATUS, + PCI_ERR_COR_STATUS); + + pci_set_long(dev->config + offset + PCI_ERR_COR_MASK, + PCI_ERR_COR_MASK_DEFAULT); + pci_set_long(dev->wmask + offset + PCI_ERR_COR_MASK, + PCI_ERR_COR_SUPPORTED); + + /* capabilities and control. multiple header logging is supported */ + if (dev->exp.aer_log.log_max > 0) { + pci_set_long(dev->config + offset + PCI_ERR_CAP, + PCI_ERR_CAP_ECRC_GENC | PCI_ERR_CAP_ECRC_CHKC | + PCI_ERR_CAP_MHRC); + pci_set_long(dev->wmask + offset + PCI_ERR_CAP, + PCI_ERR_CAP_ECRC_GENE | PCI_ERR_CAP_ECRC_CHKE | + PCI_ERR_CAP_MHRE); + } else { + pci_set_long(dev->config + offset + PCI_ERR_CAP, + PCI_ERR_CAP_ECRC_GENC | PCI_ERR_CAP_ECRC_CHKC); + pci_set_long(dev->wmask + offset + PCI_ERR_CAP, + PCI_ERR_CAP_ECRC_GENE | PCI_ERR_CAP_ECRC_CHKE); + } + + switch (pcie_cap_get_type(dev)) { + case PCI_EXP_TYPE_ROOT_PORT: + /* this case will be set by pcie_aer_root_init() */ + /* fallthrough */ + case PCI_EXP_TYPE_DOWNSTREAM: + case PCI_EXP_TYPE_UPSTREAM: + pci_word_test_and_set_mask(dev->wmask + PCI_BRIDGE_CONTROL, + PCI_BRIDGE_CTL_SERR); + pci_long_test_and_set_mask(dev->w1cmask + PCI_STATUS, + PCI_SEC_STATUS_RCV_SYSTEM_ERROR); + break; + default: + /* nothing */ + break; + } + return 0; +} + +void pcie_aer_exit(PCIDevice *dev) +{ + g_free(dev->exp.aer_log.log); +} + +static void pcie_aer_update_uncor_status(PCIDevice *dev) +{ + uint8_t *aer_cap = dev->config + dev->exp.aer_cap; + PCIEAERLog *aer_log = &dev->exp.aer_log; + + uint16_t i; + for (i = 0; i < aer_log->log_num; i++) { + pci_long_test_and_set_mask(aer_cap + PCI_ERR_UNCOR_STATUS, + dev->exp.aer_log.log[i].status); + } +} + +/* + * return value: + * true: error message needs to be sent up + * false: error message is masked + * + * 6.2.6 Error Message Control + * Figure 6-3 + * all pci express devices part + */ +static bool +pcie_aer_msg_alldev(PCIDevice *dev, const PCIEAERMsg *msg) +{ + if (!(pcie_aer_msg_is_uncor(msg) && + (pci_get_word(dev->config + PCI_COMMAND) & PCI_COMMAND_SERR))) { + return false; + } + + /* Signaled System Error + * + * 7.5.1.1 Command register + * Bit 8 SERR# Enable + * + * When Set, this bit enables reporting of Non-fatal and Fatal + * errors detected by the Function to the Root Complex. Note that + * errors are reported if enabled either through this bit or through + * the PCI Express specific bits in the Device Control register (see + * Section 7.8.4). + */ + pci_word_test_and_set_mask(dev->config + PCI_STATUS, + PCI_STATUS_SIG_SYSTEM_ERROR); + + if (!(msg->severity & + pci_get_word(dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL))) { + return false; + } + + /* send up error message */ + return true; +} + +/* + * return value: + * true: error message is sent up + * false: error message is masked + * + * 6.2.6 Error Message Control + * Figure 6-3 + * virtual pci bridge part + */ +static bool pcie_aer_msg_vbridge(PCIDevice *dev, const PCIEAERMsg *msg) +{ + uint16_t bridge_control = pci_get_word(dev->config + PCI_BRIDGE_CONTROL); + + if (pcie_aer_msg_is_uncor(msg)) { + /* Received System Error */ + pci_word_test_and_set_mask(dev->config + PCI_SEC_STATUS, + PCI_SEC_STATUS_RCV_SYSTEM_ERROR); + } + + if (!(bridge_control & PCI_BRIDGE_CTL_SERR)) { + return false; + } + return true; +} + +void pcie_aer_root_set_vector(PCIDevice *dev, unsigned int vector) +{ + uint8_t *aer_cap = dev->config + dev->exp.aer_cap; + assert(vector < PCI_ERR_ROOT_IRQ_MAX); + pci_long_test_and_clear_mask(aer_cap + PCI_ERR_ROOT_STATUS, + PCI_ERR_ROOT_IRQ); + pci_long_test_and_set_mask(aer_cap + PCI_ERR_ROOT_STATUS, + vector << PCI_ERR_ROOT_IRQ_SHIFT); +} + +static unsigned int pcie_aer_root_get_vector(PCIDevice *dev) +{ + uint8_t *aer_cap = dev->config + dev->exp.aer_cap; + uint32_t root_status = pci_get_long(aer_cap + PCI_ERR_ROOT_STATUS); + return (root_status & PCI_ERR_ROOT_IRQ) >> PCI_ERR_ROOT_IRQ_SHIFT; +} + +/* Given a status register, get corresponding bits in the command register */ +static uint32_t pcie_aer_status_to_cmd(uint32_t status) +{ + uint32_t cmd = 0; + if (status & PCI_ERR_ROOT_COR_RCV) { + cmd |= PCI_ERR_ROOT_CMD_COR_EN; + } + if (status & PCI_ERR_ROOT_NONFATAL_RCV) { + cmd |= PCI_ERR_ROOT_CMD_NONFATAL_EN; + } + if (status & PCI_ERR_ROOT_FATAL_RCV) { + cmd |= PCI_ERR_ROOT_CMD_FATAL_EN; + } + return cmd; +} + +static void pcie_aer_root_notify(PCIDevice *dev) +{ + if (msix_enabled(dev)) { + msix_notify(dev, pcie_aer_root_get_vector(dev)); + } else if (msi_enabled(dev)) { + msi_notify(dev, pcie_aer_root_get_vector(dev)); + } else { + qemu_set_irq(dev->irq[dev->exp.aer_intx], 1); + } +} + +/* + * 6.2.6 Error Message Control + * Figure 6-3 + * root port part + */ +static void pcie_aer_msg_root_port(PCIDevice *dev, const PCIEAERMsg *msg) +{ + uint16_t cmd; + uint8_t *aer_cap; + uint32_t root_cmd; + uint32_t root_status, prev_status; + + cmd = pci_get_word(dev->config + PCI_COMMAND); + aer_cap = dev->config + dev->exp.aer_cap; + root_cmd = pci_get_long(aer_cap + PCI_ERR_ROOT_COMMAND); + prev_status = root_status = pci_get_long(aer_cap + PCI_ERR_ROOT_STATUS); + + if (cmd & PCI_COMMAND_SERR) { + /* System Error. + * + * The way to report System Error is platform specific and + * it isn't implemented in qemu right now. + * So just discard the error for now. + * OS which cares of aer would receive errors via + * native aer mechanims, so this wouldn't matter. + */ + } + + /* Errro Message Received: Root Error Status register */ + switch (msg->severity) { + case PCI_ERR_ROOT_CMD_COR_EN: + if (root_status & PCI_ERR_ROOT_COR_RCV) { + root_status |= PCI_ERR_ROOT_MULTI_COR_RCV; + } else { + pci_set_word(aer_cap + PCI_ERR_ROOT_ERR_SRC + PCI_ERR_SRC_COR_OFFS, + msg->source_id); + } + root_status |= PCI_ERR_ROOT_COR_RCV; + break; + case PCI_ERR_ROOT_CMD_NONFATAL_EN: + root_status |= PCI_ERR_ROOT_NONFATAL_RCV; + break; + case PCI_ERR_ROOT_CMD_FATAL_EN: + if (!(root_status & PCI_ERR_ROOT_UNCOR_RCV)) { + root_status |= PCI_ERR_ROOT_FIRST_FATAL; + } + root_status |= PCI_ERR_ROOT_FATAL_RCV; + break; + default: + abort(); + break; + } + if (pcie_aer_msg_is_uncor(msg)) { + if (root_status & PCI_ERR_ROOT_UNCOR_RCV) { + root_status |= PCI_ERR_ROOT_MULTI_UNCOR_RCV; + } else { + pci_set_word(aer_cap + PCI_ERR_ROOT_ERR_SRC + + PCI_ERR_SRC_UNCOR_OFFS, msg->source_id); + } + root_status |= PCI_ERR_ROOT_UNCOR_RCV; + } + pci_set_long(aer_cap + PCI_ERR_ROOT_STATUS, root_status); + + /* 6.2.4.1.2 Interrupt Generation */ + /* All the above did was set some bits in the status register. + * Specifically these that match message severity. + * The below code relies on this fact. */ + if (!(root_cmd & msg->severity) || + (pcie_aer_status_to_cmd(prev_status) & root_cmd)) { + /* Condition is not being set or was already true so nothing to do. */ + return; + } + + pcie_aer_root_notify(dev); +} + +/* + * 6.2.6 Error Message Control Figure 6-3 + * + * Walk up the bus tree from the device, propagate the error message. + */ +static void pcie_aer_msg(PCIDevice *dev, const PCIEAERMsg *msg) +{ + uint8_t type; + + while (dev) { + if (!pci_is_express(dev)) { + /* just ignore it */ + /* TODO: Shouldn't we set PCI_STATUS_SIG_SYSTEM_ERROR? + * Consider e.g. a PCI bridge above a PCI Express device. */ + return; + } + + type = pcie_cap_get_type(dev); + if ((type == PCI_EXP_TYPE_ROOT_PORT || + type == PCI_EXP_TYPE_UPSTREAM || + type == PCI_EXP_TYPE_DOWNSTREAM) && + !pcie_aer_msg_vbridge(dev, msg)) { + return; + } + if (!pcie_aer_msg_alldev(dev, msg)) { + return; + } + if (type == PCI_EXP_TYPE_ROOT_PORT) { + pcie_aer_msg_root_port(dev, msg); + /* Root port can notify system itself, + or send the error message to root complex event collector. */ + /* + * if root port is associated with an event collector, + * return the root complex event collector here. + * For now root complex event collector isn't supported. + */ + return; + } + dev = pci_bridge_get_device(dev->bus); + } +} + +static void pcie_aer_update_log(PCIDevice *dev, const PCIEAERErr *err) +{ + uint8_t *aer_cap = dev->config + dev->exp.aer_cap; + uint8_t first_bit = ffs(err->status) - 1; + uint32_t errcap = pci_get_long(aer_cap + PCI_ERR_CAP); + int i; + + assert(err->status); + assert(!(err->status & (err->status - 1))); + + errcap &= ~(PCI_ERR_CAP_FEP_MASK | PCI_ERR_CAP_TLP); + errcap |= PCI_ERR_CAP_FEP(first_bit); + + if (err->flags & PCIE_AER_ERR_HEADER_VALID) { + for (i = 0; i < ARRAY_SIZE(err->header); ++i) { + /* 7.10.8 Header Log Register */ + uint8_t *header_log = + aer_cap + PCI_ERR_HEADER_LOG + i * sizeof err->header[0]; + cpu_to_be32wu((uint32_t*)header_log, err->header[i]); + } + } else { + assert(!(err->flags & PCIE_AER_ERR_TLP_PREFIX_PRESENT)); + memset(aer_cap + PCI_ERR_HEADER_LOG, 0, PCI_ERR_HEADER_LOG_SIZE); + } + + if ((err->flags & PCIE_AER_ERR_TLP_PREFIX_PRESENT) && + (pci_get_long(dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL2) & + PCI_EXP_DEVCAP2_EETLPP)) { + for (i = 0; i < ARRAY_SIZE(err->prefix); ++i) { + /* 7.10.12 tlp prefix log register */ + uint8_t *prefix_log = + aer_cap + PCI_ERR_TLP_PREFIX_LOG + i * sizeof err->prefix[0]; + cpu_to_be32wu((uint32_t*)prefix_log, err->prefix[i]); + } + errcap |= PCI_ERR_CAP_TLP; + } else { + memset(aer_cap + PCI_ERR_TLP_PREFIX_LOG, 0, + PCI_ERR_TLP_PREFIX_LOG_SIZE); + } + pci_set_long(aer_cap + PCI_ERR_CAP, errcap); +} + +static void pcie_aer_clear_log(PCIDevice *dev) +{ + uint8_t *aer_cap = dev->config + dev->exp.aer_cap; + + pci_long_test_and_clear_mask(aer_cap + PCI_ERR_CAP, + PCI_ERR_CAP_FEP_MASK | PCI_ERR_CAP_TLP); + + memset(aer_cap + PCI_ERR_HEADER_LOG, 0, PCI_ERR_HEADER_LOG_SIZE); + memset(aer_cap + PCI_ERR_TLP_PREFIX_LOG, 0, PCI_ERR_TLP_PREFIX_LOG_SIZE); +} + +static void pcie_aer_clear_error(PCIDevice *dev) +{ + uint8_t *aer_cap = dev->config + dev->exp.aer_cap; + uint32_t errcap = pci_get_long(aer_cap + PCI_ERR_CAP); + PCIEAERLog *aer_log = &dev->exp.aer_log; + PCIEAERErr err; + + if (!(errcap & PCI_ERR_CAP_MHRE) || !aer_log->log_num) { + pcie_aer_clear_log(dev); + return; + } + + /* + * If more errors are queued, set corresponding bits in uncorrectable + * error status. + * We emulate uncorrectable error status register as W1CS. + * So set bit in uncorrectable error status here again for multiple + * error recording support. + * + * 6.2.4.2 Multiple Error Handling(Advanced Error Reporting Capability) + */ + pcie_aer_update_uncor_status(dev); + + aer_log_del_err(aer_log, &err); + pcie_aer_update_log(dev, &err); +} + +static int pcie_aer_record_error(PCIDevice *dev, + const PCIEAERErr *err) +{ + uint8_t *aer_cap = dev->config + dev->exp.aer_cap; + uint32_t errcap = pci_get_long(aer_cap + PCI_ERR_CAP); + int fep = PCI_ERR_CAP_FEP(errcap); + + assert(err->status); + assert(!(err->status & (err->status - 1))); + + if (errcap & PCI_ERR_CAP_MHRE && + (pci_get_long(aer_cap + PCI_ERR_UNCOR_STATUS) & (1U << fep))) { + /* Not first error. queue error */ + if (aer_log_add_err(&dev->exp.aer_log, err) < 0) { + /* overflow */ + return -1; + } + return 0; + } + + pcie_aer_update_log(dev, err); + return 0; +} + +typedef struct PCIEAERInject { + PCIDevice *dev; + uint8_t *aer_cap; + const PCIEAERErr *err; + uint16_t devctl; + uint16_t devsta; + uint32_t error_status; + bool unsupported_request; + bool log_overflow; + PCIEAERMsg msg; +} PCIEAERInject; + +static bool pcie_aer_inject_cor_error(PCIEAERInject *inj, + uint32_t uncor_status, + bool is_advisory_nonfatal) +{ + PCIDevice *dev = inj->dev; + + inj->devsta |= PCI_EXP_DEVSTA_CED; + if (inj->unsupported_request) { + inj->devsta |= PCI_EXP_DEVSTA_URD; + } + pci_set_word(dev->config + dev->exp.exp_cap + PCI_EXP_DEVSTA, inj->devsta); + + if (inj->aer_cap) { + uint32_t mask; + pci_long_test_and_set_mask(inj->aer_cap + PCI_ERR_COR_STATUS, + inj->error_status); + mask = pci_get_long(inj->aer_cap + PCI_ERR_COR_MASK); + if (mask & inj->error_status) { + return false; + } + if (is_advisory_nonfatal) { + uint32_t uncor_mask = + pci_get_long(inj->aer_cap + PCI_ERR_UNCOR_MASK); + if (!(uncor_mask & uncor_status)) { + inj->log_overflow = !!pcie_aer_record_error(dev, inj->err); + } + pci_long_test_and_set_mask(inj->aer_cap + PCI_ERR_UNCOR_STATUS, + uncor_status); + } + } + + if (inj->unsupported_request && !(inj->devctl & PCI_EXP_DEVCTL_URRE)) { + return false; + } + if (!(inj->devctl & PCI_EXP_DEVCTL_CERE)) { + return false; + } + + inj->msg.severity = PCI_ERR_ROOT_CMD_COR_EN; + return true; +} + +static bool pcie_aer_inject_uncor_error(PCIEAERInject *inj, bool is_fatal) +{ + PCIDevice *dev = inj->dev; + uint16_t cmd; + + if (is_fatal) { + inj->devsta |= PCI_EXP_DEVSTA_FED; + } else { + inj->devsta |= PCI_EXP_DEVSTA_NFED; + } + if (inj->unsupported_request) { + inj->devsta |= PCI_EXP_DEVSTA_URD; + } + pci_set_long(dev->config + dev->exp.exp_cap + PCI_EXP_DEVSTA, inj->devsta); + + if (inj->aer_cap) { + uint32_t mask = pci_get_long(inj->aer_cap + PCI_ERR_UNCOR_MASK); + if (mask & inj->error_status) { + pci_long_test_and_set_mask(inj->aer_cap + PCI_ERR_UNCOR_STATUS, + inj->error_status); + return false; + } + + inj->log_overflow = !!pcie_aer_record_error(dev, inj->err); + pci_long_test_and_set_mask(inj->aer_cap + PCI_ERR_UNCOR_STATUS, + inj->error_status); + } + + cmd = pci_get_word(dev->config + PCI_COMMAND); + if (inj->unsupported_request && + !(inj->devctl & PCI_EXP_DEVCTL_URRE) && !(cmd & PCI_COMMAND_SERR)) { + return false; + } + if (is_fatal) { + if (!((cmd & PCI_COMMAND_SERR) || + (inj->devctl & PCI_EXP_DEVCTL_FERE))) { + return false; + } + inj->msg.severity = PCI_ERR_ROOT_CMD_FATAL_EN; + } else { + if (!((cmd & PCI_COMMAND_SERR) || + (inj->devctl & PCI_EXP_DEVCTL_NFERE))) { + return false; + } + inj->msg.severity = PCI_ERR_ROOT_CMD_NONFATAL_EN; + } + return true; +} + +/* + * non-Function specific error must be recorded in all functions. + * It is the responsibility of the caller of this function. + * It is also caller's responsibility to determine which function should + * report the rerror. + * + * 6.2.4 Error Logging + * 6.2.5 Sqeunce of Device Error Signaling and Logging Operations + * table 6-2: Flowchard Showing Sequence of Device Error Signaling and Logging + * Operations + */ +int pcie_aer_inject_error(PCIDevice *dev, const PCIEAERErr *err) +{ + uint8_t *aer_cap = NULL; + uint16_t devctl = 0; + uint16_t devsta = 0; + uint32_t error_status = err->status; + PCIEAERInject inj; + + if (!pci_is_express(dev)) { + return -ENOSYS; + } + + if (err->flags & PCIE_AER_ERR_IS_CORRECTABLE) { + error_status &= PCI_ERR_COR_SUPPORTED; + } else { + error_status &= PCI_ERR_UNC_SUPPORTED; + } + + /* invalid status bit. one and only one bit must be set */ + if (!error_status || (error_status & (error_status - 1))) { + return -EINVAL; + } + + if (dev->exp.aer_cap) { + uint8_t *exp_cap = dev->config + dev->exp.exp_cap; + aer_cap = dev->config + dev->exp.aer_cap; + devctl = pci_get_long(exp_cap + PCI_EXP_DEVCTL); + devsta = pci_get_long(exp_cap + PCI_EXP_DEVSTA); + } + + inj.dev = dev; + inj.aer_cap = aer_cap; + inj.err = err; + inj.devctl = devctl; + inj.devsta = devsta; + inj.error_status = error_status; + inj.unsupported_request = !(err->flags & PCIE_AER_ERR_IS_CORRECTABLE) && + err->status == PCI_ERR_UNC_UNSUP; + inj.log_overflow = false; + + if (err->flags & PCIE_AER_ERR_IS_CORRECTABLE) { + if (!pcie_aer_inject_cor_error(&inj, 0, false)) { + return 0; + } + } else { + bool is_fatal = + pcie_aer_uncor_default_severity(error_status) == + PCI_ERR_ROOT_CMD_FATAL_EN; + if (aer_cap) { + is_fatal = + error_status & pci_get_long(aer_cap + PCI_ERR_UNCOR_SEVER); + } + if (!is_fatal && (err->flags & PCIE_AER_ERR_MAYBE_ADVISORY)) { + inj.error_status = PCI_ERR_COR_ADV_NONFATAL; + if (!pcie_aer_inject_cor_error(&inj, error_status, true)) { + return 0; + } + } else { + if (!pcie_aer_inject_uncor_error(&inj, is_fatal)) { + return 0; + } + } + } + + /* send up error message */ + inj.msg.source_id = err->source_id; + pcie_aer_msg(dev, &inj.msg); + + if (inj.log_overflow) { + PCIEAERErr header_log_overflow = { + .status = PCI_ERR_COR_HL_OVERFLOW, + .flags = PCIE_AER_ERR_IS_CORRECTABLE, + }; + int ret = pcie_aer_inject_error(dev, &header_log_overflow); + assert(!ret); + } + return 0; +} + +void pcie_aer_write_config(PCIDevice *dev, + uint32_t addr, uint32_t val, int len) +{ + uint8_t *aer_cap = dev->config + dev->exp.aer_cap; + uint32_t errcap = pci_get_long(aer_cap + PCI_ERR_CAP); + uint32_t first_error = 1U << PCI_ERR_CAP_FEP(errcap); + uint32_t uncorsta = pci_get_long(aer_cap + PCI_ERR_UNCOR_STATUS); + + /* uncorrectable error */ + if (!(uncorsta & first_error)) { + /* the bit that corresponds to the first error is cleared */ + pcie_aer_clear_error(dev); + } else if (errcap & PCI_ERR_CAP_MHRE) { + /* When PCI_ERR_CAP_MHRE is enabled and the first error isn't cleared + * nothing should happen. So we have to revert the modification to + * the register. + */ + pcie_aer_update_uncor_status(dev); + } else { + /* capability & control + * PCI_ERR_CAP_MHRE might be cleared, so clear of header log. + */ + aer_log_clear_all_err(&dev->exp.aer_log); + } +} + +void pcie_aer_root_init(PCIDevice *dev) +{ + uint16_t pos = dev->exp.aer_cap; + + pci_set_long(dev->wmask + pos + PCI_ERR_ROOT_COMMAND, + PCI_ERR_ROOT_CMD_EN_MASK); + pci_set_long(dev->w1cmask + pos + PCI_ERR_ROOT_STATUS, + PCI_ERR_ROOT_STATUS_REPORT_MASK); + /* PCI_ERR_ROOT_IRQ is RO but devices change it using a + * device-specific method. + */ + pci_set_long(dev->cmask + pos + PCI_ERR_ROOT_STATUS, + ~PCI_ERR_ROOT_IRQ); +} + +void pcie_aer_root_reset(PCIDevice *dev) +{ + uint8_t* aer_cap = dev->config + dev->exp.aer_cap; + + pci_set_long(aer_cap + PCI_ERR_ROOT_COMMAND, 0); + + /* + * Advanced Error Interrupt Message Number in Root Error Status Register + * must be updated by chip dependent code because it's chip dependent + * which number is used. + */ +} + +void pcie_aer_root_write_config(PCIDevice *dev, + uint32_t addr, uint32_t val, int len, + uint32_t root_cmd_prev) +{ + uint8_t *aer_cap = dev->config + dev->exp.aer_cap; + uint32_t root_status = pci_get_long(aer_cap + PCI_ERR_ROOT_STATUS); + uint32_t enabled_cmd = pcie_aer_status_to_cmd(root_status); + uint32_t root_cmd = pci_get_long(aer_cap + PCI_ERR_ROOT_COMMAND); + /* 6.2.4.1.2 Interrupt Generation */ + if (!msix_enabled(dev) && !msi_enabled(dev)) { + qemu_set_irq(dev->irq[dev->exp.aer_intx], !!(root_cmd & enabled_cmd)); + return; + } + + if ((root_cmd_prev & enabled_cmd) || !(root_cmd & enabled_cmd)) { + /* Send MSI on transition from false to true. */ + return; + } + + pcie_aer_root_notify(dev); +} + +static const VMStateDescription vmstate_pcie_aer_err = { + .name = "PCIE_AER_ERROR", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(status, PCIEAERErr), + VMSTATE_UINT16(source_id, PCIEAERErr), + VMSTATE_UINT16(flags, PCIEAERErr), + VMSTATE_UINT32_ARRAY(header, PCIEAERErr, 4), + VMSTATE_UINT32_ARRAY(prefix, PCIEAERErr, 4), + VMSTATE_END_OF_LIST() + } +}; + +const VMStateDescription vmstate_pcie_aer_log = { + .name = "PCIE_AER_ERROR_LOG", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT16(log_num, PCIEAERLog), + VMSTATE_UINT16(log_max, PCIEAERLog), + VMSTATE_STRUCT_VARRAY_POINTER_UINT16(log, PCIEAERLog, log_num, + vmstate_pcie_aer_err, PCIEAERErr), + VMSTATE_END_OF_LIST() + } +}; + +void pcie_aer_inject_error_print(Monitor *mon, const QObject *data) +{ + QDict *qdict; + int devfn; + assert(qobject_type(data) == QTYPE_QDICT); + qdict = qobject_to_qdict(data); + + devfn = (int)qdict_get_int(qdict, "devfn"); + monitor_printf(mon, "OK id: %s domain: %x, bus: %x devfn: %x.%x\n", + qdict_get_str(qdict, "id"), + (int) qdict_get_int(qdict, "domain"), + (int) qdict_get_int(qdict, "bus"), + PCI_SLOT(devfn), PCI_FUNC(devfn)); +} + +typedef struct PCIEAERErrorName { + const char *name; + uint32_t val; + bool correctable; +} PCIEAERErrorName; + +/* + * AER error name -> value conversion table + * This naming scheme is same to linux aer-injection tool. + */ +static const struct PCIEAERErrorName pcie_aer_error_list[] = { + { + .name = "TRAIN", + .val = PCI_ERR_UNC_TRAIN, + .correctable = false, + }, { + .name = "DLP", + .val = PCI_ERR_UNC_DLP, + .correctable = false, + }, { + .name = "SDN", + .val = PCI_ERR_UNC_SDN, + .correctable = false, + }, { + .name = "POISON_TLP", + .val = PCI_ERR_UNC_POISON_TLP, + .correctable = false, + }, { + .name = "FCP", + .val = PCI_ERR_UNC_FCP, + .correctable = false, + }, { + .name = "COMP_TIME", + .val = PCI_ERR_UNC_COMP_TIME, + .correctable = false, + }, { + .name = "COMP_ABORT", + .val = PCI_ERR_UNC_COMP_ABORT, + .correctable = false, + }, { + .name = "UNX_COMP", + .val = PCI_ERR_UNC_UNX_COMP, + .correctable = false, + }, { + .name = "RX_OVER", + .val = PCI_ERR_UNC_RX_OVER, + .correctable = false, + }, { + .name = "MALF_TLP", + .val = PCI_ERR_UNC_MALF_TLP, + .correctable = false, + }, { + .name = "ECRC", + .val = PCI_ERR_UNC_ECRC, + .correctable = false, + }, { + .name = "UNSUP", + .val = PCI_ERR_UNC_UNSUP, + .correctable = false, + }, { + .name = "ACSV", + .val = PCI_ERR_UNC_ACSV, + .correctable = false, + }, { + .name = "INTN", + .val = PCI_ERR_UNC_INTN, + .correctable = false, + }, { + .name = "MCBTLP", + .val = PCI_ERR_UNC_MCBTLP, + .correctable = false, + }, { + .name = "ATOP_EBLOCKED", + .val = PCI_ERR_UNC_ATOP_EBLOCKED, + .correctable = false, + }, { + .name = "TLP_PRF_BLOCKED", + .val = PCI_ERR_UNC_TLP_PRF_BLOCKED, + .correctable = false, + }, { + .name = "RCVR", + .val = PCI_ERR_COR_RCVR, + .correctable = true, + }, { + .name = "BAD_TLP", + .val = PCI_ERR_COR_BAD_TLP, + .correctable = true, + }, { + .name = "BAD_DLLP", + .val = PCI_ERR_COR_BAD_DLLP, + .correctable = true, + }, { + .name = "REP_ROLL", + .val = PCI_ERR_COR_REP_ROLL, + .correctable = true, + }, { + .name = "REP_TIMER", + .val = PCI_ERR_COR_REP_TIMER, + .correctable = true, + }, { + .name = "ADV_NONFATAL", + .val = PCI_ERR_COR_ADV_NONFATAL, + .correctable = true, + }, { + .name = "INTERNAL", + .val = PCI_ERR_COR_INTERNAL, + .correctable = true, + }, { + .name = "HL_OVERFLOW", + .val = PCI_ERR_COR_HL_OVERFLOW, + .correctable = true, + }, +}; + +static int pcie_aer_parse_error_string(const char *error_name, + uint32_t *status, bool *correctable) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(pcie_aer_error_list); i++) { + const PCIEAERErrorName *e = &pcie_aer_error_list[i]; + if (strcmp(error_name, e->name)) { + continue; + } + + *status = e->val; + *correctable = e->correctable; + return 0; + } + return -EINVAL; +} + +int do_pcie_aer_inject_error(Monitor *mon, + const QDict *qdict, QObject **ret_data) +{ + const char *id = qdict_get_str(qdict, "id"); + const char *error_name; + uint32_t error_status; + bool correctable; + PCIDevice *dev; + PCIEAERErr err; + int ret; + + ret = pci_qdev_find_device(id, &dev); + if (ret < 0) { + monitor_printf(mon, + "id or pci device path is invalid or device not " + "found. %s\n", id); + return ret; + } + if (!pci_is_express(dev)) { + monitor_printf(mon, "the device doesn't support pci express. %s\n", + id); + return -ENOSYS; + } + + error_name = qdict_get_str(qdict, "error_status"); + if (pcie_aer_parse_error_string(error_name, &error_status, &correctable)) { + char *e = NULL; + error_status = strtoul(error_name, &e, 0); + correctable = qdict_get_try_bool(qdict, "correctable", 0); + if (!e || *e != '\0') { + monitor_printf(mon, "invalid error status value. \"%s\"", + error_name); + return -EINVAL; + } + } + err.status = error_status; + err.source_id = (pci_bus_num(dev->bus) << 8) | dev->devfn; + + err.flags = 0; + if (correctable) { + err.flags |= PCIE_AER_ERR_IS_CORRECTABLE; + } + if (qdict_get_try_bool(qdict, "advisory_non_fatal", 0)) { + err.flags |= PCIE_AER_ERR_MAYBE_ADVISORY; + } + if (qdict_haskey(qdict, "header0")) { + err.flags |= PCIE_AER_ERR_HEADER_VALID; + } + if (qdict_haskey(qdict, "prefix0")) { + err.flags |= PCIE_AER_ERR_TLP_PREFIX_PRESENT; + } + + err.header[0] = qdict_get_try_int(qdict, "header0", 0); + err.header[1] = qdict_get_try_int(qdict, "header1", 0); + err.header[2] = qdict_get_try_int(qdict, "header2", 0); + err.header[3] = qdict_get_try_int(qdict, "header3", 0); + + err.prefix[0] = qdict_get_try_int(qdict, "prefix0", 0); + err.prefix[1] = qdict_get_try_int(qdict, "prefix1", 0); + err.prefix[2] = qdict_get_try_int(qdict, "prefix2", 0); + err.prefix[3] = qdict_get_try_int(qdict, "prefix3", 0); + + ret = pcie_aer_inject_error(dev, &err); + *ret_data = qobject_from_jsonf("{'id': %s, " + "'domain': %d, 'bus': %d, 'devfn': %d, " + "'ret': %d}", + id, + pci_find_domain(dev->bus), + pci_bus_num(dev->bus), dev->devfn, + ret); + assert(*ret_data); + + return 0; +} diff --git a/hw/pci/pcie_aer.h b/hw/pci/pcie_aer.h new file mode 100644 index 0000000000..7539500cd8 --- /dev/null +++ b/hw/pci/pcie_aer.h @@ -0,0 +1,106 @@ +/* + * pcie_aer.h + * + * Copyright (c) 2010 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#ifndef QEMU_PCIE_AER_H +#define QEMU_PCIE_AER_H + +#include "hw.h" + +/* definitions which PCIExpressDevice uses */ + +/* AER log */ +struct PCIEAERLog { + /* This structure is saved/loaded. + So explicitly size them instead of unsigned int */ + + /* the number of currently recorded log in log member */ + uint16_t log_num; + + /* + * The maximum number of the log. Errors can be logged up to this. + * + * This is configurable property. + * The specified value will be clipped down to PCIE_AER_LOG_MAX_LIMIT + * to avoid unreasonable memory usage. + * I bet that 128 log size would be big enough, otherwise too many errors + * for system to function normaly. But could consecutive errors occur? + */ +#define PCIE_AER_LOG_MAX_DEFAULT 8 +#define PCIE_AER_LOG_MAX_LIMIT 128 +#define PCIE_AER_LOG_MAX_UNSET 0xffff + uint16_t log_max; + + /* Error log. log_max-sized array */ + PCIEAERErr *log; +}; + +/* aer error message: error signaling message has only error sevirity and + source id. See 2.2.8.3 error signaling messages */ +struct PCIEAERMsg { + /* + * PCI_ERR_ROOT_CMD_{COR, NONFATAL, FATAL}_EN + * = PCI_EXP_DEVCTL_{CERE, NFERE, FERE} + */ + uint32_t severity; + + uint16_t source_id; /* bdf */ +}; + +static inline bool +pcie_aer_msg_is_uncor(const PCIEAERMsg *msg) +{ + return msg->severity == PCI_ERR_ROOT_CMD_NONFATAL_EN || + msg->severity == PCI_ERR_ROOT_CMD_FATAL_EN; +} + +/* error */ +struct PCIEAERErr { + uint32_t status; /* error status bits */ + uint16_t source_id; /* bdf */ + +#define PCIE_AER_ERR_IS_CORRECTABLE 0x1 /* correctable/uncorrectable */ +#define PCIE_AER_ERR_MAYBE_ADVISORY 0x2 /* maybe advisory non-fatal */ +#define PCIE_AER_ERR_HEADER_VALID 0x4 /* TLP header is logged */ +#define PCIE_AER_ERR_TLP_PREFIX_PRESENT 0x8 /* TLP Prefix is logged */ + uint16_t flags; + + uint32_t header[4]; /* TLP header */ + uint32_t prefix[4]; /* TLP header prefix */ +}; + +extern const VMStateDescription vmstate_pcie_aer_log; + +int pcie_aer_init(PCIDevice *dev, uint16_t offset); +void pcie_aer_exit(PCIDevice *dev); +void pcie_aer_write_config(PCIDevice *dev, + uint32_t addr, uint32_t val, int len); + +/* aer root port */ +void pcie_aer_root_set_vector(PCIDevice *dev, unsigned int vector); +void pcie_aer_root_init(PCIDevice *dev); +void pcie_aer_root_reset(PCIDevice *dev); +void pcie_aer_root_write_config(PCIDevice *dev, + uint32_t addr, uint32_t val, int len, + uint32_t root_cmd_prev); + +/* error injection */ +int pcie_aer_inject_error(PCIDevice *dev, const PCIEAERErr *err); + +#endif /* QEMU_PCIE_AER_H */ diff --git a/hw/pci/pcie_host.c b/hw/pci/pcie_host.c new file mode 100644 index 0000000000..c257fb43ca --- /dev/null +++ b/hw/pci/pcie_host.c @@ -0,0 +1,161 @@ +/* + * pcie_host.c + * utility functions for pci express host bridge. + * + * Copyright (c) 2009 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "hw.h" +#include "pci.h" +#include "pcie_host.h" +#include "exec-memory.h" + +/* + * PCI express mmcfig address + * bit 20 - 28: bus number + * bit 15 - 19: device number + * bit 12 - 14: function number + * bit 0 - 11: offset in configuration space of a given device + */ +#define PCIE_MMCFG_SIZE_MAX (1ULL << 28) +#define PCIE_MMCFG_SIZE_MIN (1ULL << 20) +#define PCIE_MMCFG_BUS_BIT 20 +#define PCIE_MMCFG_BUS_MASK 0x1ff +#define PCIE_MMCFG_DEVFN_BIT 12 +#define PCIE_MMCFG_DEVFN_MASK 0xff +#define PCIE_MMCFG_CONFOFFSET_MASK 0xfff +#define PCIE_MMCFG_BUS(addr) (((addr) >> PCIE_MMCFG_BUS_BIT) & \ + PCIE_MMCFG_BUS_MASK) +#define PCIE_MMCFG_DEVFN(addr) (((addr) >> PCIE_MMCFG_DEVFN_BIT) & \ + PCIE_MMCFG_DEVFN_MASK) +#define PCIE_MMCFG_CONFOFFSET(addr) ((addr) & PCIE_MMCFG_CONFOFFSET_MASK) + + +/* a helper function to get a PCIDevice for a given mmconfig address */ +static inline PCIDevice *pcie_dev_find_by_mmcfg_addr(PCIBus *s, + uint32_t mmcfg_addr) +{ + return pci_find_device(s, PCIE_MMCFG_BUS(mmcfg_addr), + PCIE_MMCFG_DEVFN(mmcfg_addr)); +} + +static void pcie_mmcfg_data_write(void *opaque, hwaddr mmcfg_addr, + uint64_t val, unsigned len) +{ + PCIExpressHost *e = opaque; + PCIBus *s = e->pci.bus; + PCIDevice *pci_dev = pcie_dev_find_by_mmcfg_addr(s, mmcfg_addr); + uint32_t addr; + uint32_t limit; + + if (!pci_dev) { + return; + } + addr = PCIE_MMCFG_CONFOFFSET(mmcfg_addr); + limit = pci_config_size(pci_dev); + if (limit <= addr) { + /* conventional pci device can be behind pcie-to-pci bridge. + 256 <= addr < 4K has no effects. */ + return; + } + pci_host_config_write_common(pci_dev, addr, limit, val, len); +} + +static uint64_t pcie_mmcfg_data_read(void *opaque, + hwaddr mmcfg_addr, + unsigned len) +{ + PCIExpressHost *e = opaque; + PCIBus *s = e->pci.bus; + PCIDevice *pci_dev = pcie_dev_find_by_mmcfg_addr(s, mmcfg_addr); + uint32_t addr; + uint32_t limit; + + if (!pci_dev) { + return ~0x0; + } + addr = PCIE_MMCFG_CONFOFFSET(mmcfg_addr); + limit = pci_config_size(pci_dev); + if (limit <= addr) { + /* conventional pci device can be behind pcie-to-pci bridge. + 256 <= addr < 4K has no effects. */ + return ~0x0; + } + return pci_host_config_read_common(pci_dev, addr, limit, len); +} + +static const MemoryRegionOps pcie_mmcfg_ops = { + .read = pcie_mmcfg_data_read, + .write = pcie_mmcfg_data_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +/* pcie_host::base_addr == PCIE_BASE_ADDR_UNMAPPED when it isn't mapped. */ +#define PCIE_BASE_ADDR_UNMAPPED ((hwaddr)-1ULL) + +int pcie_host_init(PCIExpressHost *e) +{ + e->base_addr = PCIE_BASE_ADDR_UNMAPPED; + + return 0; +} + +void pcie_host_mmcfg_unmap(PCIExpressHost *e) +{ + if (e->base_addr != PCIE_BASE_ADDR_UNMAPPED) { + memory_region_del_subregion(get_system_memory(), &e->mmio); + memory_region_destroy(&e->mmio); + e->base_addr = PCIE_BASE_ADDR_UNMAPPED; + } +} + +void pcie_host_mmcfg_map(PCIExpressHost *e, hwaddr addr, + uint32_t size) +{ + assert(!(size & (size - 1))); /* power of 2 */ + assert(size >= PCIE_MMCFG_SIZE_MIN); + assert(size <= PCIE_MMCFG_SIZE_MAX); + e->size = size; + memory_region_init_io(&e->mmio, &pcie_mmcfg_ops, e, "pcie-mmcfg", e->size); + e->base_addr = addr; + memory_region_add_subregion(get_system_memory(), e->base_addr, &e->mmio); +} + +void pcie_host_mmcfg_update(PCIExpressHost *e, + int enable, + hwaddr addr, + uint32_t size) +{ + pcie_host_mmcfg_unmap(e); + if (enable) { + pcie_host_mmcfg_map(e, addr, size); + } +} + +static const TypeInfo pcie_host_type_info = { + .name = TYPE_PCIE_HOST_BRIDGE, + .parent = TYPE_PCI_HOST_BRIDGE, + .abstract = true, + .instance_size = sizeof(PCIExpressHost), +}; + +static void pcie_host_register_types(void) +{ + type_register_static(&pcie_host_type_info); +} + +type_init(pcie_host_register_types) diff --git a/hw/pci/pcie_host.h b/hw/pci/pcie_host.h new file mode 100644 index 0000000000..392193530d --- /dev/null +++ b/hw/pci/pcie_host.h @@ -0,0 +1,54 @@ +/* + * pcie_host.h + * + * Copyright (c) 2009 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#ifndef PCIE_HOST_H +#define PCIE_HOST_H + +#include "pci_host.h" +#include "memory.h" + +#define TYPE_PCIE_HOST_BRIDGE "pcie-host-bridge" +#define PCIE_HOST_BRIDGE(obj) \ + OBJECT_CHECK(PCIExpressHost, (obj), TYPE_PCIE_HOST_BRIDGE) + +struct PCIExpressHost { + PCIHostState pci; + + /* express part */ + + /* base address where MMCONFIG area is mapped. */ + hwaddr base_addr; + + /* the size of MMCONFIG area. It's host bridge dependent */ + hwaddr size; + + /* MMCONFIG mmio area */ + MemoryRegion mmio; +}; + +int pcie_host_init(PCIExpressHost *e); +void pcie_host_mmcfg_unmap(PCIExpressHost *e); +void pcie_host_mmcfg_map(PCIExpressHost *e, hwaddr addr, uint32_t size); +void pcie_host_mmcfg_update(PCIExpressHost *e, + int enable, + hwaddr addr, + uint32_t size); + +#endif /* PCIE_HOST_H */ diff --git a/hw/pci/pcie_port.c b/hw/pci/pcie_port.c new file mode 100644 index 0000000000..d6350e5e73 --- /dev/null +++ b/hw/pci/pcie_port.c @@ -0,0 +1,114 @@ +/* + * pcie_port.c + * + * Copyright (c) 2010 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "pcie_port.h" + +void pcie_port_init_reg(PCIDevice *d) +{ + /* Unlike pci bridge, + 66MHz and fast back to back don't apply to pci express port. */ + pci_set_word(d->config + PCI_STATUS, 0); + pci_set_word(d->config + PCI_SEC_STATUS, 0); + + /* Unlike conventional pci bridge, some bits are hardwired to 0. */ + pci_set_word(d->wmask + PCI_BRIDGE_CONTROL, + PCI_BRIDGE_CTL_PARITY | + PCI_BRIDGE_CTL_ISA | + PCI_BRIDGE_CTL_VGA | + PCI_BRIDGE_CTL_SERR | + PCI_BRIDGE_CTL_BUS_RESET); +} + +/************************************************************************** + * (chassis number, pcie physical slot number) -> pcie slot conversion + */ +struct PCIEChassis { + uint8_t number; + + QLIST_HEAD(, PCIESlot) slots; + QLIST_ENTRY(PCIEChassis) next; +}; + +static QLIST_HEAD(, PCIEChassis) chassis = QLIST_HEAD_INITIALIZER(chassis); + +static struct PCIEChassis *pcie_chassis_find(uint8_t chassis_number) +{ + struct PCIEChassis *c; + QLIST_FOREACH(c, &chassis, next) { + if (c->number == chassis_number) { + break; + } + } + return c; +} + +void pcie_chassis_create(uint8_t chassis_number) +{ + struct PCIEChassis *c; + c = pcie_chassis_find(chassis_number); + if (c) { + return; + } + c = g_malloc0(sizeof(*c)); + c->number = chassis_number; + QLIST_INIT(&c->slots); + QLIST_INSERT_HEAD(&chassis, c, next); +} + +static PCIESlot *pcie_chassis_find_slot_with_chassis(struct PCIEChassis *c, + uint8_t slot) +{ + PCIESlot *s; + QLIST_FOREACH(s, &c->slots, next) { + if (s->slot == slot) { + break; + } + } + return s; +} + +PCIESlot *pcie_chassis_find_slot(uint8_t chassis_number, uint16_t slot) +{ + struct PCIEChassis *c; + c = pcie_chassis_find(chassis_number); + if (!c) { + return NULL; + } + return pcie_chassis_find_slot_with_chassis(c, slot); +} + +int pcie_chassis_add_slot(struct PCIESlot *slot) +{ + struct PCIEChassis *c; + c = pcie_chassis_find(slot->chassis); + if (!c) { + return -ENODEV; + } + if (pcie_chassis_find_slot_with_chassis(c, slot->slot)) { + return -EBUSY; + } + QLIST_INSERT_HEAD(&c->slots, slot, next); + return 0; +} + +void pcie_chassis_del_slot(PCIESlot *s) +{ + QLIST_REMOVE(s, next); +} diff --git a/hw/pci/pcie_port.h b/hw/pci/pcie_port.h new file mode 100644 index 0000000000..3709583cc0 --- /dev/null +++ b/hw/pci/pcie_port.h @@ -0,0 +1,51 @@ +/* + * pcie_port.h + * + * Copyright (c) 2010 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#ifndef QEMU_PCIE_PORT_H +#define QEMU_PCIE_PORT_H + +#include "pci_bridge.h" +#include "pci_internals.h" + +struct PCIEPort { + PCIBridge br; + + /* pci express switch port */ + uint8_t port; +}; + +void pcie_port_init_reg(PCIDevice *d); + +struct PCIESlot { + PCIEPort port; + + /* pci express switch port with slot */ + uint8_t chassis; + uint16_t slot; + QLIST_ENTRY(PCIESlot) next; +}; + +void pcie_chassis_create(uint8_t chassis_number); +void pcie_main_chassis_create(void); +PCIESlot *pcie_chassis_find_slot(uint8_t chassis, uint16_t slot); +int pcie_chassis_add_slot(struct PCIESlot *slot); +void pcie_chassis_del_slot(PCIESlot *s); + +#endif /* QEMU_PCIE_PORT_H */ diff --git a/hw/pci/pcie_regs.h b/hw/pci/pcie_regs.h new file mode 100644 index 0000000000..4d123d9fcc --- /dev/null +++ b/hw/pci/pcie_regs.h @@ -0,0 +1,156 @@ +/* + * constants for pcie configurations space from pci express spec. + * + * TODO: + * Those constants and macros should go to Linux pci_regs.h + * Once they're merged, they will go away. + */ +#ifndef QEMU_PCIE_REGS_H +#define QEMU_PCIE_REGS_H + + +/* express capability */ + +#define PCI_EXP_VER2_SIZEOF 0x3c /* express capability of ver. 2 */ +#define PCI_EXT_CAP_VER_SHIFT 16 +#define PCI_EXT_CAP_NEXT_SHIFT 20 +#define PCI_EXT_CAP_NEXT_MASK (0xffc << PCI_EXT_CAP_NEXT_SHIFT) + +#define PCI_EXT_CAP(id, ver, next) \ + ((id) | \ + ((ver) << PCI_EXT_CAP_VER_SHIFT) | \ + ((next) << PCI_EXT_CAP_NEXT_SHIFT)) + +#define PCI_EXT_CAP_ALIGN 4 +#define PCI_EXT_CAP_ALIGNUP(x) \ + (((x) + PCI_EXT_CAP_ALIGN - 1) & ~(PCI_EXT_CAP_ALIGN - 1)) + +/* PCI_EXP_FLAGS */ +#define PCI_EXP_FLAGS_VER2 2 /* for now, supports only ver. 2 */ +#define PCI_EXP_FLAGS_IRQ_SHIFT (ffs(PCI_EXP_FLAGS_IRQ) - 1) +#define PCI_EXP_FLAGS_TYPE_SHIFT (ffs(PCI_EXP_FLAGS_TYPE) - 1) + + +/* PCI_EXP_LINK{CAP, STA} */ +/* link speed */ +#define PCI_EXP_LNK_LS_25 1 + +#define PCI_EXP_LNK_MLW_SHIFT (ffs(PCI_EXP_LNKCAP_MLW) - 1) +#define PCI_EXP_LNK_MLW_1 (1 << PCI_EXP_LNK_MLW_SHIFT) + +/* PCI_EXP_LINKCAP */ +#define PCI_EXP_LNKCAP_ASPMS_SHIFT (ffs(PCI_EXP_LNKCAP_ASPMS) - 1) +#define PCI_EXP_LNKCAP_ASPMS_0S (1 << PCI_EXP_LNKCAP_ASPMS_SHIFT) + +#define PCI_EXP_LNKCAP_PN_SHIFT (ffs(PCI_EXP_LNKCAP_PN) - 1) + +#define PCI_EXP_SLTCAP_PSN_SHIFT (ffs(PCI_EXP_SLTCAP_PSN) - 1) + +#define PCI_EXP_SLTCTL_IND_RESERVED 0x0 +#define PCI_EXP_SLTCTL_IND_ON 0x1 +#define PCI_EXP_SLTCTL_IND_BLINK 0x2 +#define PCI_EXP_SLTCTL_IND_OFF 0x3 +#define PCI_EXP_SLTCTL_AIC_SHIFT (ffs(PCI_EXP_SLTCTL_AIC) - 1) +#define PCI_EXP_SLTCTL_AIC_OFF \ + (PCI_EXP_SLTCTL_IND_OFF << PCI_EXP_SLTCTL_AIC_SHIFT) + +#define PCI_EXP_SLTCTL_PIC_SHIFT (ffs(PCI_EXP_SLTCTL_PIC) - 1) +#define PCI_EXP_SLTCTL_PIC_OFF \ + (PCI_EXP_SLTCTL_IND_OFF << PCI_EXP_SLTCTL_PIC_SHIFT) + +#define PCI_EXP_SLTCTL_SUPPORTED \ + (PCI_EXP_SLTCTL_ABPE | \ + PCI_EXP_SLTCTL_PDCE | \ + PCI_EXP_SLTCTL_CCIE | \ + PCI_EXP_SLTCTL_HPIE | \ + PCI_EXP_SLTCTL_AIC | \ + PCI_EXP_SLTCTL_PCC | \ + PCI_EXP_SLTCTL_EIC) + +#define PCI_EXP_DEVCAP2_EFF 0x100000 +#define PCI_EXP_DEVCAP2_EETLPP 0x200000 + +#define PCI_EXP_DEVCTL2_EETLPPB 0x80 + +/* ARI */ +#define PCI_ARI_VER 1 +#define PCI_ARI_SIZEOF 8 + +/* AER */ +#define PCI_ERR_VER 2 +#define PCI_ERR_SIZEOF 0x48 + +#define PCI_ERR_UNC_SDN 0x00000020 /* surprise down */ +#define PCI_ERR_UNC_ACSV 0x00200000 /* ACS Violation */ +#define PCI_ERR_UNC_INTN 0x00400000 /* Internal Error */ +#define PCI_ERR_UNC_MCBTLP 0x00800000 /* MC Blcoked TLP */ +#define PCI_ERR_UNC_ATOP_EBLOCKED 0x01000000 /* atomic op egress blocked */ +#define PCI_ERR_UNC_TLP_PRF_BLOCKED 0x02000000 /* TLP Prefix Blocked */ +#define PCI_ERR_COR_ADV_NONFATAL 0x00002000 /* Advisory Non-Fatal */ +#define PCI_ERR_COR_INTERNAL 0x00004000 /* Corrected Internal */ +#define PCI_ERR_COR_HL_OVERFLOW 0x00008000 /* Header Long Overflow */ +#define PCI_ERR_CAP_FEP_MASK 0x0000001f +#define PCI_ERR_CAP_MHRC 0x00000200 +#define PCI_ERR_CAP_MHRE 0x00000400 +#define PCI_ERR_CAP_TLP 0x00000800 + +#define PCI_ERR_HEADER_LOG_SIZE 16 +#define PCI_ERR_TLP_PREFIX_LOG 0x38 +#define PCI_ERR_TLP_PREFIX_LOG_SIZE 16 + +#define PCI_SEC_STATUS_RCV_SYSTEM_ERROR 0x4000 + +/* aer root error command/status */ +#define PCI_ERR_ROOT_CMD_EN_MASK (PCI_ERR_ROOT_CMD_COR_EN | \ + PCI_ERR_ROOT_CMD_NONFATAL_EN | \ + PCI_ERR_ROOT_CMD_FATAL_EN) + +#define PCI_ERR_ROOT_IRQ_MAX 32 +#define PCI_ERR_ROOT_IRQ 0xf8000000 +#define PCI_ERR_ROOT_IRQ_SHIFT (ffs(PCI_ERR_ROOT_IRQ) - 1) +#define PCI_ERR_ROOT_STATUS_REPORT_MASK (PCI_ERR_ROOT_COR_RCV | \ + PCI_ERR_ROOT_MULTI_COR_RCV | \ + PCI_ERR_ROOT_UNCOR_RCV | \ + PCI_ERR_ROOT_MULTI_UNCOR_RCV | \ + PCI_ERR_ROOT_FIRST_FATAL | \ + PCI_ERR_ROOT_NONFATAL_RCV | \ + PCI_ERR_ROOT_FATAL_RCV) + +#define PCI_ERR_UNC_SUPPORTED (PCI_ERR_UNC_DLP | \ + PCI_ERR_UNC_SDN | \ + PCI_ERR_UNC_POISON_TLP | \ + PCI_ERR_UNC_FCP | \ + PCI_ERR_UNC_COMP_TIME | \ + PCI_ERR_UNC_COMP_ABORT | \ + PCI_ERR_UNC_UNX_COMP | \ + PCI_ERR_UNC_RX_OVER | \ + PCI_ERR_UNC_MALF_TLP | \ + PCI_ERR_UNC_ECRC | \ + PCI_ERR_UNC_UNSUP | \ + PCI_ERR_UNC_ACSV | \ + PCI_ERR_UNC_INTN | \ + PCI_ERR_UNC_MCBTLP | \ + PCI_ERR_UNC_ATOP_EBLOCKED | \ + PCI_ERR_UNC_TLP_PRF_BLOCKED) + +#define PCI_ERR_UNC_SEVERITY_DEFAULT (PCI_ERR_UNC_DLP | \ + PCI_ERR_UNC_SDN | \ + PCI_ERR_UNC_FCP | \ + PCI_ERR_UNC_RX_OVER | \ + PCI_ERR_UNC_MALF_TLP | \ + PCI_ERR_UNC_INTN) + +#define PCI_ERR_COR_SUPPORTED (PCI_ERR_COR_RCVR | \ + PCI_ERR_COR_BAD_TLP | \ + PCI_ERR_COR_BAD_DLLP | \ + PCI_ERR_COR_REP_ROLL | \ + PCI_ERR_COR_REP_TIMER | \ + PCI_ERR_COR_ADV_NONFATAL | \ + PCI_ERR_COR_INTERNAL | \ + PCI_ERR_COR_HL_OVERFLOW) + +#define PCI_ERR_COR_MASK_DEFAULT (PCI_ERR_COR_ADV_NONFATAL | \ + PCI_ERR_COR_INTERNAL | \ + PCI_ERR_COR_HL_OVERFLOW) + +#endif /* QEMU_PCIE_REGS_H */ diff --git a/hw/pci/shpc.c b/hw/pci/shpc.c new file mode 100644 index 0000000000..4597bbde75 --- /dev/null +++ b/hw/pci/shpc.c @@ -0,0 +1,681 @@ +#include +#include +#include "range.h" +#include "range.h" +#include "shpc.h" +#include "pci.h" +#include "pci_internals.h" +#include "msi.h" + +/* TODO: model power only and disabled slot states. */ +/* TODO: handle SERR and wakeups */ +/* TODO: consider enabling 66MHz support */ + +/* TODO: remove fully only on state DISABLED and LED off. + * track state to properly record this. */ + +/* SHPC Working Register Set */ +#define SHPC_BASE_OFFSET 0x00 /* 4 bytes */ +#define SHPC_SLOTS_33 0x04 /* 4 bytes. Also encodes PCI-X slots. */ +#define SHPC_SLOTS_66 0x08 /* 4 bytes. */ +#define SHPC_NSLOTS 0x0C /* 1 byte */ +#define SHPC_FIRST_DEV 0x0D /* 1 byte */ +#define SHPC_PHYS_SLOT 0x0E /* 2 byte */ +#define SHPC_PHYS_NUM_MAX 0x7ff +#define SHPC_PHYS_NUM_UP 0x2000 +#define SHPC_PHYS_MRL 0x4000 +#define SHPC_PHYS_BUTTON 0x8000 +#define SHPC_SEC_BUS 0x10 /* 2 bytes */ +#define SHPC_SEC_BUS_33 0x0 +#define SHPC_SEC_BUS_66 0x1 /* Unused */ +#define SHPC_SEC_BUS_MASK 0x7 +#define SHPC_MSI_CTL 0x12 /* 1 byte */ +#define SHPC_PROG_IFC 0x13 /* 1 byte */ +#define SHPC_PROG_IFC_1_0 0x1 +#define SHPC_CMD_CODE 0x14 /* 1 byte */ +#define SHPC_CMD_TRGT 0x15 /* 1 byte */ +#define SHPC_CMD_TRGT_MIN 0x1 +#define SHPC_CMD_TRGT_MAX 0x1f +#define SHPC_CMD_STATUS 0x16 /* 2 bytes */ +#define SHPC_CMD_STATUS_BUSY 0x1 +#define SHPC_CMD_STATUS_MRL_OPEN 0x2 +#define SHPC_CMD_STATUS_INVALID_CMD 0x4 +#define SHPC_CMD_STATUS_INVALID_MODE 0x8 +#define SHPC_INT_LOCATOR 0x18 /* 4 bytes */ +#define SHPC_INT_COMMAND 0x1 +#define SHPC_SERR_LOCATOR 0x1C /* 4 bytes */ +#define SHPC_SERR_INT 0x20 /* 4 bytes */ +#define SHPC_INT_DIS 0x1 +#define SHPC_SERR_DIS 0x2 +#define SHPC_CMD_INT_DIS 0x4 +#define SHPC_ARB_SERR_DIS 0x8 +#define SHPC_CMD_DETECTED 0x10000 +#define SHPC_ARB_DETECTED 0x20000 + /* 4 bytes * slot # (start from 0) */ +#define SHPC_SLOT_REG(s) (0x24 + (s) * 4) + /* 2 bytes */ +#define SHPC_SLOT_STATUS(s) (0x0 + SHPC_SLOT_REG(s)) + +/* Same slot state masks are used for command and status registers */ +#define SHPC_SLOT_STATE_MASK 0x03 +#define SHPC_SLOT_STATE_SHIFT \ + (ffs(SHPC_SLOT_STATE_MASK) - 1) + +#define SHPC_STATE_NO 0x0 +#define SHPC_STATE_PWRONLY 0x1 +#define SHPC_STATE_ENABLED 0x2 +#define SHPC_STATE_DISABLED 0x3 + +#define SHPC_SLOT_PWR_LED_MASK 0xC +#define SHPC_SLOT_PWR_LED_SHIFT \ + (ffs(SHPC_SLOT_PWR_LED_MASK) - 1) +#define SHPC_SLOT_ATTN_LED_MASK 0x30 +#define SHPC_SLOT_ATTN_LED_SHIFT \ + (ffs(SHPC_SLOT_ATTN_LED_MASK) - 1) + +#define SHPC_LED_NO 0x0 +#define SHPC_LED_ON 0x1 +#define SHPC_LED_BLINK 0x2 +#define SHPC_LED_OFF 0x3 + +#define SHPC_SLOT_STATUS_PWR_FAULT 0x40 +#define SHPC_SLOT_STATUS_BUTTON 0x80 +#define SHPC_SLOT_STATUS_MRL_OPEN 0x100 +#define SHPC_SLOT_STATUS_66 0x200 +#define SHPC_SLOT_STATUS_PRSNT_MASK 0xC00 +#define SHPC_SLOT_STATUS_PRSNT_EMPTY 0x3 +#define SHPC_SLOT_STATUS_PRSNT_25W 0x1 +#define SHPC_SLOT_STATUS_PRSNT_15W 0x2 +#define SHPC_SLOT_STATUS_PRSNT_7_5W 0x0 + +#define SHPC_SLOT_STATUS_PRSNT_PCIX 0x3000 + + + /* 1 byte */ +#define SHPC_SLOT_EVENT_LATCH(s) (0x2 + SHPC_SLOT_REG(s)) + /* 1 byte */ +#define SHPC_SLOT_EVENT_SERR_INT_DIS(d, s) (0x3 + SHPC_SLOT_REG(s)) +#define SHPC_SLOT_EVENT_PRESENCE 0x01 +#define SHPC_SLOT_EVENT_ISOLATED_FAULT 0x02 +#define SHPC_SLOT_EVENT_BUTTON 0x04 +#define SHPC_SLOT_EVENT_MRL 0x08 +#define SHPC_SLOT_EVENT_CONNECTED_FAULT 0x10 +/* Bits below are used for Serr/Int disable only */ +#define SHPC_SLOT_EVENT_MRL_SERR_DIS 0x20 +#define SHPC_SLOT_EVENT_CONNECTED_FAULT_SERR_DIS 0x40 + +#define SHPC_MIN_SLOTS 1 +#define SHPC_MAX_SLOTS 31 +#define SHPC_SIZEOF(d) SHPC_SLOT_REG((d)->shpc->nslots) + +/* SHPC Slot identifiers */ + +/* Hotplug supported at 31 slots out of the total 32. We reserve slot 0, + and give the rest of them physical *and* pci numbers starting from 1, so + they match logical numbers. Note: this means that multiple slots must have + different chassis number values, to make chassis+physical slot unique. + TODO: make this configurable? */ +#define SHPC_IDX_TO_LOGICAL(slot) ((slot) + 1) +#define SHPC_LOGICAL_TO_IDX(target) ((target) - 1) +#define SHPC_IDX_TO_PCI(slot) ((slot) + 1) +#define SHPC_PCI_TO_IDX(pci_slot) ((pci_slot) - 1) +#define SHPC_IDX_TO_PHYSICAL(slot) ((slot) + 1) + +static int roundup_pow_of_two(int x) +{ + x |= (x >> 1); + x |= (x >> 2); + x |= (x >> 4); + x |= (x >> 8); + x |= (x >> 16); + return x + 1; +} + +static uint16_t shpc_get_status(SHPCDevice *shpc, int slot, uint16_t msk) +{ + uint8_t *status = shpc->config + SHPC_SLOT_STATUS(slot); + return (pci_get_word(status) & msk) >> (ffs(msk) - 1); +} + +static void shpc_set_status(SHPCDevice *shpc, + int slot, uint8_t value, uint16_t msk) +{ + uint8_t *status = shpc->config + SHPC_SLOT_STATUS(slot); + pci_word_test_and_clear_mask(status, msk); + pci_word_test_and_set_mask(status, value << (ffs(msk) - 1)); +} + +static void shpc_interrupt_update(PCIDevice *d) +{ + SHPCDevice *shpc = d->shpc; + int slot; + int level = 0; + uint32_t serr_int; + uint32_t int_locator = 0; + + /* Update interrupt locator register */ + for (slot = 0; slot < shpc->nslots; ++slot) { + uint8_t event = shpc->config[SHPC_SLOT_EVENT_LATCH(slot)]; + uint8_t disable = shpc->config[SHPC_SLOT_EVENT_SERR_INT_DIS(d, slot)]; + uint32_t mask = 1 << SHPC_IDX_TO_LOGICAL(slot); + if (event & ~disable) { + int_locator |= mask; + } + } + serr_int = pci_get_long(shpc->config + SHPC_SERR_INT); + if ((serr_int & SHPC_CMD_DETECTED) && !(serr_int & SHPC_CMD_INT_DIS)) { + int_locator |= SHPC_INT_COMMAND; + } + pci_set_long(shpc->config + SHPC_INT_LOCATOR, int_locator); + level = (!(serr_int & SHPC_INT_DIS) && int_locator) ? 1 : 0; + if (msi_enabled(d) && shpc->msi_requested != level) + msi_notify(d, 0); + else + qemu_set_irq(d->irq[0], level); + shpc->msi_requested = level; +} + +static void shpc_set_sec_bus_speed(SHPCDevice *shpc, uint8_t speed) +{ + switch (speed) { + case SHPC_SEC_BUS_33: + shpc->config[SHPC_SEC_BUS] &= ~SHPC_SEC_BUS_MASK; + shpc->config[SHPC_SEC_BUS] |= speed; + break; + default: + pci_word_test_and_set_mask(shpc->config + SHPC_CMD_STATUS, + SHPC_CMD_STATUS_INVALID_MODE); + } +} + +void shpc_reset(PCIDevice *d) +{ + SHPCDevice *shpc = d->shpc; + int nslots = shpc->nslots; + int i; + memset(shpc->config, 0, SHPC_SIZEOF(d)); + pci_set_byte(shpc->config + SHPC_NSLOTS, nslots); + pci_set_long(shpc->config + SHPC_SLOTS_33, nslots); + pci_set_long(shpc->config + SHPC_SLOTS_66, 0); + pci_set_byte(shpc->config + SHPC_FIRST_DEV, SHPC_IDX_TO_PCI(0)); + pci_set_word(shpc->config + SHPC_PHYS_SLOT, + SHPC_IDX_TO_PHYSICAL(0) | + SHPC_PHYS_NUM_UP | + SHPC_PHYS_MRL | + SHPC_PHYS_BUTTON); + pci_set_long(shpc->config + SHPC_SERR_INT, SHPC_INT_DIS | + SHPC_SERR_DIS | + SHPC_CMD_INT_DIS | + SHPC_ARB_SERR_DIS); + pci_set_byte(shpc->config + SHPC_PROG_IFC, SHPC_PROG_IFC_1_0); + pci_set_word(shpc->config + SHPC_SEC_BUS, SHPC_SEC_BUS_33); + for (i = 0; i < shpc->nslots; ++i) { + pci_set_byte(shpc->config + SHPC_SLOT_EVENT_SERR_INT_DIS(d, i), + SHPC_SLOT_EVENT_PRESENCE | + SHPC_SLOT_EVENT_ISOLATED_FAULT | + SHPC_SLOT_EVENT_BUTTON | + SHPC_SLOT_EVENT_MRL | + SHPC_SLOT_EVENT_CONNECTED_FAULT | + SHPC_SLOT_EVENT_MRL_SERR_DIS | + SHPC_SLOT_EVENT_CONNECTED_FAULT_SERR_DIS); + if (shpc->sec_bus->devices[PCI_DEVFN(SHPC_IDX_TO_PCI(i), 0)]) { + shpc_set_status(shpc, i, SHPC_STATE_ENABLED, SHPC_SLOT_STATE_MASK); + shpc_set_status(shpc, i, 0, SHPC_SLOT_STATUS_MRL_OPEN); + shpc_set_status(shpc, i, SHPC_SLOT_STATUS_PRSNT_7_5W, + SHPC_SLOT_STATUS_PRSNT_MASK); + shpc_set_status(shpc, i, SHPC_LED_ON, SHPC_SLOT_PWR_LED_MASK); + } else { + shpc_set_status(shpc, i, SHPC_STATE_DISABLED, SHPC_SLOT_STATE_MASK); + shpc_set_status(shpc, i, 1, SHPC_SLOT_STATUS_MRL_OPEN); + shpc_set_status(shpc, i, SHPC_SLOT_STATUS_PRSNT_EMPTY, + SHPC_SLOT_STATUS_PRSNT_MASK); + shpc_set_status(shpc, i, SHPC_LED_OFF, SHPC_SLOT_PWR_LED_MASK); + } + shpc_set_status(shpc, i, 0, SHPC_SLOT_STATUS_66); + } + shpc_set_sec_bus_speed(shpc, SHPC_SEC_BUS_33); + shpc->msi_requested = 0; + shpc_interrupt_update(d); +} + +static void shpc_invalid_command(SHPCDevice *shpc) +{ + pci_word_test_and_set_mask(shpc->config + SHPC_CMD_STATUS, + SHPC_CMD_STATUS_INVALID_CMD); +} + +static void shpc_free_devices_in_slot(SHPCDevice *shpc, int slot) +{ + int devfn; + int pci_slot = SHPC_IDX_TO_PCI(slot); + for (devfn = PCI_DEVFN(pci_slot, 0); + devfn <= PCI_DEVFN(pci_slot, PCI_FUNC_MAX - 1); + ++devfn) { + PCIDevice *affected_dev = shpc->sec_bus->devices[devfn]; + if (affected_dev) { + qdev_free(&affected_dev->qdev); + } + } +} + +static void shpc_slot_command(SHPCDevice *shpc, uint8_t target, + uint8_t state, uint8_t power, uint8_t attn) +{ + uint8_t current_state; + int slot = SHPC_LOGICAL_TO_IDX(target); + if (target < SHPC_CMD_TRGT_MIN || slot >= shpc->nslots) { + shpc_invalid_command(shpc); + return; + } + current_state = shpc_get_status(shpc, slot, SHPC_SLOT_STATE_MASK); + if (current_state == SHPC_STATE_ENABLED && state == SHPC_STATE_PWRONLY) { + shpc_invalid_command(shpc); + return; + } + + switch (power) { + case SHPC_LED_NO: + break; + default: + /* TODO: send event to monitor */ + shpc_set_status(shpc, slot, power, SHPC_SLOT_PWR_LED_MASK); + } + switch (attn) { + case SHPC_LED_NO: + break; + default: + /* TODO: send event to monitor */ + shpc_set_status(shpc, slot, attn, SHPC_SLOT_ATTN_LED_MASK); + } + + if ((current_state == SHPC_STATE_DISABLED && state == SHPC_STATE_PWRONLY) || + (current_state == SHPC_STATE_DISABLED && state == SHPC_STATE_ENABLED)) { + shpc_set_status(shpc, slot, state, SHPC_SLOT_STATE_MASK); + } else if ((current_state == SHPC_STATE_ENABLED || + current_state == SHPC_STATE_PWRONLY) && + state == SHPC_STATE_DISABLED) { + shpc_set_status(shpc, slot, state, SHPC_SLOT_STATE_MASK); + power = shpc_get_status(shpc, slot, SHPC_SLOT_PWR_LED_MASK); + /* TODO: track what monitor requested. */ + /* Look at LED to figure out whether it's ok to remove the device. */ + if (power == SHPC_LED_OFF) { + shpc_free_devices_in_slot(shpc, slot); + shpc_set_status(shpc, slot, 1, SHPC_SLOT_STATUS_MRL_OPEN); + shpc_set_status(shpc, slot, SHPC_SLOT_STATUS_PRSNT_EMPTY, + SHPC_SLOT_STATUS_PRSNT_MASK); + shpc->config[SHPC_SLOT_EVENT_LATCH(slot)] |= + SHPC_SLOT_EVENT_BUTTON | + SHPC_SLOT_EVENT_MRL | + SHPC_SLOT_EVENT_PRESENCE; + } + } +} + +static void shpc_command(SHPCDevice *shpc) +{ + uint8_t code = pci_get_byte(shpc->config + SHPC_CMD_CODE); + uint8_t speed; + uint8_t target; + uint8_t attn; + uint8_t power; + uint8_t state; + int i; + + /* Clear status from the previous command. */ + pci_word_test_and_clear_mask(shpc->config + SHPC_CMD_STATUS, + SHPC_CMD_STATUS_BUSY | + SHPC_CMD_STATUS_MRL_OPEN | + SHPC_CMD_STATUS_INVALID_CMD | + SHPC_CMD_STATUS_INVALID_MODE); + switch (code) { + case 0x00 ... 0x3f: + target = shpc->config[SHPC_CMD_TRGT] & SHPC_CMD_TRGT_MAX; + state = (code & SHPC_SLOT_STATE_MASK) >> SHPC_SLOT_STATE_SHIFT; + power = (code & SHPC_SLOT_PWR_LED_MASK) >> SHPC_SLOT_PWR_LED_SHIFT; + attn = (code & SHPC_SLOT_ATTN_LED_MASK) >> SHPC_SLOT_ATTN_LED_SHIFT; + shpc_slot_command(shpc, target, state, power, attn); + break; + case 0x40 ... 0x47: + speed = code & SHPC_SEC_BUS_MASK; + shpc_set_sec_bus_speed(shpc, speed); + break; + case 0x48: + /* Power only all slots */ + /* first verify no slots are enabled */ + for (i = 0; i < shpc->nslots; ++i) { + state = shpc_get_status(shpc, i, SHPC_SLOT_STATE_MASK); + if (state == SHPC_STATE_ENABLED) { + shpc_invalid_command(shpc); + goto done; + } + } + for (i = 0; i < shpc->nslots; ++i) { + if (!(shpc_get_status(shpc, i, SHPC_SLOT_STATUS_MRL_OPEN))) { + shpc_slot_command(shpc, i + SHPC_CMD_TRGT_MIN, + SHPC_STATE_PWRONLY, SHPC_LED_ON, SHPC_LED_NO); + } else { + shpc_slot_command(shpc, i + SHPC_CMD_TRGT_MIN, + SHPC_STATE_NO, SHPC_LED_OFF, SHPC_LED_NO); + } + } + break; + case 0x49: + /* Enable all slots */ + /* TODO: Spec says this shall fail if some are already enabled. + * This doesn't make sense - why not? a spec bug? */ + for (i = 0; i < shpc->nslots; ++i) { + state = shpc_get_status(shpc, i, SHPC_SLOT_STATE_MASK); + if (state == SHPC_STATE_ENABLED) { + shpc_invalid_command(shpc); + goto done; + } + } + for (i = 0; i < shpc->nslots; ++i) { + if (!(shpc_get_status(shpc, i, SHPC_SLOT_STATUS_MRL_OPEN))) { + shpc_slot_command(shpc, i + SHPC_CMD_TRGT_MIN, + SHPC_STATE_ENABLED, SHPC_LED_ON, SHPC_LED_NO); + } else { + shpc_slot_command(shpc, i + SHPC_CMD_TRGT_MIN, + SHPC_STATE_NO, SHPC_LED_OFF, SHPC_LED_NO); + } + } + break; + default: + shpc_invalid_command(shpc); + break; + } +done: + pci_long_test_and_set_mask(shpc->config + SHPC_SERR_INT, SHPC_CMD_DETECTED); +} + +static void shpc_write(PCIDevice *d, unsigned addr, uint64_t val, int l) +{ + SHPCDevice *shpc = d->shpc; + int i; + if (addr >= SHPC_SIZEOF(d)) { + return; + } + l = MIN(l, SHPC_SIZEOF(d) - addr); + + /* TODO: code duplicated from pci.c */ + for (i = 0; i < l; val >>= 8, ++i) { + unsigned a = addr + i; + uint8_t wmask = shpc->wmask[a]; + uint8_t w1cmask = shpc->w1cmask[a]; + assert(!(wmask & w1cmask)); + shpc->config[a] = (shpc->config[a] & ~wmask) | (val & wmask); + shpc->config[a] &= ~(val & w1cmask); /* W1C: Write 1 to Clear */ + } + if (ranges_overlap(addr, l, SHPC_CMD_CODE, 2)) { + shpc_command(shpc); + } + shpc_interrupt_update(d); +} + +static uint64_t shpc_read(PCIDevice *d, unsigned addr, int l) +{ + uint64_t val = 0x0; + if (addr >= SHPC_SIZEOF(d)) { + return val; + } + l = MIN(l, SHPC_SIZEOF(d) - addr); + memcpy(&val, d->shpc->config + addr, l); + return val; +} + +/* SHPC Bridge Capability */ +#define SHPC_CAP_LENGTH 0x08 +#define SHPC_CAP_DWORD_SELECT 0x2 /* 1 byte */ +#define SHPC_CAP_CxP 0x3 /* 1 byte: CSP, CIP */ +#define SHPC_CAP_DWORD_DATA 0x4 /* 4 bytes */ +#define SHPC_CAP_CSP_MASK 0x4 +#define SHPC_CAP_CIP_MASK 0x8 + +static uint8_t shpc_cap_dword(PCIDevice *d) +{ + return pci_get_byte(d->config + d->shpc->cap + SHPC_CAP_DWORD_SELECT); +} + +/* Update dword data capability register */ +static void shpc_cap_update_dword(PCIDevice *d) +{ + unsigned data; + data = shpc_read(d, shpc_cap_dword(d) * 4, 4); + pci_set_long(d->config + d->shpc->cap + SHPC_CAP_DWORD_DATA, data); +} + +/* Add SHPC capability to the config space for the device. */ +static int shpc_cap_add_config(PCIDevice *d) +{ + uint8_t *config; + int config_offset; + config_offset = pci_add_capability(d, PCI_CAP_ID_SHPC, + 0, SHPC_CAP_LENGTH); + if (config_offset < 0) { + return config_offset; + } + config = d->config + config_offset; + + pci_set_byte(config + SHPC_CAP_DWORD_SELECT, 0); + pci_set_byte(config + SHPC_CAP_CxP, 0); + pci_set_long(config + SHPC_CAP_DWORD_DATA, 0); + d->shpc->cap = config_offset; + /* Make dword select and data writeable. */ + pci_set_byte(d->wmask + config_offset + SHPC_CAP_DWORD_SELECT, 0xff); + pci_set_long(d->wmask + config_offset + SHPC_CAP_DWORD_DATA, 0xffffffff); + return 0; +} + +static uint64_t shpc_mmio_read(void *opaque, hwaddr addr, + unsigned size) +{ + return shpc_read(opaque, addr, size); +} + +static void shpc_mmio_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + shpc_write(opaque, addr, val, size); +} + +static const MemoryRegionOps shpc_mmio_ops = { + .read = shpc_mmio_read, + .write = shpc_mmio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + /* SHPC ECN requires dword accesses, but the original 1.0 spec doesn't. + * It's easier to suppport all sizes than worry about it. */ + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static int shpc_device_hotplug(DeviceState *qdev, PCIDevice *affected_dev, + PCIHotplugState hotplug_state) +{ + int pci_slot = PCI_SLOT(affected_dev->devfn); + uint8_t state; + uint8_t led; + PCIDevice *d = DO_UPCAST(PCIDevice, qdev, qdev); + SHPCDevice *shpc = d->shpc; + int slot = SHPC_PCI_TO_IDX(pci_slot); + if (pci_slot < SHPC_IDX_TO_PCI(0) || slot >= shpc->nslots) { + error_report("Unsupported PCI slot %d for standard hotplug " + "controller. Valid slots are between %d and %d.", + pci_slot, SHPC_IDX_TO_PCI(0), + SHPC_IDX_TO_PCI(shpc->nslots) - 1); + return -1; + } + /* Don't send event when device is enabled during qemu machine creation: + * it is present on boot, no hotplug event is necessary. We do send an + * event when the device is disabled later. */ + if (hotplug_state == PCI_COLDPLUG_ENABLED) { + shpc_set_status(shpc, slot, 0, SHPC_SLOT_STATUS_MRL_OPEN); + shpc_set_status(shpc, slot, SHPC_SLOT_STATUS_PRSNT_7_5W, + SHPC_SLOT_STATUS_PRSNT_MASK); + return 0; + } + if (hotplug_state == PCI_HOTPLUG_DISABLED) { + shpc->config[SHPC_SLOT_EVENT_LATCH(slot)] |= SHPC_SLOT_EVENT_BUTTON; + state = shpc_get_status(shpc, slot, SHPC_SLOT_STATE_MASK); + led = shpc_get_status(shpc, slot, SHPC_SLOT_PWR_LED_MASK); + if (state == SHPC_STATE_DISABLED && led == SHPC_LED_OFF) { + shpc_free_devices_in_slot(shpc, slot); + shpc_set_status(shpc, slot, 1, SHPC_SLOT_STATUS_MRL_OPEN); + shpc_set_status(shpc, slot, SHPC_SLOT_STATUS_PRSNT_EMPTY, + SHPC_SLOT_STATUS_PRSNT_MASK); + shpc->config[SHPC_SLOT_EVENT_LATCH(slot)] |= + SHPC_SLOT_EVENT_MRL | + SHPC_SLOT_EVENT_PRESENCE; + } + } else { + /* This could be a cancellation of the previous removal. + * We check MRL state to figure out. */ + if (shpc_get_status(shpc, slot, SHPC_SLOT_STATUS_MRL_OPEN)) { + shpc_set_status(shpc, slot, 0, SHPC_SLOT_STATUS_MRL_OPEN); + shpc_set_status(shpc, slot, SHPC_SLOT_STATUS_PRSNT_7_5W, + SHPC_SLOT_STATUS_PRSNT_MASK); + shpc->config[SHPC_SLOT_EVENT_LATCH(slot)] |= + SHPC_SLOT_EVENT_BUTTON | + SHPC_SLOT_EVENT_MRL | + SHPC_SLOT_EVENT_PRESENCE; + } else { + /* Press attention button to cancel removal */ + shpc->config[SHPC_SLOT_EVENT_LATCH(slot)] |= + SHPC_SLOT_EVENT_BUTTON; + } + } + shpc_set_status(shpc, slot, 0, SHPC_SLOT_STATUS_66); + shpc_interrupt_update(d); + return 0; +} + +/* Initialize the SHPC structure in bridge's BAR. */ +int shpc_init(PCIDevice *d, PCIBus *sec_bus, MemoryRegion *bar, unsigned offset) +{ + int i, ret; + int nslots = SHPC_MAX_SLOTS; /* TODO: qdev property? */ + SHPCDevice *shpc = d->shpc = g_malloc0(sizeof(*d->shpc)); + shpc->sec_bus = sec_bus; + ret = shpc_cap_add_config(d); + if (ret) { + g_free(d->shpc); + return ret; + } + if (nslots < SHPC_MIN_SLOTS) { + return 0; + } + if (nslots > SHPC_MAX_SLOTS || + SHPC_IDX_TO_PCI(nslots) > PCI_SLOT_MAX) { + /* TODO: report an error mesage that makes sense. */ + return -EINVAL; + } + shpc->nslots = nslots; + shpc->config = g_malloc0(SHPC_SIZEOF(d)); + shpc->cmask = g_malloc0(SHPC_SIZEOF(d)); + shpc->wmask = g_malloc0(SHPC_SIZEOF(d)); + shpc->w1cmask = g_malloc0(SHPC_SIZEOF(d)); + + shpc_reset(d); + + pci_set_long(shpc->config + SHPC_BASE_OFFSET, offset); + + pci_set_byte(shpc->wmask + SHPC_CMD_CODE, 0xff); + pci_set_byte(shpc->wmask + SHPC_CMD_TRGT, SHPC_CMD_TRGT_MAX); + pci_set_byte(shpc->wmask + SHPC_CMD_TRGT, SHPC_CMD_TRGT_MAX); + pci_set_long(shpc->wmask + SHPC_SERR_INT, + SHPC_INT_DIS | + SHPC_SERR_DIS | + SHPC_CMD_INT_DIS | + SHPC_ARB_SERR_DIS); + pci_set_long(shpc->w1cmask + SHPC_SERR_INT, + SHPC_CMD_DETECTED | + SHPC_ARB_DETECTED); + for (i = 0; i < nslots; ++i) { + pci_set_byte(shpc->wmask + + SHPC_SLOT_EVENT_SERR_INT_DIS(d, i), + SHPC_SLOT_EVENT_PRESENCE | + SHPC_SLOT_EVENT_ISOLATED_FAULT | + SHPC_SLOT_EVENT_BUTTON | + SHPC_SLOT_EVENT_MRL | + SHPC_SLOT_EVENT_CONNECTED_FAULT | + SHPC_SLOT_EVENT_MRL_SERR_DIS | + SHPC_SLOT_EVENT_CONNECTED_FAULT_SERR_DIS); + pci_set_byte(shpc->w1cmask + + SHPC_SLOT_EVENT_LATCH(i), + SHPC_SLOT_EVENT_PRESENCE | + SHPC_SLOT_EVENT_ISOLATED_FAULT | + SHPC_SLOT_EVENT_BUTTON | + SHPC_SLOT_EVENT_MRL | + SHPC_SLOT_EVENT_CONNECTED_FAULT); + } + + /* TODO: init cmask */ + memory_region_init_io(&shpc->mmio, &shpc_mmio_ops, d, "shpc-mmio", + SHPC_SIZEOF(d)); + shpc_cap_update_dword(d); + memory_region_add_subregion(bar, offset, &shpc->mmio); + pci_bus_hotplug(sec_bus, shpc_device_hotplug, &d->qdev); + + d->cap_present |= QEMU_PCI_CAP_SHPC; + return 0; +} + +int shpc_bar_size(PCIDevice *d) +{ + return roundup_pow_of_two(SHPC_SLOT_REG(SHPC_MAX_SLOTS)); +} + +void shpc_cleanup(PCIDevice *d, MemoryRegion *bar) +{ + SHPCDevice *shpc = d->shpc; + d->cap_present &= ~QEMU_PCI_CAP_SHPC; + memory_region_del_subregion(bar, &shpc->mmio); + /* TODO: cleanup config space changes? */ + g_free(shpc->config); + g_free(shpc->cmask); + g_free(shpc->wmask); + g_free(shpc->w1cmask); + memory_region_destroy(&shpc->mmio); + g_free(shpc); +} + +void shpc_cap_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l) +{ + if (!ranges_overlap(addr, l, d->shpc->cap, SHPC_CAP_LENGTH)) { + return; + } + if (ranges_overlap(addr, l, d->shpc->cap + SHPC_CAP_DWORD_DATA, 4)) { + unsigned dword_data; + dword_data = pci_get_long(d->shpc->config + d->shpc->cap + + SHPC_CAP_DWORD_DATA); + shpc_write(d, shpc_cap_dword(d) * 4, dword_data, 4); + } + /* Update cap dword data in case guest is going to read it. */ + shpc_cap_update_dword(d); +} + +static void shpc_save(QEMUFile *f, void *pv, size_t size) +{ + PCIDevice *d = container_of(pv, PCIDevice, shpc); + qemu_put_buffer(f, d->shpc->config, SHPC_SIZEOF(d)); +} + +static int shpc_load(QEMUFile *f, void *pv, size_t size) +{ + PCIDevice *d = container_of(pv, PCIDevice, shpc); + int ret = qemu_get_buffer(f, d->shpc->config, SHPC_SIZEOF(d)); + if (ret != SHPC_SIZEOF(d)) { + return -EINVAL; + } + /* Make sure we don't lose notifications. An extra interrupt is harmless. */ + d->shpc->msi_requested = 0; + shpc_interrupt_update(d); + return 0; +} + +VMStateInfo shpc_vmstate_info = { + .name = "shpc", + .get = shpc_load, + .put = shpc_save, +}; diff --git a/hw/pci/shpc.h b/hw/pci/shpc.h new file mode 100644 index 0000000000..130b71df30 --- /dev/null +++ b/hw/pci/shpc.h @@ -0,0 +1,48 @@ +#ifndef SHPC_H +#define SHPC_H + +#include "qemu-common.h" +#include "memory.h" +#include "vmstate.h" + +struct SHPCDevice { + /* Capability offset in device's config space */ + int cap; + + /* # of hot-pluggable slots */ + int nslots; + + /* SHPC WRS: working register set */ + uint8_t *config; + + /* Used to enable checks on load. Note that writable bits are + * never checked even if set in cmask. */ + uint8_t *cmask; + + /* Used to implement R/W bytes */ + uint8_t *wmask; + + /* Used to implement RW1C(Write 1 to Clear) bytes */ + uint8_t *w1cmask; + + /* MMIO for the SHPC BAR */ + MemoryRegion mmio; + + /* Bus controlled by this SHPC */ + PCIBus *sec_bus; + + /* MSI already requested for this event */ + int msi_requested; +}; + +void shpc_reset(PCIDevice *d); +int shpc_bar_size(PCIDevice *dev); +int shpc_init(PCIDevice *dev, PCIBus *sec_bus, MemoryRegion *bar, unsigned off); +void shpc_cleanup(PCIDevice *dev, MemoryRegion *bar); +void shpc_cap_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int len); + +extern VMStateInfo shpc_vmstate_info; +#define SHPC_VMSTATE(_field, _type) \ + VMSTATE_BUFFER_UNSAFE_INFO(_field, _type, 0, shpc_vmstate_info, 0) + +#endif diff --git a/hw/pci/slotid_cap.c b/hw/pci/slotid_cap.c new file mode 100644 index 0000000000..01064521a9 --- /dev/null +++ b/hw/pci/slotid_cap.c @@ -0,0 +1,44 @@ +#include "slotid_cap.h" +#include "pci.h" + +#define SLOTID_CAP_LENGTH 4 +#define SLOTID_NSLOTS_SHIFT (ffs(PCI_SID_ESR_NSLOTS) - 1) + +int slotid_cap_init(PCIDevice *d, int nslots, + uint8_t chassis, + unsigned offset) +{ + int cap; + if (!chassis) { + error_report("Bridge chassis not specified. Each bridge is required " + "to be assigned a unique chassis id > 0."); + return -EINVAL; + } + if (nslots < 0 || nslots > (PCI_SID_ESR_NSLOTS >> SLOTID_NSLOTS_SHIFT)) { + /* TODO: error report? */ + return -EINVAL; + } + + cap = pci_add_capability(d, PCI_CAP_ID_SLOTID, offset, SLOTID_CAP_LENGTH); + if (cap < 0) { + return cap; + } + /* We make each chassis unique, this way each bridge is First in Chassis */ + d->config[cap + PCI_SID_ESR] = PCI_SID_ESR_FIC | + (nslots << SLOTID_NSLOTS_SHIFT); + d->cmask[cap + PCI_SID_ESR] = 0xff; + d->config[cap + PCI_SID_CHASSIS_NR] = chassis; + /* Note: Chassis number register is non-volatile, + so we don't reset it. */ + /* TODO: store in eeprom? */ + d->wmask[cap + PCI_SID_CHASSIS_NR] = 0xff; + + d->cap_present |= QEMU_PCI_CAP_SLOTID; + return 0; +} + +void slotid_cap_cleanup(PCIDevice *d) +{ + /* TODO: cleanup config space? */ + d->cap_present &= ~QEMU_PCI_CAP_SLOTID; +} diff --git a/hw/pci/slotid_cap.h b/hw/pci/slotid_cap.h new file mode 100644 index 0000000000..70db0470b0 --- /dev/null +++ b/hw/pci/slotid_cap.h @@ -0,0 +1,11 @@ +#ifndef PCI_SLOTID_CAP_H +#define PCI_SLOTID_CAP_H + +#include "qemu-common.h" + +int slotid_cap_init(PCIDevice *dev, int nslots, + uint8_t chassis, + unsigned offset); +void slotid_cap_cleanup(PCIDevice *dev); + +#endif diff --git a/hw/pci_bridge.c b/hw/pci_bridge.c deleted file mode 100644 index 4680501e4e..0000000000 --- a/hw/pci_bridge.c +++ /dev/null @@ -1,363 +0,0 @@ -/* - * QEMU PCI bus manager - * - * Copyright (c) 2004 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to dea - - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM - - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -/* - * split out from pci.c - * Copyright (c) 2010 Isaku Yamahata - * VA Linux Systems Japan K.K. - */ - -#include "pci_bridge.h" -#include "pci_internals.h" -#include "range.h" - -/* PCI bridge subsystem vendor ID helper functions */ -#define PCI_SSVID_SIZEOF 8 -#define PCI_SSVID_SVID 4 -#define PCI_SSVID_SSID 6 - -int pci_bridge_ssvid_init(PCIDevice *dev, uint8_t offset, - uint16_t svid, uint16_t ssid) -{ - int pos; - pos = pci_add_capability(dev, PCI_CAP_ID_SSVID, offset, PCI_SSVID_SIZEOF); - if (pos < 0) { - return pos; - } - - pci_set_word(dev->config + pos + PCI_SSVID_SVID, svid); - pci_set_word(dev->config + pos + PCI_SSVID_SSID, ssid); - return pos; -} - -/* Accessor function to get parent bridge device from pci bus. */ -PCIDevice *pci_bridge_get_device(PCIBus *bus) -{ - return bus->parent_dev; -} - -/* Accessor function to get secondary bus from pci-to-pci bridge device */ -PCIBus *pci_bridge_get_sec_bus(PCIBridge *br) -{ - return &br->sec_bus; -} - -static uint32_t pci_config_get_io_base(const PCIDevice *d, - uint32_t base, uint32_t base_upper16) -{ - uint32_t val; - - val = ((uint32_t)d->config[base] & PCI_IO_RANGE_MASK) << 8; - if (d->config[base] & PCI_IO_RANGE_TYPE_32) { - val |= (uint32_t)pci_get_word(d->config + base_upper16) << 16; - } - return val; -} - -static pcibus_t pci_config_get_memory_base(const PCIDevice *d, uint32_t base) -{ - return ((pcibus_t)pci_get_word(d->config + base) & PCI_MEMORY_RANGE_MASK) - << 16; -} - -static pcibus_t pci_config_get_pref_base(const PCIDevice *d, - uint32_t base, uint32_t upper) -{ - pcibus_t tmp; - pcibus_t val; - - tmp = (pcibus_t)pci_get_word(d->config + base); - val = (tmp & PCI_PREF_RANGE_MASK) << 16; - if (tmp & PCI_PREF_RANGE_TYPE_64) { - val |= (pcibus_t)pci_get_long(d->config + upper) << 32; - } - return val; -} - -/* accessor function to get bridge filtering base address */ -pcibus_t pci_bridge_get_base(const PCIDevice *bridge, uint8_t type) -{ - pcibus_t base; - if (type & PCI_BASE_ADDRESS_SPACE_IO) { - base = pci_config_get_io_base(bridge, - PCI_IO_BASE, PCI_IO_BASE_UPPER16); - } else { - if (type & PCI_BASE_ADDRESS_MEM_PREFETCH) { - base = pci_config_get_pref_base( - bridge, PCI_PREF_MEMORY_BASE, PCI_PREF_BASE_UPPER32); - } else { - base = pci_config_get_memory_base(bridge, PCI_MEMORY_BASE); - } - } - - return base; -} - -/* accessor funciton to get bridge filtering limit */ -pcibus_t pci_bridge_get_limit(const PCIDevice *bridge, uint8_t type) -{ - pcibus_t limit; - if (type & PCI_BASE_ADDRESS_SPACE_IO) { - limit = pci_config_get_io_base(bridge, - PCI_IO_LIMIT, PCI_IO_LIMIT_UPPER16); - limit |= 0xfff; /* PCI bridge spec 3.2.5.6. */ - } else { - if (type & PCI_BASE_ADDRESS_MEM_PREFETCH) { - limit = pci_config_get_pref_base( - bridge, PCI_PREF_MEMORY_LIMIT, PCI_PREF_LIMIT_UPPER32); - } else { - limit = pci_config_get_memory_base(bridge, PCI_MEMORY_LIMIT); - } - limit |= 0xfffff; /* PCI bridge spec 3.2.5.{1, 8}. */ - } - return limit; -} - -static void pci_bridge_init_alias(PCIBridge *bridge, MemoryRegion *alias, - uint8_t type, const char *name, - MemoryRegion *space, - MemoryRegion *parent_space, - bool enabled) -{ - pcibus_t base = pci_bridge_get_base(&bridge->dev, type); - pcibus_t limit = pci_bridge_get_limit(&bridge->dev, type); - /* TODO: this doesn't handle base = 0 limit = 2^64 - 1 correctly. - * Apparently no way to do this with existing memory APIs. */ - pcibus_t size = enabled && limit >= base ? limit + 1 - base : 0; - - memory_region_init_alias(alias, name, space, base, size); - memory_region_add_subregion_overlap(parent_space, base, alias, 1); -} - -static PCIBridgeWindows *pci_bridge_region_init(PCIBridge *br) -{ - PCIBus *parent = br->dev.bus; - PCIBridgeWindows *w = g_new(PCIBridgeWindows, 1); - uint16_t cmd = pci_get_word(br->dev.config + PCI_COMMAND); - - pci_bridge_init_alias(br, &w->alias_pref_mem, - PCI_BASE_ADDRESS_MEM_PREFETCH, - "pci_bridge_pref_mem", - &br->address_space_mem, - parent->address_space_mem, - cmd & PCI_COMMAND_MEMORY); - pci_bridge_init_alias(br, &w->alias_mem, - PCI_BASE_ADDRESS_SPACE_MEMORY, - "pci_bridge_mem", - &br->address_space_mem, - parent->address_space_mem, - cmd & PCI_COMMAND_MEMORY); - pci_bridge_init_alias(br, &w->alias_io, - PCI_BASE_ADDRESS_SPACE_IO, - "pci_bridge_io", - &br->address_space_io, - parent->address_space_io, - cmd & PCI_COMMAND_IO); - /* TODO: optinal VGA and VGA palette snooping support. */ - - return w; -} - -static void pci_bridge_region_del(PCIBridge *br, PCIBridgeWindows *w) -{ - PCIBus *parent = br->dev.bus; - - memory_region_del_subregion(parent->address_space_io, &w->alias_io); - memory_region_del_subregion(parent->address_space_mem, &w->alias_mem); - memory_region_del_subregion(parent->address_space_mem, &w->alias_pref_mem); -} - -static void pci_bridge_region_cleanup(PCIBridge *br, PCIBridgeWindows *w) -{ - memory_region_destroy(&w->alias_io); - memory_region_destroy(&w->alias_mem); - memory_region_destroy(&w->alias_pref_mem); - g_free(w); -} - -static void pci_bridge_update_mappings(PCIBridge *br) -{ - PCIBridgeWindows *w = br->windows; - - /* Make updates atomic to: handle the case of one VCPU updating the bridge - * while another accesses an unaffected region. */ - memory_region_transaction_begin(); - pci_bridge_region_del(br, br->windows); - br->windows = pci_bridge_region_init(br); - memory_region_transaction_commit(); - pci_bridge_region_cleanup(br, w); -} - -/* default write_config function for PCI-to-PCI bridge */ -void pci_bridge_write_config(PCIDevice *d, - uint32_t address, uint32_t val, int len) -{ - PCIBridge *s = container_of(d, PCIBridge, dev); - uint16_t oldctl = pci_get_word(d->config + PCI_BRIDGE_CONTROL); - uint16_t newctl; - - pci_default_write_config(d, address, val, len); - - if (ranges_overlap(address, len, PCI_COMMAND, 2) || - - /* io base/limit */ - ranges_overlap(address, len, PCI_IO_BASE, 2) || - - /* memory base/limit, prefetchable base/limit and - io base/limit upper 16 */ - ranges_overlap(address, len, PCI_MEMORY_BASE, 20)) { - pci_bridge_update_mappings(s); - } - - newctl = pci_get_word(d->config + PCI_BRIDGE_CONTROL); - if (~oldctl & newctl & PCI_BRIDGE_CTL_BUS_RESET) { - /* Trigger hot reset on 0->1 transition. */ - pci_bus_reset(&s->sec_bus); - } -} - -void pci_bridge_disable_base_limit(PCIDevice *dev) -{ - uint8_t *conf = dev->config; - - pci_byte_test_and_set_mask(conf + PCI_IO_BASE, - PCI_IO_RANGE_MASK & 0xff); - pci_byte_test_and_clear_mask(conf + PCI_IO_LIMIT, - PCI_IO_RANGE_MASK & 0xff); - pci_word_test_and_set_mask(conf + PCI_MEMORY_BASE, - PCI_MEMORY_RANGE_MASK & 0xffff); - pci_word_test_and_clear_mask(conf + PCI_MEMORY_LIMIT, - PCI_MEMORY_RANGE_MASK & 0xffff); - pci_word_test_and_set_mask(conf + PCI_PREF_MEMORY_BASE, - PCI_PREF_RANGE_MASK & 0xffff); - pci_word_test_and_clear_mask(conf + PCI_PREF_MEMORY_LIMIT, - PCI_PREF_RANGE_MASK & 0xffff); - pci_set_long(conf + PCI_PREF_BASE_UPPER32, 0); - pci_set_long(conf + PCI_PREF_LIMIT_UPPER32, 0); -} - -/* reset bridge specific configuration registers */ -void pci_bridge_reset(DeviceState *qdev) -{ - PCIDevice *dev = PCI_DEVICE(qdev); - uint8_t *conf = dev->config; - - conf[PCI_PRIMARY_BUS] = 0; - conf[PCI_SECONDARY_BUS] = 0; - conf[PCI_SUBORDINATE_BUS] = 0; - conf[PCI_SEC_LATENCY_TIMER] = 0; - - /* - * the default values for base/limit registers aren't specified - * in the PCI-to-PCI-bridge spec. So we don't thouch them here. - * Each implementation can override it. - * typical implementation does - * zero base/limit registers or - * disable forwarding: pci_bridge_disable_base_limit() - * If disable forwarding is wanted, call pci_bridge_disable_base_limit() - * after this function. - */ - pci_byte_test_and_clear_mask(conf + PCI_IO_BASE, - PCI_IO_RANGE_MASK & 0xff); - pci_byte_test_and_clear_mask(conf + PCI_IO_LIMIT, - PCI_IO_RANGE_MASK & 0xff); - pci_word_test_and_clear_mask(conf + PCI_MEMORY_BASE, - PCI_MEMORY_RANGE_MASK & 0xffff); - pci_word_test_and_clear_mask(conf + PCI_MEMORY_LIMIT, - PCI_MEMORY_RANGE_MASK & 0xffff); - pci_word_test_and_clear_mask(conf + PCI_PREF_MEMORY_BASE, - PCI_PREF_RANGE_MASK & 0xffff); - pci_word_test_and_clear_mask(conf + PCI_PREF_MEMORY_LIMIT, - PCI_PREF_RANGE_MASK & 0xffff); - pci_set_long(conf + PCI_PREF_BASE_UPPER32, 0); - pci_set_long(conf + PCI_PREF_LIMIT_UPPER32, 0); - - pci_set_word(conf + PCI_BRIDGE_CONTROL, 0); -} - -/* default qdev initialization function for PCI-to-PCI bridge */ -int pci_bridge_initfn(PCIDevice *dev) -{ - PCIBus *parent = dev->bus; - PCIBridge *br = DO_UPCAST(PCIBridge, dev, dev); - PCIBus *sec_bus = &br->sec_bus; - - pci_word_test_and_set_mask(dev->config + PCI_STATUS, - PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK); - pci_config_set_class(dev->config, PCI_CLASS_BRIDGE_PCI); - dev->config[PCI_HEADER_TYPE] = - (dev->config[PCI_HEADER_TYPE] & PCI_HEADER_TYPE_MULTI_FUNCTION) | - PCI_HEADER_TYPE_BRIDGE; - pci_set_word(dev->config + PCI_SEC_STATUS, - PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK); - - /* - * If we don't specify the name, the bus will be addressed as .0, where - * id is the device id. - * Since PCI Bridge devices have a single bus each, we don't need the index: - * let users address the bus using the device name. - */ - if (!br->bus_name && dev->qdev.id && *dev->qdev.id) { - br->bus_name = dev->qdev.id; - } - - qbus_create_inplace(&sec_bus->qbus, TYPE_PCI_BUS, &dev->qdev, - br->bus_name); - sec_bus->parent_dev = dev; - sec_bus->map_irq = br->map_irq; - sec_bus->address_space_mem = &br->address_space_mem; - memory_region_init(&br->address_space_mem, "pci_bridge_pci", INT64_MAX); - sec_bus->address_space_io = &br->address_space_io; - memory_region_init(&br->address_space_io, "pci_bridge_io", 65536); - br->windows = pci_bridge_region_init(br); - QLIST_INIT(&sec_bus->child); - QLIST_INSERT_HEAD(&parent->child, sec_bus, sibling); - return 0; -} - -/* default qdev clean up function for PCI-to-PCI bridge */ -void pci_bridge_exitfn(PCIDevice *pci_dev) -{ - PCIBridge *s = DO_UPCAST(PCIBridge, dev, pci_dev); - assert(QLIST_EMPTY(&s->sec_bus.child)); - QLIST_REMOVE(&s->sec_bus, sibling); - pci_bridge_region_del(s, s->windows); - pci_bridge_region_cleanup(s, s->windows); - memory_region_destroy(&s->address_space_mem); - memory_region_destroy(&s->address_space_io); - /* qbus_free() is called automatically by qdev_free() */ -} - -/* - * before qdev initialization(qdev_init()), this function sets bus_name and - * map_irq callback which are necessry for pci_bridge_initfn() to - * initialize bus. - */ -void pci_bridge_map_irq(PCIBridge *br, const char* bus_name, - pci_map_irq_fn map_irq) -{ - br->map_irq = map_irq; - br->bus_name = bus_name; -} diff --git a/hw/pci_bridge.h b/hw/pci_bridge.h deleted file mode 100644 index a00accc172..0000000000 --- a/hw/pci_bridge.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * QEMU PCI bridge - * - * Copyright (c) 2004 Fabrice Bellard - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * split out pci bus specific stuff from pci.[hc] to pci_bridge.[hc] - * Copyright (c) 2009 Isaku Yamahata - * VA Linux Systems Japan K.K. - * - */ - -#ifndef QEMU_PCI_BRIDGE_H -#define QEMU_PCI_BRIDGE_H - -#include "pci.h" - -int pci_bridge_ssvid_init(PCIDevice *dev, uint8_t offset, - uint16_t svid, uint16_t ssid); - -PCIDevice *pci_bridge_get_device(PCIBus *bus); -PCIBus *pci_bridge_get_sec_bus(PCIBridge *br); - -pcibus_t pci_bridge_get_base(const PCIDevice *bridge, uint8_t type); -pcibus_t pci_bridge_get_limit(const PCIDevice *bridge, uint8_t type); - -void pci_bridge_write_config(PCIDevice *d, - uint32_t address, uint32_t val, int len); -void pci_bridge_disable_base_limit(PCIDevice *dev); -void pci_bridge_reset_reg(PCIDevice *dev); -void pci_bridge_reset(DeviceState *qdev); - -int pci_bridge_initfn(PCIDevice *pci_dev); -void pci_bridge_exitfn(PCIDevice *pci_dev); - - -/* - * before qdev initialization(qdev_init()), this function sets bus_name and - * map_irq callback which are necessry for pci_bridge_initfn() to - * initialize bus. - */ -void pci_bridge_map_irq(PCIBridge *br, const char* bus_name, - pci_map_irq_fn map_irq); - -#endif /* QEMU_PCI_BRIDGE_H */ -/* - * Local variables: - * c-indent-level: 4 - * c-basic-offset: 4 - * tab-width: 8 - * indent-tab-mode: nil - * End: - */ diff --git a/hw/pci_host.c b/hw/pci_host.c deleted file mode 100644 index 68e328cd23..0000000000 --- a/hw/pci_host.c +++ /dev/null @@ -1,180 +0,0 @@ -/* - * pci_host.c - * - * Copyright (c) 2009 Isaku Yamahata - * VA Linux Systems Japan K.K. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "pci.h" -#include "pci_host.h" - -/* debug PCI */ -//#define DEBUG_PCI - -#ifdef DEBUG_PCI -#define PCI_DPRINTF(fmt, ...) \ -do { printf("pci_host_data: " fmt , ## __VA_ARGS__); } while (0) -#else -#define PCI_DPRINTF(fmt, ...) -#endif - -/* - * PCI address - * bit 16 - 24: bus number - * bit 8 - 15: devfun number - * bit 0 - 7: offset in configuration space of a given pci device - */ - -/* the helper functio to get a PCIDeice* for a given pci address */ -static inline PCIDevice *pci_dev_find_by_addr(PCIBus *bus, uint32_t addr) -{ - uint8_t bus_num = addr >> 16; - uint8_t devfn = addr >> 8; - - return pci_find_device(bus, bus_num, devfn); -} - -void pci_host_config_write_common(PCIDevice *pci_dev, uint32_t addr, - uint32_t limit, uint32_t val, uint32_t len) -{ - assert(len <= 4); - pci_dev->config_write(pci_dev, addr, val, MIN(len, limit - addr)); -} - -uint32_t pci_host_config_read_common(PCIDevice *pci_dev, uint32_t addr, - uint32_t limit, uint32_t len) -{ - assert(len <= 4); - return pci_dev->config_read(pci_dev, addr, MIN(len, limit - addr)); -} - -void pci_data_write(PCIBus *s, uint32_t addr, uint32_t val, int len) -{ - PCIDevice *pci_dev = pci_dev_find_by_addr(s, addr); - uint32_t config_addr = addr & (PCI_CONFIG_SPACE_SIZE - 1); - - if (!pci_dev) { - return; - } - - PCI_DPRINTF("%s: %s: addr=%02" PRIx32 " val=%08" PRIx32 " len=%d\n", - __func__, pci_dev->name, config_addr, val, len); - pci_host_config_write_common(pci_dev, config_addr, PCI_CONFIG_SPACE_SIZE, - val, len); -} - -uint32_t pci_data_read(PCIBus *s, uint32_t addr, int len) -{ - PCIDevice *pci_dev = pci_dev_find_by_addr(s, addr); - uint32_t config_addr = addr & (PCI_CONFIG_SPACE_SIZE - 1); - uint32_t val; - - if (!pci_dev) { - return ~0x0; - } - - val = pci_host_config_read_common(pci_dev, config_addr, - PCI_CONFIG_SPACE_SIZE, len); - PCI_DPRINTF("%s: %s: addr=%02"PRIx32" val=%08"PRIx32" len=%d\n", - __func__, pci_dev->name, config_addr, val, len); - - return val; -} - -static void pci_host_config_write(void *opaque, hwaddr addr, - uint64_t val, unsigned len) -{ - PCIHostState *s = opaque; - - PCI_DPRINTF("%s addr " TARGET_FMT_plx " len %d val %"PRIx64"\n", - __func__, addr, len, val); - if (addr != 0 || len != 4) { - return; - } - s->config_reg = val; -} - -static uint64_t pci_host_config_read(void *opaque, hwaddr addr, - unsigned len) -{ - PCIHostState *s = opaque; - uint32_t val = s->config_reg; - - PCI_DPRINTF("%s addr " TARGET_FMT_plx " len %d val %"PRIx32"\n", - __func__, addr, len, val); - return val; -} - -static void pci_host_data_write(void *opaque, hwaddr addr, - uint64_t val, unsigned len) -{ - PCIHostState *s = opaque; - PCI_DPRINTF("write addr " TARGET_FMT_plx " len %d val %x\n", - addr, len, (unsigned)val); - if (s->config_reg & (1u << 31)) - pci_data_write(s->bus, s->config_reg | (addr & 3), val, len); -} - -static uint64_t pci_host_data_read(void *opaque, - hwaddr addr, unsigned len) -{ - PCIHostState *s = opaque; - uint32_t val; - if (!(s->config_reg & (1 << 31))) - return 0xffffffff; - val = pci_data_read(s->bus, s->config_reg | (addr & 3), len); - PCI_DPRINTF("read addr " TARGET_FMT_plx " len %d val %x\n", - addr, len, val); - return val; -} - -const MemoryRegionOps pci_host_conf_le_ops = { - .read = pci_host_config_read, - .write = pci_host_config_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -const MemoryRegionOps pci_host_conf_be_ops = { - .read = pci_host_config_read, - .write = pci_host_config_write, - .endianness = DEVICE_BIG_ENDIAN, -}; - -const MemoryRegionOps pci_host_data_le_ops = { - .read = pci_host_data_read, - .write = pci_host_data_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -const MemoryRegionOps pci_host_data_be_ops = { - .read = pci_host_data_read, - .write = pci_host_data_write, - .endianness = DEVICE_BIG_ENDIAN, -}; - -static const TypeInfo pci_host_type_info = { - .name = TYPE_PCI_HOST_BRIDGE, - .parent = TYPE_SYS_BUS_DEVICE, - .abstract = true, - .instance_size = sizeof(PCIHostState), -}; - -static void pci_host_register_types(void) -{ - type_register_static(&pci_host_type_info); -} - -type_init(pci_host_register_types) diff --git a/hw/pci_host.h b/hw/pci_host.h deleted file mode 100644 index 4b9c300fcf..0000000000 --- a/hw/pci_host.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * QEMU Common PCI Host bridge configuration data space access routines. - * - * Copyright (c) 2006 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* Worker routines for a PCI host controller that uses an {address,data} - register pair to access PCI configuration space. */ - -#ifndef PCI_HOST_H -#define PCI_HOST_H - -#include "sysbus.h" - -#define TYPE_PCI_HOST_BRIDGE "pci-host-bridge" -#define PCI_HOST_BRIDGE(obj) \ - OBJECT_CHECK(PCIHostState, (obj), TYPE_PCI_HOST_BRIDGE) - -struct PCIHostState { - SysBusDevice busdev; - - MemoryRegion conf_mem; - MemoryRegion data_mem; - MemoryRegion mmcfg; - MemoryRegion *address_space; - uint32_t config_reg; - PCIBus *bus; -}; - -/* common internal helpers for PCI/PCIe hosts, cut off overflows */ -void pci_host_config_write_common(PCIDevice *pci_dev, uint32_t addr, - uint32_t limit, uint32_t val, uint32_t len); -uint32_t pci_host_config_read_common(PCIDevice *pci_dev, uint32_t addr, - uint32_t limit, uint32_t len); - -void pci_data_write(PCIBus *s, uint32_t addr, uint32_t val, int len); -uint32_t pci_data_read(PCIBus *s, uint32_t addr, int len); - -extern const MemoryRegionOps pci_host_conf_le_ops; -extern const MemoryRegionOps pci_host_conf_be_ops; -extern const MemoryRegionOps pci_host_data_le_ops; -extern const MemoryRegionOps pci_host_data_be_ops; - -#endif /* PCI_HOST_H */ diff --git a/hw/pci_ids.h b/hw/pci_ids.h deleted file mode 100644 index 5df7245349..0000000000 --- a/hw/pci_ids.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - * PCI Class, Vendor and Device IDs - * - * Please keep sorted. - * - * Abbreviated version of linux/pci_ids.h - * - * QEMU-specific definitions belong in pci.h - */ - -/* Device classes and subclasses */ - -#define PCI_BASE_CLASS_STORAGE 0x01 -#define PCI_BASE_CLASS_NETWORK 0x02 - -#define PCI_CLASS_STORAGE_SCSI 0x0100 -#define PCI_CLASS_STORAGE_IDE 0x0101 -#define PCI_CLASS_STORAGE_RAID 0x0104 -#define PCI_CLASS_STORAGE_SATA 0x0106 -#define PCI_CLASS_STORAGE_OTHER 0x0180 - -#define PCI_CLASS_NETWORK_ETHERNET 0x0200 - -#define PCI_CLASS_DISPLAY_VGA 0x0300 -#define PCI_CLASS_DISPLAY_OTHER 0x0380 - -#define PCI_CLASS_MULTIMEDIA_AUDIO 0x0401 - -#define PCI_CLASS_MEMORY_RAM 0x0500 - -#define PCI_CLASS_SYSTEM_OTHER 0x0880 - -#define PCI_CLASS_SERIAL_USB 0x0c03 -#define PCI_CLASS_SERIAL_SMBUS 0x0c05 - -#define PCI_CLASS_BRIDGE_HOST 0x0600 -#define PCI_CLASS_BRIDGE_ISA 0x0601 -#define PCI_CLASS_BRIDGE_PCI 0x0604 -#define PCI_CLASS_BRDIGE_PCI_INF_SUB 0x01 -#define PCI_CLASS_BRIDGE_OTHER 0x0680 - -#define PCI_CLASS_COMMUNICATION_SERIAL 0x0700 -#define PCI_CLASS_COMMUNICATION_OTHER 0x0780 - -#define PCI_CLASS_PROCESSOR_CO 0x0b40 -#define PCI_CLASS_PROCESSOR_POWERPC 0x0b20 - -#define PCI_CLASS_OTHERS 0xff - -/* Vendors and devices. Sort key: vendor first, device next. */ - -#define PCI_VENDOR_ID_LSI_LOGIC 0x1000 -#define PCI_DEVICE_ID_LSI_53C895A 0x0012 -#define PCI_DEVICE_ID_LSI_SAS1078 0x0060 - -#define PCI_VENDOR_ID_DEC 0x1011 -#define PCI_DEVICE_ID_DEC_21154 0x0026 - -#define PCI_VENDOR_ID_CIRRUS 0x1013 - -#define PCI_VENDOR_ID_IBM 0x1014 - -#define PCI_VENDOR_ID_AMD 0x1022 -#define PCI_DEVICE_ID_AMD_LANCE 0x2000 -#define PCI_DEVICE_ID_AMD_SCSI 0x2020 - -#define PCI_VENDOR_ID_TI 0x104c - -#define PCI_VENDOR_ID_MOTOROLA 0x1057 -#define PCI_DEVICE_ID_MOTOROLA_MPC106 0x0002 -#define PCI_DEVICE_ID_MOTOROLA_RAVEN 0x4801 - -#define PCI_VENDOR_ID_APPLE 0x106b -#define PCI_DEVICE_ID_APPLE_UNI_N_AGP 0x0020 -#define PCI_DEVICE_ID_APPLE_U3_AGP 0x004b - -#define PCI_VENDOR_ID_SUN 0x108e -#define PCI_DEVICE_ID_SUN_EBUS 0x1000 -#define PCI_DEVICE_ID_SUN_SIMBA 0x5000 -#define PCI_DEVICE_ID_SUN_SABRE 0xa000 - -#define PCI_VENDOR_ID_CMD 0x1095 -#define PCI_DEVICE_ID_CMD_646 0x0646 - -#define PCI_VENDOR_ID_REALTEK 0x10ec -#define PCI_DEVICE_ID_REALTEK_8139 0x8139 - -#define PCI_VENDOR_ID_XILINX 0x10ee - -#define PCI_VENDOR_ID_VIA 0x1106 -#define PCI_DEVICE_ID_VIA_ISA_BRIDGE 0x0686 -#define PCI_DEVICE_ID_VIA_IDE 0x0571 -#define PCI_DEVICE_ID_VIA_UHCI 0x3038 -#define PCI_DEVICE_ID_VIA_ACPI 0x3057 -#define PCI_DEVICE_ID_VIA_AC97 0x3058 -#define PCI_DEVICE_ID_VIA_MC97 0x3068 - -#define PCI_VENDOR_ID_MARVELL 0x11ab - -#define PCI_VENDOR_ID_ENSONIQ 0x1274 -#define PCI_DEVICE_ID_ENSONIQ_ES1370 0x5000 - -#define PCI_VENDOR_ID_FREESCALE 0x1957 -#define PCI_DEVICE_ID_MPC8533E 0x0030 - -#define PCI_VENDOR_ID_INTEL 0x8086 -#define PCI_DEVICE_ID_INTEL_82378 0x0484 -#define PCI_DEVICE_ID_INTEL_82441 0x1237 -#define PCI_DEVICE_ID_INTEL_82801AA_5 0x2415 -#define PCI_DEVICE_ID_INTEL_82801BA_11 0x244e -#define PCI_DEVICE_ID_INTEL_82801D 0x24CD -#define PCI_DEVICE_ID_INTEL_ESB_9 0x25ab -#define PCI_DEVICE_ID_INTEL_82371SB_0 0x7000 -#define PCI_DEVICE_ID_INTEL_82371SB_1 0x7010 -#define PCI_DEVICE_ID_INTEL_82371SB_2 0x7020 -#define PCI_DEVICE_ID_INTEL_82371AB_0 0x7110 -#define PCI_DEVICE_ID_INTEL_82371AB 0x7111 -#define PCI_DEVICE_ID_INTEL_82371AB_2 0x7112 -#define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113 - -#define PCI_DEVICE_ID_INTEL_ICH9_0 0x2910 -#define PCI_DEVICE_ID_INTEL_ICH9_1 0x2917 -#define PCI_DEVICE_ID_INTEL_ICH9_2 0x2912 -#define PCI_DEVICE_ID_INTEL_ICH9_3 0x2913 -#define PCI_DEVICE_ID_INTEL_ICH9_4 0x2914 -#define PCI_DEVICE_ID_INTEL_ICH9_5 0x2919 -#define PCI_DEVICE_ID_INTEL_ICH9_6 0x2930 -#define PCI_DEVICE_ID_INTEL_ICH9_7 0x2916 -#define PCI_DEVICE_ID_INTEL_ICH9_8 0x2918 - -#define PCI_DEVICE_ID_INTEL_82801I_UHCI1 0x2934 -#define PCI_DEVICE_ID_INTEL_82801I_UHCI2 0x2935 -#define PCI_DEVICE_ID_INTEL_82801I_UHCI3 0x2936 -#define PCI_DEVICE_ID_INTEL_82801I_UHCI4 0x2937 -#define PCI_DEVICE_ID_INTEL_82801I_UHCI5 0x2938 -#define PCI_DEVICE_ID_INTEL_82801I_UHCI6 0x2939 -#define PCI_DEVICE_ID_INTEL_82801I_EHCI1 0x293a -#define PCI_DEVICE_ID_INTEL_82801I_EHCI2 0x293c -#define PCI_DEVICE_ID_INTEL_82599_SFP_VF 0x10ed - -#define PCI_DEVICE_ID_INTEL_Q35_MCH 0x29c0 - -#define PCI_VENDOR_ID_XEN 0x5853 -#define PCI_DEVICE_ID_XEN_PLATFORM 0x0001 - -#define PCI_VENDOR_ID_NEC 0x1033 -#define PCI_DEVICE_ID_NEC_UPD720200 0x0194 diff --git a/hw/pci_internals.h b/hw/pci_internals.h deleted file mode 100644 index 21d0ce6973..0000000000 --- a/hw/pci_internals.h +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef QEMU_PCI_INTERNALS_H -#define QEMU_PCI_INTERNALS_H - -/* - * This header files is private to pci.c and pci_bridge.c - * So following structures are opaque to others and shouldn't be - * accessed. - * - * For pci-to-pci bridge needs to include this header file to embed - * PCIBridge in its structure or to get sizeof(PCIBridge), - * However, they shouldn't access those following members directly. - * Use accessor function in pci.h, pci_bridge.h - */ - -#define TYPE_PCI_BUS "PCI" -#define PCI_BUS(obj) OBJECT_CHECK(PCIBus, (obj), TYPE_PCI_BUS) - -struct PCIBus { - BusState qbus; - PCIDMAContextFunc dma_context_fn; - void *dma_context_opaque; - uint8_t devfn_min; - pci_set_irq_fn set_irq; - pci_map_irq_fn map_irq; - pci_route_irq_fn route_intx_to_irq; - pci_hotplug_fn hotplug; - DeviceState *hotplug_qdev; - void *irq_opaque; - PCIDevice *devices[PCI_SLOT_MAX * PCI_FUNC_MAX]; - PCIDevice *parent_dev; - MemoryRegion *address_space_mem; - MemoryRegion *address_space_io; - - QLIST_HEAD(, PCIBus) child; /* this will be replaced by qdev later */ - QLIST_ENTRY(PCIBus) sibling;/* this will be replaced by qdev later */ - - /* The bus IRQ state is the logical OR of the connected devices. - Keep a count of the number of devices with raised IRQs. */ - int nirq; - int *irq_count; -}; - -typedef struct PCIBridgeWindows PCIBridgeWindows; - -/* - * Aliases for each of the address space windows that the bridge - * can forward. Mapped into the bridge's parent's address space, - * as subregions. - */ -struct PCIBridgeWindows { - MemoryRegion alias_pref_mem; - MemoryRegion alias_mem; - MemoryRegion alias_io; -}; - -struct PCIBridge { - PCIDevice dev; - - /* private member */ - PCIBus sec_bus; - /* - * Memory regions for the bridge's address spaces. These regions are not - * directly added to system_memory/system_io or its descendants. - * Bridge's secondary bus points to these, so that devices - * under the bridge see these regions as its address spaces. - * The regions are as large as the entire address space - - * they don't take into account any windows. - */ - MemoryRegion address_space_mem; - MemoryRegion address_space_io; - - PCIBridgeWindows *windows; - - pci_map_irq_fn map_irq; - const char *bus_name; -}; - -#endif /* QEMU_PCI_INTERNALS_H */ diff --git a/hw/pci_regs.h b/hw/pci_regs.h deleted file mode 100644 index 56a404be6e..0000000000 --- a/hw/pci_regs.h +++ /dev/null @@ -1,717 +0,0 @@ -/* - * pci_regs.h - * - * PCI standard defines - * Copyright 1994, Drew Eckhardt - * Copyright 1997--1999 Martin Mares - * - * For more information, please consult the following manuals (look at - * http://www.pcisig.com/ for how to get them): - * - * PCI BIOS Specification - * PCI Local Bus Specification - * PCI to PCI Bridge Specification - * PCI System Design Guide - * - * For hypertransport information, please consult the following manuals - * from http://www.hypertransport.org - * - * The Hypertransport I/O Link Specification - */ - -#ifndef LINUX_PCI_REGS_H -#define LINUX_PCI_REGS_H - -/* - * Under PCI, each device has 256 bytes of configuration address space, - * of which the first 64 bytes are standardized as follows: - */ -#define PCI_VENDOR_ID 0x00 /* 16 bits */ -#define PCI_DEVICE_ID 0x02 /* 16 bits */ -#define PCI_COMMAND 0x04 /* 16 bits */ -#define PCI_COMMAND_IO 0x1 /* Enable response in I/O space */ -#define PCI_COMMAND_MEMORY 0x2 /* Enable response in Memory space */ -#define PCI_COMMAND_MASTER 0x4 /* Enable bus mastering */ -#define PCI_COMMAND_SPECIAL 0x8 /* Enable response to special cycles */ -#define PCI_COMMAND_INVALIDATE 0x10 /* Use memory write and invalidate */ -#define PCI_COMMAND_VGA_PALETTE 0x20 /* Enable palette snooping */ -#define PCI_COMMAND_PARITY 0x40 /* Enable parity checking */ -#define PCI_COMMAND_WAIT 0x80 /* Enable address/data stepping */ -#define PCI_COMMAND_SERR 0x100 /* Enable SERR */ -#define PCI_COMMAND_FAST_BACK 0x200 /* Enable back-to-back writes */ -#define PCI_COMMAND_INTX_DISABLE 0x400 /* INTx Emulation Disable */ - -#define PCI_STATUS 0x06 /* 16 bits */ -#define PCI_STATUS_INTERRUPT 0x08 /* Interrupt status */ -#define PCI_STATUS_CAP_LIST 0x10 /* Support Capability List */ -#define PCI_STATUS_66MHZ 0x20 /* Support 66 Mhz PCI 2.1 bus */ -#define PCI_STATUS_UDF 0x40 /* Support User Definable Features [obsolete] */ -#define PCI_STATUS_FAST_BACK 0x80 /* Accept fast-back to back */ -#define PCI_STATUS_PARITY 0x100 /* Detected parity error */ -#define PCI_STATUS_DEVSEL_MASK 0x600 /* DEVSEL timing */ -#define PCI_STATUS_DEVSEL_FAST 0x000 -#define PCI_STATUS_DEVSEL_MEDIUM 0x200 -#define PCI_STATUS_DEVSEL_SLOW 0x400 -#define PCI_STATUS_SIG_TARGET_ABORT 0x800 /* Set on target abort */ -#define PCI_STATUS_REC_TARGET_ABORT 0x1000 /* Master ack of " */ -#define PCI_STATUS_REC_MASTER_ABORT 0x2000 /* Set on master abort */ -#define PCI_STATUS_SIG_SYSTEM_ERROR 0x4000 /* Set when we drive SERR */ -#define PCI_STATUS_DETECTED_PARITY 0x8000 /* Set on parity error */ - -#define PCI_CLASS_REVISION 0x08 /* High 24 bits are class, low 8 revision */ -#define PCI_REVISION_ID 0x08 /* Revision ID */ -#define PCI_CLASS_PROG 0x09 /* Reg. Level Programming Interface */ -#define PCI_CLASS_DEVICE 0x0a /* Device class */ - -#define PCI_CACHE_LINE_SIZE 0x0c /* 8 bits */ -#define PCI_LATENCY_TIMER 0x0d /* 8 bits */ -#define PCI_HEADER_TYPE 0x0e /* 8 bits */ -#define PCI_HEADER_TYPE_NORMAL 0 -#define PCI_HEADER_TYPE_BRIDGE 1 -#define PCI_HEADER_TYPE_CARDBUS 2 - -#define PCI_BIST 0x0f /* 8 bits */ -#define PCI_BIST_CODE_MASK 0x0f /* Return result */ -#define PCI_BIST_START 0x40 /* 1 to start BIST, 2 secs or less */ -#define PCI_BIST_CAPABLE 0x80 /* 1 if BIST capable */ - -/* - * Base addresses specify locations in memory or I/O space. - * Decoded size can be determined by writing a value of - * 0xffffffff to the register, and reading it back. Only - * 1 bits are decoded. - */ -#define PCI_BASE_ADDRESS_0 0x10 /* 32 bits */ -#define PCI_BASE_ADDRESS_1 0x14 /* 32 bits [htype 0,1 only] */ -#define PCI_BASE_ADDRESS_2 0x18 /* 32 bits [htype 0 only] */ -#define PCI_BASE_ADDRESS_3 0x1c /* 32 bits */ -#define PCI_BASE_ADDRESS_4 0x20 /* 32 bits */ -#define PCI_BASE_ADDRESS_5 0x24 /* 32 bits */ -#define PCI_BASE_ADDRESS_SPACE 0x01 /* 0 = memory, 1 = I/O */ -#define PCI_BASE_ADDRESS_SPACE_IO 0x01 -#define PCI_BASE_ADDRESS_SPACE_MEMORY 0x00 -#define PCI_BASE_ADDRESS_MEM_TYPE_MASK 0x06 -#define PCI_BASE_ADDRESS_MEM_TYPE_32 0x00 /* 32 bit address */ -#define PCI_BASE_ADDRESS_MEM_TYPE_1M 0x02 /* Below 1M [obsolete] */ -#define PCI_BASE_ADDRESS_MEM_TYPE_64 0x04 /* 64 bit address */ -#define PCI_BASE_ADDRESS_MEM_PREFETCH 0x08 /* prefetchable? */ -#define PCI_BASE_ADDRESS_MEM_MASK (~0x0fUL) -#define PCI_BASE_ADDRESS_IO_MASK (~0x03UL) -/* bit 1 is reserved if address_space = 1 */ - -/* Header type 0 (normal devices) */ -#define PCI_CARDBUS_CIS 0x28 -#define PCI_SUBSYSTEM_VENDOR_ID 0x2c -#define PCI_SUBSYSTEM_ID 0x2e -#define PCI_ROM_ADDRESS 0x30 /* Bits 31..11 are address, 10..1 reserved */ -#define PCI_ROM_ADDRESS_ENABLE 0x01 -#define PCI_ROM_ADDRESS_MASK (~0x7ffUL) - -#define PCI_CAPABILITY_LIST 0x34 /* Offset of first capability list entry */ - -/* 0x35-0x3b are reserved */ -#define PCI_INTERRUPT_LINE 0x3c /* 8 bits */ -#define PCI_INTERRUPT_PIN 0x3d /* 8 bits */ -#define PCI_MIN_GNT 0x3e /* 8 bits */ -#define PCI_MAX_LAT 0x3f /* 8 bits */ - -/* Header type 1 (PCI-to-PCI bridges) */ -#define PCI_PRIMARY_BUS 0x18 /* Primary bus number */ -#define PCI_SECONDARY_BUS 0x19 /* Secondary bus number */ -#define PCI_SUBORDINATE_BUS 0x1a /* Highest bus number behind the bridge */ -#define PCI_SEC_LATENCY_TIMER 0x1b /* Latency timer for secondary interface */ -#define PCI_IO_BASE 0x1c /* I/O range behind the bridge */ -#define PCI_IO_LIMIT 0x1d -#define PCI_IO_RANGE_TYPE_MASK 0x0fUL /* I/O bridging type */ -#define PCI_IO_RANGE_TYPE_16 0x00 -#define PCI_IO_RANGE_TYPE_32 0x01 -#define PCI_IO_RANGE_MASK (~0x0fUL) -#define PCI_SEC_STATUS 0x1e /* Secondary status register, only bit 14 used */ -#define PCI_MEMORY_BASE 0x20 /* Memory range behind */ -#define PCI_MEMORY_LIMIT 0x22 -#define PCI_MEMORY_RANGE_TYPE_MASK 0x0fUL -#define PCI_MEMORY_RANGE_MASK (~0x0fUL) -#define PCI_PREF_MEMORY_BASE 0x24 /* Prefetchable memory range behind */ -#define PCI_PREF_MEMORY_LIMIT 0x26 -#define PCI_PREF_RANGE_TYPE_MASK 0x0fUL -#define PCI_PREF_RANGE_TYPE_32 0x00 -#define PCI_PREF_RANGE_TYPE_64 0x01 -#define PCI_PREF_RANGE_MASK (~0x0fUL) -#define PCI_PREF_BASE_UPPER32 0x28 /* Upper half of prefetchable memory range */ -#define PCI_PREF_LIMIT_UPPER32 0x2c -#define PCI_IO_BASE_UPPER16 0x30 /* Upper half of I/O addresses */ -#define PCI_IO_LIMIT_UPPER16 0x32 -/* 0x34 same as for htype 0 */ -/* 0x35-0x3b is reserved */ -#define PCI_ROM_ADDRESS1 0x38 /* Same as PCI_ROM_ADDRESS, but for htype 1 */ -/* 0x3c-0x3d are same as for htype 0 */ -#define PCI_BRIDGE_CONTROL 0x3e -#define PCI_BRIDGE_CTL_PARITY 0x01 /* Enable parity detection on secondary interface */ -#define PCI_BRIDGE_CTL_SERR 0x02 /* The same for SERR forwarding */ -#define PCI_BRIDGE_CTL_ISA 0x04 /* Enable ISA mode */ -#define PCI_BRIDGE_CTL_VGA 0x08 /* Forward VGA addresses */ -#define PCI_BRIDGE_CTL_MASTER_ABORT 0x20 /* Report master aborts */ -#define PCI_BRIDGE_CTL_BUS_RESET 0x40 /* Secondary bus reset */ -#define PCI_BRIDGE_CTL_FAST_BACK 0x80 /* Fast Back2Back enabled on secondary interface */ - -/* Header type 2 (CardBus bridges) */ -#define PCI_CB_CAPABILITY_LIST 0x14 -/* 0x15 reserved */ -#define PCI_CB_SEC_STATUS 0x16 /* Secondary status */ -#define PCI_CB_PRIMARY_BUS 0x18 /* PCI bus number */ -#define PCI_CB_CARD_BUS 0x19 /* CardBus bus number */ -#define PCI_CB_SUBORDINATE_BUS 0x1a /* Subordinate bus number */ -#define PCI_CB_LATENCY_TIMER 0x1b /* CardBus latency timer */ -#define PCI_CB_MEMORY_BASE_0 0x1c -#define PCI_CB_MEMORY_LIMIT_0 0x20 -#define PCI_CB_MEMORY_BASE_1 0x24 -#define PCI_CB_MEMORY_LIMIT_1 0x28 -#define PCI_CB_IO_BASE_0 0x2c -#define PCI_CB_IO_BASE_0_HI 0x2e -#define PCI_CB_IO_LIMIT_0 0x30 -#define PCI_CB_IO_LIMIT_0_HI 0x32 -#define PCI_CB_IO_BASE_1 0x34 -#define PCI_CB_IO_BASE_1_HI 0x36 -#define PCI_CB_IO_LIMIT_1 0x38 -#define PCI_CB_IO_LIMIT_1_HI 0x3a -#define PCI_CB_IO_RANGE_MASK (~0x03UL) -/* 0x3c-0x3d are same as for htype 0 */ -#define PCI_CB_BRIDGE_CONTROL 0x3e -#define PCI_CB_BRIDGE_CTL_PARITY 0x01 /* Similar to standard bridge control register */ -#define PCI_CB_BRIDGE_CTL_SERR 0x02 -#define PCI_CB_BRIDGE_CTL_ISA 0x04 -#define PCI_CB_BRIDGE_CTL_VGA 0x08 -#define PCI_CB_BRIDGE_CTL_MASTER_ABORT 0x20 -#define PCI_CB_BRIDGE_CTL_CB_RESET 0x40 /* CardBus reset */ -#define PCI_CB_BRIDGE_CTL_16BIT_INT 0x80 /* Enable interrupt for 16-bit cards */ -#define PCI_CB_BRIDGE_CTL_PREFETCH_MEM0 0x100 /* Prefetch enable for both memory regions */ -#define PCI_CB_BRIDGE_CTL_PREFETCH_MEM1 0x200 -#define PCI_CB_BRIDGE_CTL_POST_WRITES 0x400 -#define PCI_CB_SUBSYSTEM_VENDOR_ID 0x40 -#define PCI_CB_SUBSYSTEM_ID 0x42 -#define PCI_CB_LEGACY_MODE_BASE 0x44 /* 16-bit PC Card legacy mode base address (ExCa) */ -/* 0x48-0x7f reserved */ - -/* Capability lists */ - -#define PCI_CAP_LIST_ID 0 /* Capability ID */ -#define PCI_CAP_ID_PM 0x01 /* Power Management */ -#define PCI_CAP_ID_AGP 0x02 /* Accelerated Graphics Port */ -#define PCI_CAP_ID_VPD 0x03 /* Vital Product Data */ -#define PCI_CAP_ID_SLOTID 0x04 /* Slot Identification */ -#define PCI_CAP_ID_MSI 0x05 /* Message Signalled Interrupts */ -#define PCI_CAP_ID_CHSWP 0x06 /* CompactPCI HotSwap */ -#define PCI_CAP_ID_PCIX 0x07 /* PCI-X */ -#define PCI_CAP_ID_HT 0x08 /* HyperTransport */ -#define PCI_CAP_ID_VNDR 0x09 /* Vendor specific */ -#define PCI_CAP_ID_DBG 0x0A /* Debug port */ -#define PCI_CAP_ID_CCRC 0x0B /* CompactPCI Central Resource Control */ -#define PCI_CAP_ID_SHPC 0x0C /* PCI Standard Hot-Plug Controller */ -#define PCI_CAP_ID_SSVID 0x0D /* Bridge subsystem vendor/device ID */ -#define PCI_CAP_ID_AGP3 0x0E /* AGP Target PCI-PCI bridge */ -#define PCI_CAP_ID_EXP 0x10 /* PCI Express */ -#define PCI_CAP_ID_MSIX 0x11 /* MSI-X */ -#define PCI_CAP_ID_SATA 0x12 /* Serial ATA */ -#define PCI_CAP_ID_AF 0x13 /* PCI Advanced Features */ -#define PCI_CAP_LIST_NEXT 1 /* Next capability in the list */ -#define PCI_CAP_FLAGS 2 /* Capability defined flags (16 bits) */ -#define PCI_CAP_SIZEOF 4 - -/* Power Management Registers */ - -#define PCI_PM_PMC 2 /* PM Capabilities Register */ -#define PCI_PM_CAP_VER_MASK 0x0007 /* Version */ -#define PCI_PM_CAP_PME_CLOCK 0x0008 /* PME clock required */ -#define PCI_PM_CAP_RESERVED 0x0010 /* Reserved field */ -#define PCI_PM_CAP_DSI 0x0020 /* Device specific initialization */ -#define PCI_PM_CAP_AUX_POWER 0x01C0 /* Auxiliary power support mask */ -#define PCI_PM_CAP_D1 0x0200 /* D1 power state support */ -#define PCI_PM_CAP_D2 0x0400 /* D2 power state support */ -#define PCI_PM_CAP_PME 0x0800 /* PME pin supported */ -#define PCI_PM_CAP_PME_MASK 0xF800 /* PME Mask of all supported states */ -#define PCI_PM_CAP_PME_D0 0x0800 /* PME# from D0 */ -#define PCI_PM_CAP_PME_D1 0x1000 /* PME# from D1 */ -#define PCI_PM_CAP_PME_D2 0x2000 /* PME# from D2 */ -#define PCI_PM_CAP_PME_D3 0x4000 /* PME# from D3 (hot) */ -#define PCI_PM_CAP_PME_D3cold 0x8000 /* PME# from D3 (cold) */ -#define PCI_PM_CAP_PME_SHIFT 11 /* Start of the PME Mask in PMC */ -#define PCI_PM_CTRL 4 /* PM control and status register */ -#define PCI_PM_CTRL_STATE_MASK 0x0003 /* Current power state (D0 to D3) */ -#define PCI_PM_CTRL_NO_SOFT_RESET 0x0008 /* No reset for D3hot->D0 */ -#define PCI_PM_CTRL_PME_ENABLE 0x0100 /* PME pin enable */ -#define PCI_PM_CTRL_DATA_SEL_MASK 0x1e00 /* Data select (??) */ -#define PCI_PM_CTRL_DATA_SCALE_MASK 0x6000 /* Data scale (??) */ -#define PCI_PM_CTRL_PME_STATUS 0x8000 /* PME pin status */ -#define PCI_PM_PPB_EXTENSIONS 6 /* PPB support extensions (??) */ -#define PCI_PM_PPB_B2_B3 0x40 /* Stop clock when in D3hot (??) */ -#define PCI_PM_BPCC_ENABLE 0x80 /* Bus power/clock control enable (??) */ -#define PCI_PM_DATA_REGISTER 7 /* (??) */ -#define PCI_PM_SIZEOF 8 - -/* AGP registers */ - -#define PCI_AGP_VERSION 2 /* BCD version number */ -#define PCI_AGP_RFU 3 /* Rest of capability flags */ -#define PCI_AGP_STATUS 4 /* Status register */ -#define PCI_AGP_STATUS_RQ_MASK 0xff000000 /* Maximum number of requests - 1 */ -#define PCI_AGP_STATUS_SBA 0x0200 /* Sideband addressing supported */ -#define PCI_AGP_STATUS_64BIT 0x0020 /* 64-bit addressing supported */ -#define PCI_AGP_STATUS_FW 0x0010 /* FW transfers supported */ -#define PCI_AGP_STATUS_RATE4 0x0004 /* 4x transfer rate supported */ -#define PCI_AGP_STATUS_RATE2 0x0002 /* 2x transfer rate supported */ -#define PCI_AGP_STATUS_RATE1 0x0001 /* 1x transfer rate supported */ -#define PCI_AGP_COMMAND 8 /* Control register */ -#define PCI_AGP_COMMAND_RQ_MASK 0xff000000 /* Master: Maximum number of requests */ -#define PCI_AGP_COMMAND_SBA 0x0200 /* Sideband addressing enabled */ -#define PCI_AGP_COMMAND_AGP 0x0100 /* Allow processing of AGP transactions */ -#define PCI_AGP_COMMAND_64BIT 0x0020 /* Allow processing of 64-bit addresses */ -#define PCI_AGP_COMMAND_FW 0x0010 /* Force FW transfers */ -#define PCI_AGP_COMMAND_RATE4 0x0004 /* Use 4x rate */ -#define PCI_AGP_COMMAND_RATE2 0x0002 /* Use 2x rate */ -#define PCI_AGP_COMMAND_RATE1 0x0001 /* Use 1x rate */ -#define PCI_AGP_SIZEOF 12 - -/* Vital Product Data */ - -#define PCI_VPD_ADDR 2 /* Address to access (15 bits!) */ -#define PCI_VPD_ADDR_MASK 0x7fff /* Address mask */ -#define PCI_VPD_ADDR_F 0x8000 /* Write 0, 1 indicates completion */ -#define PCI_VPD_DATA 4 /* 32-bits of data returned here */ - -/* Slot Identification */ - -#define PCI_SID_ESR 2 /* Expansion Slot Register */ -#define PCI_SID_ESR_NSLOTS 0x1f /* Number of expansion slots available */ -#define PCI_SID_ESR_FIC 0x20 /* First In Chassis Flag */ -#define PCI_SID_CHASSIS_NR 3 /* Chassis Number */ - -/* Message Signalled Interrupts registers */ - -#define PCI_MSI_FLAGS 2 /* Various flags */ -#define PCI_MSI_FLAGS_64BIT 0x80 /* 64-bit addresses allowed */ -#define PCI_MSI_FLAGS_QSIZE 0x70 /* Message queue size configured */ -#define PCI_MSI_FLAGS_QMASK 0x0e /* Maximum queue size available */ -#define PCI_MSI_FLAGS_ENABLE 0x01 /* MSI feature enabled */ -#define PCI_MSI_FLAGS_MASKBIT 0x100 /* 64-bit mask bits allowed */ -#define PCI_MSI_RFU 3 /* Rest of capability flags */ -#define PCI_MSI_ADDRESS_LO 4 /* Lower 32 bits */ -#define PCI_MSI_ADDRESS_HI 8 /* Upper 32 bits (if PCI_MSI_FLAGS_64BIT set) */ -#define PCI_MSI_DATA_32 8 /* 16 bits of data for 32-bit devices */ -#define PCI_MSI_MASK_32 12 /* Mask bits register for 32-bit devices */ -#define PCI_MSI_DATA_64 12 /* 16 bits of data for 64-bit devices */ -#define PCI_MSI_MASK_64 16 /* Mask bits register for 64-bit devices */ - -/* MSI-X registers */ -#define PCI_MSIX_FLAGS 2 -#define PCI_MSIX_FLAGS_QSIZE 0x7FF -#define PCI_MSIX_FLAGS_ENABLE (1 << 15) -#define PCI_MSIX_FLAGS_MASKALL (1 << 14) -#define PCI_MSIX_TABLE 4 -#define PCI_MSIX_PBA 8 -#define PCI_MSIX_FLAGS_BIRMASK (7 << 0) - -/* MSI-X entry's format */ -#define PCI_MSIX_ENTRY_SIZE 16 -#define PCI_MSIX_ENTRY_LOWER_ADDR 0 -#define PCI_MSIX_ENTRY_UPPER_ADDR 4 -#define PCI_MSIX_ENTRY_DATA 8 -#define PCI_MSIX_ENTRY_VECTOR_CTRL 12 -#define PCI_MSIX_ENTRY_CTRL_MASKBIT 1 - -/* CompactPCI Hotswap Register */ - -#define PCI_CHSWP_CSR 2 /* Control and Status Register */ -#define PCI_CHSWP_DHA 0x01 /* Device Hiding Arm */ -#define PCI_CHSWP_EIM 0x02 /* ENUM# Signal Mask */ -#define PCI_CHSWP_PIE 0x04 /* Pending Insert or Extract */ -#define PCI_CHSWP_LOO 0x08 /* LED On / Off */ -#define PCI_CHSWP_PI 0x30 /* Programming Interface */ -#define PCI_CHSWP_EXT 0x40 /* ENUM# status - extraction */ -#define PCI_CHSWP_INS 0x80 /* ENUM# status - insertion */ - -/* PCI Advanced Feature registers */ - -#define PCI_AF_LENGTH 2 -#define PCI_AF_CAP 3 -#define PCI_AF_CAP_TP 0x01 -#define PCI_AF_CAP_FLR 0x02 -#define PCI_AF_CTRL 4 -#define PCI_AF_CTRL_FLR 0x01 -#define PCI_AF_STATUS 5 -#define PCI_AF_STATUS_TP 0x01 - -/* PCI-X registers */ - -#define PCI_X_CMD 2 /* Modes & Features */ -#define PCI_X_CMD_DPERR_E 0x0001 /* Data Parity Error Recovery Enable */ -#define PCI_X_CMD_ERO 0x0002 /* Enable Relaxed Ordering */ -#define PCI_X_CMD_READ_512 0x0000 /* 512 byte maximum read byte count */ -#define PCI_X_CMD_READ_1K 0x0004 /* 1Kbyte maximum read byte count */ -#define PCI_X_CMD_READ_2K 0x0008 /* 2Kbyte maximum read byte count */ -#define PCI_X_CMD_READ_4K 0x000c /* 4Kbyte maximum read byte count */ -#define PCI_X_CMD_MAX_READ 0x000c /* Max Memory Read Byte Count */ - /* Max # of outstanding split transactions */ -#define PCI_X_CMD_SPLIT_1 0x0000 /* Max 1 */ -#define PCI_X_CMD_SPLIT_2 0x0010 /* Max 2 */ -#define PCI_X_CMD_SPLIT_3 0x0020 /* Max 3 */ -#define PCI_X_CMD_SPLIT_4 0x0030 /* Max 4 */ -#define PCI_X_CMD_SPLIT_8 0x0040 /* Max 8 */ -#define PCI_X_CMD_SPLIT_12 0x0050 /* Max 12 */ -#define PCI_X_CMD_SPLIT_16 0x0060 /* Max 16 */ -#define PCI_X_CMD_SPLIT_32 0x0070 /* Max 32 */ -#define PCI_X_CMD_MAX_SPLIT 0x0070 /* Max Outstanding Split Transactions */ -#define PCI_X_CMD_VERSION(x) (((x) >> 12) & 3) /* Version */ -#define PCI_X_STATUS 4 /* PCI-X capabilities */ -#define PCI_X_STATUS_DEVFN 0x000000ff /* A copy of devfn */ -#define PCI_X_STATUS_BUS 0x0000ff00 /* A copy of bus nr */ -#define PCI_X_STATUS_64BIT 0x00010000 /* 64-bit device */ -#define PCI_X_STATUS_133MHZ 0x00020000 /* 133 MHz capable */ -#define PCI_X_STATUS_SPL_DISC 0x00040000 /* Split Completion Discarded */ -#define PCI_X_STATUS_UNX_SPL 0x00080000 /* Unexpected Split Completion */ -#define PCI_X_STATUS_COMPLEX 0x00100000 /* Device Complexity */ -#define PCI_X_STATUS_MAX_READ 0x00600000 /* Designed Max Memory Read Count */ -#define PCI_X_STATUS_MAX_SPLIT 0x03800000 /* Designed Max Outstanding Split Transactions */ -#define PCI_X_STATUS_MAX_CUM 0x1c000000 /* Designed Max Cumulative Read Size */ -#define PCI_X_STATUS_SPL_ERR 0x20000000 /* Rcvd Split Completion Error Msg */ -#define PCI_X_STATUS_266MHZ 0x40000000 /* 266 MHz capable */ -#define PCI_X_STATUS_533MHZ 0x80000000 /* 533 MHz capable */ - -/* PCI Bridge Subsystem ID registers */ - -#define PCI_SSVID_VENDOR_ID 4 /* PCI-Bridge subsystem vendor id register */ -#define PCI_SSVID_DEVICE_ID 6 /* PCI-Bridge subsystem device id register */ - -/* PCI Express capability registers */ - -#define PCI_EXP_FLAGS 2 /* Capabilities register */ -#define PCI_EXP_FLAGS_VERS 0x000f /* Capability version */ -#define PCI_EXP_FLAGS_TYPE 0x00f0 /* Device/Port type */ -#define PCI_EXP_TYPE_ENDPOINT 0x0 /* Express Endpoint */ -#define PCI_EXP_TYPE_LEG_END 0x1 /* Legacy Endpoint */ -#define PCI_EXP_TYPE_ROOT_PORT 0x4 /* Root Port */ -#define PCI_EXP_TYPE_UPSTREAM 0x5 /* Upstream Port */ -#define PCI_EXP_TYPE_DOWNSTREAM 0x6 /* Downstream Port */ -#define PCI_EXP_TYPE_PCI_BRIDGE 0x7 /* PCI/PCI-X Bridge */ -#define PCI_EXP_TYPE_PCIE_BRIDGE 0x8 /* PCI/PCI-X to PCIE Bridge */ -#define PCI_EXP_TYPE_RC_END 0x9 /* Root Complex Integrated Endpoint */ -#define PCI_EXP_TYPE_RC_EC 0xa /* Root Complex Event Collector */ -#define PCI_EXP_FLAGS_SLOT 0x0100 /* Slot implemented */ -#define PCI_EXP_FLAGS_IRQ 0x3e00 /* Interrupt message number */ -#define PCI_EXP_DEVCAP 4 /* Device capabilities */ -#define PCI_EXP_DEVCAP_PAYLOAD 0x07 /* Max_Payload_Size */ -#define PCI_EXP_DEVCAP_PHANTOM 0x18 /* Phantom functions */ -#define PCI_EXP_DEVCAP_EXT_TAG 0x20 /* Extended tags */ -#define PCI_EXP_DEVCAP_L0S 0x1c0 /* L0s Acceptable Latency */ -#define PCI_EXP_DEVCAP_L1 0xe00 /* L1 Acceptable Latency */ -#define PCI_EXP_DEVCAP_ATN_BUT 0x1000 /* Attention Button Present */ -#define PCI_EXP_DEVCAP_ATN_IND 0x2000 /* Attention Indicator Present */ -#define PCI_EXP_DEVCAP_PWR_IND 0x4000 /* Power Indicator Present */ -#define PCI_EXP_DEVCAP_RBER 0x8000 /* Role-Based Error Reporting */ -#define PCI_EXP_DEVCAP_PWR_VAL 0x3fc0000 /* Slot Power Limit Value */ -#define PCI_EXP_DEVCAP_PWR_SCL 0xc000000 /* Slot Power Limit Scale */ -#define PCI_EXP_DEVCAP_FLR 0x10000000 /* Function Level Reset */ -#define PCI_EXP_DEVCTL 8 /* Device Control */ -#define PCI_EXP_DEVCTL_CERE 0x0001 /* Correctable Error Reporting En. */ -#define PCI_EXP_DEVCTL_NFERE 0x0002 /* Non-Fatal Error Reporting Enable */ -#define PCI_EXP_DEVCTL_FERE 0x0004 /* Fatal Error Reporting Enable */ -#define PCI_EXP_DEVCTL_URRE 0x0008 /* Unsupported Request Reporting En. */ -#define PCI_EXP_DEVCTL_RELAX_EN 0x0010 /* Enable relaxed ordering */ -#define PCI_EXP_DEVCTL_PAYLOAD 0x00e0 /* Max_Payload_Size */ -#define PCI_EXP_DEVCTL_EXT_TAG 0x0100 /* Extended Tag Field Enable */ -#define PCI_EXP_DEVCTL_PHANTOM 0x0200 /* Phantom Functions Enable */ -#define PCI_EXP_DEVCTL_AUX_PME 0x0400 /* Auxiliary Power PM Enable */ -#define PCI_EXP_DEVCTL_NOSNOOP_EN 0x0800 /* Enable No Snoop */ -#define PCI_EXP_DEVCTL_READRQ 0x7000 /* Max_Read_Request_Size */ -#define PCI_EXP_DEVCTL_BCR_FLR 0x8000 /* Bridge Configuration Retry / FLR */ -#define PCI_EXP_DEVSTA 10 /* Device Status */ -#define PCI_EXP_DEVSTA_CED 0x01 /* Correctable Error Detected */ -#define PCI_EXP_DEVSTA_NFED 0x02 /* Non-Fatal Error Detected */ -#define PCI_EXP_DEVSTA_FED 0x04 /* Fatal Error Detected */ -#define PCI_EXP_DEVSTA_URD 0x08 /* Unsupported Request Detected */ -#define PCI_EXP_DEVSTA_AUXPD 0x10 /* AUX Power Detected */ -#define PCI_EXP_DEVSTA_TRPND 0x20 /* Transactions Pending */ -#define PCI_EXP_LNKCAP 12 /* Link Capabilities */ -#define PCI_EXP_LNKCAP_SLS 0x0000000f /* Supported Link Speeds */ -#define PCI_EXP_LNKCAP_MLW 0x000003f0 /* Maximum Link Width */ -#define PCI_EXP_LNKCAP_ASPMS 0x00000c00 /* ASPM Support */ -#define PCI_EXP_LNKCAP_L0SEL 0x00007000 /* L0s Exit Latency */ -#define PCI_EXP_LNKCAP_L1EL 0x00038000 /* L1 Exit Latency */ -#define PCI_EXP_LNKCAP_CLKPM 0x00040000 /* L1 Clock Power Management */ -#define PCI_EXP_LNKCAP_SDERC 0x00080000 /* Surprise Down Error Reporting Capable */ -#define PCI_EXP_LNKCAP_DLLLARC 0x00100000 /* Data Link Layer Link Active Reporting Capable */ -#define PCI_EXP_LNKCAP_LBNC 0x00200000 /* Link Bandwidth Notification Capability */ -#define PCI_EXP_LNKCAP_PN 0xff000000 /* Port Number */ -#define PCI_EXP_LNKCTL 16 /* Link Control */ -#define PCI_EXP_LNKCTL_ASPMC 0x0003 /* ASPM Control */ -#define PCI_EXP_LNKCTL_RCB 0x0008 /* Read Completion Boundary */ -#define PCI_EXP_LNKCTL_LD 0x0010 /* Link Disable */ -#define PCI_EXP_LNKCTL_RL 0x0020 /* Retrain Link */ -#define PCI_EXP_LNKCTL_CCC 0x0040 /* Common Clock Configuration */ -#define PCI_EXP_LNKCTL_ES 0x0080 /* Extended Synch */ -#define PCI_EXP_LNKCTL_CLKREQ_EN 0x100 /* Enable clkreq */ -#define PCI_EXP_LNKCTL_HAWD 0x0200 /* Hardware Autonomous Width Disable */ -#define PCI_EXP_LNKCTL_LBMIE 0x0400 /* Link Bandwidth Management Interrupt Enable */ -#define PCI_EXP_LNKCTL_LABIE 0x0800 /* Lnk Autonomous Bandwidth Interrupt Enable */ -#define PCI_EXP_LNKSTA 18 /* Link Status */ -#define PCI_EXP_LNKSTA_CLS 0x000f /* Current Link Speed */ -#define PCI_EXP_LNKSTA_CLS_2_5GB 0x01 /* Current Link Speed 2.5GT/s */ -#define PCI_EXP_LNKSTA_CLS_5_0GB 0x02 /* Current Link Speed 5.0GT/s */ -#define PCI_EXP_LNKSTA_NLW 0x03f0 /* Nogotiated Link Width */ -#define PCI_EXP_LNKSTA_NLW_SHIFT 4 /* start of NLW mask in link status */ -#define PCI_EXP_LNKSTA_LT 0x0800 /* Link Training */ -#define PCI_EXP_LNKSTA_SLC 0x1000 /* Slot Clock Configuration */ -#define PCI_EXP_LNKSTA_DLLLA 0x2000 /* Data Link Layer Link Active */ -#define PCI_EXP_LNKSTA_LBMS 0x4000 /* Link Bandwidth Management Status */ -#define PCI_EXP_LNKSTA_LABS 0x8000 /* Link Autonomous Bandwidth Status */ -#define PCI_EXP_SLTCAP 20 /* Slot Capabilities */ -#define PCI_EXP_SLTCAP_ABP 0x00000001 /* Attention Button Present */ -#define PCI_EXP_SLTCAP_PCP 0x00000002 /* Power Controller Present */ -#define PCI_EXP_SLTCAP_MRLSP 0x00000004 /* MRL Sensor Present */ -#define PCI_EXP_SLTCAP_AIP 0x00000008 /* Attention Indicator Present */ -#define PCI_EXP_SLTCAP_PIP 0x00000010 /* Power Indicator Present */ -#define PCI_EXP_SLTCAP_HPS 0x00000020 /* Hot-Plug Surprise */ -#define PCI_EXP_SLTCAP_HPC 0x00000040 /* Hot-Plug Capable */ -#define PCI_EXP_SLTCAP_SPLV 0x00007f80 /* Slot Power Limit Value */ -#define PCI_EXP_SLTCAP_SPLS 0x00018000 /* Slot Power Limit Scale */ -#define PCI_EXP_SLTCAP_EIP 0x00020000 /* Electromechanical Interlock Present */ -#define PCI_EXP_SLTCAP_NCCS 0x00040000 /* No Command Completed Support */ -#define PCI_EXP_SLTCAP_PSN 0xfff80000 /* Physical Slot Number */ -#define PCI_EXP_SLTCTL 24 /* Slot Control */ -#define PCI_EXP_SLTCTL_ABPE 0x0001 /* Attention Button Pressed Enable */ -#define PCI_EXP_SLTCTL_PFDE 0x0002 /* Power Fault Detected Enable */ -#define PCI_EXP_SLTCTL_MRLSCE 0x0004 /* MRL Sensor Changed Enable */ -#define PCI_EXP_SLTCTL_PDCE 0x0008 /* Presence Detect Changed Enable */ -#define PCI_EXP_SLTCTL_CCIE 0x0010 /* Command Completed Interrupt Enable */ -#define PCI_EXP_SLTCTL_HPIE 0x0020 /* Hot-Plug Interrupt Enable */ -#define PCI_EXP_SLTCTL_AIC 0x00c0 /* Attention Indicator Control */ -#define PCI_EXP_SLTCTL_PIC 0x0300 /* Power Indicator Control */ -#define PCI_EXP_SLTCTL_PCC 0x0400 /* Power Controller Control */ -#define PCI_EXP_SLTCTL_EIC 0x0800 /* Electromechanical Interlock Control */ -#define PCI_EXP_SLTCTL_DLLSCE 0x1000 /* Data Link Layer State Changed Enable */ -#define PCI_EXP_SLTSTA 26 /* Slot Status */ -#define PCI_EXP_SLTSTA_ABP 0x0001 /* Attention Button Pressed */ -#define PCI_EXP_SLTSTA_PFD 0x0002 /* Power Fault Detected */ -#define PCI_EXP_SLTSTA_MRLSC 0x0004 /* MRL Sensor Changed */ -#define PCI_EXP_SLTSTA_PDC 0x0008 /* Presence Detect Changed */ -#define PCI_EXP_SLTSTA_CC 0x0010 /* Command Completed */ -#define PCI_EXP_SLTSTA_MRLSS 0x0020 /* MRL Sensor State */ -#define PCI_EXP_SLTSTA_PDS 0x0040 /* Presence Detect State */ -#define PCI_EXP_SLTSTA_EIS 0x0080 /* Electromechanical Interlock Status */ -#define PCI_EXP_SLTSTA_DLLSC 0x0100 /* Data Link Layer State Changed */ -#define PCI_EXP_RTCTL 28 /* Root Control */ -#define PCI_EXP_RTCTL_SECEE 0x01 /* System Error on Correctable Error */ -#define PCI_EXP_RTCTL_SENFEE 0x02 /* System Error on Non-Fatal Error */ -#define PCI_EXP_RTCTL_SEFEE 0x04 /* System Error on Fatal Error */ -#define PCI_EXP_RTCTL_PMEIE 0x08 /* PME Interrupt Enable */ -#define PCI_EXP_RTCTL_CRSSVE 0x10 /* CRS Software Visibility Enable */ -#define PCI_EXP_RTCAP 30 /* Root Capabilities */ -#define PCI_EXP_RTSTA 32 /* Root Status */ -#define PCI_EXP_RTSTA_PME 0x10000 /* PME status */ -#define PCI_EXP_RTSTA_PENDING 0x20000 /* PME pending */ -#define PCI_EXP_DEVCAP2 36 /* Device Capabilities 2 */ -#define PCI_EXP_DEVCAP2_ARI 0x20 /* Alternative Routing-ID */ -#define PCI_EXP_DEVCAP2_LTR 0x800 /* Latency tolerance reporting */ -#define PCI_EXP_OBFF_MASK 0xc0000 /* OBFF support mechanism */ -#define PCI_EXP_OBFF_MSG 0x40000 /* New message signaling */ -#define PCI_EXP_OBFF_WAKE 0x80000 /* Re-use WAKE# for OBFF */ -#define PCI_EXP_DEVCTL2 40 /* Device Control 2 */ -#define PCI_EXP_DEVCTL2_ARI 0x20 /* Alternative Routing-ID */ -#define PCI_EXP_IDO_REQ_EN 0x100 /* ID-based ordering request enable */ -#define PCI_EXP_IDO_CMP_EN 0x200 /* ID-based ordering completion enable */ -#define PCI_EXP_LTR_EN 0x400 /* Latency tolerance reporting */ -#define PCI_EXP_OBFF_MSGA_EN 0x2000 /* OBFF enable with Message type A */ -#define PCI_EXP_OBFF_MSGB_EN 0x4000 /* OBFF enable with Message type B */ -#define PCI_EXP_OBFF_WAKE_EN 0x6000 /* OBFF using WAKE# signaling */ -#define PCI_EXP_LNKCTL2 48 /* Link Control 2 */ -#define PCI_EXP_SLTCTL2 56 /* Slot Control 2 */ - -/* Extended Capabilities (PCI-X 2.0 and Express) */ -#define PCI_EXT_CAP_ID(header) (header & 0x0000ffff) -#define PCI_EXT_CAP_VER(header) ((header >> 16) & 0xf) -#define PCI_EXT_CAP_NEXT(header) ((header >> 20) & 0xffc) - -#define PCI_EXT_CAP_ID_ERR 1 -#define PCI_EXT_CAP_ID_VC 2 -#define PCI_EXT_CAP_ID_DSN 3 -#define PCI_EXT_CAP_ID_PWR 4 -#define PCI_EXT_CAP_ID_VNDR 11 -#define PCI_EXT_CAP_ID_ACS 13 -#define PCI_EXT_CAP_ID_ARI 14 -#define PCI_EXT_CAP_ID_ATS 15 -#define PCI_EXT_CAP_ID_SRIOV 16 -#define PCI_EXT_CAP_ID_LTR 24 - -/* Advanced Error Reporting */ -#define PCI_ERR_UNCOR_STATUS 4 /* Uncorrectable Error Status */ -#define PCI_ERR_UNC_TRAIN 0x00000001 /* Training */ -#define PCI_ERR_UNC_DLP 0x00000010 /* Data Link Protocol */ -#define PCI_ERR_UNC_POISON_TLP 0x00001000 /* Poisoned TLP */ -#define PCI_ERR_UNC_FCP 0x00002000 /* Flow Control Protocol */ -#define PCI_ERR_UNC_COMP_TIME 0x00004000 /* Completion Timeout */ -#define PCI_ERR_UNC_COMP_ABORT 0x00008000 /* Completer Abort */ -#define PCI_ERR_UNC_UNX_COMP 0x00010000 /* Unexpected Completion */ -#define PCI_ERR_UNC_RX_OVER 0x00020000 /* Receiver Overflow */ -#define PCI_ERR_UNC_MALF_TLP 0x00040000 /* Malformed TLP */ -#define PCI_ERR_UNC_ECRC 0x00080000 /* ECRC Error Status */ -#define PCI_ERR_UNC_UNSUP 0x00100000 /* Unsupported Request */ -#define PCI_ERR_UNCOR_MASK 8 /* Uncorrectable Error Mask */ - /* Same bits as above */ -#define PCI_ERR_UNCOR_SEVER 12 /* Uncorrectable Error Severity */ - /* Same bits as above */ -#define PCI_ERR_COR_STATUS 16 /* Correctable Error Status */ -#define PCI_ERR_COR_RCVR 0x00000001 /* Receiver Error Status */ -#define PCI_ERR_COR_BAD_TLP 0x00000040 /* Bad TLP Status */ -#define PCI_ERR_COR_BAD_DLLP 0x00000080 /* Bad DLLP Status */ -#define PCI_ERR_COR_REP_ROLL 0x00000100 /* REPLAY_NUM Rollover */ -#define PCI_ERR_COR_REP_TIMER 0x00001000 /* Replay Timer Timeout */ -#define PCI_ERR_COR_MASK 20 /* Correctable Error Mask */ - /* Same bits as above */ -#define PCI_ERR_CAP 24 /* Advanced Error Capabilities */ -#define PCI_ERR_CAP_FEP(x) ((x) & 31) /* First Error Pointer */ -#define PCI_ERR_CAP_ECRC_GENC 0x00000020 /* ECRC Generation Capable */ -#define PCI_ERR_CAP_ECRC_GENE 0x00000040 /* ECRC Generation Enable */ -#define PCI_ERR_CAP_ECRC_CHKC 0x00000080 /* ECRC Check Capable */ -#define PCI_ERR_CAP_ECRC_CHKE 0x00000100 /* ECRC Check Enable */ -#define PCI_ERR_HEADER_LOG 28 /* Header Log Register (16 bytes) */ -#define PCI_ERR_ROOT_COMMAND 44 /* Root Error Command */ -/* Correctable Err Reporting Enable */ -#define PCI_ERR_ROOT_CMD_COR_EN 0x00000001 -/* Non-fatal Err Reporting Enable */ -#define PCI_ERR_ROOT_CMD_NONFATAL_EN 0x00000002 -/* Fatal Err Reporting Enable */ -#define PCI_ERR_ROOT_CMD_FATAL_EN 0x00000004 -#define PCI_ERR_ROOT_STATUS 48 -#define PCI_ERR_ROOT_COR_RCV 0x00000001 /* ERR_COR Received */ -/* Multi ERR_COR Received */ -#define PCI_ERR_ROOT_MULTI_COR_RCV 0x00000002 -/* ERR_FATAL/NONFATAL Recevied */ -#define PCI_ERR_ROOT_UNCOR_RCV 0x00000004 -/* Multi ERR_FATAL/NONFATAL Recevied */ -#define PCI_ERR_ROOT_MULTI_UNCOR_RCV 0x00000008 -#define PCI_ERR_ROOT_FIRST_FATAL 0x00000010 /* First Fatal */ -#define PCI_ERR_ROOT_NONFATAL_RCV 0x00000020 /* Non-Fatal Received */ -#define PCI_ERR_ROOT_FATAL_RCV 0x00000040 /* Fatal Received */ -#define PCI_ERR_ROOT_ERR_SRC 52 /* Error Source Identification */ - -/* Virtual Channel */ -#define PCI_VC_PORT_REG1 4 -#define PCI_VC_PORT_REG2 8 -#define PCI_VC_PORT_CTRL 12 -#define PCI_VC_PORT_STATUS 14 -#define PCI_VC_RES_CAP 16 -#define PCI_VC_RES_CTRL 20 -#define PCI_VC_RES_STATUS 26 - -/* Power Budgeting */ -#define PCI_PWR_DSR 4 /* Data Select Register */ -#define PCI_PWR_DATA 8 /* Data Register */ -#define PCI_PWR_DATA_BASE(x) ((x) & 0xff) /* Base Power */ -#define PCI_PWR_DATA_SCALE(x) (((x) >> 8) & 3) /* Data Scale */ -#define PCI_PWR_DATA_PM_SUB(x) (((x) >> 10) & 7) /* PM Sub State */ -#define PCI_PWR_DATA_PM_STATE(x) (((x) >> 13) & 3) /* PM State */ -#define PCI_PWR_DATA_TYPE(x) (((x) >> 15) & 7) /* Type */ -#define PCI_PWR_DATA_RAIL(x) (((x) >> 18) & 7) /* Power Rail */ -#define PCI_PWR_CAP 12 /* Capability */ -#define PCI_PWR_CAP_BUDGET(x) ((x) & 1) /* Included in system budget */ - -/* - * Hypertransport sub capability types - * - * Unfortunately there are both 3 bit and 5 bit capability types defined - * in the HT spec, catering for that is a little messy. You probably don't - * want to use these directly, just use pci_find_ht_capability() and it - * will do the right thing for you. - */ -#define HT_3BIT_CAP_MASK 0xE0 -#define HT_CAPTYPE_SLAVE 0x00 /* Slave/Primary link configuration */ -#define HT_CAPTYPE_HOST 0x20 /* Host/Secondary link configuration */ - -#define HT_5BIT_CAP_MASK 0xF8 -#define HT_CAPTYPE_IRQ 0x80 /* IRQ Configuration */ -#define HT_CAPTYPE_REMAPPING_40 0xA0 /* 40 bit address remapping */ -#define HT_CAPTYPE_REMAPPING_64 0xA2 /* 64 bit address remapping */ -#define HT_CAPTYPE_UNITID_CLUMP 0x90 /* Unit ID clumping */ -#define HT_CAPTYPE_EXTCONF 0x98 /* Extended Configuration Space Access */ -#define HT_CAPTYPE_MSI_MAPPING 0xA8 /* MSI Mapping Capability */ -#define HT_MSI_FLAGS 0x02 /* Offset to flags */ -#define HT_MSI_FLAGS_ENABLE 0x1 /* Mapping enable */ -#define HT_MSI_FLAGS_FIXED 0x2 /* Fixed mapping only */ -#define HT_MSI_FIXED_ADDR 0x00000000FEE00000ULL /* Fixed addr */ -#define HT_MSI_ADDR_LO 0x04 /* Offset to low addr bits */ -#define HT_MSI_ADDR_LO_MASK 0xFFF00000 /* Low address bit mask */ -#define HT_MSI_ADDR_HI 0x08 /* Offset to high addr bits */ -#define HT_CAPTYPE_DIRECT_ROUTE 0xB0 /* Direct routing configuration */ -#define HT_CAPTYPE_VCSET 0xB8 /* Virtual Channel configuration */ -#define HT_CAPTYPE_ERROR_RETRY 0xC0 /* Retry on error configuration */ -#define HT_CAPTYPE_GEN3 0xD0 /* Generation 3 hypertransport configuration */ -#define HT_CAPTYPE_PM 0xE0 /* Hypertransport powermanagement configuration */ - -/* Alternative Routing-ID Interpretation */ -#define PCI_ARI_CAP 0x04 /* ARI Capability Register */ -#define PCI_ARI_CAP_MFVC 0x0001 /* MFVC Function Groups Capability */ -#define PCI_ARI_CAP_ACS 0x0002 /* ACS Function Groups Capability */ -#define PCI_ARI_CAP_NFN(x) (((x) >> 8) & 0xff) /* Next Function Number */ -#define PCI_ARI_CTRL 0x06 /* ARI Control Register */ -#define PCI_ARI_CTRL_MFVC 0x0001 /* MFVC Function Groups Enable */ -#define PCI_ARI_CTRL_ACS 0x0002 /* ACS Function Groups Enable */ -#define PCI_ARI_CTRL_FG(x) (((x) >> 4) & 7) /* Function Group */ - -/* Address Translation Service */ -#define PCI_ATS_CAP 0x04 /* ATS Capability Register */ -#define PCI_ATS_CAP_QDEP(x) ((x) & 0x1f) /* Invalidate Queue Depth */ -#define PCI_ATS_MAX_QDEP 32 /* Max Invalidate Queue Depth */ -#define PCI_ATS_CTRL 0x06 /* ATS Control Register */ -#define PCI_ATS_CTRL_ENABLE 0x8000 /* ATS Enable */ -#define PCI_ATS_CTRL_STU(x) ((x) & 0x1f) /* Smallest Translation Unit */ -#define PCI_ATS_MIN_STU 12 /* shift of minimum STU block */ - -/* Single Root I/O Virtualization */ -#define PCI_SRIOV_CAP 0x04 /* SR-IOV Capabilities */ -#define PCI_SRIOV_CAP_VFM 0x01 /* VF Migration Capable */ -#define PCI_SRIOV_CAP_INTR(x) ((x) >> 21) /* Interrupt Message Number */ -#define PCI_SRIOV_CTRL 0x08 /* SR-IOV Control */ -#define PCI_SRIOV_CTRL_VFE 0x01 /* VF Enable */ -#define PCI_SRIOV_CTRL_VFM 0x02 /* VF Migration Enable */ -#define PCI_SRIOV_CTRL_INTR 0x04 /* VF Migration Interrupt Enable */ -#define PCI_SRIOV_CTRL_MSE 0x08 /* VF Memory Space Enable */ -#define PCI_SRIOV_CTRL_ARI 0x10 /* ARI Capable Hierarchy */ -#define PCI_SRIOV_STATUS 0x0a /* SR-IOV Status */ -#define PCI_SRIOV_STATUS_VFM 0x01 /* VF Migration Status */ -#define PCI_SRIOV_INITIAL_VF 0x0c /* Initial VFs */ -#define PCI_SRIOV_TOTAL_VF 0x0e /* Total VFs */ -#define PCI_SRIOV_NUM_VF 0x10 /* Number of VFs */ -#define PCI_SRIOV_FUNC_LINK 0x12 /* Function Dependency Link */ -#define PCI_SRIOV_VF_OFFSET 0x14 /* First VF Offset */ -#define PCI_SRIOV_VF_STRIDE 0x16 /* Following VF Stride */ -#define PCI_SRIOV_VF_DID 0x1a /* VF Device ID */ -#define PCI_SRIOV_SUP_PGSIZE 0x1c /* Supported Page Sizes */ -#define PCI_SRIOV_SYS_PGSIZE 0x20 /* System Page Size */ -#define PCI_SRIOV_BAR 0x24 /* VF BAR0 */ -#define PCI_SRIOV_NUM_BARS 6 /* Number of VF BARs */ -#define PCI_SRIOV_VFM 0x3c /* VF Migration State Array Offset*/ -#define PCI_SRIOV_VFM_BIR(x) ((x) & 7) /* State BIR */ -#define PCI_SRIOV_VFM_OFFSET(x) ((x) & ~7) /* State Offset */ -#define PCI_SRIOV_VFM_UA 0x0 /* Inactive.Unavailable */ -#define PCI_SRIOV_VFM_MI 0x1 /* Dormant.MigrateIn */ -#define PCI_SRIOV_VFM_MO 0x2 /* Active.MigrateOut */ -#define PCI_SRIOV_VFM_AV 0x3 /* Active.Available */ - -#define PCI_LTR_MAX_SNOOP_LAT 0x4 -#define PCI_LTR_MAX_NOSNOOP_LAT 0x6 -#define PCI_LTR_VALUE_MASK 0x000003ff -#define PCI_LTR_SCALE_MASK 0x00001c00 -#define PCI_LTR_SCALE_SHIFT 10 - -/* Access Control Service */ -#define PCI_ACS_CAP 0x04 /* ACS Capability Register */ -#define PCI_ACS_SV 0x01 /* Source Validation */ -#define PCI_ACS_TB 0x02 /* Translation Blocking */ -#define PCI_ACS_RR 0x04 /* P2P Request Redirect */ -#define PCI_ACS_CR 0x08 /* P2P Completion Redirect */ -#define PCI_ACS_UF 0x10 /* Upstream Forwarding */ -#define PCI_ACS_EC 0x20 /* P2P Egress Control */ -#define PCI_ACS_DT 0x40 /* Direct Translated P2P */ -#define PCI_ACS_CTRL 0x06 /* ACS Control Register */ -#define PCI_ACS_EGRESS_CTL_V 0x08 /* ACS Egress Control Vector */ - -#endif /* LINUX_PCI_REGS_H */ diff --git a/hw/pcie.c b/hw/pcie.c deleted file mode 100644 index 7c92f193e7..0000000000 --- a/hw/pcie.c +++ /dev/null @@ -1,555 +0,0 @@ -/* - * pcie.c - * - * Copyright (c) 2010 Isaku Yamahata - * VA Linux Systems Japan K.K. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu-common.h" -#include "pci_bridge.h" -#include "pcie.h" -#include "msix.h" -#include "msi.h" -#include "pci_internals.h" -#include "pcie_regs.h" -#include "range.h" - -//#define DEBUG_PCIE -#ifdef DEBUG_PCIE -# define PCIE_DPRINTF(fmt, ...) \ - fprintf(stderr, "%s:%d " fmt, __func__, __LINE__, ## __VA_ARGS__) -#else -# define PCIE_DPRINTF(fmt, ...) do {} while (0) -#endif -#define PCIE_DEV_PRINTF(dev, fmt, ...) \ - PCIE_DPRINTF("%s:%x "fmt, (dev)->name, (dev)->devfn, ## __VA_ARGS__) - - -/*************************************************************************** - * pci express capability helper functions - */ -int pcie_cap_init(PCIDevice *dev, uint8_t offset, uint8_t type, uint8_t port) -{ - int pos; - uint8_t *exp_cap; - - assert(pci_is_express(dev)); - - pos = pci_add_capability(dev, PCI_CAP_ID_EXP, offset, - PCI_EXP_VER2_SIZEOF); - if (pos < 0) { - return pos; - } - dev->exp.exp_cap = pos; - exp_cap = dev->config + pos; - - /* capability register - interrupt message number defaults to 0 */ - pci_set_word(exp_cap + PCI_EXP_FLAGS, - ((type << PCI_EXP_FLAGS_TYPE_SHIFT) & PCI_EXP_FLAGS_TYPE) | - PCI_EXP_FLAGS_VER2); - - /* device capability register - * table 7-12: - * roll based error reporting bit must be set by all - * Functions conforming to the ECN, PCI Express Base - * Specification, Revision 1.1., or subsequent PCI Express Base - * Specification revisions. - */ - pci_set_long(exp_cap + PCI_EXP_DEVCAP, PCI_EXP_DEVCAP_RBER); - - pci_set_long(exp_cap + PCI_EXP_LNKCAP, - (port << PCI_EXP_LNKCAP_PN_SHIFT) | - PCI_EXP_LNKCAP_ASPMS_0S | - PCI_EXP_LNK_MLW_1 | - PCI_EXP_LNK_LS_25); - - pci_set_word(exp_cap + PCI_EXP_LNKSTA, - PCI_EXP_LNK_MLW_1 | PCI_EXP_LNK_LS_25); - - pci_set_long(exp_cap + PCI_EXP_DEVCAP2, - PCI_EXP_DEVCAP2_EFF | PCI_EXP_DEVCAP2_EETLPP); - - pci_set_word(dev->wmask + pos, PCI_EXP_DEVCTL2_EETLPPB); - return pos; -} - -void pcie_cap_exit(PCIDevice *dev) -{ - pci_del_capability(dev, PCI_CAP_ID_EXP, PCI_EXP_VER2_SIZEOF); -} - -uint8_t pcie_cap_get_type(const PCIDevice *dev) -{ - uint32_t pos = dev->exp.exp_cap; - assert(pos > 0); - return (pci_get_word(dev->config + pos + PCI_EXP_FLAGS) & - PCI_EXP_FLAGS_TYPE) >> PCI_EXP_FLAGS_TYPE_SHIFT; -} - -/* MSI/MSI-X */ -/* pci express interrupt message number */ -/* 7.8.2 PCI Express Capabilities Register: Interrupt Message Number */ -void pcie_cap_flags_set_vector(PCIDevice *dev, uint8_t vector) -{ - uint8_t *exp_cap = dev->config + dev->exp.exp_cap; - assert(vector < 32); - pci_word_test_and_clear_mask(exp_cap + PCI_EXP_FLAGS, PCI_EXP_FLAGS_IRQ); - pci_word_test_and_set_mask(exp_cap + PCI_EXP_FLAGS, - vector << PCI_EXP_FLAGS_IRQ_SHIFT); -} - -uint8_t pcie_cap_flags_get_vector(PCIDevice *dev) -{ - return (pci_get_word(dev->config + dev->exp.exp_cap + PCI_EXP_FLAGS) & - PCI_EXP_FLAGS_IRQ) >> PCI_EXP_FLAGS_IRQ_SHIFT; -} - -void pcie_cap_deverr_init(PCIDevice *dev) -{ - uint32_t pos = dev->exp.exp_cap; - pci_long_test_and_set_mask(dev->config + pos + PCI_EXP_DEVCAP, - PCI_EXP_DEVCAP_RBER); - pci_long_test_and_set_mask(dev->wmask + pos + PCI_EXP_DEVCTL, - PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | - PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE); - pci_long_test_and_set_mask(dev->w1cmask + pos + PCI_EXP_DEVSTA, - PCI_EXP_DEVSTA_CED | PCI_EXP_DEVSTA_NFED | - PCI_EXP_DEVSTA_URD | PCI_EXP_DEVSTA_URD); -} - -void pcie_cap_deverr_reset(PCIDevice *dev) -{ - uint8_t *devctl = dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL; - pci_long_test_and_clear_mask(devctl, - PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | - PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE); -} - -static void hotplug_event_update_event_status(PCIDevice *dev) -{ - uint32_t pos = dev->exp.exp_cap; - uint8_t *exp_cap = dev->config + pos; - uint16_t sltctl = pci_get_word(exp_cap + PCI_EXP_SLTCTL); - uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA); - - dev->exp.hpev_notified = (sltctl & PCI_EXP_SLTCTL_HPIE) && - (sltsta & sltctl & PCI_EXP_HP_EV_SUPPORTED); -} - -static void hotplug_event_notify(PCIDevice *dev) -{ - bool prev = dev->exp.hpev_notified; - - hotplug_event_update_event_status(dev); - - if (prev == dev->exp.hpev_notified) { - return; - } - - /* Note: the logic above does not take into account whether interrupts - * are masked. The result is that interrupt will be sent when it is - * subsequently unmasked. This appears to be legal: Section 6.7.3.4: - * The Port may optionally send an MSI when there are hot-plug events that - * occur while interrupt generation is disabled, and interrupt generation is - * subsequently enabled. */ - if (msix_enabled(dev)) { - msix_notify(dev, pcie_cap_flags_get_vector(dev)); - } else if (msi_enabled(dev)) { - msi_notify(dev, pcie_cap_flags_get_vector(dev)); - } else { - qemu_set_irq(dev->irq[dev->exp.hpev_intx], dev->exp.hpev_notified); - } -} - -static void hotplug_event_clear(PCIDevice *dev) -{ - hotplug_event_update_event_status(dev); - if (!msix_enabled(dev) && !msi_enabled(dev) && !dev->exp.hpev_notified) { - qemu_set_irq(dev->irq[dev->exp.hpev_intx], 0); - } -} - -/* - * A PCI Express Hot-Plug Event has occurred, so update slot status register - * and notify OS of the event if necessary. - * - * 6.7.3 PCI Express Hot-Plug Events - * 6.7.3.4 Software Notification of Hot-Plug Events - */ -static void pcie_cap_slot_event(PCIDevice *dev, PCIExpressHotPlugEvent event) -{ - /* Minor optimization: if nothing changed - no event is needed. */ - if (pci_word_test_and_set_mask(dev->config + dev->exp.exp_cap + - PCI_EXP_SLTSTA, event)) { - return; - } - hotplug_event_notify(dev); -} - -static int pcie_cap_slot_hotplug(DeviceState *qdev, - PCIDevice *pci_dev, PCIHotplugState state) -{ - PCIDevice *d = PCI_DEVICE(qdev); - uint8_t *exp_cap = d->config + d->exp.exp_cap; - uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA); - - /* Don't send event when device is enabled during qemu machine creation: - * it is present on boot, no hotplug event is necessary. We do send an - * event when the device is disabled later. */ - if (state == PCI_COLDPLUG_ENABLED) { - pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA, - PCI_EXP_SLTSTA_PDS); - return 0; - } - - PCIE_DEV_PRINTF(pci_dev, "hotplug state: %d\n", state); - if (sltsta & PCI_EXP_SLTSTA_EIS) { - /* the slot is electromechanically locked. - * This error is propagated up to qdev and then to HMP/QMP. - */ - return -EBUSY; - } - - /* TODO: multifunction hot-plug. - * Right now, only a device of function = 0 is allowed to be - * hot plugged/unplugged. - */ - assert(PCI_FUNC(pci_dev->devfn) == 0); - - if (state == PCI_HOTPLUG_ENABLED) { - pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA, - PCI_EXP_SLTSTA_PDS); - pcie_cap_slot_event(d, PCI_EXP_HP_EV_PDC); - } else { - qdev_free(&pci_dev->qdev); - pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTSTA, - PCI_EXP_SLTSTA_PDS); - pcie_cap_slot_event(d, PCI_EXP_HP_EV_PDC); - } - return 0; -} - -/* pci express slot for pci express root/downstream port - PCI express capability slot registers */ -void pcie_cap_slot_init(PCIDevice *dev, uint16_t slot) -{ - uint32_t pos = dev->exp.exp_cap; - - pci_word_test_and_set_mask(dev->config + pos + PCI_EXP_FLAGS, - PCI_EXP_FLAGS_SLOT); - - pci_long_test_and_clear_mask(dev->config + pos + PCI_EXP_SLTCAP, - ~PCI_EXP_SLTCAP_PSN); - pci_long_test_and_set_mask(dev->config + pos + PCI_EXP_SLTCAP, - (slot << PCI_EXP_SLTCAP_PSN_SHIFT) | - PCI_EXP_SLTCAP_EIP | - PCI_EXP_SLTCAP_HPS | - PCI_EXP_SLTCAP_HPC | - PCI_EXP_SLTCAP_PIP | - PCI_EXP_SLTCAP_AIP | - PCI_EXP_SLTCAP_ABP); - - pci_word_test_and_clear_mask(dev->config + pos + PCI_EXP_SLTCTL, - PCI_EXP_SLTCTL_PIC | - PCI_EXP_SLTCTL_AIC); - pci_word_test_and_set_mask(dev->config + pos + PCI_EXP_SLTCTL, - PCI_EXP_SLTCTL_PIC_OFF | - PCI_EXP_SLTCTL_AIC_OFF); - pci_word_test_and_set_mask(dev->wmask + pos + PCI_EXP_SLTCTL, - PCI_EXP_SLTCTL_PIC | - PCI_EXP_SLTCTL_AIC | - PCI_EXP_SLTCTL_HPIE | - PCI_EXP_SLTCTL_CCIE | - PCI_EXP_SLTCTL_PDCE | - PCI_EXP_SLTCTL_ABPE); - /* Although reading PCI_EXP_SLTCTL_EIC returns always 0, - * make the bit writable here in order to detect 1b is written. - * pcie_cap_slot_write_config() test-and-clear the bit, so - * this bit always returns 0 to the guest. - */ - pci_word_test_and_set_mask(dev->wmask + pos + PCI_EXP_SLTCTL, - PCI_EXP_SLTCTL_EIC); - - pci_word_test_and_set_mask(dev->w1cmask + pos + PCI_EXP_SLTSTA, - PCI_EXP_HP_EV_SUPPORTED); - - dev->exp.hpev_notified = false; - - pci_bus_hotplug(pci_bridge_get_sec_bus(DO_UPCAST(PCIBridge, dev, dev)), - pcie_cap_slot_hotplug, &dev->qdev); -} - -void pcie_cap_slot_reset(PCIDevice *dev) -{ - uint8_t *exp_cap = dev->config + dev->exp.exp_cap; - - PCIE_DEV_PRINTF(dev, "reset\n"); - - pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTCTL, - PCI_EXP_SLTCTL_EIC | - PCI_EXP_SLTCTL_PIC | - PCI_EXP_SLTCTL_AIC | - PCI_EXP_SLTCTL_HPIE | - PCI_EXP_SLTCTL_CCIE | - PCI_EXP_SLTCTL_PDCE | - PCI_EXP_SLTCTL_ABPE); - pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTCTL, - PCI_EXP_SLTCTL_PIC_OFF | - PCI_EXP_SLTCTL_AIC_OFF); - - pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTSTA, - PCI_EXP_SLTSTA_EIS |/* on reset, - the lock is released */ - PCI_EXP_SLTSTA_CC | - PCI_EXP_SLTSTA_PDC | - PCI_EXP_SLTSTA_ABP); - - hotplug_event_update_event_status(dev); -} - -void pcie_cap_slot_write_config(PCIDevice *dev, - uint32_t addr, uint32_t val, int len) -{ - uint32_t pos = dev->exp.exp_cap; - uint8_t *exp_cap = dev->config + pos; - uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA); - - if (ranges_overlap(addr, len, pos + PCI_EXP_SLTSTA, 2)) { - hotplug_event_clear(dev); - } - - if (!ranges_overlap(addr, len, pos + PCI_EXP_SLTCTL, 2)) { - return; - } - - if (pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTCTL, - PCI_EXP_SLTCTL_EIC)) { - sltsta ^= PCI_EXP_SLTSTA_EIS; /* toggle PCI_EXP_SLTSTA_EIS bit */ - pci_set_word(exp_cap + PCI_EXP_SLTSTA, sltsta); - PCIE_DEV_PRINTF(dev, "PCI_EXP_SLTCTL_EIC: " - "sltsta -> 0x%02"PRIx16"\n", - sltsta); - } - - hotplug_event_notify(dev); - - /* - * 6.7.3.2 Command Completed Events - * - * Software issues a command to a hot-plug capable Downstream Port by - * issuing a write transaction that targets any portion of the Port’s Slot - * Control register. A single write to the Slot Control register is - * considered to be a single command, even if the write affects more than - * one field in the Slot Control register. In response to this transaction, - * the Port must carry out the requested actions and then set the - * associated status field for the command completed event. */ - - /* Real hardware might take a while to complete requested command because - * physical movement would be involved like locking the electromechanical - * lock. However in our case, command is completed instantaneously above, - * so send a command completion event right now. - */ - pcie_cap_slot_event(dev, PCI_EXP_HP_EV_CCI); -} - -int pcie_cap_slot_post_load(void *opaque, int version_id) -{ - PCIDevice *dev = opaque; - hotplug_event_update_event_status(dev); - return 0; -} - -void pcie_cap_slot_push_attention_button(PCIDevice *dev) -{ - pcie_cap_slot_event(dev, PCI_EXP_HP_EV_ABP); -} - -/* root control/capabilities/status. PME isn't emulated for now */ -void pcie_cap_root_init(PCIDevice *dev) -{ - pci_set_word(dev->wmask + dev->exp.exp_cap + PCI_EXP_RTCTL, - PCI_EXP_RTCTL_SECEE | PCI_EXP_RTCTL_SENFEE | - PCI_EXP_RTCTL_SEFEE); -} - -void pcie_cap_root_reset(PCIDevice *dev) -{ - pci_set_word(dev->config + dev->exp.exp_cap + PCI_EXP_RTCTL, 0); -} - -/* function level reset(FLR) */ -void pcie_cap_flr_init(PCIDevice *dev) -{ - pci_long_test_and_set_mask(dev->config + dev->exp.exp_cap + PCI_EXP_DEVCAP, - PCI_EXP_DEVCAP_FLR); - - /* Although reading BCR_FLR returns always 0, - * the bit is made writable here in order to detect the 1b is written - * pcie_cap_flr_write_config() test-and-clear the bit, so - * this bit always returns 0 to the guest. - */ - pci_word_test_and_set_mask(dev->wmask + dev->exp.exp_cap + PCI_EXP_DEVCTL, - PCI_EXP_DEVCTL_BCR_FLR); -} - -void pcie_cap_flr_write_config(PCIDevice *dev, - uint32_t addr, uint32_t val, int len) -{ - uint8_t *devctl = dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL; - if (pci_get_word(devctl) & PCI_EXP_DEVCTL_BCR_FLR) { - /* Clear PCI_EXP_DEVCTL_BCR_FLR after invoking the reset handler - so the handler can detect FLR by looking at this bit. */ - pci_device_reset(dev); - pci_word_test_and_clear_mask(devctl, PCI_EXP_DEVCTL_BCR_FLR); - } -} - -/* Alternative Routing-ID Interpretation (ARI) */ -/* ari forwarding support for down stream port */ -void pcie_cap_ari_init(PCIDevice *dev) -{ - uint32_t pos = dev->exp.exp_cap; - pci_long_test_and_set_mask(dev->config + pos + PCI_EXP_DEVCAP2, - PCI_EXP_DEVCAP2_ARI); - pci_long_test_and_set_mask(dev->wmask + pos + PCI_EXP_DEVCTL2, - PCI_EXP_DEVCTL2_ARI); -} - -void pcie_cap_ari_reset(PCIDevice *dev) -{ - uint8_t *devctl2 = dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL2; - pci_long_test_and_clear_mask(devctl2, PCI_EXP_DEVCTL2_ARI); -} - -bool pcie_cap_is_ari_enabled(const PCIDevice *dev) -{ - if (!pci_is_express(dev)) { - return false; - } - if (!dev->exp.exp_cap) { - return false; - } - - return pci_get_long(dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL2) & - PCI_EXP_DEVCTL2_ARI; -} - -/************************************************************************** - * pci express extended capability allocation functions - * uint16_t ext_cap_id (16 bit) - * uint8_t cap_ver (4 bit) - * uint16_t cap_offset (12 bit) - * uint16_t ext_cap_size - */ - -static uint16_t pcie_find_capability_list(PCIDevice *dev, uint16_t cap_id, - uint16_t *prev_p) -{ - uint16_t prev = 0; - uint16_t next; - uint32_t header = pci_get_long(dev->config + PCI_CONFIG_SPACE_SIZE); - - if (!header) { - /* no extended capability */ - next = 0; - goto out; - } - for (next = PCI_CONFIG_SPACE_SIZE; next; - prev = next, next = PCI_EXT_CAP_NEXT(header)) { - - assert(next >= PCI_CONFIG_SPACE_SIZE); - assert(next <= PCIE_CONFIG_SPACE_SIZE - 8); - - header = pci_get_long(dev->config + next); - if (PCI_EXT_CAP_ID(header) == cap_id) { - break; - } - } - -out: - if (prev_p) { - *prev_p = prev; - } - return next; -} - -uint16_t pcie_find_capability(PCIDevice *dev, uint16_t cap_id) -{ - return pcie_find_capability_list(dev, cap_id, NULL); -} - -static void pcie_ext_cap_set_next(PCIDevice *dev, uint16_t pos, uint16_t next) -{ - uint16_t header = pci_get_long(dev->config + pos); - assert(!(next & (PCI_EXT_CAP_ALIGN - 1))); - header = (header & ~PCI_EXT_CAP_NEXT_MASK) | - ((next << PCI_EXT_CAP_NEXT_SHIFT) & PCI_EXT_CAP_NEXT_MASK); - pci_set_long(dev->config + pos, header); -} - -/* - * caller must supply valid (offset, size) * such that the range shouldn't - * overlap with other capability or other registers. - * This function doesn't check it. - */ -void pcie_add_capability(PCIDevice *dev, - uint16_t cap_id, uint8_t cap_ver, - uint16_t offset, uint16_t size) -{ - uint32_t header; - uint16_t next; - - assert(offset >= PCI_CONFIG_SPACE_SIZE); - assert(offset < offset + size); - assert(offset + size < PCIE_CONFIG_SPACE_SIZE); - assert(size >= 8); - assert(pci_is_express(dev)); - - if (offset == PCI_CONFIG_SPACE_SIZE) { - header = pci_get_long(dev->config + offset); - next = PCI_EXT_CAP_NEXT(header); - } else { - uint16_t prev; - - /* 0 is reserved cap id. use internally to find the last capability - in the linked list */ - next = pcie_find_capability_list(dev, 0, &prev); - - assert(prev >= PCI_CONFIG_SPACE_SIZE); - assert(next == 0); - pcie_ext_cap_set_next(dev, prev, offset); - } - pci_set_long(dev->config + offset, PCI_EXT_CAP(cap_id, cap_ver, next)); - - /* Make capability read-only by default */ - memset(dev->wmask + offset, 0, size); - memset(dev->w1cmask + offset, 0, size); - /* Check capability by default */ - memset(dev->cmask + offset, 0xFF, size); -} - -/************************************************************************** - * pci express extended capability helper functions - */ - -/* ARI */ -void pcie_ari_init(PCIDevice *dev, uint16_t offset, uint16_t nextfn) -{ - pcie_add_capability(dev, PCI_EXT_CAP_ID_ARI, PCI_ARI_VER, - offset, PCI_ARI_SIZEOF); - pci_set_long(dev->config + offset + PCI_ARI_CAP, PCI_ARI_CAP_NFN(nextfn)); -} diff --git a/hw/pcie.h b/hw/pcie.h deleted file mode 100644 index 4889194ae6..0000000000 --- a/hw/pcie.h +++ /dev/null @@ -1,142 +0,0 @@ -/* - * pcie.h - * - * Copyright (c) 2010 Isaku Yamahata - * VA Linux Systems Japan K.K. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#ifndef QEMU_PCIE_H -#define QEMU_PCIE_H - -#include "hw.h" -#include "pci_regs.h" -#include "pcie_regs.h" -#include "pcie_aer.h" - -typedef enum { - /* for attention and power indicator */ - PCI_EXP_HP_IND_RESERVED = PCI_EXP_SLTCTL_IND_RESERVED, - PCI_EXP_HP_IND_ON = PCI_EXP_SLTCTL_IND_ON, - PCI_EXP_HP_IND_BLINK = PCI_EXP_SLTCTL_IND_BLINK, - PCI_EXP_HP_IND_OFF = PCI_EXP_SLTCTL_IND_OFF, -} PCIExpressIndicator; - -typedef enum { - /* these bits must match the bits in Slot Control/Status registers. - * PCI_EXP_HP_EV_xxx = PCI_EXP_SLTCTL_xxxE = PCI_EXP_SLTSTA_xxx - * - * Not all the bits of slot control register match with the ones of - * slot status. Not some bits of slot status register is used to - * show status, not to report event occurrence. - * So such bits must be masked out when checking the software - * notification condition. - */ - PCI_EXP_HP_EV_ABP = PCI_EXP_SLTCTL_ABPE, - /* attention button pressed */ - PCI_EXP_HP_EV_PDC = PCI_EXP_SLTCTL_PDCE, - /* presence detect changed */ - PCI_EXP_HP_EV_CCI = PCI_EXP_SLTCTL_CCIE, - /* command completed */ - - PCI_EXP_HP_EV_SUPPORTED = PCI_EXP_HP_EV_ABP | - PCI_EXP_HP_EV_PDC | - PCI_EXP_HP_EV_CCI, - /* supported event mask */ - - /* events not listed aren't supported */ -} PCIExpressHotPlugEvent; - -struct PCIExpressDevice { - /* Offset of express capability in config space */ - uint8_t exp_cap; - - /* SLOT */ - unsigned int hpev_intx; /* INTx for hot plug event (0-3:INT[A-D]#) - * default is 0 = INTA# - * If the chip wants to use other interrupt - * line, initialize this member with the - * desired number. - * If the chip dynamically changes this member, - * also initialize it when loaded as - * appropreately. - */ - bool hpev_notified; /* Logical AND of conditions for hot plug event. - Following 6.7.3.4: - Software Notification of Hot-Plug Events, an interrupt - is sent whenever the logical and of these conditions - transitions from false to true. */ - - /* AER */ - uint16_t aer_cap; - PCIEAERLog aer_log; - unsigned int aer_intx; /* INTx for error reporting - * default is 0 = INTA# - * If the chip wants to use other interrupt - * line, initialize this member with the - * desired number. - * If the chip dynamically changes this member, - * also initialize it when loaded as - * appropreately. - */ -}; - -/* PCI express capability helper functions */ -int pcie_cap_init(PCIDevice *dev, uint8_t offset, uint8_t type, uint8_t port); -void pcie_cap_exit(PCIDevice *dev); -uint8_t pcie_cap_get_type(const PCIDevice *dev); -void pcie_cap_flags_set_vector(PCIDevice *dev, uint8_t vector); -uint8_t pcie_cap_flags_get_vector(PCIDevice *dev); - -void pcie_cap_deverr_init(PCIDevice *dev); -void pcie_cap_deverr_reset(PCIDevice *dev); - -void pcie_cap_slot_init(PCIDevice *dev, uint16_t slot); -void pcie_cap_slot_reset(PCIDevice *dev); -void pcie_cap_slot_write_config(PCIDevice *dev, - uint32_t addr, uint32_t val, int len); -int pcie_cap_slot_post_load(void *opaque, int version_id); -void pcie_cap_slot_push_attention_button(PCIDevice *dev); - -void pcie_cap_root_init(PCIDevice *dev); -void pcie_cap_root_reset(PCIDevice *dev); - -void pcie_cap_flr_init(PCIDevice *dev); -void pcie_cap_flr_write_config(PCIDevice *dev, - uint32_t addr, uint32_t val, int len); - -void pcie_cap_ari_init(PCIDevice *dev); -void pcie_cap_ari_reset(PCIDevice *dev); -bool pcie_cap_is_ari_enabled(const PCIDevice *dev); - -/* PCI express extended capability helper functions */ -uint16_t pcie_find_capability(PCIDevice *dev, uint16_t cap_id); -void pcie_add_capability(PCIDevice *dev, - uint16_t cap_id, uint8_t cap_ver, - uint16_t offset, uint16_t size); - -void pcie_ari_init(PCIDevice *dev, uint16_t offset, uint16_t nextfn); - -extern const VMStateDescription vmstate_pcie_device; - -#define VMSTATE_PCIE_DEVICE(_field, _state) { \ - .name = (stringify(_field)), \ - .size = sizeof(PCIDevice), \ - .vmsd = &vmstate_pcie_device, \ - .flags = VMS_STRUCT, \ - .offset = vmstate_offset_value(_state, _field, PCIDevice), \ -} - -#endif /* QEMU_PCIE_H */ diff --git a/hw/pcie_aer.c b/hw/pcie_aer.c deleted file mode 100644 index b04c164e22..0000000000 --- a/hw/pcie_aer.c +++ /dev/null @@ -1,1032 +0,0 @@ -/* - * pcie_aer.c - * - * Copyright (c) 2010 Isaku Yamahata - * VA Linux Systems Japan K.K. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "sysemu.h" -#include "qemu-objects.h" -#include "monitor.h" -#include "pci_bridge.h" -#include "pcie.h" -#include "msix.h" -#include "msi.h" -#include "pci_internals.h" -#include "pcie_regs.h" - -//#define DEBUG_PCIE -#ifdef DEBUG_PCIE -# define PCIE_DPRINTF(fmt, ...) \ - fprintf(stderr, "%s:%d " fmt, __func__, __LINE__, ## __VA_ARGS__) -#else -# define PCIE_DPRINTF(fmt, ...) do {} while (0) -#endif -#define PCIE_DEV_PRINTF(dev, fmt, ...) \ - PCIE_DPRINTF("%s:%x "fmt, (dev)->name, (dev)->devfn, ## __VA_ARGS__) - -#define PCI_ERR_SRC_COR_OFFS 0 -#define PCI_ERR_SRC_UNCOR_OFFS 2 - -/* From 6.2.7 Error Listing and Rules. Table 6-2, 6-3 and 6-4 */ -static uint32_t pcie_aer_uncor_default_severity(uint32_t status) -{ - switch (status) { - case PCI_ERR_UNC_INTN: - case PCI_ERR_UNC_DLP: - case PCI_ERR_UNC_SDN: - case PCI_ERR_UNC_RX_OVER: - case PCI_ERR_UNC_FCP: - case PCI_ERR_UNC_MALF_TLP: - return PCI_ERR_ROOT_CMD_FATAL_EN; - case PCI_ERR_UNC_POISON_TLP: - case PCI_ERR_UNC_ECRC: - case PCI_ERR_UNC_UNSUP: - case PCI_ERR_UNC_COMP_TIME: - case PCI_ERR_UNC_COMP_ABORT: - case PCI_ERR_UNC_UNX_COMP: - case PCI_ERR_UNC_ACSV: - case PCI_ERR_UNC_MCBTLP: - case PCI_ERR_UNC_ATOP_EBLOCKED: - case PCI_ERR_UNC_TLP_PRF_BLOCKED: - return PCI_ERR_ROOT_CMD_NONFATAL_EN; - default: - abort(); - break; - } - return PCI_ERR_ROOT_CMD_FATAL_EN; -} - -static int aer_log_add_err(PCIEAERLog *aer_log, const PCIEAERErr *err) -{ - if (aer_log->log_num == aer_log->log_max) { - return -1; - } - memcpy(&aer_log->log[aer_log->log_num], err, sizeof *err); - aer_log->log_num++; - return 0; -} - -static void aer_log_del_err(PCIEAERLog *aer_log, PCIEAERErr *err) -{ - assert(aer_log->log_num); - *err = aer_log->log[0]; - aer_log->log_num--; - memmove(&aer_log->log[0], &aer_log->log[1], - aer_log->log_num * sizeof *err); -} - -static void aer_log_clear_all_err(PCIEAERLog *aer_log) -{ - aer_log->log_num = 0; -} - -int pcie_aer_init(PCIDevice *dev, uint16_t offset) -{ - PCIExpressDevice *exp; - - pcie_add_capability(dev, PCI_EXT_CAP_ID_ERR, PCI_ERR_VER, - offset, PCI_ERR_SIZEOF); - exp = &dev->exp; - exp->aer_cap = offset; - - /* log_max is property */ - if (dev->exp.aer_log.log_max == PCIE_AER_LOG_MAX_UNSET) { - dev->exp.aer_log.log_max = PCIE_AER_LOG_MAX_DEFAULT; - } - /* clip down the value to avoid unreasobale memory usage */ - if (dev->exp.aer_log.log_max > PCIE_AER_LOG_MAX_LIMIT) { - return -EINVAL; - } - dev->exp.aer_log.log = g_malloc0(sizeof dev->exp.aer_log.log[0] * - dev->exp.aer_log.log_max); - - pci_set_long(dev->w1cmask + offset + PCI_ERR_UNCOR_STATUS, - PCI_ERR_UNC_SUPPORTED); - - pci_set_long(dev->config + offset + PCI_ERR_UNCOR_SEVER, - PCI_ERR_UNC_SEVERITY_DEFAULT); - pci_set_long(dev->wmask + offset + PCI_ERR_UNCOR_SEVER, - PCI_ERR_UNC_SUPPORTED); - - pci_long_test_and_set_mask(dev->w1cmask + offset + PCI_ERR_COR_STATUS, - PCI_ERR_COR_STATUS); - - pci_set_long(dev->config + offset + PCI_ERR_COR_MASK, - PCI_ERR_COR_MASK_DEFAULT); - pci_set_long(dev->wmask + offset + PCI_ERR_COR_MASK, - PCI_ERR_COR_SUPPORTED); - - /* capabilities and control. multiple header logging is supported */ - if (dev->exp.aer_log.log_max > 0) { - pci_set_long(dev->config + offset + PCI_ERR_CAP, - PCI_ERR_CAP_ECRC_GENC | PCI_ERR_CAP_ECRC_CHKC | - PCI_ERR_CAP_MHRC); - pci_set_long(dev->wmask + offset + PCI_ERR_CAP, - PCI_ERR_CAP_ECRC_GENE | PCI_ERR_CAP_ECRC_CHKE | - PCI_ERR_CAP_MHRE); - } else { - pci_set_long(dev->config + offset + PCI_ERR_CAP, - PCI_ERR_CAP_ECRC_GENC | PCI_ERR_CAP_ECRC_CHKC); - pci_set_long(dev->wmask + offset + PCI_ERR_CAP, - PCI_ERR_CAP_ECRC_GENE | PCI_ERR_CAP_ECRC_CHKE); - } - - switch (pcie_cap_get_type(dev)) { - case PCI_EXP_TYPE_ROOT_PORT: - /* this case will be set by pcie_aer_root_init() */ - /* fallthrough */ - case PCI_EXP_TYPE_DOWNSTREAM: - case PCI_EXP_TYPE_UPSTREAM: - pci_word_test_and_set_mask(dev->wmask + PCI_BRIDGE_CONTROL, - PCI_BRIDGE_CTL_SERR); - pci_long_test_and_set_mask(dev->w1cmask + PCI_STATUS, - PCI_SEC_STATUS_RCV_SYSTEM_ERROR); - break; - default: - /* nothing */ - break; - } - return 0; -} - -void pcie_aer_exit(PCIDevice *dev) -{ - g_free(dev->exp.aer_log.log); -} - -static void pcie_aer_update_uncor_status(PCIDevice *dev) -{ - uint8_t *aer_cap = dev->config + dev->exp.aer_cap; - PCIEAERLog *aer_log = &dev->exp.aer_log; - - uint16_t i; - for (i = 0; i < aer_log->log_num; i++) { - pci_long_test_and_set_mask(aer_cap + PCI_ERR_UNCOR_STATUS, - dev->exp.aer_log.log[i].status); - } -} - -/* - * return value: - * true: error message needs to be sent up - * false: error message is masked - * - * 6.2.6 Error Message Control - * Figure 6-3 - * all pci express devices part - */ -static bool -pcie_aer_msg_alldev(PCIDevice *dev, const PCIEAERMsg *msg) -{ - if (!(pcie_aer_msg_is_uncor(msg) && - (pci_get_word(dev->config + PCI_COMMAND) & PCI_COMMAND_SERR))) { - return false; - } - - /* Signaled System Error - * - * 7.5.1.1 Command register - * Bit 8 SERR# Enable - * - * When Set, this bit enables reporting of Non-fatal and Fatal - * errors detected by the Function to the Root Complex. Note that - * errors are reported if enabled either through this bit or through - * the PCI Express specific bits in the Device Control register (see - * Section 7.8.4). - */ - pci_word_test_and_set_mask(dev->config + PCI_STATUS, - PCI_STATUS_SIG_SYSTEM_ERROR); - - if (!(msg->severity & - pci_get_word(dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL))) { - return false; - } - - /* send up error message */ - return true; -} - -/* - * return value: - * true: error message is sent up - * false: error message is masked - * - * 6.2.6 Error Message Control - * Figure 6-3 - * virtual pci bridge part - */ -static bool pcie_aer_msg_vbridge(PCIDevice *dev, const PCIEAERMsg *msg) -{ - uint16_t bridge_control = pci_get_word(dev->config + PCI_BRIDGE_CONTROL); - - if (pcie_aer_msg_is_uncor(msg)) { - /* Received System Error */ - pci_word_test_and_set_mask(dev->config + PCI_SEC_STATUS, - PCI_SEC_STATUS_RCV_SYSTEM_ERROR); - } - - if (!(bridge_control & PCI_BRIDGE_CTL_SERR)) { - return false; - } - return true; -} - -void pcie_aer_root_set_vector(PCIDevice *dev, unsigned int vector) -{ - uint8_t *aer_cap = dev->config + dev->exp.aer_cap; - assert(vector < PCI_ERR_ROOT_IRQ_MAX); - pci_long_test_and_clear_mask(aer_cap + PCI_ERR_ROOT_STATUS, - PCI_ERR_ROOT_IRQ); - pci_long_test_and_set_mask(aer_cap + PCI_ERR_ROOT_STATUS, - vector << PCI_ERR_ROOT_IRQ_SHIFT); -} - -static unsigned int pcie_aer_root_get_vector(PCIDevice *dev) -{ - uint8_t *aer_cap = dev->config + dev->exp.aer_cap; - uint32_t root_status = pci_get_long(aer_cap + PCI_ERR_ROOT_STATUS); - return (root_status & PCI_ERR_ROOT_IRQ) >> PCI_ERR_ROOT_IRQ_SHIFT; -} - -/* Given a status register, get corresponding bits in the command register */ -static uint32_t pcie_aer_status_to_cmd(uint32_t status) -{ - uint32_t cmd = 0; - if (status & PCI_ERR_ROOT_COR_RCV) { - cmd |= PCI_ERR_ROOT_CMD_COR_EN; - } - if (status & PCI_ERR_ROOT_NONFATAL_RCV) { - cmd |= PCI_ERR_ROOT_CMD_NONFATAL_EN; - } - if (status & PCI_ERR_ROOT_FATAL_RCV) { - cmd |= PCI_ERR_ROOT_CMD_FATAL_EN; - } - return cmd; -} - -static void pcie_aer_root_notify(PCIDevice *dev) -{ - if (msix_enabled(dev)) { - msix_notify(dev, pcie_aer_root_get_vector(dev)); - } else if (msi_enabled(dev)) { - msi_notify(dev, pcie_aer_root_get_vector(dev)); - } else { - qemu_set_irq(dev->irq[dev->exp.aer_intx], 1); - } -} - -/* - * 6.2.6 Error Message Control - * Figure 6-3 - * root port part - */ -static void pcie_aer_msg_root_port(PCIDevice *dev, const PCIEAERMsg *msg) -{ - uint16_t cmd; - uint8_t *aer_cap; - uint32_t root_cmd; - uint32_t root_status, prev_status; - - cmd = pci_get_word(dev->config + PCI_COMMAND); - aer_cap = dev->config + dev->exp.aer_cap; - root_cmd = pci_get_long(aer_cap + PCI_ERR_ROOT_COMMAND); - prev_status = root_status = pci_get_long(aer_cap + PCI_ERR_ROOT_STATUS); - - if (cmd & PCI_COMMAND_SERR) { - /* System Error. - * - * The way to report System Error is platform specific and - * it isn't implemented in qemu right now. - * So just discard the error for now. - * OS which cares of aer would receive errors via - * native aer mechanims, so this wouldn't matter. - */ - } - - /* Errro Message Received: Root Error Status register */ - switch (msg->severity) { - case PCI_ERR_ROOT_CMD_COR_EN: - if (root_status & PCI_ERR_ROOT_COR_RCV) { - root_status |= PCI_ERR_ROOT_MULTI_COR_RCV; - } else { - pci_set_word(aer_cap + PCI_ERR_ROOT_ERR_SRC + PCI_ERR_SRC_COR_OFFS, - msg->source_id); - } - root_status |= PCI_ERR_ROOT_COR_RCV; - break; - case PCI_ERR_ROOT_CMD_NONFATAL_EN: - root_status |= PCI_ERR_ROOT_NONFATAL_RCV; - break; - case PCI_ERR_ROOT_CMD_FATAL_EN: - if (!(root_status & PCI_ERR_ROOT_UNCOR_RCV)) { - root_status |= PCI_ERR_ROOT_FIRST_FATAL; - } - root_status |= PCI_ERR_ROOT_FATAL_RCV; - break; - default: - abort(); - break; - } - if (pcie_aer_msg_is_uncor(msg)) { - if (root_status & PCI_ERR_ROOT_UNCOR_RCV) { - root_status |= PCI_ERR_ROOT_MULTI_UNCOR_RCV; - } else { - pci_set_word(aer_cap + PCI_ERR_ROOT_ERR_SRC + - PCI_ERR_SRC_UNCOR_OFFS, msg->source_id); - } - root_status |= PCI_ERR_ROOT_UNCOR_RCV; - } - pci_set_long(aer_cap + PCI_ERR_ROOT_STATUS, root_status); - - /* 6.2.4.1.2 Interrupt Generation */ - /* All the above did was set some bits in the status register. - * Specifically these that match message severity. - * The below code relies on this fact. */ - if (!(root_cmd & msg->severity) || - (pcie_aer_status_to_cmd(prev_status) & root_cmd)) { - /* Condition is not being set or was already true so nothing to do. */ - return; - } - - pcie_aer_root_notify(dev); -} - -/* - * 6.2.6 Error Message Control Figure 6-3 - * - * Walk up the bus tree from the device, propagate the error message. - */ -static void pcie_aer_msg(PCIDevice *dev, const PCIEAERMsg *msg) -{ - uint8_t type; - - while (dev) { - if (!pci_is_express(dev)) { - /* just ignore it */ - /* TODO: Shouldn't we set PCI_STATUS_SIG_SYSTEM_ERROR? - * Consider e.g. a PCI bridge above a PCI Express device. */ - return; - } - - type = pcie_cap_get_type(dev); - if ((type == PCI_EXP_TYPE_ROOT_PORT || - type == PCI_EXP_TYPE_UPSTREAM || - type == PCI_EXP_TYPE_DOWNSTREAM) && - !pcie_aer_msg_vbridge(dev, msg)) { - return; - } - if (!pcie_aer_msg_alldev(dev, msg)) { - return; - } - if (type == PCI_EXP_TYPE_ROOT_PORT) { - pcie_aer_msg_root_port(dev, msg); - /* Root port can notify system itself, - or send the error message to root complex event collector. */ - /* - * if root port is associated with an event collector, - * return the root complex event collector here. - * For now root complex event collector isn't supported. - */ - return; - } - dev = pci_bridge_get_device(dev->bus); - } -} - -static void pcie_aer_update_log(PCIDevice *dev, const PCIEAERErr *err) -{ - uint8_t *aer_cap = dev->config + dev->exp.aer_cap; - uint8_t first_bit = ffs(err->status) - 1; - uint32_t errcap = pci_get_long(aer_cap + PCI_ERR_CAP); - int i; - - assert(err->status); - assert(!(err->status & (err->status - 1))); - - errcap &= ~(PCI_ERR_CAP_FEP_MASK | PCI_ERR_CAP_TLP); - errcap |= PCI_ERR_CAP_FEP(first_bit); - - if (err->flags & PCIE_AER_ERR_HEADER_VALID) { - for (i = 0; i < ARRAY_SIZE(err->header); ++i) { - /* 7.10.8 Header Log Register */ - uint8_t *header_log = - aer_cap + PCI_ERR_HEADER_LOG + i * sizeof err->header[0]; - cpu_to_be32wu((uint32_t*)header_log, err->header[i]); - } - } else { - assert(!(err->flags & PCIE_AER_ERR_TLP_PREFIX_PRESENT)); - memset(aer_cap + PCI_ERR_HEADER_LOG, 0, PCI_ERR_HEADER_LOG_SIZE); - } - - if ((err->flags & PCIE_AER_ERR_TLP_PREFIX_PRESENT) && - (pci_get_long(dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL2) & - PCI_EXP_DEVCAP2_EETLPP)) { - for (i = 0; i < ARRAY_SIZE(err->prefix); ++i) { - /* 7.10.12 tlp prefix log register */ - uint8_t *prefix_log = - aer_cap + PCI_ERR_TLP_PREFIX_LOG + i * sizeof err->prefix[0]; - cpu_to_be32wu((uint32_t*)prefix_log, err->prefix[i]); - } - errcap |= PCI_ERR_CAP_TLP; - } else { - memset(aer_cap + PCI_ERR_TLP_PREFIX_LOG, 0, - PCI_ERR_TLP_PREFIX_LOG_SIZE); - } - pci_set_long(aer_cap + PCI_ERR_CAP, errcap); -} - -static void pcie_aer_clear_log(PCIDevice *dev) -{ - uint8_t *aer_cap = dev->config + dev->exp.aer_cap; - - pci_long_test_and_clear_mask(aer_cap + PCI_ERR_CAP, - PCI_ERR_CAP_FEP_MASK | PCI_ERR_CAP_TLP); - - memset(aer_cap + PCI_ERR_HEADER_LOG, 0, PCI_ERR_HEADER_LOG_SIZE); - memset(aer_cap + PCI_ERR_TLP_PREFIX_LOG, 0, PCI_ERR_TLP_PREFIX_LOG_SIZE); -} - -static void pcie_aer_clear_error(PCIDevice *dev) -{ - uint8_t *aer_cap = dev->config + dev->exp.aer_cap; - uint32_t errcap = pci_get_long(aer_cap + PCI_ERR_CAP); - PCIEAERLog *aer_log = &dev->exp.aer_log; - PCIEAERErr err; - - if (!(errcap & PCI_ERR_CAP_MHRE) || !aer_log->log_num) { - pcie_aer_clear_log(dev); - return; - } - - /* - * If more errors are queued, set corresponding bits in uncorrectable - * error status. - * We emulate uncorrectable error status register as W1CS. - * So set bit in uncorrectable error status here again for multiple - * error recording support. - * - * 6.2.4.2 Multiple Error Handling(Advanced Error Reporting Capability) - */ - pcie_aer_update_uncor_status(dev); - - aer_log_del_err(aer_log, &err); - pcie_aer_update_log(dev, &err); -} - -static int pcie_aer_record_error(PCIDevice *dev, - const PCIEAERErr *err) -{ - uint8_t *aer_cap = dev->config + dev->exp.aer_cap; - uint32_t errcap = pci_get_long(aer_cap + PCI_ERR_CAP); - int fep = PCI_ERR_CAP_FEP(errcap); - - assert(err->status); - assert(!(err->status & (err->status - 1))); - - if (errcap & PCI_ERR_CAP_MHRE && - (pci_get_long(aer_cap + PCI_ERR_UNCOR_STATUS) & (1U << fep))) { - /* Not first error. queue error */ - if (aer_log_add_err(&dev->exp.aer_log, err) < 0) { - /* overflow */ - return -1; - } - return 0; - } - - pcie_aer_update_log(dev, err); - return 0; -} - -typedef struct PCIEAERInject { - PCIDevice *dev; - uint8_t *aer_cap; - const PCIEAERErr *err; - uint16_t devctl; - uint16_t devsta; - uint32_t error_status; - bool unsupported_request; - bool log_overflow; - PCIEAERMsg msg; -} PCIEAERInject; - -static bool pcie_aer_inject_cor_error(PCIEAERInject *inj, - uint32_t uncor_status, - bool is_advisory_nonfatal) -{ - PCIDevice *dev = inj->dev; - - inj->devsta |= PCI_EXP_DEVSTA_CED; - if (inj->unsupported_request) { - inj->devsta |= PCI_EXP_DEVSTA_URD; - } - pci_set_word(dev->config + dev->exp.exp_cap + PCI_EXP_DEVSTA, inj->devsta); - - if (inj->aer_cap) { - uint32_t mask; - pci_long_test_and_set_mask(inj->aer_cap + PCI_ERR_COR_STATUS, - inj->error_status); - mask = pci_get_long(inj->aer_cap + PCI_ERR_COR_MASK); - if (mask & inj->error_status) { - return false; - } - if (is_advisory_nonfatal) { - uint32_t uncor_mask = - pci_get_long(inj->aer_cap + PCI_ERR_UNCOR_MASK); - if (!(uncor_mask & uncor_status)) { - inj->log_overflow = !!pcie_aer_record_error(dev, inj->err); - } - pci_long_test_and_set_mask(inj->aer_cap + PCI_ERR_UNCOR_STATUS, - uncor_status); - } - } - - if (inj->unsupported_request && !(inj->devctl & PCI_EXP_DEVCTL_URRE)) { - return false; - } - if (!(inj->devctl & PCI_EXP_DEVCTL_CERE)) { - return false; - } - - inj->msg.severity = PCI_ERR_ROOT_CMD_COR_EN; - return true; -} - -static bool pcie_aer_inject_uncor_error(PCIEAERInject *inj, bool is_fatal) -{ - PCIDevice *dev = inj->dev; - uint16_t cmd; - - if (is_fatal) { - inj->devsta |= PCI_EXP_DEVSTA_FED; - } else { - inj->devsta |= PCI_EXP_DEVSTA_NFED; - } - if (inj->unsupported_request) { - inj->devsta |= PCI_EXP_DEVSTA_URD; - } - pci_set_long(dev->config + dev->exp.exp_cap + PCI_EXP_DEVSTA, inj->devsta); - - if (inj->aer_cap) { - uint32_t mask = pci_get_long(inj->aer_cap + PCI_ERR_UNCOR_MASK); - if (mask & inj->error_status) { - pci_long_test_and_set_mask(inj->aer_cap + PCI_ERR_UNCOR_STATUS, - inj->error_status); - return false; - } - - inj->log_overflow = !!pcie_aer_record_error(dev, inj->err); - pci_long_test_and_set_mask(inj->aer_cap + PCI_ERR_UNCOR_STATUS, - inj->error_status); - } - - cmd = pci_get_word(dev->config + PCI_COMMAND); - if (inj->unsupported_request && - !(inj->devctl & PCI_EXP_DEVCTL_URRE) && !(cmd & PCI_COMMAND_SERR)) { - return false; - } - if (is_fatal) { - if (!((cmd & PCI_COMMAND_SERR) || - (inj->devctl & PCI_EXP_DEVCTL_FERE))) { - return false; - } - inj->msg.severity = PCI_ERR_ROOT_CMD_FATAL_EN; - } else { - if (!((cmd & PCI_COMMAND_SERR) || - (inj->devctl & PCI_EXP_DEVCTL_NFERE))) { - return false; - } - inj->msg.severity = PCI_ERR_ROOT_CMD_NONFATAL_EN; - } - return true; -} - -/* - * non-Function specific error must be recorded in all functions. - * It is the responsibility of the caller of this function. - * It is also caller's responsibility to determine which function should - * report the rerror. - * - * 6.2.4 Error Logging - * 6.2.5 Sqeunce of Device Error Signaling and Logging Operations - * table 6-2: Flowchard Showing Sequence of Device Error Signaling and Logging - * Operations - */ -int pcie_aer_inject_error(PCIDevice *dev, const PCIEAERErr *err) -{ - uint8_t *aer_cap = NULL; - uint16_t devctl = 0; - uint16_t devsta = 0; - uint32_t error_status = err->status; - PCIEAERInject inj; - - if (!pci_is_express(dev)) { - return -ENOSYS; - } - - if (err->flags & PCIE_AER_ERR_IS_CORRECTABLE) { - error_status &= PCI_ERR_COR_SUPPORTED; - } else { - error_status &= PCI_ERR_UNC_SUPPORTED; - } - - /* invalid status bit. one and only one bit must be set */ - if (!error_status || (error_status & (error_status - 1))) { - return -EINVAL; - } - - if (dev->exp.aer_cap) { - uint8_t *exp_cap = dev->config + dev->exp.exp_cap; - aer_cap = dev->config + dev->exp.aer_cap; - devctl = pci_get_long(exp_cap + PCI_EXP_DEVCTL); - devsta = pci_get_long(exp_cap + PCI_EXP_DEVSTA); - } - - inj.dev = dev; - inj.aer_cap = aer_cap; - inj.err = err; - inj.devctl = devctl; - inj.devsta = devsta; - inj.error_status = error_status; - inj.unsupported_request = !(err->flags & PCIE_AER_ERR_IS_CORRECTABLE) && - err->status == PCI_ERR_UNC_UNSUP; - inj.log_overflow = false; - - if (err->flags & PCIE_AER_ERR_IS_CORRECTABLE) { - if (!pcie_aer_inject_cor_error(&inj, 0, false)) { - return 0; - } - } else { - bool is_fatal = - pcie_aer_uncor_default_severity(error_status) == - PCI_ERR_ROOT_CMD_FATAL_EN; - if (aer_cap) { - is_fatal = - error_status & pci_get_long(aer_cap + PCI_ERR_UNCOR_SEVER); - } - if (!is_fatal && (err->flags & PCIE_AER_ERR_MAYBE_ADVISORY)) { - inj.error_status = PCI_ERR_COR_ADV_NONFATAL; - if (!pcie_aer_inject_cor_error(&inj, error_status, true)) { - return 0; - } - } else { - if (!pcie_aer_inject_uncor_error(&inj, is_fatal)) { - return 0; - } - } - } - - /* send up error message */ - inj.msg.source_id = err->source_id; - pcie_aer_msg(dev, &inj.msg); - - if (inj.log_overflow) { - PCIEAERErr header_log_overflow = { - .status = PCI_ERR_COR_HL_OVERFLOW, - .flags = PCIE_AER_ERR_IS_CORRECTABLE, - }; - int ret = pcie_aer_inject_error(dev, &header_log_overflow); - assert(!ret); - } - return 0; -} - -void pcie_aer_write_config(PCIDevice *dev, - uint32_t addr, uint32_t val, int len) -{ - uint8_t *aer_cap = dev->config + dev->exp.aer_cap; - uint32_t errcap = pci_get_long(aer_cap + PCI_ERR_CAP); - uint32_t first_error = 1U << PCI_ERR_CAP_FEP(errcap); - uint32_t uncorsta = pci_get_long(aer_cap + PCI_ERR_UNCOR_STATUS); - - /* uncorrectable error */ - if (!(uncorsta & first_error)) { - /* the bit that corresponds to the first error is cleared */ - pcie_aer_clear_error(dev); - } else if (errcap & PCI_ERR_CAP_MHRE) { - /* When PCI_ERR_CAP_MHRE is enabled and the first error isn't cleared - * nothing should happen. So we have to revert the modification to - * the register. - */ - pcie_aer_update_uncor_status(dev); - } else { - /* capability & control - * PCI_ERR_CAP_MHRE might be cleared, so clear of header log. - */ - aer_log_clear_all_err(&dev->exp.aer_log); - } -} - -void pcie_aer_root_init(PCIDevice *dev) -{ - uint16_t pos = dev->exp.aer_cap; - - pci_set_long(dev->wmask + pos + PCI_ERR_ROOT_COMMAND, - PCI_ERR_ROOT_CMD_EN_MASK); - pci_set_long(dev->w1cmask + pos + PCI_ERR_ROOT_STATUS, - PCI_ERR_ROOT_STATUS_REPORT_MASK); - /* PCI_ERR_ROOT_IRQ is RO but devices change it using a - * device-specific method. - */ - pci_set_long(dev->cmask + pos + PCI_ERR_ROOT_STATUS, - ~PCI_ERR_ROOT_IRQ); -} - -void pcie_aer_root_reset(PCIDevice *dev) -{ - uint8_t* aer_cap = dev->config + dev->exp.aer_cap; - - pci_set_long(aer_cap + PCI_ERR_ROOT_COMMAND, 0); - - /* - * Advanced Error Interrupt Message Number in Root Error Status Register - * must be updated by chip dependent code because it's chip dependent - * which number is used. - */ -} - -void pcie_aer_root_write_config(PCIDevice *dev, - uint32_t addr, uint32_t val, int len, - uint32_t root_cmd_prev) -{ - uint8_t *aer_cap = dev->config + dev->exp.aer_cap; - uint32_t root_status = pci_get_long(aer_cap + PCI_ERR_ROOT_STATUS); - uint32_t enabled_cmd = pcie_aer_status_to_cmd(root_status); - uint32_t root_cmd = pci_get_long(aer_cap + PCI_ERR_ROOT_COMMAND); - /* 6.2.4.1.2 Interrupt Generation */ - if (!msix_enabled(dev) && !msi_enabled(dev)) { - qemu_set_irq(dev->irq[dev->exp.aer_intx], !!(root_cmd & enabled_cmd)); - return; - } - - if ((root_cmd_prev & enabled_cmd) || !(root_cmd & enabled_cmd)) { - /* Send MSI on transition from false to true. */ - return; - } - - pcie_aer_root_notify(dev); -} - -static const VMStateDescription vmstate_pcie_aer_err = { - .name = "PCIE_AER_ERROR", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(status, PCIEAERErr), - VMSTATE_UINT16(source_id, PCIEAERErr), - VMSTATE_UINT16(flags, PCIEAERErr), - VMSTATE_UINT32_ARRAY(header, PCIEAERErr, 4), - VMSTATE_UINT32_ARRAY(prefix, PCIEAERErr, 4), - VMSTATE_END_OF_LIST() - } -}; - -const VMStateDescription vmstate_pcie_aer_log = { - .name = "PCIE_AER_ERROR_LOG", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT16(log_num, PCIEAERLog), - VMSTATE_UINT16(log_max, PCIEAERLog), - VMSTATE_STRUCT_VARRAY_POINTER_UINT16(log, PCIEAERLog, log_num, - vmstate_pcie_aer_err, PCIEAERErr), - VMSTATE_END_OF_LIST() - } -}; - -void pcie_aer_inject_error_print(Monitor *mon, const QObject *data) -{ - QDict *qdict; - int devfn; - assert(qobject_type(data) == QTYPE_QDICT); - qdict = qobject_to_qdict(data); - - devfn = (int)qdict_get_int(qdict, "devfn"); - monitor_printf(mon, "OK id: %s domain: %x, bus: %x devfn: %x.%x\n", - qdict_get_str(qdict, "id"), - (int) qdict_get_int(qdict, "domain"), - (int) qdict_get_int(qdict, "bus"), - PCI_SLOT(devfn), PCI_FUNC(devfn)); -} - -typedef struct PCIEAERErrorName { - const char *name; - uint32_t val; - bool correctable; -} PCIEAERErrorName; - -/* - * AER error name -> value conversion table - * This naming scheme is same to linux aer-injection tool. - */ -static const struct PCIEAERErrorName pcie_aer_error_list[] = { - { - .name = "TRAIN", - .val = PCI_ERR_UNC_TRAIN, - .correctable = false, - }, { - .name = "DLP", - .val = PCI_ERR_UNC_DLP, - .correctable = false, - }, { - .name = "SDN", - .val = PCI_ERR_UNC_SDN, - .correctable = false, - }, { - .name = "POISON_TLP", - .val = PCI_ERR_UNC_POISON_TLP, - .correctable = false, - }, { - .name = "FCP", - .val = PCI_ERR_UNC_FCP, - .correctable = false, - }, { - .name = "COMP_TIME", - .val = PCI_ERR_UNC_COMP_TIME, - .correctable = false, - }, { - .name = "COMP_ABORT", - .val = PCI_ERR_UNC_COMP_ABORT, - .correctable = false, - }, { - .name = "UNX_COMP", - .val = PCI_ERR_UNC_UNX_COMP, - .correctable = false, - }, { - .name = "RX_OVER", - .val = PCI_ERR_UNC_RX_OVER, - .correctable = false, - }, { - .name = "MALF_TLP", - .val = PCI_ERR_UNC_MALF_TLP, - .correctable = false, - }, { - .name = "ECRC", - .val = PCI_ERR_UNC_ECRC, - .correctable = false, - }, { - .name = "UNSUP", - .val = PCI_ERR_UNC_UNSUP, - .correctable = false, - }, { - .name = "ACSV", - .val = PCI_ERR_UNC_ACSV, - .correctable = false, - }, { - .name = "INTN", - .val = PCI_ERR_UNC_INTN, - .correctable = false, - }, { - .name = "MCBTLP", - .val = PCI_ERR_UNC_MCBTLP, - .correctable = false, - }, { - .name = "ATOP_EBLOCKED", - .val = PCI_ERR_UNC_ATOP_EBLOCKED, - .correctable = false, - }, { - .name = "TLP_PRF_BLOCKED", - .val = PCI_ERR_UNC_TLP_PRF_BLOCKED, - .correctable = false, - }, { - .name = "RCVR", - .val = PCI_ERR_COR_RCVR, - .correctable = true, - }, { - .name = "BAD_TLP", - .val = PCI_ERR_COR_BAD_TLP, - .correctable = true, - }, { - .name = "BAD_DLLP", - .val = PCI_ERR_COR_BAD_DLLP, - .correctable = true, - }, { - .name = "REP_ROLL", - .val = PCI_ERR_COR_REP_ROLL, - .correctable = true, - }, { - .name = "REP_TIMER", - .val = PCI_ERR_COR_REP_TIMER, - .correctable = true, - }, { - .name = "ADV_NONFATAL", - .val = PCI_ERR_COR_ADV_NONFATAL, - .correctable = true, - }, { - .name = "INTERNAL", - .val = PCI_ERR_COR_INTERNAL, - .correctable = true, - }, { - .name = "HL_OVERFLOW", - .val = PCI_ERR_COR_HL_OVERFLOW, - .correctable = true, - }, -}; - -static int pcie_aer_parse_error_string(const char *error_name, - uint32_t *status, bool *correctable) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(pcie_aer_error_list); i++) { - const PCIEAERErrorName *e = &pcie_aer_error_list[i]; - if (strcmp(error_name, e->name)) { - continue; - } - - *status = e->val; - *correctable = e->correctable; - return 0; - } - return -EINVAL; -} - -int do_pcie_aer_inject_error(Monitor *mon, - const QDict *qdict, QObject **ret_data) -{ - const char *id = qdict_get_str(qdict, "id"); - const char *error_name; - uint32_t error_status; - bool correctable; - PCIDevice *dev; - PCIEAERErr err; - int ret; - - ret = pci_qdev_find_device(id, &dev); - if (ret < 0) { - monitor_printf(mon, - "id or pci device path is invalid or device not " - "found. %s\n", id); - return ret; - } - if (!pci_is_express(dev)) { - monitor_printf(mon, "the device doesn't support pci express. %s\n", - id); - return -ENOSYS; - } - - error_name = qdict_get_str(qdict, "error_status"); - if (pcie_aer_parse_error_string(error_name, &error_status, &correctable)) { - char *e = NULL; - error_status = strtoul(error_name, &e, 0); - correctable = qdict_get_try_bool(qdict, "correctable", 0); - if (!e || *e != '\0') { - monitor_printf(mon, "invalid error status value. \"%s\"", - error_name); - return -EINVAL; - } - } - err.status = error_status; - err.source_id = (pci_bus_num(dev->bus) << 8) | dev->devfn; - - err.flags = 0; - if (correctable) { - err.flags |= PCIE_AER_ERR_IS_CORRECTABLE; - } - if (qdict_get_try_bool(qdict, "advisory_non_fatal", 0)) { - err.flags |= PCIE_AER_ERR_MAYBE_ADVISORY; - } - if (qdict_haskey(qdict, "header0")) { - err.flags |= PCIE_AER_ERR_HEADER_VALID; - } - if (qdict_haskey(qdict, "prefix0")) { - err.flags |= PCIE_AER_ERR_TLP_PREFIX_PRESENT; - } - - err.header[0] = qdict_get_try_int(qdict, "header0", 0); - err.header[1] = qdict_get_try_int(qdict, "header1", 0); - err.header[2] = qdict_get_try_int(qdict, "header2", 0); - err.header[3] = qdict_get_try_int(qdict, "header3", 0); - - err.prefix[0] = qdict_get_try_int(qdict, "prefix0", 0); - err.prefix[1] = qdict_get_try_int(qdict, "prefix1", 0); - err.prefix[2] = qdict_get_try_int(qdict, "prefix2", 0); - err.prefix[3] = qdict_get_try_int(qdict, "prefix3", 0); - - ret = pcie_aer_inject_error(dev, &err); - *ret_data = qobject_from_jsonf("{'id': %s, " - "'domain': %d, 'bus': %d, 'devfn': %d, " - "'ret': %d}", - id, - pci_find_domain(dev->bus), - pci_bus_num(dev->bus), dev->devfn, - ret); - assert(*ret_data); - - return 0; -} diff --git a/hw/pcie_aer.h b/hw/pcie_aer.h deleted file mode 100644 index 7539500cd8..0000000000 --- a/hw/pcie_aer.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * pcie_aer.h - * - * Copyright (c) 2010 Isaku Yamahata - * VA Linux Systems Japan K.K. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#ifndef QEMU_PCIE_AER_H -#define QEMU_PCIE_AER_H - -#include "hw.h" - -/* definitions which PCIExpressDevice uses */ - -/* AER log */ -struct PCIEAERLog { - /* This structure is saved/loaded. - So explicitly size them instead of unsigned int */ - - /* the number of currently recorded log in log member */ - uint16_t log_num; - - /* - * The maximum number of the log. Errors can be logged up to this. - * - * This is configurable property. - * The specified value will be clipped down to PCIE_AER_LOG_MAX_LIMIT - * to avoid unreasonable memory usage. - * I bet that 128 log size would be big enough, otherwise too many errors - * for system to function normaly. But could consecutive errors occur? - */ -#define PCIE_AER_LOG_MAX_DEFAULT 8 -#define PCIE_AER_LOG_MAX_LIMIT 128 -#define PCIE_AER_LOG_MAX_UNSET 0xffff - uint16_t log_max; - - /* Error log. log_max-sized array */ - PCIEAERErr *log; -}; - -/* aer error message: error signaling message has only error sevirity and - source id. See 2.2.8.3 error signaling messages */ -struct PCIEAERMsg { - /* - * PCI_ERR_ROOT_CMD_{COR, NONFATAL, FATAL}_EN - * = PCI_EXP_DEVCTL_{CERE, NFERE, FERE} - */ - uint32_t severity; - - uint16_t source_id; /* bdf */ -}; - -static inline bool -pcie_aer_msg_is_uncor(const PCIEAERMsg *msg) -{ - return msg->severity == PCI_ERR_ROOT_CMD_NONFATAL_EN || - msg->severity == PCI_ERR_ROOT_CMD_FATAL_EN; -} - -/* error */ -struct PCIEAERErr { - uint32_t status; /* error status bits */ - uint16_t source_id; /* bdf */ - -#define PCIE_AER_ERR_IS_CORRECTABLE 0x1 /* correctable/uncorrectable */ -#define PCIE_AER_ERR_MAYBE_ADVISORY 0x2 /* maybe advisory non-fatal */ -#define PCIE_AER_ERR_HEADER_VALID 0x4 /* TLP header is logged */ -#define PCIE_AER_ERR_TLP_PREFIX_PRESENT 0x8 /* TLP Prefix is logged */ - uint16_t flags; - - uint32_t header[4]; /* TLP header */ - uint32_t prefix[4]; /* TLP header prefix */ -}; - -extern const VMStateDescription vmstate_pcie_aer_log; - -int pcie_aer_init(PCIDevice *dev, uint16_t offset); -void pcie_aer_exit(PCIDevice *dev); -void pcie_aer_write_config(PCIDevice *dev, - uint32_t addr, uint32_t val, int len); - -/* aer root port */ -void pcie_aer_root_set_vector(PCIDevice *dev, unsigned int vector); -void pcie_aer_root_init(PCIDevice *dev); -void pcie_aer_root_reset(PCIDevice *dev); -void pcie_aer_root_write_config(PCIDevice *dev, - uint32_t addr, uint32_t val, int len, - uint32_t root_cmd_prev); - -/* error injection */ -int pcie_aer_inject_error(PCIDevice *dev, const PCIEAERErr *err); - -#endif /* QEMU_PCIE_AER_H */ diff --git a/hw/pcie_host.c b/hw/pcie_host.c deleted file mode 100644 index c257fb43ca..0000000000 --- a/hw/pcie_host.c +++ /dev/null @@ -1,161 +0,0 @@ -/* - * pcie_host.c - * utility functions for pci express host bridge. - * - * Copyright (c) 2009 Isaku Yamahata - * VA Linux Systems Japan K.K. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "hw.h" -#include "pci.h" -#include "pcie_host.h" -#include "exec-memory.h" - -/* - * PCI express mmcfig address - * bit 20 - 28: bus number - * bit 15 - 19: device number - * bit 12 - 14: function number - * bit 0 - 11: offset in configuration space of a given device - */ -#define PCIE_MMCFG_SIZE_MAX (1ULL << 28) -#define PCIE_MMCFG_SIZE_MIN (1ULL << 20) -#define PCIE_MMCFG_BUS_BIT 20 -#define PCIE_MMCFG_BUS_MASK 0x1ff -#define PCIE_MMCFG_DEVFN_BIT 12 -#define PCIE_MMCFG_DEVFN_MASK 0xff -#define PCIE_MMCFG_CONFOFFSET_MASK 0xfff -#define PCIE_MMCFG_BUS(addr) (((addr) >> PCIE_MMCFG_BUS_BIT) & \ - PCIE_MMCFG_BUS_MASK) -#define PCIE_MMCFG_DEVFN(addr) (((addr) >> PCIE_MMCFG_DEVFN_BIT) & \ - PCIE_MMCFG_DEVFN_MASK) -#define PCIE_MMCFG_CONFOFFSET(addr) ((addr) & PCIE_MMCFG_CONFOFFSET_MASK) - - -/* a helper function to get a PCIDevice for a given mmconfig address */ -static inline PCIDevice *pcie_dev_find_by_mmcfg_addr(PCIBus *s, - uint32_t mmcfg_addr) -{ - return pci_find_device(s, PCIE_MMCFG_BUS(mmcfg_addr), - PCIE_MMCFG_DEVFN(mmcfg_addr)); -} - -static void pcie_mmcfg_data_write(void *opaque, hwaddr mmcfg_addr, - uint64_t val, unsigned len) -{ - PCIExpressHost *e = opaque; - PCIBus *s = e->pci.bus; - PCIDevice *pci_dev = pcie_dev_find_by_mmcfg_addr(s, mmcfg_addr); - uint32_t addr; - uint32_t limit; - - if (!pci_dev) { - return; - } - addr = PCIE_MMCFG_CONFOFFSET(mmcfg_addr); - limit = pci_config_size(pci_dev); - if (limit <= addr) { - /* conventional pci device can be behind pcie-to-pci bridge. - 256 <= addr < 4K has no effects. */ - return; - } - pci_host_config_write_common(pci_dev, addr, limit, val, len); -} - -static uint64_t pcie_mmcfg_data_read(void *opaque, - hwaddr mmcfg_addr, - unsigned len) -{ - PCIExpressHost *e = opaque; - PCIBus *s = e->pci.bus; - PCIDevice *pci_dev = pcie_dev_find_by_mmcfg_addr(s, mmcfg_addr); - uint32_t addr; - uint32_t limit; - - if (!pci_dev) { - return ~0x0; - } - addr = PCIE_MMCFG_CONFOFFSET(mmcfg_addr); - limit = pci_config_size(pci_dev); - if (limit <= addr) { - /* conventional pci device can be behind pcie-to-pci bridge. - 256 <= addr < 4K has no effects. */ - return ~0x0; - } - return pci_host_config_read_common(pci_dev, addr, limit, len); -} - -static const MemoryRegionOps pcie_mmcfg_ops = { - .read = pcie_mmcfg_data_read, - .write = pcie_mmcfg_data_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -/* pcie_host::base_addr == PCIE_BASE_ADDR_UNMAPPED when it isn't mapped. */ -#define PCIE_BASE_ADDR_UNMAPPED ((hwaddr)-1ULL) - -int pcie_host_init(PCIExpressHost *e) -{ - e->base_addr = PCIE_BASE_ADDR_UNMAPPED; - - return 0; -} - -void pcie_host_mmcfg_unmap(PCIExpressHost *e) -{ - if (e->base_addr != PCIE_BASE_ADDR_UNMAPPED) { - memory_region_del_subregion(get_system_memory(), &e->mmio); - memory_region_destroy(&e->mmio); - e->base_addr = PCIE_BASE_ADDR_UNMAPPED; - } -} - -void pcie_host_mmcfg_map(PCIExpressHost *e, hwaddr addr, - uint32_t size) -{ - assert(!(size & (size - 1))); /* power of 2 */ - assert(size >= PCIE_MMCFG_SIZE_MIN); - assert(size <= PCIE_MMCFG_SIZE_MAX); - e->size = size; - memory_region_init_io(&e->mmio, &pcie_mmcfg_ops, e, "pcie-mmcfg", e->size); - e->base_addr = addr; - memory_region_add_subregion(get_system_memory(), e->base_addr, &e->mmio); -} - -void pcie_host_mmcfg_update(PCIExpressHost *e, - int enable, - hwaddr addr, - uint32_t size) -{ - pcie_host_mmcfg_unmap(e); - if (enable) { - pcie_host_mmcfg_map(e, addr, size); - } -} - -static const TypeInfo pcie_host_type_info = { - .name = TYPE_PCIE_HOST_BRIDGE, - .parent = TYPE_PCI_HOST_BRIDGE, - .abstract = true, - .instance_size = sizeof(PCIExpressHost), -}; - -static void pcie_host_register_types(void) -{ - type_register_static(&pcie_host_type_info); -} - -type_init(pcie_host_register_types) diff --git a/hw/pcie_host.h b/hw/pcie_host.h deleted file mode 100644 index 392193530d..0000000000 --- a/hw/pcie_host.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * pcie_host.h - * - * Copyright (c) 2009 Isaku Yamahata - * VA Linux Systems Japan K.K. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#ifndef PCIE_HOST_H -#define PCIE_HOST_H - -#include "pci_host.h" -#include "memory.h" - -#define TYPE_PCIE_HOST_BRIDGE "pcie-host-bridge" -#define PCIE_HOST_BRIDGE(obj) \ - OBJECT_CHECK(PCIExpressHost, (obj), TYPE_PCIE_HOST_BRIDGE) - -struct PCIExpressHost { - PCIHostState pci; - - /* express part */ - - /* base address where MMCONFIG area is mapped. */ - hwaddr base_addr; - - /* the size of MMCONFIG area. It's host bridge dependent */ - hwaddr size; - - /* MMCONFIG mmio area */ - MemoryRegion mmio; -}; - -int pcie_host_init(PCIExpressHost *e); -void pcie_host_mmcfg_unmap(PCIExpressHost *e); -void pcie_host_mmcfg_map(PCIExpressHost *e, hwaddr addr, uint32_t size); -void pcie_host_mmcfg_update(PCIExpressHost *e, - int enable, - hwaddr addr, - uint32_t size); - -#endif /* PCIE_HOST_H */ diff --git a/hw/pcie_port.c b/hw/pcie_port.c deleted file mode 100644 index d6350e5e73..0000000000 --- a/hw/pcie_port.c +++ /dev/null @@ -1,114 +0,0 @@ -/* - * pcie_port.c - * - * Copyright (c) 2010 Isaku Yamahata - * VA Linux Systems Japan K.K. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "pcie_port.h" - -void pcie_port_init_reg(PCIDevice *d) -{ - /* Unlike pci bridge, - 66MHz and fast back to back don't apply to pci express port. */ - pci_set_word(d->config + PCI_STATUS, 0); - pci_set_word(d->config + PCI_SEC_STATUS, 0); - - /* Unlike conventional pci bridge, some bits are hardwired to 0. */ - pci_set_word(d->wmask + PCI_BRIDGE_CONTROL, - PCI_BRIDGE_CTL_PARITY | - PCI_BRIDGE_CTL_ISA | - PCI_BRIDGE_CTL_VGA | - PCI_BRIDGE_CTL_SERR | - PCI_BRIDGE_CTL_BUS_RESET); -} - -/************************************************************************** - * (chassis number, pcie physical slot number) -> pcie slot conversion - */ -struct PCIEChassis { - uint8_t number; - - QLIST_HEAD(, PCIESlot) slots; - QLIST_ENTRY(PCIEChassis) next; -}; - -static QLIST_HEAD(, PCIEChassis) chassis = QLIST_HEAD_INITIALIZER(chassis); - -static struct PCIEChassis *pcie_chassis_find(uint8_t chassis_number) -{ - struct PCIEChassis *c; - QLIST_FOREACH(c, &chassis, next) { - if (c->number == chassis_number) { - break; - } - } - return c; -} - -void pcie_chassis_create(uint8_t chassis_number) -{ - struct PCIEChassis *c; - c = pcie_chassis_find(chassis_number); - if (c) { - return; - } - c = g_malloc0(sizeof(*c)); - c->number = chassis_number; - QLIST_INIT(&c->slots); - QLIST_INSERT_HEAD(&chassis, c, next); -} - -static PCIESlot *pcie_chassis_find_slot_with_chassis(struct PCIEChassis *c, - uint8_t slot) -{ - PCIESlot *s; - QLIST_FOREACH(s, &c->slots, next) { - if (s->slot == slot) { - break; - } - } - return s; -} - -PCIESlot *pcie_chassis_find_slot(uint8_t chassis_number, uint16_t slot) -{ - struct PCIEChassis *c; - c = pcie_chassis_find(chassis_number); - if (!c) { - return NULL; - } - return pcie_chassis_find_slot_with_chassis(c, slot); -} - -int pcie_chassis_add_slot(struct PCIESlot *slot) -{ - struct PCIEChassis *c; - c = pcie_chassis_find(slot->chassis); - if (!c) { - return -ENODEV; - } - if (pcie_chassis_find_slot_with_chassis(c, slot->slot)) { - return -EBUSY; - } - QLIST_INSERT_HEAD(&c->slots, slot, next); - return 0; -} - -void pcie_chassis_del_slot(PCIESlot *s) -{ - QLIST_REMOVE(s, next); -} diff --git a/hw/pcie_port.h b/hw/pcie_port.h deleted file mode 100644 index 3709583cc0..0000000000 --- a/hw/pcie_port.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * pcie_port.h - * - * Copyright (c) 2010 Isaku Yamahata - * VA Linux Systems Japan K.K. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#ifndef QEMU_PCIE_PORT_H -#define QEMU_PCIE_PORT_H - -#include "pci_bridge.h" -#include "pci_internals.h" - -struct PCIEPort { - PCIBridge br; - - /* pci express switch port */ - uint8_t port; -}; - -void pcie_port_init_reg(PCIDevice *d); - -struct PCIESlot { - PCIEPort port; - - /* pci express switch port with slot */ - uint8_t chassis; - uint16_t slot; - QLIST_ENTRY(PCIESlot) next; -}; - -void pcie_chassis_create(uint8_t chassis_number); -void pcie_main_chassis_create(void); -PCIESlot *pcie_chassis_find_slot(uint8_t chassis, uint16_t slot); -int pcie_chassis_add_slot(struct PCIESlot *slot); -void pcie_chassis_del_slot(PCIESlot *s); - -#endif /* QEMU_PCIE_PORT_H */ diff --git a/hw/pcie_regs.h b/hw/pcie_regs.h deleted file mode 100644 index 4d123d9fcc..0000000000 --- a/hw/pcie_regs.h +++ /dev/null @@ -1,156 +0,0 @@ -/* - * constants for pcie configurations space from pci express spec. - * - * TODO: - * Those constants and macros should go to Linux pci_regs.h - * Once they're merged, they will go away. - */ -#ifndef QEMU_PCIE_REGS_H -#define QEMU_PCIE_REGS_H - - -/* express capability */ - -#define PCI_EXP_VER2_SIZEOF 0x3c /* express capability of ver. 2 */ -#define PCI_EXT_CAP_VER_SHIFT 16 -#define PCI_EXT_CAP_NEXT_SHIFT 20 -#define PCI_EXT_CAP_NEXT_MASK (0xffc << PCI_EXT_CAP_NEXT_SHIFT) - -#define PCI_EXT_CAP(id, ver, next) \ - ((id) | \ - ((ver) << PCI_EXT_CAP_VER_SHIFT) | \ - ((next) << PCI_EXT_CAP_NEXT_SHIFT)) - -#define PCI_EXT_CAP_ALIGN 4 -#define PCI_EXT_CAP_ALIGNUP(x) \ - (((x) + PCI_EXT_CAP_ALIGN - 1) & ~(PCI_EXT_CAP_ALIGN - 1)) - -/* PCI_EXP_FLAGS */ -#define PCI_EXP_FLAGS_VER2 2 /* for now, supports only ver. 2 */ -#define PCI_EXP_FLAGS_IRQ_SHIFT (ffs(PCI_EXP_FLAGS_IRQ) - 1) -#define PCI_EXP_FLAGS_TYPE_SHIFT (ffs(PCI_EXP_FLAGS_TYPE) - 1) - - -/* PCI_EXP_LINK{CAP, STA} */ -/* link speed */ -#define PCI_EXP_LNK_LS_25 1 - -#define PCI_EXP_LNK_MLW_SHIFT (ffs(PCI_EXP_LNKCAP_MLW) - 1) -#define PCI_EXP_LNK_MLW_1 (1 << PCI_EXP_LNK_MLW_SHIFT) - -/* PCI_EXP_LINKCAP */ -#define PCI_EXP_LNKCAP_ASPMS_SHIFT (ffs(PCI_EXP_LNKCAP_ASPMS) - 1) -#define PCI_EXP_LNKCAP_ASPMS_0S (1 << PCI_EXP_LNKCAP_ASPMS_SHIFT) - -#define PCI_EXP_LNKCAP_PN_SHIFT (ffs(PCI_EXP_LNKCAP_PN) - 1) - -#define PCI_EXP_SLTCAP_PSN_SHIFT (ffs(PCI_EXP_SLTCAP_PSN) - 1) - -#define PCI_EXP_SLTCTL_IND_RESERVED 0x0 -#define PCI_EXP_SLTCTL_IND_ON 0x1 -#define PCI_EXP_SLTCTL_IND_BLINK 0x2 -#define PCI_EXP_SLTCTL_IND_OFF 0x3 -#define PCI_EXP_SLTCTL_AIC_SHIFT (ffs(PCI_EXP_SLTCTL_AIC) - 1) -#define PCI_EXP_SLTCTL_AIC_OFF \ - (PCI_EXP_SLTCTL_IND_OFF << PCI_EXP_SLTCTL_AIC_SHIFT) - -#define PCI_EXP_SLTCTL_PIC_SHIFT (ffs(PCI_EXP_SLTCTL_PIC) - 1) -#define PCI_EXP_SLTCTL_PIC_OFF \ - (PCI_EXP_SLTCTL_IND_OFF << PCI_EXP_SLTCTL_PIC_SHIFT) - -#define PCI_EXP_SLTCTL_SUPPORTED \ - (PCI_EXP_SLTCTL_ABPE | \ - PCI_EXP_SLTCTL_PDCE | \ - PCI_EXP_SLTCTL_CCIE | \ - PCI_EXP_SLTCTL_HPIE | \ - PCI_EXP_SLTCTL_AIC | \ - PCI_EXP_SLTCTL_PCC | \ - PCI_EXP_SLTCTL_EIC) - -#define PCI_EXP_DEVCAP2_EFF 0x100000 -#define PCI_EXP_DEVCAP2_EETLPP 0x200000 - -#define PCI_EXP_DEVCTL2_EETLPPB 0x80 - -/* ARI */ -#define PCI_ARI_VER 1 -#define PCI_ARI_SIZEOF 8 - -/* AER */ -#define PCI_ERR_VER 2 -#define PCI_ERR_SIZEOF 0x48 - -#define PCI_ERR_UNC_SDN 0x00000020 /* surprise down */ -#define PCI_ERR_UNC_ACSV 0x00200000 /* ACS Violation */ -#define PCI_ERR_UNC_INTN 0x00400000 /* Internal Error */ -#define PCI_ERR_UNC_MCBTLP 0x00800000 /* MC Blcoked TLP */ -#define PCI_ERR_UNC_ATOP_EBLOCKED 0x01000000 /* atomic op egress blocked */ -#define PCI_ERR_UNC_TLP_PRF_BLOCKED 0x02000000 /* TLP Prefix Blocked */ -#define PCI_ERR_COR_ADV_NONFATAL 0x00002000 /* Advisory Non-Fatal */ -#define PCI_ERR_COR_INTERNAL 0x00004000 /* Corrected Internal */ -#define PCI_ERR_COR_HL_OVERFLOW 0x00008000 /* Header Long Overflow */ -#define PCI_ERR_CAP_FEP_MASK 0x0000001f -#define PCI_ERR_CAP_MHRC 0x00000200 -#define PCI_ERR_CAP_MHRE 0x00000400 -#define PCI_ERR_CAP_TLP 0x00000800 - -#define PCI_ERR_HEADER_LOG_SIZE 16 -#define PCI_ERR_TLP_PREFIX_LOG 0x38 -#define PCI_ERR_TLP_PREFIX_LOG_SIZE 16 - -#define PCI_SEC_STATUS_RCV_SYSTEM_ERROR 0x4000 - -/* aer root error command/status */ -#define PCI_ERR_ROOT_CMD_EN_MASK (PCI_ERR_ROOT_CMD_COR_EN | \ - PCI_ERR_ROOT_CMD_NONFATAL_EN | \ - PCI_ERR_ROOT_CMD_FATAL_EN) - -#define PCI_ERR_ROOT_IRQ_MAX 32 -#define PCI_ERR_ROOT_IRQ 0xf8000000 -#define PCI_ERR_ROOT_IRQ_SHIFT (ffs(PCI_ERR_ROOT_IRQ) - 1) -#define PCI_ERR_ROOT_STATUS_REPORT_MASK (PCI_ERR_ROOT_COR_RCV | \ - PCI_ERR_ROOT_MULTI_COR_RCV | \ - PCI_ERR_ROOT_UNCOR_RCV | \ - PCI_ERR_ROOT_MULTI_UNCOR_RCV | \ - PCI_ERR_ROOT_FIRST_FATAL | \ - PCI_ERR_ROOT_NONFATAL_RCV | \ - PCI_ERR_ROOT_FATAL_RCV) - -#define PCI_ERR_UNC_SUPPORTED (PCI_ERR_UNC_DLP | \ - PCI_ERR_UNC_SDN | \ - PCI_ERR_UNC_POISON_TLP | \ - PCI_ERR_UNC_FCP | \ - PCI_ERR_UNC_COMP_TIME | \ - PCI_ERR_UNC_COMP_ABORT | \ - PCI_ERR_UNC_UNX_COMP | \ - PCI_ERR_UNC_RX_OVER | \ - PCI_ERR_UNC_MALF_TLP | \ - PCI_ERR_UNC_ECRC | \ - PCI_ERR_UNC_UNSUP | \ - PCI_ERR_UNC_ACSV | \ - PCI_ERR_UNC_INTN | \ - PCI_ERR_UNC_MCBTLP | \ - PCI_ERR_UNC_ATOP_EBLOCKED | \ - PCI_ERR_UNC_TLP_PRF_BLOCKED) - -#define PCI_ERR_UNC_SEVERITY_DEFAULT (PCI_ERR_UNC_DLP | \ - PCI_ERR_UNC_SDN | \ - PCI_ERR_UNC_FCP | \ - PCI_ERR_UNC_RX_OVER | \ - PCI_ERR_UNC_MALF_TLP | \ - PCI_ERR_UNC_INTN) - -#define PCI_ERR_COR_SUPPORTED (PCI_ERR_COR_RCVR | \ - PCI_ERR_COR_BAD_TLP | \ - PCI_ERR_COR_BAD_DLLP | \ - PCI_ERR_COR_REP_ROLL | \ - PCI_ERR_COR_REP_TIMER | \ - PCI_ERR_COR_ADV_NONFATAL | \ - PCI_ERR_COR_INTERNAL | \ - PCI_ERR_COR_HL_OVERFLOW) - -#define PCI_ERR_COR_MASK_DEFAULT (PCI_ERR_COR_ADV_NONFATAL | \ - PCI_ERR_COR_INTERNAL | \ - PCI_ERR_COR_HL_OVERFLOW) - -#endif /* QEMU_PCIE_REGS_H */ diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index 8fe21235f0..cb7cf8fba5 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -10,7 +10,7 @@ obj-y += ppc_newworld.o # IBM pSeries (sPAPR) obj-$(CONFIG_PSERIES) += spapr.o spapr_hcall.o spapr_rtas.o spapr_vio.o obj-$(CONFIG_PSERIES) += xics.o spapr_vty.o spapr_llan.o spapr_vscsi.o -obj-$(CONFIG_PSERIES) += spapr_pci.o pci-hotplug.o spapr_iommu.o +obj-$(CONFIG_PSERIES) += spapr_pci.o pci/pci-hotplug.o spapr_iommu.o obj-$(CONFIG_PSERIES) += spapr_events.o # PowerPC 4xx boards obj-y += ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o diff --git a/hw/shpc.c b/hw/shpc.c deleted file mode 100644 index 4597bbde75..0000000000 --- a/hw/shpc.c +++ /dev/null @@ -1,681 +0,0 @@ -#include -#include -#include "range.h" -#include "range.h" -#include "shpc.h" -#include "pci.h" -#include "pci_internals.h" -#include "msi.h" - -/* TODO: model power only and disabled slot states. */ -/* TODO: handle SERR and wakeups */ -/* TODO: consider enabling 66MHz support */ - -/* TODO: remove fully only on state DISABLED and LED off. - * track state to properly record this. */ - -/* SHPC Working Register Set */ -#define SHPC_BASE_OFFSET 0x00 /* 4 bytes */ -#define SHPC_SLOTS_33 0x04 /* 4 bytes. Also encodes PCI-X slots. */ -#define SHPC_SLOTS_66 0x08 /* 4 bytes. */ -#define SHPC_NSLOTS 0x0C /* 1 byte */ -#define SHPC_FIRST_DEV 0x0D /* 1 byte */ -#define SHPC_PHYS_SLOT 0x0E /* 2 byte */ -#define SHPC_PHYS_NUM_MAX 0x7ff -#define SHPC_PHYS_NUM_UP 0x2000 -#define SHPC_PHYS_MRL 0x4000 -#define SHPC_PHYS_BUTTON 0x8000 -#define SHPC_SEC_BUS 0x10 /* 2 bytes */ -#define SHPC_SEC_BUS_33 0x0 -#define SHPC_SEC_BUS_66 0x1 /* Unused */ -#define SHPC_SEC_BUS_MASK 0x7 -#define SHPC_MSI_CTL 0x12 /* 1 byte */ -#define SHPC_PROG_IFC 0x13 /* 1 byte */ -#define SHPC_PROG_IFC_1_0 0x1 -#define SHPC_CMD_CODE 0x14 /* 1 byte */ -#define SHPC_CMD_TRGT 0x15 /* 1 byte */ -#define SHPC_CMD_TRGT_MIN 0x1 -#define SHPC_CMD_TRGT_MAX 0x1f -#define SHPC_CMD_STATUS 0x16 /* 2 bytes */ -#define SHPC_CMD_STATUS_BUSY 0x1 -#define SHPC_CMD_STATUS_MRL_OPEN 0x2 -#define SHPC_CMD_STATUS_INVALID_CMD 0x4 -#define SHPC_CMD_STATUS_INVALID_MODE 0x8 -#define SHPC_INT_LOCATOR 0x18 /* 4 bytes */ -#define SHPC_INT_COMMAND 0x1 -#define SHPC_SERR_LOCATOR 0x1C /* 4 bytes */ -#define SHPC_SERR_INT 0x20 /* 4 bytes */ -#define SHPC_INT_DIS 0x1 -#define SHPC_SERR_DIS 0x2 -#define SHPC_CMD_INT_DIS 0x4 -#define SHPC_ARB_SERR_DIS 0x8 -#define SHPC_CMD_DETECTED 0x10000 -#define SHPC_ARB_DETECTED 0x20000 - /* 4 bytes * slot # (start from 0) */ -#define SHPC_SLOT_REG(s) (0x24 + (s) * 4) - /* 2 bytes */ -#define SHPC_SLOT_STATUS(s) (0x0 + SHPC_SLOT_REG(s)) - -/* Same slot state masks are used for command and status registers */ -#define SHPC_SLOT_STATE_MASK 0x03 -#define SHPC_SLOT_STATE_SHIFT \ - (ffs(SHPC_SLOT_STATE_MASK) - 1) - -#define SHPC_STATE_NO 0x0 -#define SHPC_STATE_PWRONLY 0x1 -#define SHPC_STATE_ENABLED 0x2 -#define SHPC_STATE_DISABLED 0x3 - -#define SHPC_SLOT_PWR_LED_MASK 0xC -#define SHPC_SLOT_PWR_LED_SHIFT \ - (ffs(SHPC_SLOT_PWR_LED_MASK) - 1) -#define SHPC_SLOT_ATTN_LED_MASK 0x30 -#define SHPC_SLOT_ATTN_LED_SHIFT \ - (ffs(SHPC_SLOT_ATTN_LED_MASK) - 1) - -#define SHPC_LED_NO 0x0 -#define SHPC_LED_ON 0x1 -#define SHPC_LED_BLINK 0x2 -#define SHPC_LED_OFF 0x3 - -#define SHPC_SLOT_STATUS_PWR_FAULT 0x40 -#define SHPC_SLOT_STATUS_BUTTON 0x80 -#define SHPC_SLOT_STATUS_MRL_OPEN 0x100 -#define SHPC_SLOT_STATUS_66 0x200 -#define SHPC_SLOT_STATUS_PRSNT_MASK 0xC00 -#define SHPC_SLOT_STATUS_PRSNT_EMPTY 0x3 -#define SHPC_SLOT_STATUS_PRSNT_25W 0x1 -#define SHPC_SLOT_STATUS_PRSNT_15W 0x2 -#define SHPC_SLOT_STATUS_PRSNT_7_5W 0x0 - -#define SHPC_SLOT_STATUS_PRSNT_PCIX 0x3000 - - - /* 1 byte */ -#define SHPC_SLOT_EVENT_LATCH(s) (0x2 + SHPC_SLOT_REG(s)) - /* 1 byte */ -#define SHPC_SLOT_EVENT_SERR_INT_DIS(d, s) (0x3 + SHPC_SLOT_REG(s)) -#define SHPC_SLOT_EVENT_PRESENCE 0x01 -#define SHPC_SLOT_EVENT_ISOLATED_FAULT 0x02 -#define SHPC_SLOT_EVENT_BUTTON 0x04 -#define SHPC_SLOT_EVENT_MRL 0x08 -#define SHPC_SLOT_EVENT_CONNECTED_FAULT 0x10 -/* Bits below are used for Serr/Int disable only */ -#define SHPC_SLOT_EVENT_MRL_SERR_DIS 0x20 -#define SHPC_SLOT_EVENT_CONNECTED_FAULT_SERR_DIS 0x40 - -#define SHPC_MIN_SLOTS 1 -#define SHPC_MAX_SLOTS 31 -#define SHPC_SIZEOF(d) SHPC_SLOT_REG((d)->shpc->nslots) - -/* SHPC Slot identifiers */ - -/* Hotplug supported at 31 slots out of the total 32. We reserve slot 0, - and give the rest of them physical *and* pci numbers starting from 1, so - they match logical numbers. Note: this means that multiple slots must have - different chassis number values, to make chassis+physical slot unique. - TODO: make this configurable? */ -#define SHPC_IDX_TO_LOGICAL(slot) ((slot) + 1) -#define SHPC_LOGICAL_TO_IDX(target) ((target) - 1) -#define SHPC_IDX_TO_PCI(slot) ((slot) + 1) -#define SHPC_PCI_TO_IDX(pci_slot) ((pci_slot) - 1) -#define SHPC_IDX_TO_PHYSICAL(slot) ((slot) + 1) - -static int roundup_pow_of_two(int x) -{ - x |= (x >> 1); - x |= (x >> 2); - x |= (x >> 4); - x |= (x >> 8); - x |= (x >> 16); - return x + 1; -} - -static uint16_t shpc_get_status(SHPCDevice *shpc, int slot, uint16_t msk) -{ - uint8_t *status = shpc->config + SHPC_SLOT_STATUS(slot); - return (pci_get_word(status) & msk) >> (ffs(msk) - 1); -} - -static void shpc_set_status(SHPCDevice *shpc, - int slot, uint8_t value, uint16_t msk) -{ - uint8_t *status = shpc->config + SHPC_SLOT_STATUS(slot); - pci_word_test_and_clear_mask(status, msk); - pci_word_test_and_set_mask(status, value << (ffs(msk) - 1)); -} - -static void shpc_interrupt_update(PCIDevice *d) -{ - SHPCDevice *shpc = d->shpc; - int slot; - int level = 0; - uint32_t serr_int; - uint32_t int_locator = 0; - - /* Update interrupt locator register */ - for (slot = 0; slot < shpc->nslots; ++slot) { - uint8_t event = shpc->config[SHPC_SLOT_EVENT_LATCH(slot)]; - uint8_t disable = shpc->config[SHPC_SLOT_EVENT_SERR_INT_DIS(d, slot)]; - uint32_t mask = 1 << SHPC_IDX_TO_LOGICAL(slot); - if (event & ~disable) { - int_locator |= mask; - } - } - serr_int = pci_get_long(shpc->config + SHPC_SERR_INT); - if ((serr_int & SHPC_CMD_DETECTED) && !(serr_int & SHPC_CMD_INT_DIS)) { - int_locator |= SHPC_INT_COMMAND; - } - pci_set_long(shpc->config + SHPC_INT_LOCATOR, int_locator); - level = (!(serr_int & SHPC_INT_DIS) && int_locator) ? 1 : 0; - if (msi_enabled(d) && shpc->msi_requested != level) - msi_notify(d, 0); - else - qemu_set_irq(d->irq[0], level); - shpc->msi_requested = level; -} - -static void shpc_set_sec_bus_speed(SHPCDevice *shpc, uint8_t speed) -{ - switch (speed) { - case SHPC_SEC_BUS_33: - shpc->config[SHPC_SEC_BUS] &= ~SHPC_SEC_BUS_MASK; - shpc->config[SHPC_SEC_BUS] |= speed; - break; - default: - pci_word_test_and_set_mask(shpc->config + SHPC_CMD_STATUS, - SHPC_CMD_STATUS_INVALID_MODE); - } -} - -void shpc_reset(PCIDevice *d) -{ - SHPCDevice *shpc = d->shpc; - int nslots = shpc->nslots; - int i; - memset(shpc->config, 0, SHPC_SIZEOF(d)); - pci_set_byte(shpc->config + SHPC_NSLOTS, nslots); - pci_set_long(shpc->config + SHPC_SLOTS_33, nslots); - pci_set_long(shpc->config + SHPC_SLOTS_66, 0); - pci_set_byte(shpc->config + SHPC_FIRST_DEV, SHPC_IDX_TO_PCI(0)); - pci_set_word(shpc->config + SHPC_PHYS_SLOT, - SHPC_IDX_TO_PHYSICAL(0) | - SHPC_PHYS_NUM_UP | - SHPC_PHYS_MRL | - SHPC_PHYS_BUTTON); - pci_set_long(shpc->config + SHPC_SERR_INT, SHPC_INT_DIS | - SHPC_SERR_DIS | - SHPC_CMD_INT_DIS | - SHPC_ARB_SERR_DIS); - pci_set_byte(shpc->config + SHPC_PROG_IFC, SHPC_PROG_IFC_1_0); - pci_set_word(shpc->config + SHPC_SEC_BUS, SHPC_SEC_BUS_33); - for (i = 0; i < shpc->nslots; ++i) { - pci_set_byte(shpc->config + SHPC_SLOT_EVENT_SERR_INT_DIS(d, i), - SHPC_SLOT_EVENT_PRESENCE | - SHPC_SLOT_EVENT_ISOLATED_FAULT | - SHPC_SLOT_EVENT_BUTTON | - SHPC_SLOT_EVENT_MRL | - SHPC_SLOT_EVENT_CONNECTED_FAULT | - SHPC_SLOT_EVENT_MRL_SERR_DIS | - SHPC_SLOT_EVENT_CONNECTED_FAULT_SERR_DIS); - if (shpc->sec_bus->devices[PCI_DEVFN(SHPC_IDX_TO_PCI(i), 0)]) { - shpc_set_status(shpc, i, SHPC_STATE_ENABLED, SHPC_SLOT_STATE_MASK); - shpc_set_status(shpc, i, 0, SHPC_SLOT_STATUS_MRL_OPEN); - shpc_set_status(shpc, i, SHPC_SLOT_STATUS_PRSNT_7_5W, - SHPC_SLOT_STATUS_PRSNT_MASK); - shpc_set_status(shpc, i, SHPC_LED_ON, SHPC_SLOT_PWR_LED_MASK); - } else { - shpc_set_status(shpc, i, SHPC_STATE_DISABLED, SHPC_SLOT_STATE_MASK); - shpc_set_status(shpc, i, 1, SHPC_SLOT_STATUS_MRL_OPEN); - shpc_set_status(shpc, i, SHPC_SLOT_STATUS_PRSNT_EMPTY, - SHPC_SLOT_STATUS_PRSNT_MASK); - shpc_set_status(shpc, i, SHPC_LED_OFF, SHPC_SLOT_PWR_LED_MASK); - } - shpc_set_status(shpc, i, 0, SHPC_SLOT_STATUS_66); - } - shpc_set_sec_bus_speed(shpc, SHPC_SEC_BUS_33); - shpc->msi_requested = 0; - shpc_interrupt_update(d); -} - -static void shpc_invalid_command(SHPCDevice *shpc) -{ - pci_word_test_and_set_mask(shpc->config + SHPC_CMD_STATUS, - SHPC_CMD_STATUS_INVALID_CMD); -} - -static void shpc_free_devices_in_slot(SHPCDevice *shpc, int slot) -{ - int devfn; - int pci_slot = SHPC_IDX_TO_PCI(slot); - for (devfn = PCI_DEVFN(pci_slot, 0); - devfn <= PCI_DEVFN(pci_slot, PCI_FUNC_MAX - 1); - ++devfn) { - PCIDevice *affected_dev = shpc->sec_bus->devices[devfn]; - if (affected_dev) { - qdev_free(&affected_dev->qdev); - } - } -} - -static void shpc_slot_command(SHPCDevice *shpc, uint8_t target, - uint8_t state, uint8_t power, uint8_t attn) -{ - uint8_t current_state; - int slot = SHPC_LOGICAL_TO_IDX(target); - if (target < SHPC_CMD_TRGT_MIN || slot >= shpc->nslots) { - shpc_invalid_command(shpc); - return; - } - current_state = shpc_get_status(shpc, slot, SHPC_SLOT_STATE_MASK); - if (current_state == SHPC_STATE_ENABLED && state == SHPC_STATE_PWRONLY) { - shpc_invalid_command(shpc); - return; - } - - switch (power) { - case SHPC_LED_NO: - break; - default: - /* TODO: send event to monitor */ - shpc_set_status(shpc, slot, power, SHPC_SLOT_PWR_LED_MASK); - } - switch (attn) { - case SHPC_LED_NO: - break; - default: - /* TODO: send event to monitor */ - shpc_set_status(shpc, slot, attn, SHPC_SLOT_ATTN_LED_MASK); - } - - if ((current_state == SHPC_STATE_DISABLED && state == SHPC_STATE_PWRONLY) || - (current_state == SHPC_STATE_DISABLED && state == SHPC_STATE_ENABLED)) { - shpc_set_status(shpc, slot, state, SHPC_SLOT_STATE_MASK); - } else if ((current_state == SHPC_STATE_ENABLED || - current_state == SHPC_STATE_PWRONLY) && - state == SHPC_STATE_DISABLED) { - shpc_set_status(shpc, slot, state, SHPC_SLOT_STATE_MASK); - power = shpc_get_status(shpc, slot, SHPC_SLOT_PWR_LED_MASK); - /* TODO: track what monitor requested. */ - /* Look at LED to figure out whether it's ok to remove the device. */ - if (power == SHPC_LED_OFF) { - shpc_free_devices_in_slot(shpc, slot); - shpc_set_status(shpc, slot, 1, SHPC_SLOT_STATUS_MRL_OPEN); - shpc_set_status(shpc, slot, SHPC_SLOT_STATUS_PRSNT_EMPTY, - SHPC_SLOT_STATUS_PRSNT_MASK); - shpc->config[SHPC_SLOT_EVENT_LATCH(slot)] |= - SHPC_SLOT_EVENT_BUTTON | - SHPC_SLOT_EVENT_MRL | - SHPC_SLOT_EVENT_PRESENCE; - } - } -} - -static void shpc_command(SHPCDevice *shpc) -{ - uint8_t code = pci_get_byte(shpc->config + SHPC_CMD_CODE); - uint8_t speed; - uint8_t target; - uint8_t attn; - uint8_t power; - uint8_t state; - int i; - - /* Clear status from the previous command. */ - pci_word_test_and_clear_mask(shpc->config + SHPC_CMD_STATUS, - SHPC_CMD_STATUS_BUSY | - SHPC_CMD_STATUS_MRL_OPEN | - SHPC_CMD_STATUS_INVALID_CMD | - SHPC_CMD_STATUS_INVALID_MODE); - switch (code) { - case 0x00 ... 0x3f: - target = shpc->config[SHPC_CMD_TRGT] & SHPC_CMD_TRGT_MAX; - state = (code & SHPC_SLOT_STATE_MASK) >> SHPC_SLOT_STATE_SHIFT; - power = (code & SHPC_SLOT_PWR_LED_MASK) >> SHPC_SLOT_PWR_LED_SHIFT; - attn = (code & SHPC_SLOT_ATTN_LED_MASK) >> SHPC_SLOT_ATTN_LED_SHIFT; - shpc_slot_command(shpc, target, state, power, attn); - break; - case 0x40 ... 0x47: - speed = code & SHPC_SEC_BUS_MASK; - shpc_set_sec_bus_speed(shpc, speed); - break; - case 0x48: - /* Power only all slots */ - /* first verify no slots are enabled */ - for (i = 0; i < shpc->nslots; ++i) { - state = shpc_get_status(shpc, i, SHPC_SLOT_STATE_MASK); - if (state == SHPC_STATE_ENABLED) { - shpc_invalid_command(shpc); - goto done; - } - } - for (i = 0; i < shpc->nslots; ++i) { - if (!(shpc_get_status(shpc, i, SHPC_SLOT_STATUS_MRL_OPEN))) { - shpc_slot_command(shpc, i + SHPC_CMD_TRGT_MIN, - SHPC_STATE_PWRONLY, SHPC_LED_ON, SHPC_LED_NO); - } else { - shpc_slot_command(shpc, i + SHPC_CMD_TRGT_MIN, - SHPC_STATE_NO, SHPC_LED_OFF, SHPC_LED_NO); - } - } - break; - case 0x49: - /* Enable all slots */ - /* TODO: Spec says this shall fail if some are already enabled. - * This doesn't make sense - why not? a spec bug? */ - for (i = 0; i < shpc->nslots; ++i) { - state = shpc_get_status(shpc, i, SHPC_SLOT_STATE_MASK); - if (state == SHPC_STATE_ENABLED) { - shpc_invalid_command(shpc); - goto done; - } - } - for (i = 0; i < shpc->nslots; ++i) { - if (!(shpc_get_status(shpc, i, SHPC_SLOT_STATUS_MRL_OPEN))) { - shpc_slot_command(shpc, i + SHPC_CMD_TRGT_MIN, - SHPC_STATE_ENABLED, SHPC_LED_ON, SHPC_LED_NO); - } else { - shpc_slot_command(shpc, i + SHPC_CMD_TRGT_MIN, - SHPC_STATE_NO, SHPC_LED_OFF, SHPC_LED_NO); - } - } - break; - default: - shpc_invalid_command(shpc); - break; - } -done: - pci_long_test_and_set_mask(shpc->config + SHPC_SERR_INT, SHPC_CMD_DETECTED); -} - -static void shpc_write(PCIDevice *d, unsigned addr, uint64_t val, int l) -{ - SHPCDevice *shpc = d->shpc; - int i; - if (addr >= SHPC_SIZEOF(d)) { - return; - } - l = MIN(l, SHPC_SIZEOF(d) - addr); - - /* TODO: code duplicated from pci.c */ - for (i = 0; i < l; val >>= 8, ++i) { - unsigned a = addr + i; - uint8_t wmask = shpc->wmask[a]; - uint8_t w1cmask = shpc->w1cmask[a]; - assert(!(wmask & w1cmask)); - shpc->config[a] = (shpc->config[a] & ~wmask) | (val & wmask); - shpc->config[a] &= ~(val & w1cmask); /* W1C: Write 1 to Clear */ - } - if (ranges_overlap(addr, l, SHPC_CMD_CODE, 2)) { - shpc_command(shpc); - } - shpc_interrupt_update(d); -} - -static uint64_t shpc_read(PCIDevice *d, unsigned addr, int l) -{ - uint64_t val = 0x0; - if (addr >= SHPC_SIZEOF(d)) { - return val; - } - l = MIN(l, SHPC_SIZEOF(d) - addr); - memcpy(&val, d->shpc->config + addr, l); - return val; -} - -/* SHPC Bridge Capability */ -#define SHPC_CAP_LENGTH 0x08 -#define SHPC_CAP_DWORD_SELECT 0x2 /* 1 byte */ -#define SHPC_CAP_CxP 0x3 /* 1 byte: CSP, CIP */ -#define SHPC_CAP_DWORD_DATA 0x4 /* 4 bytes */ -#define SHPC_CAP_CSP_MASK 0x4 -#define SHPC_CAP_CIP_MASK 0x8 - -static uint8_t shpc_cap_dword(PCIDevice *d) -{ - return pci_get_byte(d->config + d->shpc->cap + SHPC_CAP_DWORD_SELECT); -} - -/* Update dword data capability register */ -static void shpc_cap_update_dword(PCIDevice *d) -{ - unsigned data; - data = shpc_read(d, shpc_cap_dword(d) * 4, 4); - pci_set_long(d->config + d->shpc->cap + SHPC_CAP_DWORD_DATA, data); -} - -/* Add SHPC capability to the config space for the device. */ -static int shpc_cap_add_config(PCIDevice *d) -{ - uint8_t *config; - int config_offset; - config_offset = pci_add_capability(d, PCI_CAP_ID_SHPC, - 0, SHPC_CAP_LENGTH); - if (config_offset < 0) { - return config_offset; - } - config = d->config + config_offset; - - pci_set_byte(config + SHPC_CAP_DWORD_SELECT, 0); - pci_set_byte(config + SHPC_CAP_CxP, 0); - pci_set_long(config + SHPC_CAP_DWORD_DATA, 0); - d->shpc->cap = config_offset; - /* Make dword select and data writeable. */ - pci_set_byte(d->wmask + config_offset + SHPC_CAP_DWORD_SELECT, 0xff); - pci_set_long(d->wmask + config_offset + SHPC_CAP_DWORD_DATA, 0xffffffff); - return 0; -} - -static uint64_t shpc_mmio_read(void *opaque, hwaddr addr, - unsigned size) -{ - return shpc_read(opaque, addr, size); -} - -static void shpc_mmio_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - shpc_write(opaque, addr, val, size); -} - -static const MemoryRegionOps shpc_mmio_ops = { - .read = shpc_mmio_read, - .write = shpc_mmio_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - /* SHPC ECN requires dword accesses, but the original 1.0 spec doesn't. - * It's easier to suppport all sizes than worry about it. */ - .min_access_size = 1, - .max_access_size = 4, - }, -}; - -static int shpc_device_hotplug(DeviceState *qdev, PCIDevice *affected_dev, - PCIHotplugState hotplug_state) -{ - int pci_slot = PCI_SLOT(affected_dev->devfn); - uint8_t state; - uint8_t led; - PCIDevice *d = DO_UPCAST(PCIDevice, qdev, qdev); - SHPCDevice *shpc = d->shpc; - int slot = SHPC_PCI_TO_IDX(pci_slot); - if (pci_slot < SHPC_IDX_TO_PCI(0) || slot >= shpc->nslots) { - error_report("Unsupported PCI slot %d for standard hotplug " - "controller. Valid slots are between %d and %d.", - pci_slot, SHPC_IDX_TO_PCI(0), - SHPC_IDX_TO_PCI(shpc->nslots) - 1); - return -1; - } - /* Don't send event when device is enabled during qemu machine creation: - * it is present on boot, no hotplug event is necessary. We do send an - * event when the device is disabled later. */ - if (hotplug_state == PCI_COLDPLUG_ENABLED) { - shpc_set_status(shpc, slot, 0, SHPC_SLOT_STATUS_MRL_OPEN); - shpc_set_status(shpc, slot, SHPC_SLOT_STATUS_PRSNT_7_5W, - SHPC_SLOT_STATUS_PRSNT_MASK); - return 0; - } - if (hotplug_state == PCI_HOTPLUG_DISABLED) { - shpc->config[SHPC_SLOT_EVENT_LATCH(slot)] |= SHPC_SLOT_EVENT_BUTTON; - state = shpc_get_status(shpc, slot, SHPC_SLOT_STATE_MASK); - led = shpc_get_status(shpc, slot, SHPC_SLOT_PWR_LED_MASK); - if (state == SHPC_STATE_DISABLED && led == SHPC_LED_OFF) { - shpc_free_devices_in_slot(shpc, slot); - shpc_set_status(shpc, slot, 1, SHPC_SLOT_STATUS_MRL_OPEN); - shpc_set_status(shpc, slot, SHPC_SLOT_STATUS_PRSNT_EMPTY, - SHPC_SLOT_STATUS_PRSNT_MASK); - shpc->config[SHPC_SLOT_EVENT_LATCH(slot)] |= - SHPC_SLOT_EVENT_MRL | - SHPC_SLOT_EVENT_PRESENCE; - } - } else { - /* This could be a cancellation of the previous removal. - * We check MRL state to figure out. */ - if (shpc_get_status(shpc, slot, SHPC_SLOT_STATUS_MRL_OPEN)) { - shpc_set_status(shpc, slot, 0, SHPC_SLOT_STATUS_MRL_OPEN); - shpc_set_status(shpc, slot, SHPC_SLOT_STATUS_PRSNT_7_5W, - SHPC_SLOT_STATUS_PRSNT_MASK); - shpc->config[SHPC_SLOT_EVENT_LATCH(slot)] |= - SHPC_SLOT_EVENT_BUTTON | - SHPC_SLOT_EVENT_MRL | - SHPC_SLOT_EVENT_PRESENCE; - } else { - /* Press attention button to cancel removal */ - shpc->config[SHPC_SLOT_EVENT_LATCH(slot)] |= - SHPC_SLOT_EVENT_BUTTON; - } - } - shpc_set_status(shpc, slot, 0, SHPC_SLOT_STATUS_66); - shpc_interrupt_update(d); - return 0; -} - -/* Initialize the SHPC structure in bridge's BAR. */ -int shpc_init(PCIDevice *d, PCIBus *sec_bus, MemoryRegion *bar, unsigned offset) -{ - int i, ret; - int nslots = SHPC_MAX_SLOTS; /* TODO: qdev property? */ - SHPCDevice *shpc = d->shpc = g_malloc0(sizeof(*d->shpc)); - shpc->sec_bus = sec_bus; - ret = shpc_cap_add_config(d); - if (ret) { - g_free(d->shpc); - return ret; - } - if (nslots < SHPC_MIN_SLOTS) { - return 0; - } - if (nslots > SHPC_MAX_SLOTS || - SHPC_IDX_TO_PCI(nslots) > PCI_SLOT_MAX) { - /* TODO: report an error mesage that makes sense. */ - return -EINVAL; - } - shpc->nslots = nslots; - shpc->config = g_malloc0(SHPC_SIZEOF(d)); - shpc->cmask = g_malloc0(SHPC_SIZEOF(d)); - shpc->wmask = g_malloc0(SHPC_SIZEOF(d)); - shpc->w1cmask = g_malloc0(SHPC_SIZEOF(d)); - - shpc_reset(d); - - pci_set_long(shpc->config + SHPC_BASE_OFFSET, offset); - - pci_set_byte(shpc->wmask + SHPC_CMD_CODE, 0xff); - pci_set_byte(shpc->wmask + SHPC_CMD_TRGT, SHPC_CMD_TRGT_MAX); - pci_set_byte(shpc->wmask + SHPC_CMD_TRGT, SHPC_CMD_TRGT_MAX); - pci_set_long(shpc->wmask + SHPC_SERR_INT, - SHPC_INT_DIS | - SHPC_SERR_DIS | - SHPC_CMD_INT_DIS | - SHPC_ARB_SERR_DIS); - pci_set_long(shpc->w1cmask + SHPC_SERR_INT, - SHPC_CMD_DETECTED | - SHPC_ARB_DETECTED); - for (i = 0; i < nslots; ++i) { - pci_set_byte(shpc->wmask + - SHPC_SLOT_EVENT_SERR_INT_DIS(d, i), - SHPC_SLOT_EVENT_PRESENCE | - SHPC_SLOT_EVENT_ISOLATED_FAULT | - SHPC_SLOT_EVENT_BUTTON | - SHPC_SLOT_EVENT_MRL | - SHPC_SLOT_EVENT_CONNECTED_FAULT | - SHPC_SLOT_EVENT_MRL_SERR_DIS | - SHPC_SLOT_EVENT_CONNECTED_FAULT_SERR_DIS); - pci_set_byte(shpc->w1cmask + - SHPC_SLOT_EVENT_LATCH(i), - SHPC_SLOT_EVENT_PRESENCE | - SHPC_SLOT_EVENT_ISOLATED_FAULT | - SHPC_SLOT_EVENT_BUTTON | - SHPC_SLOT_EVENT_MRL | - SHPC_SLOT_EVENT_CONNECTED_FAULT); - } - - /* TODO: init cmask */ - memory_region_init_io(&shpc->mmio, &shpc_mmio_ops, d, "shpc-mmio", - SHPC_SIZEOF(d)); - shpc_cap_update_dword(d); - memory_region_add_subregion(bar, offset, &shpc->mmio); - pci_bus_hotplug(sec_bus, shpc_device_hotplug, &d->qdev); - - d->cap_present |= QEMU_PCI_CAP_SHPC; - return 0; -} - -int shpc_bar_size(PCIDevice *d) -{ - return roundup_pow_of_two(SHPC_SLOT_REG(SHPC_MAX_SLOTS)); -} - -void shpc_cleanup(PCIDevice *d, MemoryRegion *bar) -{ - SHPCDevice *shpc = d->shpc; - d->cap_present &= ~QEMU_PCI_CAP_SHPC; - memory_region_del_subregion(bar, &shpc->mmio); - /* TODO: cleanup config space changes? */ - g_free(shpc->config); - g_free(shpc->cmask); - g_free(shpc->wmask); - g_free(shpc->w1cmask); - memory_region_destroy(&shpc->mmio); - g_free(shpc); -} - -void shpc_cap_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l) -{ - if (!ranges_overlap(addr, l, d->shpc->cap, SHPC_CAP_LENGTH)) { - return; - } - if (ranges_overlap(addr, l, d->shpc->cap + SHPC_CAP_DWORD_DATA, 4)) { - unsigned dword_data; - dword_data = pci_get_long(d->shpc->config + d->shpc->cap - + SHPC_CAP_DWORD_DATA); - shpc_write(d, shpc_cap_dword(d) * 4, dword_data, 4); - } - /* Update cap dword data in case guest is going to read it. */ - shpc_cap_update_dword(d); -} - -static void shpc_save(QEMUFile *f, void *pv, size_t size) -{ - PCIDevice *d = container_of(pv, PCIDevice, shpc); - qemu_put_buffer(f, d->shpc->config, SHPC_SIZEOF(d)); -} - -static int shpc_load(QEMUFile *f, void *pv, size_t size) -{ - PCIDevice *d = container_of(pv, PCIDevice, shpc); - int ret = qemu_get_buffer(f, d->shpc->config, SHPC_SIZEOF(d)); - if (ret != SHPC_SIZEOF(d)) { - return -EINVAL; - } - /* Make sure we don't lose notifications. An extra interrupt is harmless. */ - d->shpc->msi_requested = 0; - shpc_interrupt_update(d); - return 0; -} - -VMStateInfo shpc_vmstate_info = { - .name = "shpc", - .get = shpc_load, - .put = shpc_save, -}; diff --git a/hw/shpc.h b/hw/shpc.h deleted file mode 100644 index 130b71df30..0000000000 --- a/hw/shpc.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef SHPC_H -#define SHPC_H - -#include "qemu-common.h" -#include "memory.h" -#include "vmstate.h" - -struct SHPCDevice { - /* Capability offset in device's config space */ - int cap; - - /* # of hot-pluggable slots */ - int nslots; - - /* SHPC WRS: working register set */ - uint8_t *config; - - /* Used to enable checks on load. Note that writable bits are - * never checked even if set in cmask. */ - uint8_t *cmask; - - /* Used to implement R/W bytes */ - uint8_t *wmask; - - /* Used to implement RW1C(Write 1 to Clear) bytes */ - uint8_t *w1cmask; - - /* MMIO for the SHPC BAR */ - MemoryRegion mmio; - - /* Bus controlled by this SHPC */ - PCIBus *sec_bus; - - /* MSI already requested for this event */ - int msi_requested; -}; - -void shpc_reset(PCIDevice *d); -int shpc_bar_size(PCIDevice *dev); -int shpc_init(PCIDevice *dev, PCIBus *sec_bus, MemoryRegion *bar, unsigned off); -void shpc_cleanup(PCIDevice *dev, MemoryRegion *bar); -void shpc_cap_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int len); - -extern VMStateInfo shpc_vmstate_info; -#define SHPC_VMSTATE(_field, _type) \ - VMSTATE_BUFFER_UNSAFE_INFO(_field, _type, 0, shpc_vmstate_info, 0) - -#endif diff --git a/hw/slotid_cap.c b/hw/slotid_cap.c deleted file mode 100644 index 01064521a9..0000000000 --- a/hw/slotid_cap.c +++ /dev/null @@ -1,44 +0,0 @@ -#include "slotid_cap.h" -#include "pci.h" - -#define SLOTID_CAP_LENGTH 4 -#define SLOTID_NSLOTS_SHIFT (ffs(PCI_SID_ESR_NSLOTS) - 1) - -int slotid_cap_init(PCIDevice *d, int nslots, - uint8_t chassis, - unsigned offset) -{ - int cap; - if (!chassis) { - error_report("Bridge chassis not specified. Each bridge is required " - "to be assigned a unique chassis id > 0."); - return -EINVAL; - } - if (nslots < 0 || nslots > (PCI_SID_ESR_NSLOTS >> SLOTID_NSLOTS_SHIFT)) { - /* TODO: error report? */ - return -EINVAL; - } - - cap = pci_add_capability(d, PCI_CAP_ID_SLOTID, offset, SLOTID_CAP_LENGTH); - if (cap < 0) { - return cap; - } - /* We make each chassis unique, this way each bridge is First in Chassis */ - d->config[cap + PCI_SID_ESR] = PCI_SID_ESR_FIC | - (nslots << SLOTID_NSLOTS_SHIFT); - d->cmask[cap + PCI_SID_ESR] = 0xff; - d->config[cap + PCI_SID_CHASSIS_NR] = chassis; - /* Note: Chassis number register is non-volatile, - so we don't reset it. */ - /* TODO: store in eeprom? */ - d->wmask[cap + PCI_SID_CHASSIS_NR] = 0xff; - - d->cap_present |= QEMU_PCI_CAP_SLOTID; - return 0; -} - -void slotid_cap_cleanup(PCIDevice *d) -{ - /* TODO: cleanup config space? */ - d->cap_present &= ~QEMU_PCI_CAP_SLOTID; -} diff --git a/hw/slotid_cap.h b/hw/slotid_cap.h deleted file mode 100644 index 70db0470b0..0000000000 --- a/hw/slotid_cap.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef PCI_SLOTID_CAP_H -#define PCI_SLOTID_CAP_H - -#include "qemu-common.h" - -int slotid_cap_init(PCIDevice *dev, int nslots, - uint8_t chassis, - unsigned offset); -void slotid_cap_cleanup(PCIDevice *dev); - -#endif -- cgit v1.2.1