VirtualBox

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

Last change on this file was 109033, checked in by vboxsync, 13 days ago

VMM/GIC: bugref:10877 Some GIC, ITS cleanup.

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