mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-01 23:03:54 -06:00
target-arm queue:
* Update copyright dates to 2022 * hw/armv7m: Fix broken VMStateDescription * hw/char/exynos4210_uart: Fix crash on trying to load VM state * rtc: Move RTC function prototypes to their own header * xlnx-versal-virt: Support PMC SLCR * xlnx-versal-virt: Support OSPI flash memory controller * scripts: Explain the difference between linux-headers and standard-headers * target/arm: Log CPU index in 'Taking exception' log * arm_gicv3_its: Various bugfixes and cleanups * arm_gicv3_its: Implement the missing MOVI and MOVALL commands * ast2600: Fix address mapping of second SPI controller * target/arm: Use correct entrypoint for SVC taken from Hyp to Hyp -----BEGIN PGP SIGNATURE----- iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAmH0C+AZHHBldGVyLm1h eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3gG4D/9biXPVdkOd7lIslRX0ihRg AZkZrMNk6VF/MW6xJNVWWd+44cyjLopFqF5dS+Vjebt7pEtZvxY0K5mYmzClk6lg 2U89gWuLEDJDKNVfKAmsmj24Os4xRj4sJPq/Mee8lsBdOAwEQ3C36p0RnWGBcTJN 9VfzRMSGvdjQFJjGAaro078zrA1Q11msA4BbLht+YGTE1aeyryyfF/qGSRlrlTn8 +r0ZWBD4ttz8IsqSLtnpQvT6EbL79w0jBywVauVzCOGQGpti3HdHJNYR7cKgTMja Hffx6f6iv/O4SAUUGS0WMWdfW/MEVxOFxJ7Zc2twGqDMuVWlFiLT0X1MZuHi0FpG CjbhTsvJIrKom1Ib+LPkWscrlHHEf0cvME0WokErLOJDXvbqKj04oOkpQmqUIv0+ 5j7o4mlQFuLXIyzcrBZxmwT/Ekg8KZA8aUR0ddUd0vBmGMdO2En/c4Qr/x4H2gXH HL/18oPRaSV6mP08mxcda+hJ9m5MC+7l0+KKoDfaPM9d4hl5StI0zTlH+5ffbK+m UWthMnrrZw2ZU8AzGPZxOAW5K5S3XOso5Z9credkRGuSDriaGuNY0s5gSvNawZGe ioIrUl50t+5/o2+tba7FA2ePiGeC9/zS671zHG9Rdpe86JpJXCzWO7OYiVulV3Yu dmQYrhgnUqNjh3SAiXUFVA== =m7N5 -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20220128' into staging target-arm queue: * Update copyright dates to 2022 * hw/armv7m: Fix broken VMStateDescription * hw/char/exynos4210_uart: Fix crash on trying to load VM state * rtc: Move RTC function prototypes to their own header * xlnx-versal-virt: Support PMC SLCR * xlnx-versal-virt: Support OSPI flash memory controller * scripts: Explain the difference between linux-headers and standard-headers * target/arm: Log CPU index in 'Taking exception' log * arm_gicv3_its: Various bugfixes and cleanups * arm_gicv3_its: Implement the missing MOVI and MOVALL commands * ast2600: Fix address mapping of second SPI controller * target/arm: Use correct entrypoint for SVC taken from Hyp to Hyp # gpg: Signature made Fri 28 Jan 2022 15:29:36 GMT # gpg: using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE # gpg: issuer "peter.maydell@linaro.org" # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@gmail.com>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [ultimate] # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * remotes/pmaydell/tags/pull-target-arm-20220128: (32 commits) target/arm: Use correct entrypoint for SVC taken from Hyp to Hyp hw/arm: ast2600: Fix address mapping of second SPI controller hw/intc/arm_gicv3_its: Implement MOVI hw/intc/arm_gicv3_its: Implement MOVALL hw/intc/arm_gicv3_its: Check table bounds against correct limit hw/intc/arm_gicv3_its: Make GITS_BASER<n> RAZ/WI for unimplemented registers hw/intc/arm_gicv3_its: Provide read accessor for translation_ops hw/intc/arm_gicv3: Set GICR_CTLR.CES if LPIs are supported hw/intc/arm_gicv3_redist: Remove unnecessary zero checks hw/intc/arm_gicv3_its: Sort ITS command list into numeric order hw/intc/arm_gicv3: Honour GICD_CTLR.EnableGrp1NS for LPIs hw/intc/arm_gicv3_its: Don't clear GITS_CWRITER on writes to GITS_CBASER hw/intc/arm_gicv3_its: Don't clear GITS_CREADR when GITS_CTLR.ENABLED is set hw/intc/arm_gicv3: Initialise dma_as in GIC, not ITS hw/intc/arm_gicv3_its: Add tracepoints target/arm: Log CPU index in 'Taking exception' log scripts: Explain the difference between linux-headers and standard-headers MAINTAINERS: Remove myself (for raspi). MAINTAINERS: Add an entry for Xilinx Versal OSPI hw/arm/xlnx-versal-virt: Connect mt35xu01g flashes to the OSPI ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
95a6af2a00
52 changed files with 4300 additions and 74 deletions
|
@ -166,6 +166,7 @@ static void gicv3_redist_update_noirqset(GICv3CPUState *cs)
|
|||
}
|
||||
|
||||
if ((cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) && cs->gic->lpi_enable &&
|
||||
(cs->gic->gicd_ctlr & GICD_CTLR_EN_GRP1NS) &&
|
||||
(cs->hpplpi.prio != 0xff)) {
|
||||
if (irqbetter(cs, cs->hpplpi.irq, cs->hpplpi.prio)) {
|
||||
cs->hppi.irq = cs->hpplpi.irq;
|
||||
|
|
|
@ -357,6 +357,11 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp)
|
|||
return;
|
||||
}
|
||||
|
||||
if (s->lpi_enable) {
|
||||
address_space_init(&s->dma_as, s->dma,
|
||||
"gicv3-its-sysmem");
|
||||
}
|
||||
|
||||
s->cpu = g_new0(GICv3CPUState, s->num_cpu);
|
||||
|
||||
for (i = 0; i < s->num_cpu; i++) {
|
||||
|
@ -424,6 +429,10 @@ static void arm_gicv3_common_reset(DeviceState *dev)
|
|||
|
||||
cs->level = 0;
|
||||
cs->gicr_ctlr = 0;
|
||||
if (s->lpi_enable) {
|
||||
/* Our implementation supports clearing GICR_CTLR.EnableLPIs */
|
||||
cs->gicr_ctlr |= GICR_CTLR_CES;
|
||||
}
|
||||
cs->gicr_statusr[GICV3_S] = 0;
|
||||
cs->gicr_statusr[GICV3_NS] = 0;
|
||||
cs->gicr_waker = GICR_WAKER_ProcessorSleep | GICR_WAKER_ChildrenAsleep;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "trace.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/intc/arm_gicv3_its_common.h"
|
||||
#include "gicv3_internal.h"
|
||||
|
@ -255,10 +256,10 @@ static ItsCmdResult process_its_cmd(GICv3ITSState *s, uint64_t value,
|
|||
|
||||
eventid = (value & EVENTID_MASK);
|
||||
|
||||
if (devid >= s->dt.num_ids) {
|
||||
if (devid >= s->dt.num_entries) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: invalid command attributes: devid %d>=%d",
|
||||
__func__, devid, s->dt.num_ids);
|
||||
__func__, devid, s->dt.num_entries);
|
||||
return CMD_CONTINUE;
|
||||
}
|
||||
|
||||
|
@ -299,7 +300,7 @@ static ItsCmdResult process_its_cmd(GICv3ITSState *s, uint64_t value,
|
|||
return CMD_CONTINUE;
|
||||
}
|
||||
|
||||
if (icid >= s->ct.num_ids) {
|
||||
if (icid >= s->ct.num_entries) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: invalid ICID 0x%x in ITE (table corrupted?)\n",
|
||||
__func__, icid);
|
||||
|
@ -383,10 +384,10 @@ static ItsCmdResult process_mapti(GICv3ITSState *s, uint64_t value,
|
|||
|
||||
icid = value & ICID_MASK;
|
||||
|
||||
if (devid >= s->dt.num_ids) {
|
||||
if (devid >= s->dt.num_entries) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: invalid command attributes: devid %d>=%d",
|
||||
__func__, devid, s->dt.num_ids);
|
||||
__func__, devid, s->dt.num_entries);
|
||||
return CMD_CONTINUE;
|
||||
}
|
||||
|
||||
|
@ -399,7 +400,7 @@ static ItsCmdResult process_mapti(GICv3ITSState *s, uint64_t value,
|
|||
num_eventids = 1ULL << (FIELD_EX64(dte, DTE, SIZE) + 1);
|
||||
num_intids = 1ULL << (GICD_TYPER_IDBITS + 1);
|
||||
|
||||
if ((icid >= s->ct.num_ids)
|
||||
if ((icid >= s->ct.num_entries)
|
||||
|| !dte_valid || (eventid >= num_eventids) ||
|
||||
(((pIntid < GICV3_LPI_INTID_START) || (pIntid >= num_intids)) &&
|
||||
(pIntid != INTID_SPURIOUS))) {
|
||||
|
@ -484,7 +485,7 @@ static ItsCmdResult process_mapc(GICv3ITSState *s, uint32_t offset)
|
|||
|
||||
valid = (value & CMD_FIELD_VALID_MASK);
|
||||
|
||||
if ((icid >= s->ct.num_ids) || (rdbase >= s->gicv3->num_cpu)) {
|
||||
if ((icid >= s->ct.num_entries) || (rdbase >= s->gicv3->num_cpu)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"ITS MAPC: invalid collection table attributes "
|
||||
"icid %d rdbase %" PRIu64 "\n", icid, rdbase);
|
||||
|
@ -565,7 +566,7 @@ static ItsCmdResult process_mapd(GICv3ITSState *s, uint64_t value,
|
|||
|
||||
valid = (value & CMD_FIELD_VALID_MASK);
|
||||
|
||||
if ((devid >= s->dt.num_ids) ||
|
||||
if ((devid >= s->dt.num_entries) ||
|
||||
(size > FIELD_EX64(s->typer, GITS_TYPER, IDBITS))) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"ITS MAPD: invalid device table attributes "
|
||||
|
@ -581,6 +582,201 @@ static ItsCmdResult process_mapd(GICv3ITSState *s, uint64_t value,
|
|||
return update_dte(s, devid, valid, size, itt_addr) ? CMD_CONTINUE : CMD_STALL;
|
||||
}
|
||||
|
||||
static ItsCmdResult process_movall(GICv3ITSState *s, uint64_t value,
|
||||
uint32_t offset)
|
||||
{
|
||||
AddressSpace *as = &s->gicv3->dma_as;
|
||||
MemTxResult res = MEMTX_OK;
|
||||
uint64_t rd1, rd2;
|
||||
|
||||
/* No fields in dwords 0 or 1 */
|
||||
offset += NUM_BYTES_IN_DW;
|
||||
offset += NUM_BYTES_IN_DW;
|
||||
value = address_space_ldq_le(as, s->cq.base_addr + offset,
|
||||
MEMTXATTRS_UNSPECIFIED, &res);
|
||||
if (res != MEMTX_OK) {
|
||||
return CMD_STALL;
|
||||
}
|
||||
|
||||
rd1 = FIELD_EX64(value, MOVALL_2, RDBASE1);
|
||||
if (rd1 >= s->gicv3->num_cpu) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: RDBASE1 %" PRId64
|
||||
" out of range (must be less than %d)\n",
|
||||
__func__, rd1, s->gicv3->num_cpu);
|
||||
return CMD_CONTINUE;
|
||||
}
|
||||
|
||||
offset += NUM_BYTES_IN_DW;
|
||||
value = address_space_ldq_le(as, s->cq.base_addr + offset,
|
||||
MEMTXATTRS_UNSPECIFIED, &res);
|
||||
if (res != MEMTX_OK) {
|
||||
return CMD_STALL;
|
||||
}
|
||||
|
||||
rd2 = FIELD_EX64(value, MOVALL_3, RDBASE2);
|
||||
if (rd2 >= s->gicv3->num_cpu) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: RDBASE2 %" PRId64
|
||||
" out of range (must be less than %d)\n",
|
||||
__func__, rd2, s->gicv3->num_cpu);
|
||||
return CMD_CONTINUE;
|
||||
}
|
||||
|
||||
if (rd1 == rd2) {
|
||||
/* Move to same target must succeed as a no-op */
|
||||
return CMD_CONTINUE;
|
||||
}
|
||||
|
||||
/* Move all pending LPIs from redistributor 1 to redistributor 2 */
|
||||
gicv3_redist_movall_lpis(&s->gicv3->cpu[rd1], &s->gicv3->cpu[rd2]);
|
||||
|
||||
return CMD_CONTINUE;
|
||||
}
|
||||
|
||||
static ItsCmdResult process_movi(GICv3ITSState *s, uint64_t value,
|
||||
uint32_t offset)
|
||||
{
|
||||
AddressSpace *as = &s->gicv3->dma_as;
|
||||
MemTxResult res = MEMTX_OK;
|
||||
uint32_t devid, eventid, intid;
|
||||
uint16_t old_icid, new_icid;
|
||||
uint64_t old_cte, new_cte;
|
||||
uint64_t old_rdbase, new_rdbase;
|
||||
uint64_t dte;
|
||||
bool dte_valid, ite_valid, cte_valid;
|
||||
uint64_t num_eventids;
|
||||
IteEntry ite = {};
|
||||
|
||||
devid = FIELD_EX64(value, MOVI_0, DEVICEID);
|
||||
|
||||
offset += NUM_BYTES_IN_DW;
|
||||
value = address_space_ldq_le(as, s->cq.base_addr + offset,
|
||||
MEMTXATTRS_UNSPECIFIED, &res);
|
||||
if (res != MEMTX_OK) {
|
||||
return CMD_STALL;
|
||||
}
|
||||
eventid = FIELD_EX64(value, MOVI_1, EVENTID);
|
||||
|
||||
offset += NUM_BYTES_IN_DW;
|
||||
value = address_space_ldq_le(as, s->cq.base_addr + offset,
|
||||
MEMTXATTRS_UNSPECIFIED, &res);
|
||||
if (res != MEMTX_OK) {
|
||||
return CMD_STALL;
|
||||
}
|
||||
new_icid = FIELD_EX64(value, MOVI_2, ICID);
|
||||
|
||||
if (devid >= s->dt.num_entries) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: invalid command attributes: devid %d>=%d",
|
||||
__func__, devid, s->dt.num_entries);
|
||||
return CMD_CONTINUE;
|
||||
}
|
||||
dte = get_dte(s, devid, &res);
|
||||
if (res != MEMTX_OK) {
|
||||
return CMD_STALL;
|
||||
}
|
||||
|
||||
dte_valid = FIELD_EX64(dte, DTE, VALID);
|
||||
if (!dte_valid) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: invalid command attributes: "
|
||||
"invalid dte: %"PRIx64" for %d\n",
|
||||
__func__, dte, devid);
|
||||
return CMD_CONTINUE;
|
||||
}
|
||||
|
||||
num_eventids = 1ULL << (FIELD_EX64(dte, DTE, SIZE) + 1);
|
||||
if (eventid >= num_eventids) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: invalid command attributes: eventid %d >= %"
|
||||
PRId64 "\n",
|
||||
__func__, eventid, num_eventids);
|
||||
return CMD_CONTINUE;
|
||||
}
|
||||
|
||||
ite_valid = get_ite(s, eventid, dte, &old_icid, &intid, &res);
|
||||
if (res != MEMTX_OK) {
|
||||
return CMD_STALL;
|
||||
}
|
||||
|
||||
if (!ite_valid) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: invalid command attributes: invalid ITE\n",
|
||||
__func__);
|
||||
return CMD_CONTINUE;
|
||||
}
|
||||
|
||||
if (old_icid >= s->ct.num_entries) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: invalid ICID 0x%x in ITE (table corrupted?)\n",
|
||||
__func__, old_icid);
|
||||
return CMD_CONTINUE;
|
||||
}
|
||||
|
||||
if (new_icid >= s->ct.num_entries) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: invalid command attributes: ICID 0x%x\n",
|
||||
__func__, new_icid);
|
||||
return CMD_CONTINUE;
|
||||
}
|
||||
|
||||
cte_valid = get_cte(s, old_icid, &old_cte, &res);
|
||||
if (res != MEMTX_OK) {
|
||||
return CMD_STALL;
|
||||
}
|
||||
if (!cte_valid) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: invalid command attributes: "
|
||||
"invalid cte: %"PRIx64"\n",
|
||||
__func__, old_cte);
|
||||
return CMD_CONTINUE;
|
||||
}
|
||||
|
||||
cte_valid = get_cte(s, new_icid, &new_cte, &res);
|
||||
if (res != MEMTX_OK) {
|
||||
return CMD_STALL;
|
||||
}
|
||||
if (!cte_valid) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: invalid command attributes: "
|
||||
"invalid cte: %"PRIx64"\n",
|
||||
__func__, new_cte);
|
||||
return CMD_CONTINUE;
|
||||
}
|
||||
|
||||
old_rdbase = FIELD_EX64(old_cte, CTE, RDBASE);
|
||||
if (old_rdbase >= s->gicv3->num_cpu) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: CTE has invalid rdbase 0x%"PRIx64"\n",
|
||||
__func__, old_rdbase);
|
||||
return CMD_CONTINUE;
|
||||
}
|
||||
|
||||
new_rdbase = FIELD_EX64(new_cte, CTE, RDBASE);
|
||||
if (new_rdbase >= s->gicv3->num_cpu) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: CTE has invalid rdbase 0x%"PRIx64"\n",
|
||||
__func__, new_rdbase);
|
||||
return CMD_CONTINUE;
|
||||
}
|
||||
|
||||
if (old_rdbase != new_rdbase) {
|
||||
/* Move the LPI from the old redistributor to the new one */
|
||||
gicv3_redist_mov_lpi(&s->gicv3->cpu[old_rdbase],
|
||||
&s->gicv3->cpu[new_rdbase],
|
||||
intid);
|
||||
}
|
||||
|
||||
/* Update the ICID field in the interrupt translation table entry */
|
||||
ite.itel = FIELD_DP64(ite.itel, ITE_L, VALID, 1);
|
||||
ite.itel = FIELD_DP64(ite.itel, ITE_L, INTTYPE, ITE_INTTYPE_PHYSICAL);
|
||||
ite.itel = FIELD_DP64(ite.itel, ITE_L, INTID, intid);
|
||||
ite.itel = FIELD_DP64(ite.itel, ITE_L, DOORBELL, INTID_SPURIOUS);
|
||||
ite.iteh = FIELD_DP32(ite.iteh, ITE_H, ICID, new_icid);
|
||||
return update_ite(s, eventid, dte, ite) ? CMD_CONTINUE : CMD_STALL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Current implementation blocks until all
|
||||
* commands are processed
|
||||
|
@ -634,6 +830,8 @@ static void process_cmdq(GICv3ITSState *s)
|
|||
|
||||
cmd = (data & CMD_MASK);
|
||||
|
||||
trace_gicv3_its_process_command(rd_offset, cmd);
|
||||
|
||||
switch (cmd) {
|
||||
case GITS_CMD_INT:
|
||||
result = process_its_cmd(s, data, cq_offset, INTERRUPT);
|
||||
|
@ -676,6 +874,12 @@ static void process_cmdq(GICv3ITSState *s)
|
|||
gicv3_redist_update_lpi(&s->gicv3->cpu[i]);
|
||||
}
|
||||
break;
|
||||
case GITS_CMD_MOVI:
|
||||
result = process_movi(s, data, cq_offset);
|
||||
break;
|
||||
case GITS_CMD_MOVALL:
|
||||
result = process_movall(s, data, cq_offset);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -788,7 +992,7 @@ static void extract_table_params(GICv3ITSState *s)
|
|||
L1TABLE_ENTRY_SIZE) *
|
||||
(page_sz / td->entry_sz));
|
||||
}
|
||||
td->num_ids = 1ULL << idbits;
|
||||
td->num_entries = MIN(td->num_entries, 1ULL << idbits);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -810,6 +1014,18 @@ static void extract_cmdq_params(GICv3ITSState *s)
|
|||
}
|
||||
}
|
||||
|
||||
static MemTxResult gicv3_its_translation_read(void *opaque, hwaddr offset,
|
||||
uint64_t *data, unsigned size,
|
||||
MemTxAttrs attrs)
|
||||
{
|
||||
/*
|
||||
* GITS_TRANSLATER is write-only, and all other addresses
|
||||
* in the interrupt translation space frame are RES0.
|
||||
*/
|
||||
*data = 0;
|
||||
return MEMTX_OK;
|
||||
}
|
||||
|
||||
static MemTxResult gicv3_its_translation_write(void *opaque, hwaddr offset,
|
||||
uint64_t data, unsigned size,
|
||||
MemTxAttrs attrs)
|
||||
|
@ -818,6 +1034,8 @@ static MemTxResult gicv3_its_translation_write(void *opaque, hwaddr offset,
|
|||
bool result = true;
|
||||
uint32_t devid = 0;
|
||||
|
||||
trace_gicv3_its_translation_write(offset, data, size, attrs.requester_id);
|
||||
|
||||
switch (offset) {
|
||||
case GITS_TRANSLATER:
|
||||
if (s->ctlr & R_GITS_CTLR_ENABLED_MASK) {
|
||||
|
@ -848,7 +1066,6 @@ static bool its_writel(GICv3ITSState *s, hwaddr offset,
|
|||
s->ctlr |= R_GITS_CTLR_ENABLED_MASK;
|
||||
extract_table_params(s);
|
||||
extract_cmdq_params(s);
|
||||
s->creadr = 0;
|
||||
process_cmdq(s);
|
||||
} else {
|
||||
s->ctlr &= ~R_GITS_CTLR_ENABLED_MASK;
|
||||
|
@ -862,7 +1079,6 @@ static bool its_writel(GICv3ITSState *s, hwaddr offset,
|
|||
if (!(s->ctlr & R_GITS_CTLR_ENABLED_MASK)) {
|
||||
s->cbaser = deposit64(s->cbaser, 0, 32, value);
|
||||
s->creadr = 0;
|
||||
s->cwriter = s->creadr;
|
||||
}
|
||||
break;
|
||||
case GITS_CBASER + 4:
|
||||
|
@ -873,7 +1089,6 @@ static bool its_writel(GICv3ITSState *s, hwaddr offset,
|
|||
if (!(s->ctlr & R_GITS_CTLR_ENABLED_MASK)) {
|
||||
s->cbaser = deposit64(s->cbaser, 32, 32, value);
|
||||
s->creadr = 0;
|
||||
s->cwriter = s->creadr;
|
||||
}
|
||||
break;
|
||||
case GITS_CWRITER:
|
||||
|
@ -915,6 +1130,10 @@ static bool its_writel(GICv3ITSState *s, hwaddr offset,
|
|||
if (!(s->ctlr & R_GITS_CTLR_ENABLED_MASK)) {
|
||||
index = (offset - GITS_BASER) / 8;
|
||||
|
||||
if (s->baser[index] == 0) {
|
||||
/* Unimplemented GITS_BASERn: RAZ/WI */
|
||||
break;
|
||||
}
|
||||
if (offset & 7) {
|
||||
value <<= 32;
|
||||
value &= ~GITS_BASER_RO_MASK;
|
||||
|
@ -1011,6 +1230,10 @@ static bool its_writell(GICv3ITSState *s, hwaddr offset,
|
|||
*/
|
||||
if (!(s->ctlr & R_GITS_CTLR_ENABLED_MASK)) {
|
||||
index = (offset - GITS_BASER) / 8;
|
||||
if (s->baser[index] == 0) {
|
||||
/* Unimplemented GITS_BASERn: RAZ/WI */
|
||||
break;
|
||||
}
|
||||
s->baser[index] &= GITS_BASER_RO_MASK;
|
||||
s->baser[index] |= (value & ~GITS_BASER_RO_MASK);
|
||||
}
|
||||
|
@ -1023,7 +1246,6 @@ static bool its_writell(GICv3ITSState *s, hwaddr offset,
|
|||
if (!(s->ctlr & R_GITS_CTLR_ENABLED_MASK)) {
|
||||
s->cbaser = value;
|
||||
s->creadr = 0;
|
||||
s->cwriter = s->creadr;
|
||||
}
|
||||
break;
|
||||
case GITS_CWRITER:
|
||||
|
@ -1107,6 +1329,7 @@ static MemTxResult gicv3_its_read(void *opaque, hwaddr offset, uint64_t *data,
|
|||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: invalid guest read at offset " TARGET_FMT_plx
|
||||
"size %u\n", __func__, offset, size);
|
||||
trace_gicv3_its_badread(offset, size);
|
||||
/*
|
||||
* The spec requires that reserved registers are RAZ/WI;
|
||||
* so use false returns from leaf functions as a way to
|
||||
|
@ -1114,6 +1337,8 @@ static MemTxResult gicv3_its_read(void *opaque, hwaddr offset, uint64_t *data,
|
|||
* the caller, or we'll cause a spurious guest data abort.
|
||||
*/
|
||||
*data = 0;
|
||||
} else {
|
||||
trace_gicv3_its_read(offset, *data, size);
|
||||
}
|
||||
return MEMTX_OK;
|
||||
}
|
||||
|
@ -1140,12 +1365,15 @@ static MemTxResult gicv3_its_write(void *opaque, hwaddr offset, uint64_t data,
|
|||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: invalid guest write at offset " TARGET_FMT_plx
|
||||
"size %u\n", __func__, offset, size);
|
||||
trace_gicv3_its_badwrite(offset, data, size);
|
||||
/*
|
||||
* The spec requires that reserved registers are RAZ/WI;
|
||||
* so use false returns from leaf functions as a way to
|
||||
* trigger the guest-error logging but don't return it to
|
||||
* the caller, or we'll cause a spurious guest data abort.
|
||||
*/
|
||||
} else {
|
||||
trace_gicv3_its_write(offset, data, size);
|
||||
}
|
||||
return MEMTX_OK;
|
||||
}
|
||||
|
@ -1161,6 +1389,7 @@ static const MemoryRegionOps gicv3_its_control_ops = {
|
|||
};
|
||||
|
||||
static const MemoryRegionOps gicv3_its_translation_ops = {
|
||||
.read_with_attrs = gicv3_its_translation_read,
|
||||
.write_with_attrs = gicv3_its_translation_write,
|
||||
.valid.min_access_size = 2,
|
||||
.valid.max_access_size = 4,
|
||||
|
@ -1183,9 +1412,6 @@ static void gicv3_arm_its_realize(DeviceState *dev, Error **errp)
|
|||
|
||||
gicv3_its_init_mmio(s, &gicv3_its_control_ops, &gicv3_its_translation_ops);
|
||||
|
||||
address_space_init(&s->gicv3->dma_as, s->gicv3->dma,
|
||||
"gicv3-its-sysmem");
|
||||
|
||||
/* set the ITS default features supported */
|
||||
s->typer = FIELD_DP64(s->typer, GITS_TYPER, PHYSICAL, 1);
|
||||
s->typer = FIELD_DP64(s->typer, GITS_TYPER, ITT_ENTRY_SIZE,
|
||||
|
|
|
@ -591,8 +591,7 @@ void gicv3_redist_update_lpi_only(GICv3CPUState *cs)
|
|||
idbits = MIN(FIELD_EX64(cs->gicr_propbaser, GICR_PROPBASER, IDBITS),
|
||||
GICD_TYPER_IDBITS);
|
||||
|
||||
if (!(cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) || !cs->gicr_propbaser ||
|
||||
!cs->gicr_pendbaser) {
|
||||
if (!(cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -673,9 +672,8 @@ void gicv3_redist_process_lpi(GICv3CPUState *cs, int irq, int level)
|
|||
idbits = MIN(FIELD_EX64(cs->gicr_propbaser, GICR_PROPBASER, IDBITS),
|
||||
GICD_TYPER_IDBITS);
|
||||
|
||||
if (!(cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) || !cs->gicr_propbaser ||
|
||||
!cs->gicr_pendbaser || (irq > (1ULL << (idbits + 1)) - 1) ||
|
||||
irq < GICV3_LPI_INTID_START) {
|
||||
if (!(cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) ||
|
||||
(irq > (1ULL << (idbits + 1)) - 1) || irq < GICV3_LPI_INTID_START) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -683,6 +681,113 @@ void gicv3_redist_process_lpi(GICv3CPUState *cs, int irq, int level)
|
|||
gicv3_redist_lpi_pending(cs, irq, level);
|
||||
}
|
||||
|
||||
void gicv3_redist_mov_lpi(GICv3CPUState *src, GICv3CPUState *dest, int irq)
|
||||
{
|
||||
/*
|
||||
* Move the specified LPI's pending state from the source redistributor
|
||||
* to the destination.
|
||||
*
|
||||
* If LPIs are disabled on dest this is CONSTRAINED UNPREDICTABLE:
|
||||
* we choose to NOP. If LPIs are disabled on source there's nothing
|
||||
* to be transferred anyway.
|
||||
*/
|
||||
AddressSpace *as = &src->gic->dma_as;
|
||||
uint64_t idbits;
|
||||
uint32_t pendt_size;
|
||||
uint64_t src_baddr;
|
||||
uint8_t src_pend;
|
||||
|
||||
if (!(src->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) ||
|
||||
!(dest->gicr_ctlr & GICR_CTLR_ENABLE_LPIS)) {
|
||||
return;
|
||||
}
|
||||
|
||||
idbits = MIN(FIELD_EX64(src->gicr_propbaser, GICR_PROPBASER, IDBITS),
|
||||
GICD_TYPER_IDBITS);
|
||||
idbits = MIN(FIELD_EX64(dest->gicr_propbaser, GICR_PROPBASER, IDBITS),
|
||||
idbits);
|
||||
|
||||
pendt_size = 1ULL << (idbits + 1);
|
||||
if ((irq / 8) >= pendt_size) {
|
||||
return;
|
||||
}
|
||||
|
||||
src_baddr = src->gicr_pendbaser & R_GICR_PENDBASER_PHYADDR_MASK;
|
||||
|
||||
address_space_read(as, src_baddr + (irq / 8),
|
||||
MEMTXATTRS_UNSPECIFIED, &src_pend, sizeof(src_pend));
|
||||
if (!extract32(src_pend, irq % 8, 1)) {
|
||||
/* Not pending on source, nothing to do */
|
||||
return;
|
||||
}
|
||||
src_pend &= ~(1 << (irq % 8));
|
||||
address_space_write(as, src_baddr + (irq / 8),
|
||||
MEMTXATTRS_UNSPECIFIED, &src_pend, sizeof(src_pend));
|
||||
if (irq == src->hpplpi.irq) {
|
||||
/*
|
||||
* We just made this LPI not-pending so only need to update
|
||||
* if it was previously the highest priority pending LPI
|
||||
*/
|
||||
gicv3_redist_update_lpi(src);
|
||||
}
|
||||
/* Mark it pending on the destination */
|
||||
gicv3_redist_lpi_pending(dest, irq, 1);
|
||||
}
|
||||
|
||||
void gicv3_redist_movall_lpis(GICv3CPUState *src, GICv3CPUState *dest)
|
||||
{
|
||||
/*
|
||||
* We must move all pending LPIs from the source redistributor
|
||||
* to the destination. That is, for every pending LPI X on
|
||||
* src, we must set it not-pending on src and pending on dest.
|
||||
* LPIs that are already pending on dest are not cleared.
|
||||
*
|
||||
* If LPIs are disabled on dest this is CONSTRAINED UNPREDICTABLE:
|
||||
* we choose to NOP. If LPIs are disabled on source there's nothing
|
||||
* to be transferred anyway.
|
||||
*/
|
||||
AddressSpace *as = &src->gic->dma_as;
|
||||
uint64_t idbits;
|
||||
uint32_t pendt_size;
|
||||
uint64_t src_baddr, dest_baddr;
|
||||
int i;
|
||||
|
||||
if (!(src->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) ||
|
||||
!(dest->gicr_ctlr & GICR_CTLR_ENABLE_LPIS)) {
|
||||
return;
|
||||
}
|
||||
|
||||
idbits = MIN(FIELD_EX64(src->gicr_propbaser, GICR_PROPBASER, IDBITS),
|
||||
GICD_TYPER_IDBITS);
|
||||
idbits = MIN(FIELD_EX64(dest->gicr_propbaser, GICR_PROPBASER, IDBITS),
|
||||
idbits);
|
||||
|
||||
pendt_size = 1ULL << (idbits + 1);
|
||||
src_baddr = src->gicr_pendbaser & R_GICR_PENDBASER_PHYADDR_MASK;
|
||||
dest_baddr = dest->gicr_pendbaser & R_GICR_PENDBASER_PHYADDR_MASK;
|
||||
|
||||
for (i = GICV3_LPI_INTID_START / 8; i < pendt_size / 8; i++) {
|
||||
uint8_t src_pend, dest_pend;
|
||||
|
||||
address_space_read(as, src_baddr + i, MEMTXATTRS_UNSPECIFIED,
|
||||
&src_pend, sizeof(src_pend));
|
||||
if (!src_pend) {
|
||||
continue;
|
||||
}
|
||||
address_space_read(as, dest_baddr + i, MEMTXATTRS_UNSPECIFIED,
|
||||
&dest_pend, sizeof(dest_pend));
|
||||
dest_pend |= src_pend;
|
||||
src_pend = 0;
|
||||
address_space_write(as, src_baddr + i, MEMTXATTRS_UNSPECIFIED,
|
||||
&src_pend, sizeof(src_pend));
|
||||
address_space_write(as, dest_baddr + i, MEMTXATTRS_UNSPECIFIED,
|
||||
&dest_pend, sizeof(dest_pend));
|
||||
}
|
||||
|
||||
gicv3_redist_update_lpi(src);
|
||||
gicv3_redist_update_lpi(dest);
|
||||
}
|
||||
|
||||
void gicv3_redist_set_irq(GICv3CPUState *cs, int irq, int level)
|
||||
{
|
||||
/* Update redistributor state for a change in an external PPI input line */
|
||||
|
|
|
@ -110,6 +110,7 @@
|
|||
#define GICR_NSACR (GICR_SGI_OFFSET + 0x0E00)
|
||||
|
||||
#define GICR_CTLR_ENABLE_LPIS (1U << 0)
|
||||
#define GICR_CTLR_CES (1U << 1)
|
||||
#define GICR_CTLR_RWP (1U << 3)
|
||||
#define GICR_CTLR_DPG0 (1U << 24)
|
||||
#define GICR_CTLR_DPG1NS (1U << 25)
|
||||
|
@ -314,16 +315,18 @@ FIELD(GITS_TYPER, CIL, 36, 1)
|
|||
#define CMD_MASK 0xff
|
||||
|
||||
/* ITS Commands */
|
||||
#define GITS_CMD_CLEAR 0x04
|
||||
#define GITS_CMD_DISCARD 0x0F
|
||||
#define GITS_CMD_MOVI 0x01
|
||||
#define GITS_CMD_INT 0x03
|
||||
#define GITS_CMD_MAPC 0x09
|
||||
#define GITS_CMD_CLEAR 0x04
|
||||
#define GITS_CMD_SYNC 0x05
|
||||
#define GITS_CMD_MAPD 0x08
|
||||
#define GITS_CMD_MAPI 0x0B
|
||||
#define GITS_CMD_MAPC 0x09
|
||||
#define GITS_CMD_MAPTI 0x0A
|
||||
#define GITS_CMD_MAPI 0x0B
|
||||
#define GITS_CMD_INV 0x0C
|
||||
#define GITS_CMD_INVALL 0x0D
|
||||
#define GITS_CMD_SYNC 0x05
|
||||
#define GITS_CMD_MOVALL 0x0E
|
||||
#define GITS_CMD_DISCARD 0x0F
|
||||
|
||||
/* MAPC command fields */
|
||||
#define ICID_LENGTH 16
|
||||
|
@ -354,6 +357,15 @@ FIELD(MAPC, RDBASE, 16, 32)
|
|||
#define L2_TABLE_VALID_MASK CMD_FIELD_VALID_MASK
|
||||
#define TABLE_ENTRY_VALID_MASK (1ULL << 0)
|
||||
|
||||
/* MOVALL command fields */
|
||||
FIELD(MOVALL_2, RDBASE1, 16, 36)
|
||||
FIELD(MOVALL_3, RDBASE2, 16, 36)
|
||||
|
||||
/* MOVI command fields */
|
||||
FIELD(MOVI_0, DEVICEID, 32, 32)
|
||||
FIELD(MOVI_1, EVENTID, 0, 32)
|
||||
FIELD(MOVI_2, ICID, 0, 16)
|
||||
|
||||
/*
|
||||
* 12 bytes Interrupt translation Table Entry size
|
||||
* as per Table 5.3 in GICv3 spec
|
||||
|
@ -496,6 +508,27 @@ void gicv3_redist_update_lpi(GICv3CPUState *cs);
|
|||
* an incoming migration has loaded new state.
|
||||
*/
|
||||
void gicv3_redist_update_lpi_only(GICv3CPUState *cs);
|
||||
/**
|
||||
* gicv3_redist_mov_lpi:
|
||||
* @src: source redistributor
|
||||
* @dest: destination redistributor
|
||||
* @irq: LPI to update
|
||||
*
|
||||
* Move the pending state of the specified LPI from @src to @dest,
|
||||
* as required by the ITS MOVI command.
|
||||
*/
|
||||
void gicv3_redist_mov_lpi(GICv3CPUState *src, GICv3CPUState *dest, int irq);
|
||||
/**
|
||||
* gicv3_redist_movall_lpis:
|
||||
* @src: source redistributor
|
||||
* @dest: destination redistributor
|
||||
*
|
||||
* Scan the LPI pending table for @src, and for each pending LPI there
|
||||
* mark it as not-pending for @src and pending for @dest, as required
|
||||
* by the ITS MOVALL command.
|
||||
*/
|
||||
void gicv3_redist_movall_lpis(GICv3CPUState *src, GICv3CPUState *dest);
|
||||
|
||||
void gicv3_redist_send_sgi(GICv3CPUState *cs, int grp, int irq, bool ns);
|
||||
void gicv3_init_cpuif(GICv3State *s);
|
||||
|
||||
|
|
|
@ -169,6 +169,14 @@ gicv3_redist_badwrite(uint32_t cpu, uint64_t offset, uint64_t data, unsigned siz
|
|||
gicv3_redist_set_irq(uint32_t cpu, int irq, int level) "GICv3 redistributor 0x%x interrupt %d level changed to %d"
|
||||
gicv3_redist_send_sgi(uint32_t cpu, int irq) "GICv3 redistributor 0x%x pending SGI %d"
|
||||
|
||||
# arm_gicv3_its.c
|
||||
gicv3_its_read(uint64_t offset, uint64_t data, unsigned size) "GICv3 ITS read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
|
||||
gicv3_its_badread(uint64_t offset, unsigned size) "GICv3 ITS read: offset 0x%" PRIx64 " size %u: error"
|
||||
gicv3_its_write(uint64_t offset, uint64_t data, unsigned size) "GICv3 ITS write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
|
||||
gicv3_its_badwrite(uint64_t offset, uint64_t data, unsigned size) "GICv3 ITS write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u: error"
|
||||
gicv3_its_translation_write(uint64_t offset, uint64_t data, unsigned size, uint32_t requester_id) "GICv3 ITS TRANSLATER write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u requester_id 0x%x"
|
||||
gicv3_its_process_command(uint32_t rd_offset, uint8_t cmd) "GICv3 ITS: processing command at offset 0x%x: 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"
|
||||
nvic_recompute_state_secure(int vectpending, bool vectpending_is_s_banked, int vectpending_prio, int exception_prio) "NVIC state recomputed: vectpending %d is_s_banked %d vectpending_prio %d exception_prio %d"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue