VirtualBox

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

Last change on this file since 109021 was 109021, checked in by vboxsync, 3 weeks ago

VMM/GIC: bugref:10877 GIC ITS, work-in-progress.

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