]>
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" | |
16 | ||
17 | #include "hw/pci/pci_regs.h" | |
18 | ||
19 | #include "qemu-common.h" | |
20 | #include "qemu/host-utils.h" | |
21 | ||
c4efe1ca | 22 | |
2f8b2767 IM |
23 | #define ACPI_PCIHP_ADDR 0xae00 |
24 | #define PCI_EJ_BASE 0x0008 | |
25 | ||
c4efe1ca AL |
26 | typedef 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 | ||
39 | static 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 { | |
0e162974 | 47 | value = readb(port); |
c4efe1ca AL |
48 | } |
49 | ||
50 | return value; | |
51 | } | |
52 | ||
53 | static 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 { | |
0e162974 | 61 | value = readw(port); |
c4efe1ca AL |
62 | } |
63 | ||
64 | return value; | |
65 | } | |
66 | ||
67 | static 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 { | |
0e162974 | 75 | value = readl(port); |
c4efe1ca AL |
76 | } |
77 | ||
78 | return value; | |
79 | } | |
80 | ||
81 | static 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 { | |
0e162974 | 88 | writeb(port, value); |
c4efe1ca AL |
89 | } |
90 | } | |
91 | ||
92 | static 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 { | |
0e162974 | 99 | writew(port, value); |
c4efe1ca AL |
100 | } |
101 | } | |
102 | ||
103 | static 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 { | |
0e162974 | 110 | writel(port, value); |
c4efe1ca AL |
111 | } |
112 | } | |
113 | ||
114 | static uint8_t qpci_pc_config_readb(QPCIBus *bus, int devfn, uint8_t offset) | |
115 | { | |
a879125b | 116 | outl(0xcf8, (1U << 31) | (devfn << 8) | offset); |
c4efe1ca AL |
117 | return inb(0xcfc); |
118 | } | |
119 | ||
120 | static uint16_t qpci_pc_config_readw(QPCIBus *bus, int devfn, uint8_t offset) | |
121 | { | |
a879125b | 122 | outl(0xcf8, (1U << 31) | (devfn << 8) | offset); |
c4efe1ca AL |
123 | return inw(0xcfc); |
124 | } | |
125 | ||
126 | static uint32_t qpci_pc_config_readl(QPCIBus *bus, int devfn, uint8_t offset) | |
127 | { | |
a879125b | 128 | outl(0xcf8, (1U << 31) | (devfn << 8) | offset); |
c4efe1ca AL |
129 | return inl(0xcfc); |
130 | } | |
131 | ||
132 | static void qpci_pc_config_writeb(QPCIBus *bus, int devfn, uint8_t offset, uint8_t value) | |
133 | { | |
a879125b | 134 | outl(0xcf8, (1U << 31) | (devfn << 8) | offset); |
c4efe1ca AL |
135 | outb(0xcfc, value); |
136 | } | |
137 | ||
138 | static void qpci_pc_config_writew(QPCIBus *bus, int devfn, uint8_t offset, uint16_t value) | |
139 | { | |
a879125b | 140 | outl(0xcf8, (1U << 31) | (devfn << 8) | offset); |
c4efe1ca AL |
141 | outw(0xcfc, value); |
142 | } | |
143 | ||
144 | static void qpci_pc_config_writel(QPCIBus *bus, int devfn, uint8_t offset, uint32_t value) | |
145 | { | |
a879125b | 146 | outl(0xcf8, (1U << 31) | (devfn << 8) | offset); |
c4efe1ca AL |
147 | outl(0xcfc, value); |
148 | } | |
149 | ||
6ce7100e | 150 | static void *qpci_pc_iomap(QPCIBus *bus, QPCIDevice *dev, int barno, uint64_t *sizeptr) |
c4efe1ca AL |
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 | } | |
6ce7100e JS |
179 | if (sizeptr) { |
180 | *sizeptr = size; | |
181 | } | |
c4efe1ca AL |
182 | |
183 | if (io_type == PCI_BASE_ADDRESS_SPACE_IO) { | |
184 | uint16_t loc; | |
185 | ||
99826172 MA |
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); | |
c4efe1ca AL |
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 | ||
99826172 MA |
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); | |
c4efe1ca AL |
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 | ||
210 | static void qpci_pc_iounmap(QPCIBus *bus, void *data) | |
211 | { | |
212 | /* FIXME */ | |
213 | } | |
214 | ||
215 | QPCIBus *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 | } | |
7f2a5ae6 JS |
250 | |
251 | void qpci_free_pc(QPCIBus *bus) | |
252 | { | |
253 | QPCIBusPC *s = container_of(bus, QPCIBusPC, bus); | |
254 | ||
255 | g_free(s); | |
256 | } | |
2f8b2767 | 257 | |
2f8b2767 IM |
258 | void qpci_unplug_acpi_device_test(const char *id, uint8_t slot) |
259 | { | |
260 | QDict *response; | |
261 | char *cmd; | |
262 | ||
263 | cmd = g_strdup_printf("{'execute': 'device_del'," | |
264 | " 'arguments': {" | |
265 | " 'id': '%s'" | |
266 | "}}", id); | |
267 | response = qmp(cmd); | |
268 | g_free(cmd); | |
269 | g_assert(response); | |
270 | g_assert(!qdict_haskey(response, "error")); | |
271 | QDECREF(response); | |
272 | ||
273 | outb(ACPI_PCIHP_ADDR + PCI_EJ_BASE, 1 << slot); | |
274 | ||
275 | response = qmp(""); | |
276 | g_assert(response); | |
277 | g_assert(qdict_haskey(response, "event")); | |
278 | g_assert(!strcmp(qdict_get_str(response, "event"), "DEVICE_DELETED")); | |
279 | QDECREF(response); | |
280 | } |