VirtualBox

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

Last change on this file since 109128 was 109113, checked in by vboxsync, 12 days ago

VMM/GIC: bugref:10877 Assert and some fixes to the GITS_CTLR quiescent bit.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.3 KB
Line 
1/* $Id: GITSAll.cpp 109113 2025-04-30 07:18:23Z 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 /** @todo Clear ITS caches when GITS_BASER<n>.Valid = 0. */
347 return;
348 }
349
350 switch (offReg)
351 {
352 case GITS_CTRL_REG_CTLR_OFF:
353 Assert(cb == 4);
354 Assert(!(pGitsDev->uTypeReg.u & GITS_BF_CTRL_REG_TYPER_UMSI_IRQ_MASK));
355 GIC_SET_REG_U32(pGitsDev->uCtrlReg, uValue, GITS_BF_CTRL_REG_CTLR_RW_MASK);
356 if (RT_BF_GET(uValue, GITS_BF_CTRL_REG_CTLR_ENABLED))
357 pGitsDev->uCtrlReg &= ~GITS_BF_CTRL_REG_CTLR_QUIESCENT_MASK;
358 else
359 {
360 pGitsDev->uCtrlReg |= GITS_BF_CTRL_REG_CTLR_QUIESCENT_MASK;
361 /** @todo Clear ITS caches. */
362 }
363 gitsCmdQueueThreadWakeUpIfNeeded(pDevIns, pGitsDev);
364 break;
365
366 case GITS_CTRL_REG_CBASER_OFF:
367 if (cb == 8)
368 GIC_SET_REG_U64_FULL(pGitsDev->uCmdBaseReg.u, uValue, GITS_CTRL_REG_CBASER_RW_MASK);
369 else
370 GIC_SET_REG_U64_LO(pGitsDev->uCmdBaseReg.s.Lo, uValue, GITS_CTRL_REG_CBASER_RW_MASK);
371 gitsCmdQueueThreadWakeUpIfNeeded(pDevIns, pGitsDev);
372 break;
373
374 case GITS_CTRL_REG_CBASER_OFF + 4:
375 Assert(cb == 4);
376 GIC_SET_REG_U64_HI(pGitsDev->uCmdBaseReg.s.Hi, uValue, GITS_CTRL_REG_CBASER_RW_MASK);
377 gitsCmdQueueThreadWakeUpIfNeeded(pDevIns, pGitsDev);
378 break;
379
380 case GITS_CTRL_REG_CWRITER_OFF:
381 GIC_SET_REG_U32(pGitsDev->uCmdWriteReg, uValue, GITS_CTRL_REG_CWRITER_RW_MASK);
382 gitsCmdQueueThreadWakeUpIfNeeded(pDevIns, pGitsDev);
383 break;
384
385 case GITS_CTRL_REG_CWRITER_OFF + 4:
386 /* Upper 32-bits are all reserved, ignore write. Fedora 40 arm64 guests (and probably others) do this. */
387 Assert(uValue == 0);
388 gitsCmdQueueThreadWakeUpIfNeeded(pDevIns, pGitsDev);
389 break;
390
391 default:
392 AssertReleaseMsgFailed(("offReg=%#x (%s) uValue=%#RX32\n", offReg, gitsGetCtrlRegDescription(offReg), uValue));
393 break;
394 }
395
396 Log4Func(("offReg=%#RX16 (%s) uValue=%#RX32 [%u-bit]\n", offReg, gitsGetCtrlRegDescription(offReg), uValue, cb << 3));
397}
398
399
400DECL_HIDDEN_CALLBACK(void) gitsMmioWriteTranslate(PGITSDEV pGitsDev, uint16_t offReg, uint64_t uValue, unsigned cb)
401{
402 RT_NOREF(pGitsDev);
403 Assert(cb == 8 || cb == 4);
404 Assert(!(offReg & 3));
405 Log4Func(("offReg=%u uValue=%#RX64 cb=%u\n", offReg, uValue, cb));
406 AssertReleaseMsgFailed(("offReg=%#x uValue=%#RX64 [%u-bit]\n", offReg, uValue, cb << 3));
407}
408
409
410DECL_HIDDEN_CALLBACK(void) gitsInit(PGITSDEV pGitsDev)
411{
412 Log4Func(("\n"));
413
414 /* GITS_CTLR.*/
415 pGitsDev->uCtrlReg = RT_BF_MAKE(GITS_BF_CTRL_REG_CTLR_QUIESCENT, 1);
416
417 /* GITS_TYPER. */
418 pGitsDev->uTypeReg.u = RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_PHYSICAL, 1) /* Physical LPIs supported. */
419 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_VIRTUAL, 0) */ /* Virtual LPIs not supported. */
420 | RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_CCT, 0) /* Collections in memory not supported. */
421 | RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_ITT_ENTRY_SIZE, GITS_ITE_SIZE - 1) /* ITE size in bytes minus 1. */
422 | RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_ID_BITS, 31) /* 32-bit event IDs. */
423 | RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_DEV_BITS, 31) /* 32-bit device IDs. */
424 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_SEIS, 0) */ /* Locally generated errors not recommended. */
425 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_PTA, 0) */ /* Target is VCPU ID not address. */
426 | RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_HCC, 255) /* Collection count. */
427 | RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_CID_BITS, 0) /* Collections in memory not supported. */
428 | RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_CIL, 0) /* Collections in memory not supported. */
429 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_VMOVP, 0) */ /* VMOVP not supported. */
430 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_MPAM, 0) */ /* MPAM no supported. */
431 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_VSGI, 0) */ /* VSGI not supported. */
432 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_VMAPP, 0) */ /* VMAPP not supported. */
433 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_SVPET, 0) */ /* SVPET not supported. */
434 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_NID, 0) */ /* NID (doorbell) not supported. */
435 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_UMSI, 0) */ /** @todo Reporting receipt of unmapped MSIs. */
436 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_UMSI_IRQ, 0) */ /** @todo Generating interrupt on unmapped MSI. */
437 | RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_INV, 1); /* ITS caches invalidated when clearing
438 GITS_CTLR.Enabled and GITS_BASER<n>.Valid. */
439 Assert(RT_ELEMENTS(pGitsDev->auCtes) >= RT_BF_GET(pGitsDev->uTypeReg.u, GITS_BF_CTRL_REG_TYPER_HCC));
440
441 /* GITS_BASER<n>. */
442 RT_ZERO(pGitsDev->aItsTableRegs);
443 pGitsDev->aItsTableRegs[0].u = RT_BF_MAKE(GITS_BF_CTRL_REG_BASER_ENTRY_SIZE, GITS_ITE_SIZE - 1)
444 | RT_BF_MAKE(GITS_BF_CTRL_REG_BASER_TYPE, GITS_BASER_TYPE_DEVICES);
445
446 /* GITS_CBASER, GITS_CREADR, GITS_CWRITER. */
447 pGitsDev->uCmdBaseReg.u = 0;
448 pGitsDev->uCmdReadReg = 0;
449 pGitsDev->uCmdWriteReg = 0;
450
451 /* Collection Table. */
452 RT_ZERO(pGitsDev->auCtes);
453
454 /* Misc. stuff. */
455 pGitsDev->cCmdQueueErrors = 0;
456}
457
458
459#ifdef IN_RING3
460DECL_HIDDEN_CALLBACK(void) gitsR3DbgInfo(PCGITSDEV pGitsDev, PCDBGFINFOHLP pHlp)
461{
462 pHlp->pfnPrintf(pHlp, "GIC ITS:\n");
463
464 /* Basic info, GITS_CTLR and GITS_TYPER. */
465 {
466 uint32_t const uCtrlReg = pGitsDev->uCtrlReg;
467 GITSDIAG const enmDiag = pGitsDev->enmDiag;
468 pHlp->pfnPrintf(pHlp, " uArchRev = %u\n", pGitsDev->uArchRev);
469 pHlp->pfnPrintf(pHlp, " Cmd queue errors = %RU64\n", pGitsDev->cCmdQueueErrors);
470 pHlp->pfnPrintf(pHlp, " Last error = %#RX32 (%s)\n", enmDiag, gitsGetDiagDescription(enmDiag));
471 pHlp->pfnPrintf(pHlp, " GITS_CTLR = %#RX32\n", uCtrlReg);
472 pHlp->pfnPrintf(pHlp, " Enabled = %RTbool\n", RT_BF_GET(uCtrlReg, GITS_BF_CTRL_REG_CTLR_ENABLED));
473 pHlp->pfnPrintf(pHlp, " UMSI IRQ = %RTbool\n", RT_BF_GET(uCtrlReg, GITS_BF_CTRL_REG_CTLR_UMSI_IRQ));
474 pHlp->pfnPrintf(pHlp, " Quiescent = %RTbool\n", RT_BF_GET(uCtrlReg, GITS_BF_CTRL_REG_CTLR_QUIESCENT));
475 }
476
477 /* GITS_BASER<n>. */
478 for (unsigned i = 0; i < RT_ELEMENTS(pGitsDev->aItsTableRegs); i++)
479 {
480 static uint32_t const s_acbPageSize[] = { _4K, _16K, _64K, _64K };
481 static const char* const s_apszType[] = { "UnImpl", "Devices", "vPEs", "Intr Collections" };
482 uint64_t const uReg = pGitsDev->aItsTableRegs[i].u;
483 bool const fValid = RT_BOOL(RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_VALID));
484 uint8_t const idxType = RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_TYPE);
485 if ( fValid
486 || idxType != GITS_BASER_TYPE_UNIMPL)
487 {
488 uint16_t const uSize = RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_SIZE);
489 uint16_t const cPages = uSize > 0 ? uSize + 1 : 0;
490 uint8_t const idxPageSize = RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_PAGESIZE);
491 uint64_t const cbItsTable = cPages * s_acbPageSize[idxPageSize];
492 uint8_t const uEntrySize = RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_ENTRY_SIZE);
493 bool const fIndirect = RT_BOOL(RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_INDIRECT));
494 const char *pszType = s_apszType[idxType];
495 pHlp->pfnPrintf(pHlp, " GITS_BASER[%u] = %#RX64\n", i, uReg);
496 pHlp->pfnPrintf(pHlp, " Size = %#x (pages=%u total=%.Rhcb)\n", uSize, cPages, cbItsTable);
497 pHlp->pfnPrintf(pHlp, " Page size = %#x (%.Rhcb)\n", idxPageSize, s_acbPageSize[idxPageSize]);
498 pHlp->pfnPrintf(pHlp, " Shareability = %#x\n", RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_SHAREABILITY));
499 pHlp->pfnPrintf(pHlp, " Phys addr = %#RX64 (addr=%#RX64)\n", uReg & GITS_BF_CTRL_REG_BASER_PHYS_ADDR_MASK,
500 gitsGetBaseRegPhysAddr(uReg));
501 pHlp->pfnPrintf(pHlp, " Entry size = %#x (%u bytes)\n", uEntrySize, uEntrySize > 0 ? uEntrySize + 1 : 0);
502 pHlp->pfnPrintf(pHlp, " Outer cache = %#x\n", RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_OUTER_CACHE));
503 pHlp->pfnPrintf(pHlp, " Type = %#x (%s)\n", idxType, pszType);
504 pHlp->pfnPrintf(pHlp, " Inner cache = %#x\n", RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_INNER_CACHE));
505 pHlp->pfnPrintf(pHlp, " Indirect = %RTbool\n", fIndirect);
506 pHlp->pfnPrintf(pHlp, " Valid = %RTbool\n", fValid);
507 }
508 }
509
510 /* GITS_CBASER. */
511 {
512 uint64_t const uReg = pGitsDev->uCmdBaseReg.u;
513 uint8_t const uSize = RT_BF_GET(uReg, GITS_BF_CTRL_REG_CBASER_SIZE);
514 uint16_t const cPages = uSize > 0 ? uSize + 1 : 0;
515 pHlp->pfnPrintf(pHlp, " GITS_CBASER = %#RX64\n", uReg);
516 pHlp->pfnPrintf(pHlp, " Size = %#x (pages=%u total=%.Rhcb)\n", uSize, cPages, _4K * cPages);
517 pHlp->pfnPrintf(pHlp, " Shareability = %#x\n", RT_BF_GET(uReg, GITS_BF_CTRL_REG_CBASER_SHAREABILITY));
518 pHlp->pfnPrintf(pHlp, " Phys addr = %#RX64\n", uReg & GITS_BF_CTRL_REG_CBASER_PHYS_ADDR_MASK);
519 pHlp->pfnPrintf(pHlp, " Outer cache = %#x\n", RT_BF_GET(uReg, GITS_BF_CTRL_REG_CBASER_OUTER_CACHE));
520 pHlp->pfnPrintf(pHlp, " Inner cache = %#x\n", RT_BF_GET(uReg, GITS_BF_CTRL_REG_CBASER_INNER_CACHE));
521 pHlp->pfnPrintf(pHlp, " Valid = %RTbool\n", RT_BF_GET(uReg, GITS_BF_CTRL_REG_CBASER_VALID));
522 }
523
524 /* GITS_CREADR. */
525 {
526 uint32_t const uReg = pGitsDev->uCmdReadReg;
527 pHlp->pfnPrintf(pHlp, " GITS_CREADR = 0x%05RX32 (stalled=%RTbool offset=%RU32)\n", uReg,
528 RT_BF_GET(uReg, GITS_BF_CTRL_REG_CREADR_STALLED), uReg & GITS_BF_CTRL_REG_CREADR_OFFSET_MASK);
529 }
530
531 /* GITS_CWRITER. */
532 {
533 uint32_t const uReg = pGitsDev->uCmdWriteReg;
534 pHlp->pfnPrintf(pHlp, " GITS_CWRITER = 0x%05RX32 ( retry=%RTbool offset=%RU32)\n", uReg,
535 RT_BF_GET(uReg, GITS_BF_CTRL_REG_CWRITER_RETRY), uReg & GITS_BF_CTRL_REG_CWRITER_OFFSET_MASK);
536 }
537
538 /* Interrupt Collection Table. */
539 {
540 pHlp->pfnPrintf(pHlp, " Collection Table:\n");
541 bool fHasValidCtes = false;
542 for (unsigned i = 0; i < RT_ELEMENTS(pGitsDev->auCtes); i++)
543 {
544 bool const fValid = RT_BF_GET(pGitsDev->auCtes[i], GITS_BF_CTE_VALID);
545 if (fValid)
546 {
547 uint32_t const uCte = pGitsDev->auCtes[i];
548 uint16_t const uTargetCpuId = RT_BF_GET(pGitsDev->auCtes[i], GITS_BF_CTE_RDBASE);
549 pHlp->pfnPrintf(pHlp, " [%3u] = %#RX32 (target cpu=%#RX16)\n", i, uCte, uTargetCpuId);
550 fHasValidCtes = true;
551 }
552 }
553 if (!fHasValidCtes)
554 pHlp->pfnPrintf(pHlp, " Empty (no valid entries)\n");
555 }
556}
557
558
559DECL_HIDDEN_CALLBACK(int) gitsR3CmdQueueProcess(PPDMDEVINS pDevIns, PGITSDEV pGitsDev, void *pvBuf, uint32_t cbBuf)
560{
561 Log4Func(("cbBuf=%RU32\n", cbBuf));
562
563 /* Hold the critical section as we could be accessing the device state simultaneously with MMIO accesses. */
564 GIC_CRIT_SECT_ENTER(pDevIns);
565
566 if (gitsCmdQueueCanProcessRequests(pGitsDev))
567 {
568 uint32_t offRead;
569 uint32_t offWrite;
570 bool const fIsEmpty = gitsCmdQueueIsEmptyEx(pGitsDev, &offRead, &offWrite);
571 if (!fIsEmpty)
572 {
573 uint32_t const cCmdQueuePages = RT_BF_GET(pGitsDev->uCmdBaseReg.u, GITS_BF_CTRL_REG_CBASER_SIZE) + 1;
574 uint32_t const cbCmdQueue = cCmdQueuePages << GITS_CMD_QUEUE_PAGE_SHIFT;
575 AssertRelease(cbCmdQueue <= cbBuf); /** @todo Paranoia; make this a debug assert later. */
576
577 /*
578 * Read all the commands from guest memory into our command queue buffer.
579 */
580 int rc;
581 uint32_t cbCmds;
582 RTGCPHYS const GCPhysCmds = pGitsDev->uCmdBaseReg.u & GITS_BF_CTRL_REG_CBASER_PHYS_ADDR_MASK;
583
584 /* Leave the critical section while reading (a potentially large number of) commands from guest memory. */
585 GIC_CRIT_SECT_LEAVE(pDevIns);
586
587 if (offWrite > offRead)
588 {
589 /* The write offset has not wrapped around, read them in one go. */
590 cbCmds = offWrite - offRead;
591 Assert(cbCmds <= cbBuf);
592 rc = PDMDevHlpPhysReadMeta(pDevIns, GCPhysCmds + offRead, pvBuf, cbCmds);
593 }
594 else
595 {
596 /* The write offset has wrapped around, read till end of buffer followed by wrapped-around data. */
597 uint32_t const cbForward = cbCmdQueue - offRead;
598 uint32_t const cbWrapped = offWrite;
599 Assert(cbForward + cbWrapped <= cbBuf);
600 rc = PDMDevHlpPhysReadMeta(pDevIns, GCPhysCmds + offRead, pvBuf, cbForward);
601 if ( RT_SUCCESS(rc)
602 && cbWrapped > 0)
603 rc = PDMDevHlpPhysReadMeta(pDevIns, GCPhysCmds, (void *)((uintptr_t)pvBuf + cbForward), cbWrapped);
604 cbCmds = cbForward + cbWrapped;
605 }
606
607 /*
608 * Process the commands in the buffer.
609 */
610 if (RT_SUCCESS(rc))
611 {
612 /* Indicate to the guest we've fetched all commands. */
613 GIC_CRIT_SECT_ENTER(pDevIns);
614 pGitsDev->uCmdReadReg = offWrite;
615 pGitsDev->uCmdWriteReg &= ~GITS_BF_CTRL_REG_CWRITER_RETRY_MASK;
616
617 /* Don't hold the critical section while processing commands. */
618 GIC_CRIT_SECT_LEAVE(pDevIns);
619
620 uint32_t const cCmds = cbCmds / sizeof(GITSCMD);
621 for (uint32_t idxCmd = 0; idxCmd < cCmds; idxCmd++)
622 {
623 PCGITSCMD pCmd = (PCGITSCMD)((uintptr_t)pvBuf + (idxCmd * sizeof(GITSCMD)));
624 uint8_t const uCmdId = pCmd->common.uCmdId;
625 switch (uCmdId)
626 {
627 case GITS_CMD_ID_MAPC:
628 {
629 /* Map interrupt collection with a target CPU ID. */
630 uint64_t const uDw2 = pCmd->au64[2].u;
631 uint8_t const fValid = RT_BF_GET(uDw2, GITS_BF_CMD_MAPC_DW2_VALID);
632 uint16_t const uTargetCpuId = RT_BF_GET(uDw2, GITS_BF_CMD_MAPC_DW2_RDBASE);
633 uint16_t const uIntrCollectionId = RT_BF_GET(uDw2, GITS_BF_CMD_MAPC_DW2_IC_ID);
634
635 if (RT_LIKELY(uIntrCollectionId < RT_ELEMENTS(pGitsDev->auCtes)))
636 {
637 GIC_CRIT_SECT_ENTER(pDevIns);
638 Assert(!RT_BF_GET(pGitsDev->uTypeReg.u, GITS_BF_CTRL_REG_TYPER_PTA));
639 pGitsDev->auCtes[uIntrCollectionId] = RT_BF_MAKE(GITS_BF_CTE_VALID, fValid)
640 | RT_BF_MAKE(GITS_BF_CTE_RDBASE, uTargetCpuId);
641 GIC_CRIT_SECT_LEAVE(pDevIns);
642 }
643 else
644 gitsCmdQueueSetError(pDevIns, pGitsDev, kGitsDiag_CmdQueue_Cmd_Mapc_Icid_Overflow,
645 false /* fStall */);
646 STAM_COUNTER_INC(&pGitsDev->StatCmdMapc);
647 break;
648 }
649
650 case GITS_CMD_ID_SYNC:
651 /* Nothing to do since all previous commands have committed their changes to device state. */
652 STAM_COUNTER_INC(&pGitsDev->StatCmdSync);
653 break;
654
655 case GITS_CMD_ID_INVALL:
656 {
657 /* Reading the table is likely to take the same time as reading just one entry. */
658 uint64_t const uDw2 = pCmd->au64[2].u;
659 uint16_t const uIntrCollectionId = RT_BF_GET(uDw2, GITS_BF_CMD_INVALL_DW2_IC_ID);
660 if (RT_LIKELY(uIntrCollectionId < RT_ELEMENTS(pGitsDev->auCtes)))
661 gicDistReadLpiConfigTableFromMem(pDevIns);
662 else
663 gitsCmdQueueSetError(pDevIns, pGitsDev, kGitsDiag_CmdQueue_Cmd_Invall_Icid_Overflow,
664 false /* fStall */);
665 STAM_COUNTER_INC(&pGitsDev->StatCmdInvall);
666 break;
667 }
668
669 default:
670 {
671 /* Record an internal error but do NOT stall queue as we have already advanced the read offset. */
672 gitsCmdQueueSetError(pDevIns, pGitsDev, kGitsDiag_CmdQueue_Basic_Unknown_Cmd, false /* fStall */);
673 AssertReleaseMsgFailed(("Cmd=%#x (%s) idxCmd=%u cCmds=%u offRead=%#RX32 offWrite=%#RX32\n",
674 uCmdId, gitsGetCommandName(uCmdId), idxCmd, cCmds, offRead, offWrite));
675 break;
676 }
677 }
678 }
679 return VINF_SUCCESS;
680 }
681
682 /* Failed to read command queue from the physical address specified by the guest, stall queue and retry later. */
683 gitsCmdQueueSetError(pDevIns, pGitsDev, kGitsDiag_CmdQueue_Basic_Invalid_PhysAddr, true /* fStall */);
684 return VINF_TRY_AGAIN;
685 }
686 }
687
688 GIC_CRIT_SECT_LEAVE(pDevIns);
689 return VINF_SUCCESS;
690}
691#endif /* IN_RING3 */
692
693
694/**
695 * @interface_method_impl{PDMGICBACKEND,pfnSendMsi}
696 */
697DECL_HIDDEN_CALLBACK(int) gitsSendMsi(PVMCC pVM, PCIBDF uBusDevFn, PCMSIMSG pMsi, uint32_t uEventId, uint32_t uTagSrc)
698{
699 Log4Func(("uBusDevFn=%#RX32 uEventId=%#RX32\n", uBusDevFn, uEventId));
700 RT_NOREF(pVM, uBusDevFn, pMsi, uEventId, uTagSrc);
701 return VERR_NOT_IMPLEMENTED;
702}
703
704#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
705
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