| /* |
| * Copyright (c) 2019-2022 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_redistributor.hh" |
| |
| #include "arch/arm/utility.hh" |
| #include "base/compiler.hh" |
| #include "debug/GIC.hh" |
| #include "dev/arm/gic_v3_cpu_interface.hh" |
| #include "dev/arm/gic_v3_distributor.hh" |
| |
| namespace gem5 |
| { |
| |
| using namespace ArmISA; |
| |
| const AddrRange Gicv3Redistributor::GICR_IPRIORITYR(SGI_base + 0x0400, |
| SGI_base + 0x0420); |
| |
| Gicv3Redistributor::Gicv3Redistributor(Gicv3 * gic, uint32_t cpu_id) |
| : gic(gic), |
| distributor(nullptr), |
| cpuInterface(nullptr), |
| cpuId(cpu_id), |
| memProxy(nullptr), |
| peInLowPowerState(true), |
| irqGroup(Gicv3::SGI_MAX + Gicv3::PPI_MAX, 0), |
| irqEnabled(Gicv3::SGI_MAX + Gicv3::PPI_MAX, false), |
| irqPending(Gicv3::SGI_MAX + Gicv3::PPI_MAX, false), |
| irqPendingIspendr(Gicv3::SGI_MAX + Gicv3::PPI_MAX, false), |
| irqActive(Gicv3::SGI_MAX + Gicv3::PPI_MAX, false), |
| irqPriority(Gicv3::SGI_MAX + Gicv3::PPI_MAX, 0), |
| irqConfig(Gicv3::SGI_MAX + Gicv3::PPI_MAX, Gicv3::INT_EDGE_TRIGGERED), |
| irqGrpmod(Gicv3::SGI_MAX + Gicv3::PPI_MAX, 0), |
| irqNsacr(Gicv3::SGI_MAX + Gicv3::PPI_MAX, 0), |
| DPG1S(false), |
| DPG1NS(false), |
| DPG0(false), |
| EnableLPIs(false), |
| lpiConfigurationTablePtr(0), |
| lpiIDBits(0), |
| lpiPendingTablePtr(0), |
| addrRangeSize(gic->params().gicv4 ? 0x40000 : 0x20000) |
| { |
| } |
| |
| void |
| Gicv3Redistributor::init() |
| { |
| distributor = gic->getDistributor(); |
| cpuInterface = gic->getCPUInterface(cpuId); |
| |
| memProxy = &gic->getSystem()->physProxy; |
| } |
| |
| uint64_t |
| Gicv3Redistributor::read(Addr addr, size_t size, bool is_secure_access) |
| { |
| if (GICR_IPRIORITYR.contains(addr)) { // Interrupt Priority Registers |
| uint64_t value = 0; |
| int first_intid = addr - GICR_IPRIORITYR.start(); |
| |
| for (int i = 0, int_id = first_intid; i < size; i++, int_id++) { |
| uint8_t prio = irqPriority[int_id]; |
| |
| if (!distributor->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; |
| } |
| } |
| |
| value |= prio << (i * 8); |
| } |
| |
| return value; |
| } |
| |
| switch (addr) { |
| case GICR_CTLR: { // Control Register |
| uint64_t value = 0; |
| |
| if (DPG1S) { |
| value |= GICR_CTLR_DPG1S; |
| } |
| |
| if (DPG1NS) { |
| value |= GICR_CTLR_DPG1NS; |
| } |
| |
| if (DPG0) { |
| value |= GICR_CTLR_DPG0; |
| } |
| |
| if (EnableLPIs) { |
| value |= GICR_CTLR_ENABLE_LPIS; |
| } |
| |
| return value; |
| } |
| |
| case GICR_IIDR: // Implementer Identification Register |
| //return 0x43b; // r0p0 GIC-500 |
| return 0; |
| |
| case GICR_TYPER: { // Type Register |
| /* |
| * Affinity_Value [63:32] == X |
| * (The identity of the PE associated with this Redistributor) |
| * CommonLPIAff [25:24] == 01 |
| * (All Redistributors with the same Aff3 value must share an |
| * LPI Configuration table) |
| * Processor_Number [23:8] == X |
| * (A unique identifier for the PE) |
| * DPGS [5] == 1 |
| * (GICR_CTLR.DPG* bits are supported) |
| * Last [4] == X |
| * (This Redistributor is the highest-numbered Redistributor in |
| * a series of contiguous Redistributor pages) |
| * DirectLPI [3] == 1 |
| * (direct injection of LPIs supported) |
| * VLPIS [1] == 0 |
| * (virtual LPIs not supported) |
| * PLPIS [0] == 1 |
| * (physical LPIs supported) |
| */ |
| uint64_t affinity = getAffinity(); |
| int last = cpuId == (gic->getSystem()->threads.size() - 1); |
| return (affinity << 32) | (1 << 24) | (cpuId << 8) | |
| (1 << 5) | (last << 4) | (1 << 3) | (1 << 0); |
| } |
| |
| case GICR_WAKER: // Wake Register |
| if (!distributor->DS && !is_secure_access) { |
| // RAZ/WI for non-secure accesses |
| return 0; |
| } |
| |
| if (peInLowPowerState) { |
| return GICR_WAKER_ChildrenAsleep | GICR_WAKER_ProcessorSleep; |
| } else { |
| return 0; |
| } |
| |
| case GICR_PIDR0: { // Peripheral ID0 Register |
| return 0x92; // Part number, bits[7:0] |
| } |
| |
| case GICR_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 GICR_PIDR2: { // Peripheral ID2 Register |
| return gic->getDistributor()->gicdPidr2; |
| } |
| |
| case GICR_PIDR3: // Peripheral ID3 Register |
| return 0x0; // Implementation defined |
| |
| case GICR_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 GICR_PIDR5: // Peripheral ID5 Register |
| case GICR_PIDR6: // Peripheral ID6 Register |
| case GICR_PIDR7: // Peripheral ID7 Register |
| return 0; // RES0 |
| |
| case GICR_IGROUPR0: { // Interrupt Group Register 0 |
| uint64_t value = 0; |
| |
| if (!distributor->DS && !is_secure_access) { |
| // RAZ/WI for non-secure accesses |
| return 0; |
| } |
| |
| for (int int_id = 0; int_id < 8 * size; int_id++) { |
| value |= (irqGroup[int_id] << int_id); |
| } |
| |
| return value; |
| } |
| |
| case GICR_ISENABLER0: // Interrupt Set-Enable Register 0 |
| case GICR_ICENABLER0: { // Interrupt Clear-Enable Register 0 |
| uint64_t value = 0; |
| |
| for (int int_id = 0; int_id < 8 * size; int_id++) { |
| if (!distributor->DS && !is_secure_access) { |
| // RAZ/WI for non-secure accesses for secure interrupts |
| if (getIntGroup(int_id) != Gicv3::G1NS) { |
| continue; |
| } |
| } |
| |
| if (irqEnabled[int_id]) { |
| value |= (1 << int_id); |
| } |
| } |
| |
| return value; |
| } |
| |
| case GICR_ISPENDR0: // Interrupt Set-Pending Register 0 |
| case GICR_ICPENDR0: { // Interrupt Clear-Pending Register 0 |
| uint64_t value = 0; |
| |
| for (int int_id = 0; int_id < 8 * size; int_id++) { |
| if (!distributor->DS && !is_secure_access) { |
| // RAZ/WI for non-secure accesses for secure interrupts |
| if (getIntGroup(int_id) != Gicv3::G1NS) { |
| continue; |
| } |
| } |
| |
| value |= (irqPending[int_id] << int_id); |
| } |
| |
| return value; |
| } |
| |
| case GICR_ISACTIVER0: // Interrupt Set-Active Register 0 |
| case GICR_ICACTIVER0: { // Interrupt Clear-Active Register 0 |
| uint64_t value = 0; |
| |
| for (int int_id = 0; int_id < 8 * size; int_id++) { |
| if (!distributor->DS && !is_secure_access) { |
| // RAZ/WI for non-secure accesses for secure interrupts |
| if (getIntGroup(int_id) != Gicv3::G1NS) { |
| continue; |
| } |
| } |
| |
| value |= irqActive[int_id] << int_id; |
| } |
| |
| return value; |
| } |
| |
| case GICR_ICFGR0: // SGI Configuration Register |
| case GICR_ICFGR1: { // PPI Configuration Register |
| uint64_t value = 0; |
| uint32_t first_int_id = addr == GICR_ICFGR0 ? 0 : Gicv3::SGI_MAX; |
| |
| for (int i = 0, int_id = first_int_id; i < 32; |
| i = i + 2, int_id++) { |
| if (!distributor->DS && !is_secure_access) { |
| // RAZ/WI for non-secure accesses for secure interrupts |
| if (getIntGroup(int_id) != Gicv3::G1NS) { |
| continue; |
| } |
| } |
| |
| if (irqConfig[int_id] == Gicv3::INT_EDGE_TRIGGERED) { |
| value |= (0x2) << i; |
| } |
| } |
| |
| return value; |
| } |
| |
| case GICR_IGRPMODR0: { // Interrupt Group Modifier Register 0 |
| uint64_t value = 0; |
| |
| if (distributor->DS) { |
| value = 0; |
| } else { |
| if (!is_secure_access) { |
| // RAZ/WI for non-secure accesses |
| value = 0; |
| } else { |
| for (int int_id = 0; int_id < 8 * size; int_id++) { |
| value |= irqGrpmod[int_id] << int_id; |
| } |
| } |
| } |
| |
| return value; |
| } |
| |
| case GICR_NSACR: { // Non-secure Access Control Register |
| uint64_t value = 0; |
| |
| if (distributor->DS) { |
| // RAZ/WI |
| value = 0; |
| } else { |
| if (!is_secure_access) { |
| // RAZ/WI |
| value = 0; |
| } else { |
| for (int i = 0, int_id = 0; i < 8 * size; |
| i = i + 2, int_id++) { |
| value |= irqNsacr[int_id] << i; |
| } |
| } |
| } |
| |
| return value; |
| } |
| |
| case GICR_PROPBASER: // Redistributor Properties Base Address Register |
| // OuterCache, bits [58:56] |
| // 000 Memory type defined in InnerCache field |
| // Physical_Address, bits [51:12] |
| // Bits [51:12] of the physical address containing the LPI |
| // Configuration table |
| // Shareability, bits [11:10] |
| // 00 Non-shareable |
| // InnerCache, bits [9:7] |
| // 000 Device-nGnRnE |
| // IDbits, bits [4:0] |
| // limited by GICD_TYPER.IDbits |
| return lpiConfigurationTablePtr | lpiIDBits; |
| |
| // Redistributor LPI Pending Table Base Address Register |
| case GICR_PENDBASER: |
| // PTZ, bit [62] |
| // Pending Table Zero |
| // OuterCache, bits [58:56] |
| // 000 Memory type defined in InnerCache field |
| // Physical_Address, bits [51:16] |
| // Bits [51:16] of the physical address containing the LPI Pending |
| // table |
| // Shareability, bits [11:10] |
| // 00 Non-shareable |
| // InnerCache, bits [9:7] |
| // 000 Device-nGnRnE |
| return lpiPendingTablePtr; |
| |
| // Redistributor Synchronize Register |
| case GICR_SYNCR: |
| return 0; |
| |
| default: |
| panic("Gicv3Redistributor::read(): invalid offset %#x\n", addr); |
| break; |
| } |
| } |
| |
| void |
| Gicv3Redistributor::write(Addr addr, uint64_t data, size_t size, |
| bool is_secure_access) |
| { |
| if (GICR_IPRIORITYR.contains(addr)) { // Interrupt Priority Registers |
| int first_intid = addr - GICR_IPRIORITYR.start(); |
| |
| for (int i = 0, int_id = first_intid; i < size; i++, int_id++) { |
| uint8_t prio = bits(data, (i + 1) * 8 - 1, (i * 8)); |
| |
| if (!distributor->DS && !is_secure_access) { |
| if (getIntGroup(int_id) != Gicv3::G1NS) { |
| // RAZ/WI for non-secure accesses for secure interrupts |
| continue; |
| } else { |
| // NS view |
| prio = 0x80 | (prio >> 1); |
| } |
| } |
| |
| irqPriority[int_id] = prio; |
| DPRINTF(GIC, "Gicv3Redistributor::write(): " |
| "int_id %d priority %d\n", int_id, irqPriority[int_id]); |
| } |
| |
| return; |
| } |
| |
| switch (addr) { |
| case GICR_CTLR: { |
| // GICR_TYPER.LPIS is 0 so EnableLPIs is RES0 |
| EnableLPIs = data & GICR_CTLR_ENABLE_LPIS; |
| DPG1S = data & GICR_CTLR_DPG1S; |
| DPG1NS = data & GICR_CTLR_DPG1NS; |
| DPG0 = data & GICR_CTLR_DPG0; |
| break; |
| } |
| |
| case GICR_WAKER: // Wake Register |
| { |
| if (!distributor->DS && !is_secure_access) { |
| // RAZ/WI for non-secure accesses |
| return; |
| } |
| |
| bool pe_was_low_power = peInLowPowerState; |
| peInLowPowerState = data & GICR_WAKER_ProcessorSleep; |
| if (!pe_was_low_power && peInLowPowerState) { |
| DPRINTF(GIC, "Gicv3Redistributor::write(): " |
| "PE entering in low power state\n"); |
| updateDistributor(); |
| } else if (pe_was_low_power && !peInLowPowerState) { |
| DPRINTF(GIC, "Gicv3Redistributor::write(): powering up PE\n"); |
| cpuInterface->deassertWakeRequest(); |
| updateDistributor(); |
| } |
| break; |
| } |
| |
| case GICR_IGROUPR0: // Interrupt Group Register 0 |
| if (!distributor->DS && !is_secure_access) { |
| // RAZ/WI for non-secure accesses |
| return; |
| } |
| |
| for (int int_id = 0; int_id < 8 * size; int_id++) { |
| irqGroup[int_id] = data & (1 << int_id) ? 1 : 0; |
| DPRINTF(GIC, "Gicv3Redistributor::write(): " |
| "int_id %d group %d\n", int_id, irqGroup[int_id]); |
| } |
| |
| break; |
| |
| case GICR_ISENABLER0: // Interrupt Set-Enable Register 0 |
| for (int int_id = 0; int_id < 8 * size; int_id++) { |
| if (!distributor->DS && !is_secure_access) { |
| // RAZ/WI for non-secure accesses for secure interrupts |
| if (getIntGroup(int_id) != Gicv3::G1NS) { |
| continue; |
| } |
| } |
| |
| bool enable = data & (1 << int_id) ? 1 : 0; |
| |
| if (enable) { |
| irqEnabled[int_id] = true; |
| } |
| |
| DPRINTF(GIC, "Gicv3Redistributor::write(): " |
| "int_id %d enable %i\n", int_id, irqEnabled[int_id]); |
| } |
| |
| break; |
| |
| case GICR_ICENABLER0: // Interrupt Clear-Enable Register 0 |
| for (int int_id = 0; int_id < 8 * size; int_id++) { |
| if (!distributor->DS && !is_secure_access) { |
| // RAZ/WI for non-secure accesses for secure interrupts |
| if (getIntGroup(int_id) != Gicv3::G1NS) { |
| continue; |
| } |
| } |
| |
| bool disable = data & (1 << int_id) ? 1 : 0; |
| |
| if (disable) { |
| irqEnabled[int_id] = false; |
| } |
| |
| DPRINTF(GIC, "Gicv3Redistributor::write(): " |
| "int_id %d enable %i\n", int_id, irqEnabled[int_id]); |
| } |
| |
| break; |
| |
| case GICR_ISPENDR0: // Interrupt Set-Pending Register 0 |
| for (int int_id = 0; int_id < 8 * size; int_id++) { |
| if (!distributor->DS && !is_secure_access) { |
| // RAZ/WI for non-secure accesses for secure interrupts |
| if (getIntGroup(int_id) != Gicv3::G1NS) { |
| continue; |
| } |
| } |
| |
| bool pending = data & (1 << int_id) ? 1 : 0; |
| |
| if (pending) { |
| DPRINTF(GIC, "Gicv3Redistributor::write() " |
| "(GICR_ISPENDR0): int_id %d (PPI) " |
| "pending bit set\n", int_id); |
| irqPending[int_id] = true; |
| irqPendingIspendr[int_id] = true; |
| } |
| } |
| |
| updateDistributor(); |
| break; |
| |
| case GICR_ICPENDR0:// Interrupt Clear-Pending Register 0 |
| for (int int_id = 0; int_id < 8 * size; int_id++) { |
| if (!distributor->DS && !is_secure_access) { |
| // RAZ/WI for non-secure accesses for secure interrupts |
| if (getIntGroup(int_id) != Gicv3::G1NS) { |
| continue; |
| } |
| } |
| |
| bool clear = data & (1 << int_id) ? 1 : 0; |
| |
| if (clear && treatAsEdgeTriggered(int_id)) { |
| irqPending[int_id] = false; |
| } |
| } |
| |
| break; |
| |
| case GICR_ISACTIVER0: // Interrupt Set-Active Register 0 |
| for (int int_id = 0; int_id < 8 * size; int_id++) { |
| if (!distributor->DS && !is_secure_access) { |
| // RAZ/WI for non-secure accesses for secure interrupts |
| if (getIntGroup(int_id) != Gicv3::G1NS) { |
| continue; |
| } |
| } |
| |
| bool activate = data & (1 << int_id) ? 1 : 0; |
| |
| if (activate) { |
| if (!irqActive[int_id]) { |
| DPRINTF(GIC, "Gicv3Redistributor::write(): " |
| "int_id %d active set\n", int_id); |
| } |
| |
| irqActive[int_id] = true; |
| } |
| } |
| |
| break; |
| |
| case GICR_ICACTIVER0: // Interrupt Clear-Active Register 0 |
| for (int int_id = 0; int_id < 8 * size; int_id++) { |
| if (!distributor->DS && !is_secure_access) { |
| // RAZ/WI for non-secure accesses for secure interrupts |
| if (getIntGroup(int_id) != Gicv3::G1NS) { |
| continue; |
| } |
| } |
| |
| bool clear = data & (1 << int_id) ? 1 : 0; |
| |
| if (clear) { |
| if (irqActive[int_id]) { |
| DPRINTF(GIC, "Gicv3Redistributor::write(): " |
| "int_id %d active cleared\n", int_id); |
| } |
| |
| irqActive[int_id] = false; |
| } |
| } |
| |
| break; |
| |
| case GICR_ICFGR0: // SGI Configuration Register |
| // WI |
| return; |
| case GICR_ICFGR1: { // PPI Configuration Register |
| int first_intid = Gicv3::SGI_MAX; |
| |
| for (int i = 0, int_id = first_intid; i < 8 * size; |
| i = i + 2, int_id++) { |
| if (!distributor->DS && !is_secure_access) { |
| // RAZ/WI for non-secure accesses for secure interrupts |
| if (getIntGroup(int_id) != Gicv3::G1NS) { |
| continue; |
| } |
| } |
| |
| irqConfig[int_id] = data & (0x2 << i) ? |
| Gicv3::INT_EDGE_TRIGGERED : |
| Gicv3::INT_LEVEL_SENSITIVE; |
| DPRINTF(GIC, "Gicv3Redistributor::write(): " |
| "int_id %d (PPI) config %d\n", |
| int_id, irqConfig[int_id]); |
| } |
| |
| break; |
| } |
| |
| case GICR_IGRPMODR0: { // Interrupt Group Modifier Register 0 |
| if (distributor->DS) { |
| // RAZ/WI if secutiry disabled |
| } else { |
| for (int int_id = 0; int_id < 8 * size; int_id++) { |
| if (!is_secure_access) { |
| // RAZ/WI for non-secure accesses |
| continue; |
| } |
| |
| irqGrpmod[int_id] = data & (1 << int_id); |
| } |
| } |
| |
| break; |
| } |
| |
| case GICR_NSACR: { // Non-secure Access Control Register |
| if (distributor->DS) { |
| // RAZ/WI |
| } else { |
| if (!is_secure_access) { |
| // RAZ/WI |
| } else { |
| for (int i = 0, int_id = 0; i < 8 * size; |
| i = i + 2, int_id++) { |
| irqNsacr[int_id] = (data >> i) & 0x3; |
| } |
| } |
| } |
| |
| break; |
| } |
| |
| case GICR_SETLPIR: // Set LPI Pending Register |
| setClrLPI(data, true); |
| break; |
| |
| case GICR_CLRLPIR: // Clear LPI Pending Register |
| setClrLPI(data, false); |
| break; |
| |
| case GICR_PROPBASER: { // Redistributor Properties Base Address Register |
| // OuterCache, bits [58:56] |
| // 000 Memory type defined in InnerCache field |
| // Physical_Address, bits [51:12] |
| // Bits [51:12] of the physical address containing the LPI |
| // Configuration table |
| // Shareability, bits [11:10] |
| // 00 Non-shareable |
| // InnerCache, bits [9:7] |
| // 000 Device-nGnRnE |
| // IDbits, bits [4:0] |
| // limited by GICD_TYPER.IDbits (= 0xf) |
| lpiConfigurationTablePtr = data & 0xFFFFFFFFFF000; |
| lpiIDBits = data & 0x1f; |
| |
| // 0xf here matches the value of GICD_TYPER.IDbits. |
| // TODO - make GICD_TYPER.IDbits a parameter instead of a hardcoded |
| // value |
| if (lpiIDBits > 0xf) { |
| lpiIDBits = 0xf; |
| } |
| |
| break; |
| } |
| |
| // Redistributor LPI Pending Table Base Address Register |
| case GICR_PENDBASER: |
| // PTZ, bit [62] |
| // Pending Table Zero |
| // OuterCache, bits [58:56] |
| // 000 Memory type defined in InnerCache field |
| // Physical_Address, bits [51:16] |
| // Bits [51:16] of the physical address containing the LPI Pending |
| // table |
| // Shareability, bits [11:10] |
| // 00 Non-shareable |
| // InnerCache, bits [9:7] |
| // 000 Device-nGnRnE |
| lpiPendingTablePtr = data & 0xFFFFFFFFF0000; |
| break; |
| |
| case GICR_INVLPIR: { // Redistributor Invalidate LPI Register |
| // Do nothing: no caching supported |
| break; |
| } |
| |
| case GICR_INVALLR: { // Redistributor Invalidate All Register |
| // Do nothing: no caching supported |
| break; |
| } |
| |
| default: |
| panic("Gicv3Redistributor::write(): invalid offset %#x\n", addr); |
| break; |
| } |
| } |
| |
| void |
| Gicv3Redistributor::sendPPInt(uint32_t int_id) |
| { |
| assert((int_id >= Gicv3::SGI_MAX) && |
| (int_id < Gicv3::SGI_MAX + Gicv3::PPI_MAX)); |
| irqPending[int_id] = true; |
| irqPendingIspendr[int_id] = false; |
| DPRINTF(GIC, "Gicv3Redistributor::sendPPInt(): " |
| "int_id %d (PPI) pending bit set\n", int_id); |
| updateDistributor(); |
| } |
| |
| void |
| Gicv3Redistributor::clearPPInt(uint32_t int_id) |
| { |
| assert((int_id >= Gicv3::SGI_MAX) && |
| (int_id < Gicv3::SGI_MAX + Gicv3::PPI_MAX)); |
| |
| if (isLevelSensitive(int_id)) { |
| irqPending[int_id] = false; |
| } |
| } |
| |
| void |
| Gicv3Redistributor::sendSGI(uint32_t int_id, Gicv3::GroupId group, bool ns) |
| { |
| assert(int_id < Gicv3::SGI_MAX); |
| Gicv3::GroupId int_group = getIntGroup(int_id); |
| |
| bool forward = false; |
| |
| if (ns) { |
| // Non-Secure EL1 and EL2 access |
| int nsaccess = irqNsacr[int_id]; |
| if (int_group == Gicv3::G0S) { |
| |
| forward = distributor->DS || (nsaccess >= 1); |
| |
| } else if (int_group == Gicv3::G1S) { |
| forward = ((group == Gicv3::G1S || group == Gicv3::G1NS ) && |
| nsaccess == 2); |
| } else { |
| // G1NS |
| forward = group == Gicv3::G1NS; |
| } |
| } else { |
| // Secure EL1 and EL3 access |
| forward = (group == int_group) || |
| (group == Gicv3::G1S && int_group == Gicv3::G0S && |
| distributor->DS); |
| } |
| |
| if (!forward) return; |
| |
| irqPending[int_id] = true; |
| irqPendingIspendr[int_id] = false; |
| DPRINTF(GIC, "Gicv3ReDistributor::sendSGI(): " |
| "int_id %d (SGI) pending bit set\n", int_id); |
| updateDistributor(); |
| } |
| |
| Gicv3::IntStatus |
| Gicv3Redistributor::intStatus(uint32_t int_id) const |
| { |
| assert(int_id < Gicv3::SGI_MAX + Gicv3::PPI_MAX); |
| |
| 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; |
| } |
| } |
| |
| void |
| Gicv3Redistributor::updateDistributor() |
| { |
| distributor->update(); |
| } |
| |
| /* |
| * Recalculate the highest priority pending interrupt after a |
| * change to redistributor state. |
| */ |
| void |
| Gicv3Redistributor::update() |
| { |
| if (gic->blockIntUpdate()) |
| return; |
| |
| for (int int_id = 0; int_id < Gicv3::SGI_MAX + Gicv3::PPI_MAX; int_id++) { |
| Gicv3::GroupId int_group = getIntGroup(int_id); |
| bool group_enabled = distributor->groupEnabled(int_group); |
| |
| if (irqPending[int_id] && irqEnabled[int_id] && |
| !irqActive[int_id] && group_enabled) { |
| if ((irqPriority[int_id] < cpuInterface->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] == cpuInterface->hppi.prio && |
| int_id < cpuInterface->hppi.intid)) { |
| cpuInterface->hppi.intid = int_id; |
| cpuInterface->hppi.prio = irqPriority[int_id]; |
| cpuInterface->hppi.group = int_group; |
| } |
| } |
| } |
| |
| // Check LPIs |
| if (EnableLPIs) { |
| |
| const uint32_t largest_lpi_id = 1 << (lpiIDBits + 1); |
| const uint32_t number_lpis = largest_lpi_id - SMALLEST_LPI_ID + 1; |
| |
| uint8_t lpi_pending_table[largest_lpi_id / 8]; |
| uint8_t lpi_config_table[number_lpis]; |
| |
| memProxy->readBlob(lpiPendingTablePtr, |
| lpi_pending_table, |
| sizeof(lpi_pending_table)); |
| |
| memProxy->readBlob(lpiConfigurationTablePtr, |
| lpi_config_table, |
| sizeof(lpi_config_table)); |
| |
| for (int lpi_id = SMALLEST_LPI_ID; lpi_id < largest_lpi_id; |
| lpi_id++) { |
| uint32_t lpi_pending_entry_byte = lpi_id / 8; |
| uint8_t lpi_pending_entry_bit_position = lpi_id % 8; |
| bool lpi_is_pending = lpi_pending_table[lpi_pending_entry_byte] & |
| 1 << lpi_pending_entry_bit_position; |
| uint32_t lpi_configuration_entry_index = lpi_id - SMALLEST_LPI_ID; |
| |
| LPIConfigurationTableEntry config_entry = |
| lpi_config_table[lpi_configuration_entry_index]; |
| |
| bool lpi_is_enable = config_entry.enable; |
| |
| // LPIs are always Non-secure Group 1 interrupts, |
| // in a system where two Security states are enabled. |
| Gicv3::GroupId lpi_group = Gicv3::G1NS; |
| bool group_enabled = distributor->groupEnabled(lpi_group); |
| |
| if (lpi_is_pending && lpi_is_enable && group_enabled) { |
| uint8_t lpi_priority = config_entry.priority << 2; |
| |
| if ((lpi_priority < cpuInterface->hppi.prio) || |
| (lpi_priority == cpuInterface->hppi.prio && |
| lpi_id < cpuInterface->hppi.intid)) { |
| cpuInterface->hppi.intid = lpi_id; |
| cpuInterface->hppi.prio = lpi_priority; |
| cpuInterface->hppi.group = lpi_group; |
| } |
| } |
| } |
| } |
| |
| if (peInLowPowerState) { |
| if (cpuInterface->havePendingInterrupts()) { |
| cpuInterface->assertWakeRequest(); |
| cpuInterface->clearPendingInterrupts(); |
| } |
| } else { |
| cpuInterface->update(); |
| } |
| } |
| |
| uint8_t |
| Gicv3Redistributor::readEntryLPI(uint32_t lpi_id) |
| { |
| Addr lpi_pending_entry_ptr = lpiPendingTablePtr + (lpi_id / 8); |
| |
| uint8_t lpi_pending_entry; |
| memProxy->readBlob(lpi_pending_entry_ptr, |
| &lpi_pending_entry, |
| sizeof(lpi_pending_entry)); |
| |
| return lpi_pending_entry; |
| } |
| |
| void |
| Gicv3Redistributor::writeEntryLPI(uint32_t lpi_id, uint8_t lpi_pending_entry) |
| { |
| Addr lpi_pending_entry_ptr = lpiPendingTablePtr + (lpi_id / 8); |
| |
| memProxy->writeBlob(lpi_pending_entry_ptr, |
| &lpi_pending_entry, |
| sizeof(lpi_pending_entry)); |
| } |
| |
| bool |
| Gicv3Redistributor::isPendingLPI(uint32_t lpi_id) |
| { |
| // Fetch the LPI pending entry from memory |
| uint8_t lpi_pending_entry = readEntryLPI(lpi_id); |
| |
| uint8_t lpi_pending_entry_bit_position = lpi_id % 8; |
| bool is_set = lpi_pending_entry & (1 << lpi_pending_entry_bit_position); |
| |
| return is_set; |
| } |
| |
| void |
| Gicv3Redistributor::setClrLPI(uint64_t data, bool set) |
| { |
| if (!EnableLPIs) { |
| // Writes to GICR_SETLPIR or GICR_CLRLPIR have not effect if |
| // GICR_CTLR.EnableLPIs == 0. |
| return; |
| } |
| |
| uint32_t lpi_id = data & 0xffffffff; |
| uint32_t largest_lpi_id = 1 << (lpiIDBits + 1); |
| |
| if (lpi_id > largest_lpi_id) { |
| // Writes to GICR_SETLPIR or GICR_CLRLPIR have not effect if |
| // pINTID value specifies an unimplemented LPI. |
| return; |
| } |
| |
| // Fetch the LPI pending entry from memory |
| uint8_t lpi_pending_entry = readEntryLPI(lpi_id); |
| |
| uint8_t lpi_pending_entry_bit_position = lpi_id % 8; |
| bool is_set = lpi_pending_entry & (1 << lpi_pending_entry_bit_position); |
| |
| if (set) { |
| if (is_set) { |
| // Writes to GICR_SETLPIR have not effect if the pINTID field |
| // corresponds to an LPI that is already pending. |
| return; |
| } |
| |
| lpi_pending_entry |= 1 << (lpi_pending_entry_bit_position); |
| } else { |
| if (!is_set) { |
| // Writes to GICR_SETLPIR have not effect if the pINTID field |
| // corresponds to an LPI that is not pending. |
| return; |
| } |
| |
| lpi_pending_entry &= ~(1 << (lpi_pending_entry_bit_position)); |
| |
| // Remove the pending state from the cpu interface |
| cpuInterface->resetHppi(lpi_id); |
| } |
| |
| writeEntryLPI(lpi_id, lpi_pending_entry); |
| |
| updateDistributor(); |
| } |
| |
| Gicv3::GroupId |
| Gicv3Redistributor::getIntGroup(int int_id) const |
| { |
| assert(int_id < (Gicv3::SGI_MAX + Gicv3::PPI_MAX)); |
| |
| if (distributor->DS) { |
| if (irqGroup[int_id] == 0) { |
| return Gicv3::G0S; |
| } else { |
| return Gicv3::G1NS; |
| } |
| } 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; |
| } |
| } |
| |
| GEM5_UNREACHABLE; |
| } |
| |
| void |
| Gicv3Redistributor::activateIRQ(uint32_t int_id) |
| { |
| if (treatAsEdgeTriggered(int_id)) { |
| irqPending[int_id] = false; |
| } |
| irqActive[int_id] = true; |
| } |
| |
| void |
| Gicv3Redistributor::deactivateIRQ(uint32_t int_id) |
| { |
| irqActive[int_id] = false; |
| } |
| |
| uint32_t |
| Gicv3Redistributor::getAffinity() const |
| { |
| ThreadContext *tc = gic->getSystem()->threads[cpuId]; |
| return gem5::ArmISA::getAffinity(gic->getSystem(), tc); |
| } |
| |
| bool |
| Gicv3Redistributor::canBeSelectedFor1toNInterrupt(Gicv3::GroupId group) const |
| { |
| if (peInLowPowerState) { |
| return false; |
| } |
| |
| if (!distributor->groupEnabled(group)) { |
| return false; |
| } |
| |
| if ((group == Gicv3::G1S) && DPG1S) { |
| return false; |
| } |
| |
| if ((group == Gicv3::G1NS) && DPG1NS) { |
| return false; |
| } |
| |
| if ((group == Gicv3::G0S) && DPG0) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void |
| Gicv3Redistributor::copy(Gicv3Registers *from, Gicv3Registers *to) |
| { |
| const auto affinity = getAffinity(); |
| // SGI_Base regs |
| gic->copyRedistRegister(from, to, affinity, GICR_CTLR); |
| gic->copyRedistRegister(from, to, affinity, GICR_WAKER); |
| |
| gic->clearRedistRegister(to, affinity, GICR_ICENABLER0); |
| gic->clearRedistRegister(to, affinity, GICR_ICPENDR0); |
| gic->clearRedistRegister(to, affinity, GICR_ICACTIVER0); |
| |
| gic->copyRedistRegister(from, to, affinity, GICR_ISENABLER0); |
| gic->copyRedistRegister(from, to, affinity, GICR_ISPENDR0); |
| gic->copyRedistRegister(from, to, affinity, GICR_ISACTIVER0); |
| gic->copyRedistRegister(from, to, affinity, GICR_ICFGR0); |
| gic->copyRedistRegister(from, to, affinity, GICR_ICFGR1); |
| gic->copyRedistRegister(from, to, affinity, GICR_IGRPMODR0); |
| gic->copyRedistRegister(from, to, affinity, GICR_NSACR); |
| |
| gic->copyRedistRange(from, to, affinity, |
| GICR_IPRIORITYR.start(), GICR_IPRIORITYR.size()); |
| |
| // RD_Base regs |
| gic->copyRedistRegister(from, to, affinity, GICR_PROPBASER); |
| gic->copyRedistRegister(from, to, affinity, GICR_PENDBASER); |
| } |
| |
| void |
| Gicv3Redistributor::serialize(CheckpointOut & cp) const |
| { |
| SERIALIZE_SCALAR(peInLowPowerState); |
| 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_SCALAR(DPG1S); |
| SERIALIZE_SCALAR(DPG1NS); |
| SERIALIZE_SCALAR(DPG0); |
| SERIALIZE_SCALAR(EnableLPIs); |
| SERIALIZE_SCALAR(lpiConfigurationTablePtr); |
| SERIALIZE_SCALAR(lpiIDBits); |
| SERIALIZE_SCALAR(lpiPendingTablePtr); |
| } |
| |
| void |
| Gicv3Redistributor::unserialize(CheckpointIn & cp) |
| { |
| UNSERIALIZE_SCALAR(peInLowPowerState); |
| 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_SCALAR(DPG1S); |
| UNSERIALIZE_SCALAR(DPG1NS); |
| UNSERIALIZE_SCALAR(DPG0); |
| UNSERIALIZE_SCALAR(EnableLPIs); |
| UNSERIALIZE_SCALAR(lpiConfigurationTablePtr); |
| UNSERIALIZE_SCALAR(lpiIDBits); |
| UNSERIALIZE_SCALAR(lpiPendingTablePtr); |
| } |
| |
| } // namespace gem5 |