VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/CPUMR3SysReg-armv8.cpp

Last change on this file was 109008, checked in by vboxsync, 2 weeks ago

VMM,Main: Working on ARM CPU profile support, which is neede/useful for getting info about the host CPU as well. The CPUDBENTRY typedef is used externally by Main, so we can't have two definitions of it, so left the bits that are common to both x86 and ARM in CPUDBENTRY and created sub-structures for each of the two targets/platforms. Also started reworking the VBoxCpuReport tool so we can use it on arm as well (much left to do there, though). jiraref:VBP-1598

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.6 KB
Line 
1/* $Id: CPUMR3SysReg-armv8.cpp 109008 2025-04-16 20:59:36Z vboxsync $ */
2/** @file
3 * CPUM - ARMv8 System Registers Management.
4 */
5
6/*
7 * Copyright (C) 2023-2025 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_CPUM
33#include <VBox/vmm/cpum.h>
34#include "CPUMInternal-armv8.h"
35#include <VBox/vmm/vm.h>
36
37#include <iprt/errcore.h>
38#include <iprt/armv8.h>
39#include <iprt/mem.h>
40#include <iprt/string.h>
41
42
43/**
44 * Binary search used by cpumR3SysRegRangesInsert and has some special properties
45 * wrt to mismatches.
46 *
47 * @returns Insert location.
48 * @param paSysRegRanges The system register ranges to search.
49 * @param cSysRegRanges The number of system register ranges.
50 * @param uSysReg What to search for.
51 */
52static uint32_t cpumR3SysRegRangesBinSearch(PCCPUMSYSREGRANGE paSysRegRanges, uint32_t cSysRegRanges, uint16_t uSysReg)
53{
54 if (!cSysRegRanges)
55 return 0;
56
57 uint32_t iStart = 0;
58 uint32_t iLast = cSysRegRanges - 1;
59 for (;;)
60 {
61 uint32_t i = iStart + (iLast - iStart + 1) / 2;
62 if ( uSysReg >= paSysRegRanges[i].uFirst
63 && uSysReg <= paSysRegRanges[i].uLast)
64 return i;
65 if (uSysReg < paSysRegRanges[i].uFirst)
66 {
67 if (i <= iStart)
68 return i;
69 iLast = i - 1;
70 }
71 else
72 {
73 if (i >= iLast)
74 {
75 if (i < cSysRegRanges)
76 i++;
77 return i;
78 }
79 iStart = i + 1;
80 }
81 }
82}
83
84
85/**
86 * Ensures that there is space for at least @a cNewRanges in the table,
87 * reallocating the table if necessary.
88 *
89 * @returns Pointer to the system register ranges on success, NULL on failure. On failure
90 * @a *ppaSysRegRanges is freed and set to NULL.
91 * @param pVM The cross context VM structure. If NULL,
92 * use the process heap, otherwise the VM's hyper heap.
93 * @param ppaSysRegRanges The variable pointing to the ranges (input/output).
94 * @param cSysRegRanges The current number of ranges.
95 * @param cNewRanges The number of ranges to be added.
96 */
97static PCPUMSYSREGRANGE cpumR3SysRegRangesEnsureSpace(PVM pVM, PCPUMSYSREGRANGE *ppaSysRegRanges, uint32_t cSysRegRanges, uint32_t cNewRanges)
98{
99 if ( cSysRegRanges + cNewRanges
100 > RT_ELEMENTS(pVM->cpum.s.GuestInfo.aSysRegRanges) + (pVM ? 0 : 128 /* Catch too many system registers in CPU reporter! */))
101 {
102 LogRel(("CPUM: Too many system register ranges! %#x, max %#x\n",
103 cSysRegRanges + cNewRanges, RT_ELEMENTS(pVM->cpum.s.GuestInfo.aSysRegRanges)));
104 return NULL;
105 }
106 if (pVM)
107 {
108 Assert(cSysRegRanges == pVM->cpum.s.GuestInfo.cSysRegRanges);
109 Assert(*ppaSysRegRanges == pVM->cpum.s.GuestInfo.aSysRegRanges);
110 }
111 else
112 {
113 if (cSysRegRanges + cNewRanges > RT_ALIGN_32(cSysRegRanges, 16))
114 {
115
116 uint32_t const cNew = RT_ALIGN_32(cSysRegRanges + cNewRanges, 16);
117 void *pvNew = RTMemRealloc(*ppaSysRegRanges, cNew * sizeof(**ppaSysRegRanges));
118 if (pvNew)
119 *ppaSysRegRanges = (PCPUMSYSREGRANGE)pvNew;
120 else
121 {
122 RTMemFree(*ppaSysRegRanges);
123 *ppaSysRegRanges = NULL;
124 return NULL;
125 }
126 }
127 }
128
129 return *ppaSysRegRanges;
130}
131
132
133/**
134 * Inserts a new system register range in into an sorted system register range array.
135 *
136 * If the new system register range overlaps existing ranges, the existing ones will be
137 * adjusted/removed to fit in the new one.
138 *
139 * @returns VBox status code.
140 * @retval VINF_SUCCESS
141 * @retval VERR_NO_MEMORY
142 *
143 * @param pVM The cross context VM structure. If NULL,
144 * use the process heap, otherwise the VM's hyper heap.
145 * @param ppaSysRegRanges The variable pointing to the ranges (input/output).
146 * Must be NULL if using the hyper heap.
147 * @param pcSysRegRanges The variable holding number of ranges. Must be NULL
148 * if using the hyper heap.
149 * @param pNewRange The new range.
150 */
151static int cpumR3SysRegRangesInsert(PVM pVM, PCPUMSYSREGRANGE *ppaSysRegRanges, uint32_t *pcSysRegRanges, PCCPUMSYSREGRANGE pNewRange)
152{
153 Assert(pNewRange->uLast >= pNewRange->uFirst);
154 Assert(pNewRange->enmRdFn > kCpumSysRegRdFn_Invalid && pNewRange->enmRdFn < kCpumSysRegRdFn_End);
155 Assert(pNewRange->enmWrFn > kCpumSysRegWrFn_Invalid && pNewRange->enmWrFn < kCpumSysRegWrFn_End);
156
157 /*
158 * Validate and use the VM's system register ranges array if we are using the hyper heap.
159 */
160 if (pVM)
161 {
162 AssertReturn(!ppaSysRegRanges, VERR_INVALID_PARAMETER);
163 AssertReturn(!pcSysRegRanges, VERR_INVALID_PARAMETER);
164
165 ppaSysRegRanges = &pVM->cpum.s.GuestInfo.paSysRegRangesR3;
166 pcSysRegRanges = &pVM->cpum.s.GuestInfo.cSysRegRanges;
167 }
168 else
169 {
170 AssertReturn(ppaSysRegRanges, VERR_INVALID_POINTER);
171 AssertReturn(pcSysRegRanges, VERR_INVALID_POINTER);
172 }
173
174 uint32_t cSysRegRanges = *pcSysRegRanges;
175 PCPUMSYSREGRANGE paSysRegRanges = *ppaSysRegRanges;
176
177 /*
178 * Optimize the linear insertion case where we add new entries at the end.
179 */
180 if ( cSysRegRanges > 0
181 && paSysRegRanges[cSysRegRanges - 1].uLast < pNewRange->uFirst)
182 {
183 paSysRegRanges = cpumR3SysRegRangesEnsureSpace(pVM, ppaSysRegRanges, cSysRegRanges, 1);
184 if (!paSysRegRanges)
185 return VERR_NO_MEMORY;
186 paSysRegRanges[cSysRegRanges] = *pNewRange;
187 *pcSysRegRanges += 1;
188 }
189 else
190 {
191 uint32_t i = cpumR3SysRegRangesBinSearch(paSysRegRanges, cSysRegRanges, pNewRange->uFirst);
192 Assert(i == cSysRegRanges || pNewRange->uFirst <= paSysRegRanges[i].uLast);
193 Assert(i == 0 || pNewRange->uFirst > paSysRegRanges[i - 1].uLast);
194
195 /*
196 * Adding an entirely new entry?
197 */
198 if ( i >= cSysRegRanges
199 || pNewRange->uLast < paSysRegRanges[i].uFirst)
200 {
201 paSysRegRanges = cpumR3SysRegRangesEnsureSpace(pVM, ppaSysRegRanges, cSysRegRanges, 1);
202 if (!paSysRegRanges)
203 return VERR_NO_MEMORY;
204 if (i < cSysRegRanges)
205 memmove(&paSysRegRanges[i + 1], &paSysRegRanges[i], (cSysRegRanges - i) * sizeof(paSysRegRanges[0]));
206 paSysRegRanges[i] = *pNewRange;
207 *pcSysRegRanges += 1;
208 }
209 /*
210 * Replace existing entry?
211 */
212 else if ( pNewRange->uFirst == paSysRegRanges[i].uFirst
213 && pNewRange->uLast == paSysRegRanges[i].uLast)
214 paSysRegRanges[i] = *pNewRange;
215 /*
216 * Splitting an existing entry?
217 */
218 else if ( pNewRange->uFirst > paSysRegRanges[i].uFirst
219 && pNewRange->uLast < paSysRegRanges[i].uLast)
220 {
221 paSysRegRanges = cpumR3SysRegRangesEnsureSpace(pVM, ppaSysRegRanges, cSysRegRanges, 2);
222 if (!paSysRegRanges)
223 return VERR_NO_MEMORY;
224 Assert(i < cSysRegRanges);
225 memmove(&paSysRegRanges[i + 2], &paSysRegRanges[i], (cSysRegRanges - i) * sizeof(paSysRegRanges[0]));
226 paSysRegRanges[i + 1] = *pNewRange;
227 paSysRegRanges[i + 2] = paSysRegRanges[i];
228 paSysRegRanges[i ].uLast = pNewRange->uFirst - 1;
229 paSysRegRanges[i + 2].uFirst = pNewRange->uLast + 1;
230 *pcSysRegRanges += 2;
231 }
232 /*
233 * Complicated scenarios that can affect more than one range.
234 *
235 * The current code does not optimize memmove calls when replacing
236 * one or more existing ranges, because it's tedious to deal with and
237 * not expected to be a frequent usage scenario.
238 */
239 else
240 {
241 /* Adjust start of first match? */
242 if ( pNewRange->uFirst <= paSysRegRanges[i].uFirst
243 && pNewRange->uLast < paSysRegRanges[i].uLast)
244 paSysRegRanges[i].uFirst = pNewRange->uLast + 1;
245 else
246 {
247 /* Adjust end of first match? */
248 if (pNewRange->uFirst > paSysRegRanges[i].uFirst)
249 {
250 Assert(paSysRegRanges[i].uLast >= pNewRange->uFirst);
251 paSysRegRanges[i].uLast = pNewRange->uFirst - 1;
252 i++;
253 }
254 /* Replace the whole first match (lazy bird). */
255 else
256 {
257 if (i + 1 < cSysRegRanges)
258 memmove(&paSysRegRanges[i], &paSysRegRanges[i + 1], (cSysRegRanges - i - 1) * sizeof(paSysRegRanges[0]));
259 cSysRegRanges = *pcSysRegRanges -= 1;
260 }
261
262 /* Do the new range affect more ranges? */
263 while ( i < cSysRegRanges
264 && pNewRange->uLast >= paSysRegRanges[i].uFirst)
265 {
266 if (pNewRange->uLast < paSysRegRanges[i].uLast)
267 {
268 /* Adjust the start of it, then we're done. */
269 paSysRegRanges[i].uFirst = pNewRange->uLast + 1;
270 break;
271 }
272
273 /* Remove it entirely. */
274 if (i + 1 < cSysRegRanges)
275 memmove(&paSysRegRanges[i], &paSysRegRanges[i + 1], (cSysRegRanges - i - 1) * sizeof(paSysRegRanges[0]));
276 cSysRegRanges = *pcSysRegRanges -= 1;
277 }
278 }
279
280 /* Now, perform a normal insertion. */
281 paSysRegRanges = cpumR3SysRegRangesEnsureSpace(pVM, ppaSysRegRanges, cSysRegRanges, 1);
282 if (!paSysRegRanges)
283 return VERR_NO_MEMORY;
284 if (i < cSysRegRanges)
285 memmove(&paSysRegRanges[i + 1], &paSysRegRanges[i], (cSysRegRanges - i) * sizeof(paSysRegRanges[0]));
286 paSysRegRanges[i] = *pNewRange;
287 *pcSysRegRanges += 1;
288 }
289 }
290
291 return VINF_SUCCESS;
292}
293
294
295/**
296 * Insert an system register range into the VM.
297 *
298 * If the new system register range overlaps existing ranges, the existing ones will be
299 * adjusted/removed to fit in the new one.
300 *
301 * @returns VBox status code.
302 * @param pVM The cross context VM structure.
303 * @param pNewRange Pointer to the MSR range being inserted.
304 */
305VMMR3DECL(int) CPUMR3SysRegRangesInsert(PVM pVM, PCCPUMSYSREGRANGE pNewRange)
306{
307 AssertReturn(pVM, VERR_INVALID_PARAMETER);
308 AssertReturn(pNewRange, VERR_INVALID_PARAMETER);
309
310 return cpumR3SysRegRangesInsert(pVM, NULL /* ppaSysRegRanges */, NULL /* pcSysRegRanges */, pNewRange);
311}
312
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