/* * 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 #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 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); }