]>
Commit | Line | Data |
---|---|---|
c4efe1ca AL |
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 | ||
681c28a3 | 13 | #include "qemu/osdep.h" |
c4efe1ca AL |
14 | #include "libqtest.h" |
15 | #include "libqos/pci-pc.h" | |
452fcdbc | 16 | #include "qapi/qmp/qdict.h" |
c4efe1ca AL |
17 | #include "hw/pci/pci_regs.h" |
18 | ||
0b8fa32f | 19 | #include "qemu/module.h" |
c4efe1ca | 20 | |
2f8b2767 IM |
21 | #define ACPI_PCIHP_ADDR 0xae00 |
22 | #define PCI_EJ_BASE 0x0008 | |
23 | ||
a795fc08 | 24 | static uint8_t qpci_pc_pio_readb(QPCIBus *bus, uint32_t addr) |
c4efe1ca | 25 | { |
d786f782 | 26 | return qtest_inb(bus->qts, addr); |
c4efe1ca AL |
27 | } |
28 | ||
a795fc08 DG |
29 | static void qpci_pc_pio_writeb(QPCIBus *bus, uint32_t addr, uint8_t val) |
30 | { | |
d786f782 | 31 | qtest_outb(bus->qts, addr, val); |
c4efe1ca AL |
32 | } |
33 | ||
a795fc08 DG |
34 | static uint16_t qpci_pc_pio_readw(QPCIBus *bus, uint32_t addr) |
35 | { | |
d786f782 | 36 | return qtest_inw(bus->qts, addr); |
a795fc08 | 37 | } |
c4efe1ca | 38 | |
a795fc08 | 39 | static void qpci_pc_pio_writew(QPCIBus *bus, uint32_t addr, uint16_t val) |
c4efe1ca | 40 | { |
d786f782 | 41 | qtest_outw(bus->qts, addr, val); |
a795fc08 | 42 | } |
c4efe1ca | 43 | |
a795fc08 | 44 | static uint32_t qpci_pc_pio_readl(QPCIBus *bus, uint32_t addr) |
c4efe1ca | 45 | { |
d786f782 | 46 | return qtest_inl(bus->qts, addr); |
a795fc08 | 47 | } |
c4efe1ca | 48 | |
a795fc08 | 49 | static void qpci_pc_pio_writel(QPCIBus *bus, uint32_t addr, uint32_t val) |
c4efe1ca | 50 | { |
d786f782 | 51 | qtest_outl(bus->qts, addr, val); |
a795fc08 | 52 | } |
c4efe1ca | 53 | |
f775f45a DG |
54 | static uint64_t qpci_pc_pio_readq(QPCIBus *bus, uint32_t addr) |
55 | { | |
d786f782 TH |
56 | return (uint64_t)qtest_inl(bus->qts, addr) + |
57 | ((uint64_t)qtest_inl(bus->qts, addr + 4) << 32); | |
f775f45a DG |
58 | } |
59 | ||
60 | static void qpci_pc_pio_writeq(QPCIBus *bus, uint32_t addr, uint64_t val) | |
61 | { | |
d786f782 TH |
62 | qtest_outl(bus->qts, addr, val & 0xffffffff); |
63 | qtest_outl(bus->qts, addr + 4, val >> 32); | |
f775f45a DG |
64 | } |
65 | ||
9a84f889 DG |
66 | static void qpci_pc_memread(QPCIBus *bus, uint32_t addr, void *buf, size_t len) |
67 | { | |
d786f782 | 68 | qtest_memread(bus->qts, addr, buf, len); |
9a84f889 DG |
69 | } |
70 | ||
71 | static void qpci_pc_memwrite(QPCIBus *bus, uint32_t addr, | |
72 | const void *buf, size_t len) | |
73 | { | |
d786f782 | 74 | qtest_memwrite(bus->qts, addr, buf, len); |
9a84f889 DG |
75 | } |
76 | ||
c4efe1ca AL |
77 | static uint8_t qpci_pc_config_readb(QPCIBus *bus, int devfn, uint8_t offset) |
78 | { | |
d786f782 TH |
79 | qtest_outl(bus->qts, 0xcf8, (1U << 31) | (devfn << 8) | offset); |
80 | return qtest_inb(bus->qts, 0xcfc); | |
c4efe1ca AL |
81 | } |
82 | ||
83 | static uint16_t qpci_pc_config_readw(QPCIBus *bus, int devfn, uint8_t offset) | |
84 | { | |
d786f782 TH |
85 | qtest_outl(bus->qts, 0xcf8, (1U << 31) | (devfn << 8) | offset); |
86 | return qtest_inw(bus->qts, 0xcfc); | |
c4efe1ca AL |
87 | } |
88 | ||
89 | static uint32_t qpci_pc_config_readl(QPCIBus *bus, int devfn, uint8_t offset) | |
90 | { | |
d786f782 TH |
91 | qtest_outl(bus->qts, 0xcf8, (1U << 31) | (devfn << 8) | offset); |
92 | return qtest_inl(bus->qts, 0xcfc); | |
c4efe1ca AL |
93 | } |
94 | ||
95 | static void qpci_pc_config_writeb(QPCIBus *bus, int devfn, uint8_t offset, uint8_t value) | |
96 | { | |
d786f782 TH |
97 | qtest_outl(bus->qts, 0xcf8, (1U << 31) | (devfn << 8) | offset); |
98 | qtest_outb(bus->qts, 0xcfc, value); | |
c4efe1ca AL |
99 | } |
100 | ||
101 | static void qpci_pc_config_writew(QPCIBus *bus, int devfn, uint8_t offset, uint16_t value) | |
102 | { | |
d786f782 TH |
103 | qtest_outl(bus->qts, 0xcf8, (1U << 31) | (devfn << 8) | offset); |
104 | qtest_outw(bus->qts, 0xcfc, value); | |
c4efe1ca AL |
105 | } |
106 | ||
107 | static void qpci_pc_config_writel(QPCIBus *bus, int devfn, uint8_t offset, uint32_t value) | |
108 | { | |
d786f782 TH |
109 | qtest_outl(bus->qts, 0xcf8, (1U << 31) | (devfn << 8) | offset); |
110 | qtest_outl(bus->qts, 0xcfc, value); | |
c4efe1ca AL |
111 | } |
112 | ||
85af0057 | 113 | static void *qpci_pc_get_driver(void *obj, const char *interface) |
c4efe1ca | 114 | { |
85af0057 EGE |
115 | QPCIBusPC *qpci = obj; |
116 | if (!g_strcmp0(interface, "pci-bus")) { | |
117 | return &qpci->bus; | |
118 | } | |
119 | fprintf(stderr, "%s not present in pci-bus-pc\n", interface); | |
120 | g_assert_not_reached(); | |
121 | } | |
c4efe1ca | 122 | |
85af0057 EGE |
123 | void qpci_init_pc(QPCIBusPC *qpci, QTestState *qts, QGuestAllocator *alloc) |
124 | { | |
e5d1730d | 125 | assert(qts); |
c4efe1ca | 126 | |
92bbafc7 | 127 | /* tests can use pci-bus */ |
08f7ad1b | 128 | qpci->bus.has_buggy_msi = false; |
92bbafc7 | 129 | |
85af0057 EGE |
130 | qpci->bus.pio_readb = qpci_pc_pio_readb; |
131 | qpci->bus.pio_readw = qpci_pc_pio_readw; | |
132 | qpci->bus.pio_readl = qpci_pc_pio_readl; | |
133 | qpci->bus.pio_readq = qpci_pc_pio_readq; | |
134 | ||
135 | qpci->bus.pio_writeb = qpci_pc_pio_writeb; | |
136 | qpci->bus.pio_writew = qpci_pc_pio_writew; | |
137 | qpci->bus.pio_writel = qpci_pc_pio_writel; | |
138 | qpci->bus.pio_writeq = qpci_pc_pio_writeq; | |
139 | ||
140 | qpci->bus.memread = qpci_pc_memread; | |
141 | qpci->bus.memwrite = qpci_pc_memwrite; | |
a795fc08 | 142 | |
85af0057 EGE |
143 | qpci->bus.config_readb = qpci_pc_config_readb; |
144 | qpci->bus.config_readw = qpci_pc_config_readw; | |
145 | qpci->bus.config_readl = qpci_pc_config_readl; | |
a795fc08 | 146 | |
85af0057 EGE |
147 | qpci->bus.config_writeb = qpci_pc_config_writeb; |
148 | qpci->bus.config_writew = qpci_pc_config_writew; | |
149 | qpci->bus.config_writel = qpci_pc_config_writel; | |
9a84f889 | 150 | |
85af0057 EGE |
151 | qpci->bus.qts = qts; |
152 | qpci->bus.pio_alloc_ptr = 0xc000; | |
153 | qpci->bus.mmio_alloc_ptr = 0xE0000000; | |
154 | qpci->bus.mmio_limit = 0x100000000ULL; | |
c4efe1ca | 155 | |
85af0057 EGE |
156 | qpci->obj.get_driver = qpci_pc_get_driver; |
157 | } | |
c4efe1ca | 158 | |
85af0057 EGE |
159 | QPCIBus *qpci_new_pc(QTestState *qts, QGuestAllocator *alloc) |
160 | { | |
161 | QPCIBusPC *qpci = g_new0(QPCIBusPC, 1); | |
162 | qpci_init_pc(qpci, qts, alloc); | |
c4efe1ca | 163 | |
85af0057 | 164 | return &qpci->bus; |
c4efe1ca | 165 | } |
7f2a5ae6 JS |
166 | |
167 | void qpci_free_pc(QPCIBus *bus) | |
168 | { | |
85af0057 EGE |
169 | QPCIBusPC *s; |
170 | ||
171 | if (!bus) { | |
172 | return; | |
173 | } | |
174 | s = container_of(bus, QPCIBusPC, bus); | |
7f2a5ae6 JS |
175 | |
176 | g_free(s); | |
177 | } | |
2f8b2767 | 178 | |
6ebb8d2a | 179 | void qpci_unplug_acpi_device_test(QTestState *qts, const char *id, uint8_t slot) |
2f8b2767 IM |
180 | { |
181 | QDict *response; | |
015715f5 | 182 | |
6ebb8d2a TH |
183 | response = qtest_qmp(qts, "{'execute': 'device_del'," |
184 | " 'arguments': {'id': %s}}", id); | |
2f8b2767 IM |
185 | g_assert(response); |
186 | g_assert(!qdict_haskey(response, "error")); | |
cb3e7f08 | 187 | qobject_unref(response); |
2f8b2767 | 188 | |
6ebb8d2a | 189 | qtest_outb(qts, ACPI_PCIHP_ADDR + PCI_EJ_BASE, 1 << slot); |
2f8b2767 | 190 | |
6ebb8d2a | 191 | qtest_qmp_eventwait(qts, "DEVICE_DELETED"); |
2f8b2767 | 192 | } |
85af0057 EGE |
193 | |
194 | static void qpci_pc_register_nodes(void) | |
195 | { | |
196 | qos_node_create_driver("pci-bus-pc", NULL); | |
197 | qos_node_produces("pci-bus-pc", "pci-bus"); | |
198 | } | |
199 | ||
200 | libqos_init(qpci_pc_register_nodes); |