hw/intc/arm_gic: Fix handling of GICC_APR<n>, GICC_NSAPR<n> registers
A GICv2 has both GICC_APR<n> and GICC_NSAPR<n> registers, with the latter holding the active priority bits for Group 1 interrupts (usually Nonsecure interrupts), and the Nonsecure view of the GICC_APR<n> is the second half of the GICC_NSAPR<n> registers. Turn our half-hearted implementation of APR<n> into a proper implementation of both APR<n> and NSAPR<n>: * Add the underlying state for NSAPR<n> * Make sure APR<n> aren't visible for pre-GICv2 * Implement reading of NSAPR<n> * Make non-secure reads of APR<n> behave correctly * Implement writing to APR<n> and NSAPR<n> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 1438089748-5528-4-git-send-email-peter.maydell@linaro.org
This commit is contained in:
parent
df92cfa60e
commit
51fd06e0ee
@ -948,6 +948,68 @@ static MemTxResult gic_dist_write(void *opaque, hwaddr offset, uint64_t data,
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint32_t gic_apr_ns_view(GICState *s, int cpu, int regno)
|
||||
{
|
||||
/* Return the Nonsecure view of GICC_APR<regno>. This is the
|
||||
* second half of GICC_NSAPR.
|
||||
*/
|
||||
switch (GIC_MIN_BPR) {
|
||||
case 0:
|
||||
if (regno < 2) {
|
||||
return s->nsapr[regno + 2][cpu];
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (regno == 0) {
|
||||
return s->nsapr[regno + 1][cpu];
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (regno == 0) {
|
||||
return extract32(s->nsapr[0][cpu], 16, 16);
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (regno == 0) {
|
||||
return extract32(s->nsapr[0][cpu], 8, 8);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void gic_apr_write_ns_view(GICState *s, int cpu, int regno,
|
||||
uint32_t value)
|
||||
{
|
||||
/* Write the Nonsecure view of GICC_APR<regno>. */
|
||||
switch (GIC_MIN_BPR) {
|
||||
case 0:
|
||||
if (regno < 2) {
|
||||
s->nsapr[regno + 2][cpu] = value;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (regno == 0) {
|
||||
s->nsapr[regno + 1][cpu] = value;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (regno == 0) {
|
||||
s->nsapr[0][cpu] = deposit32(s->nsapr[0][cpu], 16, 16, value);
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (regno == 0) {
|
||||
s->nsapr[0][cpu] = deposit32(s->nsapr[0][cpu], 8, 8, value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
static MemTxResult gic_cpu_read(GICState *s, int cpu, int offset,
|
||||
uint64_t *data, MemTxAttrs attrs)
|
||||
{
|
||||
@ -988,8 +1050,31 @@ static MemTxResult gic_cpu_read(GICState *s, int cpu, int offset,
|
||||
}
|
||||
break;
|
||||
case 0xd0: case 0xd4: case 0xd8: case 0xdc:
|
||||
*data = s->apr[(offset - 0xd0) / 4][cpu];
|
||||
{
|
||||
int regno = (offset - 0xd0) / 4;
|
||||
|
||||
if (regno >= GIC_NR_APRS || s->revision != 2) {
|
||||
*data = 0;
|
||||
} else if (s->security_extn && !attrs.secure) {
|
||||
/* NS view of GICC_APR<n> is the top half of GIC_NSAPR<n> */
|
||||
*data = gic_apr_ns_view(s, regno, cpu);
|
||||
} else {
|
||||
*data = s->apr[regno][cpu];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0xe0: case 0xe4: case 0xe8: case 0xec:
|
||||
{
|
||||
int regno = (offset - 0xe0) / 4;
|
||||
|
||||
if (regno >= GIC_NR_APRS || s->revision != 2 || !gic_has_groups(s) ||
|
||||
(s->security_extn && !attrs.secure)) {
|
||||
*data = 0;
|
||||
} else {
|
||||
*data = s->nsapr[regno][cpu];
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"gic_cpu_read: Bad offset %x\n", (int)offset);
|
||||
@ -1027,8 +1112,33 @@ static MemTxResult gic_cpu_write(GICState *s, int cpu, int offset,
|
||||
}
|
||||
break;
|
||||
case 0xd0: case 0xd4: case 0xd8: case 0xdc:
|
||||
qemu_log_mask(LOG_UNIMP, "Writing APR not implemented\n");
|
||||
{
|
||||
int regno = (offset - 0xd0) / 4;
|
||||
|
||||
if (regno >= GIC_NR_APRS || s->revision != 2) {
|
||||
return MEMTX_OK;
|
||||
}
|
||||
if (s->security_extn && !attrs.secure) {
|
||||
/* NS view of GICC_APR<n> is the top half of GIC_NSAPR<n> */
|
||||
gic_apr_write_ns_view(s, regno, cpu, value);
|
||||
} else {
|
||||
s->apr[regno][cpu] = value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0xe0: case 0xe4: case 0xe8: case 0xec:
|
||||
{
|
||||
int regno = (offset - 0xe0) / 4;
|
||||
|
||||
if (regno >= GIC_NR_APRS || s->revision != 2) {
|
||||
return MEMTX_OK;
|
||||
}
|
||||
if (!gic_has_groups(s) || (s->security_extn && !attrs.secure)) {
|
||||
return MEMTX_OK;
|
||||
}
|
||||
s->nsapr[regno][cpu] = value;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"gic_cpu_write: Bad offset %x\n", (int)offset);
|
||||
|
@ -59,8 +59,8 @@ static const VMStateDescription vmstate_gic_irq_state = {
|
||||
|
||||
static const VMStateDescription vmstate_gic = {
|
||||
.name = "arm_gic",
|
||||
.version_id = 10,
|
||||
.minimum_version_id = 10,
|
||||
.version_id = 11,
|
||||
.minimum_version_id = 11,
|
||||
.pre_save = gic_pre_save,
|
||||
.post_load = gic_post_load,
|
||||
.fields = (VMStateField[]) {
|
||||
@ -80,6 +80,7 @@ static const VMStateDescription vmstate_gic = {
|
||||
VMSTATE_UINT8_ARRAY(bpr, GICState, GIC_NCPU),
|
||||
VMSTATE_UINT8_ARRAY(abpr, GICState, GIC_NCPU),
|
||||
VMSTATE_UINT32_2DARRAY(apr, GICState, GIC_NR_APRS, GIC_NCPU),
|
||||
VMSTATE_UINT32_2DARRAY(nsapr, GICState, GIC_NR_APRS, GIC_NCPU),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
@ -106,6 +106,7 @@ typedef struct GICState {
|
||||
* the GIC.
|
||||
*/
|
||||
uint32_t apr[GIC_NR_APRS][GIC_NCPU];
|
||||
uint32_t nsapr[GIC_NR_APRS][GIC_NCPU];
|
||||
|
||||
uint32_t num_cpu;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user