[PATCH 23/28] kvm tools: Endian-sanitise pci.h and PCI device setup

Matt Evans
2011-12-06T03:42:14Z

vesa, pci-shmem and virtio-pci devices need to set up config space with
little-endian conversions (as config space is LE). The pci_config_address
bitfield also needs to be reversed when building on BE systems.

Signed-off-by: Matt Evans <matt@ozlabs.org>
---
tools/kvm/hw/pci-shmem.c | 23 +++++++++++----------
tools/kvm/hw/vesa.c | 15 +++++++------
tools/kvm/include/kvm/ioport.h | 11 +++++----
tools/kvm/include/kvm/pci.h | 24 +++++++++++++++++-----
tools/kvm/pci.c | 4 +-
tools/kvm/virtio/pci.c | 41 +++++++++++++++++++++------------------
6 files changed, 68 insertions(+), 50 deletions(-)

diff --git a/tools/kvm/hw/pci-shmem.c b/tools/kvm/hw/pci-shmem.c
index 780a377..fd954c5 100644
--- a/tools/kvm/hw/pci-shmem.c
+++ b/tools/kvm/hw/pci-shmem.c
@@ -8,21 +8,22 @@
#include "kvm/ioeventfd.h"

#include <linux/kvm.h>
+#include <linux/byteorder.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <sys/mman.h>

static struct pci_device_header pci_shmem_pci_device = {
- .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET,
- .device_id = 0x1110,
+ .vendor_id = cpu_to_le16(PCI_VENDOR_ID_REDHAT_QUMRANET),
+ .device_id = cpu_to_le16(0x1110),
.header_type = PCI_HEADER_TYPE_NORMAL,
- .class = 0xFF0000, /* misc pci device */
- .status = PCI_STATUS_CAP_LIST,
+ .class[2] = 0xFF, /* misc pci device */
+ .status = cpu_to_le16(PCI_STATUS_CAP_LIST),
.capabilities = (void *)&pci_shmem_pci_device.msix - (void *)&pci_shmem_pci_device,
.msix.cap = PCI_CAP_ID_MSIX,
- .msix.ctrl = 1,
- .msix.table_offset = 1, /* Use BAR 1 */
- .msix.pba_offset = 0x1001, /* Use BAR 1 */
+ .msix.ctrl = cpu_to_le16(1),
+ .msix.table_offset = cpu_to_le32(1), /* Use BAR 1 */
+ .msix.pba_offset = cpu_to_le32(0x1001), /* Use BAR 1 */
};

/* registers for the Inter-VM shared memory device */
@@ -123,7 +124,7 @@ int pci_shmem__get_local_irqfd(struct kvm *kvm)
if (fd < 0)
return fd;

- if (pci_shmem_pci_device.msix.ctrl & PCI_MSIX_FLAGS_ENABLE) {
+ if (pci_shmem_pci_device.msix.ctrl & cpu_to_le16(PCI_MSIX_FLAGS_ENABLE)) {
gsi = irq__add_msix_route(kvm, &msix_table[0].msg);
} else {
gsi = pci_shmem_pci_device.irq_line;
@@ -241,11 +242,11 @@ int pci_shmem__init(struct kvm *kvm)
* 1 - MSI-X MMIO space
* 2 - Shared memory block
*/
- pci_shmem_pci_device.bar[0] = ivshmem_registers | PCI_BASE_ADDRESS_SPACE_IO;
+ pci_shmem_pci_device.bar[0] = cpu_to_le32(ivshmem_registers | PCI_BASE_ADDRESS_SPACE_IO);
pci_shmem_pci_device.bar_size[0] = shmem_region->size;
- pci_shmem_pci_device.bar[1] = msix_block | PCI_BASE_ADDRESS_SPACE_MEMORY;
+ pci_shmem_pci_device.bar[1] = cpu_to_le32(msix_block | PCI_BASE_ADDRESS_SPACE_MEMORY);
pci_shmem_pci_device.bar_size[1] = 0x1010;
- pci_shmem_pci_device.bar[2] = shmem_region->phys_addr | PCI_BASE_ADDRESS_SPACE_MEMORY;
+ pci_shmem_pci_device.bar[2] = cpu_to_le32(shmem_region->phys_addr | PCI_BASE_ADDRESS_SPACE_MEMORY);
pci_shmem_pci_device.bar_size[2] = shmem_region->size;

pci__register(&pci_shmem_pci_device, dev);
diff --git a/tools/kvm/hw/vesa.c b/tools/kvm/hw/vesa.c
index 22b1652..63f1082 100644
--- a/tools/kvm/hw/vesa.c
+++ b/tools/kvm/hw/vesa.c
@@ -8,6 +8,7 @@
#include "kvm/irq.h"
#include "kvm/kvm.h"
#include "kvm/pci.h"
+#include <linux/byteorder.h>
#include <sys/mman.h>

#include <sys/types.h>
@@ -31,14 +32,14 @@ static struct ioport_operations vesa_io_ops = {
};

static struct pci_device_header vesa_pci_device = {
- .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET,
- .device_id = PCI_DEVICE_ID_VESA,
+ .vendor_id = cpu_to_le16(PCI_VENDOR_ID_REDHAT_QUMRANET),
+ .device_id = cpu_to_le16(PCI_DEVICE_ID_VESA),
.header_type = PCI_HEADER_TYPE_NORMAL,
.revision_id = 0,
- .class = 0x030000,
- .subsys_vendor_id = PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
- .subsys_id = PCI_SUBSYSTEM_ID_VESA,
- .bar[1] = VESA_MEM_ADDR | PCI_BASE_ADDRESS_SPACE_MEMORY,
+ .class[2] = 0x03,
+ .subsys_vendor_id = cpu_to_le16(PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET),
+ .subsys_id = cpu_to_le16(PCI_SUBSYSTEM_ID_VESA),
+ .bar[1] = cpu_to_le32(VESA_MEM_ADDR | PCI_BASE_ADDRESS_SPACE_MEMORY),
.bar_size[1] = VESA_MEM_SIZE,
};

@@ -56,7 +57,7 @@ struct framebuffer *vesa__init(struct kvm *kvm)
vesa_pci_device.irq_pin = pin;
vesa_pci_device.irq_line = line;
vesa_base_addr = ioport__register(IOPORT_EMPTY, &vesa_io_ops, IOPORT_SIZE, NULL);
- vesa_pci_device.bar[0] = vesa_base_addr | PCI_BASE_ADDRESS_SPACE_IO;
+ vesa_pci_device.bar[0] = cpu_to_le32(vesa_base_addr | PCI_BASE_ADDRESS_SPACE_IO);
pci__register(&vesa_pci_device, dev);

mem = mmap(NULL, VESA_MEM_SIZE, PROT_RW, MAP_ANON_NORESERVE, -1, 0);
diff --git a/tools/kvm/include/kvm/ioport.h b/tools/kvm/include/kvm/ioport.h
index 61a70ec..09bf876 100644
--- a/tools/kvm/include/kvm/ioport.h
+++ b/tools/kvm/include/kvm/ioport.h
@@ -7,6 +7,7 @@
#include <limits.h>
#include <asm/types.h>
#include <linux/types.h>
+#include <linux/byteorder.h>

/* some ports we reserve for own use */
#define IOPORT_DBG 0xe0
@@ -36,15 +37,15 @@ static inline u8 ioport__read8(u8 *data)
{
return *data;
}
-
+/* On BE platforms, PCI I/O is byteswapped, i.e. LE, so swap back. */
static inline u16 ioport__read16(u16 *data)
{
- return *data;
+ return le16_to_cpu(*data);
}

static inline u32 ioport__read32(u32 *data)
{
- return *data;
+ return le32_to_cpu(*data);
}

static inline void ioport__write8(u8 *data, u8 value)
@@ -54,12 +55,12 @@ static inline void ioport__write8(u8 *data, u8 value)

static inline void ioport__write16(u16 *data, u16 value)
{
- *data = value;
+ *data = cpu_to_le16(value);
}

static inline void ioport__write32(u32 *data, u32 value)
{
- *data = value;
+ *data = cpu_to_le32(value);
}

#endif /* KVM__IOPORT_H */
diff --git a/tools/kvm/include/kvm/pci.h b/tools/kvm/include/kvm/pci.h
index b578ad7..88e92dc 100644
--- a/tools/kvm/include/kvm/pci.h
+++ b/tools/kvm/include/kvm/pci.h
@@ -5,6 +5,7 @@
#include <linux/kvm.h>
#include <linux/pci_regs.h>
#include <linux/msi.h>
+#include <endian.h>

#define PCI_MAX_DEVICES 256
/*
@@ -17,7 +18,8 @@
#define PCI_CONFIG_BUS_FORWARD 0xcfa
#define PCI_IO_SIZE 0x100

-struct pci_config_address {
+union pci_config_address {
+#if __BYTE_ORDER = __LITTLE_ENDIAN
unsigned zeros : 2; /* 1 .. 0 */
unsigned register_number : 6; /* 7 .. 2 */
unsigned function_number : 3; /* 10 .. 8 */
@@ -25,6 +27,16 @@ struct pci_config_address {
unsigned bus_number : 8; /* 23 .. 16 */
unsigned reserved : 7; /* 30 .. 24 */
unsigned enable_bit : 1; /* 31 */
+#else
+ unsigned enable_bit : 1; /* 31 */
+ unsigned reserved : 7; /* 30 .. 24 */
+ unsigned bus_number : 8; /* 23 .. 16 */
+ unsigned device_number : 5; /* 15 .. 11 */
+ unsigned function_number : 3; /* 10 .. 8 */
+ unsigned register_number : 6; /* 7 .. 2 */
+ unsigned zeros : 2; /* 1 .. 0 */
+#endif
+ u32 w;
};

struct msix_table {
@@ -45,8 +57,8 @@ struct pci_device_header {
u16 device_id;
u16 command;
u16 status;
- u16 revision_id : 8;
- u32 class : 24;
+ u8 revision_id;
+ u8 class[3];
u8 cacheline_size;
u8 latency_timer;
u8 header_type;
@@ -56,8 +68,8 @@ struct pci_device_header {
u16 subsys_vendor_id;
u16 subsys_id;
u32 exp_rom_bar;
- u32 capabilities : 8;
- u32 reserved1 : 24;
+ u8 capabilities;
+ u8 reserved1[3];
u32 reserved2;
u8 irq_line;
u8 irq_pin;
@@ -66,7 +78,7 @@ struct pci_device_header {
struct msix_cap msix;
u8 empty[136]; /* Rest of PCI config space */
u32 bar_size[6];
-};
+} __attribute__((packed));

void pci__init(void);
void pci__register(struct pci_device_header *dev, u8 dev_num);
diff --git a/tools/kvm/pci.c b/tools/kvm/pci.c
index 920e13e..5bbcbc7 100644
--- a/tools/kvm/pci.c
+++ b/tools/kvm/pci.c
@@ -9,7 +9,7 @@

static struct pci_device_header *pci_devices[PCI_MAX_DEVICES];

-static struct pci_config_address pci_config_address;
+static union pci_config_address pci_config_address;

/* This is within our PCI gap - in an unused area */
static u32 io_space_blocks = KVM_32BIT_GAP_START + 0x1000000;
@@ -105,7 +105,7 @@ static bool pci_config_data_out(struct ioport *ioport, struct kvm *kvm, u16 port
* When the kernel got the size it would write the address
* back.
*/
- if (ioport__read32(p + offset)) {
+ if (*(u32 *)(p + offset)) {
/* See if kernel tries to mask one of the BARs */
if ((offset >= PCI_BAR_OFFSET(0)) &&
(offset <= PCI_BAR_OFFSET(6)) &&
diff --git a/tools/kvm/virtio/pci.c b/tools/kvm/virtio/pci.c
index 0737ae7..0ae93fb 100644
--- a/tools/kvm/virtio/pci.c
+++ b/tools/kvm/virtio/pci.c
@@ -9,6 +9,7 @@
#include "kvm/virtio-trans.h"

#include <linux/virtio_pci.h>
+#include <linux/byteorder.h>
#include <string.h>

struct virtio_trans_ops *virtio_pci__get_trans_ops(void)
@@ -59,7 +60,7 @@ static int virtio_pci__init_ioeventfd(struct kvm *kvm, struct virtio_trans *vtra

static inline bool virtio_pci__msix_enabled(struct virtio_pci *vpci)
{
- return vpci->pci_hdr.msix.ctrl & PCI_MSIX_FLAGS_ENABLE;
+ return vpci->pci_hdr.msix.ctrl & cpu_to_le16(PCI_MSIX_FLAGS_ENABLE);
}

static bool virtio_pci__specific_io_in(struct kvm *kvm, struct virtio_trans *vtrans, u16 port,
@@ -245,8 +246,8 @@ int virtio_pci__signal_vq(struct kvm *kvm, struct virtio_trans *vtrans, u32 vq)
int tbl = vpci->vq_vector[vq];

if (virtio_pci__msix_enabled(vpci)) {
- if (vpci->pci_hdr.msix.ctrl & PCI_MSIX_FLAGS_MASKALL ||
- vpci->msix_table[tbl].ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT) {
+ if (vpci->pci_hdr.msix.ctrl & cpu_to_le16(PCI_MSIX_FLAGS_MASKALL) ||
+ vpci->msix_table[tbl].ctrl & cpu_to_le16(PCI_MSIX_ENTRY_CTRL_MASKBIT)) {

vpci->msix_pba |= 1 << tbl;
return 0;
@@ -266,8 +267,8 @@ int virtio_pci__signal_config(struct kvm *kvm, struct virtio_trans *vtrans)
int tbl = vpci->config_vector;

if (virtio_pci__msix_enabled(vpci)) {
- if (vpci->pci_hdr.msix.ctrl & PCI_MSIX_FLAGS_MASKALL ||
- vpci->msix_table[tbl].ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT) {
+ if (vpci->pci_hdr.msix.ctrl & cpu_to_le16(PCI_MSIX_FLAGS_MASKALL) ||
+ vpci->msix_table[tbl].ctrl & cpu_to_le16(PCI_MSIX_ENTRY_CTRL_MASKBIT)) {

vpci->msix_pba |= 1 << tbl;
return 0;
@@ -297,19 +298,21 @@ int virtio_pci__init(struct kvm *kvm, struct virtio_trans *vtrans, void *dev,
kvm__register_mmio(kvm, vpci->msix_pba_block, 0x100, callback_mmio_pba, vpci);

vpci->pci_hdr = (struct pci_device_header) {
- .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET,
- .device_id = device_id,
+ .vendor_id = cpu_to_le16(PCI_VENDOR_ID_REDHAT_QUMRANET),
+ .device_id = cpu_to_le16(device_id),
.header_type = PCI_HEADER_TYPE_NORMAL,
.revision_id = 0,
- .class = class,
- .subsys_vendor_id = PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
- .subsys_id = subsys_id,
- .bar[0] = vpci->base_addr | PCI_BASE_ADDRESS_SPACE_IO,
- .bar[1] = vpci->msix_io_block | PCI_BASE_ADDRESS_SPACE_MEMORY
- | PCI_BASE_ADDRESS_MEM_TYPE_64,
- .bar[3] = vpci->msix_pba_block | PCI_BASE_ADDRESS_SPACE_MEMORY
- | PCI_BASE_ADDRESS_MEM_TYPE_64,
- .status = PCI_STATUS_CAP_LIST,
+ .class[0] = class & 0xff,
+ .class[1] = (class >> 8) & 0xff,
+ .class[2] = (class >> 16) & 0xff,
+ .subsys_vendor_id = cpu_to_le16(PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET),
+ .subsys_id = cpu_to_le16(subsys_id),
+ .bar[0] = cpu_to_le32(vpci->base_addr | PCI_BASE_ADDRESS_SPACE_IO),
+ .bar[1] = cpu_to_le32(vpci->msix_io_block | PCI_BASE_ADDRESS_SPACE_MEMORY
+ | PCI_BASE_ADDRESS_MEM_TYPE_64),
+ .bar[3] = cpu_to_le32(vpci->msix_pba_block | PCI_BASE_ADDRESS_SPACE_MEMORY
+ | PCI_BASE_ADDRESS_MEM_TYPE_64),
+ .status = cpu_to_le16(PCI_STATUS_CAP_LIST),
.capabilities = (void *)&vpci->pci_hdr.msix - (void *)&vpci->pci_hdr,
};

@@ -326,14 +329,14 @@ int virtio_pci__init(struct kvm *kvm, struct virtio_trans *vtrans, void *dev,
* For example, a returned value of "00000000011"
* indicates a table size of 4.
*/
- vpci->pci_hdr.msix.ctrl = (VIRTIO_PCI_MAX_VQ + VIRTIO_PCI_MAX_CONFIG - 1);
+ vpci->pci_hdr.msix.ctrl = cpu_to_le16(VIRTIO_PCI_MAX_VQ + VIRTIO_PCI_MAX_CONFIG - 1);

/*
* Both table and PBA could be mapped on the same BAR, but for now
* we're not in short of BARs
*/
- vpci->pci_hdr.msix.table_offset = 1; /* Use BAR 1 */
- vpci->pci_hdr.msix.pba_offset = 3; /* Use BAR 3 */
+ vpci->pci_hdr.msix.table_offset = cpu_to_le32(1); /* Use BAR 1 */
+ vpci->pci_hdr.msix.pba_offset = cpu_to_le32(3); /* Use BAR 3 */
vpci->config_vector = 0;

if (irq__register_device(subsys_id, &ndev, &pin, &line) < 0)