]>
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 | ||
a795fc08 | 39 | static uint8_t qpci_pc_pio_readb(QPCIBus *bus, uint32_t addr) |
c4efe1ca | 40 | { |
a795fc08 | 41 | return inb(addr); |
c4efe1ca AL |
42 | } |
43 | ||
a795fc08 | 44 | static uint8_t qpci_pc_mmio_readb(QPCIBus *bus, uint32_t addr) |
c4efe1ca | 45 | { |
a795fc08 DG |
46 | return readb(addr); |
47 | } | |
c4efe1ca | 48 | |
a795fc08 DG |
49 | static void qpci_pc_pio_writeb(QPCIBus *bus, uint32_t addr, uint8_t val) |
50 | { | |
51 | outb(addr, val); | |
c4efe1ca AL |
52 | } |
53 | ||
a795fc08 | 54 | static void qpci_pc_mmio_writeb(QPCIBus *bus, uint32_t addr, uint8_t val) |
c4efe1ca | 55 | { |
a795fc08 DG |
56 | writeb(addr, val); |
57 | } | |
c4efe1ca | 58 | |
a795fc08 DG |
59 | static uint16_t qpci_pc_pio_readw(QPCIBus *bus, uint32_t addr) |
60 | { | |
61 | return inw(addr); | |
62 | } | |
c4efe1ca | 63 | |
a795fc08 DG |
64 | static uint16_t qpci_pc_mmio_readw(QPCIBus *bus, uint32_t addr) |
65 | { | |
66 | return readw(addr); | |
c4efe1ca AL |
67 | } |
68 | ||
a795fc08 | 69 | static void qpci_pc_pio_writew(QPCIBus *bus, uint32_t addr, uint16_t val) |
c4efe1ca | 70 | { |
a795fc08 DG |
71 | outw(addr, val); |
72 | } | |
c4efe1ca | 73 | |
a795fc08 DG |
74 | static void qpci_pc_mmio_writew(QPCIBus *bus, uint32_t addr, uint16_t val) |
75 | { | |
76 | writew(addr, val); | |
c4efe1ca AL |
77 | } |
78 | ||
a795fc08 | 79 | static uint32_t qpci_pc_pio_readl(QPCIBus *bus, uint32_t addr) |
c4efe1ca | 80 | { |
a795fc08 DG |
81 | return inl(addr); |
82 | } | |
c4efe1ca | 83 | |
a795fc08 DG |
84 | static uint32_t qpci_pc_mmio_readl(QPCIBus *bus, uint32_t addr) |
85 | { | |
86 | return readl(addr); | |
c4efe1ca AL |
87 | } |
88 | ||
a795fc08 | 89 | static void qpci_pc_pio_writel(QPCIBus *bus, uint32_t addr, uint32_t val) |
c4efe1ca | 90 | { |
a795fc08 DG |
91 | outl(addr, val); |
92 | } | |
c4efe1ca | 93 | |
a795fc08 DG |
94 | static void qpci_pc_mmio_writel(QPCIBus *bus, uint32_t addr, uint32_t val) |
95 | { | |
96 | writel(addr, val); | |
c4efe1ca AL |
97 | } |
98 | ||
99 | static uint8_t qpci_pc_config_readb(QPCIBus *bus, int devfn, uint8_t offset) | |
100 | { | |
a879125b | 101 | outl(0xcf8, (1U << 31) | (devfn << 8) | offset); |
c4efe1ca AL |
102 | return inb(0xcfc); |
103 | } | |
104 | ||
105 | static uint16_t qpci_pc_config_readw(QPCIBus *bus, int devfn, uint8_t offset) | |
106 | { | |
a879125b | 107 | outl(0xcf8, (1U << 31) | (devfn << 8) | offset); |
c4efe1ca AL |
108 | return inw(0xcfc); |
109 | } | |
110 | ||
111 | static uint32_t qpci_pc_config_readl(QPCIBus *bus, int devfn, uint8_t offset) | |
112 | { | |
a879125b | 113 | outl(0xcf8, (1U << 31) | (devfn << 8) | offset); |
c4efe1ca AL |
114 | return inl(0xcfc); |
115 | } | |
116 | ||
117 | static void qpci_pc_config_writeb(QPCIBus *bus, int devfn, uint8_t offset, uint8_t value) | |
118 | { | |
a879125b | 119 | outl(0xcf8, (1U << 31) | (devfn << 8) | offset); |
c4efe1ca AL |
120 | outb(0xcfc, value); |
121 | } | |
122 | ||
123 | static void qpci_pc_config_writew(QPCIBus *bus, int devfn, uint8_t offset, uint16_t value) | |
124 | { | |
a879125b | 125 | outl(0xcf8, (1U << 31) | (devfn << 8) | offset); |
c4efe1ca AL |
126 | outw(0xcfc, value); |
127 | } | |
128 | ||
129 | static void qpci_pc_config_writel(QPCIBus *bus, int devfn, uint8_t offset, uint32_t value) | |
130 | { | |
a879125b | 131 | outl(0xcf8, (1U << 31) | (devfn << 8) | offset); |
c4efe1ca AL |
132 | outl(0xcfc, value); |
133 | } | |
134 | ||
6ce7100e | 135 | static void *qpci_pc_iomap(QPCIBus *bus, QPCIDevice *dev, int barno, uint64_t *sizeptr) |
c4efe1ca AL |
136 | { |
137 | QPCIBusPC *s = container_of(bus, QPCIBusPC, bus); | |
138 | static const int bar_reg_map[] = { | |
139 | PCI_BASE_ADDRESS_0, PCI_BASE_ADDRESS_1, PCI_BASE_ADDRESS_2, | |
140 | PCI_BASE_ADDRESS_3, PCI_BASE_ADDRESS_4, PCI_BASE_ADDRESS_5, | |
141 | }; | |
142 | int bar_reg; | |
143 | uint32_t addr; | |
144 | uint64_t size; | |
145 | uint32_t io_type; | |
146 | ||
147 | g_assert(barno >= 0 && barno <= 5); | |
148 | bar_reg = bar_reg_map[barno]; | |
149 | ||
150 | qpci_config_writel(dev, bar_reg, 0xFFFFFFFF); | |
151 | addr = qpci_config_readl(dev, bar_reg); | |
152 | ||
153 | io_type = addr & PCI_BASE_ADDRESS_SPACE; | |
154 | if (io_type == PCI_BASE_ADDRESS_SPACE_IO) { | |
155 | addr &= PCI_BASE_ADDRESS_IO_MASK; | |
156 | } else { | |
157 | addr &= PCI_BASE_ADDRESS_MEM_MASK; | |
158 | } | |
159 | ||
160 | size = (1ULL << ctzl(addr)); | |
161 | if (size == 0) { | |
162 | return NULL; | |
163 | } | |
6ce7100e JS |
164 | if (sizeptr) { |
165 | *sizeptr = size; | |
166 | } | |
c4efe1ca AL |
167 | |
168 | if (io_type == PCI_BASE_ADDRESS_SPACE_IO) { | |
169 | uint16_t loc; | |
170 | ||
99826172 MA |
171 | g_assert(QEMU_ALIGN_UP(s->pci_iohole_alloc, size) + size |
172 | <= s->pci_iohole_size); | |
173 | s->pci_iohole_alloc = QEMU_ALIGN_UP(s->pci_iohole_alloc, size); | |
c4efe1ca AL |
174 | loc = s->pci_iohole_start + s->pci_iohole_alloc; |
175 | s->pci_iohole_alloc += size; | |
176 | ||
177 | qpci_config_writel(dev, bar_reg, loc | PCI_BASE_ADDRESS_SPACE_IO); | |
178 | ||
179 | return (void *)(intptr_t)loc; | |
180 | } else { | |
181 | uint64_t loc; | |
182 | ||
99826172 MA |
183 | g_assert(QEMU_ALIGN_UP(s->pci_hole_alloc, size) + size |
184 | <= s->pci_hole_size); | |
185 | s->pci_hole_alloc = QEMU_ALIGN_UP(s->pci_hole_alloc, size); | |
c4efe1ca AL |
186 | loc = s->pci_hole_start + s->pci_hole_alloc; |
187 | s->pci_hole_alloc += size; | |
188 | ||
189 | qpci_config_writel(dev, bar_reg, loc); | |
190 | ||
191 | return (void *)(intptr_t)loc; | |
192 | } | |
193 | } | |
194 | ||
195 | static void qpci_pc_iounmap(QPCIBus *bus, void *data) | |
196 | { | |
197 | /* FIXME */ | |
198 | } | |
199 | ||
2ecd7e2f | 200 | QPCIBus *qpci_init_pc(QGuestAllocator *alloc) |
c4efe1ca AL |
201 | { |
202 | QPCIBusPC *ret; | |
203 | ||
204 | ret = g_malloc(sizeof(*ret)); | |
205 | ||
a795fc08 DG |
206 | ret->bus.pio_readb = qpci_pc_pio_readb; |
207 | ret->bus.pio_readw = qpci_pc_pio_readw; | |
208 | ret->bus.pio_readl = qpci_pc_pio_readl; | |
209 | ||
210 | ret->bus.pio_writeb = qpci_pc_pio_writeb; | |
211 | ret->bus.pio_writew = qpci_pc_pio_writew; | |
212 | ret->bus.pio_writel = qpci_pc_pio_writel; | |
213 | ||
214 | ret->bus.mmio_readb = qpci_pc_mmio_readb; | |
215 | ret->bus.mmio_readw = qpci_pc_mmio_readw; | |
216 | ret->bus.mmio_readl = qpci_pc_mmio_readl; | |
c4efe1ca | 217 | |
a795fc08 DG |
218 | ret->bus.mmio_writeb = qpci_pc_mmio_writeb; |
219 | ret->bus.mmio_writew = qpci_pc_mmio_writew; | |
220 | ret->bus.mmio_writel = qpci_pc_mmio_writel; | |
c4efe1ca AL |
221 | |
222 | ret->bus.config_readb = qpci_pc_config_readb; | |
223 | ret->bus.config_readw = qpci_pc_config_readw; | |
224 | ret->bus.config_readl = qpci_pc_config_readl; | |
225 | ||
226 | ret->bus.config_writeb = qpci_pc_config_writeb; | |
227 | ret->bus.config_writew = qpci_pc_config_writew; | |
228 | ret->bus.config_writel = qpci_pc_config_writel; | |
229 | ||
230 | ret->bus.iomap = qpci_pc_iomap; | |
231 | ret->bus.iounmap = qpci_pc_iounmap; | |
232 | ||
233 | ret->pci_hole_start = 0xE0000000; | |
234 | ret->pci_hole_size = 0x20000000; | |
235 | ret->pci_hole_alloc = 0; | |
236 | ||
237 | ret->pci_iohole_start = 0xc000; | |
238 | ret->pci_iohole_size = 0x4000; | |
239 | ret->pci_iohole_alloc = 0; | |
240 | ||
241 | return &ret->bus; | |
242 | } | |
7f2a5ae6 JS |
243 | |
244 | void qpci_free_pc(QPCIBus *bus) | |
245 | { | |
246 | QPCIBusPC *s = container_of(bus, QPCIBusPC, bus); | |
247 | ||
248 | g_free(s); | |
249 | } | |
2f8b2767 | 250 | |
2f8b2767 IM |
251 | void qpci_unplug_acpi_device_test(const char *id, uint8_t slot) |
252 | { | |
253 | QDict *response; | |
254 | char *cmd; | |
255 | ||
256 | cmd = g_strdup_printf("{'execute': 'device_del'," | |
257 | " 'arguments': {" | |
258 | " 'id': '%s'" | |
259 | "}}", id); | |
260 | response = qmp(cmd); | |
261 | g_free(cmd); | |
262 | g_assert(response); | |
263 | g_assert(!qdict_haskey(response, "error")); | |
264 | QDECREF(response); | |
265 | ||
266 | outb(ACPI_PCIHP_ADDR + PCI_EJ_BASE, 1 << slot); | |
267 | ||
268 | response = qmp(""); | |
269 | g_assert(response); | |
270 | g_assert(qdict_haskey(response, "event")); | |
271 | g_assert(!strcmp(qdict_get_str(response, "event"), "DEVICE_DELETED")); | |
272 | QDECREF(response); | |
273 | } |