VirtualBox

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

Last change on this file since 109212 was 109212, checked in by vboxsync, 10 days ago

GIC: bugref:10877 Add MSI registeration callbacks in PCI ECAM configured when the ITS is enabled. Added invoking of the GIC backend's MSI handler from PDM.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 44.3 KB
Line 
1/* $Id: GITSAll.cpp 109212 2025-05-09 06:25:38Z 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 <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_TRANSLATERR";
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 AssertReleaseMsgFailed(("offReg=%#x uValue=%#RX64 [%u-bit]\n", offReg, uValue, cb << 3));
444}
445
446
447DECL_HIDDEN_CALLBACK(void) gitsInit(PGITSDEV pGitsDev)
448{
449 Log4Func(("\n"));
450
451 /* GITS_CTLR.*/
452 pGitsDev->uCtrlReg = RT_BF_MAKE(GITS_BF_CTRL_REG_CTLR_QUIESCENT, 1);
453
454 /* GITS_TYPER. */
455 pGitsDev->uTypeReg.u = RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_PHYSICAL, 1) /* Physical LPIs supported. */
456 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_VIRTUAL, 0) */ /* Virtual LPIs not supported. */
457 | RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_CCT, 0) /* Collections in memory not supported. */
458 | RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_ITT_ENTRY_SIZE, sizeof(GITSITE) - 1) /* ITE size in bytes minus 1. */
459 | RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_ID_BITS, 31) /* 32-bit event IDs. */
460 | RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_DEV_BITS, 31) /* 32-bit device IDs. */
461 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_SEIS, 0) */ /* Locally generated errors not recommended. */
462 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_PTA, 0) */ /* Target is VCPU ID not address. */
463 | RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_HCC, 255) /* Collection count. */
464 | RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_CID_BITS, 0) /* Collections in memory not supported. */
465 | RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_CIL, 0) /* Collections in memory not supported. */
466 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_VMOVP, 0) */ /* VMOVP not supported. */
467 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_MPAM, 0) */ /* MPAM no supported. */
468 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_VSGI, 0) */ /* VSGI not supported. */
469 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_VMAPP, 0) */ /* VMAPP not supported. */
470 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_SVPET, 0) */ /* SVPET not supported. */
471 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_NID, 0) */ /* NID (doorbell) not supported. */
472 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_UMSI, 0) */ /** @todo Reporting receipt of unmapped MSIs. */
473 /*| RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_UMSI_IRQ, 0) */ /** @todo Generating interrupt on unmapped MSI. */
474 | RT_BF_MAKE(GITS_BF_CTRL_REG_TYPER_INV, 1); /* ITS caches invalidated when clearing
475 GITS_CTLR.Enabled and GITS_BASER<n>.Valid. */
476 Assert(RT_ELEMENTS(pGitsDev->aCtes) >= RT_BF_GET(pGitsDev->uTypeReg.u, GITS_BF_CTRL_REG_TYPER_HCC));
477
478 /* GITS_BASER<n>. */
479 RT_ZERO(pGitsDev->aItsTableRegs);
480 pGitsDev->aItsTableRegs[0].u = RT_BF_MAKE(GITS_BF_CTRL_REG_BASER_ENTRY_SIZE, sizeof(GITSDTE) - 1)
481 | RT_BF_MAKE(GITS_BF_CTRL_REG_BASER_TYPE, GITS_BASER_TYPE_DEVICES);
482
483 /* GITS_CBASER, GITS_CREADR, GITS_CWRITER. */
484 pGitsDev->uCmdBaseReg.u = 0;
485 pGitsDev->uCmdReadReg = 0;
486 pGitsDev->uCmdWriteReg = 0;
487
488 /* Collection Table. */
489 for (unsigned i = 0; i < RT_ELEMENTS(pGitsDev->aCtes); i++)
490 pGitsDev->aCtes[i].idTargetCpu = NIL_VMCPUID;
491
492 /* Misc. stuff. */
493 pGitsDev->cCmdQueueErrors = 0;
494}
495
496
497#ifdef IN_RING3
498DECL_HIDDEN_CALLBACK(void) gitsR3DbgInfo(PCGITSDEV pGitsDev, PCDBGFINFOHLP pHlp)
499{
500 pHlp->pfnPrintf(pHlp, "GIC ITS:\n");
501
502 /* Basic info, GITS_CTLR and GITS_TYPER. */
503 {
504 uint32_t const uCtrlReg = pGitsDev->uCtrlReg;
505 GITSDIAG const enmDiag = pGitsDev->enmDiag;
506 pHlp->pfnPrintf(pHlp, " uArchRev = %u\n", pGitsDev->uArchRev);
507 pHlp->pfnPrintf(pHlp, " Cmd queue errors = %RU64\n", pGitsDev->cCmdQueueErrors);
508 pHlp->pfnPrintf(pHlp, " Last error = %#RX32 (%s)\n", enmDiag, gitsGetDiagDescription(enmDiag));
509 pHlp->pfnPrintf(pHlp, " GITS_CTLR = %#RX32\n", uCtrlReg);
510 pHlp->pfnPrintf(pHlp, " Enabled = %RTbool\n", RT_BF_GET(uCtrlReg, GITS_BF_CTRL_REG_CTLR_ENABLED));
511 pHlp->pfnPrintf(pHlp, " UMSI IRQ = %RTbool\n", RT_BF_GET(uCtrlReg, GITS_BF_CTRL_REG_CTLR_UMSI_IRQ));
512 pHlp->pfnPrintf(pHlp, " Quiescent = %RTbool\n", RT_BF_GET(uCtrlReg, GITS_BF_CTRL_REG_CTLR_QUIESCENT));
513 }
514
515 /* GITS_BASER<n>. */
516 for (unsigned i = 0; i < RT_ELEMENTS(pGitsDev->aItsTableRegs); i++)
517 {
518 static uint32_t const s_acbPageSize[] = { _4K, _16K, _64K, _64K };
519 static const char* const s_apszType[] = { "UnImpl", "Devices", "vPEs", "Intr Collections" };
520 uint64_t const uReg = pGitsDev->aItsTableRegs[i].u;
521 bool const fValid = RT_BOOL(RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_VALID));
522 uint8_t const idxType = RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_TYPE);
523 if ( fValid
524 || idxType != GITS_BASER_TYPE_UNIMPL)
525 {
526 uint16_t const uSize = RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_SIZE);
527 uint16_t const cPages = uSize > 0 ? uSize + 1 : 0;
528 uint8_t const idxPageSize = RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_PAGESIZE);
529 uint64_t const cbItsTable = cPages * s_acbPageSize[idxPageSize];
530 uint8_t const uEntrySize = RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_ENTRY_SIZE);
531 bool const fIndirect = RT_BOOL(RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_INDIRECT));
532 const char *pszType = s_apszType[idxType];
533 pHlp->pfnPrintf(pHlp, " GITS_BASER[%u] = %#RX64\n", i, uReg);
534 pHlp->pfnPrintf(pHlp, " Size = %#x (pages=%u total=%.Rhcb)\n", uSize, cPages, cbItsTable);
535 pHlp->pfnPrintf(pHlp, " Page size = %#x (%.Rhcb)\n", idxPageSize, s_acbPageSize[idxPageSize]);
536 pHlp->pfnPrintf(pHlp, " Shareability = %#x\n", RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_SHAREABILITY));
537 pHlp->pfnPrintf(pHlp, " Phys addr = %#RX64 (addr=%#RX64)\n", uReg & GITS_BF_CTRL_REG_BASER_PHYS_ADDR_MASK,
538 gitsGetBaseRegPhysAddr(uReg));
539 pHlp->pfnPrintf(pHlp, " Entry size = %#x (%u bytes)\n", uEntrySize, uEntrySize > 0 ? uEntrySize + 1 : 0);
540 pHlp->pfnPrintf(pHlp, " Outer cache = %#x\n", RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_OUTER_CACHE));
541 pHlp->pfnPrintf(pHlp, " Type = %#x (%s)\n", idxType, pszType);
542 pHlp->pfnPrintf(pHlp, " Inner cache = %#x\n", RT_BF_GET(uReg, GITS_BF_CTRL_REG_BASER_INNER_CACHE));
543 pHlp->pfnPrintf(pHlp, " Indirect = %RTbool\n", fIndirect);
544 pHlp->pfnPrintf(pHlp, " Valid = %RTbool\n", fValid);
545 }
546 }
547
548 /* GITS_CBASER. */
549 {
550 uint64_t const uReg = pGitsDev->uCmdBaseReg.u;
551 uint8_t const uSize = RT_BF_GET(uReg, GITS_BF_CTRL_REG_CBASER_SIZE);
552 uint16_t const cPages = uSize > 0 ? uSize + 1 : 0;
553 pHlp->pfnPrintf(pHlp, " GITS_CBASER = %#RX64\n", uReg);
554 pHlp->pfnPrintf(pHlp, " Size = %#x (pages=%u total=%.Rhcb)\n", uSize, cPages, _4K * cPages);
555 pHlp->pfnPrintf(pHlp, " Shareability = %#x\n", RT_BF_GET(uReg, GITS_BF_CTRL_REG_CBASER_SHAREABILITY));
556 pHlp->pfnPrintf(pHlp, " Phys addr = %#RX64\n", uReg & GITS_BF_CTRL_REG_CBASER_PHYS_ADDR_MASK);
557 pHlp->pfnPrintf(pHlp, " Outer cache = %#x\n", RT_BF_GET(uReg, GITS_BF_CTRL_REG_CBASER_OUTER_CACHE));
558 pHlp->pfnPrintf(pHlp, " Inner cache = %#x\n", RT_BF_GET(uReg, GITS_BF_CTRL_REG_CBASER_INNER_CACHE));
559 pHlp->pfnPrintf(pHlp, " Valid = %RTbool\n", RT_BF_GET(uReg, GITS_BF_CTRL_REG_CBASER_VALID));
560 }
561
562 /* GITS_CREADR. */
563 {
564 uint32_t const uReg = pGitsDev->uCmdReadReg;
565 pHlp->pfnPrintf(pHlp, " GITS_CREADR = 0x%05RX32 (stalled=%RTbool offset=%RU32)\n", uReg,
566 RT_BF_GET(uReg, GITS_BF_CTRL_REG_CREADR_STALLED), uReg & GITS_BF_CTRL_REG_CREADR_OFFSET_MASK);
567 }
568
569 /* GITS_CWRITER. */
570 {
571 uint32_t const uReg = pGitsDev->uCmdWriteReg;
572 pHlp->pfnPrintf(pHlp, " GITS_CWRITER = 0x%05RX32 ( retry=%RTbool offset=%RU32)\n", uReg,
573 RT_BF_GET(uReg, GITS_BF_CTRL_REG_CWRITER_RETRY), uReg & GITS_BF_CTRL_REG_CWRITER_OFFSET_MASK);
574 }
575
576 /* Interrupt Collection Table. */
577 {
578 pHlp->pfnPrintf(pHlp, " Collection Table:\n");
579 bool fHasValidCtes = false;
580 for (unsigned i = 0; i < RT_ELEMENTS(pGitsDev->aCtes); i++)
581 {
582 VMCPUID const idTargetCpu = pGitsDev->aCtes[i].idTargetCpu;
583 if (idTargetCpu != NIL_VMCPUID)
584 {
585 pHlp->pfnPrintf(pHlp, " [%3u] = %RU32\n", i, idTargetCpu);
586 fHasValidCtes = true;
587 }
588 }
589 if (!fHasValidCtes)
590 pHlp->pfnPrintf(pHlp, " Empty (no valid entries)\n");
591 }
592}
593
594
595#if 0
596static void gitsR3DteCacheAdd(PGITSDEV pGitsDev, uint32_t uDevId, RTGCPHYS GCPhysItt, uint8_t cDevIdBits, PGITSDTE pDte)
597{
598 pDte->fValid = 1;
599 pDte->afPadding = 0;
600 pDte->uDevId = uDevId;
601 pDte->GCPhysItt = GCPhysItt;
602 pDte->cbItt = RT_BIT_32(cDevIdBits) - 1;
603
604 unsigned idxFree = 0;
605 for (unsigned i = 0; i < RT_ELEMENTS(pGitsDev->aDtes); i++)
606 {
607 PCGITSDTE pCurDte = &pGitsDev->aDtes[i];
608 if (!pCurDte->fValid)
609 {
610 idxFree = i;
611 break;
612 }
613 }
614 memcpy(&pGitsDev->aDtes[idxFree], pDte, sizeof(*pDte));
615}
616
617
618static void gitsR3DteCacheRemove(PGITSDEV pGitsDev, uint32_t uDevId)
619{
620 for (unsigned i = 0; i < RT_ELEMENTS(pGitsDev->aDtes); i++)
621 {
622 PGITSDTE pCurDte = &pGitsDev->aDtes[i];
623 if (pCurDte->uDevId == uDevId)
624 {
625 RT_ZERO(*pCurDte);
626 return;
627 }
628 }
629}
630#endif
631
632
633static int gitsR3DteGetAddr(PPDMDEVINS pDevIns, PGITSDEV pGitsDev, uint32_t uDevId, PRTGCPHYS pGCPhysDte)
634{
635 uint64_t const uBaseReg = pGitsDev->aItsTableRegs[0].u;
636 bool const fIndirect = RT_BF_GET(uBaseReg, GITS_BF_CTRL_REG_BASER_INDIRECT);
637 RTGCPHYS GCPhysDevTable = gitsGetBaseRegPhysAddr(uBaseReg);
638 if (!fIndirect)
639 {
640 *pGCPhysDte = GCPhysDevTable + uDevId * sizeof(GITSDTE);
641 return VINF_SUCCESS;
642 }
643
644 RTGCPHYS offDte = 0;
645 static uint32_t const s_acbPageSizes[] = { _4K, _16K, _64K, _64K };
646 static uint64_t const s_auPhysAddrMasks[] =
647 {
648 UINT64_C(0x000ffffffffff000), /* 4K bits[51:12] */
649 UINT64_C(0x000fffffffffc000), /* 16K bits[51:14] */
650 UINT64_C(0x000fffffffff0000), /* 64K bits[51:16] */
651 UINT64_C(0x000fffffffff0000) /* 64K bits[51:16] */
652 };
653
654 uint8_t const idxPageSize = RT_BF_GET(uBaseReg, GITS_BF_CTRL_REG_BASER_PAGESIZE);
655 uint32_t const cbPage = s_acbPageSizes[idxPageSize];
656
657 /* Read the the level 1 table device-table entry. */
658 uint32_t const cLevel1Entries = cbPage / GITS_ITE_INDIRECT_LVL1_SIZE;
659 RTGCPHYS const offLevel1Dte = (uDevId % cLevel1Entries) * GITS_ITE_INDIRECT_LVL1_SIZE;
660 uint64_t uLevel1Dte = 0;
661 int rc = PDMDevHlpPhysReadMeta(pDevIns, GCPhysDevTable + offLevel1Dte, &uLevel1Dte, sizeof(uLevel1Dte));
662 if (RT_SUCCESS(rc))
663 {
664 /* Check if the entry is valid. */
665 bool const fValid = RT_BF_GET(uLevel1Dte, GITS_BF_ITE_INDIRECT_LVL1_4K_VALID);
666 if (fValid)
667 {
668 /* Compute the physical address of the device-table entry from the level 1 entry. */
669 uint32_t const cEntries = cbPage / sizeof(GITSDTE);
670 GCPhysDevTable = uLevel1Dte & s_auPhysAddrMasks[idxPageSize];
671 offDte = (uDevId % cEntries) * sizeof(GITSDTE);
672
673 *pGCPhysDte = GCPhysDevTable + offDte;
674 return VINF_SUCCESS;
675 }
676 rc = VERR_NOT_FOUND;
677 }
678
679 /* Something went wrong (usually shouldn't happen but could be faulty/misbehaving guest). */
680 *pGCPhysDte = NIL_RTGCPHYS;
681 return rc;
682}
683
684
685static int gitsR3DteRead(PPDMDEVINS pDevIns, PGITSDEV pGitsDev, uint32_t uDevId, GITSDTE *puDte)
686{
687 RTGCPHYS GCPhysDte;
688 int const rc = gitsR3DteGetAddr(pDevIns, pGitsDev, uDevId, &GCPhysDte);
689 if (RT_SUCCESS(rc))
690 return PDMDevHlpPhysReadMeta(pDevIns, GCPhysDte, (void *)puDte, sizeof(*puDte));
691 AssertMsgFailed(("Failed to get device-table entry address for device ID %#RX32 rc=%Rrc\n", uDevId, rc));
692 return rc;
693}
694
695
696static int gitsR3DteWrite(PPDMDEVINS pDevIns, PGITSDEV pGitsDev, uint32_t uDevId, GITSDTE uDte)
697{
698 RTGCPHYS GCPhysDte;
699 int const rc = gitsR3DteGetAddr(pDevIns, pGitsDev, uDevId, &GCPhysDte);
700 if (RT_SUCCESS(rc))
701 return PDMDevHlpPhysWriteMeta(pDevIns, GCPhysDte, (const void *)&uDte, sizeof(uDte));
702 AssertMsgFailed(("Failed to get device-table entry address for device ID %#RX32 rc=%Rrc\n", uDevId, rc));
703 return rc;
704}
705
706
707static int gitsR3IteWrite(PPDMDEVINS pDevIns, GITSDTE uDte, uint32_t uEventId, GITSITE uIte)
708{
709 RTGCPHYS const GCPhysIntrTable = uDte & GITS_BF_DTE_ITT_ADDR_MASK;
710 RTGCPHYS const GCPhysIte = GCPhysIntrTable + uEventId * sizeof(GITSITE);
711 return PDMDevHlpPhysWriteMeta(pDevIns, GCPhysIte, (const void *)&uIte, sizeof(uIte));
712}
713
714
715static void gitsR3CmdMapIntr(PPDMDEVINS pDevIns, PGITSDEV pGitsDev, uint32_t uDevId, uint32_t uEventId, uint16_t uIntId,
716 uint16_t uIcId, bool fMapti)
717{
718#define GITS_CMD_QUEUE_SET_ERR_RET(a_enmDiagSuffix) \
719 do \
720 { \
721 gitsCmdQueueSetError(pDevIns, pGitsDev, \
722 fMapti ? kGitsDiag_CmdQueue_Cmd_ ## Mapti_ ## a_enmDiagSuffix \
723 : kGitsDiag_CmdQueue_Cmd_ ## Mapi_ ## a_enmDiagSuffix, false /* fStall */); \
724 return; \
725 } while (0)
726
727 /* We support 32-bits of device ID and hence it cannot be out of range (asserted below). */
728 Assert(sizeof(uDevId) * 8 >= RT_BF_GET(pGitsDev->uTypeReg.u, GITS_BF_CTRL_REG_TYPER_DEV_BITS) + 1);
729
730 /* Validate ICID. */
731 if (uIcId < RT_ELEMENTS(pGitsDev->aCtes))
732 { /* likely */ }
733 else
734 GITS_CMD_QUEUE_SET_ERR_RET(IcId_Invalid);
735
736 /* Validate LPI INTID. */
737 if (gicDistIsLpiValid(pDevIns, uIntId))
738 { /* likely */ }
739 else
740 GITS_CMD_QUEUE_SET_ERR_RET(Lpi_Invalid);
741
742 /* Read the device-table entry. */
743 GITSDTE uDte = 0;
744 int rc = gitsR3DteRead(pDevIns, pGitsDev, uDevId, &uDte);
745 if (RT_SUCCESS(rc))
746 { /* likely */ }
747 else
748 GITS_CMD_QUEUE_SET_ERR_RET(Dte_Rd_Failed);
749
750 /* Check that the device ID mapping is valid. */
751 bool const fValid = RT_BF_GET(uDte, GITS_BF_DTE_VALID);
752 if (fValid)
753 { /* likely */ }
754 else
755 GITS_CMD_QUEUE_SET_ERR_RET(DevId_Unmapped);
756
757 /* Check that the event ID (which is the index) is within range. */
758 uint32_t const cEntries = RT_BIT_32(RT_BF_GET(uDte, GITS_BF_DTE_ITT_ADDR) + 1);
759 if (uEventId < cEntries)
760 {
761 /* Write the interrupt-translation entry mapping event ID with INTID and ICID. */
762 GITSITE const uIte = RT_BF_MAKE(GITS_BF_ITE_ICID, uIcId)
763 | RT_BF_MAKE(GITS_BF_ITE_INTID, uIntId)
764 | RT_BF_MAKE(GITS_BF_ITE_IS_PHYS, 1)
765 | RT_BF_MAKE(GITS_BF_ITE_VALID, 1);
766 rc = gitsR3IteWrite(pDevIns, uDte, uEventId, uIte);
767 if (RT_SUCCESS(rc))
768 return;
769
770 GITS_CMD_QUEUE_SET_ERR_RET(Ite_Wr_Failed);
771 }
772 else
773 GITS_CMD_QUEUE_SET_ERR_RET(EventId_Invalid);
774
775#undef GITS_CMD_QUEUE_SET_ERR_RET
776}
777
778
779DECL_HIDDEN_CALLBACK(int) gitsR3CmdQueueProcess(PPDMDEVINS pDevIns, PGITSDEV pGitsDev, void *pvBuf, uint32_t cbBuf)
780{
781 Log4Func(("cbBuf=%RU32\n", cbBuf));
782
783 /* Hold the critical section as we could be accessing the device state simultaneously with MMIO accesses. */
784 GIC_CRIT_SECT_ENTER(pDevIns);
785
786 if (gitsCmdQueueCanProcessRequests(pGitsDev))
787 {
788 uint32_t offRead;
789 uint32_t offWrite;
790 bool const fIsEmpty = gitsCmdQueueIsEmptyEx(pGitsDev, &offRead, &offWrite);
791 if (!fIsEmpty)
792 {
793 uint32_t const cCmdQueuePages = RT_BF_GET(pGitsDev->uCmdBaseReg.u, GITS_BF_CTRL_REG_CBASER_SIZE) + 1;
794 uint32_t const cbCmdQueue = cCmdQueuePages << GITS_CMD_QUEUE_PAGE_SHIFT;
795 AssertRelease(cbCmdQueue <= cbBuf); /** @todo Paranoia; make this a debug assert later. */
796
797 /*
798 * Read all the commands from guest memory into our command queue buffer.
799 */
800 int rc;
801 uint32_t cbCmds;
802 RTGCPHYS const GCPhysCmds = pGitsDev->uCmdBaseReg.u & GITS_BF_CTRL_REG_CBASER_PHYS_ADDR_MASK;
803
804 /* Leave the critical section while reading (a potentially large number of) commands from guest memory. */
805 GIC_CRIT_SECT_LEAVE(pDevIns);
806
807 if (offWrite > offRead)
808 {
809 /* The write offset has not wrapped around, read them in one go. */
810 cbCmds = offWrite - offRead;
811 Assert(cbCmds <= cbBuf);
812 rc = PDMDevHlpPhysReadMeta(pDevIns, GCPhysCmds + offRead, pvBuf, cbCmds);
813 }
814 else
815 {
816 /* The write offset has wrapped around, read till end of buffer followed by wrapped-around data. */
817 uint32_t const cbForward = cbCmdQueue - offRead;
818 uint32_t const cbWrapped = offWrite;
819 Assert(cbForward + cbWrapped <= cbBuf);
820 rc = PDMDevHlpPhysReadMeta(pDevIns, GCPhysCmds + offRead, pvBuf, cbForward);
821 if ( RT_SUCCESS(rc)
822 && cbWrapped > 0)
823 rc = PDMDevHlpPhysReadMeta(pDevIns, GCPhysCmds, (void *)((uintptr_t)pvBuf + cbForward), cbWrapped);
824 cbCmds = cbForward + cbWrapped;
825 }
826
827 /*
828 * Process the commands in the buffer.
829 */
830 if (RT_SUCCESS(rc))
831 {
832 /* Indicate to the guest we've fetched all commands. */
833 GIC_CRIT_SECT_ENTER(pDevIns);
834 pGitsDev->uCmdReadReg = offWrite;
835 pGitsDev->uCmdWriteReg &= ~GITS_BF_CTRL_REG_CWRITER_RETRY_MASK;
836
837 /* Don't hold the critical section while processing commands. */
838 GIC_CRIT_SECT_LEAVE(pDevIns);
839
840 uint32_t const cCmds = cbCmds / sizeof(GITSCMD);
841 for (uint32_t idxCmd = 0; idxCmd < cCmds; idxCmd++)
842 {
843 PCGITSCMD pCmd = (PCGITSCMD)((uintptr_t)pvBuf + (idxCmd * sizeof(GITSCMD)));
844 uint8_t const uCmdId = pCmd->common.uCmdId;
845 switch (uCmdId)
846 {
847 case GITS_CMD_ID_MAPC:
848 {
849 /* Map interrupt collection with a target CPU ID. */
850 uint64_t const uDw2 = pCmd->au64[2].u;
851 uint8_t const fValid = RT_BF_GET(uDw2, GITS_BF_CMD_MAPC_DW2_VALID);
852 uint16_t const uTargetCpuId = RT_BF_GET(uDw2, GITS_BF_CMD_MAPC_DW2_RDBASE);
853 uint16_t const uIcId = RT_BF_GET(uDw2, GITS_BF_CMD_MAPC_DW2_IC_ID);
854
855 if (RT_LIKELY(uIcId < RT_ELEMENTS(pGitsDev->aCtes)))
856 {
857 GIC_CRIT_SECT_ENTER(pDevIns);
858 Assert(!RT_BF_GET(pGitsDev->uTypeReg.u, GITS_BF_CTRL_REG_TYPER_PTA));
859 pGitsDev->aCtes[uIcId].idTargetCpu = fValid ? uTargetCpuId : NIL_VMCPUID;
860 GIC_CRIT_SECT_LEAVE(pDevIns);
861 }
862 else
863 gitsCmdQueueSetError(pDevIns, pGitsDev, kGitsDiag_CmdQueue_Cmd_Mapc_Icid_Invalid,
864 false /* fStall */);
865 STAM_COUNTER_INC(&pGitsDev->StatCmdMapc);
866 break;
867 }
868
869 case GITS_CMD_ID_MAPD:
870 {
871 /* Map device ID to an interrupt translation table. */
872 uint32_t const uDevId = RT_BF_GET(pCmd->au64[0].u, GITS_BF_CMD_MAPD_DW0_DEV_ID);
873 uint8_t const cDevIdBits = RT_BF_GET(pCmd->au64[1].u, GITS_BF_CMD_MAPD_DW1_SIZE);
874 bool const fValid = RT_BF_GET(pCmd->au64[2].u, GITS_BF_CMD_MAPD_DW2_VALID);
875 RTGCPHYS const GCPhysItt = pCmd->au64[2].u & GITS_BF_CMD_MAPD_DW2_ITT_ADDR_MASK;
876 if (fValid)
877 {
878 /* We support 32-bits of device ID and hence it cannot be out of range (asserted below). */
879 Assert(sizeof(uDevId) * 8 >= RT_BF_GET(pGitsDev->uTypeReg.u, GITS_BF_CTRL_REG_TYPER_DEV_BITS) + 1);
880
881 /* Check that size is within the supported event ID range. */
882 uint8_t const cEventIdBits = RT_BF_GET(pGitsDev->uTypeReg.u, GITS_BF_CTRL_REG_TYPER_ID_BITS) + 1;
883 if (cDevIdBits <= cEventIdBits)
884 {
885 uint64_t const uDte = RT_BF_MAKE(GITS_BF_DTE_VALID, 1)
886 | RT_BF_MAKE(GITS_BF_DTE_ITT_RANGE, cDevIdBits)
887 | (GCPhysItt & GITS_BF_DTE_ITT_ADDR_MASK);
888
889 GIC_CRIT_SECT_ENTER(pDevIns);
890 rc = gitsR3DteWrite(pDevIns, pGitsDev, uDevId, uDte);
891 /** @todo Add Device ID to internal cache. */
892 GIC_CRIT_SECT_LEAVE(pDevIns);
893 AssertRC(rc);
894 }
895 else
896 gitsCmdQueueSetError(pDevIns, pGitsDev, kGitsDiag_CmdQueue_Cmd_Mapd_Size_Invalid,
897 false /* fStall */);
898 }
899 else
900 {
901 uint64_t const uDte = 0;
902 GIC_CRIT_SECT_ENTER(pDevIns);
903 rc = gitsR3DteWrite(pDevIns, pGitsDev, uDevId, uDte);
904 GIC_CRIT_SECT_LEAVE(pDevIns);
905 /** @todo Remove Device ID from internal cache. */
906 AssertRC(rc);
907 }
908 STAM_COUNTER_INC(&pGitsDev->StatCmdMapd);
909 break;
910 }
911
912 case GITS_CMD_ID_MAPTI:
913 {
914 /* Map device ID and event ID to corresponding ITE with ICID and the INTID. */
915 uint16_t const uIcId = RT_BF_GET(pCmd->au64[2].u, GITS_BF_CMD_MAPTI_DW2_IC_ID);
916 uint32_t const uDevId = RT_BF_GET(pCmd->au64[0].u, GITS_BF_CMD_MAPTI_DW0_DEV_ID);
917 uint32_t const uEventId = RT_BF_GET(pCmd->au64[1].u, GITS_BF_CMD_MAPTI_DW1_EVENT_ID);
918 uint32_t const uIntId = RT_BF_GET(pCmd->au64[1].u, GITS_BF_CMD_MAPTI_DW1_PHYS_INTID);
919
920 GIC_CRIT_SECT_ENTER(pDevIns);
921 gitsR3CmdMapIntr(pDevIns, pGitsDev, uDevId, uEventId, uIntId, uIcId, true /* fMapti */);
922 GIC_CRIT_SECT_LEAVE(pDevIns);
923 STAM_COUNTER_INC(&pGitsDev->StatCmdMapti);
924 break;
925 }
926
927 case GITS_CMD_ID_MAPI:
928 {
929 /* Map device ID and event ID to corresponding ITE with ICID and the INTID same as the event ID. */
930 uint16_t const uIcId = RT_BF_GET(pCmd->au64[2].u, GITS_BF_CMD_MAPTI_DW2_IC_ID);
931 uint32_t const uDevId = RT_BF_GET(pCmd->au64[0].u, GITS_BF_CMD_MAPTI_DW0_DEV_ID);
932 uint32_t const uEventId = RT_BF_GET(pCmd->au64[1].u, GITS_BF_CMD_MAPTI_DW1_EVENT_ID);
933 uint32_t const uIntId = uEventId;
934
935 GIC_CRIT_SECT_ENTER(pDevIns);
936 gitsR3CmdMapIntr(pDevIns, pGitsDev, uDevId, uEventId, uIntId, uIcId, false /* fMapti */);
937 GIC_CRIT_SECT_LEAVE(pDevIns);
938 STAM_COUNTER_INC(&pGitsDev->StatCmdMapti);
939 break;
940 }
941
942 case GITS_CMD_ID_INV:
943 {
944 /* Reading the table is likely to take the same time as reading just one entry. */
945 gicDistReadLpiConfigTableFromMem(pDevIns);
946 break;
947 }
948
949 case GITS_CMD_ID_SYNC:
950 /* Nothing to do since all previous commands have committed their changes to device state. */
951 STAM_COUNTER_INC(&pGitsDev->StatCmdSync);
952 break;
953
954 case GITS_CMD_ID_INVALL:
955 {
956 /* Reading the table is likely to take the same time as reading just one entry. */
957 uint64_t const uDw2 = pCmd->au64[2].u;
958 uint16_t const uIcId = RT_BF_GET(uDw2, GITS_BF_CMD_INVALL_DW2_IC_ID);
959 PCVMCC pVM = PDMDevHlpGetVM(pDevIns);
960 if (uIcId < RT_ELEMENTS(pGitsDev->aCtes))
961 {
962 if (pGitsDev->aCtes[uIcId].idTargetCpu < pVM->cCpus)
963 gicDistReadLpiConfigTableFromMem(pDevIns);
964 else
965 gitsCmdQueueSetError(pDevIns, pGitsDev, kGitsDiag_CmdQueue_Cmd_Invall_Cte_Unmapped,
966 false /* fStall */);
967 }
968 else
969 gitsCmdQueueSetError(pDevIns, pGitsDev, kGitsDiag_CmdQueue_Cmd_Invall_Icid_Invalid,
970 false /* fStall */);
971 STAM_COUNTER_INC(&pGitsDev->StatCmdInvall);
972 break;
973 }
974
975 default:
976 {
977 /* Record an internal error but do NOT stall queue as we have already advanced the read offset. */
978 gitsCmdQueueSetError(pDevIns, pGitsDev, kGitsDiag_CmdQueue_Basic_Unknown_Cmd, false /* fStall */);
979 AssertReleaseMsgFailed(("Cmd=%#x (%s) idxCmd=%u cCmds=%u offRead=%#RX32 offWrite=%#RX32\n",
980 uCmdId, gitsGetCommandName(uCmdId), idxCmd, cCmds, offRead, offWrite));
981 break;
982 }
983 }
984 }
985 return VINF_SUCCESS;
986 }
987
988 /* Failed to read command queue from the physical address specified by the guest, stall queue and retry later. */
989 gitsCmdQueueSetError(pDevIns, pGitsDev, kGitsDiag_CmdQueue_Basic_Invalid_PhysAddr, true /* fStall */);
990 return VINF_TRY_AGAIN;
991 }
992 }
993
994 GIC_CRIT_SECT_LEAVE(pDevIns);
995 return VINF_SUCCESS;
996}
997#endif /* IN_RING3 */
998
999
1000/**
1001 * @interface_method_impl{PDMGICBACKEND,pfnSendMsi}
1002 */
1003DECL_HIDDEN_CALLBACK(int) gitsSendMsi(PVMCC pVM, PCIBDF uBusDevFn, PCMSIMSG pMsi, uint32_t uTagSrc)
1004{
1005 AssertPtrReturn(pMsi, VERR_INVALID_PARAMETER);
1006 Log4Func(("uBusDevFn=%#RX32 Msi.Addr=%#RX64 Msi.Data=%#RX32\n", uBusDevFn, pMsi->Addr.u64, pMsi->Data.u32));
1007 RT_NOREF(pVM, uBusDevFn, pMsi, uTagSrc);
1008 AssertMsgFailed(("uBusDevFn=%#RX32 Msi.Addr=%#RX64 Msi.Data=%#RX32\n", uBusDevFn, pMsi->Addr.u64, pMsi->Data.u32));
1009 return VERR_NOT_IMPLEMENTED;
1010}
1011
1012#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
1013
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