blob: 27f404b2bf4a1bf2482c32f922b8704a6cb0b81c [file] [log] [blame]
/*
* Copyright (c) 2019-2020 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
* not be construed as granting a license to any other intellectual
* property including but not limited to intellectual property relating
* to a hardware implementation of the functionality of the software
* licensed hereunder. You may use the software subject to the license
* terms below provided that you ensure that this notice is replicated
* unmodified and in its entirety in all distributions of the software,
* modified or unmodified, in source code or in binary form.
*
* Copyright (c) 2018 Metempsy Technology Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer;
* redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution;
* neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "dev/arm/gic_v3_distributor.hh"
#include <algorithm>
#include "base/intmath.hh"
#include "debug/GIC.hh"
#include "dev/arm/gic_v3.hh"
#include "dev/arm/gic_v3_cpu_interface.hh"
#include "dev/arm/gic_v3_redistributor.hh"
const AddrRange Gicv3Distributor::GICD_IGROUPR (0x0080, 0x0100);
const AddrRange Gicv3Distributor::GICD_ISENABLER (0x0100, 0x0180);
const AddrRange Gicv3Distributor::GICD_ICENABLER (0x0180, 0x0200);
const AddrRange Gicv3Distributor::GICD_ISPENDR (0x0200, 0x0280);
const AddrRange Gicv3Distributor::GICD_ICPENDR (0x0280, 0x0300);
const AddrRange Gicv3Distributor::GICD_ISACTIVER (0x0300, 0x0380);
const AddrRange Gicv3Distributor::GICD_ICACTIVER (0x0380, 0x0400);
const AddrRange Gicv3Distributor::GICD_IPRIORITYR(0x0400, 0x0800);
const AddrRange Gicv3Distributor::GICD_ITARGETSR (0x0800, 0x0900);
const AddrRange Gicv3Distributor::GICD_ICFGR (0x0c00, 0x0d00);
const AddrRange Gicv3Distributor::GICD_IGRPMODR (0x0d00, 0x0d80);
const AddrRange Gicv3Distributor::GICD_NSACR (0x0e00, 0x0f00);
const AddrRange Gicv3Distributor::GICD_CPENDSGIR (0x0f10, 0x0f20);
const AddrRange Gicv3Distributor::GICD_SPENDSGIR (0x0f20, 0x0f30);
const AddrRange Gicv3Distributor::GICD_IROUTER (0x6000, 0x7fe0);
Gicv3Distributor::Gicv3Distributor(Gicv3 * gic, uint32_t it_lines)
: gic(gic),
itLines(it_lines),
ARE(true),
EnableGrp1S(0),
EnableGrp1NS(0),
EnableGrp0(0),
irqGroup(it_lines, 0),
irqEnabled(it_lines, false),
irqPending(it_lines, false),
irqPendingIspendr(it_lines, false),
irqActive(it_lines, false),
irqPriority(it_lines, 0xAA),
irqConfig(it_lines, Gicv3::INT_LEVEL_SENSITIVE),
irqGrpmod(it_lines, 0),
irqNsacr(it_lines, 0),
irqAffinityRouting(it_lines, 0),
gicdTyper(0),
gicdPidr0(0x92),
gicdPidr1(0xb4),
gicdPidr2(0x3b),
gicdPidr3(0),
gicdPidr4(0x44)
{
panic_if(it_lines > Gicv3::INTID_SECURE, "Invalid value for it_lines!");
/*
* RSS [26] == 1
* (The implementation does supports targeted SGIs with affinity
* level 0 values of 0 - 255)
* No1N [25] == 1
* (1 of N SPI interrupts are not supported)
* A3V [24] == 1
* (Supports nonzero values of Affinity level 3)
* IDbits [23:19] == 0xf
* (The number of interrupt identifier bits supported, minus one)
* DVIS [18] == 0
* (The implementation does not support Direct Virtual LPI
* injection)
* LPIS [17] == 1
* (The implementation does not support LPIs)
* MBIS [16] == 1
* (The implementation supports message-based interrupts
* by writing to Distributor registers)
* SecurityExtn [10] == X
* (The GIC implementation supports two Security states)
* CPUNumber [7:5] == 0
* (since for us ARE is always 1 [(ARE = 0) == Gicv2 legacy])
* ITLinesNumber [4:0] == N
* (MaxSPIIntId = 32 (N + 1) - 1)
*/
int max_spi_int_id = itLines - 1;
int it_lines_number = divCeil(max_spi_int_id + 1, 32) - 1;
gicdTyper = (1 << 26) | (1 << 25) | (1 << 24) | (IDBITS << 19) |
(1 << 17) | (1 << 16) |
((gic->getSystem()->haveSecurity() ? 1 : 0) << 10) |
(it_lines_number << 0);
if (gic->getSystem()->haveSecurity()) {
DS = false;
} else {
DS = true;
}
}
void
Gicv3Distributor::init()
{
}
uint64_t
Gicv3Distributor::read(Addr addr, size_t size, bool is_secure_access)
{
if (GICD_IGROUPR.contains(addr)) { // Interrupt Group Registers
uint64_t val = 0x0;
if (!DS && !is_secure_access) {
// RAZ/WI for non-secure accesses
return 0;
}
int first_intid = (addr - GICD_IGROUPR.start()) * 8;
if (isNotSPI(first_intid)) {
return 0;
}
for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
i++, int_id++) {
val |= irqGroup[int_id] << i;
}
return val;
} else if (GICD_ISENABLER.contains(addr)) {
// Interrupt Set-Enable Registers
uint64_t val = 0x0;
int first_intid = (addr - GICD_ISENABLER.start()) * 8;
if (isNotSPI(first_intid)) {
return 0;
}
for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
i++, int_id++) {
if (nsAccessToSecInt(int_id, is_secure_access))
{
continue;
}
val |= irqEnabled[int_id] << i;
}
return val;
} else if (GICD_ICENABLER.contains(addr)) {
// Interrupt Clear-Enable Registers
uint64_t val = 0x0;
int first_intid = (addr - GICD_ICENABLER.start()) * 8;
if (isNotSPI(first_intid)) {
return 0;
}
for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
i++, int_id++) {
if (nsAccessToSecInt(int_id, is_secure_access))
{
continue;
}
val |= (irqEnabled[int_id] << i);
}
return val;
} else if (GICD_ISPENDR.contains(addr)) {
// Interrupt Set-Pending Registers
uint64_t val = 0x0;
int first_intid = (addr - GICD_ISPENDR.start()) * 8;
if (isNotSPI(first_intid)) {
return 0;
}
for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
i++, int_id++) {
if (nsAccessToSecInt(int_id, is_secure_access))
{
if (irqNsacr[int_id] == 0) {
// Group 0 or Secure Group 1 interrupts are RAZ/WI
continue;
}
}
val |= (irqPending[int_id] << i);
}
return val;
} else if (GICD_ICPENDR.contains(addr)) {
// Interrupt Clear-Pending Registers
uint64_t val = 0x0;
int first_intid = (addr - GICD_ICPENDR.start()) * 8;
if (isNotSPI(first_intid)) {
return 0;
}
for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
i++, int_id++) {
if (nsAccessToSecInt(int_id, is_secure_access))
{
if (irqNsacr[int_id] < 2) {
// Group 0 or Secure Group 1 interrupts are RAZ/WI
continue;
}
}
val |= (irqPending[int_id] << i);
}
return val;
} else if (GICD_ISACTIVER.contains(addr)) {
// Interrupt Set-Active Registers
int first_intid = (addr - GICD_ISACTIVER.start()) * 8;
if (isNotSPI(first_intid)) {
return 0;
}
uint64_t val = 0x0;
for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
i++, int_id++) {
if (nsAccessToSecInt(int_id, is_secure_access))
{
// Group 0 or Secure Group 1 interrupts are RAZ/WI
if (irqNsacr[int_id] < 2) {
continue;
}
}
val |= (irqActive[int_id] << i);
}
return val;
} else if (GICD_ICACTIVER.contains(addr)) {
// Interrupt Clear-Active Registers
int first_intid = (addr - GICD_ICACTIVER.start()) * 8;
if (isNotSPI(first_intid)) {
return 0;
}
uint64_t val = 0x0;
for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
i++, int_id++) {
if (nsAccessToSecInt(int_id, is_secure_access))
{
if (irqNsacr[int_id] < 2) {
continue;
}
}
val |= (irqActive[int_id] << i);
}
return val;
} else if (GICD_IPRIORITYR.contains(addr)) {
// Interrupt Priority Registers
uint64_t val = 0x0;
int first_intid = addr - GICD_IPRIORITYR.start();
if (isNotSPI(first_intid)) {
return 0;
}
for (int i = 0, int_id = first_intid; i < size && int_id < itLines;
i++, int_id++) {
uint8_t prio = irqPriority[int_id];
if (!DS && !is_secure_access) {
if (getIntGroup(int_id) != Gicv3::G1NS) {
// RAZ/WI for non-secure accesses for secure interrupts
continue;
} else {
// NS view
prio = (prio << 1) & 0xff;
}
}
val |= prio << (i * 8);
}
return val;
} else if (GICD_ITARGETSR.contains(addr)) {
// Interrupt Processor Targets Registers
// ARE always on, RAZ/WI
warn("Gicv3Distributor::read(): "
"GICD_ITARGETSR is RAZ/WI, legacy not supported!\n");
return 0;
} else if (GICD_ICFGR.contains(addr)) {
// Interrupt Configuration Registers
int first_intid = (addr - GICD_ICFGR.start()) * 4;
if (isNotSPI(first_intid)) {
return 0;
}
uint64_t val = 0x0;
for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
i = i + 2, int_id++) {
if (nsAccessToSecInt(int_id, is_secure_access))
{
continue;
}
if (irqConfig[int_id] == Gicv3::INT_EDGE_TRIGGERED) {
val |= (0x2 << i);
}
}
return val;
} else if (GICD_IGRPMODR.contains(addr)) {
// Interrupt Group Modifier Registers
if (DS) {
// RAZ/WI if security disabled
return 0;
} else {
if (!is_secure_access) {
// RAZ/WI for non-secure accesses
return 0;
} else {
int first_intid = (addr - GICD_IGRPMODR.start()) * 8;
if (isNotSPI(first_intid)) {
return 0;
}
uint64_t val = 0x0;
for (int i = 0, int_id = first_intid;
i < 8 * size && int_id < itLines; i++, int_id++) {
val |= irqGrpmod[int_id] << i;
}
return val;
}
}
} else if (GICD_NSACR.contains(addr)) {
// Non-secure Access Control Registers
// 2 bits per interrupt
int first_intid = (addr - GICD_NSACR.start()) * 4;
if (isNotSPI(first_intid)) {
return 0;
}
if (DS || (!DS && !is_secure_access)) {
return 0;
}
uint64_t val = 0x0;
for (int i = 0, int_id = first_intid;
i < 8 * size && int_id < itLines; i = i + 2, int_id++) {
val |= irqNsacr[int_id] << i;
}
return val;
} else if (GICD_CPENDSGIR.contains(addr)) { // SGI Clear-Pending Registers
// ARE always on, RAZ/WI
warn("Gicv3Distributor::read(): "
"GICD_CPENDSGIR is RAZ/WI, legacy not supported!\n");
return 0x0;
} else if (GICD_SPENDSGIR.contains(addr)) { // SGI Set-Pending Registers
// ARE always on, RAZ/WI
warn("Gicv3Distributor::read(): "
"GICD_SPENDSGIR is RAZ/WI, legacy not supported!\n");
return 0x0;
} else if (GICD_IROUTER.contains(addr)) { // Interrupt Routing Registers
// 64 bit registers. 2 or 1 access.
int int_id = (addr - GICD_IROUTER.start()) / 8;
if (isNotSPI(int_id)) {
return 0;
}
if (nsAccessToSecInt(int_id, is_secure_access))
{
if (irqNsacr[int_id] < 3) {
return 0;
}
}
if (size == 4) {
if (addr & 7) { // high half of 64 bit register
return irqAffinityRouting[int_id] >> 32;
} else { // high low of 64 bit register
return irqAffinityRouting[int_id] & 0xFFFFFFFF;
}
} else {
return irqAffinityRouting[int_id];
}
}
switch (addr) {
case GICD_CTLR: // Control Register
if (!DS) {
if (is_secure_access) {
// E1NWF [7] RAZ/WI
// DS [6] - Disable Security
// ARE_NS [5] RAO/WI
// ARE_S [4] RAO/WI
// EnableGrp1S [2]
// EnableGrp1NS [1]
// EnableGrp0 [0]
return (EnableGrp0 << 0) |
(EnableGrp1NS << 1) |
(EnableGrp1S << 2) |
(1 << 4) |
(1 << 5) |
(DS << 6);
} else {
// ARE_NS [4] RAO/WI;
// EnableGrp1A [1] is a read-write alias of the Secure
// GICD_CTLR.EnableGrp1NS
// EnableGrp1 [0] RES0
return (1 << 4) | (EnableGrp1NS << 1);
}
} else {
return (DS << 6) | (ARE << 4) |
(EnableGrp1NS << 1) | (EnableGrp0 << 0);
}
case GICD_TYPER: // Interrupt Controller Type Register
return gicdTyper;
case GICD_IIDR: // Implementer Identification Register
//return 0x43b; // ARM JEP106 code (r0p0 GIC-500)
return 0;
case GICD_STATUSR: // Error Reporting Status Register
// Optional register, RAZ/WI
return 0x0;
case GICD_PIDR0: // Peripheral ID0 Register
return gicdPidr0;
case GICD_PIDR1: // Peripheral ID1 Register
return gicdPidr1;
case GICD_PIDR2: // Peripheral ID2 Register
return gicdPidr2;
case GICD_PIDR3: // Peripheral ID3 Register
return gicdPidr3;
case GICD_PIDR4: // Peripheral ID4 Register
return gicdPidr4;
case GICD_PIDR5: // Peripheral ID5 Register
case GICD_PIDR6: // Peripheral ID6 Register
case GICD_PIDR7: // Peripheral ID7 Register
return 0; // RES0
default:
panic("Gicv3Distributor::read(): invalid offset %#x\n", addr);
break;
}
}
void
Gicv3Distributor::write(Addr addr, uint64_t data, size_t size,
bool is_secure_access)
{
if (GICD_IGROUPR.contains(addr)) { // Interrupt Group Registers
if (!DS && !is_secure_access) {
// RAZ/WI for non-secure accesses
return;
}
int first_intid = (addr - GICD_IGROUPR.start()) * 8;
if (isNotSPI(first_intid)) {
return;
}
for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
i++, int_id++) {
irqGroup[int_id] = data & (1 << i) ? 1 : 0;
DPRINTF(GIC, "Gicv3Distributor::write(): int_id %d group %d\n",
int_id, irqGroup[int_id]);
}
return;
} else if (GICD_ISENABLER.contains(addr)) {
// Interrupt Set-Enable Registers
int first_intid = (addr - GICD_ISENABLER.start()) * 8;
if (isNotSPI(first_intid)) {
return;
}
for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
i++, int_id++) {
if (nsAccessToSecInt(int_id, is_secure_access))
{
continue;
}
bool enable = data & (1 << i) ? 1 : 0;
if (enable) {
if (!irqEnabled[int_id]) {
DPRINTF(GIC, "Gicv3Distributor::write(): "
"int_id %d enabled\n", int_id);
}
irqEnabled[int_id] = true;
}
}
return;
} else if (GICD_ICENABLER.contains(addr)) {
// Interrupt Clear-Enable Registers
int first_intid = (addr - GICD_ICENABLER.start()) * 8;
if (isNotSPI(first_intid)) {
return;
}
for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
i++, int_id++) {
if (nsAccessToSecInt(int_id, is_secure_access))
{
continue;
}
bool disable = data & (1 << i) ? 1 : 0;
if (disable) {
if (irqEnabled[int_id]) {
DPRINTF(GIC, "Gicv3Distributor::write(): "
"int_id %d disabled\n", int_id);
}
irqEnabled[int_id] = false;
}
}
return;
} else if (GICD_ISPENDR.contains(addr)) {
// Interrupt Set-Pending Registers
int first_intid = (addr - GICD_ISPENDR.start()) * 8;
if (isNotSPI(first_intid)) {
return;
}
for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
i++, int_id++) {
if (nsAccessToSecInt(int_id, is_secure_access))
{
if (irqNsacr[int_id] == 0) {
// Group 0 or Secure Group 1 interrupts are RAZ/WI
continue;
}
}
bool pending = data & (1 << i) ? 1 : 0;
if (pending) {
DPRINTF(GIC, "Gicv3Distributor::write() (GICD_ISPENDR): "
"int_id %d (SPI) pending bit set\n", int_id);
irqPending[int_id] = true;
irqPendingIspendr[int_id] = true;
}
}
update();
return;
} else if (GICD_ICPENDR.contains(addr)) {
// Interrupt Clear-Pending Registers
int first_intid = (addr - GICD_ICPENDR.start()) * 8;
if (isNotSPI(first_intid)) {
return;
}
for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
i++, int_id++) {
if (nsAccessToSecInt(int_id, is_secure_access))
{
if (irqNsacr[int_id] < 2) {
// Group 0 or Secure Group 1 interrupts are RAZ/WI
continue;
}
}
bool clear = data & (1 << i) ? 1 : 0;
if (clear && treatAsEdgeTriggered(int_id)) {
irqPending[int_id] = false;
clearIrqCpuInterface(int_id);
}
}
update();
return;
} else if (GICD_ISACTIVER.contains(addr)) {
// Interrupt Set-Active Registers
int first_intid = (addr - GICD_ISACTIVER.start()) * 8;
if (isNotSPI(first_intid)) {
return;
}
for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
i++, int_id++) {
if (nsAccessToSecInt(int_id, is_secure_access))
{
continue;
}
bool active = data & (1 << i) ? 1 : 0;
if (active) {
irqActive[int_id] = 1;
}
}
return;
} else if (GICD_ICACTIVER.contains(addr)) {
// Interrupt Clear-Active Registers
int first_intid = (addr - GICD_ICACTIVER.start()) * 8;
if (isNotSPI(first_intid)) {
return;
}
for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
i++, int_id++) {
if (nsAccessToSecInt(int_id, is_secure_access))
{
continue;
}
bool clear = data & (1 << i) ? 1 : 0;
if (clear) {
if (irqActive[int_id]) {
DPRINTF(GIC, "Gicv3Distributor::write(): "
"int_id %d active cleared\n", int_id);
}
irqActive[int_id] = false;
}
}
return;
} else if (GICD_IPRIORITYR.contains(addr)) {
// Interrupt Priority Registers
int first_intid = addr - GICD_IPRIORITYR.start();
if (isNotSPI(first_intid)) {
return;
}
for (int i = 0, int_id = first_intid; i < size && int_id < itLines;
i++, int_id++) {
uint8_t prio = bits(data, (i + 1) * 8 - 1, (i * 8));
if (!DS && !is_secure_access) {
if (getIntGroup(int_id) != Gicv3::G1NS) {
// RAZ/WI for non-secure accesses to secure interrupts
continue;
} else {
prio = 0x80 | (prio >> 1);
}
}
irqPriority[int_id] = prio;
DPRINTF(GIC, "Gicv3Distributor::write(): int_id %d priority %d\n",
int_id, irqPriority[int_id]);
}
return;
} else if (GICD_ITARGETSR.contains(addr)) {
// Interrupt Processor Targets Registers
// ARE always on, RAZ/WI
warn("Gicv3Distributor::write(): "
"GICD_ITARGETSR is RAZ/WI, legacy not supported!\n");
return;
} else if (GICD_ICFGR.contains(addr)) {
// Interrupt Configuration Registers
// for x = 0 to 15:
// GICD_ICFGR[2x] = RES0
// GICD_ICFGR[2x + 1] =
// 0 level-sensitive
// 1 edge-triggered
int first_intid = (addr - GICD_ICFGR.start()) * 4;
if (isNotSPI(first_intid)) {
return;
}
for (int i = 0, int_id = first_intid; i < 8 * size && int_id < itLines;
i = i + 2, int_id++) {
if (nsAccessToSecInt(int_id, is_secure_access)) {
continue;
}
irqConfig[int_id] = data & (0x2 << i) ?
Gicv3::INT_EDGE_TRIGGERED :
Gicv3::INT_LEVEL_SENSITIVE;
DPRINTF(GIC, "Gicv3Distributor::write(): int_id %d config %d\n",
int_id, irqConfig[int_id]);
}
return;
} else if (GICD_IGRPMODR.contains(addr)) {
// Interrupt Group Modifier Registers
if (DS) {
return;
} else {
if (!is_secure_access) {
// RAZ/WI for non-secure accesses
return;
} else {
int first_intid = (addr - GICD_IGRPMODR.start()) * 8;
if (isNotSPI(first_intid)) {
return;
}
for (int i = 0, int_id = first_intid;
i < 8 * size && int_id < itLines; i++, int_id++) {
irqGrpmod[int_id] = bits(data, i);
}
return ;
}
}
} else if (GICD_NSACR.contains(addr)) {
// Non-secure Access Control Registers
// 2 bits per interrupt
int first_intid = (addr - GICD_NSACR.start()) * 4;
if (isNotSPI(first_intid)) {
return;
}
if (DS || (!DS && !is_secure_access)) {
return;
}
for (int i = 0, int_id = first_intid;
i < 8 * size && int_id < itLines; i = i + 2, int_id++) {
irqNsacr[int_id] = (data >> (2 * int_id)) & 0x3;
}
return;
} else if (GICD_IROUTER.contains(addr)) { // Interrupt Routing Registers
// 64 bit registers. 2 accesses.
int int_id = (addr - GICD_IROUTER.start()) / 8;
if (isNotSPI(int_id)) {
return;
}
if (nsAccessToSecInt(int_id, is_secure_access))
{
if (irqNsacr[int_id] < 3) {
// Group 0 or Secure Group 1 interrupts are RAZ/WI
return;
}
}
if (size == 4) {
if (addr & 7) { // high half of 64 bit register
irqAffinityRouting[int_id] =
(irqAffinityRouting[int_id] & 0xffffffff) | (data << 32);
} else { // low half of 64 bit register
irqAffinityRouting[int_id] =
(irqAffinityRouting[int_id] & 0xffffffff00000000) |
(data & 0xffffffff);
}
} else {
irqAffinityRouting[int_id] = data;
}
DPRINTF(GIC, "Gicv3Distributor::write(): "
"int_id %d GICD_IROUTER %#llx\n",
int_id, irqAffinityRouting[int_id]);
return;
}
switch (addr) {
case GICD_CTLR: // Control Register
if (DS) {
/*
* E1NWF [7]
* 1 of N wakeup functionality not supported, RAZ/WI
* DS [6] - RAO/WI
* ARE [4]
* affinity routing always on, no GICv2 legacy, RAO/WI
* EnableGrp1 [1]
* EnableGrp0 [0]
*/
if ((data & (1 << 4)) == 0) {
warn("Gicv3Distributor::write(): "
"setting ARE to 0 is not supported!\n");
}
EnableGrp1NS = data & GICD_CTLR_ENABLEGRP1NS;
EnableGrp0 = data & GICD_CTLR_ENABLEGRP0;
DPRINTF(GIC, "Gicv3Distributor::write(): (DS 1)"
"EnableGrp1NS %d EnableGrp0 %d\n",
EnableGrp1NS, EnableGrp0);
} else {
if (is_secure_access) {
/*
* E1NWF [7]
* 1 of N wakeup functionality not supported, RAZ/WI
* DS [6]
* ARE_NS [5]
* affinity routing always on, no GICv2 legacy, RAO/WI
* ARE_S [4]
* affinity routing always on, no GICv2 legacy, RAO/WI
* EnableGrp1S [2]
* EnableGrp1NS [1]
* EnableGrp0 [0]
*/
if ((data & (1 << 5)) == 0) {
warn("Gicv3Distributor::write(): "
"setting ARE_NS to 0 is not supported!\n");
}
if ((data & (1 << 4)) == 0) {
warn("Gicv3Distributor::write(): "
"setting ARE_S to 0 is not supported!\n");
}
DS = data & GICD_CTLR_DS;
EnableGrp1S = data & GICD_CTLR_ENABLEGRP1S;
EnableGrp1NS = data & GICD_CTLR_ENABLEGRP1NS;
EnableGrp0 = data & GICD_CTLR_ENABLEGRP0;
DPRINTF(GIC, "Gicv3Distributor::write(): (DS 0 secure)"
"DS %d "
"EnableGrp1S %d EnableGrp1NS %d EnableGrp0 %d\n",
DS, EnableGrp1S, EnableGrp1NS, EnableGrp0);
if (data & GICD_CTLR_DS) {
EnableGrp1S = 0;
}
} else {
/*
* ARE_NS [4] RAO/WI;
* EnableGrp1A [1] is a read-write alias of the Secure
* GICD_CTLR.EnableGrp1NS
* EnableGrp1 [0] RES0
*/
if ((data & (1 << 4)) == 0) {
warn("Gicv3Distributor::write(): "
"setting ARE_NS to 0 is not supported!\n");
}
EnableGrp1NS = data & GICD_CTLR_ENABLEGRP1A;
DPRINTF(GIC, "Gicv3Distributor::write(): (DS 0 non-secure)"
"EnableGrp1NS %d\n", EnableGrp1NS);
}
}
update();
break;
case GICD_SGIR: // Error Reporting Status Register
// Only if affinity routing is disabled, RES0
break;
case GICD_SETSPI_NSR: {
// Writes to this register have no effect if:
// * The value written specifies an invalid SPI.
// * The SPI is already pending.
// * The value written specifies a Secure SPI, the value is
// written by a Non-secure access, and the value of the
// corresponding GICD_NSACR<n> register is 0.
const uint32_t intid = bits(data, 9, 0);
if (isNotSPI(intid) || irqPending[intid] ||
(nsAccessToSecInt(intid, is_secure_access) &&
irqNsacr[intid] == 0)) {
return;
} else {
// Valid SPI, set interrupt pending
sendInt(intid);
}
break;
}
case GICD_CLRSPI_NSR: {
// Writes to this register have no effect if:
// * The value written specifies an invalid SPI.
// * The SPI is not pending.
// * The value written specifies a Secure SPI, the value is
// written by a Non-secure access, and the value of the
// corresponding GICD_NSACR<n> register is less than 0b10.
const uint32_t intid = bits(data, 9, 0);
if (isNotSPI(intid) || !irqPending[intid] ||
(nsAccessToSecInt(intid, is_secure_access) &&
irqNsacr[intid] < 2)) {
return;
} else {
// Valid SPI, clear interrupt pending
deassertSPI(intid);
}
break;
}
case GICD_SETSPI_SR: {
// Writes to this register have no effect if:
// * GICD_CTLR.DS = 1 (WI)
// * The value written specifies an invalid SPI.
// * The SPI is already pending.
// * The value is written by a Non-secure access.
const uint32_t intid = bits(data, 9, 0);
if (DS || isNotSPI(intid) || irqPending[intid] || !is_secure_access) {
return;
} else {
// Valid SPI, set interrupt pending
sendInt(intid);
}
break;
}
case GICD_CLRSPI_SR: {
// Writes to this register have no effect if:
// * GICD_CTLR.DS = 1 (WI)
// * The value written specifies an invalid SPI.
// * The SPI is not pending.
// * The value is written by a Non-secure access.
const uint32_t intid = bits(data, 9, 0);
if (DS || isNotSPI(intid) || !irqPending[intid] || !is_secure_access) {
return;
} else {
// Valid SPI, clear interrupt pending
deassertSPI(intid);
}
break;
}
default:
panic("Gicv3Distributor::write(): invalid offset %#x\n", addr);
break;
}
}
void
Gicv3Distributor::sendInt(uint32_t int_id)
{
panic_if(int_id < Gicv3::SGI_MAX + Gicv3::PPI_MAX, "Invalid SPI!");
panic_if(int_id > itLines, "Invalid SPI!");
irqPending[int_id] = true;
irqPendingIspendr[int_id] = false;
DPRINTF(GIC, "Gicv3Distributor::sendInt(): "
"int_id %d (SPI) pending bit set\n", int_id);
update();
}
void
Gicv3Distributor::clearInt(uint32_t int_id)
{
// Edge-triggered interrupts remain pending until software
// writes GICD_ICPENDR, GICD_CLRSPI_* or activates them via ICC_IAR
if (isLevelSensitive(int_id)) {
deassertSPI(int_id);
}
}
void
Gicv3Distributor::deassertSPI(uint32_t int_id)
{
panic_if(int_id < Gicv3::SGI_MAX + Gicv3::PPI_MAX, "Invalid SPI!");
panic_if(int_id > itLines, "Invalid SPI!");
irqPending[int_id] = false;
clearIrqCpuInterface(int_id);
update();
}
Gicv3CPUInterface*
Gicv3Distributor::route(uint32_t int_id)
{
IROUTER affinity_routing = irqAffinityRouting[int_id];
Gicv3Redistributor * target_redistributor = nullptr;
const Gicv3::GroupId int_group = getIntGroup(int_id);
if (affinity_routing.IRM) {
// Interrupts routed to any PE defined as a participating node
for (int i = 0; i < gic->getSystem()->threads.size(); i++) {
Gicv3Redistributor * redistributor_i =
gic->getRedistributor(i);
if (redistributor_i->
canBeSelectedFor1toNInterrupt(int_group)) {
target_redistributor = redistributor_i;
break;
}
}
} else {
uint32_t affinity = (affinity_routing.Aff3 << 24) |
(affinity_routing.Aff2 << 16) |
(affinity_routing.Aff1 << 8) |
(affinity_routing.Aff0 << 0);
target_redistributor =
gic->getRedistributorByAffinity(affinity);
}
if (!target_redistributor) {
// Interrrupts targeting not present cpus must remain pending
return nullptr;
} else {
return target_redistributor->getCPUInterface();
}
}
void
Gicv3Distributor::clearIrqCpuInterface(uint32_t int_id)
{
auto cpu_interface = route(int_id);
if (cpu_interface)
cpu_interface->resetHppi(int_id);
}
void
Gicv3Distributor::update()
{
// Find the highest priority pending SPI
for (int int_id = Gicv3::SGI_MAX + Gicv3::PPI_MAX; int_id < itLines;
int_id++) {
Gicv3::GroupId int_group = getIntGroup(int_id);
bool group_enabled = groupEnabled(int_group);
if (irqPending[int_id] && irqEnabled[int_id] &&
!irqActive[int_id] && group_enabled) {
// Find the cpu interface where to route the interrupt
Gicv3CPUInterface *target_cpu_interface = route(int_id);
// Invalid routing
if (!target_cpu_interface) continue;
if ((irqPriority[int_id] < target_cpu_interface->hppi.prio) ||
(irqPriority[int_id] == target_cpu_interface->hppi.prio &&
int_id < target_cpu_interface->hppi.intid)) {
target_cpu_interface->hppi.intid = int_id;
target_cpu_interface->hppi.prio = irqPriority[int_id];
target_cpu_interface->hppi.group = int_group;
}
}
}
// Update all redistributors
for (int i = 0; i < gic->getSystem()->threads.size(); i++) {
gic->getRedistributor(i)->update();
}
}
Gicv3::IntStatus
Gicv3Distributor::intStatus(uint32_t int_id) const
{
panic_if(int_id < Gicv3::SGI_MAX + Gicv3::PPI_MAX, "Invalid SPI!");
panic_if(int_id > itLines, "Invalid SPI!");
if (irqPending[int_id]) {
if (irqActive[int_id]) {
return Gicv3::INT_ACTIVE_PENDING;
}
return Gicv3::INT_PENDING;
} else if (irqActive[int_id]) {
return Gicv3::INT_ACTIVE;
} else {
return Gicv3::INT_INACTIVE;
}
}
Gicv3::GroupId
Gicv3Distributor::getIntGroup(int int_id) const
{
panic_if(int_id < Gicv3::SGI_MAX + Gicv3::PPI_MAX, "Invalid SPI!");
panic_if(int_id > itLines, "Invalid SPI!");
if (DS) {
if (irqGroup[int_id] == 1) {
return Gicv3::G1NS;
} else {
return Gicv3::G0S;
}
} else {
if (irqGrpmod[int_id] == 0 && irqGroup[int_id] == 0) {
return Gicv3::G0S;
} else if (irqGrpmod[int_id] == 0 && irqGroup[int_id] == 1) {
return Gicv3::G1NS;
} else if (irqGrpmod[int_id] == 1 && irqGroup[int_id] == 0) {
return Gicv3::G1S;
} else if (irqGrpmod[int_id] == 1 && irqGroup[int_id] == 1) {
return Gicv3::G1NS;
}
}
M5_UNREACHABLE;
}
void
Gicv3Distributor::activateIRQ(uint32_t int_id)
{
if (treatAsEdgeTriggered(int_id)) {
irqPending[int_id] = false;
}
irqActive[int_id] = true;
}
void
Gicv3Distributor::deactivateIRQ(uint32_t int_id)
{
irqActive[int_id] = false;
}
void
Gicv3Distributor::serialize(CheckpointOut & cp) const
{
SERIALIZE_SCALAR(ARE);
SERIALIZE_SCALAR(DS);
SERIALIZE_SCALAR(EnableGrp1S);
SERIALIZE_SCALAR(EnableGrp1NS);
SERIALIZE_SCALAR(EnableGrp0);
SERIALIZE_CONTAINER(irqGroup);
SERIALIZE_CONTAINER(irqEnabled);
SERIALIZE_CONTAINER(irqPending);
SERIALIZE_CONTAINER(irqPendingIspendr);
SERIALIZE_CONTAINER(irqActive);
SERIALIZE_CONTAINER(irqPriority);
SERIALIZE_CONTAINER(irqConfig);
SERIALIZE_CONTAINER(irqGrpmod);
SERIALIZE_CONTAINER(irqNsacr);
SERIALIZE_CONTAINER(irqAffinityRouting);
}
void
Gicv3Distributor::unserialize(CheckpointIn & cp)
{
UNSERIALIZE_SCALAR(ARE);
UNSERIALIZE_SCALAR(DS);
UNSERIALIZE_SCALAR(EnableGrp1S);
UNSERIALIZE_SCALAR(EnableGrp1NS);
UNSERIALIZE_SCALAR(EnableGrp0);
UNSERIALIZE_CONTAINER(irqGroup);
UNSERIALIZE_CONTAINER(irqEnabled);
UNSERIALIZE_CONTAINER(irqPending);
UNSERIALIZE_CONTAINER(irqPendingIspendr);
UNSERIALIZE_CONTAINER(irqActive);
UNSERIALIZE_CONTAINER(irqPriority);
UNSERIALIZE_CONTAINER(irqConfig);
UNSERIALIZE_CONTAINER(irqGrpmod);
UNSERIALIZE_CONTAINER(irqNsacr);
UNSERIALIZE_CONTAINER(irqAffinityRouting);
}