Merge commit '1dd3a74d2e' into HEAD

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Paolo Bonzini 2012-12-17 18:17:08 +01:00
commit 6f991980a5
138 changed files with 293 additions and 290 deletions

9
hw/pci/Makefile.objs Normal file
View file

@ -0,0 +1,9 @@
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
extra-obj-y += pci-stub.o

395
hw/pci/msi.c Normal file
View file

@ -0,0 +1,395 @@
/*
* msi.c
*
* Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
* 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 <http://www.gnu.org/licenses/>.
*/
#include "hw/pci/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);
}

50
hw/pci/msi.h Normal file
View file

@ -0,0 +1,50 @@
/*
* msi.h
*
* Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef QEMU_MSI_H
#define QEMU_MSI_H
#include "qemu-common.h"
#include "hw/pci/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 */

560
hw/pci/msix.c Normal file
View file

@ -0,0 +1,560 @@
/*
* MSI-X device support
*
* This module includes support for MSI-X in pci devices.
*
* Author: Michael S. Tsirkin <mst@redhat.com>
*
* 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/hw.h"
#include "hw/pci/msi.h"
#include "hw/pci/msix.h"
#include "hw/pci/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,
.endianness = DEVICE_LITTLE_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,
.endianness = DEVICE_LITTLE_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;
}

41
hw/pci/msix.h Normal file
View file

@ -0,0 +1,41 @@
#ifndef QEMU_MSIX_H
#define QEMU_MSIX_H
#include "qemu-common.h"
#include "hw/pci/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

292
hw/pci/pci-hotplug.c Normal file
View file

@ -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/hw.h"
#include "hw/boards.h"
#include "hw/pci/pci.h"
#include "net.h"
#include "hw/pc.h"
#include "monitor.h"
#include "hw/scsi.h"
#include "hw/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"));
}

47
hw/pci/pci-stub.c Normal file
View file

@ -0,0 +1,47 @@
/*
* PCI stubs for platforms that don't support pci bus.
*
* Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
* 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 <http://www.gnu.org/licenses/>.
*/
#include "sysemu.h"
#include "monitor.h"
#include "hw/pci/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);
}

2168
hw/pci/pci.c Normal file

File diff suppressed because it is too large Load diff

684
hw/pci/pci.h Normal file
View file

@ -0,0 +1,684 @@
#ifndef QEMU_PCI_H
#define QEMU_PCI_H
#include "qemu-common.h"
#include "hw/qdev.h"
#include "memory.h"
#include "dma.h"
/* PCI includes legacy ISA access. */
#include "hw/isa.h"
#include "hw/pci/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 "hw/pci/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 "hw/pci/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

363
hw/pci/pci_bridge.c Normal file
View file

@ -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 <yamahata at valinux co jp>
* VA Linux Systems Japan K.K.
*/
#include "hw/pci/pci_bridge.h"
#include "hw/pci/pci_bus.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 <id>.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;
}

66
hw/pci/pci_bridge.h Normal file
View file

@ -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 <yamahata at valinux co jp>
* VA Linux Systems Japan K.K.
*
*/
#ifndef QEMU_PCI_BRIDGE_H
#define QEMU_PCI_BRIDGE_H
#include "hw/pci/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:
*/

74
hw/pci/pci_bus.h Normal file
View file

@ -0,0 +1,74 @@
#ifndef QEMU_PCI_BUS_H
#define QEMU_PCI_BUS_H
/*
* PCI Bus and Bridge datastructures.
*
* Do not access the following members directly;
* use accessor functions 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_BUS_H */

180
hw/pci/pci_host.c Normal file
View file

@ -0,0 +1,180 @@
/*
* pci_host.c
*
* Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp>
* 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 <http://www.gnu.org/licenses/>.
*/
#include "hw/pci/pci.h"
#include "hw/pci/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)

62
hw/pci/pci_host.h Normal file
View file

@ -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 "hw/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 */

147
hw/pci/pci_ids.h Normal file
View file

@ -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

717
hw/pci/pci_regs.h Normal file
View file

@ -0,0 +1,717 @@
/*
* pci_regs.h
*
* PCI standard defines
* Copyright 1994, Drew Eckhardt
* Copyright 1997--1999 Martin Mares <mj@ucw.cz>
*
* 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 */

555
hw/pci/pcie.c Normal file
View file

@ -0,0 +1,555 @@
/*
* pcie.c
*
* Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
* 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 <http://www.gnu.org/licenses/>.
*/
#include "qemu-common.h"
#include "hw/pci/pci_bridge.h"
#include "hw/pci/pcie.h"
#include "hw/pci/msix.h"
#include "hw/pci/msi.h"
#include "hw/pci/pci_bus.h"
#include "hw/pci/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 Ports 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));
}

142
hw/pci/pcie.h Normal file
View file

@ -0,0 +1,142 @@
/*
* pcie.h
*
* Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef QEMU_PCIE_H
#define QEMU_PCIE_H
#include "hw/hw.h"
#include "hw/pci/pci_regs.h"
#include "hw/pci/pcie_regs.h"
#include "hw/pci/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 */

1032
hw/pci/pcie_aer.c Normal file

File diff suppressed because it is too large Load diff

106
hw/pci/pcie_aer.h Normal file
View file

@ -0,0 +1,106 @@
/*
* pcie_aer.h
*
* Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef QEMU_PCIE_AER_H
#define QEMU_PCIE_AER_H
#include "hw/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 */

161
hw/pci/pcie_host.c Normal file
View file

@ -0,0 +1,161 @@
/*
* pcie_host.c
* utility functions for pci express host bridge.
*
* Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp>
* 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 <http://www.gnu.org/licenses/>.
*/
#include "hw/hw.h"
#include "hw/pci/pci.h"
#include "hw/pci/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)

54
hw/pci/pcie_host.h Normal file
View file

@ -0,0 +1,54 @@
/*
* pcie_host.h
*
* Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp>
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef PCIE_HOST_H
#define PCIE_HOST_H
#include "hw/pci/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 */

114
hw/pci/pcie_port.c Normal file
View file

@ -0,0 +1,114 @@
/*
* pcie_port.c
*
* Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
* 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 <http://www.gnu.org/licenses/>.
*/
#include "hw/pci/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);
}

51
hw/pci/pcie_port.h Normal file
View file

@ -0,0 +1,51 @@
/*
* pcie_port.h
*
* Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef QEMU_PCIE_PORT_H
#define QEMU_PCIE_PORT_H
#include "hw/pci/pci_bridge.h"
#include "hw/pci/pci_bus.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 */

156
hw/pci/pcie_regs.h Normal file
View file

@ -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 */

681
hw/pci/shpc.c Normal file
View file

@ -0,0 +1,681 @@
#include <strings.h>
#include <stdint.h>
#include "range.h"
#include "range.h"
#include "hw/pci/shpc.h"
#include "hw/pci/pci.h"
#include "hw/pci/pci_bus.h"
#include "hw/pci/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,
};

48
hw/pci/shpc.h Normal file
View file

@ -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

44
hw/pci/slotid_cap.c Normal file
View file

@ -0,0 +1,44 @@
#include "hw/pci/slotid_cap.h"
#include "hw/pci/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;
}

11
hw/pci/slotid_cap.h Normal file
View file

@ -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