]>
Commit | Line | Data |
---|---|---|
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 |
21 | typedef struct QPCIWindow { |
22 | uint64_t pci_base; /* window address in PCI space */ | |
23 | uint64_t size; /* window size */ | |
24 | } QPCIWindow; | |
cf716b31 LV |
25 | |
26 | typedef 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 | ||
53 | static 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 | ||
66 | static 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 | ||
79 | static 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 | ||
92 | static 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 | ||
103 | static 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 | ||
115 | static 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 | ||
127 | static 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 | ||
134 | static 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 | ||
141 | static 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 | ||
148 | static 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 | ||
156 | static 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 | ||
164 | static 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 | ||
172 | static 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 | ||
233 | static 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 |
244 | QPCIBus *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 | ||
298 | void qpci_free_spapr(QPCIBus *bus) | |
299 | { | |
300 | QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); | |
301 | ||
302 | g_free(s); | |
303 | } |