1 | /* |
2 | * QEMU i440FX/PIIX3 PCI Bridge Emulation |
3 | * |
4 | * Copyright (c) 2006 Fabrice Bellard |
5 | * |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
7 | * of this software and associated documentation files (the "Software"), to deal |
8 | * in the Software without restriction, including without limitation the rights |
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
10 | * copies of the Software, and to permit persons to whom the Software is |
11 | * furnished to do so, subject to the following conditions: |
12 | * |
13 | * The above copyright notice and this permission notice shall be included in |
14 | * all copies or substantial portions of the Software. |
15 | * |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
22 | * THE SOFTWARE. |
23 | */ |
24 | |
25 | #include "qemu/osdep.h" |
26 | #include "hw/i386/pc.h" |
27 | #include "hw/irq.h" |
28 | #include "hw/pci/pci.h" |
29 | #include "hw/pci/pci_host.h" |
30 | #include "hw/qdev-properties.h" |
31 | #include "hw/isa/isa.h" |
32 | #include "hw/sysbus.h" |
33 | #include "qapi/error.h" |
34 | #include "qemu/range.h" |
35 | #include "hw/xen/xen.h" |
36 | #include "migration/qemu-file-types.h" |
37 | #include "migration/vmstate.h" |
38 | #include "hw/pci-host/pam.h" |
39 | #include "sysemu/reset.h" |
40 | #include "sysemu/runstate.h" |
41 | #include "hw/i386/ioapic.h" |
42 | #include "qapi/visitor.h" |
43 | #include "qemu/error-report.h" |
44 | |
45 | /* |
46 | * I440FX chipset data sheet. |
47 | * https://wiki.qemu.org/File:29054901.pdf |
48 | */ |
49 | |
50 | #define I440FX_PCI_HOST_BRIDGE(obj) \ |
51 | OBJECT_CHECK(I440FXState, (obj), TYPE_I440FX_PCI_HOST_BRIDGE) |
52 | |
53 | typedef struct I440FXState { |
54 | PCIHostState parent_obj; |
55 | Range pci_hole; |
56 | uint64_t pci_hole64_size; |
57 | bool pci_hole64_fix; |
58 | uint32_t short_root_bus; |
59 | } I440FXState; |
60 | |
61 | #define PIIX_NUM_PIC_IRQS 16 /* i8259 * 2 */ |
62 | #define PIIX_NUM_PIRQS 4ULL /* PIRQ[A-D] */ |
63 | #define XEN_PIIX_NUM_PIRQS 128ULL |
64 | #define PIIX_PIRQC 0x60 |
65 | |
66 | typedef struct PIIX3State { |
67 | PCIDevice dev; |
68 | |
69 | /* |
70 | * bitmap to track pic levels. |
71 | * The pic level is the logical OR of all the PCI irqs mapped to it |
72 | * So one PIC level is tracked by PIIX_NUM_PIRQS bits. |
73 | * |
74 | * PIRQ is mapped to PIC pins, we track it by |
75 | * PIIX_NUM_PIRQS * PIIX_NUM_PIC_IRQS = 64 bits with |
76 | * pic_irq * PIIX_NUM_PIRQS + pirq |
77 | */ |
78 | #if PIIX_NUM_PIC_IRQS * PIIX_NUM_PIRQS > 64 |
79 | #error "unable to encode pic state in 64bit in pic_levels." |
80 | #endif |
81 | uint64_t pic_levels; |
82 | |
83 | qemu_irq *pic; |
84 | |
85 | /* This member isn't used. Just for save/load compatibility */ |
86 | int32_t pci_irq_levels_vmstate[PIIX_NUM_PIRQS]; |
87 | |
88 | /* Reset Control Register contents */ |
89 | uint8_t rcr; |
90 | |
91 | /* IO memory region for Reset Control Register (RCR_IOPORT) */ |
92 | MemoryRegion rcr_mem; |
93 | } PIIX3State; |
94 | |
95 | #define TYPE_PIIX3_PCI_DEVICE "pci-piix3" |
96 | #define PIIX3_PCI_DEVICE(obj) \ |
97 | OBJECT_CHECK(PIIX3State, (obj), TYPE_PIIX3_PCI_DEVICE) |
98 | |
99 | #define I440FX_PCI_DEVICE(obj) \ |
100 | OBJECT_CHECK(PCII440FXState, (obj), TYPE_I440FX_PCI_DEVICE) |
101 | |
102 | #define TYPE_PIIX3_DEVICE "PIIX3" |
103 | #define TYPE_PIIX3_XEN_DEVICE "PIIX3-xen" |
104 | |
105 | struct PCII440FXState { |
106 | /*< private >*/ |
107 | PCIDevice parent_obj; |
108 | /*< public >*/ |
109 | |
110 | MemoryRegion *system_memory; |
111 | MemoryRegion *pci_address_space; |
112 | MemoryRegion *ram_memory; |
113 | PAMMemoryRegion pam_regions[13]; |
114 | MemoryRegion smram_region; |
115 | MemoryRegion smram, low_smram; |
116 | }; |
117 | |
118 | |
119 | #define I440FX_PAM 0x59 |
120 | #define I440FX_PAM_SIZE 7 |
121 | #define I440FX_SMRAM 0x72 |
122 | |
123 | /* Keep it 2G to comply with older win32 guests */ |
124 | #define I440FX_PCI_HOST_HOLE64_SIZE_DEFAULT (1ULL << 31) |
125 | |
126 | /* Older coreboot versions (4.0 and older) read a config register that doesn't |
127 | * exist in real hardware, to get the RAM size from QEMU. |
128 | */ |
129 | #define I440FX_COREBOOT_RAM_SIZE 0x57 |
130 | |
131 | static void piix3_set_irq(void *opaque, int pirq, int level); |
132 | static PCIINTxRoute piix3_route_intx_pin_to_irq(void *opaque, int pci_intx); |
133 | static void piix3_write_config_xen(PCIDevice *dev, |
134 | uint32_t address, uint32_t val, int len); |
135 | |
136 | /* return the global irq number corresponding to a given device irq |
137 | pin. We could also use the bus number to have a more precise |
138 | mapping. */ |
139 | static int pci_slot_get_pirq(PCIDevice *pci_dev, int pci_intx) |
140 | { |
141 | int slot_addend; |
142 | slot_addend = (pci_dev->devfn >> 3) - 1; |
143 | return (pci_intx + slot_addend) & 3; |
144 | } |
145 | |
146 | static void i440fx_update_memory_mappings(PCII440FXState *d) |
147 | { |
148 | int i; |
149 | PCIDevice *pd = PCI_DEVICE(d); |
150 | |
151 | memory_region_transaction_begin(); |
152 | for (i = 0; i < ARRAY_SIZE(d->pam_regions); i++) { |
153 | pam_update(&d->pam_regions[i], i, |
154 | pd->config[I440FX_PAM + DIV_ROUND_UP(i, 2)]); |
155 | } |
156 | memory_region_set_enabled(&d->smram_region, |
157 | !(pd->config[I440FX_SMRAM] & SMRAM_D_OPEN)); |
158 | memory_region_set_enabled(&d->smram, |
159 | pd->config[I440FX_SMRAM] & SMRAM_G_SMRAME); |
160 | memory_region_transaction_commit(); |
161 | } |
162 | |
163 | |
164 | static void i440fx_write_config(PCIDevice *dev, |
165 | uint32_t address, uint32_t val, int len) |
166 | { |
167 | PCII440FXState *d = I440FX_PCI_DEVICE(dev); |
168 | |
169 | /* XXX: implement SMRAM.D_LOCK */ |
170 | pci_default_write_config(dev, address, val, len); |
171 | if (ranges_overlap(address, len, I440FX_PAM, I440FX_PAM_SIZE) || |
172 | range_covers_byte(address, len, I440FX_SMRAM)) { |
173 | i440fx_update_memory_mappings(d); |
174 | } |
175 | } |
176 | |
177 | static int i440fx_load_old(QEMUFile* f, void *opaque, int version_id) |
178 | { |
179 | PCII440FXState *d = opaque; |
180 | PCIDevice *pd = PCI_DEVICE(d); |
181 | int ret, i; |
182 | uint8_t smm_enabled; |
183 | |
184 | ret = pci_device_load(pd, f); |
185 | if (ret < 0) |
186 | return ret; |
187 | i440fx_update_memory_mappings(d); |
188 | qemu_get_8s(f, &smm_enabled); |
189 | |
190 | if (version_id == 2) { |
191 | for (i = 0; i < PIIX_NUM_PIRQS; i++) { |
192 | qemu_get_be32(f); /* dummy load for compatibility */ |
193 | } |
194 | } |
195 | |
196 | return 0; |
197 | } |
198 | |
199 | static int i440fx_post_load(void *opaque, int version_id) |
200 | { |
201 | PCII440FXState *d = opaque; |
202 | |
203 | i440fx_update_memory_mappings(d); |
204 | return 0; |
205 | } |
206 | |
207 | static const VMStateDescription vmstate_i440fx = { |
208 | .name = "I440FX" , |
209 | .version_id = 3, |
210 | .minimum_version_id = 3, |
211 | .minimum_version_id_old = 1, |
212 | .load_state_old = i440fx_load_old, |
213 | .post_load = i440fx_post_load, |
214 | .fields = (VMStateField[]) { |
215 | VMSTATE_PCI_DEVICE(parent_obj, PCII440FXState), |
216 | /* Used to be smm_enabled, which was basically always zero because |
217 | * SeaBIOS hardly uses SMM. SMRAM is now handled by CPU code. |
218 | */ |
219 | VMSTATE_UNUSED(1), |
220 | VMSTATE_END_OF_LIST() |
221 | } |
222 | }; |
223 | |
224 | static void i440fx_pcihost_get_pci_hole_start(Object *obj, Visitor *v, |
225 | const char *name, void *opaque, |
226 | Error **errp) |
227 | { |
228 | I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj); |
229 | uint64_t val64; |
230 | uint32_t value; |
231 | |
232 | val64 = range_is_empty(&s->pci_hole) ? 0 : range_lob(&s->pci_hole); |
233 | value = val64; |
234 | assert(value == val64); |
235 | visit_type_uint32(v, name, &value, errp); |
236 | } |
237 | |
238 | static void i440fx_pcihost_get_pci_hole_end(Object *obj, Visitor *v, |
239 | const char *name, void *opaque, |
240 | Error **errp) |
241 | { |
242 | I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj); |
243 | uint64_t val64; |
244 | uint32_t value; |
245 | |
246 | val64 = range_is_empty(&s->pci_hole) ? 0 : range_upb(&s->pci_hole) + 1; |
247 | value = val64; |
248 | assert(value == val64); |
249 | visit_type_uint32(v, name, &value, errp); |
250 | } |
251 | |
252 | /* |
253 | * The 64bit PCI hole start is set by the Guest firmware |
254 | * as the address of the first 64bit PCI MEM resource. |
255 | * If no PCI device has resources on the 64bit area, |
256 | * the 64bit PCI hole will start after "over 4G RAM" and the |
257 | * reserved space for memory hotplug if any. |
258 | */ |
259 | static uint64_t i440fx_pcihost_get_pci_hole64_start_value(Object *obj) |
260 | { |
261 | PCIHostState *h = PCI_HOST_BRIDGE(obj); |
262 | I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj); |
263 | Range w64; |
264 | uint64_t value; |
265 | |
266 | pci_bus_get_w64_range(h->bus, &w64); |
267 | value = range_is_empty(&w64) ? 0 : range_lob(&w64); |
268 | if (!value && s->pci_hole64_fix) { |
269 | value = pc_pci_hole64_start(); |
270 | } |
271 | return value; |
272 | } |
273 | |
274 | static void i440fx_pcihost_get_pci_hole64_start(Object *obj, Visitor *v, |
275 | const char *name, |
276 | void *opaque, Error **errp) |
277 | { |
278 | uint64_t hole64_start = i440fx_pcihost_get_pci_hole64_start_value(obj); |
279 | |
280 | visit_type_uint64(v, name, &hole64_start, errp); |
281 | } |
282 | |
283 | /* |
284 | * The 64bit PCI hole end is set by the Guest firmware |
285 | * as the address of the last 64bit PCI MEM resource. |
286 | * Then it is expanded to the PCI_HOST_PROP_PCI_HOLE64_SIZE |
287 | * that can be configured by the user. |
288 | */ |
289 | static void i440fx_pcihost_get_pci_hole64_end(Object *obj, Visitor *v, |
290 | const char *name, void *opaque, |
291 | Error **errp) |
292 | { |
293 | PCIHostState *h = PCI_HOST_BRIDGE(obj); |
294 | I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj); |
295 | uint64_t hole64_start = i440fx_pcihost_get_pci_hole64_start_value(obj); |
296 | Range w64; |
297 | uint64_t value, hole64_end; |
298 | |
299 | pci_bus_get_w64_range(h->bus, &w64); |
300 | value = range_is_empty(&w64) ? 0 : range_upb(&w64) + 1; |
301 | hole64_end = ROUND_UP(hole64_start + s->pci_hole64_size, 1ULL << 30); |
302 | if (s->pci_hole64_fix && value < hole64_end) { |
303 | value = hole64_end; |
304 | } |
305 | visit_type_uint64(v, name, &value, errp); |
306 | } |
307 | |
308 | static void i440fx_pcihost_initfn(Object *obj) |
309 | { |
310 | PCIHostState *s = PCI_HOST_BRIDGE(obj); |
311 | |
312 | memory_region_init_io(&s->conf_mem, obj, &pci_host_conf_le_ops, s, |
313 | "pci-conf-idx" , 4); |
314 | memory_region_init_io(&s->data_mem, obj, &pci_host_data_le_ops, s, |
315 | "pci-conf-data" , 4); |
316 | |
317 | object_property_add(obj, PCI_HOST_PROP_PCI_HOLE_START, "uint32" , |
318 | i440fx_pcihost_get_pci_hole_start, |
319 | NULL, NULL, NULL, NULL); |
320 | |
321 | object_property_add(obj, PCI_HOST_PROP_PCI_HOLE_END, "uint32" , |
322 | i440fx_pcihost_get_pci_hole_end, |
323 | NULL, NULL, NULL, NULL); |
324 | |
325 | object_property_add(obj, PCI_HOST_PROP_PCI_HOLE64_START, "uint64" , |
326 | i440fx_pcihost_get_pci_hole64_start, |
327 | NULL, NULL, NULL, NULL); |
328 | |
329 | object_property_add(obj, PCI_HOST_PROP_PCI_HOLE64_END, "uint64" , |
330 | i440fx_pcihost_get_pci_hole64_end, |
331 | NULL, NULL, NULL, NULL); |
332 | } |
333 | |
334 | static void i440fx_pcihost_realize(DeviceState *dev, Error **errp) |
335 | { |
336 | PCIHostState *s = PCI_HOST_BRIDGE(dev); |
337 | SysBusDevice *sbd = SYS_BUS_DEVICE(dev); |
338 | |
339 | sysbus_add_io(sbd, 0xcf8, &s->conf_mem); |
340 | sysbus_init_ioports(sbd, 0xcf8, 4); |
341 | |
342 | sysbus_add_io(sbd, 0xcfc, &s->data_mem); |
343 | sysbus_init_ioports(sbd, 0xcfc, 4); |
344 | |
345 | /* register i440fx 0xcf8 port as coalesced pio */ |
346 | memory_region_set_flush_coalesced(&s->data_mem); |
347 | memory_region_add_coalescing(&s->conf_mem, 0, 4); |
348 | } |
349 | |
350 | static void i440fx_realize(PCIDevice *dev, Error **errp) |
351 | { |
352 | dev->config[I440FX_SMRAM] = 0x02; |
353 | |
354 | if (object_property_get_bool(qdev_get_machine(), "iommu" , NULL)) { |
355 | warn_report("i440fx doesn't support emulated iommu" ); |
356 | } |
357 | } |
358 | |
359 | PCIBus *i440fx_init(const char *host_type, const char *pci_type, |
360 | PCII440FXState **pi440fx_state, |
361 | int *piix3_devfn, |
362 | ISABus **isa_bus, qemu_irq *pic, |
363 | MemoryRegion *address_space_mem, |
364 | MemoryRegion *address_space_io, |
365 | ram_addr_t ram_size, |
366 | ram_addr_t below_4g_mem_size, |
367 | ram_addr_t above_4g_mem_size, |
368 | MemoryRegion *pci_address_space, |
369 | MemoryRegion *ram_memory) |
370 | { |
371 | DeviceState *dev; |
372 | PCIBus *b; |
373 | PCIDevice *d; |
374 | PCIHostState *s; |
375 | PIIX3State *piix3; |
376 | PCII440FXState *f; |
377 | unsigned i; |
378 | I440FXState *i440fx; |
379 | |
380 | dev = qdev_create(NULL, host_type); |
381 | s = PCI_HOST_BRIDGE(dev); |
382 | b = pci_root_bus_new(dev, NULL, pci_address_space, |
383 | address_space_io, 0, TYPE_PCI_BUS); |
384 | s->bus = b; |
385 | object_property_add_child(qdev_get_machine(), "i440fx" , OBJECT(dev), NULL); |
386 | qdev_init_nofail(dev); |
387 | |
388 | d = pci_create_simple(b, 0, pci_type); |
389 | *pi440fx_state = I440FX_PCI_DEVICE(d); |
390 | f = *pi440fx_state; |
391 | f->system_memory = address_space_mem; |
392 | f->pci_address_space = pci_address_space; |
393 | f->ram_memory = ram_memory; |
394 | |
395 | i440fx = I440FX_PCI_HOST_BRIDGE(dev); |
396 | range_set_bounds(&i440fx->pci_hole, below_4g_mem_size, |
397 | IO_APIC_DEFAULT_ADDRESS - 1); |
398 | |
399 | /* setup pci memory mapping */ |
400 | pc_pci_as_mapping_init(OBJECT(f), f->system_memory, |
401 | f->pci_address_space); |
402 | |
403 | /* if *disabled* show SMRAM to all CPUs */ |
404 | memory_region_init_alias(&f->smram_region, OBJECT(d), "smram-region" , |
405 | f->pci_address_space, 0xa0000, 0x20000); |
406 | memory_region_add_subregion_overlap(f->system_memory, 0xa0000, |
407 | &f->smram_region, 1); |
408 | memory_region_set_enabled(&f->smram_region, true); |
409 | |
410 | /* smram, as seen by SMM CPUs */ |
411 | memory_region_init(&f->smram, OBJECT(d), "smram" , 1ull << 32); |
412 | memory_region_set_enabled(&f->smram, true); |
413 | memory_region_init_alias(&f->low_smram, OBJECT(d), "smram-low" , |
414 | f->ram_memory, 0xa0000, 0x20000); |
415 | memory_region_set_enabled(&f->low_smram, true); |
416 | memory_region_add_subregion(&f->smram, 0xa0000, &f->low_smram); |
417 | object_property_add_const_link(qdev_get_machine(), "smram" , |
418 | OBJECT(&f->smram), &error_abort); |
419 | |
420 | init_pam(dev, f->ram_memory, f->system_memory, f->pci_address_space, |
421 | &f->pam_regions[0], PAM_BIOS_BASE, PAM_BIOS_SIZE); |
422 | for (i = 0; i < ARRAY_SIZE(f->pam_regions) - 1; ++i) { |
423 | init_pam(dev, f->ram_memory, f->system_memory, f->pci_address_space, |
424 | &f->pam_regions[i+1], PAM_EXPAN_BASE + i * PAM_EXPAN_SIZE, |
425 | PAM_EXPAN_SIZE); |
426 | } |
427 | |
428 | /* Xen supports additional interrupt routes from the PCI devices to |
429 | * the IOAPIC: the four pins of each PCI device on the bus are also |
430 | * connected to the IOAPIC directly. |
431 | * These additional routes can be discovered through ACPI. */ |
432 | if (xen_enabled()) { |
433 | PCIDevice *pci_dev = pci_create_simple_multifunction(b, |
434 | -1, true, TYPE_PIIX3_XEN_DEVICE); |
435 | piix3 = PIIX3_PCI_DEVICE(pci_dev); |
436 | pci_bus_irqs(b, xen_piix3_set_irq, xen_pci_slot_get_pirq, |
437 | piix3, XEN_PIIX_NUM_PIRQS); |
438 | } else { |
439 | PCIDevice *pci_dev = pci_create_simple_multifunction(b, |
440 | -1, true, TYPE_PIIX3_DEVICE); |
441 | piix3 = PIIX3_PCI_DEVICE(pci_dev); |
442 | pci_bus_irqs(b, piix3_set_irq, pci_slot_get_pirq, piix3, |
443 | PIIX_NUM_PIRQS); |
444 | pci_bus_set_route_irq_fn(b, piix3_route_intx_pin_to_irq); |
445 | } |
446 | piix3->pic = pic; |
447 | *isa_bus = ISA_BUS(qdev_get_child_bus(DEVICE(piix3), "isa.0" )); |
448 | |
449 | *piix3_devfn = piix3->dev.devfn; |
450 | |
451 | ram_size = ram_size / 8 / 1024 / 1024; |
452 | if (ram_size > 255) { |
453 | ram_size = 255; |
454 | } |
455 | d->config[I440FX_COREBOOT_RAM_SIZE] = ram_size; |
456 | |
457 | i440fx_update_memory_mappings(f); |
458 | |
459 | return b; |
460 | } |
461 | |
462 | PCIBus *find_i440fx(void) |
463 | { |
464 | PCIHostState *s = OBJECT_CHECK(PCIHostState, |
465 | object_resolve_path("/machine/i440fx" , NULL), |
466 | TYPE_PCI_HOST_BRIDGE); |
467 | return s ? s->bus : NULL; |
468 | } |
469 | |
470 | /* PIIX3 PCI to ISA bridge */ |
471 | static void piix3_set_irq_pic(PIIX3State *piix3, int pic_irq) |
472 | { |
473 | qemu_set_irq(piix3->pic[pic_irq], |
474 | !!(piix3->pic_levels & |
475 | (((1ULL << PIIX_NUM_PIRQS) - 1) << |
476 | (pic_irq * PIIX_NUM_PIRQS)))); |
477 | } |
478 | |
479 | static void piix3_set_irq_level_internal(PIIX3State *piix3, int pirq, int level) |
480 | { |
481 | int pic_irq; |
482 | uint64_t mask; |
483 | |
484 | pic_irq = piix3->dev.config[PIIX_PIRQC + pirq]; |
485 | if (pic_irq >= PIIX_NUM_PIC_IRQS) { |
486 | return; |
487 | } |
488 | |
489 | mask = 1ULL << ((pic_irq * PIIX_NUM_PIRQS) + pirq); |
490 | piix3->pic_levels &= ~mask; |
491 | piix3->pic_levels |= mask * !!level; |
492 | } |
493 | |
494 | static void piix3_set_irq_level(PIIX3State *piix3, int pirq, int level) |
495 | { |
496 | int pic_irq; |
497 | |
498 | pic_irq = piix3->dev.config[PIIX_PIRQC + pirq]; |
499 | if (pic_irq >= PIIX_NUM_PIC_IRQS) { |
500 | return; |
501 | } |
502 | |
503 | piix3_set_irq_level_internal(piix3, pirq, level); |
504 | |
505 | piix3_set_irq_pic(piix3, pic_irq); |
506 | } |
507 | |
508 | static void piix3_set_irq(void *opaque, int pirq, int level) |
509 | { |
510 | PIIX3State *piix3 = opaque; |
511 | piix3_set_irq_level(piix3, pirq, level); |
512 | } |
513 | |
514 | static PCIINTxRoute piix3_route_intx_pin_to_irq(void *opaque, int pin) |
515 | { |
516 | PIIX3State *piix3 = opaque; |
517 | int irq = piix3->dev.config[PIIX_PIRQC + pin]; |
518 | PCIINTxRoute route; |
519 | |
520 | if (irq < PIIX_NUM_PIC_IRQS) { |
521 | route.mode = PCI_INTX_ENABLED; |
522 | route.irq = irq; |
523 | } else { |
524 | route.mode = PCI_INTX_DISABLED; |
525 | route.irq = -1; |
526 | } |
527 | return route; |
528 | } |
529 | |
530 | /* irq routing is changed. so rebuild bitmap */ |
531 | static void piix3_update_irq_levels(PIIX3State *piix3) |
532 | { |
533 | PCIBus *bus = pci_get_bus(&piix3->dev); |
534 | int pirq; |
535 | |
536 | piix3->pic_levels = 0; |
537 | for (pirq = 0; pirq < PIIX_NUM_PIRQS; pirq++) { |
538 | piix3_set_irq_level(piix3, pirq, pci_bus_get_irq_level(bus, pirq)); |
539 | } |
540 | } |
541 | |
542 | static void piix3_write_config(PCIDevice *dev, |
543 | uint32_t address, uint32_t val, int len) |
544 | { |
545 | pci_default_write_config(dev, address, val, len); |
546 | if (ranges_overlap(address, len, PIIX_PIRQC, 4)) { |
547 | PIIX3State *piix3 = PIIX3_PCI_DEVICE(dev); |
548 | int pic_irq; |
549 | |
550 | pci_bus_fire_intx_routing_notifier(pci_get_bus(&piix3->dev)); |
551 | piix3_update_irq_levels(piix3); |
552 | for (pic_irq = 0; pic_irq < PIIX_NUM_PIC_IRQS; pic_irq++) { |
553 | piix3_set_irq_pic(piix3, pic_irq); |
554 | } |
555 | } |
556 | } |
557 | |
558 | static void piix3_write_config_xen(PCIDevice *dev, |
559 | uint32_t address, uint32_t val, int len) |
560 | { |
561 | xen_piix_pci_write_config_client(address, val, len); |
562 | piix3_write_config(dev, address, val, len); |
563 | } |
564 | |
565 | static void piix3_reset(void *opaque) |
566 | { |
567 | PIIX3State *d = opaque; |
568 | uint8_t *pci_conf = d->dev.config; |
569 | |
570 | pci_conf[0x04] = 0x07; /* master, memory and I/O */ |
571 | pci_conf[0x05] = 0x00; |
572 | pci_conf[0x06] = 0x00; |
573 | pci_conf[0x07] = 0x02; /* PCI_status_devsel_medium */ |
574 | pci_conf[0x4c] = 0x4d; |
575 | pci_conf[0x4e] = 0x03; |
576 | pci_conf[0x4f] = 0x00; |
577 | pci_conf[0x60] = 0x80; |
578 | pci_conf[0x61] = 0x80; |
579 | pci_conf[0x62] = 0x80; |
580 | pci_conf[0x63] = 0x80; |
581 | pci_conf[0x69] = 0x02; |
582 | pci_conf[0x70] = 0x80; |
583 | pci_conf[0x76] = 0x0c; |
584 | pci_conf[0x77] = 0x0c; |
585 | pci_conf[0x78] = 0x02; |
586 | pci_conf[0x79] = 0x00; |
587 | pci_conf[0x80] = 0x00; |
588 | pci_conf[0x82] = 0x00; |
589 | pci_conf[0xa0] = 0x08; |
590 | pci_conf[0xa2] = 0x00; |
591 | pci_conf[0xa3] = 0x00; |
592 | pci_conf[0xa4] = 0x00; |
593 | pci_conf[0xa5] = 0x00; |
594 | pci_conf[0xa6] = 0x00; |
595 | pci_conf[0xa7] = 0x00; |
596 | pci_conf[0xa8] = 0x0f; |
597 | pci_conf[0xaa] = 0x00; |
598 | pci_conf[0xab] = 0x00; |
599 | pci_conf[0xac] = 0x00; |
600 | pci_conf[0xae] = 0x00; |
601 | |
602 | d->pic_levels = 0; |
603 | d->rcr = 0; |
604 | } |
605 | |
606 | static int piix3_post_load(void *opaque, int version_id) |
607 | { |
608 | PIIX3State *piix3 = opaque; |
609 | int pirq; |
610 | |
611 | /* Because the i8259 has not been deserialized yet, qemu_irq_raise |
612 | * might bring the system to a different state than the saved one; |
613 | * for example, the interrupt could be masked but the i8259 would |
614 | * not know that yet and would trigger an interrupt in the CPU. |
615 | * |
616 | * Here, we update irq levels without raising the interrupt. |
617 | * Interrupt state will be deserialized separately through the i8259. |
618 | */ |
619 | piix3->pic_levels = 0; |
620 | for (pirq = 0; pirq < PIIX_NUM_PIRQS; pirq++) { |
621 | piix3_set_irq_level_internal(piix3, pirq, |
622 | pci_bus_get_irq_level(pci_get_bus(&piix3->dev), pirq)); |
623 | } |
624 | return 0; |
625 | } |
626 | |
627 | static int piix3_pre_save(void *opaque) |
628 | { |
629 | int i; |
630 | PIIX3State *piix3 = opaque; |
631 | |
632 | for (i = 0; i < ARRAY_SIZE(piix3->pci_irq_levels_vmstate); i++) { |
633 | piix3->pci_irq_levels_vmstate[i] = |
634 | pci_bus_get_irq_level(pci_get_bus(&piix3->dev), i); |
635 | } |
636 | |
637 | return 0; |
638 | } |
639 | |
640 | static bool piix3_rcr_needed(void *opaque) |
641 | { |
642 | PIIX3State *piix3 = opaque; |
643 | |
644 | return (piix3->rcr != 0); |
645 | } |
646 | |
647 | static const VMStateDescription vmstate_piix3_rcr = { |
648 | .name = "PIIX3/rcr" , |
649 | .version_id = 1, |
650 | .minimum_version_id = 1, |
651 | .needed = piix3_rcr_needed, |
652 | .fields = (VMStateField[]) { |
653 | VMSTATE_UINT8(rcr, PIIX3State), |
654 | VMSTATE_END_OF_LIST() |
655 | } |
656 | }; |
657 | |
658 | static const VMStateDescription vmstate_piix3 = { |
659 | .name = "PIIX3" , |
660 | .version_id = 3, |
661 | .minimum_version_id = 2, |
662 | .post_load = piix3_post_load, |
663 | .pre_save = piix3_pre_save, |
664 | .fields = (VMStateField[]) { |
665 | VMSTATE_PCI_DEVICE(dev, PIIX3State), |
666 | VMSTATE_INT32_ARRAY_V(pci_irq_levels_vmstate, PIIX3State, |
667 | PIIX_NUM_PIRQS, 3), |
668 | VMSTATE_END_OF_LIST() |
669 | }, |
670 | .subsections = (const VMStateDescription*[]) { |
671 | &vmstate_piix3_rcr, |
672 | NULL |
673 | } |
674 | }; |
675 | |
676 | |
677 | static void rcr_write(void *opaque, hwaddr addr, uint64_t val, unsigned len) |
678 | { |
679 | PIIX3State *d = opaque; |
680 | |
681 | if (val & 4) { |
682 | qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); |
683 | return; |
684 | } |
685 | d->rcr = val & 2; /* keep System Reset type only */ |
686 | } |
687 | |
688 | static uint64_t rcr_read(void *opaque, hwaddr addr, unsigned len) |
689 | { |
690 | PIIX3State *d = opaque; |
691 | |
692 | return d->rcr; |
693 | } |
694 | |
695 | static const MemoryRegionOps rcr_ops = { |
696 | .read = rcr_read, |
697 | .write = rcr_write, |
698 | .endianness = DEVICE_LITTLE_ENDIAN |
699 | }; |
700 | |
701 | static void piix3_realize(PCIDevice *dev, Error **errp) |
702 | { |
703 | PIIX3State *d = PIIX3_PCI_DEVICE(dev); |
704 | |
705 | if (!isa_bus_new(DEVICE(d), get_system_memory(), |
706 | pci_address_space_io(dev), errp)) { |
707 | return; |
708 | } |
709 | |
710 | memory_region_init_io(&d->rcr_mem, OBJECT(dev), &rcr_ops, d, |
711 | "piix3-reset-control" , 1); |
712 | memory_region_add_subregion_overlap(pci_address_space_io(dev), RCR_IOPORT, |
713 | &d->rcr_mem, 1); |
714 | |
715 | qemu_register_reset(piix3_reset, d); |
716 | } |
717 | |
718 | static void pci_piix3_class_init(ObjectClass *klass, void *data) |
719 | { |
720 | DeviceClass *dc = DEVICE_CLASS(klass); |
721 | PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); |
722 | |
723 | dc->desc = "ISA bridge" ; |
724 | dc->vmsd = &vmstate_piix3; |
725 | dc->hotpluggable = false; |
726 | k->realize = piix3_realize; |
727 | k->vendor_id = PCI_VENDOR_ID_INTEL; |
728 | /* 82371SB PIIX3 PCI-to-ISA bridge (Step A1) */ |
729 | k->device_id = PCI_DEVICE_ID_INTEL_82371SB_0; |
730 | k->class_id = PCI_CLASS_BRIDGE_ISA; |
731 | /* |
732 | * Reason: part of PIIX3 southbridge, needs to be wired up by |
733 | * pc_piix.c's pc_init1() |
734 | */ |
735 | dc->user_creatable = false; |
736 | } |
737 | |
738 | static const TypeInfo piix3_pci_type_info = { |
739 | .name = TYPE_PIIX3_PCI_DEVICE, |
740 | .parent = TYPE_PCI_DEVICE, |
741 | .instance_size = sizeof(PIIX3State), |
742 | .abstract = true, |
743 | .class_init = pci_piix3_class_init, |
744 | .interfaces = (InterfaceInfo[]) { |
745 | { INTERFACE_CONVENTIONAL_PCI_DEVICE }, |
746 | { }, |
747 | }, |
748 | }; |
749 | |
750 | static void piix3_class_init(ObjectClass *klass, void *data) |
751 | { |
752 | PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); |
753 | |
754 | k->config_write = piix3_write_config; |
755 | } |
756 | |
757 | static const TypeInfo piix3_info = { |
758 | .name = TYPE_PIIX3_DEVICE, |
759 | .parent = TYPE_PIIX3_PCI_DEVICE, |
760 | .class_init = piix3_class_init, |
761 | }; |
762 | |
763 | static void piix3_xen_class_init(ObjectClass *klass, void *data) |
764 | { |
765 | PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); |
766 | |
767 | k->config_write = piix3_write_config_xen; |
768 | }; |
769 | |
770 | static const TypeInfo piix3_xen_info = { |
771 | .name = TYPE_PIIX3_XEN_DEVICE, |
772 | .parent = TYPE_PIIX3_PCI_DEVICE, |
773 | .class_init = piix3_xen_class_init, |
774 | }; |
775 | |
776 | static void i440fx_class_init(ObjectClass *klass, void *data) |
777 | { |
778 | DeviceClass *dc = DEVICE_CLASS(klass); |
779 | PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); |
780 | |
781 | k->realize = i440fx_realize; |
782 | k->config_write = i440fx_write_config; |
783 | k->vendor_id = PCI_VENDOR_ID_INTEL; |
784 | k->device_id = PCI_DEVICE_ID_INTEL_82441; |
785 | k->revision = 0x02; |
786 | k->class_id = PCI_CLASS_BRIDGE_HOST; |
787 | dc->desc = "Host bridge" ; |
788 | dc->vmsd = &vmstate_i440fx; |
789 | /* |
790 | * PCI-facing part of the host bridge, not usable without the |
791 | * host-facing part, which can't be device_add'ed, yet. |
792 | */ |
793 | dc->user_creatable = false; |
794 | dc->hotpluggable = false; |
795 | } |
796 | |
797 | static const TypeInfo i440fx_info = { |
798 | .name = TYPE_I440FX_PCI_DEVICE, |
799 | .parent = TYPE_PCI_DEVICE, |
800 | .instance_size = sizeof(PCII440FXState), |
801 | .class_init = i440fx_class_init, |
802 | .interfaces = (InterfaceInfo[]) { |
803 | { INTERFACE_CONVENTIONAL_PCI_DEVICE }, |
804 | { }, |
805 | }, |
806 | }; |
807 | |
808 | /* IGD Passthrough Host Bridge. */ |
809 | typedef struct { |
810 | uint8_t offset; |
811 | uint8_t len; |
812 | } IGDHostInfo; |
813 | |
814 | /* Here we just expose minimal host bridge offset subset. */ |
815 | static const IGDHostInfo igd_host_bridge_infos[] = { |
816 | {0x08, 2}, /* revision id */ |
817 | {0x2c, 2}, /* sybsystem vendor id */ |
818 | {0x2e, 2}, /* sybsystem id */ |
819 | {0x50, 2}, /* SNB: processor graphics control register */ |
820 | {0x52, 2}, /* processor graphics control register */ |
821 | {0xa4, 4}, /* SNB: graphics base of stolen memory */ |
822 | {0xa8, 4}, /* SNB: base of GTT stolen memory */ |
823 | }; |
824 | |
825 | static void host_pci_config_read(int pos, int len, uint32_t *val, Error **errp) |
826 | { |
827 | int rc, config_fd; |
828 | /* Access real host bridge. */ |
829 | char *path = g_strdup_printf("/sys/bus/pci/devices/%04x:%02x:%02x.%d/%s" , |
830 | 0, 0, 0, 0, "config" ); |
831 | |
832 | config_fd = open(path, O_RDWR); |
833 | if (config_fd < 0) { |
834 | error_setg_errno(errp, errno, "Failed to open: %s" , path); |
835 | goto out; |
836 | } |
837 | |
838 | if (lseek(config_fd, pos, SEEK_SET) != pos) { |
839 | error_setg_errno(errp, errno, "Failed to seek: %s" , path); |
840 | goto out_close_fd; |
841 | } |
842 | |
843 | do { |
844 | rc = read(config_fd, (uint8_t *)val, len); |
845 | } while (rc < 0 && (errno == EINTR || errno == EAGAIN)); |
846 | if (rc != len) { |
847 | error_setg_errno(errp, errno, "Failed to read: %s" , path); |
848 | } |
849 | |
850 | out_close_fd: |
851 | close(config_fd); |
852 | out: |
853 | g_free(path); |
854 | } |
855 | |
856 | static void igd_pt_i440fx_realize(PCIDevice *pci_dev, Error **errp) |
857 | { |
858 | uint32_t val = 0; |
859 | int i, num; |
860 | int pos, len; |
861 | Error *local_err = NULL; |
862 | |
863 | num = ARRAY_SIZE(igd_host_bridge_infos); |
864 | for (i = 0; i < num; i++) { |
865 | pos = igd_host_bridge_infos[i].offset; |
866 | len = igd_host_bridge_infos[i].len; |
867 | host_pci_config_read(pos, len, &val, &local_err); |
868 | if (local_err) { |
869 | error_propagate(errp, local_err); |
870 | return; |
871 | } |
872 | pci_default_write_config(pci_dev, pos, val, len); |
873 | } |
874 | } |
875 | |
876 | static void igd_passthrough_i440fx_class_init(ObjectClass *klass, void *data) |
877 | { |
878 | DeviceClass *dc = DEVICE_CLASS(klass); |
879 | PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); |
880 | |
881 | k->realize = igd_pt_i440fx_realize; |
882 | dc->desc = "IGD Passthrough Host bridge" ; |
883 | } |
884 | |
885 | static const TypeInfo igd_passthrough_i440fx_info = { |
886 | .name = TYPE_IGD_PASSTHROUGH_I440FX_PCI_DEVICE, |
887 | .parent = TYPE_I440FX_PCI_DEVICE, |
888 | .instance_size = sizeof(PCII440FXState), |
889 | .class_init = igd_passthrough_i440fx_class_init, |
890 | }; |
891 | |
892 | static const char *i440fx_pcihost_root_bus_path(PCIHostState *host_bridge, |
893 | PCIBus *rootbus) |
894 | { |
895 | I440FXState *s = I440FX_PCI_HOST_BRIDGE(host_bridge); |
896 | |
897 | /* For backwards compat with old device paths */ |
898 | if (s->short_root_bus) { |
899 | return "0000" ; |
900 | } |
901 | return "0000:00" ; |
902 | } |
903 | |
904 | static Property i440fx_props[] = { |
905 | DEFINE_PROP_SIZE(PCI_HOST_PROP_PCI_HOLE64_SIZE, I440FXState, |
906 | pci_hole64_size, I440FX_PCI_HOST_HOLE64_SIZE_DEFAULT), |
907 | DEFINE_PROP_UINT32("short_root_bus" , I440FXState, short_root_bus, 0), |
908 | DEFINE_PROP_BOOL("x-pci-hole64-fix" , I440FXState, pci_hole64_fix, true), |
909 | DEFINE_PROP_END_OF_LIST(), |
910 | }; |
911 | |
912 | static void i440fx_pcihost_class_init(ObjectClass *klass, void *data) |
913 | { |
914 | DeviceClass *dc = DEVICE_CLASS(klass); |
915 | PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); |
916 | |
917 | hc->root_bus_path = i440fx_pcihost_root_bus_path; |
918 | dc->realize = i440fx_pcihost_realize; |
919 | dc->fw_name = "pci" ; |
920 | dc->props = i440fx_props; |
921 | /* Reason: needs to be wired up by pc_init1 */ |
922 | dc->user_creatable = false; |
923 | } |
924 | |
925 | static const TypeInfo i440fx_pcihost_info = { |
926 | .name = TYPE_I440FX_PCI_HOST_BRIDGE, |
927 | .parent = TYPE_PCI_HOST_BRIDGE, |
928 | .instance_size = sizeof(I440FXState), |
929 | .instance_init = i440fx_pcihost_initfn, |
930 | .class_init = i440fx_pcihost_class_init, |
931 | }; |
932 | |
933 | static void i440fx_register_types(void) |
934 | { |
935 | type_register_static(&i440fx_info); |
936 | type_register_static(&igd_passthrough_i440fx_info); |
937 | type_register_static(&piix3_pci_type_info); |
938 | type_register_static(&piix3_info); |
939 | type_register_static(&piix3_xen_info); |
940 | type_register_static(&i440fx_pcihost_info); |
941 | } |
942 | |
943 | type_init(i440fx_register_types) |
944 | |