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