VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/ResourceAssignmentManager.cpp@ 106957

Last change on this file since 106957 was 106957, checked in by vboxsync, 2 months ago

Main/ResourceAssignmentManager: Rewrite to accomodate for the fact that Windows guests have several requirements on where devices are located (TPM needs to be at fixed 0xfed40000, PL061 GPIO driver doesn't support 64bit MMIO, the GIC re-disitributor emulated by Hyper-V requires 128KiB of MMIO space rather than the 64KiB we currently reserve). This will break saved state compatibility (whether we want to restore this functionality is up for discussion), bugref:10732

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.9 KB
Line 
1/* $Id: ResourceAssignmentManager.cpp 106957 2024-11-12 12:09:04Z vboxsync $ */
2/** @file
3 * VirtualBox bus slots assignment manager
4 */
5
6/*
7 * Copyright (C) 2010-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_MAIN
33#include "LoggingNew.h"
34
35#include "ResourceAssignmentManager.h"
36
37#include <VBox/com/array.h>
38
39#include <iprt/asm.h>
40#include <iprt/mem.h>
41#include <iprt/string.h>
42
43
44/*********************************************************************************************************************************
45* Structures and Typedefs *
46*********************************************************************************************************************************/
47/**
48 * Address space range type.
49 */
50typedef enum RESOURCEREGIONTYPE
51{
52 /** Free region. */
53 kResourceRegionType_Free = 0,
54 /** RAM. */
55 kResourceRegionType_Ram,
56 /** ROM. */
57 kResourceRegionType_Rom,
58 /** MMIO. */
59 kResourceRegionType_Mmio,
60 /** 32bit hack. */
61 kResourceRegionType_32Bit_Hack = 0x7fffffff
62} RESOURCEREGIONTYPE;
63
64
65/**
66 * Address space range descriptor.
67 */
68typedef struct RESOURCEREGION
69{
70 /** Region name. */
71 char szName[32];
72 /** Physical start address of the region. */
73 RTGCPHYS GCPhysStart;
74 /** Physical end address of the region. */
75 RTGCPHYS GCPhysEnd;
76 /** Region type. */
77 RESOURCEREGIONTYPE enmType;
78 /** Padding. */
79 uint32_t u32Pad;
80} RESOURCEREGION;
81AssertCompileSize(RESOURCEREGION, 56);
82/** Pointer to a resource region. */
83typedef RESOURCEREGION *PRESOURCEREGION;
84/** Pointer to a const resource region. */
85typedef const RESOURCEREGION *PCRESOURCEREGION;
86
87
88/*********************************************************************************************************************************
89* Global Variables *
90*********************************************************************************************************************************/
91
92
93/**
94 * Resource assignment manage state data.
95 * @internal
96 */
97struct ResourceAssignmentManager::State
98{
99 ChipsetType_T mChipsetType;
100 IommuType_T mIommuType;
101
102 /** Pointer to the array of regions (sorted by address, not overlapping, adjacent). */
103 PRESOURCEREGION m_paRegions;
104 /** Number of used regions. */
105 uint32_t m_cRegions;
106 /** Number of regions the allocated array can hold. */
107 uint32_t m_cRegionsMax;
108
109 uint32_t mcInterrupts;
110 uint32_t miInterrupt;
111
112 State()
113 : mChipsetType(ChipsetType_Null)
114 , mIommuType(IommuType_None)
115 , m_paRegions(NULL)
116 , m_cRegions(0)
117 , m_cRegionsMax(0)
118 , mcInterrupts(0)
119 , miInterrupt(0)
120 {}
121 ~State()
122 {}
123
124 HRESULT init(ChipsetType_T chipsetType, IommuType_T iommuType, uint32_t cInterrupts);
125
126 HRESULT ensureAdditionalRegions(uint32_t cRegions);
127 void setRegion(PRESOURCEREGION pRegion, const char *pszName, RESOURCEREGIONTYPE enmType,
128 RTGCPHYS GCPhysStart, RTGCPHYS GCPhysEnd);
129 HRESULT addAddrRange(const char *pszName, RESOURCEREGIONTYPE enmType, RTGCPHYS GCPhysStart, RTGCPHYS GCPhysEnd);
130 HRESULT findNextFreeAddrRange(RTGCPHYS cbRegion, RTGCPHYS cbAlignment, RTGCPHYS GCPhysMax, RTGCPHYS GCPhysHint,
131 PRTGCPHYS pGCPhysStart, PRTGCPHYS pcbRegion);
132
133 void dumpToReleaseLog(void);
134};
135
136
137HRESULT ResourceAssignmentManager::State::init(ChipsetType_T chipsetType, IommuType_T iommuType,
138 uint32_t cInterrupts)
139{
140 Assert(chipsetType == ChipsetType_ARMv8Virtual);
141 Assert(iommuType == IommuType_None); /* For now no IOMMU support on ARMv8. */
142
143 mChipsetType = chipsetType;
144 mIommuType = iommuType;
145 mcInterrupts = cInterrupts;
146 miInterrupt = 0;
147
148 m_paRegions = (PRESOURCEREGION)RTMemRealloc(m_paRegions, 32 * sizeof(*m_paRegions));
149 if (!m_paRegions)
150 return E_OUTOFMEMORY;
151
152 m_paRegions[m_cRegions].enmType = kResourceRegionType_Free;
153 m_paRegions[m_cRegions].GCPhysStart = 0;
154 m_paRegions[m_cRegions].GCPhysEnd = UINT64_MAX;
155 strcpy(&m_paRegions[m_cRegions].szName[0], "Free");
156 m_cRegions++;
157
158 return S_OK;
159}
160
161
162HRESULT ResourceAssignmentManager::State::ensureAdditionalRegions(uint32_t cRegions)
163{
164 if (m_cRegions + cRegions == m_cRegionsMax)
165 {
166 uint32_t const cRegionsNew = m_cRegionsMax + 32;
167 PRESOURCEREGION paDescsNew = (PRESOURCEREGION)RTMemRealloc(m_paRegions, cRegionsNew * sizeof(*paDescsNew));
168 if (!paDescsNew)
169 return E_OUTOFMEMORY;
170
171 m_paRegions = paDescsNew;
172 m_cRegionsMax = cRegionsNew;
173 }
174
175 return S_OK;
176}
177
178
179void ResourceAssignmentManager::State::setRegion(PRESOURCEREGION pRegion, const char *pszName, RESOURCEREGIONTYPE enmType,
180 RTGCPHYS GCPhysStart, RTGCPHYS GCPhysEnd)
181{
182 strncpy(&pRegion->szName[0], pszName, sizeof(pRegion->szName));
183 pRegion->szName[sizeof(pRegion->szName) - 1] = '\0';
184 pRegion->enmType = enmType;
185 pRegion->GCPhysStart = GCPhysStart;
186 pRegion->GCPhysEnd = GCPhysEnd;
187}
188
189
190HRESULT ResourceAssignmentManager::State::addAddrRange(const char *pszName, RESOURCEREGIONTYPE enmType,
191 RTGCPHYS GCPhysStart, RTGCPHYS cbRegion)
192{
193 RTGCPHYS GCPhysEnd = GCPhysStart + cbRegion - 1;
194
195 /* Find the right spot in the array where to insert the range, it must not overlap with an existing used range. */
196 uint32_t iRegion = 0;
197 while ( iRegion < m_cRegions
198 && GCPhysStart > m_paRegions[iRegion].GCPhysEnd)
199 iRegion++;
200
201 Assert( iRegion == m_cRegions - 1
202 || m_paRegions[iRegion].enmType != m_paRegions[iRegion + 1].enmType);
203
204 /* Check that there is sufficient free space. */
205 if ( (m_paRegions[iRegion].enmType != kResourceRegionType_Free)
206 || GCPhysEnd > m_paRegions[iRegion].GCPhysEnd)
207 return E_INVALIDARG;
208
209 Assert(m_paRegions[iRegion].enmType == kResourceRegionType_Free);
210
211 /* Requested region fits exactly into the free region. */
212 if ( GCPhysStart == m_paRegions[iRegion].GCPhysStart
213 && GCPhysEnd == m_paRegions[iRegion].GCPhysEnd)
214 {
215 setRegion(&m_paRegions[iRegion], pszName, enmType, GCPhysStart, GCPhysEnd);
216 return S_OK;
217 }
218
219 HRESULT hrc = ensureAdditionalRegions(2 /*cRegions*/); /* Need two additional region descriptors max. */
220 if (FAILED(hrc))
221 return hrc;
222
223 /* Need to split the region into two or three depending on where the requested region lies. */
224 if (GCPhysStart == m_paRegions[iRegion].GCPhysStart)
225 {
226 /* At the start, move the free regions and everything at the end. */
227 memmove(&m_paRegions[iRegion + 1], &m_paRegions[iRegion], (m_cRegions - iRegion) * sizeof(*m_paRegions));
228 setRegion(&m_paRegions[iRegion], pszName, enmType, GCPhysStart, GCPhysEnd);
229
230 /* Adjust the free region. */
231 m_paRegions[iRegion + 1].GCPhysStart = GCPhysStart + cbRegion;
232 m_cRegions++;
233 }
234 else if (GCPhysStart + cbRegion - 1 == m_paRegions[iRegion].GCPhysEnd)
235 {
236 /* At the end of the region, move the remaining regions and adjust ranges. */
237 if (iRegion < m_cRegions)
238 memmove(&m_paRegions[iRegion + 2], &m_paRegions[iRegion + 1], (m_cRegions - iRegion) * sizeof(*m_paRegions));
239 setRegion(&m_paRegions[iRegion + 1], pszName, enmType, GCPhysStart, GCPhysEnd);
240 m_paRegions[iRegion].GCPhysEnd = GCPhysStart - 1;
241 m_cRegions++;
242 }
243 else
244 {
245 /* Somewhere in the middle, split into three regions. */
246 if (iRegion < m_cRegions)
247 memmove(&m_paRegions[iRegion + 3], &m_paRegions[iRegion + 1], (m_cRegions - iRegion) * sizeof(*m_paRegions));
248 setRegion(&m_paRegions[iRegion + 1], pszName, enmType, GCPhysStart, GCPhysEnd);
249 setRegion(&m_paRegions[iRegion + 2], "Free", kResourceRegionType_Free, GCPhysStart + cbRegion, m_paRegions[iRegion].GCPhysEnd);
250 m_paRegions[iRegion].GCPhysEnd = GCPhysStart - 1;
251 m_cRegions += 2;
252 }
253
254 return S_OK;
255}
256
257
258HRESULT ResourceAssignmentManager::State::findNextFreeAddrRange(RTGCPHYS cbRegion, RTGCPHYS cbAlignment, RTGCPHYS GCPhysMax,
259 RTGCPHYS GCPhysHint, PRTGCPHYS pGCPhysStart, PRTGCPHYS pcbRegion)
260{
261 AssertReturn(GCPhysMax >= cbRegion, E_FAIL);
262 GCPhysMax -= cbRegion;
263
264 uint32_t iRegion = 0;
265 if (GCPhysHint)
266 {
267 /* Look for free address range downwards from the hint first. */
268 iRegion = m_cRegions - 1;
269 while (iRegion)
270 {
271 PCRESOURCEREGION pRegion = &m_paRegions[iRegion];
272
273 /* Region must be free and satisfy the alignment++ requirements. */
274 RTGCPHYS GCPhysStartAligned = RT_ALIGN_T(pRegion->GCPhysEnd - cbRegion + 1, cbAlignment, RTGCPHYS);
275 if ( pRegion->enmType == kResourceRegionType_Free
276 && GCPhysHint >= pRegion->GCPhysEnd
277 && GCPhysMax >= pRegion->GCPhysEnd
278 && GCPhysStartAligned >= pRegion->GCPhysStart
279 && pRegion->GCPhysEnd - GCPhysStartAligned + 1 >= cbRegion)
280 {
281 *pGCPhysStart = GCPhysStartAligned;
282 *pcbRegion = cbRegion;
283 return S_OK;
284 }
285
286 iRegion--;
287 }
288 }
289
290 /* Find a free region which satisfies or requirements. */
291 while ( iRegion < m_cRegions
292 && GCPhysMax >= m_paRegions[iRegion].GCPhysStart)
293 {
294 PCRESOURCEREGION pRegion = &m_paRegions[iRegion];
295
296 /* Region must be free and satisfy the alignment++ requirements. */
297 RTGCPHYS GCPhysStartAligned = RT_ALIGN_T(pRegion->GCPhysStart, cbAlignment, RTGCPHYS);
298 if ( pRegion->enmType == kResourceRegionType_Free
299 && GCPhysStartAligned >= pRegion->GCPhysStart
300 && pRegion->GCPhysEnd - GCPhysStartAligned + 1 >= cbRegion)
301 {
302 *pGCPhysStart = pRegion->GCPhysStart;
303 *pcbRegion = cbRegion;
304 return S_OK;
305 }
306
307 iRegion++;
308 }
309
310 return E_OUTOFMEMORY;
311}
312
313
314static const char *resourceManagerRegionType2Str(RESOURCEREGIONTYPE enmType)
315{
316 switch (enmType)
317 {
318 case kResourceRegionType_Free: return "FREE";
319 case kResourceRegionType_Ram: return "RAM ";
320 case kResourceRegionType_Rom: return "ROM ";
321 case kResourceRegionType_Mmio: return "MMIO";
322 default: AssertFailed(); return "UNKNOWN";
323 }
324}
325
326
327void ResourceAssignmentManager::State::dumpToReleaseLog(void)
328{
329 LogRel(("Memory Regions:\n"));
330 for (uint32_t iRegion = 0; iRegion < m_cRegions; iRegion++)
331 {
332 LogRel((" %RGp - %RGp (%zu) %s %s\n",
333 m_paRegions[iRegion].GCPhysStart,
334 m_paRegions[iRegion].GCPhysEnd,
335 m_paRegions[iRegion].GCPhysEnd - m_paRegions[iRegion].GCPhysStart + 1,
336 m_paRegions[iRegion].szName));
337 }
338}
339
340
341ResourceAssignmentManager::ResourceAssignmentManager()
342 : m_pState(NULL)
343{
344 m_pState = new State();
345 Assert(m_pState);
346}
347
348
349ResourceAssignmentManager::~ResourceAssignmentManager()
350{
351 if (m_pState)
352 {
353 delete m_pState;
354 m_pState = NULL;
355 }
356}
357
358
359ResourceAssignmentManager *ResourceAssignmentManager::createInstance(PCVMMR3VTABLE pVMM, ChipsetType_T chipsetType, IommuType_T iommuType,
360 uint32_t cInterrupts, RTGCPHYS GCPhysMmioHint)
361{
362 RT_NOREF(pVMM);
363
364 ResourceAssignmentManager *pInstance = new ResourceAssignmentManager();
365
366 pInstance->m_GCPhysMmioHint = GCPhysMmioHint;
367 pInstance->m_pState->init(chipsetType, iommuType, cInterrupts);
368 Assert(pInstance);
369 return pInstance;
370}
371
372
373HRESULT ResourceAssignmentManager::assignFixedRomRegion(const char *pszName, RTGCPHYS GCPhysStart, RTGCPHYS cbRegion)
374{
375 return m_pState->addAddrRange(pszName, kResourceRegionType_Rom, GCPhysStart, cbRegion);
376}
377
378
379HRESULT ResourceAssignmentManager::assignFixedRamRegion(const char *pszName, RTGCPHYS GCPhysStart, RTGCPHYS cbRegion)
380{
381 return m_pState->addAddrRange(pszName, kResourceRegionType_Ram, GCPhysStart, cbRegion);
382}
383
384
385HRESULT ResourceAssignmentManager::assignFixedMmioRegion(const char *pszName, RTGCPHYS GCPhysStart, RTGCPHYS cbRegion)
386{
387 return m_pState->addAddrRange(pszName, kResourceRegionType_Mmio, GCPhysStart, cbRegion);
388}
389
390
391HRESULT ResourceAssignmentManager::assignMmioRegionAligned(const char *pszName, RTGCPHYS cbRegion, RTGCPHYS cbAlignment, PRTGCPHYS pGCPhysStart, PRTGCPHYS pcbRegion,
392 bool fOnly32Bit)
393{
394 RTGCPHYS GCPhysRegionStart;
395 RTGCPHYS cbRegionFinal;
396 HRESULT hrc = m_pState->findNextFreeAddrRange(cbRegion, cbAlignment, fOnly32Bit ? _4G : RTGCPHYS_MAX,
397 m_GCPhysMmioHint, &GCPhysRegionStart, &cbRegionFinal);
398 if (SUCCEEDED(hrc))
399 {
400 *pGCPhysStart = GCPhysRegionStart;
401 *pcbRegion = cbRegionFinal;
402 return assignFixedMmioRegion(pszName, GCPhysRegionStart, cbRegion);
403 }
404
405 return hrc;
406}
407
408
409HRESULT ResourceAssignmentManager::assignMmioRegion(const char *pszName, RTGCPHYS cbRegion, PRTGCPHYS pGCPhysStart, PRTGCPHYS pcbRegion)
410{
411 return assignMmioRegionAligned(pszName, cbRegion, _4K, pGCPhysStart, pcbRegion, false /*fOnly32Bit*/);
412}
413
414
415HRESULT ResourceAssignmentManager::assignMmio32Region(const char *pszName, RTGCPHYS cbRegion, PRTGCPHYS pGCPhysStart, PRTGCPHYS pcbRegion)
416{
417 return assignMmioRegionAligned(pszName, cbRegion, _4K, pGCPhysStart, pcbRegion, true /*fOnly32Bit*/);
418}
419
420
421HRESULT ResourceAssignmentManager::assignInterrupts(const char *pszName, uint32_t cInterrupts, uint32_t *piInterruptFirst)
422{
423 if (m_pState->miInterrupt + cInterrupts > m_pState->mcInterrupts)
424 {
425 AssertLogRelMsgFailed(("ResourceAssignmentManager: Interrupt count for %s exceeds number of available interrupts\n", pszName));
426 return E_INVALIDARG;
427 }
428
429 *piInterruptFirst = m_pState->miInterrupt;
430 m_pState->miInterrupt += cInterrupts;
431 return S_OK;
432}
433
434HRESULT ResourceAssignmentManager::queryMmioRegion(PRTGCPHYS pGCPhysMmioStart, PRTGCPHYS pcbMmio)
435{
436 /*
437 * This ASSUMES that there are adjacent MMIO regions above 4GiB which can be combined into one single region
438 * (adjacent and no ROM/RAM regions inbetween).
439 */
440 *pGCPhysMmioStart = 0;
441 *pcbMmio = 0;
442
443 /* Look for the start. */
444 for (uint32_t i = 0; i < m_pState->m_cRegions; i++)
445 {
446 PCRESOURCEREGION pRegion = &m_pState->m_paRegions[i];
447
448 if ( pRegion->GCPhysStart >= _4G
449 && pRegion->enmType == kResourceRegionType_Mmio)
450 {
451 RTGCPHYS cbMmio = pRegion->GCPhysEnd - pRegion->GCPhysStart + 1;
452
453 *pGCPhysMmioStart = pRegion->GCPhysStart;
454
455 /* Add up any remaining MMIO regions adjacent to this one. */
456 for (uint32_t j = i; j < m_pState->m_cRegions; j++)
457 {
458 pRegion = &m_pState->m_paRegions[i];
459 if ( pRegion->enmType == kResourceRegionType_Mmio
460 && pRegion->GCPhysStart == *pGCPhysMmioStart + cbMmio)
461 cbMmio += pRegion->GCPhysEnd - pRegion->GCPhysStart + 1;
462 else
463 break;
464 }
465
466 *pcbMmio = cbMmio;
467 return S_OK;
468 }
469 }
470
471 return S_OK;
472}
473
474
475HRESULT ResourceAssignmentManager::queryMmio32Region(PRTGCPHYS pGCPhysMmioStart, PRTGCPHYS pcbMmio)
476{
477 RT_NOREF(pGCPhysMmioStart, pcbMmio);
478 return S_OK;
479}
480
481
482HRESULT ResourceAssignmentManager::assignSingleInterrupt(const char *pszName, uint32_t *piInterrupt)
483{
484 return assignInterrupts(pszName, 1, piInterrupt);
485}
486
487
488void ResourceAssignmentManager::dumpMemoryRegionsToReleaseLog(void)
489{
490 m_pState->dumpToReleaseLog();
491}
Note: See TracBrowser for help on using the repository browser.

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