]> git.proxmox.com Git - mirror_qemu.git/blame - tests/libqos/pci-spapr.c
spapr_pci: Add a 64-bit MMIO window
[mirror_qemu.git] / tests / libqos / pci-spapr.c
CommitLineData
cf716b31
LV
1/*
2 * libqos PCI bindings for SPAPR
3 *
4 * This work is licensed under the terms of the GNU GPL, version 2 or later.
5 * See the COPYING file in the top-level directory.
6 */
7
8#include "qemu/osdep.h"
9#include "libqtest.h"
10#include "libqos/pci-spapr.h"
11#include "libqos/rtas.h"
12
13#include "hw/pci/pci_regs.h"
14
15#include "qemu-common.h"
16#include "qemu/host-utils.h"
17
18
19/* From include/hw/pci-host/spapr.h */
20
cd1b354e
DG
21typedef struct QPCIWindow {
22 uint64_t pci_base; /* window address in PCI space */
23 uint64_t size; /* window size */
24} QPCIWindow;
cf716b31
LV
25
26typedef struct QPCIBusSPAPR {
27 QPCIBus bus;
28 QGuestAllocator *alloc;
29
cd1b354e
DG
30 uint64_t buid;
31
32 uint64_t pio_cpu_base;
33 QPCIWindow pio;
34
8360544a
DG
35 uint64_t mmio32_cpu_base;
36 QPCIWindow mmio32;
cd1b354e 37
cf716b31
LV
38 uint64_t pci_hole_start;
39 uint64_t pci_hole_size;
40 uint64_t pci_hole_alloc;
41
42 uint32_t pci_iohole_start;
43 uint32_t pci_iohole_size;
44 uint32_t pci_iohole_alloc;
45} QPCIBusSPAPR;
46
47/*
48 * PCI devices are always little-endian
49 * SPAPR by default is big-endian
50 * so PCI accessors need to swap data endianness
51 */
52
53static uint8_t qpci_spapr_io_readb(QPCIBus *bus, void *addr)
54{
cd1b354e 55 QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
cf716b31
LV
56 uint64_t port = (uintptr_t)addr;
57 uint8_t v;
cd1b354e
DG
58 if (port < s->pio.size) {
59 v = readb(s->pio_cpu_base + port);
cf716b31 60 } else {
8360544a 61 v = readb(s->mmio32_cpu_base + port);
cf716b31
LV
62 }
63 return v;
64}
65
66static uint16_t qpci_spapr_io_readw(QPCIBus *bus, void *addr)
67{
cd1b354e 68 QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
cf716b31
LV
69 uint64_t port = (uintptr_t)addr;
70 uint16_t v;
cd1b354e
DG
71 if (port < s->pio.size) {
72 v = readw(s->pio_cpu_base + port);
cf716b31 73 } else {
8360544a 74 v = readw(s->mmio32_cpu_base + port);
cf716b31
LV
75 }
76 return bswap16(v);
77}
78
79static uint32_t qpci_spapr_io_readl(QPCIBus *bus, void *addr)
80{
cd1b354e 81 QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
cf716b31
LV
82 uint64_t port = (uintptr_t)addr;
83 uint32_t v;
cd1b354e
DG
84 if (port < s->pio.size) {
85 v = readl(s->pio_cpu_base + port);
cf716b31 86 } else {
8360544a 87 v = readl(s->mmio32_cpu_base + port);
cf716b31
LV
88 }
89 return bswap32(v);
90}
91
92static void qpci_spapr_io_writeb(QPCIBus *bus, void *addr, uint8_t value)
93{
cd1b354e 94 QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
cf716b31 95 uint64_t port = (uintptr_t)addr;
cd1b354e
DG
96 if (port < s->pio.size) {
97 writeb(s->pio_cpu_base + port, value);
cf716b31 98 } else {
8360544a 99 writeb(s->mmio32_cpu_base + port, value);
cf716b31
LV
100 }
101}
102
103static void qpci_spapr_io_writew(QPCIBus *bus, void *addr, uint16_t value)
104{
cd1b354e 105 QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
cf716b31
LV
106 uint64_t port = (uintptr_t)addr;
107 value = bswap16(value);
cd1b354e
DG
108 if (port < s->pio.size) {
109 writew(s->pio_cpu_base + port, value);
cf716b31 110 } else {
8360544a 111 writew(s->mmio32_cpu_base + port, value);
cf716b31
LV
112 }
113}
114
115static void qpci_spapr_io_writel(QPCIBus *bus, void *addr, uint32_t value)
116{
cd1b354e 117 QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
cf716b31
LV
118 uint64_t port = (uintptr_t)addr;
119 value = bswap32(value);
cd1b354e
DG
120 if (port < s->pio.size) {
121 writel(s->pio_cpu_base + port, value);
cf716b31 122 } else {
8360544a 123 writel(s->mmio32_cpu_base + port, value);
cf716b31
LV
124 }
125}
126
127static uint8_t qpci_spapr_config_readb(QPCIBus *bus, int devfn, uint8_t offset)
128{
129 QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
130 uint32_t config_addr = (devfn << 8) | offset;
cd1b354e 131 return qrtas_ibm_read_pci_config(s->alloc, s->buid, config_addr, 1);
cf716b31
LV
132}
133
134static uint16_t qpci_spapr_config_readw(QPCIBus *bus, int devfn, uint8_t offset)
135{
136 QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
137 uint32_t config_addr = (devfn << 8) | offset;
cd1b354e 138 return qrtas_ibm_read_pci_config(s->alloc, s->buid, config_addr, 2);
cf716b31
LV
139}
140
141static uint32_t qpci_spapr_config_readl(QPCIBus *bus, int devfn, uint8_t offset)
142{
143 QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
144 uint32_t config_addr = (devfn << 8) | offset;
cd1b354e 145 return qrtas_ibm_read_pci_config(s->alloc, s->buid, config_addr, 4);
cf716b31
LV
146}
147
148static void qpci_spapr_config_writeb(QPCIBus *bus, int devfn, uint8_t offset,
149 uint8_t value)
150{
151 QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
152 uint32_t config_addr = (devfn << 8) | offset;
cd1b354e 153 qrtas_ibm_write_pci_config(s->alloc, s->buid, config_addr, 1, value);
cf716b31
LV
154}
155
156static void qpci_spapr_config_writew(QPCIBus *bus, int devfn, uint8_t offset,
157 uint16_t value)
158{
159 QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
160 uint32_t config_addr = (devfn << 8) | offset;
cd1b354e 161 qrtas_ibm_write_pci_config(s->alloc, s->buid, config_addr, 2, value);
cf716b31
LV
162}
163
164static void qpci_spapr_config_writel(QPCIBus *bus, int devfn, uint8_t offset,
165 uint32_t value)
166{
167 QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
168 uint32_t config_addr = (devfn << 8) | offset;
cd1b354e 169 qrtas_ibm_write_pci_config(s->alloc, s->buid, config_addr, 4, value);
cf716b31
LV
170}
171
172static void *qpci_spapr_iomap(QPCIBus *bus, QPCIDevice *dev, int barno,
173 uint64_t *sizeptr)
174{
175 QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
176 static const int bar_reg_map[] = {
177 PCI_BASE_ADDRESS_0, PCI_BASE_ADDRESS_1, PCI_BASE_ADDRESS_2,
178 PCI_BASE_ADDRESS_3, PCI_BASE_ADDRESS_4, PCI_BASE_ADDRESS_5,
179 };
180 int bar_reg;
181 uint32_t addr;
182 uint64_t size;
183 uint32_t io_type;
184
185 g_assert(barno >= 0 && barno <= 5);
186 bar_reg = bar_reg_map[barno];
187
188 qpci_config_writel(dev, bar_reg, 0xFFFFFFFF);
189 addr = qpci_config_readl(dev, bar_reg);
190
191 io_type = addr & PCI_BASE_ADDRESS_SPACE;
192 if (io_type == PCI_BASE_ADDRESS_SPACE_IO) {
193 addr &= PCI_BASE_ADDRESS_IO_MASK;
194 } else {
195 addr &= PCI_BASE_ADDRESS_MEM_MASK;
196 }
197
198 size = (1ULL << ctzl(addr));
199 if (size == 0) {
200 return NULL;
201 }
202 if (sizeptr) {
203 *sizeptr = size;
204 }
205
206 if (io_type == PCI_BASE_ADDRESS_SPACE_IO) {
207 uint16_t loc;
208
209 g_assert(QEMU_ALIGN_UP(s->pci_iohole_alloc, size) + size
210 <= s->pci_iohole_size);
211 s->pci_iohole_alloc = QEMU_ALIGN_UP(s->pci_iohole_alloc, size);
212 loc = s->pci_iohole_start + s->pci_iohole_alloc;
213 s->pci_iohole_alloc += size;
214
215 qpci_config_writel(dev, bar_reg, loc | PCI_BASE_ADDRESS_SPACE_IO);
216
217 return (void *)(unsigned long)loc;
218 } else {
219 uint64_t loc;
220
221 g_assert(QEMU_ALIGN_UP(s->pci_hole_alloc, size) + size
222 <= s->pci_hole_size);
223 s->pci_hole_alloc = QEMU_ALIGN_UP(s->pci_hole_alloc, size);
224 loc = s->pci_hole_start + s->pci_hole_alloc;
225 s->pci_hole_alloc += size;
226
227 qpci_config_writel(dev, bar_reg, loc);
228
229 return (void *)(unsigned long)loc;
230 }
231}
232
233static void qpci_spapr_iounmap(QPCIBus *bus, void *data)
234{
235 /* FIXME */
236}
237
cd1b354e 238#define SPAPR_PCI_WINDOW_BASE 0x10000000000ULL
8360544a
DG
239#define SPAPR_PCI_MMIO32_WIN_OFF 0xA0000000
240#define SPAPR_PCI_MMIO32_WIN_SIZE 0x80000000 /* 2 GiB */
cd1b354e
DG
241#define SPAPR_PCI_IO_WIN_OFF 0x80000000
242#define SPAPR_PCI_IO_WIN_SIZE 0x10000
243
cf716b31
LV
244QPCIBus *qpci_init_spapr(QGuestAllocator *alloc)
245{
246 QPCIBusSPAPR *ret;
247
248 ret = g_malloc(sizeof(*ret));
249
250 ret->alloc = alloc;
251
252 ret->bus.io_readb = qpci_spapr_io_readb;
253 ret->bus.io_readw = qpci_spapr_io_readw;
254 ret->bus.io_readl = qpci_spapr_io_readl;
255
256 ret->bus.io_writeb = qpci_spapr_io_writeb;
257 ret->bus.io_writew = qpci_spapr_io_writew;
258 ret->bus.io_writel = qpci_spapr_io_writel;
259
260 ret->bus.config_readb = qpci_spapr_config_readb;
261 ret->bus.config_readw = qpci_spapr_config_readw;
262 ret->bus.config_readl = qpci_spapr_config_readl;
263
264 ret->bus.config_writeb = qpci_spapr_config_writeb;
265 ret->bus.config_writew = qpci_spapr_config_writew;
266 ret->bus.config_writel = qpci_spapr_config_writel;
267
268 ret->bus.iomap = qpci_spapr_iomap;
269 ret->bus.iounmap = qpci_spapr_iounmap;
270
cd1b354e
DG
271 /* FIXME: We assume the default location of the PHB for now.
272 * Ideally we'd parse the device tree deposited in the guest to
273 * get the window locations */
274 ret->buid = 0x800000020000000ULL;
275
276 ret->pio_cpu_base = SPAPR_PCI_WINDOW_BASE + SPAPR_PCI_IO_WIN_OFF;
277 ret->pio.pci_base = 0;
278 ret->pio.size = SPAPR_PCI_IO_WIN_SIZE;
279
8360544a
DG
280 /* 32-bit portion of the MMIO window is at PCI address 2..4 GiB */
281 ret->mmio32_cpu_base = SPAPR_PCI_WINDOW_BASE + SPAPR_PCI_MMIO32_WIN_OFF;
282 ret->mmio32.pci_base = 0x80000000; /* 2 GiB */
283 ret->mmio32.size = SPAPR_PCI_MMIO32_WIN_SIZE;
cd1b354e 284
cf716b31 285 ret->pci_hole_start = 0xC0000000;
c7113690 286 ret->pci_hole_size =
8360544a 287 ret->mmio32.pci_base + ret->mmio32.size - ret->pci_hole_start;
cf716b31
LV
288 ret->pci_hole_alloc = 0;
289
290 ret->pci_iohole_start = 0xc000;
c7113690
DG
291 ret->pci_iohole_size =
292 ret->pio.pci_base + ret->pio.size - ret->pci_iohole_start;
cf716b31
LV
293 ret->pci_iohole_alloc = 0;
294
295 return &ret->bus;
296}
297
298void qpci_free_spapr(QPCIBus *bus)
299{
300 QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
301
302 g_free(s);
303}