VirtualBox

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

Last change on this file was 109233, checked in by vboxsync, 8 days ago

VMM/GIC: bugref:10877 Use GITSDTE.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 46.9 KB
Line 
1/* $Id: GITSAll.cpp 109233 2025-05-13 05:25:16Z 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.215389.xyz.
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 <VBox/vmm/vm.h> /* pVM->cCpus */
40#include <iprt/errcore.h> /* VINF_SUCCESS */
41#include <iprt/string.h> /* RT_ZERO */
42#include <iprt/mem.h> /* RTMemAllocZ, RTMemFree */
43
44
45/*********************************************************************************************************************************
46* Defined Constants And Macros *
47*********************************************************************************************************************************/
48/** The current GITS saved state version. */
49#define GITS_SAVED_STATE_VERSION 1
50
51/** GITS diagnostic enum description expansion.
52 * The below construct ensures typos in the input to this macro are caught
53 * during compile time. */
54#define GITSDIAG_DESC(a_Name) RT_CONCAT(kGitsDiag_, a_Name) < kGitsDiag_End ? RT_STR(a_Name) : "Ignored"
55
56
57/*********************************************************************************************************************************
58* Global Variables *
59*********************************************************************************************************************************/
60/** GITS diagnostics description for members in GITSDIAG. */
61static const char *const g_apszGitsDiagDesc[] =
62{
63 /* No error. */
64 GITSDIAG_DESC(None),
65
66 /* Command queue: basic operation errors. */
67 GITSDIAG_DESC(CmdQueue_Basic_Unknown_Cmd),
68 GITSDIAG_DESC(CmdQueue_Basic_Invalid_PhysAddr),
69
70 /* Command queue: INVALL. */
71 GITSDIAG_DESC(CmdQueue_Cmd_Invall_Cte_Unmapped),
72 GITSDIAG_DESC(CmdQueue_Cmd_Invall_Icid_Invalid),
73
74 /* Command: MAPV. */
75 GITSDIAG_DESC(CmdQueue_Cmd_Mapc_Icid_Invalid),
76
77 /* Command: MAPD. */
78 GITSDIAG_DESC(CmdQueue_Cmd_Mapd_Size_Invalid),
79
80 /* Command: MAPI. */
81 GITSDIAG_DESC(CmdQueue_Cmd_Mapi_DevId_Unmapped),
82 GITSDIAG_DESC(CmdQueue_Cmd_Mapi_Dte_Rd_Failed),
83 GITSDIAG_DESC(CmdQueue_Cmd_Mapi_EventId_Invalid),
84 GITSDIAG_DESC(CmdQueue_Cmd_Mapi_IcId_Invalid),
85 GITSDIAG_DESC(CmdQueue_Cmd_Mapi_Ite_Wr_Failed),
86 GITSDIAG_DESC(CmdQueue_Cmd_Mapi_Lpi_Invalid),
87
88 /* Command: MAPTI. */
89 GITSDIAG_DESC(CmdQueue_Cmd_Mapti_DevId_Unmapped),
90 GITSDIAG_DESC(CmdQueue_Cmd_Mapti_Dte_Rd_Failed),
91 GITSDIAG_DESC(CmdQueue_Cmd_Mapti_EventId_Invalid),
92 GITSDIAG_DESC(CmdQueue_Cmd_Mapti_IcId_Invalid),
93 GITSDIAG_DESC(CmdQueue_Cmd_Mapti_Ite_Wr_Failed),
94 GITSDIAG_DESC(CmdQueue_Cmd_Mapti_Lpi_Invalid),
95
96 /* kGitsDiag_End */
97};
98AssertCompile(RT_ELEMENTS(g_apszGitsDiagDesc) == kGitsDiag_End);
99#undef GITSDIAG_DESC
100
101
102#ifndef VBOX_DEVICE_STRUCT_TESTCASE
103
104DECL_HIDDEN_CALLBACK(const char *) gitsGetCtrlRegDescription(uint16_t offReg)
105{
106 if (GIC_IS_REG_IN_RANGE(offReg, GITS_CTRL_REG_BASER_OFF_FIRST, GITS_CTRL_REG_BASER_RANGE_SIZE))
107 return "GITS_BASER<n>";
108 switch (offReg)
109 {
110 case GITS_CTRL_REG_CTLR_OFF: return "GITS_CTLR";
111 case GITS_CTRL_REG_IIDR_OFF: return "GITS_IIDR";
112 case GITS_CTRL_REG_TYPER_OFF: return "GITS_TYPER";
113 case GITS_CTRL_REG_MPAMIDR_OFF: return "GITS_MPAMIDR";
114 case GITS_CTRL_REG_PARTIDR_OFF: return "GITS_PARTIDR";
115 case GITS_CTRL_REG_MPIDR_OFF: return "GITS_MPIDR";
116 case GITS_CTRL_REG_STATUSR_OFF: return "GITS_STATUSR";
117 case GITS_CTRL_REG_UMSIR_OFF: return "GITS_UMSIR";
118 case GITS_CTRL_REG_CBASER_OFF: return "GITS_CBASER";
119 case GITS_CTRL_REG_CWRITER_OFF: return "GITS_CWRITER";
120 case GITS_CTRL_REG_CREADR_OFF: return "GITS_CREADR";
121 default:
122 return "<UNKNOWN>";
123 }
124}
125
126
127DECL_HIDDEN_CALLBACK(const char *) gitsGetTranslationRegDescription(uint16_t offReg)
128{
129 switch (offReg)
130 {
131 case GITS_TRANSLATION_REG_TRANSLATER: return "GITS_TRANSLATER";
132 default:
133 return "<UNKNOWN>";
134 }
135}
136
137
138static const char *gitsGetCommandName(uint8_t uCmdId)
139{
140 switch (uCmdId)
141 {
142 case GITS_CMD_ID_CLEAR: return "CLEAR";
143 case GITS_CMD_ID_DISCARD: return "DISCARD";
144 case GITS_CMD_ID_INT: return "INT";
145 case GITS_CMD_ID_INV: return "INV";
146 case GITS_CMD_ID_INVALL: return "INVALL";
147 case GITS_CMD_ID_INVDB: return "INVDB";
148 case GITS_CMD_ID_MAPC: return "MAPC";
149 case GITS_CMD_ID_MAPD: return "MAPD";
150 case GITS_CMD_ID_MAPI: return "MAPI";
151 case GITS_CMD_ID_MAPTI: return "MAPTI";
152 case GITS_CMD_ID_MOVALL: return "MOVALL";
153 case GITS_CMD_ID_MOVI: return "MOVI";
154 case GITS_CMD_ID_SYNC: return "SYNC";
155 case GITS_CMD_ID_VINVALL: return "VINVALL";
156 case GITS_CMD_ID_VMAPI: return "VMAPI";
157 case GITS_CMD_ID_VMAPP: return "VMAPP";
158 case GITS_CMD_ID_VMAPTI: return "VMAPTI";
159 case GITS_CMD_ID_VMOVI: return "VMOVI";
160 case GITS_CMD_ID_VMOVP: return "VMOVP";
161 case GITS_CMD_ID_VSGI: return "VSGI";
162 case GITS_CMD_ID_VSYNC: return "VSYNC";
163 default:
164 return "<UNKNOWN>";
165 }
166}
167
168
169DECL_FORCE_INLINE(const char *) gitsGetDiagDescription(GITSDIAG enmDiag)
170{
171 if (enmDiag < RT_ELEMENTS(g_apszGitsDiagDesc))
172 return g_apszGitsDiagDesc[enmDiag];
173 return "<Unknown>";
174}
175
176
177static RTGCPHYS gitsGetBaseRegPhysAddr(uint64_t uGitsBaseReg)
178{
179 /* Mask for physical address bits [47:12]. */
180 static uint64_t const s_auPhysAddrLoMasks[] =
181 {
182 UINT64_C(0x0000fffffffff000), /* 4K bits[47:12] */
183 UINT64_C(0x0000ffffffffc000), /* 16K bits[47:14] */
184 UINT64_C(0x0000ffffffff0000), /* 64K bits[47:16] */
185 UINT64_C(0x0000ffffffff0000) /* 64K bits[47:16] */
186 };
187
188 /* Mask for physical address bits [51:48]. */
189 static uint64_t const s_auPhysAddrHiMasks[] =
190 {
191 UINT64_C(0x0), /* 4K bits[51:48] = 0 */
192 UINT64_C(0x0), /* 16K bits[51:48] = 0 */
193 UINT64_C(0x000000000000f000), /* 64K bits[51:48] = bits[15:12] */
194 UINT64_C(0x000000000000f000) /* 64K bits[51:48] = bits[15:12] */
195 };
196 AssertCompile(RT_ELEMENTS(s_auPhysAddrLoMasks) == RT_ELEMENTS(s_auPhysAddrHiMasks));
197
198 uint8_t const idxPageSize = RT_BF_GET(uGitsBaseReg, GITS_BF_CTRL_REG_BASER_PAGESIZE);
199 Assert(idxPageSize < RT_ELEMENTS(s_auPhysAddrLoMasks));
200 RTGCPHYS const GCPhys = (uGitsBaseReg & s_auPhysAddrLoMasks[idxPageSize])
201 | ((uGitsBaseReg & s_auPhysAddrHiMasks[idxPageSize]) << (48 - 12));
202 return GCPhys;
203}
204
205
206static void gitsCmdQueueSetError(PPDMDEVINS pDevIns, PGITSDEV pGitsDev, GITSDIAG enmDiag, bool fStallQueue)
207{
208 Log4Func(("enmDiag=%#RX32 (%s) fStallQueue=%RTbool\n", enmDiag, gitsGetDiagDescription(enmDiag)));
209
210 GIC_CRIT_SECT_ENTER(pDevIns);
211
212 /* Record the error and stall the queue. */
213 pGitsDev->enmDiag = enmDiag;
214 pGitsDev->cCmdQueueErrors++;
215 if (fStallQueue)
216 pGitsDev->uCmdReadReg |= GITS_BF_CTRL_REG_CREADR_STALLED_MASK;
217
218 GIC_CRIT_SECT_LEAVE(pDevIns);
219
220 /* Since we don't support SEIs, so there should be nothing more to do here. */
221 Assert(!RT_BF_GET(pGitsDev->uTypeReg.u, GITS_BF_CTRL_REG_TYPER_SEIS));
222}
223
224
225DECL_FORCE_INLINE(bool) gitsCmdQueueIsEmptyEx(PCGITSDEV pGitsDev, uint32_t *poffRead, uint32_t *poffWrite)
226{
227 *poffRead = pGitsDev->uCmdReadReg & GITS_BF_CTRL_REG_CREADR_OFFSET_MASK;
228 *poffWrite = pGitsDev->uCmdWriteReg & GITS_BF_CTRL_REG_CWRITER_OFFSET_MASK;
229 return *poffRead == *poffWrite;
230}
231
232
233DECL_FORCE_INLINE(bool) gitsCmdQueueIsEmpty(PCGITSDEV pGitsDev)
234{
235 uint32_t offRead;
236 uint32_t offWrite;
237 return gitsCmdQueueIsEmptyEx(pGitsDev, &offRead, &offWrite);
238}
239
240
241DECL_FORCE_INLINE(bool) gitsCmdQueueCanProcessRequests(PCGITSDEV pGitsDev)
242{
243 if ( (pGitsDev->uTypeReg.u & GITS_BF_CTRL_REG_CTLR_ENABLED_MASK)
244 && (pGitsDev->uCmdBaseReg.u & GITS_BF_CTRL_REG_CBASER_VALID_MASK)
245 && !(pGitsDev->uCmdReadReg & GITS_BF_CTRL_REG_CREADR_STALLED_MASK))
246 return true;
247 return false;
248}
249
250
251static void gitsCmdQueueThreadWakeUpIfNeeded(PPDMDEVINS pDevIns, PGITSDEV pGitsDev)
252{
253 Log4Func(("\n"));
254 Assert(GIC_CRIT_SECT_IS_OWNER(pDevIns));
255 if ( gitsCmdQueueCanProcessRequests(pGitsDev)
256 && !gitsCmdQueueIsEmpty(pGitsDev))
257 {
258 Log4Func(("Waking up command-queue thread\n"));
259 int const rc = PDMDevHlpSUPSemEventSignal(pDevIns, pGitsDev->hEvtCmdQueue);
260 AssertRC(rc);
261 }
262}
263
264
265DECL_HIDDEN_CALLBACK(uint64_t) gitsMmioReadCtrl(PCGITSDEV pGitsDev, uint16_t offReg, unsigned cb)
266{
267 Assert(cb == 4 || cb == 8);
268 Assert(!(offReg & 3));
269 RT_NOREF(cb);
270
271 /*
272 * GITS_BASER<n>.
273 */
274 uint64_t uReg;
275 if (GIC_IS_REG_IN_RANGE(offReg, GITS_CTRL_REG_BASER_OFF_FIRST, GITS_CTRL_REG_BASER_RANGE_SIZE))
276 {
277 uint16_t const cbReg = sizeof(uint64_t);
278 uint16_t const idxReg = (offReg - GITS_CTRL_REG_BASER_OFF_FIRST) / cbReg;
279 uReg = pGitsDev->aItsTableRegs[idxReg].u >> ((offReg & 7) << 3 /* to bits */);
280 return uReg;
281 }
282
283 switch (offReg)
284 {
285 case GITS_CTRL_REG_CTLR_OFF:
286 Assert(cb == 4);
287 uReg = pGitsDev->uCtrlReg;
288 break;
289
290 case GITS_CTRL_REG_PIDR2_OFF:
291 Assert(cb == 4);
292 Assert(pGitsDev->uArchRev <= GITS_CTRL_REG_PIDR2_ARCHREV_GICV4);
293 uReg = RT_BF_MAKE(GITS_BF_CTRL_REG_PIDR2_DES_1, GIC_JEDEC_JEP10_DES_1(GIC_JEDEC_JEP106_IDENTIFICATION_CODE))
294 | RT_BF_MAKE(GITS_BF_CTRL_REG_PIDR2_JEDEC, 1)
295 | RT_BF_MAKE(GITS_BF_CTRL_REG_PIDR2_ARCHREV, pGitsDev->uArchRev);
296 break;
297
298 case GITS_CTRL_REG_IIDR_OFF:
299 Assert(cb == 4);
300 uReg = RT_BF_MAKE(GITS_BF_CTRL_REG_IIDR_IMPL_ID_CODE, GIC_JEDEC_JEP106_IDENTIFICATION_CODE)
301 | RT_BF_MAKE(GITS_BF_CTRL_REG_IIDR_IMPL_CONT_CODE, GIC_JEDEC_JEP106_CONTINUATION_CODE);
302 break;
303
304 case GITS_CTRL_REG_TYPER_OFF:
305 case GITS_CTRL_REG_TYPER_OFF + 4:
306 uReg = pGitsDev->uTypeReg.u >> ((offReg & 7) << 3 /* to bits */);
307 break;
308
309 case GITS_CTRL_REG_CBASER_OFF:
310 uReg = pGitsDev->uCmdBaseReg.u;
311 break;
312
313 case GITS_CTRL_REG_CBASER_OFF + 4:
314 Assert(cb == 4);
315 uReg = pGitsDev->uCmdBaseReg.s.Hi;
316 break;
317
318 case GITS_CTRL_REG_CREADR_OFF:
319 uReg = pGitsDev->uCmdReadReg;
320 break;
321
322 case GITS_CTRL_REG_CREADR_OFF + 4:
323 uReg = 0; /* Upper 32-bits are reserved, MBZ. */
324 break;
325
326 case GITS_CTRL_REG_CWRITER_OFF:
327 uReg = pGitsDev->uCmdWriteReg;
328 break;
329
330 case GITS_CTRL_REG_CWRITER_OFF + 4:
331 uReg = 0; /* Upper 32-bits are reserved, MBZ. */
332 break;
333
334 default:
335 AssertReleaseMsgFailed(("offReg=%#x (%s)\n", offReg, gitsGetCtrlRegDescription(offReg)));
336 uReg = 0;
337 break;
338 }
339
340 Log4Func(("offReg=%#RX16 (%s) uReg=%#RX64 [%u-bit]\n", offReg, gitsGetCtrlRegDescription(offReg), uReg, cb << 3));
341 return uReg;
342}
343
344
345DECL_HIDDEN_CALLBACK(uint64_t) gitsMmioReadTranslate(PCGITSDEV pGitsDev, uint16_t offReg, unsigned cb)
346{
347 Assert(cb == 8 || cb == 4);
348 Assert(!(offReg & 3));
349 RT_NOREF(pGitsDev, cb);
350
351 uint64_t uReg = 0;
352 AssertReleaseMsgFailed(("offReg=%#x (%s) uReg=%#RX64 [%u-bit]\n", offReg, gitsGetTranslationRegDescription(offReg), uReg, cb << 3));
353 return uReg;
354}
355
356
357DECL_HIDDEN_CALLBACK(void) gitsMmioWriteCtrl(PPDMDEVINS pDevIns, PGITSDEV pGitsDev, uint16_t offReg, uint64_t uValue, unsigned cb)
358{
359 Assert(cb == 8 || cb == 4);
360 Assert(!(offReg & 3));
361 Log4Func(("offReg=%u uValue=%#RX64 cb=%u\n", offReg, uValue, cb));
362
363 /*
364 * GITS_BASER<n>.
365 */
366 if (GIC_IS_REG_IN_RANGE(offReg, GITS_CTRL_REG_BASER_OFF_FIRST, GITS_CTRL_REG_BASER_RANGE_SIZE))
367 {
368 uint16_t const cbReg = sizeof(uint64_t);
369 uint16_t const idxReg = (offReg - GITS_CTRL_REG_BASER_OFF_FIRST) / cbReg;
370 uint64_t const fRwMask = GITS_CTRL_REG_BASER_RW_MASK;
371 if (!(offReg & 7))
372 {
373 if (cb == 8)
374 GIC_SET_REG_U64_FULL(pGitsDev->aItsTableRegs[idxReg].u, uValue, fRwMask);
375 else
376 GIC_SET_REG_U64_LO(pGitsDev->aItsTableRegs[idxReg].s.Lo, uValue, fRwMask);
377 }
378 else
379 {
380 Assert(cb == 4);
381 GIC_SET_REG_U64_HI(pGitsDev->aItsTableRegs[idxReg].s.Hi, uValue, fRwMask);
382 }
383 /** @todo Clear ITS caches when GITS_BASER<n>.Valid = 0. */
384 return;
385 }
386
387 switch (offReg)
388 {
389 case GITS_CTRL_REG_CTLR_OFF:
390 Assert(cb == 4);
391 Assert(!(pGitsDev->uTypeReg.u & GITS_BF_CTRL_REG_TYPER_UMSI_IRQ_MASK));
392 GIC_SET_REG_U32(pGitsDev->uCtrlReg, uValue, GITS_BF_CTRL_REG_CTLR_RW_MASK);
393 if (RT_BF_GET(uValue, GITS_BF_CTRL_REG_CTLR_ENABLED))
394 pGitsDev->uCtrlReg &= ~GITS_BF_CTRL_REG_CTLR_QUIESCENT_MASK;
395 else
396 {
397 pGitsDev->uCtrlReg |= GITS_BF_CTRL_REG_CTLR_QUIESCENT_MASK;
398 /** @todo Clear ITS caches. */
399 }
400 gitsCmdQueueThreadWakeUpIfNeeded(pDevIns, pGitsDev);
401 break;
402
403 case GITS_CTRL_REG_CBASER_OFF:
404 if (cb == 8)
405 GIC_SET_REG_U64_FULL(pGitsDev->uCmdBaseReg.u, uValue, GITS_CTRL_REG_CBASER_RW_MASK);
406 else
407 GIC_SET_REG_U64_LO(pGitsDev->uCmdBaseReg.s.Lo, uValue, GITS_CTRL_REG_CBASER_RW_MASK);
408 gitsCmdQueueThreadWakeUpIfNeeded(pDevIns, pGitsDev);
409 break;
410
411 case GITS_CTRL_REG_CBASER_OFF + 4:
412 Assert(cb == 4);
413 GIC_SET_REG_U64_HI(pGitsDev->uCmdBaseReg.s.Hi, uValue, GITS_CTRL_REG_CBASER_RW_MASK);
414 gitsCmdQueueThreadWakeUpIfNeeded(pDevIns, pGitsDev);
415 break;
416
417 case GITS_CTRL_REG_CWRITER_OFF:
418 GIC_SET_REG_U32(pGitsDev->uCmdWriteReg, uValue, GITS_CTRL_REG_CWRITER_RW_MASK);
419 gitsCmdQueueThreadWakeUpIfNeeded(pDevIns, pGitsDev);
420 break;
421
422 case GITS_CTRL_REG_CWRITER_OFF + 4:
423 /* Upper 32-bits are all reserved, ignore write. Fedora 40 arm64 guests (and probably others) do this. */
424 Assert(uValue == 0);
425 gitsCmdQueueThreadWakeUpIfNeeded(pDevIns, pGitsDev);
426 break;
427
428 default:
429 AssertReleaseMsgFailed(("offReg=%#x (%s) uValue=%#RX32\n", offReg, gitsGetCtrlRegDescription(offReg), uValue));
430 break;
431 }
432
433 Log4Func(("offReg=%#RX16 (%s) uValue=%#RX32 [%u-bit]\n", offReg, gitsGetCtrlRegDescription(offReg), uValue, cb << 3));
434}
435
436
437DECL_HIDDEN_CALLBACK(void) gitsMmioWriteTranslate(PGITSDEV pGitsDev, uint16_t offReg, uint64_t uValue, unsigned cb)
438{
439 RT_NOREF(pGitsDev);
440 Assert(cb == 8 || cb == 4);
441 Assert(!(offReg & 3));
442 Log4Func(("offReg=%u uValue=%#RX64 cb=%u\n", offReg, uValue, cb));
443 /** @todo Call gitsSetLpi for GITS_TRANSLATER register offset write. */
444 AssertReleaseMsgFailed(("offReg=%#x uValue=%#RX64 [%u-bit]\n", offReg, uValue, cb << 3));
445}
446
447
448DECL_HIDDEN_CALLBACK(void) gitsInit(PGITSDEV pGitsDev)
449{
450 Log4Func(("\n"));
451
452 /* GITS_CTLR.*/
453 pGitsDev->uCtrlReg = RT_BF_MAKE(GITS_BF_CTRL_REG_CTLR_QUIESCENT, 1);
454
455 /* GITS_TYPER. */
456 pGitsDev->uTypeReg.u = RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_PHYSICAL, 1) /* Physical LPIs supported. */
457 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_VIRTUAL, 0) */ /* Virtual LPIs not supported. */
458 | RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_CCT, 0) /* Collections in memory not supported. */
459 | RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_ITT_ENTRY_SIZE, sizeof(GITSITE) - 1) /* ITE size in bytes minus 1. */
460 | RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_ID_BITS, 31) /* 32-bit event IDs. */
461 | RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_DEV_BITS, 31) /* 32-bit device IDs. */
462 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_SEIS, 0) */ /* Locally generated errors not recommended. */
463 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_PTA, 0) */ /* Target is VCPU ID not address. */
464 | RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_HCC, 255) /* Collection count. */
465 | RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_CID_BITS, 0) /* Collections in memory not supported. */
466 | RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_CIL, 0) /* Collections in memory not supported. */
467 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_VMOVP, 0) */ /* VMOVP not supported. */
468 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_MPAM, 0) */ /* MPAM no supported. */
469 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_VSGI, 0) */ /* VSGI not supported. */
470 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_VMAPP, 0) */ /* VMAPP not supported. */
471 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_SVPET, 0) */ /* SVPET not supported. */
472 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_NID, 0) */ /* NID (doorbell) not supported. */
473 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_UMSI, 0) */ /** @todo Reporting receipt of unmapped MSIs. */
474 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_UMSI_IRQ, 0) */ /** @todo Generating interrupt on unmapped MSI. */
475 | RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_INV, 1); /* ITS caches invalidated when clearing
476 GITS_CTLR.Enabled and GITS_BASER<n>.Valid. */
477 Assert(RT_ELEMENTS(pGitsDev->aCtes) >= RT_BF_GET(pGitsDev->uTypeReg.u, GITS_BF_CTRL_REG_TYPER_HCC));
478
479 /* GITS_BASER<n>. */
480 RT_ZERO(pGitsDev->aItsTableRegs);
481 pGitsDev->aItsTableRegs[0].u = RT_BF_MAKE(GITS_BF_CTRL_REG_BASER_ENTRY_SIZE, sizeof(GITSDTE) - 1)
482 | RT_BF_MAKE(GITS_BF_CTRL_REG_BASER_TYPE, GITS_BASER_TYPE_DEVICES);
483
484 /* GITS_CBASER, GITS_CREADR, GITS_CWRITER. */
485 pGitsDev->uCmdBaseReg.u = 0;
486 pGitsDev->uCmdReadReg = 0;
487 pGitsDev->uCmdWriteReg = 0;
488
489 /* Collection Table. */
490 for (unsigned i = 0; i < RT_ELEMENTS(pGitsDev->aCtes); i++)
491 pGitsDev->aCtes[i].idTargetCpu = NIL_VMCPUID;
492
493 /* Misc. stuff. */
494 pGitsDev->cCmdQueueErrors = 0;
495}
496
497
498#ifdef IN_RING3
499DECL_HIDDEN_CALLBACK(void) gitsR3DbgInfo(PCGITSDEV pGitsDev, PCDBGFINFOHLP pHlp)
500{
501 pHlp->pfnPrintf(pHlp, "GIC ITS:\n");
502
503 /* Basic info, GITS_CTLR and GITS_TYPER. */
504 {
505 uint32_t const uCtrlReg = pGitsDev->uCtrlReg;
506 GITSDIAG const enmDiag = pGitsDev->enmDiag;
507 pHlp->pfnPrintf(pHlp, " uArchRev = %u\n", pGitsDev->uArchRev);
508 pHlp->pfnPrintf(pHlp, " Cmd queue errors = %RU64\n", pGitsDev->cCmdQueueErrors);
509 pHlp->pfnPrintf(pHlp, " Last error = %#RX32 (%s)\n", enmDiag, gitsGetDiagDescription(enmDiag));
510 pHlp->pfnPrintf(pHlp, " GITS_CTLR = %#RX32\n", uCtrlReg);
511 pHlp->pfnPrintf(pHlp, " Enabled = %RTbool\n", RT_BF_GET(uCtrlReg, GITS_BF_CTRL_REG_CTLR_ENABLED));
512 pHlp->pfnPrintf(pHlp, " UMSI IRQ = %RTbool\n", RT_BF_GET(uCtrlReg, GITS_BF_CTRL_REG_CTLR_UMSI_IRQ));
513 pHlp->pfnPrintf(pHlp, " Quiescent = %RTbool\n", RT_BF_GET(uCtrlReg, GITS_BF_CTRL_REG_CTLR_QUIESCENT));
514 }
515
516 /* GITS_BASER<n>. */
517 for (unsigned i = 0; i < RT_ELEMENTS(pGitsDev->aItsTableRegs); i++)
518 {
519 static uint32_t const s_acbPageSize[] = { _4K, _16K, _64K, _64K };
520 static const char* const s_apszType[] = { "UnImpl", "Devices", "vPEs", "Intr Collections" };
521 uint64_t const uReg = pGitsDev->aItsTableRegs[i].u;
522 bool const fValid = RT_BOOL(RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_VALID));
523 uint8_t const idxType = RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_TYPE);
524 if ( fValid
525 || idxType != GITS_BASER_TYPE_UNIMPL)
526 {
527 uint16_t const uSize = RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_SIZE);
528 uint16_t const cPages = uSize > 0 ? uSize + 1 : 0;
529 uint8_t const idxPageSize = RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_PAGESIZE);
530 uint64_t const cbItsTable = cPages * s_acbPageSize[idxPageSize];
531 uint8_t const uEntrySize = RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_ENTRY_SIZE);
532 bool const fIndirect = RT_BOOL(RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_INDIRECT));
533 const char *pszType = s_apszType[idxType];
534 pHlp->pfnPrintf(pHlp, " GITS_BASER[%u] = %#RX64\n", i, uReg);
535 pHlp->pfnPrintf(pHlp, " Size = %#x (pages=%u total=%.Rhcb)\n", uSize, cPages, cbItsTable);
536 pHlp->pfnPrintf(pHlp, " Page size = %#x (%.Rhcb)\n", idxPageSize, s_acbPageSize[idxPageSize]);
537 pHlp->pfnPrintf(pHlp, " Shareability = %#x\n", RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_SHAREABILITY));
538 pHlp->pfnPrintf(pHlp, " Phys addr = %#RX64 (addr=%#RX64)\n", uReg & GITS_BF_CTRL_REG_BASER_PHYS_ADDR_MASK,
539 gitsGetBaseRegPhysAddr(uReg));
540 pHlp->pfnPrintf(pHlp, " Entry size = %#x (%u bytes)\n", uEntrySize, uEntrySize > 0 ? uEntrySize + 1 : 0);
541 pHlp->pfnPrintf(pHlp, " Outer cache = %#x\n", RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_OUTER_CACHE));
542 pHlp->pfnPrintf(pHlp, " Type = %#x (%s)\n", idxType, pszType);
543 pHlp->pfnPrintf(pHlp, " Inner cache = %#x\n", RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_INNER_CACHE));
544 pHlp->pfnPrintf(pHlp, " Indirect = %RTbool\n", fIndirect);
545 pHlp->pfnPrintf(pHlp, " Valid = %RTbool\n", fValid);
546 }
547 }
548
549 /* GITS_CBASER. */
550 {
551 uint64_t const uReg = pGitsDev->uCmdBaseReg.u;
552 uint8_t const uSize = RT_BF_GET(uReg, GITS_BF_CTRL_REG_CBASER_SIZE);
553 uint16_t const cPages = uSize > 0 ? uSize + 1 : 0;
554 pHlp->pfnPrintf(pHlp, " GITS_CBASER = %#RX64\n", uReg);
555 pHlp->pfnPrintf(pHlp, " Size = %#x (pages=%u total=%.Rhcb)\n", uSize, cPages, _4K * cPages);
556 pHlp->pfnPrintf(pHlp, " Shareability = %#x\n", RT_BF_GET(uReg, GITS_BF_CTRL_REG_CBASER_SHAREABILITY));
557 pHlp->pfnPrintf(pHlp, " Phys addr = %#RX64\n", uReg & GITS_BF_CTRL_REG_CBASER_PHYS_ADDR_MASK);
558 pHlp->pfnPrintf(pHlp, " Outer cache = %#x\n", RT_BF_GET(uReg, GITS_BF_CTRL_REG_CBASER_OUTER_CACHE));
559 pHlp->pfnPrintf(pHlp, " Inner cache = %#x\n", RT_BF_GET(uReg, GITS_BF_CTRL_REG_CBASER_INNER_CACHE));
560 pHlp->pfnPrintf(pHlp, " Valid = %RTbool\n", RT_BF_GET(uReg, GITS_BF_CTRL_REG_CBASER_VALID));
561 }
562
563 /* GITS_CREADR. */
564 {
565 uint32_t const uReg = pGitsDev->uCmdReadReg;
566 pHlp->pfnPrintf(pHlp, " GITS_CREADR = 0x%05RX32 (stalled=%RTbool offset=%RU32)\n", uReg,
567 RT_BF_GET(uReg, GITS_BF_CTRL_REG_CREADR_STALLED), uReg & GITS_BF_CTRL_REG_CREADR_OFFSET_MASK);
568 }
569
570 /* GITS_CWRITER. */
571 {
572 uint32_t const uReg = pGitsDev->uCmdWriteReg;
573 pHlp->pfnPrintf(pHlp, " GITS_CWRITER = 0x%05RX32 ( retry=%RTbool offset=%RU32)\n", uReg,
574 RT_BF_GET(uReg, GITS_BF_CTRL_REG_CWRITER_RETRY), uReg & GITS_BF_CTRL_REG_CWRITER_OFFSET_MASK);
575 }
576
577 /* Interrupt Collection Table. */
578 {
579 pHlp->pfnPrintf(pHlp, " Collection Table:\n");
580 bool fHasValidCtes = false;
581 for (unsigned i = 0; i < RT_ELEMENTS(pGitsDev->aCtes); i++)
582 {
583 VMCPUID const idTargetCpu = pGitsDev->aCtes[i].idTargetCpu;
584 if (idTargetCpu != NIL_VMCPUID)
585 {
586 pHlp->pfnPrintf(pHlp, " [%3u] = %RU32\n", i, idTargetCpu);
587 fHasValidCtes = true;
588 }
589 }
590 if (!fHasValidCtes)
591 pHlp->pfnPrintf(pHlp, " Empty (no valid entries)\n");
592 }
593}
594
595
596#if 0
597static void gitsR3DteCacheAdd(PGITSDEV pGitsDev, uint32_t uDevId, RTGCPHYS GCPhysItt, uint8_t cDevIdBits, PGITSDTE pDte)
598{
599 pDte->fValid = 1;
600 pDte->afPadding = 0;
601 pDte->uDevId = uDevId;
602 pDte->GCPhysItt = GCPhysItt;
603 pDte->cbItt = RT_BIT_32(cDevIdBits) - 1;
604
605 unsigned idxFree = 0;
606 for (unsigned i = 0; i < RT_ELEMENTS(pGitsDev->aDtes); i++)
607 {
608 PCGITSDTE pCurDte = &pGitsDev->aDtes[i];
609 if (!pCurDte->fValid)
610 {
611 idxFree = i;
612 break;
613 }
614 }
615 memcpy(&pGitsDev->aDtes[idxFree], pDte, sizeof(*pDte));
616}
617
618
619static void gitsR3DteCacheRemove(PGITSDEV pGitsDev, uint32_t uDevId)
620{
621 for (unsigned i = 0; i < RT_ELEMENTS(pGitsDev->aDtes); i++)
622 {
623 PGITSDTE pCurDte = &pGitsDev->aDtes[i];
624 if (pCurDte->uDevId == uDevId)
625 {
626 RT_ZERO(*pCurDte);
627 return;
628 }
629 }
630}
631#endif
632
633
634static int gitsR3DteGetAddr(PPDMDEVINS pDevIns, PGITSDEV pGitsDev, uint32_t uDevId, PRTGCPHYS pGCPhysDte)
635{
636 uint64_t const uBaseReg = pGitsDev->aItsTableRegs[0].u;
637 bool const fIndirect = RT_BF_GET(uBaseReg, GITS_BF_CTRL_REG_BASER_INDIRECT);
638 RTGCPHYS GCPhysDevTable = gitsGetBaseRegPhysAddr(uBaseReg);
639 if (!fIndirect)
640 {
641 *pGCPhysDte = GCPhysDevTable + uDevId * sizeof(GITSDTE);
642 return VINF_SUCCESS;
643 }
644
645 RTGCPHYS offDte = 0;
646 static uint32_t const s_acbPageSizes[] = { _4K, _16K, _64K, _64K };
647 static uint64_t const s_auPhysAddrMasks[] =
648 {
649 UINT64_C(0x000ffffffffff000), /* 4K bits[51:12] */
650 UINT64_C(0x000fffffffffc000), /* 16K bits[51:14] */
651 UINT64_C(0x000fffffffff0000), /* 64K bits[51:16] */
652 UINT64_C(0x000fffffffff0000) /* 64K bits[51:16] */
653 };
654
655 uint8_t const idxPageSize = RT_BF_GET(uBaseReg, GITS_BF_CTRL_REG_BASER_PAGESIZE);
656 uint32_t const cbPage = s_acbPageSizes[idxPageSize];
657
658 /* Read the the level 1 table device-table entry. */
659 uint32_t const cLevel1Entries = cbPage / GITS_ITE_INDIRECT_LVL1_SIZE;
660 RTGCPHYS const offLevel1Dte = (uDevId % cLevel1Entries) * GITS_ITE_INDIRECT_LVL1_SIZE;
661 uint64_t uLevel1Dte = 0;
662 int rc = PDMDevHlpPhysReadMeta(pDevIns, GCPhysDevTable + offLevel1Dte, &uLevel1Dte, sizeof(uLevel1Dte));
663 if (RT_SUCCESS(rc))
664 {
665 /* Check if the entry is valid. */
666 bool const fValid = RT_BF_GET(uLevel1Dte, GITS_BF_ITE_INDIRECT_LVL1_4K_VALID);
667 if (fValid)
668 {
669 /* Compute the physical address of the device-table entry from the level 1 entry. */
670 uint32_t const cEntries = cbPage / sizeof(GITSDTE);
671 GCPhysDevTable = uLevel1Dte & s_auPhysAddrMasks[idxPageSize];
672 offDte = (uDevId % cEntries) * sizeof(GITSDTE);
673
674 *pGCPhysDte = GCPhysDevTable + offDte;
675 return VINF_SUCCESS;
676 }
677 rc = VERR_NOT_FOUND;
678 }
679
680 /* Something went wrong (usually shouldn't happen but could be faulty/misbehaving guest). */
681 *pGCPhysDte = NIL_RTGCPHYS;
682 return rc;
683}
684
685
686static int gitsR3DteRead(PPDMDEVINS pDevIns, PGITSDEV pGitsDev, uint32_t uDevId, GITSDTE *puDte)
687{
688 RTGCPHYS GCPhysDte;
689 int const rc = gitsR3DteGetAddr(pDevIns, pGitsDev, uDevId, &GCPhysDte);
690 if (RT_SUCCESS(rc))
691 return PDMDevHlpPhysReadMeta(pDevIns, GCPhysDte, (void *)puDte, sizeof(*puDte));
692 AssertMsgFailed(("Failed to get device-table entry address for device ID %#RX32 rc=%Rrc\n", uDevId, rc));
693 return rc;
694}
695
696
697static int gitsR3DteWrite(PPDMDEVINS pDevIns, PGITSDEV pGitsDev, uint32_t uDevId, GITSDTE uDte)
698{
699 RTGCPHYS GCPhysDte;
700 int const rc = gitsR3DteGetAddr(pDevIns, pGitsDev, uDevId, &GCPhysDte);
701 if (RT_SUCCESS(rc))
702 return PDMDevHlpPhysWriteMeta(pDevIns, GCPhysDte, (const void *)&uDte, sizeof(uDte));
703 AssertMsgFailed(("Failed to get device-table entry address for device ID %#RX32 rc=%Rrc\n", uDevId, rc));
704 return rc;
705}
706
707
708static int gitsR3IteRead(PPDMDEVINS pDevIns, GITSDTE uDte, uint32_t uEventId, GITSITE *puIte)
709{
710 RTGCPHYS const GCPhysIntrTable = uDte & GITS_BF_DTE_ITT_ADDR_MASK;
711 RTGCPHYS const GCPhysIte = GCPhysIntrTable + uEventId * sizeof(GITSITE);
712 return PDMDevHlpPhysReadMeta(pDevIns, GCPhysIte, (void *)puIte, sizeof(*puIte));
713}
714
715
716static int gitsR3IteWrite(PPDMDEVINS pDevIns, GITSDTE uDte, uint32_t uEventId, GITSITE uIte)
717{
718 RTGCPHYS const GCPhysIntrTable = uDte & GITS_BF_DTE_ITT_ADDR_MASK;
719 RTGCPHYS const GCPhysIte = GCPhysIntrTable + uEventId * sizeof(GITSITE);
720 return PDMDevHlpPhysWriteMeta(pDevIns, GCPhysIte, (const void *)&uIte, sizeof(uIte));
721}
722
723
724static void gitsR3CmdMapIntr(PPDMDEVINS pDevIns, PGITSDEV pGitsDev, uint32_t uDevId, uint32_t uEventId, uint16_t uIntId,
725 uint16_t uIcId, bool fMapti)
726{
727#define GITS_CMD_QUEUE_SET_ERR_RET(a_enmDiagSuffix) \
728 do \
729 { \
730 gitsCmdQueueSetError(pDevIns, pGitsDev, \
731 fMapti ? kGitsDiag_CmdQueue_Cmd_ ## Mapti_ ## a_enmDiagSuffix \
732 : kGitsDiag_CmdQueue_Cmd_ ## Mapi_ ## a_enmDiagSuffix, false /* fStall */); \
733 return; \
734 } while (0)
735
736 /* We support 32-bits of device ID and hence it cannot be out of range (asserted below). */
737 Assert(sizeof(uDevId) * 8 >= RT_BF_GET(pGitsDev->uTypeReg.u, GITS_BF_CTRL_REG_TYPER_DEV_BITS) + 1);
738
739 /* Validate ICID. */
740 if (uIcId < RT_ELEMENTS(pGitsDev->aCtes))
741 { /* likely */ }
742 else
743 GITS_CMD_QUEUE_SET_ERR_RET(IcId_Invalid);
744
745 /* Validate LPI INTID. */
746 if (gicDistIsLpiValid(pDevIns, uIntId))
747 { /* likely */ }
748 else
749 GITS_CMD_QUEUE_SET_ERR_RET(Lpi_Invalid);
750
751 /* Read the device-table entry. */
752 GITSDTE uDte = 0;
753 int rc = gitsR3DteRead(pDevIns, pGitsDev, uDevId, &uDte);
754 if (RT_SUCCESS(rc))
755 { /* likely */ }
756 else
757 GITS_CMD_QUEUE_SET_ERR_RET(Dte_Rd_Failed);
758
759 /* Check that the device ID mapping is valid. */
760 bool const fValid = RT_BF_GET(uDte, GITS_BF_DTE_VALID);
761 if (fValid)
762 { /* likely */ }
763 else
764 GITS_CMD_QUEUE_SET_ERR_RET(DevId_Unmapped);
765
766 /* Check that the event ID (which is the index) is within range. */
767 uint32_t const cEntries = RT_BIT_32(RT_BF_GET(uDte, GITS_BF_DTE_ITT_ADDR) + 1);
768 if (uEventId < cEntries)
769 {
770 /* Write the interrupt-translation entry mapping event ID with INTID and ICID. */
771 GITSITE const uIte = RT_BF_MAKE(GITS_BF_ITE_ICID, uIcId)
772 | RT_BF_MAKE(GITS_BF_ITE_INTID, uIntId)
773 | RT_BF_MAKE(GITS_BF_ITE_IS_PHYS, 1)
774 | RT_BF_MAKE(GITS_BF_ITE_VALID, 1);
775 rc = gitsR3IteWrite(pDevIns, uDte, uEventId, uIte);
776 if (RT_SUCCESS(rc))
777 return;
778
779 GITS_CMD_QUEUE_SET_ERR_RET(Ite_Wr_Failed);
780 }
781 else
782 GITS_CMD_QUEUE_SET_ERR_RET(EventId_Invalid);
783
784#undef GITS_CMD_QUEUE_SET_ERR_RET
785}
786
787
788DECL_HIDDEN_CALLBACK(int) gitsR3CmdQueueProcess(PPDMDEVINS pDevIns, PGITSDEV pGitsDev, void *pvBuf, uint32_t cbBuf)
789{
790 Log4Func(("cbBuf=%RU32\n", cbBuf));
791
792 /* Hold the critical section as we could be accessing the device state simultaneously with MMIO accesses. */
793 GIC_CRIT_SECT_ENTER(pDevIns);
794
795 if (gitsCmdQueueCanProcessRequests(pGitsDev))
796 {
797 uint32_t offRead;
798 uint32_t offWrite;
799 bool const fIsEmpty = gitsCmdQueueIsEmptyEx(pGitsDev, &offRead, &offWrite);
800 if (!fIsEmpty)
801 {
802 uint32_t const cCmdQueuePages = RT_BF_GET(pGitsDev->uCmdBaseReg.u, GITS_BF_CTRL_REG_CBASER_SIZE) + 1;
803 uint32_t const cbCmdQueue = cCmdQueuePages << GITS_CMD_QUEUE_PAGE_SHIFT;
804 AssertRelease(cbCmdQueue <= cbBuf); /** @todo Paranoia; make this a debug assert later. */
805
806 /*
807 * Read all the commands from guest memory into our command queue buffer.
808 */
809 int rc;
810 uint32_t cbCmds;
811 RTGCPHYS const GCPhysCmds = pGitsDev->uCmdBaseReg.u & GITS_BF_CTRL_REG_CBASER_PHYS_ADDR_MASK;
812
813 /* Leave the critical section while reading (a potentially large number of) commands from guest memory. */
814 GIC_CRIT_SECT_LEAVE(pDevIns);
815
816 if (offWrite > offRead)
817 {
818 /* The write offset has not wrapped around, read them in one go. */
819 cbCmds = offWrite - offRead;
820 Assert(cbCmds <= cbBuf);
821 rc = PDMDevHlpPhysReadMeta(pDevIns, GCPhysCmds + offRead, pvBuf, cbCmds);
822 }
823 else
824 {
825 /* The write offset has wrapped around, read till end of buffer followed by wrapped-around data. */
826 uint32_t const cbForward = cbCmdQueue - offRead;
827 uint32_t const cbWrapped = offWrite;
828 Assert(cbForward + cbWrapped <= cbBuf);
829 rc = PDMDevHlpPhysReadMeta(pDevIns, GCPhysCmds + offRead, pvBuf, cbForward);
830 if ( RT_SUCCESS(rc)
831 && cbWrapped > 0)
832 rc = PDMDevHlpPhysReadMeta(pDevIns, GCPhysCmds, (void *)((uintptr_t)pvBuf + cbForward), cbWrapped);
833 cbCmds = cbForward + cbWrapped;
834 }
835
836 /*
837 * Process the commands in the buffer.
838 */
839 if (RT_SUCCESS(rc))
840 {
841 /* Indicate to the guest we've fetched all commands. */
842 GIC_CRIT_SECT_ENTER(pDevIns);
843 pGitsDev->uCmdReadReg = offWrite;
844 pGitsDev->uCmdWriteReg &= ~GITS_BF_CTRL_REG_CWRITER_RETRY_MASK;
845
846 /* Don't hold the critical section while processing commands. */
847 GIC_CRIT_SECT_LEAVE(pDevIns);
848
849 uint32_t const cCmds = cbCmds / sizeof(GITSCMD);
850 for (uint32_t idxCmd = 0; idxCmd < cCmds; idxCmd++)
851 {
852 PCGITSCMD pCmd = (PCGITSCMD)((uintptr_t)pvBuf + (idxCmd * sizeof(GITSCMD)));
853 uint8_t const uCmdId = pCmd->common.uCmdId;
854 switch (uCmdId)
855 {
856 case GITS_CMD_ID_MAPC:
857 {
858 /* Map interrupt collection with a target CPU ID. */
859 uint64_t const uDw2 = pCmd->au64[2].u;
860 uint8_t const fValid = RT_BF_GET(uDw2, GITS_BF_CMD_MAPC_DW2_VALID);
861 uint16_t const uTargetCpuId = RT_BF_GET(uDw2, GITS_BF_CMD_MAPC_DW2_RDBASE);
862 uint16_t const uIcId = RT_BF_GET(uDw2, GITS_BF_CMD_MAPC_DW2_IC_ID);
863
864 if (RT_LIKELY(uIcId < RT_ELEMENTS(pGitsDev->aCtes)))
865 {
866 GIC_CRIT_SECT_ENTER(pDevIns);
867 Assert(!RT_BF_GET(pGitsDev->uTypeReg.u, GITS_BF_CTRL_REG_TYPER_PTA));
868 pGitsDev->aCtes[uIcId].idTargetCpu = fValid ? uTargetCpuId : NIL_VMCPUID;
869 GIC_CRIT_SECT_LEAVE(pDevIns);
870 }
871 else
872 gitsCmdQueueSetError(pDevIns, pGitsDev, kGitsDiag_CmdQueue_Cmd_Mapc_Icid_Invalid,
873 false /* fStall */);
874 STAM_COUNTER_INC(&pGitsDev->StatCmdMapc);
875 break;
876 }
877
878 case GITS_CMD_ID_MAPD:
879 {
880 /* Map device ID to an interrupt translation table. */
881 uint32_t const uDevId = RT_BF_GET(pCmd->au64[0].u, GITS_BF_CMD_MAPD_DW0_DEV_ID);
882 uint8_t const cDevIdBits = RT_BF_GET(pCmd->au64[1].u, GITS_BF_CMD_MAPD_DW1_SIZE);
883 bool const fValid = RT_BF_GET(pCmd->au64[2].u, GITS_BF_CMD_MAPD_DW2_VALID);
884 RTGCPHYS const GCPhysItt = pCmd->au64[2].u & GITS_BF_CMD_MAPD_DW2_ITT_ADDR_MASK;
885 if (fValid)
886 {
887 /* We support 32-bits of device ID and hence it cannot be out of range (asserted below). */
888 Assert(sizeof(uDevId) * 8 >= RT_BF_GET(pGitsDev->uTypeReg.u, GITS_BF_CTRL_REG_TYPER_DEV_BITS) + 1);
889
890 /* Check that size is within the supported event ID range. */
891 uint8_t const cEventIdBits = RT_BF_GET(pGitsDev->uTypeReg.u, GITS_BF_CTRL_REG_TYPER_ID_BITS) + 1;
892 if (cDevIdBits <= cEventIdBits)
893 {
894 GITSDTE const uDte = RT_BF_MAKE(GITS_BF_DTE_VALID, 1)
895 | RT_BF_MAKE(GITS_BF_DTE_ITT_RANGE, cDevIdBits)
896 | (GCPhysItt & GITS_BF_DTE_ITT_ADDR_MASK);
897
898 GIC_CRIT_SECT_ENTER(pDevIns);
899 rc = gitsR3DteWrite(pDevIns, pGitsDev, uDevId, uDte);
900 /** @todo Add Device ID to internal cache. */
901 GIC_CRIT_SECT_LEAVE(pDevIns);
902 AssertRC(rc);
903 }
904 else
905 gitsCmdQueueSetError(pDevIns, pGitsDev, kGitsDiag_CmdQueue_Cmd_Mapd_Size_Invalid,
906 false /* fStall */);
907 }
908 else
909 {
910 uint64_t const uDte = 0;
911 GIC_CRIT_SECT_ENTER(pDevIns);
912 rc = gitsR3DteWrite(pDevIns, pGitsDev, uDevId, uDte);
913 GIC_CRIT_SECT_LEAVE(pDevIns);
914 /** @todo Remove Device ID from internal cache. */
915 AssertRC(rc);
916 }
917 STAM_COUNTER_INC(&pGitsDev->StatCmdMapd);
918 break;
919 }
920
921 case GITS_CMD_ID_MAPTI:
922 {
923 /* Map device ID and event ID to corresponding ITE with ICID and the INTID. */
924 uint16_t const uIcId = RT_BF_GET(pCmd->au64[2].u, GITS_BF_CMD_MAPTI_DW2_IC_ID);
925 uint32_t const uDevId = RT_BF_GET(pCmd->au64[0].u, GITS_BF_CMD_MAPTI_DW0_DEV_ID);
926 uint32_t const uEventId = RT_BF_GET(pCmd->au64[1].u, GITS_BF_CMD_MAPTI_DW1_EVENT_ID);
927 uint32_t const uIntId = RT_BF_GET(pCmd->au64[1].u, GITS_BF_CMD_MAPTI_DW1_PHYS_INTID);
928
929 GIC_CRIT_SECT_ENTER(pDevIns);
930 gitsR3CmdMapIntr(pDevIns, pGitsDev, uDevId, uEventId, uIntId, uIcId, true /* fMapti */);
931 GIC_CRIT_SECT_LEAVE(pDevIns);
932 STAM_COUNTER_INC(&pGitsDev->StatCmdMapti);
933 break;
934 }
935
936 case GITS_CMD_ID_MAPI:
937 {
938 /* Map device ID and event ID to corresponding ITE with ICID and the INTID same as the event ID. */
939 uint16_t const uIcId = RT_BF_GET(pCmd->au64[2].u, GITS_BF_CMD_MAPTI_DW2_IC_ID);
940 uint32_t const uDevId = RT_BF_GET(pCmd->au64[0].u, GITS_BF_CMD_MAPTI_DW0_DEV_ID);
941 uint32_t const uEventId = RT_BF_GET(pCmd->au64[1].u, GITS_BF_CMD_MAPTI_DW1_EVENT_ID);
942 uint32_t const uIntId = uEventId;
943
944 GIC_CRIT_SECT_ENTER(pDevIns);
945 gitsR3CmdMapIntr(pDevIns, pGitsDev, uDevId, uEventId, uIntId, uIcId, false /* fMapti */);
946 GIC_CRIT_SECT_LEAVE(pDevIns);
947 STAM_COUNTER_INC(&pGitsDev->StatCmdMapi);
948 break;
949 }
950
951 case GITS_CMD_ID_INV:
952 {
953 /* Reading the table is likely to take the same time as reading just one entry. */
954 gicDistReadLpiConfigTableFromMem(pDevIns);
955 break;
956 }
957
958 case GITS_CMD_ID_SYNC:
959 /* Nothing to do since all previous commands have committed their changes to device state. */
960 STAM_COUNTER_INC(&pGitsDev->StatCmdSync);
961 break;
962
963 case GITS_CMD_ID_INVALL:
964 {
965 /* Reading the table is likely to take the same time as reading just one entry. */
966 uint64_t const uDw2 = pCmd->au64[2].u;
967 uint16_t const uIcId = RT_BF_GET(uDw2, GITS_BF_CMD_INVALL_DW2_IC_ID);
968 PCVMCC pVM = PDMDevHlpGetVM(pDevIns);
969 if (uIcId < RT_ELEMENTS(pGitsDev->aCtes))
970 {
971 if (pGitsDev->aCtes[uIcId].idTargetCpu < pVM->cCpus)
972 gicDistReadLpiConfigTableFromMem(pDevIns);
973 else
974 gitsCmdQueueSetError(pDevIns, pGitsDev, kGitsDiag_CmdQueue_Cmd_Invall_Cte_Unmapped,
975 false /* fStall */);
976 }
977 else
978 gitsCmdQueueSetError(pDevIns, pGitsDev, kGitsDiag_CmdQueue_Cmd_Invall_Icid_Invalid,
979 false /* fStall */);
980 STAM_COUNTER_INC(&pGitsDev->StatCmdInvall);
981 break;
982 }
983
984 default:
985 {
986 /* Record an internal error but do NOT stall queue as we have already advanced the read offset. */
987 gitsCmdQueueSetError(pDevIns, pGitsDev, kGitsDiag_CmdQueue_Basic_Unknown_Cmd, false /* fStall */);
988 AssertReleaseMsgFailed(("Cmd=%#x (%s) idxCmd=%u cCmds=%u offRead=%#RX32 offWrite=%#RX32\n",
989 uCmdId, gitsGetCommandName(uCmdId), idxCmd, cCmds, offRead, offWrite));
990 break;
991 }
992 }
993 }
994 return VINF_SUCCESS;
995 }
996
997 /* Failed to read command queue from the physical address specified by the guest, stall queue and retry later. */
998 gitsCmdQueueSetError(pDevIns, pGitsDev, kGitsDiag_CmdQueue_Basic_Invalid_PhysAddr, true /* fStall */);
999 return VINF_TRY_AGAIN;
1000 }
1001 }
1002
1003 GIC_CRIT_SECT_LEAVE(pDevIns);
1004 return VINF_SUCCESS;
1005}
1006#endif /* IN_RING3 */
1007
1008
1009DECL_HIDDEN_CALLBACK(int) gitsSetLpi(PPDMDEVINS pDevIns, PGITSDEV pGitsDev, uint32_t uDevId, uint32_t uEventId, bool fAsserted)
1010{
1011 /* We support 32-bits of device ID and hence it cannot be out of range (asserted below). */
1012 Assert(sizeof(uDevId) * 8 >= RT_BF_GET(pGitsDev->uTypeReg.u, GITS_BF_CTRL_REG_TYPER_DEV_BITS) + 1);
1013
1014 /** @todo Error recording. */
1015
1016 GIC_CRIT_SECT_ENTER(pDevIns);
1017
1018 bool const fEnabled = RT_BF_GET(pGitsDev->uCtrlReg, GITS_BF_CTRL_REG_CTLR_ENABLED);
1019 if (fEnabled)
1020 {
1021 /* Read the DTE */
1022 GITSDTE uDte;
1023 int rc = gitsR3DteRead(pDevIns, pGitsDev, uDevId, &uDte);
1024 if (RT_SUCCESS(rc))
1025 {
1026 /* Check the DTE is mapped (valid). */
1027 bool const fValid = RT_BF_GET(uDte, GITS_BF_DTE_VALID);
1028 if (fValid)
1029 {
1030 /* Check that the event ID (which is the index) is within range. */
1031 uint32_t const cEntries = RT_BIT_32(RT_BF_GET(uDte, GITS_BF_DTE_ITT_ADDR) + 1);
1032 if (uEventId < cEntries)
1033 {
1034 /* Read the interrupt-translation entry. */
1035 GITSITE uIte = 0;
1036 rc = gitsR3IteRead(pDevIns, uDte, uEventId, &uIte);
1037 if (RT_SUCCESS(rc))
1038 {
1039 /* Check the interrupt ID is within range. */
1040 uint16_t const uIntId = RT_BF_GET(uIte, GITS_BF_ITE_INTID);
1041 uint16_t const uIcId = RT_BF_GET(uIte, GITS_BF_ITE_ICID);
1042 bool const fIsLpiValid = gicDistIsLpiValid(pDevIns, uIntId);
1043 if (fIsLpiValid)
1044 {
1045 /* Check the interrupt collection ID is valid. */
1046 if (uIcId < RT_ELEMENTS(pGitsDev->aCtes))
1047 {
1048 Assert(!RT_BF_GET(pGitsDev->uTypeReg.u, GITS_BF_CTRL_REG_TYPER_PTA));
1049 PCVMCC pVM = PDMDevHlpGetVM(pDevIns);
1050 VMCPUID const idCpu = pGitsDev->aCtes[uIcId].idTargetCpu;
1051
1052 /* Check that the target CPU is valid. */
1053 if (idCpu < pVM->cCpus)
1054 {
1055 /* Set or clear the LPI pending state in the redistributor. */
1056 PVMCPUCC pVCpu = pVM->CTX_SUFF(apCpus)[idCpu];
1057 gicReDistSetLpi(pDevIns, pVCpu, uIntId, fAsserted);
1058 }
1059 }
1060 }
1061 }
1062 }
1063 }
1064 }
1065 }
1066 GIC_CRIT_SECT_LEAVE(pDevIns);
1067 return VINF_SUCCESS;
1068}
1069
1070
1071#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
1072
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