hw/intc/arm_gicv3_its: Implement VMAPP

Implement the GICv4 VMAPP command, which writes an entry to the vPE
table.

For GICv4.1 this command has extra fields in the command packet
and additional behaviour. We define the 4.1-only fields with the
FIELD macro, but only implement the GICv4.0 version of the command.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20220408141550.1271295-10-peter.maydell@linaro.org
This commit is contained in:
Peter Maydell 2022-04-08 15:15:18 +01:00
parent 9de53de60c
commit 0cdf7a5dc8
3 changed files with 102 additions and 0 deletions

View File

@ -61,6 +61,12 @@ typedef struct ITEntry {
uint32_t vpeid;
} ITEntry;
typedef struct VTEntry {
bool valid;
unsigned vptsize;
uint32_t rdbase;
uint64_t vptaddr;
} VTEntry;
/*
* The ITS spec permits a range of CONSTRAINED UNPREDICTABLE options
@ -842,6 +848,85 @@ static ItsCmdResult process_movi(GICv3ITSState *s, const uint64_t *cmdpkt)
return update_ite(s, eventid, &dte, &old_ite) ? CMD_CONTINUE : CMD_STALL;
}
/*
* Update the vPE Table entry at index @vpeid with the entry @vte.
* Returns true on success, false if there was a memory access error.
*/
static bool update_vte(GICv3ITSState *s, uint32_t vpeid, const VTEntry *vte)
{
AddressSpace *as = &s->gicv3->dma_as;
uint64_t entry_addr;
uint64_t vteval = 0;
MemTxResult res = MEMTX_OK;
trace_gicv3_its_vte_write(vpeid, vte->valid, vte->vptsize, vte->vptaddr,
vte->rdbase);
if (vte->valid) {
vteval = FIELD_DP64(vteval, VTE, VALID, 1);
vteval = FIELD_DP64(vteval, VTE, VPTSIZE, vte->vptsize);
vteval = FIELD_DP64(vteval, VTE, VPTADDR, vte->vptaddr);
vteval = FIELD_DP64(vteval, VTE, RDBASE, vte->rdbase);
}
entry_addr = table_entry_addr(s, &s->vpet, vpeid, &res);
if (res != MEMTX_OK) {
return false;
}
if (entry_addr == -1) {
/* No L2 table for this index: discard write and continue */
return true;
}
address_space_stq_le(as, entry_addr, vteval, MEMTXATTRS_UNSPECIFIED, &res);
return res == MEMTX_OK;
}
static ItsCmdResult process_vmapp(GICv3ITSState *s, const uint64_t *cmdpkt)
{
VTEntry vte;
uint32_t vpeid;
if (!its_feature_virtual(s)) {
return CMD_CONTINUE;
}
vpeid = FIELD_EX64(cmdpkt[1], VMAPP_1, VPEID);
vte.rdbase = FIELD_EX64(cmdpkt[2], VMAPP_2, RDBASE);
vte.valid = FIELD_EX64(cmdpkt[2], VMAPP_2, V);
vte.vptsize = FIELD_EX64(cmdpkt[3], VMAPP_3, VPTSIZE);
vte.vptaddr = FIELD_EX64(cmdpkt[3], VMAPP_3, VPTADDR);
trace_gicv3_its_cmd_vmapp(vpeid, vte.rdbase, vte.valid,
vte.vptaddr, vte.vptsize);
/*
* For GICv4.0 the VPT_size field is only 5 bits, whereas we
* define our field macros to include the full GICv4.1 8 bits.
* The range check on VPT_size will catch the cases where
* the guest set the RES0-in-GICv4.0 bits [7:6].
*/
if (vte.vptsize > FIELD_EX64(s->typer, GITS_TYPER, IDBITS)) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: invalid VPT_size 0x%x\n", __func__, vte.vptsize);
return CMD_CONTINUE;
}
if (vte.valid && vte.rdbase >= s->gicv3->num_cpu) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: invalid rdbase 0x%x\n", __func__, vte.rdbase);
return CMD_CONTINUE;
}
if (vpeid >= s->vpet.num_entries) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: VPEID 0x%x out of range (must be less than 0x%x)\n",
__func__, vpeid, s->vpet.num_entries);
return CMD_CONTINUE;
}
return update_vte(s, vpeid, &vte) ? CMD_CONTINUE : CMD_STALL;
}
/*
* Current implementation blocks until all
* commands are processed
@ -963,6 +1048,9 @@ static void process_cmdq(GICv3ITSState *s)
case GITS_CMD_VMAPI:
result = process_vmapti(s, cmdpkt, true);
break;
case GITS_CMD_VMAPP:
result = process_vmapp(s, cmdpkt);
break;
default:
trace_gicv3_its_cmd_unknown(cmd);
break;

View File

@ -329,6 +329,7 @@ FIELD(GITS_TYPER, CIL, 36, 1)
#define GITS_CMD_INVALL 0x0D
#define GITS_CMD_MOVALL 0x0E
#define GITS_CMD_DISCARD 0x0F
#define GITS_CMD_VMAPP 0x29
#define GITS_CMD_VMAPTI 0x2A
#define GITS_CMD_VMAPI 0x2B
@ -377,6 +378,17 @@ FIELD(VMAPTI_1, VPEID, 32, 16)
FIELD(VMAPTI_2, VINTID, 0, 32) /* VMAPTI only */
FIELD(VMAPTI_2, DOORBELL, 32, 32)
/* VMAPP command fields */
FIELD(VMAPP_0, ALLOC, 8, 1) /* GICv4.1 only */
FIELD(VMAPP_0, PTZ, 9, 1) /* GICv4.1 only */
FIELD(VMAPP_0, VCONFADDR, 16, 36) /* GICv4.1 only */
FIELD(VMAPP_1, DEFAULT_DOORBELL, 0, 32) /* GICv4.1 only */
FIELD(VMAPP_1, VPEID, 32, 16)
FIELD(VMAPP_2, RDBASE, 16, 36)
FIELD(VMAPP_2, V, 63, 1)
FIELD(VMAPP_3, VPTSIZE, 0, 8) /* For GICv4.0, bits [7:6] are RES0 */
FIELD(VMAPP_3, VPTADDR, 16, 36)
/*
* 12 bytes Interrupt translation Table Entry size
* as per Table 5.3 in GICv3 spec

View File

@ -189,6 +189,7 @@ gicv3_its_cmd_movall(uint64_t rd1, uint64_t rd2) "GICv3 ITS: command MOVALL RDba
gicv3_its_cmd_movi(uint32_t devid, uint32_t eventid, uint32_t icid) "GICv3 ITS: command MOVI DeviceID 0x%x EventID 0x%x ICID 0x%x"
gicv3_its_cmd_vmapi(uint32_t devid, uint32_t eventid, uint32_t vpeid, uint32_t doorbell) "GICv3 ITS: command VMAPI DeviceID 0x%x EventID 0x%x vPEID 0x%x Dbell_pINTID 0x%x"
gicv3_its_cmd_vmapti(uint32_t devid, uint32_t eventid, uint32_t vpeid, uint32_t vintid, uint32_t doorbell) "GICv3 ITS: command VMAPI DeviceID 0x%x EventID 0x%x vPEID 0x%x vINTID 0x%x Dbell_pINTID 0x%x"
gicv3_its_cmd_vmapp(uint32_t vpeid, uint64_t rdbase, int valid, uint64_t vptaddr, uint32_t vptsize) "GICv3 ITS: command VMAPP vPEID 0x%x RDbase 0x%" PRIx64 " V %d VPT_addr 0x%" PRIx64 " VPT_size 0x%x"
gicv3_its_cmd_unknown(unsigned cmd) "GICv3 ITS: unknown command 0x%x"
gicv3_its_cte_read(uint32_t icid, int valid, uint32_t rdbase) "GICv3 ITS: Collection Table read for ICID 0x%x: valid %d RDBase 0x%x"
gicv3_its_cte_write(uint32_t icid, int valid, uint32_t rdbase) "GICv3 ITS: Collection Table write for ICID 0x%x: valid %d RDBase 0x%x"
@ -199,6 +200,7 @@ gicv3_its_ite_write(uint64_t ittaddr, uint32_t eventid, int valid, int inttype,
gicv3_its_dte_read(uint32_t devid, int valid, uint32_t size, uint64_t ittaddr) "GICv3 ITS: Device Table read for DeviceID 0x%x: valid %d size 0x%x ITTaddr 0x%" PRIx64
gicv3_its_dte_write(uint32_t devid, int valid, uint32_t size, uint64_t ittaddr) "GICv3 ITS: Device Table write for DeviceID 0x%x: valid %d size 0x%x ITTaddr 0x%" PRIx64
gicv3_its_dte_read_fault(uint32_t devid) "GICv3 ITS: Device Table read for DeviceID 0x%x: faulted"
gicv3_its_vte_write(uint32_t vpeid, int valid, uint32_t vptsize, uint64_t vptaddr, uint32_t rdbase) "GICv3 ITS: vPE Table write for vPEID 0x%x: valid %d VPTsize 0x%x VPTaddr 0x%" PRIx64 " RDbase 0x%x"
# armv7m_nvic.c
nvic_recompute_state(int vectpending, int vectpending_prio, int exception_prio) "NVIC state recomputed: vectpending %d vectpending_prio %d exception_prio %d"