VirtualBox

source: vbox/trunk/src/VBox/Devices/Bus/DevPCI.cpp@ 67583

Last change on this file since 67583 was 67583, checked in by vboxsync, 8 years ago

PDM, DevPci, DevPciIch9: Need to trigger the FakePCIBIOS code later on reset (after the devices are reset), as otherwise the device reset can destroy the carefully set up PCI config space content. It's now run in a similar place as on VM start.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 68.6 KB
Line 
1/* $Id: DevPCI.cpp 67583 2017-06-23 12:00:56Z vboxsync $ */
2/** @file
3 * DevPCI - PCI BUS Device.
4 *
5 * @remarks New code shall be added to DevPciIch9.cpp as that will become
6 * the common PCI bus code soon. Don't fix code in both DevPCI.cpp
7 * and DevPciIch9.cpp when it's possible to just make the latter
8 * version common. Common code uses the 'devpci' prefix, is
9 * prototyped in DevPciInternal.h, and is defined in DevPciIch9.cpp.
10 */
11
12/*
13 * Copyright (C) 2006-2016 Oracle Corporation
14 *
15 * This file is part of VirtualBox Open Source Edition (OSE), as
16 * available from http://www.virtualbox.org. This file is free software;
17 * you can redistribute it and/or modify it under the terms of the GNU
18 * General Public License (GPL) as published by the Free Software
19 * Foundation, in version 2 as it comes in the "COPYING" file of the
20 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
21 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
22 * --------------------------------------------------------------------
23 *
24 * This code is based on:
25 *
26 * QEMU PCI bus manager
27 *
28 * Copyright (c) 2004 Fabrice Bellard
29 *
30 * Permission is hereby granted, free of charge, to any person obtaining a copy
31 * of this software and associated documentation files (the "Software"), to deal
32 * in the Software without restriction, including without limitation the rights
33 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
34 * copies of the Software, and to permit persons to whom the Software is
35 * furnished to do so, subject to the following conditions:
36 *
37 * The above copyright notice and this permission notice shall be included in
38 * all copies or substantial portions of the Software.
39 *
40 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
41 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
42 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
43 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
44 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
45 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
46 * THE SOFTWARE.
47 */
48
49
50/*********************************************************************************************************************************
51* Header Files *
52*********************************************************************************************************************************/
53#define LOG_GROUP LOG_GROUP_DEV_PCI
54#define PDMPCIDEV_INCLUDE_PRIVATE /* Hack to get pdmpcidevint.h included at the right point. */
55#include <VBox/vmm/pdmpcidev.h>
56#include <VBox/vmm/pdmdev.h>
57#include <VBox/vmm/mm.h>
58#include <iprt/asm.h>
59#include <iprt/assert.h>
60#include <iprt/string.h>
61
62#include "PciInline.h"
63#include "VBoxDD.h"
64#include "DevPciInternal.h"
65
66
67/*********************************************************************************************************************************
68* Defined Constants And Macros *
69*********************************************************************************************************************************/
70/** Saved state version of the PCI bus device. */
71#define VBOX_PCI_SAVED_STATE_VERSION VBOX_PCI_SAVED_STATE_VERSION_REGION_SIZES
72/** Adds I/O region types and sizes for dealing changes in resource regions. */
73#define VBOX_PCI_SAVED_STATE_VERSION_REGION_SIZES 4
74/** Before region sizes, the first named one.
75 * Looking at the code though, we support even older version. */
76#define VBOX_PCI_SAVED_STATE_VERSION_IRQ_STATES 3
77/** Notes whether we use the I/O APIC. */
78#define VBOX_PCI_SAVED_STATE_VERSION_USE_IO_APIC 2
79
80
81/*********************************************************************************************************************************
82* Internal Functions *
83*********************************************************************************************************************************/
84RT_C_DECLS_BEGIN
85
86PDMBOTHCBDECL(void) pciSetIrq(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, int iIrq, int iLevel, uint32_t uTag);
87PDMBOTHCBDECL(void) pcibridgeSetIrq(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, int iIrq, int iLevel, uint32_t uTag);
88PDMBOTHCBDECL(int) pciIOPortAddressWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
89PDMBOTHCBDECL(int) pciIOPortAddressRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
90PDMBOTHCBDECL(int) pciIOPortDataWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
91PDMBOTHCBDECL(int) pciIOPortDataRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
92
93#ifdef IN_RING3
94DECLINLINE(PPDMPCIDEV) pciR3FindBridge(PDEVPCIBUS pBus, uint8_t iBus);
95#endif
96
97RT_C_DECLS_END
98
99#define DEBUG_PCI
100
101#define PCI_VENDOR_ID 0x00 /* 16 bits */
102#define PCI_DEVICE_ID 0x02 /* 16 bits */
103#define PCI_COMMAND 0x04 /* 16 bits */
104#define PCI_COMMAND_IO 0x01 /* Enable response in I/O space */
105#define PCI_COMMAND_MEMORY 0x02 /* Enable response in Memory space */
106#define PCI_CLASS_DEVICE 0x0a /* Device class */
107#define PCI_INTERRUPT_LINE 0x3c /* 8 bits */
108#define PCI_INTERRUPT_PIN 0x3d /* 8 bits */
109#define PCI_MIN_GNT 0x3e /* 8 bits */
110#define PCI_MAX_LAT 0x3f /* 8 bits */
111
112
113static int pci_data_write(PDEVPCIROOT pGlobals, uint32_t addr, uint32_t val, int len)
114{
115 uint8_t iBus, iDevice;
116 uint32_t config_addr;
117
118 Log(("pci_data_write: addr=%08x val=%08x len=%d\n", pGlobals->uConfigReg, val, len));
119
120 if (!(pGlobals->uConfigReg & (1 << 31))) {
121 return VINF_SUCCESS;
122 }
123 if ((pGlobals->uConfigReg & 0x3) != 0) {
124 return VINF_SUCCESS;
125 }
126 iBus = (pGlobals->uConfigReg >> 16) & 0xff;
127 iDevice = (pGlobals->uConfigReg >> 8) & 0xff;
128 config_addr = (pGlobals->uConfigReg & 0xfc) | (addr & 3);
129 if (iBus != 0)
130 {
131 if (pGlobals->PciBus.cBridges)
132 {
133#ifdef IN_RING3 /** @todo do lookup in R0/RC too! */
134 PPDMPCIDEV pBridgeDevice = pciR3FindBridge(&pGlobals->PciBus, iBus);
135 if (pBridgeDevice)
136 {
137 AssertPtr(pBridgeDevice->Int.s.pfnBridgeConfigWrite);
138 pBridgeDevice->Int.s.pfnBridgeConfigWrite(pBridgeDevice->Int.s.CTX_SUFF(pDevIns), iBus, iDevice, config_addr, val, len);
139 }
140#else
141 RT_NOREF2(val, len);
142 return VINF_IOM_R3_IOPORT_WRITE;
143#endif
144 }
145 }
146 else
147 {
148 R3PTRTYPE(PDMPCIDEV *) pci_dev = pGlobals->PciBus.apDevices[iDevice];
149 if (pci_dev)
150 {
151#ifdef IN_RING3
152 Log(("pci_config_write: %s: addr=%02x val=%08x len=%d\n", pci_dev->pszNameR3, config_addr, val, len));
153 pci_dev->Int.s.pfnConfigWrite(pci_dev->Int.s.CTX_SUFF(pDevIns), pci_dev, config_addr, val, len);
154#else
155 return VINF_IOM_R3_IOPORT_WRITE;
156#endif
157 }
158 }
159 return VINF_SUCCESS;
160}
161
162static int pci_data_read(PDEVPCIROOT pGlobals, uint32_t addr, int len, uint32_t *pu32)
163{
164 uint8_t iBus, iDevice;
165 uint32_t config_addr;
166
167 *pu32 = 0xffffffff;
168
169 if (!(pGlobals->uConfigReg & (1 << 31)))
170 return VINF_SUCCESS;
171 if ((pGlobals->uConfigReg & 0x3) != 0)
172 return VINF_SUCCESS;
173 iBus = (pGlobals->uConfigReg >> 16) & 0xff;
174 iDevice = (pGlobals->uConfigReg >> 8) & 0xff;
175 config_addr = (pGlobals->uConfigReg & 0xfc) | (addr & 3);
176 if (iBus != 0)
177 {
178 if (pGlobals->PciBus.cBridges)
179 {
180#ifdef IN_RING3 /** @todo do lookup in R0/RC too! */
181 PPDMPCIDEV pBridgeDevice = pciR3FindBridge(&pGlobals->PciBus, iBus);
182 if (pBridgeDevice)
183 {
184 AssertPtr(pBridgeDevice->Int.s.pfnBridgeConfigRead);
185 *pu32 = pBridgeDevice->Int.s.pfnBridgeConfigRead(pBridgeDevice->Int.s.CTX_SUFF(pDevIns), iBus, iDevice, config_addr, len);
186 }
187#else
188 NOREF(len);
189 return VINF_IOM_R3_IOPORT_READ;
190#endif
191 }
192 }
193 else
194 {
195 R3PTRTYPE(PDMPCIDEV *) pci_dev = pGlobals->PciBus.apDevices[iDevice];
196 if (pci_dev)
197 {
198#ifdef IN_RING3
199 *pu32 = pci_dev->Int.s.pfnConfigRead(pci_dev->Int.s.CTX_SUFF(pDevIns), pci_dev, config_addr, len);
200 Log(("pci_config_read: %s: addr=%02x val=%08x len=%d\n", pci_dev->pszNameR3, config_addr, *pu32, len));
201#else
202 NOREF(len);
203 return VINF_IOM_R3_IOPORT_READ;
204#endif
205 }
206 }
207
208 return VINF_SUCCESS;
209}
210
211
212
213/* return the global irq number corresponding to a given device irq
214 pin. We could also use the bus number to have a more precise
215 mapping.
216 This is the implementation note described in the PCI spec chapter 2.2.6 */
217static inline int pci_slot_get_pirq(uint8_t uDevFn, int irq_num)
218{
219 int slot_addend;
220 slot_addend = (uDevFn >> 3) - 1;
221 return (irq_num + slot_addend) & 3;
222}
223
224static inline int pci_slot_get_apic_pirq(uint8_t uDevFn, int irq_num)
225{
226 return (irq_num + (uDevFn >> 3)) & 7;
227}
228
229static inline int get_pci_irq_apic_level(PDEVPCIROOT pGlobals, int irq_num)
230{
231 return (pGlobals->auPciApicIrqLevels[irq_num] != 0);
232}
233
234static void apic_set_irq(PDEVPCIBUS pBus, uint8_t uDevFn, PDMPCIDEV *pPciDev, int irq_num1, int iLevel, int iAcpiIrq, uint32_t uTagSrc)
235{
236 /* This is only allowed to be called with a pointer to the host bus. */
237 AssertMsg(pBus->iBus == 0, ("iBus=%u\n", pBus->iBus));
238
239 if (iAcpiIrq == -1) {
240 int apic_irq, apic_level;
241 PDEVPCIROOT pGlobals = DEVPCIBUS_2_DEVPCIROOT(pBus);
242 int irq_num = pci_slot_get_apic_pirq(uDevFn, irq_num1);
243
244 if ((iLevel & PDM_IRQ_LEVEL_HIGH) == PDM_IRQ_LEVEL_HIGH)
245 ASMAtomicIncU32(&pGlobals->auPciApicIrqLevels[irq_num]);
246 else if ((iLevel & PDM_IRQ_LEVEL_HIGH) == PDM_IRQ_LEVEL_LOW)
247 ASMAtomicDecU32(&pGlobals->auPciApicIrqLevels[irq_num]);
248
249 apic_irq = irq_num + 0x10;
250 apic_level = get_pci_irq_apic_level(pGlobals, irq_num);
251 Log3(("apic_set_irq: %s: irq_num1=%d level=%d apic_irq=%d apic_level=%d irq_num1=%d\n",
252 R3STRING(pPciDev->pszNameR3), irq_num1, iLevel, apic_irq, apic_level, irq_num));
253 pBus->CTX_SUFF(pPciHlp)->pfnIoApicSetIrq(pBus->CTX_SUFF(pDevIns), apic_irq, apic_level, uTagSrc);
254
255 if ((iLevel & PDM_IRQ_LEVEL_FLIP_FLOP) == PDM_IRQ_LEVEL_FLIP_FLOP) {
256 ASMAtomicDecU32(&pGlobals->auPciApicIrqLevels[irq_num]);
257 pPciDev->Int.s.uIrqPinState = PDM_IRQ_LEVEL_LOW;
258 apic_level = get_pci_irq_apic_level(pGlobals, irq_num);
259 Log3(("apic_set_irq: %s: irq_num1=%d level=%d apic_irq=%d apic_level=%d irq_num1=%d (flop)\n",
260 R3STRING(pPciDev->pszNameR3), irq_num1, iLevel, apic_irq, apic_level, irq_num));
261 pBus->CTX_SUFF(pPciHlp)->pfnIoApicSetIrq(pBus->CTX_SUFF(pDevIns), apic_irq, apic_level, uTagSrc);
262 }
263 } else {
264 Log3(("apic_set_irq: %s: irq_num1=%d level=%d iAcpiIrq=%d\n",
265 R3STRING(pPciDev->pszNameR3), irq_num1, iLevel, iAcpiIrq));
266 pBus->CTX_SUFF(pPciHlp)->pfnIoApicSetIrq(pBus->CTX_SUFF(pDevIns), iAcpiIrq, iLevel, uTagSrc);
267 }
268}
269
270DECLINLINE(int) get_pci_irq_level(PDEVPCIROOT pGlobals, int irq_num)
271{
272 return (pGlobals->Piix3.auPciLegacyIrqLevels[irq_num] != 0);
273}
274
275/**
276 * Set the IRQ for a PCI device on the host bus - shared by host bus and bridge.
277 *
278 * @param pGlobals Device instance of the host PCI Bus.
279 * @param uDevFn The device number on the host bus which will raise the IRQ
280 * @param pPciDev The PCI device structure which raised the interrupt.
281 * @param iIrq IRQ number to set.
282 * @param iLevel IRQ level.
283 * @param uTagSrc The IRQ tag and source ID (for tracing).
284 * @remark uDevFn and pPciDev->uDevFn are not the same if the device is behind
285 * a bridge. In that case uDevFn will be the slot of the bridge which
286 * is needed to calculate the PIRQ value.
287 */
288static void pciSetIrqInternal(PDEVPCIROOT pGlobals, uint8_t uDevFn, PPDMPCIDEV pPciDev, int iIrq, int iLevel, uint32_t uTagSrc)
289{
290 PDEVPCIBUS pBus = &pGlobals->PciBus;
291 uint8_t *pbCfg = pGlobals->Piix3.PIIX3State.dev.abConfig;
292 const bool fIsAcpiDevice = pPciDev->abConfig[2] == 0x13 && pPciDev->abConfig[3] == 0x71;
293 /* If the two configuration space bytes at 0xde, 0xad are set to 0xbe, 0xef, a back door
294 * is opened to route PCI interrupts directly to the I/O APIC and bypass the PIC.
295 * See the \_SB_.PCI0._PRT method in vbox.dsl.
296 */
297 const bool fIsApicEnabled = pGlobals->fUseIoApic && pbCfg[0xde] == 0xbe && pbCfg[0xad] == 0xef;
298 int pic_irq, pic_level;
299
300 /* Check if the state changed. */
301 if (pPciDev->Int.s.uIrqPinState != iLevel)
302 {
303 pPciDev->Int.s.uIrqPinState = (iLevel & PDM_IRQ_LEVEL_HIGH);
304
305 /* Send interrupt to I/O APIC only. */
306 if (fIsApicEnabled)
307 {
308 if (fIsAcpiDevice)
309 /*
310 * ACPI needs special treatment since SCI is hardwired and
311 * should not be affected by PCI IRQ routing tables at the
312 * same time SCI IRQ is shared in PCI sense hence this
313 * kludge (i.e. we fetch the hardwired value from ACPIs
314 * PCI device configuration space).
315 */
316 apic_set_irq(pBus, uDevFn, pPciDev, -1, iLevel, pPciDev->abConfig[PCI_INTERRUPT_LINE], uTagSrc);
317 else
318 apic_set_irq(pBus, uDevFn, pPciDev, iIrq, iLevel, -1, uTagSrc);
319 return;
320 }
321
322 if (fIsAcpiDevice)
323 {
324 /* As per above treat ACPI in a special way */
325 pic_irq = pPciDev->abConfig[PCI_INTERRUPT_LINE];
326 pGlobals->Piix3.iAcpiIrq = pic_irq;
327 pGlobals->Piix3.iAcpiIrqLevel = iLevel & PDM_IRQ_LEVEL_HIGH;
328 }
329 else
330 {
331 int irq_num;
332 irq_num = pci_slot_get_pirq(uDevFn, iIrq);
333
334 if (pPciDev->Int.s.uIrqPinState == PDM_IRQ_LEVEL_HIGH)
335 ASMAtomicIncU32(&pGlobals->Piix3.auPciLegacyIrqLevels[irq_num]);
336 else if (pPciDev->Int.s.uIrqPinState == PDM_IRQ_LEVEL_LOW)
337 ASMAtomicDecU32(&pGlobals->Piix3.auPciLegacyIrqLevels[irq_num]);
338
339 /* now we change the pic irq level according to the piix irq mappings */
340 pic_irq = pbCfg[0x60 + irq_num];
341 if (pic_irq >= 16)
342 {
343 if ((iLevel & PDM_IRQ_LEVEL_FLIP_FLOP) == PDM_IRQ_LEVEL_FLIP_FLOP)
344 {
345 ASMAtomicDecU32(&pGlobals->Piix3.auPciLegacyIrqLevels[irq_num]);
346 pPciDev->Int.s.uIrqPinState = PDM_IRQ_LEVEL_LOW;
347 }
348
349 return;
350 }
351 }
352
353 /* the pic level is the logical OR of all the PCI irqs mapped to it */
354 pic_level = 0;
355 if (pic_irq == pbCfg[0x60])
356 pic_level |= get_pci_irq_level(pGlobals, 0);
357 if (pic_irq == pbCfg[0x61])
358 pic_level |= get_pci_irq_level(pGlobals, 1);
359 if (pic_irq == pbCfg[0x62])
360 pic_level |= get_pci_irq_level(pGlobals, 2);
361 if (pic_irq == pbCfg[0x63])
362 pic_level |= get_pci_irq_level(pGlobals, 3);
363 if (pic_irq == pGlobals->Piix3.iAcpiIrq)
364 pic_level |= pGlobals->Piix3.iAcpiIrqLevel;
365
366 Log3(("pciSetIrq: %s: iLevel=%d iIrq=%d pic_irq=%d pic_level=%d uTagSrc=%#x\n",
367 R3STRING(pPciDev->pszNameR3), iLevel, iIrq, pic_irq, pic_level, uTagSrc));
368 pBus->CTX_SUFF(pPciHlp)->pfnIsaSetIrq(pBus->CTX_SUFF(pDevIns), pic_irq, pic_level, uTagSrc);
369
370 /** @todo optimize pci irq flip-flop some rainy day. */
371 if ((iLevel & PDM_IRQ_LEVEL_FLIP_FLOP) == PDM_IRQ_LEVEL_FLIP_FLOP)
372 pciSetIrqInternal(pGlobals, uDevFn, pPciDev, iIrq, PDM_IRQ_LEVEL_LOW, uTagSrc);
373 }
374}
375
376
377/**
378 * @interface_method_impl{PDMPCIBUSREG,pfnSetIrqR3}
379 */
380PDMBOTHCBDECL(void) pciSetIrq(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, int iIrq, int iLevel, uint32_t uTagSrc)
381{
382 pciSetIrqInternal(PDMINS_2_DATA(pDevIns, PDEVPCIROOT), pPciDev->uDevFn, pPciDev, iIrq, iLevel, uTagSrc);
383}
384
385#ifdef IN_RING3
386
387/**
388 * Finds a bridge on the bus which contains the destination bus.
389 *
390 * @return Pointer to the device instance data of the bus or
391 * NULL if no bridge was found.
392 * @param pBus Pointer to the bus to search on.
393 * @param iBus Destination bus number.
394 */
395DECLINLINE(PPDMPCIDEV) pciR3FindBridge(PDEVPCIBUS pBus, uint8_t iBus)
396{
397 /* Search for a fitting bridge. */
398 for (uint32_t iBridge = 0; iBridge < pBus->cBridges; iBridge++)
399 {
400 /*
401 * Examine secondary and subordinate bus number.
402 * If the target bus is in the range we pass the request on to the bridge.
403 */
404 PPDMPCIDEV pBridgeTemp = pBus->papBridgesR3[iBridge];
405 AssertMsg(pBridgeTemp && pciDevIsPci2PciBridge(pBridgeTemp),
406 ("Device is not a PCI bridge but on the list of PCI bridges\n"));
407
408 if ( iBus >= pBridgeTemp->abConfig[VBOX_PCI_SECONDARY_BUS]
409 && iBus <= pBridgeTemp->abConfig[VBOX_PCI_SUBORDINATE_BUS])
410 return pBridgeTemp;
411 }
412
413 /* Nothing found. */
414 return NULL;
415}
416
417static void pciR3Piix3Reset(PIIX3ISABRIDGE *d)
418{
419 uint8_t *pci_conf = d->dev.abConfig;
420
421 pci_conf[0x04] = 0x07; /* master, memory and I/O */
422 pci_conf[0x05] = 0x00;
423 pci_conf[0x06] = 0x00;
424 pci_conf[0x07] = 0x02; /* PCI_status_devsel_medium */
425 pci_conf[0x4c] = 0x4d;
426 pci_conf[0x4e] = 0x03;
427 pci_conf[0x4f] = 0x00;
428 pci_conf[0x60] = 0x80;
429 pci_conf[0x69] = 0x02;
430 pci_conf[0x70] = 0x80;
431 pci_conf[0x76] = 0x0c;
432 pci_conf[0x77] = 0x0c;
433 pci_conf[0x78] = 0x02;
434 pci_conf[0x79] = 0x00;
435 pci_conf[0x80] = 0x00;
436 pci_conf[0x82] = 0x02; /* Get rid of the Linux guest "Enabling Passive Release" PCI quirk warning. */
437 pci_conf[0xa0] = 0x08;
438 pci_conf[0xa2] = 0x00;
439 pci_conf[0xa3] = 0x00;
440 pci_conf[0xa4] = 0x00;
441 pci_conf[0xa5] = 0x00;
442 pci_conf[0xa6] = 0x00;
443 pci_conf[0xa7] = 0x00;
444 pci_conf[0xa8] = 0x0f;
445 pci_conf[0xaa] = 0x00;
446 pci_conf[0xab] = 0x00;
447 pci_conf[0xac] = 0x00;
448 pci_conf[0xae] = 0x00;
449}
450
451static void pci_config_writel(PDEVPCIROOT pGlobals, uint8_t uBus, uint8_t uDevFn, uint32_t addr, uint32_t val)
452{
453 pGlobals->uConfigReg = 0x80000000 | (uBus << 16) |
454 (uDevFn << 8) | addr;
455 pci_data_write(pGlobals, 0, val, 4);
456}
457
458static void pci_config_writew(PDEVPCIROOT pGlobals, uint8_t uBus, uint8_t uDevFn, uint32_t addr, uint32_t val)
459{
460 pGlobals->uConfigReg = 0x80000000 | (uBus << 16) |
461 (uDevFn << 8) | (addr & ~3);
462 pci_data_write(pGlobals, addr & 3, val, 2);
463}
464
465static void pci_config_writeb(PDEVPCIROOT pGlobals, uint8_t uBus, uint8_t uDevFn, uint32_t addr, uint32_t val)
466{
467 pGlobals->uConfigReg = 0x80000000 | (uBus << 16) |
468 (uDevFn << 8) | (addr & ~3);
469 pci_data_write(pGlobals, addr & 3, val, 1);
470}
471
472static uint32_t pci_config_readl(PDEVPCIROOT pGlobals, uint8_t uBus, uint8_t uDevFn, uint32_t addr)
473{
474 pGlobals->uConfigReg = 0x80000000 | (uBus << 16) |
475 (uDevFn << 8) | addr;
476 uint32_t u32Val;
477 int rc = pci_data_read(pGlobals, 0, 4, &u32Val);
478 AssertRC(rc);
479 return u32Val;
480}
481
482static uint32_t pci_config_readw(PDEVPCIROOT pGlobals, uint8_t uBus, uint8_t uDevFn, uint32_t addr)
483{
484 pGlobals->uConfigReg = 0x80000000 | (uBus << 16) |
485 (uDevFn << 8) | (addr & ~3);
486 uint32_t u32Val;
487 int rc = pci_data_read(pGlobals, addr & 3, 2, &u32Val);
488 AssertRC(rc);
489 return u32Val;
490}
491
492static uint32_t pci_config_readb(PDEVPCIROOT pGlobals, uint8_t uBus, uint8_t uDevFn, uint32_t addr)
493{
494 pGlobals->uConfigReg = 0x80000000 | (uBus << 16) |
495 (uDevFn << 8) | (addr & ~3);
496 uint32_t u32Val;
497 int rc = pci_data_read(pGlobals, addr & 3, 1, &u32Val);
498 AssertRC(rc);
499 return u32Val;
500}
501
502/* host irqs corresponding to PCI irqs A-D */
503static const uint8_t pci_irqs[4] = { 11, 9, 11, 9 }; /* bird: added const */
504
505static void pci_set_io_region_addr(PDEVPCIROOT pGlobals, uint8_t uBus, uint8_t uDevFn, int region_num, uint32_t addr)
506{
507 uint32_t ofs;
508
509 if ( region_num == PCI_ROM_SLOT )
510 ofs = 0x30;
511 else
512 ofs = 0x10 + region_num * 4;
513
514 Log(("Set region address: %02x:%02x.%d region %d address=%lld\n",
515 uBus, uDevFn >> 3, uDevFn & 7, region_num, addr));
516
517 /* Write address of the device. */
518 pci_config_writel(pGlobals, uBus, uDevFn, ofs, addr);
519}
520
521static void pci_bios_init_device(PDEVPCIROOT pGlobals, uint8_t uBus, uint8_t uDevFn, uint8_t cBridgeDepth, uint8_t *paBridgePositions)
522{
523 uint32_t *paddr;
524 int i, pin, pic_irq;
525 uint16_t devclass, vendor_id, device_id;
526
527 devclass = pci_config_readw(pGlobals, uBus, uDevFn, PCI_CLASS_DEVICE);
528 vendor_id = pci_config_readw(pGlobals, uBus, uDevFn, PCI_VENDOR_ID);
529 device_id = pci_config_readw(pGlobals, uBus, uDevFn, PCI_DEVICE_ID);
530
531 /* Check if device is present. */
532 if (vendor_id != 0xffff)
533 {
534 switch(devclass)
535 {
536 case 0x0101:
537 if ( (vendor_id == 0x8086)
538 && (device_id == 0x7010 || device_id == 0x7111 || device_id == 0x269e))
539 {
540 /* PIIX3, PIIX4 or ICH6 IDE */
541 pci_config_writew(pGlobals, uBus, uDevFn, 0x40, 0x8000); /* enable IDE0 */
542 pci_config_writew(pGlobals, uBus, uDevFn, 0x42, 0x8000); /* enable IDE1 */
543 goto default_map;
544 }
545 else
546 {
547 /* IDE: we map it as in ISA mode */
548 pci_set_io_region_addr(pGlobals, uBus, uDevFn, 0, 0x1f0);
549 pci_set_io_region_addr(pGlobals, uBus, uDevFn, 1, 0x3f4);
550 pci_set_io_region_addr(pGlobals, uBus, uDevFn, 2, 0x170);
551 pci_set_io_region_addr(pGlobals, uBus, uDevFn, 3, 0x374);
552 pci_config_writeb(pGlobals, uBus, uDevFn, PCI_COMMAND,
553 pci_config_readb(pGlobals, uBus, uDevFn, PCI_COMMAND)
554 | PCI_COMMAND_IOACCESS);
555 }
556 break;
557 case 0x0300:
558 if (vendor_id != 0x80ee)
559 goto default_map;
560 /* VGA: map frame buffer to default Bochs VBE address */
561 pci_set_io_region_addr(pGlobals, uBus, uDevFn, 0, 0xE0000000);
562 /*
563 * Legacy VGA I/O ports are implicitly decoded by a VGA class device. But
564 * only the framebuffer (i.e., a memory region) is explicitly registered via
565 * pci_set_io_region_addr, so don't forget to enable I/O decoding.
566 */
567 pci_config_writeb(pGlobals, uBus, uDevFn, PCI_COMMAND,
568 pci_config_readb(pGlobals, uBus, uDevFn, PCI_COMMAND)
569 | PCI_COMMAND_IOACCESS | PCI_COMMAND_MEMACCESS);
570 break;
571 case 0x0800:
572 /* PIC */
573 vendor_id = pci_config_readw(pGlobals, uBus, uDevFn, PCI_VENDOR_ID);
574 device_id = pci_config_readw(pGlobals, uBus, uDevFn, PCI_DEVICE_ID);
575 if (vendor_id == 0x1014)
576 {
577 /* IBM */
578 if (device_id == 0x0046 || device_id == 0xFFFF)
579 {
580 /* MPIC & MPIC2 */
581 pci_set_io_region_addr(pGlobals, uBus, uDevFn, 0, 0x80800000 + 0x00040000);
582 pci_config_writeb(pGlobals, uBus, uDevFn, PCI_COMMAND,
583 pci_config_readb(pGlobals, uBus, uDevFn, PCI_COMMAND)
584 | PCI_COMMAND_MEMACCESS);
585 }
586 }
587 break;
588 case 0xff00:
589 if ( (vendor_id == 0x0106b)
590 && (device_id == 0x0017 || device_id == 0x0022))
591 {
592 /* macio bridge */
593 pci_set_io_region_addr(pGlobals, uBus, uDevFn, 0, 0x80800000);
594 pci_config_writeb(pGlobals, uBus, uDevFn, PCI_COMMAND,
595 pci_config_readb(pGlobals, uBus, uDevFn, PCI_COMMAND)
596 | PCI_COMMAND_MEMACCESS);
597 }
598 break;
599 case 0x0604:
600 {
601 /* Init PCI-to-PCI bridge. */
602 pci_config_writeb(pGlobals, uBus, uDevFn, VBOX_PCI_PRIMARY_BUS, uBus);
603
604 AssertMsg(pGlobals->uPciBiosBus < 255, ("Too many bridges on the bus\n"));
605 pGlobals->uPciBiosBus++;
606 pci_config_writeb(pGlobals, uBus, uDevFn, VBOX_PCI_SECONDARY_BUS, pGlobals->uPciBiosBus);
607 pci_config_writeb(pGlobals, uBus, uDevFn, VBOX_PCI_SUBORDINATE_BUS, 0xff); /* Temporary until we know how many other bridges are behind this one. */
608
609 /* Add position of this bridge into the array. */
610 paBridgePositions[cBridgeDepth+1] = (uDevFn >> 3);
611
612 /*
613 * The I/O range for the bridge must be aligned to a 4KB boundary.
614 * This does not change anything really as the access to the device is not going
615 * through the bridge but we want to be compliant to the spec.
616 */
617 if ((pGlobals->uPciBiosIo % _4K) != 0)
618 pGlobals->uPciBiosIo = RT_ALIGN_32(pGlobals->uPciBiosIo, _4K);
619 Log(("%s: Aligned I/O start address. New address %#x\n", __FUNCTION__, pGlobals->uPciBiosIo));
620 pci_config_writeb(pGlobals, uBus, uDevFn, VBOX_PCI_IO_BASE, (pGlobals->uPciBiosIo >> 8) & 0xf0);
621
622 /* The MMIO range for the bridge must be aligned to a 1MB boundary. */
623 if ((pGlobals->uPciBiosMmio % _1M) != 0)
624 pGlobals->uPciBiosMmio = RT_ALIGN_32(pGlobals->uPciBiosMmio, _1M);
625 Log(("%s: Aligned MMIO start address. New address %#x\n", __FUNCTION__, pGlobals->uPciBiosMmio));
626 pci_config_writew(pGlobals, uBus, uDevFn, VBOX_PCI_MEMORY_BASE, (pGlobals->uPciBiosMmio >> 16) & UINT32_C(0xffff0));
627
628 /* Save values to compare later to. */
629 uint32_t u32IoAddressBase = pGlobals->uPciBiosIo;
630 uint32_t u32MMIOAddressBase = pGlobals->uPciBiosMmio;
631
632 /* Init devices behind the bridge and possibly other bridges as well. */
633 for (int iDev = 0; iDev <= 255; iDev++)
634 pci_bios_init_device(pGlobals, uBus + 1, iDev, cBridgeDepth + 1, paBridgePositions);
635
636 /* The number of bridges behind the this one is now available. */
637 pci_config_writeb(pGlobals, uBus, uDevFn, VBOX_PCI_SUBORDINATE_BUS, pGlobals->uPciBiosBus);
638
639 /*
640 * Set I/O limit register. If there is no device with I/O space behind the bridge
641 * we set a lower value than in the base register.
642 * The result with a real bridge is that no I/O transactions are passed to the secondary
643 * interface. Again this doesn't really matter here but we want to be compliant to the spec.
644 */
645 if ((u32IoAddressBase != pGlobals->uPciBiosIo) && ((pGlobals->uPciBiosIo % _4K) != 0))
646 {
647 /* The upper boundary must be one byte less than a 4KB boundary. */
648 pGlobals->uPciBiosIo = RT_ALIGN_32(pGlobals->uPciBiosIo, _4K);
649 }
650 pci_config_writeb(pGlobals, uBus, uDevFn, VBOX_PCI_IO_LIMIT, ((pGlobals->uPciBiosIo >> 8) & 0xf0) - 1);
651
652 /* Same with the MMIO limit register but with 1MB boundary here. */
653 if ((u32MMIOAddressBase != pGlobals->uPciBiosMmio) && ((pGlobals->uPciBiosMmio % _1M) != 0))
654 {
655 /* The upper boundary must be one byte less than a 1MB boundary. */
656 pGlobals->uPciBiosMmio = RT_ALIGN_32(pGlobals->uPciBiosMmio, _1M);
657 }
658 pci_config_writew(pGlobals, uBus, uDevFn, VBOX_PCI_MEMORY_LIMIT, ((pGlobals->uPciBiosMmio >> 16) & UINT32_C(0xfff0)) - 1);
659
660 /*
661 * Set the prefetch base and limit registers. We currently have no device with a prefetchable region
662 * which may be behind a bridge. That's why it is unconditionally disabled here atm by writing a higher value into
663 * the base register than in the limit register.
664 */
665 pci_config_writew(pGlobals, uBus, uDevFn, VBOX_PCI_PREF_MEMORY_BASE, 0xfff0);
666 pci_config_writew(pGlobals, uBus, uDevFn, VBOX_PCI_PREF_MEMORY_LIMIT, 0x0);
667 pci_config_writel(pGlobals, uBus, uDevFn, VBOX_PCI_PREF_BASE_UPPER32, 0x00);
668 pci_config_writel(pGlobals, uBus, uDevFn, VBOX_PCI_PREF_LIMIT_UPPER32, 0x00);
669 break;
670 }
671 default:
672 default_map:
673 {
674 /* default memory mappings */
675 bool fActiveMemRegion = false;
676 bool fActiveIORegion = false;
677 /*
678 * PCI_NUM_REGIONS is 7 because of the rom region but there are only 6 base address register defined by the PCI spec.
679 * Leaving only PCI_NUM_REGIONS would cause reading another and enabling a memory region which does not exist.
680 */
681 for(i = 0; i < (PCI_NUM_REGIONS-1); i++)
682 {
683 uint32_t u32Size;
684 uint8_t u8RessourceType;
685 uint32_t u32Address = 0x10 + i * 4;
686
687 /* Calculate size. */
688 u8RessourceType = pci_config_readb(pGlobals, uBus, uDevFn, u32Address);
689 pci_config_writel(pGlobals, uBus, uDevFn, u32Address, UINT32_C(0xffffffff));
690 u32Size = pci_config_readl(pGlobals, uBus, uDevFn, u32Address);
691 bool fIsPio = ((u8RessourceType & PCI_COMMAND_IOACCESS) == PCI_COMMAND_IOACCESS);
692 /* Clear resource information depending on resource type. */
693 if (fIsPio) /* I/O */
694 u32Size &= ~(0x01);
695 else /* MMIO */
696 u32Size &= ~(0x0f);
697
698 /*
699 * Invert all bits and add 1 to get size of the region.
700 * (From PCI implementation note)
701 */
702 if (fIsPio && (u32Size & UINT32_C(0xffff0000)) == 0)
703 u32Size = (~(u32Size | UINT32_C(0xffff0000))) + 1;
704 else
705 u32Size = (~u32Size) + 1;
706
707 Log2(("%s: Size of region %u for device %d on bus %d is %u\n", __FUNCTION__, i, uDevFn, uBus, u32Size));
708
709 if (u32Size)
710 {
711 if (fIsPio)
712 paddr = &pGlobals->uPciBiosIo;
713 else
714 paddr = &pGlobals->uPciBiosMmio;
715 uint32_t uNew = *paddr;
716 uNew = (uNew + u32Size - 1) & ~(u32Size - 1);
717 if (fIsPio)
718 uNew &= UINT32_C(0xffff);
719 /* Unconditionally exclude I/O-APIC/HPET/ROM. Pessimistic, but better than causing a mess. */
720 if (!uNew || (uNew <= UINT32_C(0xffffffff) && uNew + u32Size - 1 >= UINT32_C(0xfec00000)))
721 {
722 LogRel(("PCI: no space left for BAR%u of device %u/%u/%u (vendor=%#06x device=%#06x)\n",
723 i, uBus, uDevFn >> 3, uDevFn & 7, vendor_id, device_id)); /** @todo make this a VM start failure later. */
724 /* Undo the mapping mess caused by the size probing. */
725 pci_config_writel(pGlobals, uBus, uDevFn, u32Address, UINT32_C(0));
726 }
727 else
728 {
729 Log(("%s: Start address of %s region %u is %#x\n", __FUNCTION__, (fIsPio ? "I/O" : "MMIO"), i, uNew));
730 pci_set_io_region_addr(pGlobals, uBus, uDevFn, i, uNew);
731 if (fIsPio)
732 fActiveIORegion = true;
733 else
734 fActiveMemRegion = true;
735 *paddr = uNew + u32Size;
736 Log2(("%s: New address is %#x\n", __FUNCTION__, *paddr));
737 }
738 }
739 }
740
741 /* Update the command word appropriately. */
742 pci_config_writeb(pGlobals, uBus, uDevFn, PCI_COMMAND,
743 pci_config_readb(pGlobals, uBus, uDevFn, PCI_COMMAND)
744 | (fActiveMemRegion ? PCI_COMMAND_MEMACCESS : 0)
745 | (fActiveIORegion ? PCI_COMMAND_IOACCESS : 0));
746
747 break;
748 }
749 }
750
751 /* map the interrupt */
752 pin = pci_config_readb(pGlobals, uBus, uDevFn, PCI_INTERRUPT_PIN);
753 if (pin != 0)
754 {
755 uint8_t uBridgeDevFn = uDevFn;
756 pin--;
757
758 /* We need to go up to the host bus to see which irq this device will assert there. */
759 while (cBridgeDepth != 0)
760 {
761 /* Get the pin the device would assert on the bridge. */
762 pin = ((uBridgeDevFn >> 3) + pin) & 3;
763 uBridgeDevFn = paBridgePositions[cBridgeDepth];
764 cBridgeDepth--;
765 }
766
767 pin = pci_slot_get_pirq(uDevFn, pin);
768 pic_irq = pci_irqs[pin];
769 pci_config_writeb(pGlobals, uBus, uDevFn, PCI_INTERRUPT_LINE, pic_irq);
770 }
771 }
772}
773
774#endif /* IN_RING3 */
775
776
777/* -=-=-=-=-=- I/O ports -=-=-=-=-=- */
778
779/**
780 * @callback_method_impl{FNIOMIOPORTOUT, PCI address}
781 */
782PDMBOTHCBDECL(int) pciIOPortAddressWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
783{
784 Log(("pciIOPortAddressWrite: Port=%#x u32=%#x cb=%d\n", Port, u32, cb));
785 RT_NOREF2(Port, pvUser);
786 if (cb == 4)
787 {
788 PDEVPCIROOT pThis = PDMINS_2_DATA(pDevIns, PDEVPCIROOT);
789 PCI_LOCK(pDevIns, VINF_IOM_R3_IOPORT_WRITE);
790 pThis->uConfigReg = u32 & ~3; /* Bits 0-1 are reserved and we silently clear them */
791 PCI_UNLOCK(pDevIns);
792 }
793 /* else: 440FX does "pass through to the bus" for other writes, what ever that means.
794 * Linux probes for cmd640 using byte writes/reads during ide init. We'll just ignore it. */
795 return VINF_SUCCESS;
796}
797
798
799/**
800 * @callback_method_impl{FNIOMIOPORTIN, PCI address}
801 */
802PDMBOTHCBDECL(int) pciIOPortAddressRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
803{
804 RT_NOREF2(Port, pvUser);
805 if (cb == 4)
806 {
807 PDEVPCIROOT pThis = PDMINS_2_DATA(pDevIns, PDEVPCIROOT);
808 PCI_LOCK(pDevIns, VINF_IOM_R3_IOPORT_READ);
809 *pu32 = pThis->uConfigReg;
810 PCI_UNLOCK(pDevIns);
811 Log(("pciIOPortAddressRead: Port=%#x cb=%d -> %#x\n", Port, cb, *pu32));
812 return VINF_SUCCESS;
813 }
814 /* else: 440FX does "pass through to the bus" for other writes, what ever that means.
815 * Linux probes for cmd640 using byte writes/reads during ide init. We'll just ignore it. */
816 Log(("pciIOPortAddressRead: Port=%#x cb=%d VERR_IOM_IOPORT_UNUSED\n", Port, cb));
817 return VERR_IOM_IOPORT_UNUSED;
818}
819
820
821/**
822 * @callback_method_impl{FNIOMIOPORTOUT, PCI data}
823 */
824PDMBOTHCBDECL(int) pciIOPortDataWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
825{
826 Log(("pciIOPortDataWrite: Port=%#x u32=%#x cb=%d\n", Port, u32, cb));
827 NOREF(pvUser);
828 int rc = VINF_SUCCESS;
829 if (!(Port % cb))
830 {
831 PCI_LOCK(pDevIns, VINF_IOM_R3_IOPORT_WRITE);
832 rc = pci_data_write(PDMINS_2_DATA(pDevIns, PDEVPCIROOT), Port, u32, cb);
833 PCI_UNLOCK(pDevIns);
834 }
835 else
836 AssertMsgFailed(("Write to port %#x u32=%#x cb=%d\n", Port, u32, cb));
837 return rc;
838}
839
840
841/**
842 * @callback_method_impl{FNIOMIOPORTIN, PCI data}
843 */
844PDMBOTHCBDECL(int) pciIOPortDataRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
845{
846 NOREF(pvUser);
847 if (!(Port % cb))
848 {
849 PCI_LOCK(pDevIns, VINF_IOM_R3_IOPORT_READ);
850 int rc = pci_data_read(PDMINS_2_DATA(pDevIns, PDEVPCIROOT), Port, cb, pu32);
851 PCI_UNLOCK(pDevIns);
852 Log(("pciIOPortDataRead: Port=%#x cb=%#x -> %#x (%Rrc)\n", Port, cb, *pu32, rc));
853 return rc;
854 }
855 AssertMsgFailed(("Read from port %#x cb=%d\n", Port, cb));
856 return VERR_IOM_IOPORT_UNUSED;
857}
858
859#ifdef IN_RING3
860
861/*
862 * Include code we share with the other PCI bus implementation.
863 *
864 * Note! No #ifdefs, use instant data booleans/flags/whatever. Goal is to
865 * completely merge these files! File #1 contains code we write, where
866 * as a possible file #2 contains external code if there's any left.
867 */
868# include "DevPciMerge1.cpp.h"
869
870
871/* -=-=-=-=-=- Saved state -=-=-=-=-=- */
872
873/**
874 * Common worker for pciR3SaveExec and pcibridgeR3SaveExec.
875 *
876 * @returns VBox status code.
877 * @param pBus The bus to save.
878 * @param pSSM The saved state handle.
879 */
880static int pciR3CommonSaveExec(PDEVPCIBUS pBus, PSSMHANDLE pSSM)
881{
882 /*
883 * Iterate thru all the devices.
884 */
885 for (uint32_t i = 0; i < RT_ELEMENTS(pBus->apDevices); i++)
886 {
887 PPDMPCIDEV pDev = pBus->apDevices[i];
888 if (pDev)
889 {
890 SSMR3PutU32(pSSM, i);
891 SSMR3PutMem(pSSM, pDev->abConfig, sizeof(pDev->abConfig));
892
893 SSMR3PutS32(pSSM, pDev->Int.s.uIrqPinState);
894
895 /* Save the type an size of all the regions. */
896 for (uint32_t iRegion = 0; iRegion < VBOX_PCI_NUM_REGIONS; iRegion++)
897 {
898 SSMR3PutU8(pSSM, pDev->Int.s.aIORegions[iRegion].type);
899 SSMR3PutU64(pSSM, pDev->Int.s.aIORegions[iRegion].size);
900 }
901 }
902 }
903 return SSMR3PutU32(pSSM, UINT32_MAX); /* terminator */
904}
905
906
907/**
908 * @callback_method_impl{FNSSMDEVSAVEEXEC}
909 */
910static DECLCALLBACK(int) pciR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
911{
912 uint32_t i;
913 PDEVPCIROOT pThis = PDMINS_2_DATA(pDevIns, PDEVPCIROOT);
914
915 /*
916 * Bus state data.
917 */
918 SSMR3PutU32(pSSM, pThis->uConfigReg);
919 SSMR3PutBool(pSSM, pThis->fUseIoApic);
920
921 /*
922 * Save IRQ states.
923 */
924 for (i = 0; i < RT_ELEMENTS(pThis->Piix3.auPciLegacyIrqLevels); i++)
925 SSMR3PutU32(pSSM, pThis->Piix3.auPciLegacyIrqLevels[i]);
926 for (i = 0; i < RT_ELEMENTS(pThis->auPciApicIrqLevels); i++)
927 SSMR3PutU32(pSSM, pThis->auPciApicIrqLevels[i]);
928
929 SSMR3PutU32(pSSM, pThis->Piix3.iAcpiIrqLevel);
930 SSMR3PutS32(pSSM, pThis->Piix3.iAcpiIrq);
931
932 SSMR3PutU32(pSSM, UINT32_MAX); /* separator */
933
934 /*
935 * Join paths with pcibridgeR3SaveExec.
936 */
937 return pciR3CommonSaveExec(&pThis->PciBus, pSSM);
938}
939
940
941/**
942 * Common worker for pciR3LoadExec and pcibridgeR3LoadExec.
943 *
944 * @returns VBox status code.
945 * @param pBus The bus which data is being loaded.
946 * @param pSSM The saved state handle.
947 * @param uVersion The data version.
948 * @param uPass The pass.
949 */
950static DECLCALLBACK(int) pciR3CommonLoadExec(PDEVPCIBUS pBus, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
951{
952 uint32_t u32;
953 uint32_t i;
954 int rc;
955
956 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
957
958 /*
959 * Iterate thru all the devices and write 0 to the COMMAND register so
960 * that all the memory is unmapped before we start restoring the saved
961 * mapping locations.
962 *
963 * The register value is restored afterwards so we can do proper
964 * LogRels in devpciR3CommonRestoreConfig.
965 */
966 for (i = 0; i < RT_ELEMENTS(pBus->apDevices); i++)
967 {
968 PPDMPCIDEV pDev = pBus->apDevices[i];
969 if (pDev)
970 {
971 uint16_t u16 = PCIDevGetCommand(pDev);
972 pDev->Int.s.pfnConfigWrite(pDev->Int.s.CTX_SUFF(pDevIns), pDev, VBOX_PCI_COMMAND, 0, 2);
973 PCIDevSetCommand(pDev, u16);
974 Assert(PCIDevGetCommand(pDev) == u16);
975 }
976 }
977
978 /*
979 * Iterate all the devices.
980 */
981 for (i = 0;; i++)
982 {
983 /* index / terminator */
984 rc = SSMR3GetU32(pSSM, &u32);
985 if (RT_FAILURE(rc))
986 return rc;
987 if (u32 == UINT32_MAX)
988 break;
989 if ( u32 >= RT_ELEMENTS(pBus->apDevices)
990 || u32 < i)
991 {
992 AssertMsgFailed(("u32=%#x i=%#x\n", u32, i));
993 return rc;
994 }
995
996 /* skip forward to the device checking that no new devices are present. */
997 for (; i < u32; i++)
998 {
999 if (pBus->apDevices[i])
1000 {
1001 LogRel(("PCI: New device in slot %#x, %s (vendor=%#06x device=%#06x)\n", i, pBus->apDevices[i]->pszNameR3,
1002 PCIDevGetVendorId(pBus->apDevices[i]), PCIDevGetDeviceId(pBus->apDevices[i])));
1003 if (SSMR3HandleGetAfter(pSSM) != SSMAFTER_DEBUG_IT)
1004 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("New device in slot %#x, %s (vendor=%#06x device=%#06x)"),
1005 i, pBus->apDevices[i]->pszNameR3, PCIDevGetVendorId(pBus->apDevices[i]), PCIDevGetDeviceId(pBus->apDevices[i]));
1006 }
1007 }
1008
1009 /* get the data */
1010 PDMPCIDEV DevTmp;
1011 RT_ZERO(DevTmp);
1012 DevTmp.Int.s.uIrqPinState = ~0; /* Invalid value in case we have an older saved state to force a state change in pciSetIrq. */
1013 SSMR3GetMem(pSSM, DevTmp.abConfig, sizeof(DevTmp.abConfig));
1014 if (uVersion < VBOX_PCI_SAVED_STATE_VERSION_IRQ_STATES)
1015 {
1016 int32_t i32Temp;
1017 /* Irq value not needed anymore. */
1018 rc = SSMR3GetS32(pSSM, &i32Temp);
1019 if (RT_FAILURE(rc))
1020 return rc;
1021 }
1022 else
1023 {
1024 rc = SSMR3GetS32(pSSM, &DevTmp.Int.s.uIrqPinState);
1025 if (RT_FAILURE(rc))
1026 return rc;
1027 }
1028
1029 /* Load the region types and sizes. */
1030 if (uVersion >= VBOX_PCI_SAVED_STATE_VERSION_REGION_SIZES)
1031 {
1032 for (uint32_t iRegion = 0; iRegion < VBOX_PCI_NUM_REGIONS; iRegion++)
1033 {
1034 SSMR3GetU8(pSSM, &DevTmp.Int.s.aIORegions[iRegion].type);
1035 rc = SSMR3GetU64(pSSM, &DevTmp.Int.s.aIORegions[iRegion].size);
1036 AssertLogRelRCReturn(rc, rc);
1037 }
1038 }
1039
1040 /* check that it's still around. */
1041 PPDMPCIDEV pDev = pBus->apDevices[i];
1042 if (!pDev)
1043 {
1044 LogRel(("PCI: Device in slot %#x has been removed! vendor=%#06x device=%#06x\n", i,
1045 PCIDevGetVendorId(&DevTmp), PCIDevGetDeviceId(&DevTmp)));
1046 if (SSMR3HandleGetAfter(pSSM) != SSMAFTER_DEBUG_IT)
1047 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Device in slot %#x has been removed! vendor=%#06x device=%#06x"),
1048 i, PCIDevGetVendorId(&DevTmp), PCIDevGetDeviceId(&DevTmp));
1049 continue;
1050 }
1051
1052 /* match the vendor id assuming that this will never be changed. */
1053 if ( DevTmp.abConfig[0] != pDev->abConfig[0]
1054 || DevTmp.abConfig[1] != pDev->abConfig[1])
1055 return SSMR3SetCfgError(pSSM, RT_SRC_POS,
1056 N_("Device in slot %#x (%s) vendor id mismatch! saved=%.4Rhxs current=%.4Rhxs"),
1057 i, pDev->pszNameR3, DevTmp.abConfig, pDev->abConfig);
1058
1059 /* commit the loaded device config. */
1060 rc = devpciR3CommonRestoreRegions(pSSM, pDev, DevTmp.Int.s.aIORegions,
1061 uVersion >= VBOX_PCI_SAVED_STATE_VERSION_REGION_SIZES);
1062 if (RT_FAILURE(rc))
1063 break;
1064 devpciR3CommonRestoreConfig(pDev, &DevTmp.abConfig[0]);
1065
1066 pDev->Int.s.uIrqPinState = DevTmp.Int.s.uIrqPinState;
1067 }
1068
1069 return VINF_SUCCESS;
1070}
1071
1072
1073/**
1074 * @callback_method_impl{FNSSMDEVLOADEXEC}
1075 */
1076static DECLCALLBACK(int) pciR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
1077{
1078 PDEVPCIROOT pThis = PDMINS_2_DATA(pDevIns, PDEVPCIROOT);
1079 PDEVPCIBUS pBus = &pThis->PciBus;
1080 uint32_t u32;
1081 int rc;
1082
1083 /*
1084 * Check the version.
1085 */
1086 if (uVersion > VBOX_PCI_SAVED_STATE_VERSION)
1087 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
1088 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
1089
1090 /*
1091 * Bus state data.
1092 */
1093 SSMR3GetU32(pSSM, &pThis->uConfigReg);
1094 if (uVersion >= VBOX_PCI_SAVED_STATE_VERSION_USE_IO_APIC)
1095 SSMR3GetBool(pSSM, &pThis->fUseIoApic);
1096
1097 /* Load IRQ states. */
1098 if (uVersion >= VBOX_PCI_SAVED_STATE_VERSION_IRQ_STATES)
1099 {
1100 for (uint8_t i = 0; i < RT_ELEMENTS(pThis->Piix3.auPciLegacyIrqLevels); i++)
1101 SSMR3GetU32(pSSM, (uint32_t *)&pThis->Piix3.auPciLegacyIrqLevels[i]);
1102 for (uint8_t i = 0; i < RT_ELEMENTS(pThis->auPciApicIrqLevels); i++)
1103 SSMR3GetU32(pSSM, (uint32_t *)&pThis->auPciApicIrqLevels[i]);
1104
1105 SSMR3GetU32(pSSM, &pThis->Piix3.iAcpiIrqLevel);
1106 SSMR3GetS32(pSSM, &pThis->Piix3.iAcpiIrq);
1107 }
1108
1109 /* separator */
1110 rc = SSMR3GetU32(pSSM, &u32);
1111 if (RT_FAILURE(rc))
1112 return rc;
1113 if (u32 != UINT32_MAX)
1114 AssertMsgFailedReturn(("u32=%#x\n", u32), rc);
1115
1116 /*
1117 * The devices.
1118 */
1119 return pciR3CommonLoadExec(pBus, pSSM, uVersion, uPass);
1120}
1121
1122
1123/* -=-=-=-=-=- PCI Bus Interface Methods (PDMPCIBUSREG) -=-=-=-=-=- */
1124
1125/**
1126 * @interface_method_impl{PDMPCIBUSREG,pfnFakePCIBIOSR3}
1127 */
1128static DECLCALLBACK(int) pciR3FakePCIBIOS(PPDMDEVINS pDevIns)
1129{
1130 unsigned i;
1131 uint8_t elcr[2] = {0, 0};
1132 PDEVPCIROOT pGlobals = PDMINS_2_DATA(pDevIns, PDEVPCIROOT);
1133 PVM pVM = PDMDevHlpGetVM(pDevIns); Assert(pVM);
1134 PVMCPU pVCpu = PDMDevHlpGetVMCPU(pDevIns); Assert(pVM);
1135 uint32_t const cbBelow4GB = MMR3PhysGetRamSizeBelow4GB(pVM);
1136 uint64_t const cbAbove4GB = MMR3PhysGetRamSizeAbove4GB(pVM);
1137 RT_NOREF(cbBelow4GB, cbAbove4GB);
1138
1139 /*
1140 * Set the start addresses.
1141 */
1142 pGlobals->uPciBiosBus = 0;
1143 pGlobals->uPciBiosIo = 0xd000;
1144 pGlobals->uPciBiosMmio = UINT32_C(0xf0000000);
1145
1146 /*
1147 * Activate IRQ mappings.
1148 */
1149 for (i = 0; i < 4; i++)
1150 {
1151 uint8_t irq = pci_irqs[i];
1152 /* Set to trigger level. */
1153 elcr[irq >> 3] |= (1 << (irq & 7));
1154 /* Activate irq remapping in PIIX3. */
1155 pci_config_writeb(pGlobals, 0, pGlobals->Piix3.PIIX3State.dev.uDevFn, 0x60 + i, irq);
1156 }
1157
1158 /* Tell to the PIC. */
1159 VBOXSTRICTRC rcStrict = IOMIOPortWrite(pVM, pVCpu, 0x4d0, elcr[0], sizeof(uint8_t));
1160 if (rcStrict == VINF_SUCCESS)
1161 rcStrict = IOMIOPortWrite(pVM, pVCpu, 0x4d1, elcr[1], sizeof(uint8_t));
1162 if (rcStrict != VINF_SUCCESS)
1163 {
1164 AssertMsgFailed(("Writing to PIC failed! rcStrict=%Rrc\n", VBOXSTRICTRC_VAL(rcStrict)));
1165 return RT_SUCCESS(rcStrict) ? VERR_INTERNAL_ERROR : VBOXSTRICTRC_VAL(rcStrict);
1166 }
1167
1168 /*
1169 * Init the devices.
1170 */
1171 for (i = 0; i < 256; i++)
1172 {
1173 uint8_t aBridgePositions[256];
1174
1175 memset(aBridgePositions, 0, sizeof(aBridgePositions));
1176 Log2(("PCI: Initializing device %d (%#x)\n",
1177 i, 0x80000000 | (i << 8)));
1178 pci_bios_init_device(pGlobals, 0, i, 0, aBridgePositions);
1179 }
1180
1181 return VINF_SUCCESS;
1182}
1183
1184
1185/* -=-=-=-=-=- Debug Info Handlers -=-=-=-=-=- */
1186
1187/**
1188 * @callback_method_impl{FNDBGFHANDLERDEV}
1189 */
1190static DECLCALLBACK(void) pciR3IrqRouteInfo(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
1191{
1192 PDEVPCIROOT pGlobals = PDMINS_2_DATA(pDevIns, PDEVPCIROOT);
1193 NOREF(pszArgs);
1194
1195 uint16_t router = pGlobals->Piix3.PIIX3State.dev.uDevFn;
1196 pHlp->pfnPrintf(pHlp, "PCI interrupt router at: %02X:%02X:%X\n",
1197 router >> 8, (router >> 3) & 0x1f, router & 0x7);
1198
1199 for (int i = 0; i < 4; ++i)
1200 {
1201 uint8_t irq_map = pci_config_readb(pGlobals, 0, router, 0x60 + i);
1202 if (irq_map & 0x80)
1203 pHlp->pfnPrintf(pHlp, "PIRQ%c disabled\n", 'A' + i);
1204 else
1205 pHlp->pfnPrintf(pHlp, "PIRQ%c -> IRQ%d\n", 'A' + i, irq_map & 0xf);
1206 }
1207}
1208
1209
1210/* -=-=-=-=-=- PDMDEVREG -=-=-=-=-=- */
1211
1212
1213/**
1214 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1215 */
1216static DECLCALLBACK(int) pciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1217{
1218 RT_NOREF1(iInstance);
1219 Assert(iInstance == 0);
1220 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1221
1222 /*
1223 * Validate and read configuration.
1224 */
1225 if (!CFGMR3AreValuesValid(pCfg, "IOAPIC\0" "GCEnabled\0" "R0Enabled\0"))
1226 return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
1227
1228 /* query whether we got an IOAPIC */
1229 bool fUseIoApic;
1230 int rc = CFGMR3QueryBoolDef(pCfg, "IOAPIC", &fUseIoApic, false);
1231 if (RT_FAILURE(rc))
1232 return PDMDEV_SET_ERROR(pDevIns, rc,
1233 N_("Configuration error: Failed to query boolean value \"IOAPIC\""));
1234
1235 /* check if RC code is enabled. */
1236 bool fGCEnabled;
1237 rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &fGCEnabled, true);
1238 if (RT_FAILURE(rc))
1239 return PDMDEV_SET_ERROR(pDevIns, rc,
1240 N_("Configuration error: Failed to query boolean value \"GCEnabled\""));
1241
1242 /* check if R0 code is enabled. */
1243 bool fR0Enabled;
1244 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &fR0Enabled, true);
1245 if (RT_FAILURE(rc))
1246 return PDMDEV_SET_ERROR(pDevIns, rc,
1247 N_("Configuration error: Failed to query boolean value \"R0Enabled\""));
1248 Log(("PCI: fUseIoApic=%RTbool fGCEnabled=%RTbool fR0Enabled=%RTbool\n", fUseIoApic, fGCEnabled, fR0Enabled));
1249
1250 /*
1251 * Init data and register the PCI bus.
1252 */
1253 PDEVPCIROOT pGlobals = PDMINS_2_DATA(pDevIns, PDEVPCIROOT);
1254 pGlobals->uPciBiosIo = 0xc000;
1255 pGlobals->uPciBiosMmio = 0xf0000000;
1256 memset((void *)&pGlobals->Piix3.auPciLegacyIrqLevels, 0, sizeof(pGlobals->Piix3.auPciLegacyIrqLevels));
1257 pGlobals->fUseIoApic = fUseIoApic;
1258 memset((void *)&pGlobals->auPciApicIrqLevels, 0, sizeof(pGlobals->auPciApicIrqLevels));
1259
1260 pGlobals->pDevInsR3 = pDevIns;
1261 pGlobals->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
1262 pGlobals->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
1263
1264 pGlobals->PciBus.fTypePiix3 = true;
1265 pGlobals->PciBus.fTypeIch9 = false;
1266 pGlobals->PciBus.fPureBridge = false;
1267 pGlobals->PciBus.pDevInsR3 = pDevIns;
1268 pGlobals->PciBus.pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
1269 pGlobals->PciBus.pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
1270 pGlobals->PciBus.papBridgesR3 = (PPDMPCIDEV *)PDMDevHlpMMHeapAllocZ(pDevIns,
1271 sizeof(PPDMPCIDEV)
1272 * RT_ELEMENTS(pGlobals->PciBus.apDevices));
1273 AssertLogRelReturn(pGlobals->PciBus.papBridgesR3, VERR_NO_MEMORY);
1274
1275
1276 PDMPCIBUSREG PciBusReg;
1277 PDEVPCIBUS pBus = &pGlobals->PciBus;
1278 PciBusReg.u32Version = PDM_PCIBUSREG_VERSION;
1279 PciBusReg.pfnRegisterR3 = pciR3MergedRegister;
1280 PciBusReg.pfnRegisterMsiR3 = NULL;
1281 PciBusReg.pfnIORegionRegisterR3 = devpciR3CommonIORegionRegister;
1282 PciBusReg.pfnSetConfigCallbacksR3 = devpciR3CommonSetConfigCallbacks;
1283 PciBusReg.pfnSetIrqR3 = pciSetIrq;
1284 PciBusReg.pfnFakePCIBIOSR3 = pciR3FakePCIBIOS;
1285 PciBusReg.pszSetIrqRC = fGCEnabled ? "pciSetIrq" : NULL;
1286 PciBusReg.pszSetIrqR0 = fR0Enabled ? "pciSetIrq" : NULL;
1287#if PDM_DEVHLPR3_VERSION >= PDM_VERSION_MAKE_PP(0xffe7, 20, 0)
1288 rc = PDMDevHlpPCIBusRegister(pDevIns, &PciBusReg, &pBus->pPciHlpR3, &pBus->iBus);
1289#else
1290 rc = PDMDevHlpPCIBusRegister(pDevIns, &PciBusReg, &pBus->pPciHlpR3);
1291 pBus->iBus = rc;
1292#endif
1293 if (RT_FAILURE(rc))
1294 return PDMDEV_SET_ERROR(pDevIns, rc,
1295 N_("Failed to register ourselves as a PCI Bus"));
1296 Assert(pBus->iBus == 0);
1297 if (pBus->pPciHlpR3->u32Version != PDM_PCIHLPR3_VERSION)
1298 return PDMDevHlpVMSetError(pDevIns, VERR_VERSION_MISMATCH, RT_SRC_POS,
1299 N_("PCI helper version mismatch; got %#x expected %#x"),
1300 pBus->pPciHlpR3->u32Version, PDM_PCIHLPR3_VERSION);
1301
1302 pBus->pPciHlpRC = pBus->pPciHlpR3->pfnGetRCHelpers(pDevIns);
1303 pBus->pPciHlpR0 = pBus->pPciHlpR3->pfnGetR0Helpers(pDevIns);
1304
1305 /* Disable default device locking. */
1306 rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
1307 AssertRCReturn(rc, rc);
1308
1309 /*
1310 * Fill in PCI configs and add them to the bus.
1311 */
1312 /* i440FX */
1313 PCIDevSetVendorId( &pBus->PciDev, 0x8086); /* Intel */
1314 PCIDevSetDeviceId( &pBus->PciDev, 0x1237);
1315 PCIDevSetRevisionId(&pBus->PciDev, 0x02);
1316 PCIDevSetClassSub( &pBus->PciDev, 0x00); /* host2pci */
1317 PCIDevSetClassBase( &pBus->PciDev, 0x06); /* PCI_bridge */
1318 PCIDevSetHeaderType(&pBus->PciDev, 0x00);
1319 rc = PDMDevHlpPCIRegisterEx(pDevIns, &pBus->PciDev, PDMPCIDEVREG_CFG_PRIMARY, 0 /*fFlags*/,
1320 0 /*uPciDevNo*/, 0 /*uPciFunNo*/, "i440FX");
1321 AssertLogRelRCReturn(rc, rc);
1322
1323 /* PIIX3 */
1324 PCIDevSetVendorId( &pGlobals->Piix3.PIIX3State.dev, 0x8086); /* Intel */
1325 PCIDevSetDeviceId( &pGlobals->Piix3.PIIX3State.dev, 0x7000); /* 82371SB PIIX3 PCI-to-ISA bridge (Step A1) */
1326 PCIDevSetClassSub( &pGlobals->Piix3.PIIX3State.dev, 0x01); /* PCI_ISA */
1327 PCIDevSetClassBase( &pGlobals->Piix3.PIIX3State.dev, 0x06); /* PCI_bridge */
1328 PCIDevSetHeaderType(&pGlobals->Piix3.PIIX3State.dev, 0x80); /* PCI_multifunction, generic */
1329 rc = PDMDevHlpPCIRegisterEx(pDevIns, &pGlobals->Piix3.PIIX3State.dev, PDMPCIDEVREG_CFG_NEXT, 0 /*fFlags*/,
1330 1 /*uPciDevNo*/, 0 /*uPciFunNo*/, "PIIX3");
1331 AssertLogRelRCReturn(rc, rc);
1332 pciR3Piix3Reset(&pGlobals->Piix3.PIIX3State);
1333
1334 pBus->iDevSearch = 16;
1335
1336 /*
1337 * Register I/O ports and save state.
1338 */
1339 rc = PDMDevHlpIOPortRegister(pDevIns, 0x0cf8, 1, NULL, pciIOPortAddressWrite, pciIOPortAddressRead, NULL, NULL, "i440FX (PCI)");
1340 if (RT_FAILURE(rc))
1341 return rc;
1342 rc = PDMDevHlpIOPortRegister(pDevIns, 0x0cfc, 4, NULL, pciIOPortDataWrite, pciIOPortDataRead, NULL, NULL, "i440FX (PCI)");
1343 if (RT_FAILURE(rc))
1344 return rc;
1345 if (fGCEnabled)
1346 {
1347 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x0cf8, 1, NIL_RTGCPTR, "pciIOPortAddressWrite", "pciIOPortAddressRead", NULL, NULL, "i440FX (PCI)");
1348 if (RT_FAILURE(rc))
1349 return rc;
1350 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x0cfc, 4, NIL_RTGCPTR, "pciIOPortDataWrite", "pciIOPortDataRead", NULL, NULL, "i440FX (PCI)");
1351 if (RT_FAILURE(rc))
1352 return rc;
1353 }
1354 if (fR0Enabled)
1355 {
1356 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x0cf8, 1, NIL_RTR0PTR, "pciIOPortAddressWrite", "pciIOPortAddressRead", NULL, NULL, "i440FX (PCI)");
1357 if (RT_FAILURE(rc))
1358 return rc;
1359 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x0cfc, 4, NIL_RTR0PTR, "pciIOPortDataWrite", "pciIOPortDataRead", NULL, NULL, "i440FX (PCI)");
1360 if (RT_FAILURE(rc))
1361 return rc;
1362 }
1363
1364 rc = PDMDevHlpSSMRegisterEx(pDevIns, VBOX_PCI_SAVED_STATE_VERSION, sizeof(*pBus) + 16*128, "pgm",
1365 NULL, NULL, NULL,
1366 NULL, pciR3SaveExec, NULL,
1367 NULL, pciR3LoadExec, NULL);
1368 if (RT_FAILURE(rc))
1369 return rc;
1370
1371 PDMDevHlpDBGFInfoRegister(pDevIns, "pci",
1372 "Display PCI bus status. Recognizes 'basic' or 'verbose' as arguments, defaults to 'basic'.",
1373 devpciR3InfoPci);
1374 PDMDevHlpDBGFInfoRegister(pDevIns, "pciirq", "Display PCI IRQ state. (no arguments)", devpciR3InfoPciIrq);
1375 PDMDevHlpDBGFInfoRegister(pDevIns, "irqroute", "Display PCI IRQ routing. (no arguments)", pciR3IrqRouteInfo);
1376
1377 return VINF_SUCCESS;
1378}
1379
1380
1381/**
1382 * @interface_method_impl{PDMDEVREG,pfnDestruct}
1383 */
1384static DECLCALLBACK(int) pciR3Destruct(PPDMDEVINS pDevIns)
1385{
1386 PDEVPCIROOT pGlobals = PDMINS_2_DATA(pDevIns, PDEVPCIROOT);
1387 if (pGlobals->PciBus.papBridgesR3)
1388 {
1389 PDMDevHlpMMHeapFree(pDevIns, pGlobals->PciBus.papBridgesR3);
1390 pGlobals->PciBus.papBridgesR3 = NULL;
1391 }
1392 return VINF_SUCCESS;
1393}
1394
1395
1396/**
1397 * The device registration structure.
1398 */
1399const PDMDEVREG g_DevicePCI =
1400{
1401 /* u32Version */
1402 PDM_DEVREG_VERSION,
1403 /* szName */
1404 "pci",
1405 /* szRCMod */
1406 "VBoxDDRC.rc",
1407 /* szR0Mod */
1408 "VBoxDDR0.r0",
1409 /* pszDescription */
1410 "i440FX PCI bridge and PIIX3 ISA bridge.",
1411 /* fFlags */
1412 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
1413 /* fClass */
1414 PDM_DEVREG_CLASS_BUS_PCI | PDM_DEVREG_CLASS_BUS_ISA,
1415 /* cMaxInstances */
1416 1,
1417 /* cbInstance */
1418 sizeof(DEVPCIROOT),
1419 /* pfnConstruct */
1420 pciR3Construct,
1421 /* pfnDestruct */
1422 pciR3Destruct,
1423 /* pfnRelocate */
1424 devpciR3RootRelocate,
1425 /* pfnMemSetup */
1426 NULL,
1427 /* pfnPowerOn */
1428 NULL,
1429 /* pfnReset */
1430 NULL,
1431 /* pfnSuspend */
1432 NULL,
1433 /* pfnResume */
1434 NULL,
1435 /* pfnAttach */
1436 NULL,
1437 /* pfnDetach */
1438 NULL,
1439 /* pfnQueryInterface */
1440 NULL,
1441 /* pfnInitComplete */
1442 NULL,
1443 /* pfnPowerOff */
1444 NULL,
1445 /* pfnSoftReset */
1446 NULL,
1447 /* u32VersionEnd */
1448 PDM_DEVREG_VERSION
1449
1450};
1451#endif /* IN_RING3 */
1452
1453
1454
1455/* -=-=-=-=-=- The PCI bridge specific bits -=-=-=-=-=- */
1456
1457/**
1458 * @interface_method_impl{PDMPCIBUSREG,pfnSetIrqR3}
1459 */
1460PDMBOTHCBDECL(void) pcibridgeSetIrq(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, int iIrq, int iLevel, uint32_t uTagSrc)
1461{
1462 /*
1463 * The PCI-to-PCI bridge specification defines how the interrupt pins
1464 * are routed from the secondary to the primary bus (see chapter 9).
1465 * iIrq gives the interrupt pin the pci device asserted.
1466 * We change iIrq here according to the spec and call the SetIrq function
1467 * of our parent passing the device which asserted the interrupt instead of the device of the bridge.
1468 */
1469 PDEVPCIBUS pBus = PDMINS_2_DATA(pDevIns, PDEVPCIBUS);
1470 PPDMPCIDEV pPciDevBus = pPciDev;
1471 int iIrqPinBridge = iIrq;
1472 uint8_t uDevFnBridge = 0;
1473
1474 /* Walk the chain until we reach the host bus. */
1475 do
1476 {
1477 uDevFnBridge = pBus->PciDev.uDevFn;
1478 iIrqPinBridge = ((pPciDevBus->uDevFn >> 3) + iIrqPinBridge) & 3;
1479
1480 /* Get the parent. */
1481 pBus = pBus->PciDev.Int.s.CTX_SUFF(pBus);
1482 pPciDevBus = &pBus->PciDev;
1483 } while (pBus->iBus != 0);
1484
1485 AssertMsg(pBus->iBus == 0, ("This is not the host pci bus iBus=%d\n", pBus->iBus));
1486 pciSetIrqInternal(DEVPCIBUS_2_DEVPCIROOT(pBus), uDevFnBridge, pPciDev, iIrqPinBridge, iLevel, uTagSrc);
1487}
1488
1489#ifdef IN_RING3
1490
1491/**
1492 * @callback_method_impl{FNPCIBRIDGECONFIGWRITE}
1493 */
1494static DECLCALLBACK(void) pcibridgeR3ConfigWrite(PPDMDEVINSR3 pDevIns, uint8_t iBus, uint8_t iDevice, uint32_t u32Address, uint32_t u32Value, unsigned cb)
1495{
1496 PDEVPCIBUS pBus = PDMINS_2_DATA(pDevIns, PDEVPCIBUS);
1497
1498 LogFlowFunc(("pDevIns=%p iBus=%d iDevice=%d u32Address=%u u32Value=%u cb=%d\n", pDevIns, iBus, iDevice, u32Address, u32Value, cb));
1499
1500 /* If the current bus is not the target bus search for the bus which contains the device. */
1501 if (iBus != pBus->PciDev.abConfig[VBOX_PCI_SECONDARY_BUS])
1502 {
1503 PPDMPCIDEV pBridgeDevice = pciR3FindBridge(pBus, iBus);
1504 if (pBridgeDevice)
1505 {
1506 AssertPtr(pBridgeDevice->Int.s.pfnBridgeConfigWrite);
1507 pBridgeDevice->Int.s.pfnBridgeConfigWrite(pBridgeDevice->Int.s.CTX_SUFF(pDevIns), iBus, iDevice, u32Address, u32Value, cb);
1508 }
1509 }
1510 else
1511 {
1512 /* This is the target bus, pass the write to the device. */
1513 PPDMPCIDEV pPciDev = pBus->apDevices[iDevice];
1514 if (pPciDev)
1515 {
1516 Log(("%s: %s: addr=%02x val=%08x len=%d\n", __FUNCTION__, pPciDev->pszNameR3, u32Address, u32Value, cb));
1517 pPciDev->Int.s.pfnConfigWrite(pPciDev->Int.s.CTX_SUFF(pDevIns), pPciDev, u32Address, u32Value, cb);
1518 }
1519 }
1520}
1521
1522
1523/**
1524 * @callback_method_impl{FNPCIBRIDGECONFIGREAD}
1525 */
1526static DECLCALLBACK(uint32_t) pcibridgeR3ConfigRead(PPDMDEVINSR3 pDevIns, uint8_t iBus, uint8_t iDevice, uint32_t u32Address, unsigned cb)
1527{
1528 PDEVPCIBUS pBus = PDMINS_2_DATA(pDevIns, PDEVPCIBUS);
1529 uint32_t u32Value = 0xffffffff; /* Return value in case there is no device. */
1530
1531 LogFlowFunc(("pDevIns=%p iBus=%d iDevice=%d u32Address=%u cb=%d\n", pDevIns, iBus, iDevice, u32Address, cb));
1532
1533 /* If the current bus is not the target bus search for the bus which contains the device. */
1534 if (iBus != pBus->PciDev.abConfig[VBOX_PCI_SECONDARY_BUS])
1535 {
1536 PPDMPCIDEV pBridgeDevice = pciR3FindBridge(pBus, iBus);
1537 if (pBridgeDevice)
1538 {
1539 AssertPtr( pBridgeDevice->Int.s.pfnBridgeConfigRead);
1540 u32Value = pBridgeDevice->Int.s.pfnBridgeConfigRead(pBridgeDevice->Int.s.CTX_SUFF(pDevIns), iBus, iDevice, u32Address, cb);
1541 }
1542 }
1543 else
1544 {
1545 /* This is the target bus, pass the read to the device. */
1546 PPDMPCIDEV pPciDev = pBus->apDevices[iDevice];
1547 if (pPciDev)
1548 {
1549 u32Value = pPciDev->Int.s.pfnConfigRead(pPciDev->Int.s.CTX_SUFF(pDevIns), pPciDev, u32Address, cb);
1550 Log(("%s: %s: u32Address=%02x u32Value=%08x cb=%d\n", __FUNCTION__, pPciDev->pszNameR3, u32Address, u32Value, cb));
1551 }
1552 }
1553
1554 return u32Value;
1555}
1556
1557
1558/**
1559 * @callback_method_impl{FNSSMDEVSAVEEXEC}
1560 */
1561static DECLCALLBACK(int) pcibridgeR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1562{
1563 PDEVPCIBUS pThis = PDMINS_2_DATA(pDevIns, PDEVPCIBUS);
1564 return pciR3CommonSaveExec(pThis, pSSM);
1565}
1566
1567
1568/**
1569 * @callback_method_impl{FNSSMDEVLOADEXEC}
1570 */
1571static DECLCALLBACK(int) pcibridgeR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
1572{
1573 PDEVPCIBUS pThis = PDMINS_2_DATA(pDevIns, PDEVPCIBUS);
1574 if (uVersion > VBOX_PCI_SAVED_STATE_VERSION)
1575 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
1576 return pciR3CommonLoadExec(pThis, pSSM, uVersion, uPass);
1577}
1578
1579
1580/**
1581 * @interface_method_impl{PDMDEVREG,pfnReset}
1582 */
1583static DECLCALLBACK(void) pcibridgeR3Reset(PPDMDEVINS pDevIns)
1584{
1585 PDEVPCIBUS pBus = PDMINS_2_DATA(pDevIns, PDEVPCIBUS);
1586
1587 /* Reset config space to default values. */
1588 pBus->PciDev.abConfig[VBOX_PCI_PRIMARY_BUS] = 0;
1589 pBus->PciDev.abConfig[VBOX_PCI_SECONDARY_BUS] = 0;
1590 pBus->PciDev.abConfig[VBOX_PCI_SUBORDINATE_BUS] = 0;
1591}
1592
1593
1594/**
1595 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1596 */
1597static DECLCALLBACK(int) pcibridgeR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1598{
1599 RT_NOREF(iInstance);
1600 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1601
1602 /*
1603 * Validate and read configuration.
1604 */
1605 if (!CFGMR3AreValuesValid(pCfg, "GCEnabled\0" "R0Enabled\0"))
1606 return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
1607
1608 /* check if RC code is enabled. */
1609 bool fGCEnabled;
1610 int rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &fGCEnabled, true);
1611 if (RT_FAILURE(rc))
1612 return PDMDEV_SET_ERROR(pDevIns, rc,
1613 N_("Configuration error: Failed to query boolean value \"GCEnabled\""));
1614
1615 /* check if R0 code is enabled. */
1616 bool fR0Enabled;
1617 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &fR0Enabled, true);
1618 if (RT_FAILURE(rc))
1619 return PDMDEV_SET_ERROR(pDevIns, rc,
1620 N_("Configuration error: Failed to query boolean value \"R0Enabled\""));
1621 Log(("PCI: fGCEnabled=%RTbool fR0Enabled=%RTbool\n", fGCEnabled, fR0Enabled));
1622
1623 /*
1624 * Init data and register the PCI bus.
1625 */
1626 PDEVPCIBUS pBus = PDMINS_2_DATA(pDevIns, PDEVPCIBUS);
1627 pBus->fTypePiix3 = true;
1628 pBus->fTypeIch9 = false;
1629 pBus->fPureBridge = true;
1630 pBus->pDevInsR3 = pDevIns;
1631 pBus->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
1632 pBus->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
1633 pBus->papBridgesR3 = (PPDMPCIDEV *)PDMDevHlpMMHeapAllocZ(pDevIns, sizeof(PPDMPCIDEV) * RT_ELEMENTS(pBus->apDevices));
1634 AssertLogRelReturn(pBus->papBridgesR3, VERR_NO_MEMORY);
1635
1636 PDMPCIBUSREG PciBusReg;
1637 PciBusReg.u32Version = PDM_PCIBUSREG_VERSION;
1638 PciBusReg.pfnRegisterR3 = pcibridgeR3MergedRegisterDevice;
1639 PciBusReg.pfnRegisterMsiR3 = NULL;
1640 PciBusReg.pfnIORegionRegisterR3 = devpciR3CommonIORegionRegister;
1641 PciBusReg.pfnSetConfigCallbacksR3 = devpciR3CommonSetConfigCallbacks;
1642 PciBusReg.pfnSetIrqR3 = pcibridgeSetIrq;
1643 PciBusReg.pfnFakePCIBIOSR3 = NULL; /* Only needed for the first bus. */
1644 PciBusReg.pszSetIrqRC = fGCEnabled ? "pcibridgeSetIrq" : NULL;
1645 PciBusReg.pszSetIrqR0 = fR0Enabled ? "pcibridgeSetIrq" : NULL;
1646#if PDM_DEVHLPR3_VERSION >= PDM_VERSION_MAKE_PP(0xffe7, 20, 0)
1647 rc = PDMDevHlpPCIBusRegister(pDevIns, &PciBusReg, &pBus->pPciHlpR3, &pBus->iBus);
1648#else
1649 rc = PDMDevHlpPCIBusRegister(pDevIns, &PciBusReg, &pBus->pPciHlpR3);
1650 pBus->iBus = rc;
1651#endif
1652 if (RT_FAILURE(rc))
1653 return PDMDEV_SET_ERROR(pDevIns, rc,
1654 N_("Failed to register ourselves as a PCI Bus"));
1655 Assert(pBus->iBus == (uint32_t)iInstance + 1); /* Can be removed when adding support for multiple bridge implementations. */
1656 if (pBus->pPciHlpR3->u32Version != PDM_PCIHLPR3_VERSION)
1657 return PDMDevHlpVMSetError(pDevIns, VERR_VERSION_MISMATCH, RT_SRC_POS,
1658 N_("PCI helper version mismatch; got %#x expected %#x"),
1659 pBus->pPciHlpR3->u32Version, PDM_PCIHLPR3_VERSION);
1660
1661 pBus->pPciHlpRC = pBus->pPciHlpR3->pfnGetRCHelpers(pDevIns);
1662 pBus->pPciHlpR0 = pBus->pPciHlpR3->pfnGetR0Helpers(pDevIns);
1663
1664 /*
1665 * Fill in PCI configs and add them to the bus.
1666 */
1667 PCIDevSetVendorId( &pBus->PciDev, 0x8086); /* Intel */
1668 PCIDevSetDeviceId( &pBus->PciDev, 0x2448); /* 82801 Mobile PCI bridge. */
1669 PCIDevSetRevisionId(&pBus->PciDev, 0xf2);
1670 PCIDevSetClassSub( &pBus->PciDev, 0x04); /* pci2pci */
1671 PCIDevSetClassBase( &pBus->PciDev, 0x06); /* PCI_bridge */
1672 PCIDevSetClassProg( &pBus->PciDev, 0x01); /* Supports subtractive decoding. */
1673 PCIDevSetHeaderType(&pBus->PciDev, 0x01); /* Single function device which adheres to the PCI-to-PCI bridge spec. */
1674 PCIDevSetCommand( &pBus->PciDev, 0x00);
1675 PCIDevSetStatus( &pBus->PciDev, 0x20); /* 66MHz Capable. */
1676 PCIDevSetInterruptLine(&pBus->PciDev, 0x00); /* This device does not assert interrupts. */
1677
1678 /*
1679 * This device does not generate interrupts. Interrupt delivery from
1680 * devices attached to the bus is unaffected.
1681 */
1682 PCIDevSetInterruptPin(&pBus->PciDev, 0x00);
1683
1684 /*
1685 * Register this PCI bridge. The called function will take care on which bus we will get registered.
1686 */
1687 rc = PDMDevHlpPCIRegisterEx(pDevIns, &pBus->PciDev, PDMPCIDEVREG_CFG_PRIMARY, PDMPCIDEVREG_F_PCI_BRIDGE,
1688 PDMPCIDEVREG_DEV_NO_FIRST_UNUSED, PDMPCIDEVREG_FUN_NO_FIRST_UNUSED, "pcibridge");
1689 if (RT_FAILURE(rc))
1690 return rc;
1691 pBus->PciDev.Int.s.pfnBridgeConfigRead = pcibridgeR3ConfigRead;
1692 pBus->PciDev.Int.s.pfnBridgeConfigWrite = pcibridgeR3ConfigWrite;
1693
1694 pBus->iDevSearch = 0;
1695
1696 /*
1697 * Register SSM handlers. We use the same saved state version as for the host bridge
1698 * to make changes easier.
1699 */
1700 rc = PDMDevHlpSSMRegisterEx(pDevIns, VBOX_PCI_SAVED_STATE_VERSION, sizeof(*pBus) + 16*128, "pgm",
1701 NULL, NULL, NULL,
1702 NULL, pcibridgeR3SaveExec, NULL,
1703 NULL, pcibridgeR3LoadExec, NULL);
1704 if (RT_FAILURE(rc))
1705 return rc;
1706
1707 return VINF_SUCCESS;
1708}
1709
1710
1711/**
1712 * @interface_method_impl{PDMDEVREG,pfnDestruct}
1713 */
1714static DECLCALLBACK(int) pcibridgeR3Destruct(PPDMDEVINS pDevIns)
1715{
1716 PDEVPCIBUS pBus = PDMINS_2_DATA(pDevIns, PDEVPCIBUS);
1717 if (pBus->papBridgesR3)
1718 {
1719 PDMDevHlpMMHeapFree(pDevIns, pBus->papBridgesR3);
1720 pBus->papBridgesR3 = NULL;
1721 }
1722 return VINF_SUCCESS;
1723}
1724
1725
1726/**
1727 * The device registration structure
1728 * for the PCI-to-PCI bridge.
1729 */
1730const PDMDEVREG g_DevicePCIBridge =
1731{
1732 /* u32Version */
1733 PDM_DEVREG_VERSION,
1734 /* szName */
1735 "pcibridge",
1736 /* szRCMod */
1737 "VBoxDDRC.rc",
1738 /* szR0Mod */
1739 "VBoxDDR0.r0",
1740 /* pszDescription */
1741 "82801 Mobile PCI to PCI bridge",
1742 /* fFlags */
1743 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
1744 /* fClass */
1745 PDM_DEVREG_CLASS_BUS_PCI,
1746 /* cMaxInstances */
1747 ~0U,
1748 /* cbInstance */
1749 sizeof(DEVPCIBUS),
1750 /* pfnConstruct */
1751 pcibridgeR3Construct,
1752 /* pfnDestruct */
1753 pcibridgeR3Destruct,
1754 /* pfnRelocate */
1755 devpciR3BusRelocate,
1756 /* pfnMemSetup */
1757 NULL,
1758 /* pfnPowerOn */
1759 NULL,
1760 /* pfnReset */
1761 pcibridgeR3Reset,
1762 /* pfnSuspend */
1763 NULL,
1764 /* pfnResume */
1765 NULL,
1766 /* pfnAttach */
1767 NULL,
1768 /* pfnDetach */
1769 NULL,
1770 /* pfnQueryInterface */
1771 NULL,
1772 /* pfnInitComplete */
1773 NULL,
1774 /* pfnPowerOff */
1775 NULL,
1776 /* pfnSoftReset */
1777 NULL,
1778 /* u32VersionEnd */
1779 PDM_DEVREG_VERSION
1780};
1781
1782#endif /* IN_RING3 */
1783
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette