VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/GITSAll.cpp

Last change on this file was 109011, checked in by vboxsync, 4 days ago

VMM/GIC: bugref:10877 Base register's physical address is split when 64K pages are used (Fedora guests wants to use 64K pages).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.2 KB
Line 
1/* $Id: GITSAll.cpp 109011 2025-04-17 10:19:52Z vboxsync $ */
2/** @file
3 * GITS - GIC Interrupt Translation Service (ITS) - All Contexts.
4 */
5
6/*
7 * Copyright (C) 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_DEV_GIC
33#include "GITSInternal.h"
34
35#include <VBox/log.h>
36#include <VBox/gic.h>
37#include <VBox/vmm/pdmdev.h>
38#include <VBox/vmm/dbgf.h>
39#include <iprt/errcore.h> /* VINF_SUCCESS */
40#include <iprt/string.h> /* RT_ZERO */
41#include <iprt/mem.h> /* RTMemAllocZ, RTMemFree */
42
43
44/*********************************************************************************************************************************
45* Defined Constants And Macros *
46*********************************************************************************************************************************/
47/** The current GITS saved state version. */
48#define GITS_SAVED_STATE_VERSION 1
49
50/** Gets whether the given register offset is within the specified range. */
51#define GITS_IS_REG_IN_RANGE(a_offReg, a_offFirst, a_cbRegion) ((uint32_t)(a_offReg) - (a_offFirst) < (a_cbRegion))
52
53/** Acquire the device critical section. */
54#define GITS_CRIT_SECT_ENTER(a_pDevIns) \
55 do \
56 { \
57 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VINF_SUCCESS); \
58 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rcLock); \
59 } while(0)
60
61/** Release the device critical section. */
62#define GITS_CRIT_SECT_LEAVE(a_pDevIns) PDMDevHlpCritSectLeave((a_pDevIns), (a_pDevIns)->CTX_SUFF(pCritSectRo))
63
64/** Returns whether the critical section is held. */
65#define GITS_CRIT_SECT_IS_OWNER(a_pDevIns) PDMDevHlpCritSectIsOwner((a_pDevIns), (a_pDevIns)->CTX_SUFF(pCritSectRo))
66
67/** GITS diagnostic enum description expansion.
68 * The below construct ensures typos in the input to this macro are caught
69 * during compile time. */
70#define GITSDIAG_DESC(a_Name) RT_CONCAT(kGitsDiag_, a_Name) < kGitsDiag_End ? RT_STR(a_Name) : "Ignored"
71
72/** @def GITS_SET_REG_U64_FULL
73 * Sets a 64-bit GITS register.
74 * @param a_uReg The 64-bit register to set.
75 * @param a_uValue The 64-bit value being written.
76 * @param a_fRwMask The 64-bit mask of valid read-write bits.
77 */
78#define GITS_SET_REG_U64_FULL(a_uReg, a_uValue, a_fRwMask) \
79 do \
80 { \
81 AssertCompile(sizeof(a_uReg) == sizeof(uint64_t)); \
82 AssertCompile(sizeof(a_fRwMask) == sizeof(uint64_t)); \
83 (a_uReg) = ((a_uReg) & ~(a_fRwMask)) | ((a_uValue) & (a_fRwMask)); \
84 } while (0)
85
86/** @def GITS_SET_REG_U64_LO
87 * Sets the lower half of a 64-bit GITS register.
88 * @param a_uReg The lower half of a 64-bit register to set.
89 * @param a_uValue The value being written (only lower 32-bits are used).
90 * @param a_fRwMask The 64-bit mask of valid read-write bits.
91 */
92#define GITS_SET_REG_U64_LO(a_uReg, a_uValue, a_fRwMask) \
93 do \
94 { \
95 AssertCompile(sizeof(a_uReg) == sizeof(uint32_t)); \
96 AssertCompile(sizeof(a_fRwMask) == sizeof(uint64_t)); \
97 (a_uReg) = ((a_uReg) & ~(RT_LO_U32(a_fRwMask))) | ((uint32_t)(a_uValue) & (RT_LO_U32(a_fRwMask))); \
98 } while (0)
99
100/** @def GITS_SET_REG_U64_HI
101 * Sets the upper half of a 64-bit GITS register.
102 * @param a_uReg The upper half of the 64-bit register to set.
103 * @param a_uValue The value being written (only lower 32-bits are used).
104 * @param a_fRwMask The 64-bit mask of valid read-write bits.
105 */
106#define GITS_SET_REG_U64_HI(a_uReg, a_uValue, a_fRwMask) \
107 do \
108 { \
109 AssertCompile(sizeof(a_uReg) == sizeof(uint32_t)); \
110 AssertCompile(sizeof(a_fRwMask) == sizeof(uint64_t)); \
111 (a_uReg) = ((a_uReg) & ~(RT_HI_U32(a_fRwMask))) | ((uint32_t)(a_uValue) & (RT_HI_U32(a_fRwMask))); \
112 } while (0)
113
114/** @def GITS_SET_REG_U32
115 * Sets a 32-bit GITS register.
116 * @param a_uReg The 32-bit register to set.
117 * @param a_uValue The 32-bit value being written (only lower 32-bits are
118 * used).
119 * @param a_fRwMask The mask of valid read-write bits (only lower 32-bits are
120 * used).
121 */
122#define GITS_SET_REG_U32(a_uReg, a_uValue, a_fRwMask) \
123 do \
124 { \
125 AssertCompile(sizeof(a_uReg) == sizeof(uint32_t)); \
126 (a_uReg) = ((a_uReg) & ~(a_fRwMask)) | ((uint32_t)(a_uValue) & (uint32_t)(a_fRwMask)); \
127 } while (0)
128
129
130/*********************************************************************************************************************************
131* Global Variables *
132*********************************************************************************************************************************/
133/** GITS diagnostics description for members in GITSDIAG. */
134static const char *const g_apszGitsDiagDesc[] =
135{
136 GITSDIAG_DESC(None),
137 GITSDIAG_DESC(CmdQueue_Basic_Unknown_Cmd),
138 GITSDIAG_DESC(CmdQueue_Basic_Invalid_PhysAddr),
139 GITSDIAG_DESC(CmdQueue_Cmd_Mapc_Icid_Overflow),
140 /* kGitsDiag_End */
141};
142AssertCompile(RT_ELEMENTS(g_apszGitsDiagDesc) == kGitsDiag_End);
143#undef GITSDIAG_DESC
144
145
146#ifndef VBOX_DEVICE_STRUCT_TESTCASE
147
148DECL_HIDDEN_CALLBACK(const char *) gitsGetCtrlRegDescription(uint16_t offReg)
149{
150 if (GITS_IS_REG_IN_RANGE(offReg, GITS_CTRL_REG_BASER_OFF_FIRST, GITS_CTRL_REG_BASER_RANGE_SIZE))
151 return "GITS_BASER<n>";
152 switch (offReg)
153 {
154 case GITS_CTRL_REG_CTLR_OFF: return "GITS_CTLR";
155 case GITS_CTRL_REG_IIDR_OFF: return "GITS_IIDR";
156 case GITS_CTRL_REG_TYPER_OFF: return "GITS_TYPER";
157 case GITS_CTRL_REG_MPAMIDR_OFF: return "GITS_MPAMIDR";
158 case GITS_CTRL_REG_PARTIDR_OFF: return "GITS_PARTIDR";
159 case GITS_CTRL_REG_MPIDR_OFF: return "GITS_MPIDR";
160 case GITS_CTRL_REG_STATUSR_OFF: return "GITS_STATUSR";
161 case GITS_CTRL_REG_UMSIR_OFF: return "GITS_UMSIR";
162 case GITS_CTRL_REG_CBASER_OFF: return "GITS_CBASER";
163 case GITS_CTRL_REG_CWRITER_OFF: return "GITS_CWRITER";
164 case GITS_CTRL_REG_CREADR_OFF: return "GITS_CREADR";
165 default:
166 return "<UNKNOWN>";
167 }
168}
169
170
171DECL_HIDDEN_CALLBACK(const char *) gitsGetTranslationRegDescription(uint16_t offReg)
172{
173 switch (offReg)
174 {
175 case GITS_TRANSLATION_REG_TRANSLATER: return "GITS_TRANSLATERR";
176 default:
177 return "<UNKNOWN>";
178 }
179}
180
181
182static const char *gitsGetCommandName(uint8_t uCmdId)
183{
184 switch (uCmdId)
185 {
186 case GITS_CMD_ID_CLEAR: return "CLEAR";
187 case GITS_CMD_ID_DISCARD: return "DISCARD";
188 case GITS_CMD_ID_INT: return "INT";
189 case GITS_CMD_ID_INV: return "INV";
190 case GITS_CMD_ID_INVALL: return "INVALL";
191 case GITS_CMD_ID_INVDB: return "INVDB";
192 case GITS_CMD_ID_MAPC: return "MAPC";
193 case GITS_CMD_ID_MAPD: return "MAPD";
194 case GITS_CMD_ID_MAPI: return "MAPI";
195 case GITS_CMD_ID_MAPTI: return "MAPTI";
196 case GITS_CMD_ID_MOVALL: return "MOVALL";
197 case GITS_CMD_ID_MOVI: return "MOVI";
198 case GITS_CMD_ID_SYNC: return "SYNC";
199 case GITS_CMD_ID_VINVALL: return "VINVALL";
200 case GITS_CMD_ID_VMAPI: return "VMAPI";
201 case GITS_CMD_ID_VMAPP: return "VMAPP";
202 case GITS_CMD_ID_VMAPTI: return "VMAPTI";
203 case GITS_CMD_ID_VMOVI: return "VMOVI";
204 case GITS_CMD_ID_VMOVP: return "VMOVP";
205 case GITS_CMD_ID_VSGI: return "VSGI";
206 case GITS_CMD_ID_VSYNC: return "VSYNC";
207 default:
208 return "<UNKNOWN>";
209 }
210}
211
212
213DECL_FORCE_INLINE(const char *) gitsGetDiagDescription(GITSDIAG enmDiag)
214{
215 if (enmDiag < RT_ELEMENTS(g_apszGitsDiagDesc))
216 return g_apszGitsDiagDesc[enmDiag];
217 return "<Unknown>";
218}
219
220
221static RTGCPHYS gitsGetBaseRegPhysAddr(uint64_t uGitsBaseReg)
222{
223 /* Mask for physical address bits [47:12]. */
224 static uint64_t const s_auPhysAddrLoMasks[] =
225 {
226 UINT64_C(0x0000fffffffff000), /* 4K bits[47:12] */
227 UINT64_C(0x0000ffffffffe000), /* 16K bits[47:13] */
228 UINT64_C(0x0000ffffffff0000), /* 64K bits[47:16] */
229 UINT64_C(0x0000ffffffff0000) /* 64K bits[47:16] */
230 };
231
232 /* Mask for physical address bits [51:48]. */
233 static uint64_t const s_auPhysAddrHiMasks[] =
234 {
235 UINT64_C(0x0), /* 4K bits[51:48] = 0 */
236 UINT64_C(0x0), /* 16K bits[51:48] = 0 */
237 UINT64_C(0x000000000000f000), /* 64K bits[51:48] = bits[15:12] */
238 UINT64_C(0x000000000000f000) /* 64K bits[51:48] = bits[15:12] */
239 };
240 AssertCompile(RT_ELEMENTS(s_auPhysAddrLoMasks) == RT_ELEMENTS(s_auPhysAddrHiMasks));
241
242 uint8_t const idxPageSize = RT_BF_GET(uGitsBaseReg, GITS_BF_CTRL_REG_BASER_PAGESIZE);
243 Assert(idxPageSize < RT_ELEMENTS(s_auPhysAddrLoMasks));
244 RTGCPHYS const GCPhys = (uGitsBaseReg & s_auPhysAddrLoMasks[idxPageSize])
245 | ((uGitsBaseReg & s_auPhysAddrHiMasks[idxPageSize]) << (48 - 12));
246 return GCPhys;
247}
248
249
250static void gitsCmdQueueSetError(PPDMDEVINS pDevIns, PGITSDEV pGitsDev, GITSDIAG enmDiag, bool fStallQueue)
251{
252 Log4Func(("enmDiag=%#RX32 (%s) fStallQueue=%RTbool\n", enmDiag, gitsGetDiagDescription(enmDiag)));
253
254 GITS_CRIT_SECT_ENTER(pDevIns);
255
256 /* Record the error and stall the queue. */
257 pGitsDev->enmDiag = enmDiag;
258 pGitsDev->cCmdQueueErrors++;
259 if (fStallQueue)
260 pGitsDev->uCmdReadReg |= GITS_BF_CTRL_REG_CREADR_STALLED_MASK;
261
262 GITS_CRIT_SECT_LEAVE(pDevIns);
263
264 /* Since we don't support SEIs, so there should be nothing more to do here. */
265 Assert(!RT_BF_GET(pGitsDev->uTypeReg.u, GITS_BF_CTRL_REG_TYPER_SEIS));
266}
267
268
269DECL_FORCE_INLINE(bool) gitsCmdQueueIsEmptyEx(PCGITSDEV pGitsDev, uint32_t *poffRead, uint32_t *poffWrite)
270{
271 *poffRead = pGitsDev->uCmdReadReg & GITS_BF_CTRL_REG_CREADR_OFFSET_MASK;
272 *poffWrite = pGitsDev->uCmdWriteReg & GITS_BF_CTRL_REG_CWRITER_OFFSET_MASK;
273 return *poffRead == *poffWrite;
274}
275
276
277DECL_FORCE_INLINE(bool) gitsCmdQueueIsEmpty(PCGITSDEV pGitsDev)
278{
279 uint32_t offRead;
280 uint32_t offWrite;
281 return gitsCmdQueueIsEmptyEx(pGitsDev, &offRead, &offWrite);
282}
283
284
285DECL_FORCE_INLINE(bool) gitsCmdQueueCanProcessRequests(PCGITSDEV pGitsDev)
286{
287 if ( (pGitsDev->uTypeReg.u & GITS_BF_CTRL_REG_CTLR_ENABLED_MASK)
288 && (pGitsDev->uCmdBaseReg.u & GITS_BF_CTRL_REG_CBASER_VALID_MASK)
289 && !(pGitsDev->uCmdReadReg & GITS_BF_CTRL_REG_CREADR_STALLED_MASK))
290 return true;
291 return false;
292}
293
294
295static void gitsCmdQueueThreadWakeUpIfNeeded(PPDMDEVINS pDevIns, PGITSDEV pGitsDev)
296{
297 Log4Func(("\n"));
298 Assert(GITS_CRIT_SECT_IS_OWNER(pDevIns));
299 if ( gitsCmdQueueCanProcessRequests(pGitsDev)
300 && !gitsCmdQueueIsEmpty(pGitsDev))
301 {
302 Log4Func(("Waking up command-queue thread\n"));
303 int const rc = PDMDevHlpSUPSemEventSignal(pDevIns, pGitsDev->hEvtCmdQueue);
304 AssertRC(rc);
305 }
306}
307
308
309DECL_HIDDEN_CALLBACK(uint64_t) gitsMmioReadCtrl(PCGITSDEV pGitsDev, uint16_t offReg, unsigned cb)
310{
311 Assert(cb == 4 || cb == 8);
312 Assert(!(offReg & 3));
313 RT_NOREF(cb);
314
315 /*
316 * GITS_BASER<n>.
317 */
318 uint64_t uReg;
319 if (GITS_IS_REG_IN_RANGE(offReg, GITS_CTRL_REG_BASER_OFF_FIRST, GITS_CTRL_REG_BASER_RANGE_SIZE))
320 {
321 uint16_t const cbReg = sizeof(uint64_t);
322 uint16_t const idxReg = (offReg - GITS_CTRL_REG_BASER_OFF_FIRST) / cbReg;
323 uReg = pGitsDev->aItsTableRegs[idxReg].u >> ((offReg & 7) << 3 /* to bits */);
324 return uReg;
325 }
326
327 switch (offReg)
328 {
329 case GITS_CTRL_REG_CTLR_OFF:
330 Assert(cb == 4);
331 uReg = pGitsDev->uCtrlReg;
332 break;
333
334 case GITS_CTRL_REG_PIDR2_OFF:
335 Assert(cb == 4);
336 Assert(pGitsDev->uArchRev <= GITS_CTRL_REG_PIDR2_ARCHREV_GICV4);
337 uReg = RT_BF_MAKE(GITS_BF_CTRL_REG_PIDR2_DES_1, GIC_JEDEC_JEP10_DES_1(GIC_JEDEC_JEP106_IDENTIFICATION_CODE))
338 | RT_BF_MAKE(GITS_BF_CTRL_REG_PIDR2_JEDEC, 1)
339 | RT_BF_MAKE(GITS_BF_CTRL_REG_PIDR2_ARCHREV, pGitsDev->uArchRev);
340 break;
341
342 case GITS_CTRL_REG_IIDR_OFF:
343 Assert(cb == 4);
344 uReg = RT_BF_MAKE(GITS_BF_CTRL_REG_IIDR_IMPL_ID_CODE, GIC_JEDEC_JEP106_IDENTIFICATION_CODE)
345 | RT_BF_MAKE(GITS_BF_CTRL_REG_IIDR_IMPL_CONT_CODE, GIC_JEDEC_JEP106_CONTINUATION_CODE);
346 break;
347
348 case GITS_CTRL_REG_TYPER_OFF:
349 case GITS_CTRL_REG_TYPER_OFF + 4:
350 uReg = pGitsDev->uTypeReg.u >> ((offReg & 7) << 3 /* to bits */);
351 break;
352
353 case GITS_CTRL_REG_CBASER_OFF:
354 uReg = pGitsDev->uCmdBaseReg.u;
355 break;
356
357 case GITS_CTRL_REG_CBASER_OFF + 4:
358 Assert(cb == 4);
359 uReg = pGitsDev->uCmdBaseReg.s.Hi;
360 break;
361
362 case GITS_CTRL_REG_CREADR_OFF:
363 uReg = pGitsDev->uCmdReadReg;
364 break;
365
366 case GITS_CTRL_REG_CREADR_OFF + 4:
367 uReg = 0; /* Upper 32-bits are reserved, MBZ. */
368 break;
369
370 default:
371 AssertReleaseMsgFailed(("offReg=%#x (%s)\n", offReg, gitsGetCtrlRegDescription(offReg)));
372 uReg = 0;
373 break;
374 }
375
376 Log4Func(("offReg=%#RX16 (%s) uReg=%#RX64 [%u-bit]\n", offReg, gitsGetCtrlRegDescription(offReg), uReg, cb << 3));
377 return uReg;
378}
379
380
381DECL_HIDDEN_CALLBACK(uint64_t) gitsMmioReadTranslate(PCGITSDEV pGitsDev, uint16_t offReg, unsigned cb)
382{
383 Assert(cb == 8 || cb == 4);
384 Assert(!(offReg & 3));
385 RT_NOREF(pGitsDev, cb);
386
387 uint64_t uReg = 0;
388 AssertReleaseMsgFailed(("offReg=%#x (%s) uReg=%#RX64 [%u-bit]\n", offReg, gitsGetTranslationRegDescription(offReg), uReg, cb << 3));
389 return uReg;
390}
391
392
393DECL_HIDDEN_CALLBACK(void) gitsMmioWriteCtrl(PPDMDEVINS pDevIns, PGITSDEV pGitsDev, uint16_t offReg, uint64_t uValue, unsigned cb)
394{
395 Assert(cb == 8 || cb == 4);
396 Assert(!(offReg & 3));
397 Log4Func(("offReg=%u uValue=%#RX64 cb=%u\n", offReg, uValue, cb));
398
399 /*
400 * GITS_BASER<n>.
401 */
402 if (GITS_IS_REG_IN_RANGE(offReg, GITS_CTRL_REG_BASER_OFF_FIRST, GITS_CTRL_REG_BASER_RANGE_SIZE))
403 {
404 uint16_t const cbReg = sizeof(uint64_t);
405 uint16_t const idxReg = (offReg - GITS_CTRL_REG_BASER_OFF_FIRST) / cbReg;
406 uint64_t const fRwMask = GITS_CTRL_REG_BASER_RW_MASK;
407 if (!(offReg & 7))
408 {
409 if (cb == 8)
410 GITS_SET_REG_U64_FULL(pGitsDev->aItsTableRegs[idxReg].u, uValue, fRwMask);
411 else
412 GITS_SET_REG_U64_LO(pGitsDev->aItsTableRegs[idxReg].s.Lo, uValue, fRwMask);
413 }
414 else
415 {
416 Assert(cb == 4);
417 GITS_SET_REG_U64_HI(pGitsDev->aItsTableRegs[idxReg].s.Hi, uValue, fRwMask);
418 }
419 return;
420 }
421
422 switch (offReg)
423 {
424 case GITS_CTRL_REG_CTLR_OFF:
425 Assert(cb == 4);
426 Assert(!(pGitsDev->uTypeReg.u & GITS_BF_CTRL_REG_TYPER_UMSI_IRQ_MASK));
427 GITS_SET_REG_U32(pGitsDev->uCtrlReg, uValue, GITS_BF_CTRL_REG_CTLR_RW_MASK);
428 if (RT_BF_GET(uValue, GITS_BF_CTRL_REG_CTLR_ENABLED))
429 pGitsDev->uCtrlReg &= GITS_BF_CTRL_REG_CTLR_QUIESCENT_MASK;
430 gitsCmdQueueThreadWakeUpIfNeeded(pDevIns, pGitsDev);
431 break;
432
433 case GITS_CTRL_REG_CBASER_OFF:
434 if (cb == 8)
435 GITS_SET_REG_U64_FULL(pGitsDev->uCmdBaseReg.u, uValue, GITS_CTRL_REG_CBASER_RW_MASK);
436 else
437 GITS_SET_REG_U64_LO(pGitsDev->uCmdBaseReg.s.Lo, uValue, GITS_CTRL_REG_CBASER_RW_MASK);
438 gitsCmdQueueThreadWakeUpIfNeeded(pDevIns, pGitsDev);
439 break;
440
441 case GITS_CTRL_REG_CBASER_OFF + 4:
442 Assert(cb == 4);
443 GITS_SET_REG_U64_HI(pGitsDev->uCmdBaseReg.s.Hi, uValue, GITS_CTRL_REG_CBASER_RW_MASK);
444 gitsCmdQueueThreadWakeUpIfNeeded(pDevIns, pGitsDev);
445 break;
446
447 case GITS_CTRL_REG_CWRITER_OFF:
448 GITS_SET_REG_U32(pGitsDev->uCmdWriteReg, uValue, GITS_CTRL_REG_CWRITER_RW_MASK);
449 gitsCmdQueueThreadWakeUpIfNeeded(pDevIns, pGitsDev);
450 break;
451
452 case GITS_CTRL_REG_CWRITER_OFF + 4:
453 /* Upper 32-bits are all reserved, ignore write. Fedora 40 arm64 guests (and probably others) do this. */
454 Assert(uValue == 0);
455 gitsCmdQueueThreadWakeUpIfNeeded(pDevIns, pGitsDev);
456 break;
457
458 default:
459 AssertReleaseMsgFailed(("offReg=%#x (%s) uValue=%#RX32\n", offReg, gitsGetCtrlRegDescription(offReg), uValue));
460 break;
461 }
462
463 Log4Func(("offReg=%#RX16 (%s) uValue=%#RX32 [%u-bit]\n", offReg, gitsGetCtrlRegDescription(offReg), uValue, cb << 3));
464}
465
466
467DECL_HIDDEN_CALLBACK(void) gitsMmioWriteTranslate(PGITSDEV pGitsDev, uint16_t offReg, uint64_t uValue, unsigned cb)
468{
469 RT_NOREF(pGitsDev);
470 Assert(cb == 8 || cb == 4);
471 Assert(!(offReg & 3));
472 Log4Func(("offReg=%u uValue=%#RX64 cb=%u\n", offReg, uValue, cb));
473 AssertReleaseMsgFailed(("offReg=%#x uValue=%#RX64 [%u-bit]\n", offReg, uValue, cb << 3));
474}
475
476
477DECL_HIDDEN_CALLBACK(void) gitsInit(PGITSDEV pGitsDev)
478{
479 Log4Func(("\n"));
480
481 /* GITS_CTLR.*/
482 pGitsDev->uCtrlReg = RT_BF_MAKE(GITS_BF_CTRL_REG_CTLR_QUIESCENT, 1);
483
484 /* GITS_TYPER. */
485 pGitsDev->uTypeReg.u = RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_PHYSICAL, 1) /* Physical LPIs supported. */
486 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_VIRTUAL, 0) */ /* Virtual LPIs not supported. */
487 | RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_CCT, 0) /* Collections in memory not supported. */
488 | RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_ITT_ENTRY_SIZE, GITS_ITE_SIZE - 1) /* ITE size in bytes minus 1. */
489 | RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_ID_BITS, 31) /* 32-bit event IDs. */
490 | RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_DEV_BITS, 31) /* 32-bit device IDs. */
491 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_SEIS, 0) */ /* Locally generated errors not recommended. */
492 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_PTA, 0) */ /* Target is VCPU ID not address. */
493 | RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_HCC, 255) /* Collection count. */
494 | RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_CID_BITS, 7) /* Number of collection ID bits minus 1. */
495 | RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_CIL, 1) /* Collection ID size set by GITS_BASER.CIDbits. */
496 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_VMOVP, 0) */ /* VMOVP not supported. */
497 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_MPAM, 0) */ /* MPAM no supported. */
498 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_VSGI, 0) */ /* VSGI not supported. */
499 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_VMAPP, 0) */ /* VMAPP not supported. */
500 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_SVPET, 0) */ /* SVPET not supported. */
501 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_NID, 0) */ /* NID (doorbell) not supported. */
502 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_UMSI, 0) */ /** @todo Reporting receipt of unmapped MSIs. */
503 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_UMSI_IRQ, 0) */ /** @todo Generating interrupt on unmapped MSI. */
504 | RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_INV, 1); /* ITS caches invalidated when clearing
505 GITS_CTLR.Enabled and GITS_BASER<n>.Valid. */
506 Assert(RT_ELEMENTS(pGitsDev->auCtes) >= RT_BF_GET(pGitsDev->uTypeReg.u, GITS_BF_CTRL_REG_TYPER_HCC));
507
508 /* GITS_BASER<n>. */
509 RT_ZERO(pGitsDev->aItsTableRegs);
510 pGitsDev->aItsTableRegs[0].u = RT_BF_MAKE(GITS_BF_CTRL_REG_BASER_ENTRY_SIZE, GITS_ITE_SIZE - 1)
511 | RT_BF_MAKE(GITS_BF_CTRL_REG_BASER_TYPE, GITS_BASER_TYPE_DEVICES);
512
513 /* GITS_CBASER, GITS_CREADR, GITS_CWRITER. */
514 pGitsDev->uCmdBaseReg.u = 0;
515 pGitsDev->uCmdReadReg = 0;
516 pGitsDev->uCmdWriteReg = 0;
517
518 /* Collection Table. */
519 RT_ZERO(pGitsDev->auCtes);
520
521 /* Misc. stuff. */
522 pGitsDev->cCmdQueueErrors = 0;
523}
524
525
526#ifdef IN_RING3
527DECL_HIDDEN_CALLBACK(void) gitsR3DbgInfo(PCGITSDEV pGitsDev, PCDBGFINFOHLP pHlp)
528{
529 pHlp->pfnPrintf(pHlp, "GIC ITS:\n");
530
531 /* Basic info, GITS_CTLR and GITS_TYPER. */
532 {
533 uint32_t const uCtrlReg = pGitsDev->uCtrlReg;
534 GITSDIAG const enmDiag = pGitsDev->enmDiag;
535 pHlp->pfnPrintf(pHlp, " uArchRev = %u\n", pGitsDev->uArchRev);
536 pHlp->pfnPrintf(pHlp, " Cmd queue errors = %RU64\n", pGitsDev->cCmdQueueErrors);
537 pHlp->pfnPrintf(pHlp, " Last error = %#RX32 (%s)\n", enmDiag, gitsGetDiagDescription(enmDiag));
538 pHlp->pfnPrintf(pHlp, " GITS_CTLR = %#RX32\n", uCtrlReg);
539 pHlp->pfnPrintf(pHlp, " Enabled = %RTbool\n", RT_BF_GET(uCtrlReg, GITS_BF_CTRL_REG_CTLR_ENABLED));
540 pHlp->pfnPrintf(pHlp, " UMSI IRQ = %RTbool\n", RT_BF_GET(uCtrlReg, GITS_BF_CTRL_REG_CTLR_UMSI_IRQ));
541 pHlp->pfnPrintf(pHlp, " Quiescent = %RTbool\n", RT_BF_GET(uCtrlReg, GITS_BF_CTRL_REG_CTLR_QUIESCENT));
542 }
543
544 /* GITS_BASER<n>. */
545 for (unsigned i = 0; i < RT_ELEMENTS(pGitsDev->aItsTableRegs); i++)
546 {
547 static uint32_t const s_acbPageSize[] = { _4K, _16K, _64K, _64K };
548 static const char* const s_apszType[] = { "UnImpl", "Devices", "vPEs", "Intr Collections" };
549
550 uint64_t const uReg = pGitsDev->aItsTableRegs[i].u;
551 uint16_t const uSize = RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_SIZE);
552 uint16_t const cPages = uSize > 0 ? uSize + 1 : 0;
553 uint8_t const idxPageSize = RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_PAGESIZE);
554 uint64_t const cbItsTable = cPages * s_acbPageSize[idxPageSize];
555 uint8_t const uEntrySize = RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_ENTRY_SIZE);
556 uint8_t const idxType = RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_TYPE);
557 const char *pszType = s_apszType[idxType];
558 pHlp->pfnPrintf(pHlp, " GITS_BASER[%u] = %#RX64\n", i, uReg);
559 pHlp->pfnPrintf(pHlp, " Size = %#x (pages=%u total=%.Rhcb)\n", uSize, cPages, cbItsTable);
560 pHlp->pfnPrintf(pHlp, " Page size = %#x (%.Rhcb)\n", idxPageSize, s_acbPageSize[idxPageSize]);
561 pHlp->pfnPrintf(pHlp, " Shareability = %#x\n", RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_SHAREABILITY));
562 pHlp->pfnPrintf(pHlp, " Phys addr = %#RX64 (addr=%#RX64)\n", uReg & GITS_BF_CTRL_REG_BASER_PHYS_ADDR_MASK,
563 gitsGetBaseRegPhysAddr(uReg));
564 pHlp->pfnPrintf(pHlp, " Entry size = %#x (%u bytes)\n", uEntrySize, uEntrySize > 0 ? uEntrySize + 1 : 0);
565 pHlp->pfnPrintf(pHlp, " Outer cache = %#x\n", RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_OUTER_CACHE));
566 pHlp->pfnPrintf(pHlp, " Type = %#x (%s)\n", idxType, pszType);
567 pHlp->pfnPrintf(pHlp, " Inner cache = %#x\n", RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_INNER_CACHE));
568 pHlp->pfnPrintf(pHlp, " Indirect = %RTbool\n", RT_BOOL(RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_INDIRECT)));
569 pHlp->pfnPrintf(pHlp, " Valid = %RTbool\n", RT_BOOL(RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_VALID)));
570 }
571
572 /* GITS_CBASER. */
573 {
574 uint64_t const uReg = pGitsDev->uCmdBaseReg.u;
575 uint8_t const uSize = RT_BF_GET(uReg, GITS_BF_CTRL_REG_CBASER_SIZE);
576 uint16_t const cPages = uSize > 0 ? uSize + 1 : 0;
577 pHlp->pfnPrintf(pHlp, " GITS_CBASER = %#RX64\n", uReg);
578 pHlp->pfnPrintf(pHlp, " Size = %#x (pages=%u total=%.Rhcb)\n", uSize, cPages, _4K * cPages);
579 pHlp->pfnPrintf(pHlp, " Shareability = %#x\n", RT_BF_GET(uReg, GITS_BF_CTRL_REG_CBASER_SHAREABILITY));
580 pHlp->pfnPrintf(pHlp, " Phys addr = %#RX64\n", uReg & GITS_BF_CTRL_REG_CBASER_PHYS_ADDR_MASK);
581 pHlp->pfnPrintf(pHlp, " Outer cache = %#x\n", RT_BF_GET(uReg, GITS_BF_CTRL_REG_CBASER_OUTER_CACHE));
582 pHlp->pfnPrintf(pHlp, " Inner cache = %#x\n", RT_BF_GET(uReg, GITS_BF_CTRL_REG_CBASER_INNER_CACHE));
583 pHlp->pfnPrintf(pHlp, " Valid = %RTbool\n", RT_BF_GET(uReg, GITS_BF_CTRL_REG_CBASER_VALID));
584 }
585
586 /* GITS_CREADR. */
587 {
588 uint32_t const uReg = pGitsDev->uCmdReadReg;
589 pHlp->pfnPrintf(pHlp, " GITS_CREADR = 0x%05RX32 (stalled=%RTbool offset=%RU32)\n", uReg,
590 RT_BF_GET(uReg, GITS_BF_CTRL_REG_CREADR_STALLED), uReg & GITS_BF_CTRL_REG_CREADR_OFFSET_MASK);
591 }
592
593 /* GITS_CWRITER. */
594 {
595 uint32_t const uReg = pGitsDev->uCmdWriteReg;
596 pHlp->pfnPrintf(pHlp, " GITS_CWRITER = 0x%05RX32 ( retry=%RTbool offset=%RU32)\n", uReg,
597 RT_BF_GET(uReg, GITS_BF_CTRL_REG_CWRITER_RETRY), uReg & GITS_BF_CTRL_REG_CWRITER_OFFSET_MASK);
598 }
599
600 /* Interrupt Collection Table. */
601 {
602 pHlp->pfnPrintf(pHlp, " Collection Table:\n");
603 bool fHasValidCtes = false;
604 for (unsigned i = 0; i < RT_ELEMENTS(pGitsDev->auCtes); i++)
605 {
606 bool const fValid = RT_BF_GET(pGitsDev->auCtes[i], GITS_BF_CTE_VALID);
607 if (fValid)
608 {
609 uint32_t const uCte = pGitsDev->auCtes[i];
610 uint16_t const uTargetCpuId = RT_BF_GET(pGitsDev->auCtes[i], GITS_BF_CTE_RDBASE);
611 pHlp->pfnPrintf(pHlp, " [%3u] = %#RX32 (target cpu=%#RX16)\n", i, uCte, uTargetCpuId);
612 fHasValidCtes = true;
613 }
614 }
615 if (!fHasValidCtes)
616 pHlp->pfnPrintf(pHlp, " Empty (no valid entries)\n");
617 }
618}
619
620
621DECL_HIDDEN_CALLBACK(int) gitsR3CmdQueueProcess(PPDMDEVINS pDevIns, PGITSDEV pGitsDev, void *pvBuf, uint32_t cbBuf)
622{
623 Log4Func(("cbBuf=%RU32\n", cbBuf));
624
625 /* Hold the critical section as we could be accessing the device state simultaneously with MMIO accesses. */
626 GITS_CRIT_SECT_ENTER(pDevIns);
627
628 if (gitsCmdQueueCanProcessRequests(pGitsDev))
629 {
630 uint32_t offRead;
631 uint32_t offWrite;
632 bool const fIsEmpty = gitsCmdQueueIsEmptyEx(pGitsDev, &offRead, &offWrite);
633 if (!fIsEmpty)
634 {
635 uint32_t const cCmdQueuePages = RT_BF_GET(pGitsDev->uCmdBaseReg.u, GITS_BF_CTRL_REG_CBASER_SIZE) + 1;
636 uint32_t const cbCmdQueue = cCmdQueuePages << GITS_CMD_QUEUE_PAGE_SHIFT;
637 AssertRelease(cbCmdQueue <= cbBuf); /** @todo Paranoia; make this a debug assert later. */
638
639 /*
640 * Read all the commands from guest memory into our command queue buffer.
641 */
642 int rc;
643 uint32_t cbCmds;
644 RTGCPHYS const GCPhysCmds = pGitsDev->uCmdBaseReg.u & GITS_BF_CTRL_REG_CBASER_PHYS_ADDR_MASK;
645
646 /* Leave the critical section while reading (a potentially large number of) commands from guest memory. */
647 GITS_CRIT_SECT_LEAVE(pDevIns);
648
649 if (offWrite > offRead)
650 {
651 /* The write offset has not wrapped around, read them in one go. */
652 cbCmds = offWrite - offRead;
653 Assert(cbCmds <= cbBuf);
654 rc = PDMDevHlpPhysReadMeta(pDevIns, GCPhysCmds + offRead, pvBuf, cbCmds);
655 }
656 else
657 {
658 /* The write offset has wrapped around, read till end of buffer followed by wrapped-around data. */
659 uint32_t const cbForward = cbCmdQueue - offRead;
660 uint32_t const cbWrapped = offWrite;
661 Assert(cbForward + cbWrapped <= cbBuf);
662 rc = PDMDevHlpPhysReadMeta(pDevIns, GCPhysCmds + offRead, pvBuf, cbForward);
663 if ( RT_SUCCESS(rc)
664 && cbWrapped > 0)
665 rc = PDMDevHlpPhysReadMeta(pDevIns, GCPhysCmds, (void *)((uintptr_t)pvBuf + cbForward), cbWrapped);
666 cbCmds = cbForward + cbWrapped;
667 }
668
669 /*
670 * Process the commands in the buffer.
671 */
672 if (RT_SUCCESS(rc))
673 {
674 /* Indicate to the guest we've fetched all commands. */
675 GITS_CRIT_SECT_ENTER(pDevIns);
676 pGitsDev->uCmdReadReg = offWrite;
677 pGitsDev->uCmdWriteReg &= ~GITS_BF_CTRL_REG_CWRITER_RETRY_MASK;
678
679 /* Don't hold the critical section while processing commands. */
680 GITS_CRIT_SECT_LEAVE(pDevIns);
681
682 uint32_t const cCmds = cbCmds / sizeof(GITSCMD);
683 for (uint32_t idxCmd = 0; idxCmd < cCmds; idxCmd++)
684 {
685 PCGITSCMD pCmd = (PCGITSCMD)((uintptr_t)pvBuf + (idxCmd * sizeof(GITSCMD)));
686 uint8_t const uCmdId = pCmd->common.uCmdId;
687 switch (uCmdId)
688 {
689 case GITS_CMD_ID_MAPC:
690 {
691 uint64_t const uDw2 = pCmd->au64[2].u;
692 uint8_t const fValid = RT_BF_GET(uDw2, GITS_BF_CMD_MAPC_DW2_VALID);
693 uint16_t const uTargetCpuId = RT_BF_GET(uDw2, GITS_BF_CMD_MAPC_DW2_RDBASE);
694 uint16_t const uIntrCollectionId = RT_BF_GET(uDw2, GITS_BF_CMD_MAPC_DW2_IC_ID);
695
696 if (RT_LIKELY(uIntrCollectionId < RT_ELEMENTS(pGitsDev->auCtes)))
697 {
698 GITS_CRIT_SECT_ENTER(pDevIns);
699 Assert(!RT_BF_GET(pGitsDev->uTypeReg.u, GITS_BF_CTRL_REG_TYPER_PTA));
700 pGitsDev->auCtes[uIntrCollectionId] = RT_BF_MAKE(GITS_BF_CTE_VALID, fValid)
701 | RT_BF_MAKE(GITS_BF_CTE_RDBASE, uTargetCpuId);
702 GITS_CRIT_SECT_LEAVE(pDevIns);
703 }
704 else
705 gitsCmdQueueSetError(pDevIns, pGitsDev, kGitsDiag_CmdQueue_Cmd_Mapc_Icid_Overflow,
706 false /* fStall */);
707 STAM_COUNTER_INC(&pGitsDev->StatCmdMapc);
708 break;
709 }
710
711 case GITS_CMD_ID_SYNC:
712 /* Nothing to do since all previous commands have committed their changes to device state. */
713 STAM_COUNTER_INC(&pGitsDev->StatCmdInvall);
714 break;
715
716 case GITS_CMD_ID_INVALL:
717 /* Nothing to do as we currently do not cache interrupt mappings. */
718 STAM_COUNTER_INC(&pGitsDev->StatCmdSync);
719 break;
720
721 default:
722 {
723 /* Record an internal error but do NOT stall queue as we have already advanced the read offset. */
724 gitsCmdQueueSetError(pDevIns, pGitsDev, kGitsDiag_CmdQueue_Basic_Unknown_Cmd, false /* fStall */);
725 AssertReleaseMsgFailed(("Cmd=%#x (%s) idxCmd=%u cCmds=%u offRead=%#RX32 offWrite=%#RX32\n",
726 uCmdId, gitsGetCommandName(uCmdId), idxCmd, cCmds, offRead, offWrite));
727 break;
728 }
729 }
730 }
731 return VINF_SUCCESS;
732 }
733
734 /* Failed to read command queue from the physical address specified by the guest, stall queue and retry later. */
735 gitsCmdQueueSetError(pDevIns, pGitsDev, kGitsDiag_CmdQueue_Basic_Invalid_PhysAddr, true /* fStall */);
736 return VINF_TRY_AGAIN;
737 }
738 }
739
740 GITS_CRIT_SECT_LEAVE(pDevIns);
741 return VINF_SUCCESS;
742}
743#endif /* IN_RING3 */
744
745
746/**
747 * @interface_method_impl{PDMGICBACKEND,pfnSendMsi}
748 */
749DECL_HIDDEN_CALLBACK(int) gitsSendMsi(PVMCC pVM, PCIBDF uBusDevFn, PCMSIMSG pMsi, uint32_t uEventId, uint32_t uTagSrc)
750{
751 Log4Func(("uBusDevFn=%#RX32 uEventId=%#RX32\n", uBusDevFn, uEventId));
752 RT_NOREF(pVM, uBusDevFn, pMsi, uEventId, uTagSrc);
753 return VERR_NOT_IMPLEMENTED;
754}
755
756#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
757
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