qemu/tests/libqos/pci-pc.c
<<
>>
Prefs
   1/*
   2 * libqos PCI bindings for PC
   3 *
   4 * Copyright IBM, Corp. 2012-2013
   5 *
   6 * Authors:
   7 *  Anthony Liguori   <aliguori@us.ibm.com>
   8 *
   9 * This work is licensed under the terms of the GNU GPL, version 2 or later.
  10 * See the COPYING file in the top-level directory.
  11 */
  12
  13#include "qemu/osdep.h"
  14#include "libqtest.h"
  15#include "libqos/pci-pc.h"
  16
  17#include "hw/pci/pci_regs.h"
  18
  19#include "qemu-common.h"
  20#include "qemu/host-utils.h"
  21
  22#include <glib.h>
  23
  24#define ACPI_PCIHP_ADDR         0xae00
  25#define PCI_EJ_BASE             0x0008
  26
  27typedef struct QPCIBusPC
  28{
  29    QPCIBus bus;
  30
  31    uint32_t pci_hole_start;
  32    uint32_t pci_hole_size;
  33    uint32_t pci_hole_alloc;
  34
  35    uint16_t pci_iohole_start;
  36    uint16_t pci_iohole_size;
  37    uint16_t pci_iohole_alloc;
  38} QPCIBusPC;
  39
  40static uint8_t qpci_pc_io_readb(QPCIBus *bus, void *addr)
  41{
  42    uintptr_t port = (uintptr_t)addr;
  43    uint8_t value;
  44
  45    if (port < 0x10000) {
  46        value = inb(port);
  47    } else {
  48        value = readb(port);
  49    }
  50
  51    return value;
  52}
  53
  54static uint16_t qpci_pc_io_readw(QPCIBus *bus, void *addr)
  55{
  56    uintptr_t port = (uintptr_t)addr;
  57    uint16_t value;
  58
  59    if (port < 0x10000) {
  60        value = inw(port);
  61    } else {
  62        value = readw(port);
  63    }
  64
  65    return value;
  66}
  67
  68static uint32_t qpci_pc_io_readl(QPCIBus *bus, void *addr)
  69{
  70    uintptr_t port = (uintptr_t)addr;
  71    uint32_t value;
  72
  73    if (port < 0x10000) {
  74        value = inl(port);
  75    } else {
  76        value = readl(port);
  77    }
  78
  79    return value;
  80}
  81
  82static void qpci_pc_io_writeb(QPCIBus *bus, void *addr, uint8_t value)
  83{
  84    uintptr_t port = (uintptr_t)addr;
  85
  86    if (port < 0x10000) {
  87        outb(port, value);
  88    } else {
  89        writeb(port, value);
  90    }
  91}
  92
  93static void qpci_pc_io_writew(QPCIBus *bus, void *addr, uint16_t value)
  94{
  95    uintptr_t port = (uintptr_t)addr;
  96
  97    if (port < 0x10000) {
  98        outw(port, value);
  99    } else {
 100        writew(port, value);
 101    }
 102}
 103
 104static void qpci_pc_io_writel(QPCIBus *bus, void *addr, uint32_t value)
 105{
 106    uintptr_t port = (uintptr_t)addr;
 107
 108    if (port < 0x10000) {
 109        outl(port, value);
 110    } else {
 111        writel(port, value);
 112    }
 113}
 114
 115static uint8_t qpci_pc_config_readb(QPCIBus *bus, int devfn, uint8_t offset)
 116{
 117    outl(0xcf8, (1U << 31) | (devfn << 8) | offset);
 118    return inb(0xcfc);
 119}
 120
 121static uint16_t qpci_pc_config_readw(QPCIBus *bus, int devfn, uint8_t offset)
 122{
 123    outl(0xcf8, (1U << 31) | (devfn << 8) | offset);
 124    return inw(0xcfc);
 125}
 126
 127static uint32_t qpci_pc_config_readl(QPCIBus *bus, int devfn, uint8_t offset)
 128{
 129    outl(0xcf8, (1U << 31) | (devfn << 8) | offset);
 130    return inl(0xcfc);
 131}
 132
 133static void qpci_pc_config_writeb(QPCIBus *bus, int devfn, uint8_t offset, uint8_t value)
 134{
 135    outl(0xcf8, (1U << 31) | (devfn << 8) | offset);
 136    outb(0xcfc, value);
 137}
 138
 139static void qpci_pc_config_writew(QPCIBus *bus, int devfn, uint8_t offset, uint16_t value)
 140{
 141    outl(0xcf8, (1U << 31) | (devfn << 8) | offset);
 142    outw(0xcfc, value);
 143}
 144
 145static void qpci_pc_config_writel(QPCIBus *bus, int devfn, uint8_t offset, uint32_t value)
 146{
 147    outl(0xcf8, (1U << 31) | (devfn << 8) | offset);
 148    outl(0xcfc, value);
 149}
 150
 151static void *qpci_pc_iomap(QPCIBus *bus, QPCIDevice *dev, int barno, uint64_t *sizeptr)
 152{
 153    QPCIBusPC *s = container_of(bus, QPCIBusPC, bus);
 154    static const int bar_reg_map[] = {
 155        PCI_BASE_ADDRESS_0, PCI_BASE_ADDRESS_1, PCI_BASE_ADDRESS_2,
 156        PCI_BASE_ADDRESS_3, PCI_BASE_ADDRESS_4, PCI_BASE_ADDRESS_5,
 157    };
 158    int bar_reg;
 159    uint32_t addr;
 160    uint64_t size;
 161    uint32_t io_type;
 162
 163    g_assert(barno >= 0 && barno <= 5);
 164    bar_reg = bar_reg_map[barno];
 165
 166    qpci_config_writel(dev, bar_reg, 0xFFFFFFFF);
 167    addr = qpci_config_readl(dev, bar_reg);
 168
 169    io_type = addr & PCI_BASE_ADDRESS_SPACE;
 170    if (io_type == PCI_BASE_ADDRESS_SPACE_IO) {
 171        addr &= PCI_BASE_ADDRESS_IO_MASK;
 172    } else {
 173        addr &= PCI_BASE_ADDRESS_MEM_MASK;
 174    }
 175
 176    size = (1ULL << ctzl(addr));
 177    if (size == 0) {
 178        return NULL;
 179    }
 180    if (sizeptr) {
 181        *sizeptr = size;
 182    }
 183
 184    if (io_type == PCI_BASE_ADDRESS_SPACE_IO) {
 185        uint16_t loc;
 186
 187        g_assert(QEMU_ALIGN_UP(s->pci_iohole_alloc, size) + size
 188                 <= s->pci_iohole_size);
 189        s->pci_iohole_alloc = QEMU_ALIGN_UP(s->pci_iohole_alloc, size);
 190        loc = s->pci_iohole_start + s->pci_iohole_alloc;
 191        s->pci_iohole_alloc += size;
 192
 193        qpci_config_writel(dev, bar_reg, loc | PCI_BASE_ADDRESS_SPACE_IO);
 194
 195        return (void *)(intptr_t)loc;
 196    } else {
 197        uint64_t loc;
 198
 199        g_assert(QEMU_ALIGN_UP(s->pci_hole_alloc, size) + size
 200                 <= s->pci_hole_size);
 201        s->pci_hole_alloc = QEMU_ALIGN_UP(s->pci_hole_alloc, size);
 202        loc = s->pci_hole_start + s->pci_hole_alloc;
 203        s->pci_hole_alloc += size;
 204
 205        qpci_config_writel(dev, bar_reg, loc);
 206
 207        return (void *)(intptr_t)loc;
 208    }
 209}
 210
 211static void qpci_pc_iounmap(QPCIBus *bus, void *data)
 212{
 213    /* FIXME */
 214}
 215
 216QPCIBus *qpci_init_pc(void)
 217{
 218    QPCIBusPC *ret;
 219
 220    ret = g_malloc(sizeof(*ret));
 221
 222    ret->bus.io_readb = qpci_pc_io_readb;
 223    ret->bus.io_readw = qpci_pc_io_readw;
 224    ret->bus.io_readl = qpci_pc_io_readl;
 225
 226    ret->bus.io_writeb = qpci_pc_io_writeb;
 227    ret->bus.io_writew = qpci_pc_io_writew;
 228    ret->bus.io_writel = qpci_pc_io_writel;
 229
 230    ret->bus.config_readb = qpci_pc_config_readb;
 231    ret->bus.config_readw = qpci_pc_config_readw;
 232    ret->bus.config_readl = qpci_pc_config_readl;
 233
 234    ret->bus.config_writeb = qpci_pc_config_writeb;
 235    ret->bus.config_writew = qpci_pc_config_writew;
 236    ret->bus.config_writel = qpci_pc_config_writel;
 237
 238    ret->bus.iomap = qpci_pc_iomap;
 239    ret->bus.iounmap = qpci_pc_iounmap;
 240
 241    ret->pci_hole_start = 0xE0000000;
 242    ret->pci_hole_size = 0x20000000;
 243    ret->pci_hole_alloc = 0;
 244
 245    ret->pci_iohole_start = 0xc000;
 246    ret->pci_iohole_size = 0x4000;
 247    ret->pci_iohole_alloc = 0;
 248
 249    return &ret->bus;
 250}
 251
 252void qpci_free_pc(QPCIBus *bus)
 253{
 254    QPCIBusPC *s = container_of(bus, QPCIBusPC, bus);
 255
 256    g_free(s);
 257}
 258
 259void qpci_plug_device_test(const char *driver, const char *id,
 260                           uint8_t slot, const char *opts)
 261{
 262    QDict *response;
 263    char *cmd;
 264
 265    cmd = g_strdup_printf("{'execute': 'device_add',"
 266                          " 'arguments': {"
 267                          "   'driver': '%s',"
 268                          "   'addr': '%d',"
 269                          "   %s%s"
 270                          "   'id': '%s'"
 271                          "}}", driver, slot,
 272                          opts ? opts : "", opts ? "," : "",
 273                          id);
 274    response = qmp(cmd);
 275    g_free(cmd);
 276    g_assert(response);
 277    g_assert(!qdict_haskey(response, "error"));
 278    QDECREF(response);
 279}
 280
 281void qpci_unplug_acpi_device_test(const char *id, uint8_t slot)
 282{
 283    QDict *response;
 284    char *cmd;
 285
 286    cmd = g_strdup_printf("{'execute': 'device_del',"
 287                          " 'arguments': {"
 288                          "   'id': '%s'"
 289                          "}}", id);
 290    response = qmp(cmd);
 291    g_free(cmd);
 292    g_assert(response);
 293    g_assert(!qdict_haskey(response, "error"));
 294    QDECREF(response);
 295
 296    outb(ACPI_PCIHP_ADDR + PCI_EJ_BASE, 1 << slot);
 297
 298    response = qmp("");
 299    g_assert(response);
 300    g_assert(qdict_haskey(response, "event"));
 301    g_assert(!strcmp(qdict_get_str(response, "event"), "DEVICE_DELETED"));
 302    QDECREF(response);
 303}
 304