VirtualBox

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

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

Main: Fix running VMs with less than 2GiB of RAM (r165896 regression)

Having too little RAM caused the pci-mmio region to be created below 4GiB,
which made queryMmioRegion return 0 and the page table setup code in the UEFI
fail because it would get confused by the entry having a 0 address and size and
leaving the page tables in a broken state.
Fixing that however doesn't work as the PCI MMIO space needs to be above 4GiB, so
ensure that the region will always be above 4GiB.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.8 KB
Line 
1/* $Id: ResourceAssignmentManager.cpp 107123 2024-11-22 15:46:26Z 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 GCPhysMin, 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 GCPhysMin, 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 && GCPhysMin <= pRegion->GCPhysStart
278 && GCPhysMax >= pRegion->GCPhysEnd
279 && GCPhysStartAligned >= pRegion->GCPhysStart
280 && pRegion->GCPhysEnd - GCPhysStartAligned + 1 >= cbRegion)
281 {
282 *pGCPhysStart = GCPhysStartAligned;
283 *pcbRegion = cbRegion;
284 return S_OK;
285 }
286
287 iRegion--;
288 }
289 }
290
291 while ( iRegion < m_cRegions
292 && GCPhysMin > m_paRegions[iRegion].GCPhysStart)
293 iRegion++;
294
295 /* Find a free region which satisfies or requirements. */
296 while ( iRegion < m_cRegions
297 && GCPhysMax >= m_paRegions[iRegion].GCPhysStart)
298 {
299 PCRESOURCEREGION pRegion = &m_paRegions[iRegion];
300
301 /* Region must be free and satisfy the alignment++ requirements. */
302 RTGCPHYS GCPhysStartAligned = RT_ALIGN_T(pRegion->GCPhysStart, cbAlignment, RTGCPHYS);
303 if ( pRegion->enmType == kResourceRegionType_Free
304 && GCPhysStartAligned >= pRegion->GCPhysStart
305 && pRegion->GCPhysEnd - GCPhysStartAligned + 1 >= cbRegion)
306 {
307 *pGCPhysStart = pRegion->GCPhysStart;
308 *pcbRegion = cbRegion;
309 return S_OK;
310 }
311
312 iRegion++;
313 }
314
315 return E_OUTOFMEMORY;
316}
317
318
319static const char *resourceManagerRegionType2Str(RESOURCEREGIONTYPE enmType)
320{
321 switch (enmType)
322 {
323 case kResourceRegionType_Free: return "FREE";
324 case kResourceRegionType_Ram: return "RAM ";
325 case kResourceRegionType_Rom: return "ROM ";
326 case kResourceRegionType_Mmio: return "MMIO";
327 default: AssertFailed(); return "UNKNOWN";
328 }
329}
330
331
332void ResourceAssignmentManager::State::dumpToReleaseLog(void)
333{
334 LogRel(("Memory Regions:\n"));
335 for (uint32_t iRegion = 0; iRegion < m_cRegions; iRegion++)
336 {
337 LogRel((" %RGp - %RGp (%zu) %s %s\n",
338 m_paRegions[iRegion].GCPhysStart,
339 m_paRegions[iRegion].GCPhysEnd,
340 m_paRegions[iRegion].GCPhysEnd - m_paRegions[iRegion].GCPhysStart + 1,
341 resourceManagerRegionType2Str(m_paRegions[iRegion].enmType),
342 m_paRegions[iRegion].szName));
343 }
344}
345
346
347ResourceAssignmentManager::ResourceAssignmentManager()
348 : m_pState(NULL)
349{
350 m_pState = new State();
351 Assert(m_pState);
352}
353
354
355ResourceAssignmentManager::~ResourceAssignmentManager()
356{
357 if (m_pState)
358 {
359 delete m_pState;
360 m_pState = NULL;
361 }
362}
363
364
365ResourceAssignmentManager *ResourceAssignmentManager::createInstance(PCVMMR3VTABLE pVMM, ChipsetType_T chipsetType, IommuType_T iommuType,
366 uint32_t cInterrupts, RTGCPHYS GCPhysMmioHint)
367{
368 RT_NOREF(pVMM);
369
370 ResourceAssignmentManager *pInstance = new ResourceAssignmentManager();
371
372 pInstance->m_GCPhysMmioHint = GCPhysMmioHint;
373 pInstance->m_pState->init(chipsetType, iommuType, cInterrupts);
374 Assert(pInstance);
375 return pInstance;
376}
377
378
379HRESULT ResourceAssignmentManager::assignFixedRomRegion(const char *pszName, RTGCPHYS GCPhysStart, RTGCPHYS cbRegion)
380{
381 return m_pState->addAddrRange(pszName, kResourceRegionType_Rom, GCPhysStart, cbRegion);
382}
383
384
385HRESULT ResourceAssignmentManager::assignFixedRamRegion(const char *pszName, RTGCPHYS GCPhysStart, RTGCPHYS cbRegion)
386{
387 return m_pState->addAddrRange(pszName, kResourceRegionType_Ram, GCPhysStart, cbRegion);
388}
389
390
391HRESULT ResourceAssignmentManager::assignFixedMmioRegion(const char *pszName, RTGCPHYS GCPhysStart, RTGCPHYS cbRegion)
392{
393 return m_pState->addAddrRange(pszName, kResourceRegionType_Mmio, GCPhysStart, cbRegion);
394}
395
396
397HRESULT ResourceAssignmentManager::assignMmioRegionAligned(const char *pszName, RTGCPHYS cbRegion, RTGCPHYS cbAlignment, PRTGCPHYS pGCPhysStart, PRTGCPHYS pcbRegion,
398 bool fOnly32Bit)
399{
400 RTGCPHYS GCPhysRegionStart;
401 RTGCPHYS cbRegionFinal;
402 HRESULT hrc = m_pState->findNextFreeAddrRange(cbRegion, cbAlignment, 0 /*GCPhysMin*/, fOnly32Bit ? _4G : RTGCPHYS_MAX,
403 m_GCPhysMmioHint, &GCPhysRegionStart, &cbRegionFinal);
404 if (SUCCEEDED(hrc))
405 {
406 *pGCPhysStart = GCPhysRegionStart;
407 *pcbRegion = cbRegionFinal;
408 return assignFixedMmioRegion(pszName, GCPhysRegionStart, cbRegion);
409 }
410
411 return hrc;
412}
413
414
415HRESULT ResourceAssignmentManager::assignMmioRegion(const char *pszName, RTGCPHYS cbRegion, PRTGCPHYS pGCPhysStart, PRTGCPHYS pcbRegion)
416{
417 return assignMmioRegionAligned(pszName, cbRegion, _4K, pGCPhysStart, pcbRegion, false /*fOnly32Bit*/);
418}
419
420
421HRESULT ResourceAssignmentManager::assignMmio32Region(const char *pszName, RTGCPHYS cbRegion, PRTGCPHYS pGCPhysStart, PRTGCPHYS pcbRegion)
422{
423 return assignMmioRegionAligned(pszName, cbRegion, _4K, pGCPhysStart, pcbRegion, true /*fOnly32Bit*/);
424}
425
426
427HRESULT ResourceAssignmentManager::assignMmio64Region(const char *pszName, RTGCPHYS cbRegion, PRTGCPHYS pGCPhysStart, PRTGCPHYS pcbRegion)
428{
429 RTGCPHYS GCPhysRegionStart;
430 RTGCPHYS cbRegionFinal;
431 HRESULT hrc = m_pState->findNextFreeAddrRange(cbRegion, _4K, _4G /*GCPhysMin*/, RTGCPHYS_MAX,
432 m_GCPhysMmioHint, &GCPhysRegionStart, &cbRegionFinal);
433 if (SUCCEEDED(hrc))
434 {
435 *pGCPhysStart = GCPhysRegionStart;
436 *pcbRegion = cbRegionFinal;
437 return assignFixedMmioRegion(pszName, GCPhysRegionStart, cbRegion);
438 }
439
440 return hrc;
441}
442
443
444HRESULT ResourceAssignmentManager::assignInterrupts(const char *pszName, uint32_t cInterrupts, uint32_t *piInterruptFirst)
445{
446 if (m_pState->miInterrupt + cInterrupts > m_pState->mcInterrupts)
447 {
448 AssertLogRelMsgFailed(("ResourceAssignmentManager: Interrupt count for %s exceeds number of available interrupts\n", pszName));
449 return E_INVALIDARG;
450 }
451
452 *piInterruptFirst = m_pState->miInterrupt;
453 m_pState->miInterrupt += cInterrupts;
454 return S_OK;
455}
456
457HRESULT ResourceAssignmentManager::queryMmioRegion(PRTGCPHYS pGCPhysMmioStart, PRTGCPHYS pcbMmio)
458{
459 /*
460 * This ASSUMES that there are adjacent MMIO regions above 4GiB which can be combined into one single region
461 * (adjacent and no ROM/RAM regions inbetween).
462 */
463 *pGCPhysMmioStart = 0;
464 *pcbMmio = 0;
465
466 /* Look for the start. */
467 for (uint32_t i = 0; i < m_pState->m_cRegions; i++)
468 {
469 PCRESOURCEREGION pRegion = &m_pState->m_paRegions[i];
470
471 if ( pRegion->GCPhysStart >= _4G
472 && pRegion->enmType == kResourceRegionType_Mmio)
473 {
474 RTGCPHYS cbMmio = pRegion->GCPhysEnd - pRegion->GCPhysStart + 1;
475
476 *pGCPhysMmioStart = pRegion->GCPhysStart;
477
478 /* Add up any remaining MMIO regions adjacent to this one. */
479 for (uint32_t j = i; j < m_pState->m_cRegions; j++)
480 {
481 pRegion = &m_pState->m_paRegions[i];
482 if ( pRegion->enmType == kResourceRegionType_Mmio
483 && pRegion->GCPhysStart == *pGCPhysMmioStart + cbMmio)
484 cbMmio += pRegion->GCPhysEnd - pRegion->GCPhysStart + 1;
485 else
486 break;
487 }
488
489 *pcbMmio = cbMmio;
490 return S_OK;
491 }
492 }
493
494 return S_OK;
495}
496
497
498HRESULT ResourceAssignmentManager::queryMmio32Region(PRTGCPHYS pGCPhysMmioStart, PRTGCPHYS pcbMmio)
499{
500 RT_NOREF(pGCPhysMmioStart, pcbMmio);
501 return S_OK;
502}
503
504
505HRESULT ResourceAssignmentManager::assignSingleInterrupt(const char *pszName, uint32_t *piInterrupt)
506{
507 return assignInterrupts(pszName, 1, piInterrupt);
508}
509
510
511void ResourceAssignmentManager::dumpMemoryRegionsToReleaseLog(void)
512{
513 m_pState->dumpToReleaseLog();
514}
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