diff options
Diffstat (limited to 'src/dev/arm/gic_v3_distributor.cc')
-rw-r--r-- | src/dev/arm/gic_v3_distributor.cc | 1132 |
1 files changed, 1132 insertions, 0 deletions
diff --git a/src/dev/arm/gic_v3_distributor.cc b/src/dev/arm/gic_v3_distributor.cc new file mode 100644 index 000000000..00f29a74c --- /dev/null +++ b/src/dev/arm/gic_v3_distributor.cc @@ -0,0 +1,1132 @@ +/* + * 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. + * + * Authors: Jairo Balart + */ +#include "dev/arm/gic_v3_distributor.hh" + +#include <algorithm> + +#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, 0x00ff); +const AddrRange Gicv3Distributor::GICD_ISENABLER(0x0100, 0x017f); +const AddrRange Gicv3Distributor::GICD_ICENABLER(0x0180, 0x01ff); +const AddrRange Gicv3Distributor::GICD_ISPENDR(0x0200, 0x027f); +const AddrRange Gicv3Distributor::GICD_ICPENDR(0x0280, 0x02ff); +const AddrRange Gicv3Distributor::GICD_ISACTIVER(0x0300, 0x037f); +const AddrRange Gicv3Distributor::GICD_ICACTIVER(0x0380, 0x03ff); +const AddrRange Gicv3Distributor::GICD_IPRIORITYR(0x0400, 0x07ff); +const AddrRange Gicv3Distributor::GICD_ITARGETSR(0x0800, 0x08ff); +const AddrRange Gicv3Distributor::GICD_ICFGR(0x0c00, 0x0cff); +const AddrRange Gicv3Distributor::GICD_IGRPMODR(0x0d00, 0x0d7f); +const AddrRange Gicv3Distributor::GICD_NSACR(0x0e00, 0x0eff); +const AddrRange Gicv3Distributor::GICD_CPENDSGIR(0x0f10, 0x0f1f); +const AddrRange Gicv3Distributor::GICD_SPENDSGIR(0x0f20, 0x0f2f); +const AddrRange Gicv3Distributor::GICD_IROUTER(0x6000, 0x7fe0); + +Gicv3Distributor::Gicv3Distributor(Gicv3 * gic, uint32_t it_lines) + : gic(gic), + itLines(it_lines), + irqGroup(it_lines), + irqEnabled(it_lines), + irqPending(it_lines), + irqActive(it_lines), + irqPriority(it_lines), + irqConfig(it_lines), + irqGrpmod(it_lines), + irqNsacr(it_lines), + irqAffinityRouting(it_lines) +{ + panic_if(it_lines > Gicv3::INTID_SECURE, "Invalid value for it_lines!"); +} + +Gicv3Distributor::~Gicv3Distributor() +{ +} + +void +Gicv3Distributor::init() +{ +} + +void +Gicv3Distributor::initState() +{ + reset(); +} + +void +Gicv3Distributor::reset() +{ + std::fill(irqGroup.begin(), irqGroup.end(), 0); + // Imp. defined reset value + std::fill(irqEnabled.begin(), irqEnabled.end(), false); + std::fill(irqPending.begin(), irqPending.end(), false); + std::fill(irqActive.begin(), irqActive.end(), false); + // Imp. defined reset value + std::fill(irqPriority.begin(), irqPriority.end(), 0xAAAAAAAA); + std::fill(irqConfig.begin(), irqConfig.end(), + Gicv3::INT_LEVEL_SENSITIVE); // Imp. defined reset value + std::fill(irqGrpmod.begin(), irqGrpmod.end(), 0); + std::fill(irqNsacr.begin(), irqNsacr.end(), 0); + /* + * For our implementation affinity routing is always enabled, + * no GICv2 legacy + */ + ARE = true; + + if (gic->getSystem()->haveSecurity()) { + DS = false; + } else { + DS = true; + } + + EnableGrp0 = 0; + EnableGrp1NS = 0; + EnableGrp1S = 0; +} + +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; + // Interrupt Set-Enable Registers + } else if (GICD_ISENABLER.contains(addr)) { + 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)) { + 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)) { + 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; + // Interrupt Set-Active Registers + } else if (GICD_ISACTIVER.contains(addr)) { + 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; + // Interrupt Clear-Active Registers + } else if (GICD_ICACTIVER.contains(addr)) { + 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; + // Interrupt Priority Registers + } else if (GICD_IPRIORITYR.contains(addr)) { + 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; + // Interrupt Configuration Registers + } else if (GICD_ICFGR.contains(addr)) { + 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; + } + } + + // Non-secure Access Control Registers + } else if (GICD_NSACR.contains(addr)) { + // 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 + /* + * 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] == 0 + * (The implementation does not support LPIs) + * MBIS [16] == 0 + * (The implementation does not support 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 = ceil((max_spi_int_id + 1) / 32.0) - 1; + return (1 << 26) | (1 << 25) | (1 << 24) | (0xf << 19) | + (gic->getSystem()->haveSecurity() << 10) | + (it_lines_number << 0); + } + + 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 + uint8_t part_0 = 0x92; // Part number, bits[7:0] + return part_0; + } + + case GICD_PIDR1: { // Peripheral ID1 Register + uint8_t des_0 = 0xB; // JEP106 identification code, bits[3:0] + uint8_t part_1 = 0x4; // Part number, bits[11:8] + return (des_0 << 4) | (part_1 << 0); + } + + case GICD_PIDR2: { // Peripheral ID2 Register + uint8_t arch_rev = 0x3; // 0x3 GICv3 + uint8_t jdec = 0x1; // JEP code + uint8_t des_1 = 0x3; // JEP106 identification code, bits[6:4] + return (arch_rev << 4) | (jdec << 3) | (des_1 << 0); + } + + case GICD_PIDR3: // Peripheral ID3 Register + return 0x0; // Implementation defined + + case GICD_PIDR4: { // Peripheral ID4 Register + uint8_t size = 0x4; // 64 KB software visible page + uint8_t des_2 = 0x4; // ARM implementation + return (size << 4) | (des_2 << 0); + } + + 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; + // Interrupt Set-Enable Registers + } else if (GICD_ISENABLER.contains(addr)) { + 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; + + 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)) { + 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; + } + } + + updateAndInformCPUInterfaces(); + return; + } else if (GICD_ICPENDR.contains(addr)) { + 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) { + irqPending[int_id] = false; + } + } + + updateAndInformCPUInterfaces(); + return; + // Interrupt Set-Active Registers + } else if (GICD_ISACTIVER.contains(addr)) { + 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; + // Interrupt Priority Registers + } else if (GICD_IPRIORITYR.contains(addr)) { + 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; + // Interrupt Configuration Registers + } else if (GICD_ICFGR.contains(addr)) { + /* Here only the odd bits are used; even bits are RES0 */ + 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++) { + 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] = data & (0x1 << i); + } + + return ; + } + } + + // Non-secure Access Control Registers + } else if (GICD_NSACR.contains(addr)) { + // 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); + } + } + + 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; + DPRINTF(GIC, "Gicv3Distributor::sendInt(): " + "int_id %d (SPI) pending bit set\n", int_id); + updateAndInformCPUInterfaces(); +} + +void +Gicv3Distributor::intDeasserted(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; + updateAndInformCPUInterfaces(); +} + +void +Gicv3Distributor::updateAndInformCPUInterfaces() +{ + update(); + + for (int i = 0; i < gic->getSystem()->numContexts(); i++) { + gic->getCPUInterface(i)->update(); + } +} + +void +Gicv3Distributor::fullUpdate() +{ + for (int i = 0; i < gic->getSystem()->numContexts(); i++) { + Gicv3CPUInterface * cpu_interface_i = gic->getCPUInterface(i); + cpu_interface_i->hppi.prio = 0xff; + } + + update(); + + for (int i = 0; i < gic->getSystem()->numContexts(); i++) { + Gicv3Redistributor * redistributor_i = gic->getRedistributor(i); + redistributor_i->update(); + } +} + +void +Gicv3Distributor::update() +{ + std::vector<bool> new_hppi(gic->getSystem()->numContexts(), false); + + // 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) { + IROUTER affinity_routing = irqAffinityRouting[int_id]; + Gicv3Redistributor * target_redistributor = nullptr; + + if (affinity_routing.IRM) { + // Interrupts routed to any PE defined as a participating node + for (int i = 0; i < gic->getSystem()->numContexts(); 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.Aff3 << 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; + } + + Gicv3CPUInterface * target_cpu_interface = + target_redistributor->getCPUInterface(); + uint32_t target_cpu = target_redistributor->cpuId; + + if ((irqPriority[int_id] < target_cpu_interface->hppi.prio) || + /* + * Multiple pending ints with same priority. + * Implementation choice which one to signal. + * Our implementation selects the one with the lower id. + */ + (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; + new_hppi[target_cpu] = true; + } + } + } + + for (int i = 0; i < gic->getSystem()->numContexts(); i++) { + Gicv3Redistributor * redistributor_i = gic->getRedistributor(i); + Gicv3CPUInterface * cpu_interface_i = + redistributor_i->getCPUInterface(); + + if (!new_hppi[i] && cpu_interface_i->hppi.prio != 0xff && + cpu_interface_i->hppi.intid >= + (Gicv3::SGI_MAX + Gicv3::PPI_MAX) && + cpu_interface_i->hppi.intid < Gicv3::INTID_SECURE) { + fullUpdate(); + } + } +} + +Gicv3::IntStatus +Gicv3Distributor::intStatus(uint32_t int_id) +{ + 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) +{ + 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) +{ + 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(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(irqActive); + UNSERIALIZE_CONTAINER(irqPriority); + UNSERIALIZE_CONTAINER(irqConfig); + UNSERIALIZE_CONTAINER(irqGrpmod); + UNSERIALIZE_CONTAINER(irqNsacr); + UNSERIALIZE_CONTAINER(irqAffinityRouting); +} |