1 | /* $Id: DevPciMerge1.cpp.h 80531 2019-09-01 23:03:34Z vboxsync $ */
|
---|
2 | /** @file
|
---|
3 | * DevPci - Early attempt at common code for DevPci and DevPciIch9.
|
---|
4 | *
|
---|
5 | * @note Don't add more, add code to DevPciIch9.cpp instead.
|
---|
6 | * @note We'll keep this file like this for a little while longer
|
---|
7 | * because of 5.1.
|
---|
8 | */
|
---|
9 |
|
---|
10 | /*
|
---|
11 | * Copyright (C) 2004-2019 Oracle Corporation
|
---|
12 | *
|
---|
13 | * This file is part of VirtualBox Open Source Edition (OSE), as
|
---|
14 | * available from http://www.virtualbox.org. This file is free software;
|
---|
15 | * you can redistribute it and/or modify it under the terms of the GNU
|
---|
16 | * General Public License (GPL) as published by the Free Software
|
---|
17 | * Foundation, in version 2 as it comes in the "COPYING" file of the
|
---|
18 | * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
|
---|
19 | * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
|
---|
20 | */
|
---|
21 |
|
---|
22 |
|
---|
23 | /**
|
---|
24 | * Search for a completely unused device entry (all 8 functions are unused).
|
---|
25 | *
|
---|
26 | * @returns VBox status code.
|
---|
27 | * @param pBus The bus to register with.
|
---|
28 | * @remarks Caller enters the PDM critical section.
|
---|
29 | */
|
---|
30 | static uint8_t pciR3MergedFindUnusedDeviceNo(PDEVPCIBUS pBus)
|
---|
31 | {
|
---|
32 | for (uint8_t uPciDevNo = pBus->iDevSearch >> VBOX_PCI_DEVFN_DEV_SHIFT; uPciDevNo < VBOX_PCI_MAX_DEVICES; uPciDevNo++)
|
---|
33 | if ( !pBus->apDevices[VBOX_PCI_DEVFN_MAKE(uPciDevNo, 0)]
|
---|
34 | && !pBus->apDevices[VBOX_PCI_DEVFN_MAKE(uPciDevNo, 1)]
|
---|
35 | && !pBus->apDevices[VBOX_PCI_DEVFN_MAKE(uPciDevNo, 2)]
|
---|
36 | && !pBus->apDevices[VBOX_PCI_DEVFN_MAKE(uPciDevNo, 3)]
|
---|
37 | && !pBus->apDevices[VBOX_PCI_DEVFN_MAKE(uPciDevNo, 4)]
|
---|
38 | && !pBus->apDevices[VBOX_PCI_DEVFN_MAKE(uPciDevNo, 5)]
|
---|
39 | && !pBus->apDevices[VBOX_PCI_DEVFN_MAKE(uPciDevNo, 6)]
|
---|
40 | && !pBus->apDevices[VBOX_PCI_DEVFN_MAKE(uPciDevNo, 7)])
|
---|
41 | return uPciDevNo;
|
---|
42 | return UINT8_MAX;
|
---|
43 | }
|
---|
44 |
|
---|
45 |
|
---|
46 |
|
---|
47 | /**
|
---|
48 | * Registers the device with the specified PCI bus.
|
---|
49 | *
|
---|
50 | * This is shared between the pci bus and pci bridge code.
|
---|
51 | *
|
---|
52 | * @returns VBox status code.
|
---|
53 | * @param pDevIns The PCI bus device instance.
|
---|
54 | * @param pBus The bus to register with.
|
---|
55 | * @param pPciDev The PCI device structure.
|
---|
56 | * @param fFlags Reserved for future use, PDMPCIDEVREG_F_MBZ.
|
---|
57 | * @param uPciDevNo PDMPCIDEVREG_DEV_NO_FIRST_UNUSED, or a specific
|
---|
58 | * device number (0-31).
|
---|
59 | * @param uPciFunNo PDMPCIDEVREG_FUN_NO_FIRST_UNUSED, or a specific
|
---|
60 | * function number (0-7).
|
---|
61 | * @param pszName Device name (static but not unique).
|
---|
62 | * @param pfnConfigRead The default config read method.
|
---|
63 | * @param pfnConfigWrite The default config read method.
|
---|
64 | *
|
---|
65 | * @remarks Caller enters the PDM critical section.
|
---|
66 | */
|
---|
67 | static int pciR3MergedRegisterDeviceOnBus(PPDMDEVINS pDevIns, PDEVPCIBUS pBus, PPDMPCIDEV pPciDev, uint32_t fFlags,
|
---|
68 | uint8_t uPciDevNo, uint8_t uPciFunNo, const char *pszName,
|
---|
69 | PFNPCICONFIGREAD pfnConfigRead, PFNPCICONFIGWRITE pfnConfigWrite)
|
---|
70 | {
|
---|
71 | /*
|
---|
72 | * Validate input.
|
---|
73 | */
|
---|
74 | AssertPtrReturn(pszName, VERR_INVALID_POINTER);
|
---|
75 | AssertPtrReturn(pPciDev, VERR_INVALID_POINTER);
|
---|
76 | AssertReturn(!(fFlags & ~PDMPCIDEVREG_F_VALID_MASK), VERR_INVALID_FLAGS);
|
---|
77 | AssertReturn(uPciDevNo < VBOX_PCI_MAX_DEVICES || uPciDevNo == PDMPCIDEVREG_DEV_NO_FIRST_UNUSED, VERR_INVALID_PARAMETER);
|
---|
78 | AssertReturn(uPciFunNo < VBOX_PCI_MAX_FUNCTIONS || uPciFunNo == PDMPCIDEVREG_FUN_NO_FIRST_UNUSED, VERR_INVALID_PARAMETER);
|
---|
79 |
|
---|
80 | /*
|
---|
81 | * Assign device & function numbers.
|
---|
82 | */
|
---|
83 |
|
---|
84 | /* Work the optional assignment flag. */
|
---|
85 | if (fFlags & PDMPCIDEVREG_F_NOT_MANDATORY_NO)
|
---|
86 | {
|
---|
87 | AssertLogRelMsgReturn(uPciDevNo < VBOX_PCI_MAX_DEVICES && uPciFunNo < VBOX_PCI_MAX_FUNCTIONS,
|
---|
88 | ("PDMPCIDEVREG_F_NOT_MANDATORY_NO not implemented for #Dev=%#x / #Fun=%#x\n", uPciDevNo, uPciFunNo),
|
---|
89 | VERR_NOT_IMPLEMENTED);
|
---|
90 | if (pBus->apDevices[VBOX_PCI_DEVFN_MAKE(uPciDevNo, uPciFunNo)])
|
---|
91 | {
|
---|
92 | uPciDevNo = PDMPCIDEVREG_DEV_NO_FIRST_UNUSED;
|
---|
93 | uPciFunNo = PDMPCIDEVREG_FUN_NO_FIRST_UNUSED;
|
---|
94 | }
|
---|
95 | }
|
---|
96 |
|
---|
97 | if (uPciDevNo == PDMPCIDEVREG_DEV_NO_FIRST_UNUSED)
|
---|
98 | {
|
---|
99 | /* Just find the next unused device number and we're good. */
|
---|
100 | uPciDevNo = pciR3MergedFindUnusedDeviceNo(pBus);
|
---|
101 | AssertLogRelMsgReturn(uPciDevNo < VBOX_PCI_MAX_DEVICES, ("Couldn't find a free spot!\n"), VERR_PDM_TOO_PCI_MANY_DEVICES);
|
---|
102 | if (uPciFunNo == PDMPCIDEVREG_FUN_NO_FIRST_UNUSED)
|
---|
103 | uPciFunNo = 0;
|
---|
104 | }
|
---|
105 | else
|
---|
106 | {
|
---|
107 | /*
|
---|
108 | * Direct assignment of device number can be more complicated.
|
---|
109 | */
|
---|
110 | PPDMPCIDEV pClash;
|
---|
111 | if (uPciFunNo != PDMPCIDEVREG_FUN_NO_FIRST_UNUSED)
|
---|
112 | {
|
---|
113 | /* In the case of a specified function, we only relocate an existing
|
---|
114 | device if it belongs to a different device instance. Reasoning is
|
---|
115 | that the device should figure out it's own function assignments.
|
---|
116 | Note! We could make this more flexible by relocating functions assigned
|
---|
117 | via PDMPCIDEVREG_FUN_NO_FIRST_UNUSED, but it can wait till it's needed. */
|
---|
118 | pClash = pBus->apDevices[VBOX_PCI_DEVFN_MAKE(uPciDevNo, uPciFunNo)];
|
---|
119 | if (!pClash)
|
---|
120 | { /* likely */ }
|
---|
121 | else if (pClash->Int.s.pDevInsR3 == pPciDev->Int.s.pDevInsR3)
|
---|
122 | AssertLogRelMsgFailedReturn(("PCI Configuration conflict at %u.%u: %s vs %s (same pDevIns)!\n",
|
---|
123 | uPciDevNo, uPciFunNo, pClash->pszNameR3, pszName),
|
---|
124 | VERR_PDM_TOO_PCI_MANY_DEVICES);
|
---|
125 | else if (!pClash->Int.s.fReassignableDevNo)
|
---|
126 | AssertLogRelMsgFailedReturn(("PCI Configuration conflict at %u.%u: %s vs %s (different pDevIns)!\n",
|
---|
127 | uPciDevNo, uPciFunNo, pClash->pszNameR3, pszName),
|
---|
128 | VERR_PDM_TOO_PCI_MANY_DEVICES);
|
---|
129 | }
|
---|
130 | else
|
---|
131 | {
|
---|
132 | /* First unused function slot. Again, we only relocate the whole
|
---|
133 | thing if all the device instance differs, because we otherwise
|
---|
134 | reason that a device should manage its own functions correctly. */
|
---|
135 | unsigned cSameDevInses = 0;
|
---|
136 | for (uPciFunNo = 0, pClash = NULL; uPciFunNo < VBOX_PCI_MAX_FUNCTIONS; uPciFunNo++)
|
---|
137 | {
|
---|
138 | pClash = pBus->apDevices[VBOX_PCI_DEVFN_MAKE(uPciDevNo, uPciFunNo)];
|
---|
139 | if (!pClash)
|
---|
140 | break;
|
---|
141 | cSameDevInses += pClash->Int.s.pDevInsR3 == pPciDev->Int.s.pDevInsR3;
|
---|
142 | }
|
---|
143 | if (!pClash)
|
---|
144 | Assert(uPciFunNo < VBOX_PCI_MAX_FUNCTIONS);
|
---|
145 | else
|
---|
146 | AssertLogRelMsgReturn(cSameDevInses == 0,
|
---|
147 | ("PCI Configuration conflict at %u.* appending %s (%u of %u pDevIns matches)!\n",
|
---|
148 | uPciDevNo, pszName, cSameDevInses, VBOX_PCI_MAX_FUNCTIONS),
|
---|
149 | VERR_PDM_TOO_PCI_MANY_DEVICES);
|
---|
150 | }
|
---|
151 | if (pClash)
|
---|
152 | {
|
---|
153 | /*
|
---|
154 | * Try relocate the existing device.
|
---|
155 | */
|
---|
156 | /* Check that all functions can be moved. */
|
---|
157 | for (uint8_t uMoveFun = 0; uMoveFun < VBOX_PCI_MAX_FUNCTIONS; uMoveFun++)
|
---|
158 | {
|
---|
159 | PPDMPCIDEV pMovePciDev = pBus->apDevices[VBOX_PCI_DEVFN_MAKE(uPciDevNo, uMoveFun)];
|
---|
160 | AssertLogRelMsgReturn(!pMovePciDev || pMovePciDev->Int.s.fReassignableDevNo,
|
---|
161 | ("PCI Configuration conflict at %u.%u: %s vs %s\n",
|
---|
162 | uPciDevNo, uMoveFun, pMovePciDev->pszNameR3, pszName),
|
---|
163 | VERR_PDM_TOO_PCI_MANY_DEVICES);
|
---|
164 | }
|
---|
165 |
|
---|
166 | /* Find a free device number to move it to. */
|
---|
167 | uint8_t uMoveToDevNo = pciR3MergedFindUnusedDeviceNo(pBus);
|
---|
168 | Assert(uMoveToDevNo != uPciFunNo);
|
---|
169 | AssertLogRelMsgReturn(uMoveToDevNo < VBOX_PCI_MAX_DEVICES,
|
---|
170 | ("No space to relocate device at %u so '%s' can be placed there instead!\n", uPciFunNo, pszName),
|
---|
171 | VERR_PDM_TOO_PCI_MANY_DEVICES);
|
---|
172 |
|
---|
173 | /* Execute the move. */
|
---|
174 | for (uint8_t uMoveFun = 0; uMoveFun < VBOX_PCI_MAX_FUNCTIONS; uMoveFun++)
|
---|
175 | {
|
---|
176 | PPDMPCIDEV pMovePciDev = pBus->apDevices[VBOX_PCI_DEVFN_MAKE(uPciDevNo, uMoveFun)];
|
---|
177 | if (pMovePciDev)
|
---|
178 | {
|
---|
179 | Log(("PCI: Relocating '%s' from %u.%u to %u.%u.\n", pMovePciDev->pszNameR3, uPciDevNo, uMoveFun, uMoveToDevNo, uMoveFun));
|
---|
180 | pBus->apDevices[VBOX_PCI_DEVFN_MAKE(uPciDevNo, uMoveFun)] = NULL;
|
---|
181 | pBus->apDevices[VBOX_PCI_DEVFN_MAKE(uMoveToDevNo, uMoveFun)] = pMovePciDev;
|
---|
182 | pMovePciDev->uDevFn = VBOX_PCI_DEVFN_MAKE(uMoveToDevNo, uMoveFun);
|
---|
183 | }
|
---|
184 | }
|
---|
185 | }
|
---|
186 | }
|
---|
187 |
|
---|
188 | /*
|
---|
189 | * Now, initialize the rest of the PCI device structure.
|
---|
190 | */
|
---|
191 | Assert(!pBus->apDevices[VBOX_PCI_DEVFN_MAKE(uPciDevNo, uPciFunNo)]);
|
---|
192 | pBus->apDevices[VBOX_PCI_DEVFN_MAKE(uPciDevNo, uPciFunNo)] = pPciDev;
|
---|
193 |
|
---|
194 | pPciDev->uDevFn = VBOX_PCI_DEVFN_MAKE(uPciDevNo, uPciFunNo);
|
---|
195 | pPciDev->Int.s.pBusR3 = pBus;
|
---|
196 | Assert(pBus == PDMINS_2_DATA(pDevIns, PDEVPCIBUS));
|
---|
197 | pPciDev->Int.s.pBusR0 = PDMINS_2_DATA_R0PTR(pDevIns);
|
---|
198 | pPciDev->Int.s.pBusRC = PDMINS_2_DATA_RCPTR(pDevIns);
|
---|
199 | pPciDev->Int.s.pfnConfigRead = pfnConfigRead;
|
---|
200 | pPciDev->Int.s.pfnConfigWrite = pfnConfigWrite;
|
---|
201 |
|
---|
202 | /* Remember and mark bridges. */
|
---|
203 | if (fFlags & PDMPCIDEVREG_F_PCI_BRIDGE)
|
---|
204 | {
|
---|
205 | AssertLogRelMsgReturn(pBus->cBridges < RT_ELEMENTS(pBus->apDevices),
|
---|
206 | ("Number of bridges exceeds the number of possible devices on the bus\n"),
|
---|
207 | VERR_INTERNAL_ERROR_3);
|
---|
208 | pBus->papBridgesR3[pBus->cBridges++] = pPciDev;
|
---|
209 | pciDevSetPci2PciBridge(pPciDev);
|
---|
210 | }
|
---|
211 |
|
---|
212 | Log(("PCI: Registered device %d function %d (%#x) '%s'.\n",
|
---|
213 | uPciDevNo, uPciFunNo, UINT32_C(0x80000000) | (pPciDev->uDevFn << 8), pszName));
|
---|
214 |
|
---|
215 | return VINF_SUCCESS;
|
---|
216 | }
|
---|
217 |
|
---|
218 |
|
---|
219 | /**
|
---|
220 | * @interface_method_impl{PDMPCIBUSREG,pfnRegisterR3}
|
---|
221 | */
|
---|
222 | static DECLCALLBACK(int) pciR3MergedRegister(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t fFlags,
|
---|
223 | uint8_t uPciDevNo, uint8_t uPciFunNo, const char *pszName)
|
---|
224 | {
|
---|
225 | PDEVPCIBUS pBus = PDMINS_2_DATA(pDevIns, PDEVPCIBUS);
|
---|
226 | AssertCompileMemberOffset(DEVPCIROOT, PciBus, 0);
|
---|
227 | return pciR3MergedRegisterDeviceOnBus(pDevIns, pBus, pPciDev, fFlags, uPciDevNo, uPciFunNo, pszName,
|
---|
228 | devpciR3CommonDefaultConfigRead, devpciR3CommonDefaultConfigWrite);
|
---|
229 | }
|
---|
230 |
|
---|
231 |
|
---|
232 | /**
|
---|
233 | * @interface_method_impl{PDMPCIBUSREG,pfnRegisterR3}
|
---|
234 | */
|
---|
235 | static DECLCALLBACK(int) pcibridgeR3MergedRegisterDevice(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t fFlags,
|
---|
236 | uint8_t uPciDevNo, uint8_t uPciFunNo, const char *pszName)
|
---|
237 | {
|
---|
238 | PDEVPCIBUS pBus = PDMINS_2_DATA(pDevIns, PDEVPCIBUS);
|
---|
239 | return pciR3MergedRegisterDeviceOnBus(pDevIns, pBus, pPciDev, fFlags, uPciDevNo, uPciFunNo, pszName,
|
---|
240 | devpciR3CommonDefaultConfigRead, devpciR3CommonDefaultConfigWrite);
|
---|
241 | }
|
---|
242 |
|
---|