/*
+ * 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.
*
* 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_redistributor.hh"
#include "debug/GIC.hh"
#include "dev/arm/gic_v3_cpu_interface.hh"
#include "dev/arm/gic_v3_distributor.hh"
-#include "mem/fs_translating_port_proxy.hh"
+
+using namespace ArmISA;
const AddrRange Gicv3Redistributor::GICR_IPRIORITYR(SGI_base + 0x0400,
- SGI_base + 0x041f);
+ SGI_base + 0x0420);
Gicv3Redistributor::Gicv3Redistributor(Gicv3 * gic, uint32_t cpu_id)
: gic(gic),
distributor(nullptr),
cpuInterface(nullptr),
cpuId(cpu_id),
- irqGroup(Gicv3::SGI_MAX + Gicv3::PPI_MAX),
- irqEnabled(Gicv3::SGI_MAX + Gicv3::PPI_MAX),
- irqPending(Gicv3::SGI_MAX + Gicv3::PPI_MAX),
- irqActive(Gicv3::SGI_MAX + Gicv3::PPI_MAX),
- irqPriority(Gicv3::SGI_MAX + Gicv3::PPI_MAX),
- irqConfig(Gicv3::SGI_MAX + Gicv3::PPI_MAX),
- irqGrpmod(Gicv3::SGI_MAX + Gicv3::PPI_MAX),
- irqNsacr(Gicv3::SGI_MAX + Gicv3::PPI_MAX),
+ 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)
{
}
{
distributor = gic->getDistributor();
cpuInterface = gic->getCPUInterface(cpuId);
-}
-
-void
-Gicv3Redistributor::initState()
-{
- reset();
-}
-
-void
-Gicv3Redistributor::reset()
-{
- peInLowPowerState = true;
- std::fill(irqGroup.begin(), irqGroup.end(), 0);
- std::fill(irqEnabled.begin(), irqEnabled.end(), false);
- std::fill(irqPending.begin(), irqPending.end(), false);
- std::fill(irqActive.begin(), irqActive.end(), false);
- std::fill(irqPriority.begin(), irqPriority.end(), 0);
-
- // SGIs have edge-triggered behavior
- for (uint32_t int_id = 0; int_id < Gicv3::SGI_MAX; int_id++) {
- irqConfig[int_id] = Gicv3::INT_EDGE_TRIGGERED;
- }
- std::fill(irqGrpmod.begin(), irqGrpmod.end(), 0);
- std::fill(irqNsacr.begin(), irqNsacr.end(), 0);
- DPG1S = false;
- DPG1NS = false;
- DPG0 = false;
- EnableLPIs = false;
- lpiConfigurationTablePtr = 0;
- lpiIDBits = 0;
- lpiPendingTablePtr = 0;
+ memProxy = &gic->getSystem()->physProxy;
}
uint64_t
* (physical LPIs supported)
*/
uint64_t affinity = getAffinity();
- int last = cpuId == (gic->getSystem()->numContexts() - 1);
+ 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;
}
- if (not peInLowPowerState and
- (data & GICR_WAKER_ProcessorSleep)) {
+ 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");
- } else if (peInLowPowerState and
- not(data & GICR_WAKER_ProcessorSleep)) {
+ updateDistributor();
+ } else if (pe_was_low_power && !peInLowPowerState) {
DPRINTF(GIC, "Gicv3Redistributor::write(): powering up PE\n");
+ cpuInterface->deassertWakeRequest();
+ updateDistributor();
}
-
- peInLowPowerState = data & GICR_WAKER_ProcessorSleep;
break;
+ }
case GICR_IGROUPR0: // Interrupt Group Register 0
if (!distributor->DS && !is_secure_access) {
"(GICR_ISPENDR0): int_id %d (PPI) "
"pending bit set\n", int_id);
irqPending[int_id] = true;
+ irqPendingIspendr[int_id] = true;
}
}
- updateAndInformCPUInterface();
+ updateDistributor();
break;
case GICR_ICPENDR0:// Interrupt Clear-Pending Register 0
bool clear = data & (1 << int_id) ? 1 : 0;
- if (clear) {
+ if (clear && treatAsEdgeTriggered(int_id)) {
irqPending[int_id] = false;
}
}
break;
+ case GICR_ICFGR0: // SGI Configuration Register
+ // WI
+ return;
case GICR_ICFGR1: { // PPI Configuration Register
int first_intid = Gicv3::SGI_MAX;
lpiIDBits = 0xf;
}
- uint32_t largest_lpi_id = 1 << (lpiIDBits + 1);
- uint32_t number_lpis = largest_lpi_id - SMALLEST_LPI_ID + 1;
- lpiConfigurationTable.resize(number_lpis);
break;
}
break;
case GICR_INVLPIR: { // Redistributor Invalidate LPI Register
- uint32_t lpi_id = data & 0xffffffff;
- uint32_t largest_lpi_id = 1 << (lpiIDBits + 1);
-
- if (lpi_id > largest_lpi_id) {
- return;
- }
-
- uint32_t lpi_table_entry_index = lpi_id - SMALLEST_LPI_ID;
- invalLpiConfig(lpi_table_entry_index);
+ // Do nothing: no caching supported
break;
}
case GICR_INVALLR: { // Redistributor Invalidate All Register
- for (int lpi_table_entry_index = 0;
- lpi_table_entry_index < lpiConfigurationTable.size();
- lpi_table_entry_index++) {
- invalLpiConfig(lpi_table_entry_index);
- }
-
+ // Do nothing: no caching supported
break;
}
}
}
-void
-Gicv3Redistributor::invalLpiConfig(uint32_t lpi_entry_index)
-{
- Addr lpi_table_entry_ptr = lpiConfigurationTablePtr +
- lpi_entry_index * sizeof(LPIConfigurationTableEntry);
- ThreadContext * tc = gic->getSystem()->getThreadContext(cpuId);
- tc->getVirtProxy().readBlob(lpi_table_entry_ptr,
- (uint8_t*) &lpiConfigurationTable[lpi_entry_index],
- sizeof(LPIConfigurationTableEntry));
-}
-
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);
- updateAndInformCPUInterface();
+ 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
assert(int_id < Gicv3::SGI_MAX);
Gicv3::GroupId int_group = getIntGroup(int_id);
- // asked for secure group 1
- // configured as group 0
- // send group 0
- if (int_group == Gicv3::G0S && group == Gicv3::G1S) {
- group = Gicv3::G0S;
- }
-
- if (group == Gicv3::G0S and int_group != Gicv3::G0S) {
- return;
- }
+ bool forward = false;
- if (ns && distributor->DS == 0) {
+ if (ns) {
+ // Non-Secure EL1 and EL2 access
int nsaccess = irqNsacr[int_id];
+ if (int_group == Gicv3::G0S) {
- if ((int_group == Gicv3::G0S && nsaccess < 1) ||
- (int_group == Gicv3::G1S && nsaccess < 2)) {
- return;
+ 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);
- updateAndInformCPUInterface();
+ updateDistributor();
}
Gicv3::IntStatus
}
}
+void
+Gicv3Redistributor::updateDistributor()
+{
+ distributor->update();
+}
+
/*
* Recalculate the highest priority pending interrupt after a
* change to redistributor state.
void
Gicv3Redistributor::update()
{
- bool new_hppi = false;
-
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);
cpuInterface->hppi.intid = int_id;
cpuInterface->hppi.prio = irqPriority[int_id];
cpuInterface->hppi.group = int_group;
- new_hppi = true;
}
}
}
// Check LPIs
- const uint32_t largest_lpi_id = 1 << (lpiIDBits + 1);
- char lpi_pending_table[largest_lpi_id / 8];
- ThreadContext * tc = gic->getSystem()->getThreadContext(cpuId);
- tc->getVirtProxy().readBlob(lpiPendingTablePtr,
- (uint8_t *) lpi_pending_table,
- sizeof(lpi_pending_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;
- bool lpi_is_enable =
- lpiConfigurationTable[lpi_configuration_entry_index].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 =
- lpiConfigurationTable[lpi_configuration_entry_index].priority;
-
- 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;
- new_hppi = true;
+ 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 (!new_hppi && cpuInterface->hppi.prio != 0xff &&
- cpuInterface->hppi.intid < Gicv3::SGI_MAX + Gicv3::PPI_MAX) {
- distributor->fullUpdate();
+ 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)
{
return;
}
- Addr lpi_pending_entry_ptr = lpiPendingTablePtr + (lpi_id / 8);
- uint8_t lpi_pending_entry;
- ThreadContext * tc = gic->getSystem()->getThreadContext(cpuId);
- tc->getVirtProxy().readBlob(lpi_pending_entry_ptr,
- (uint8_t*) &lpi_pending_entry,
- sizeof(lpi_pending_entry));
+ // 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);
}
lpi_pending_entry &= ~(1 << (lpi_pending_entry_bit_position));
+
+ // Remove the pending state from the cpu interface
+ cpuInterface->resetHppi(lpi_id);
}
- tc->getVirtProxy().writeBlob(lpi_pending_entry_ptr,
- (uint8_t*) &lpi_pending_entry,
- sizeof(lpi_pending_entry));
- updateAndInformCPUInterface();
-}
+ writeEntryLPI(lpi_id, lpi_pending_entry);
-void
-Gicv3Redistributor::updateAndInformCPUInterface()
-{
- update();
- cpuInterface->update();
+ updateDistributor();
}
Gicv3::GroupId
void
Gicv3Redistributor::activateIRQ(uint32_t int_id)
{
- irqPending[int_id] = false;
+ if (treatAsEdgeTriggered(int_id)) {
+ irqPending[int_id] = false;
+ }
irqActive[int_id] = true;
}
uint32_t
Gicv3Redistributor::getAffinity() const
{
- ThreadContext * tc = gic->getSystem()->getThreadContext(cpuId);
+ ThreadContext *tc = gic->getSystem()->threads[cpuId];
uint64_t mpidr = getMPIDR(gic->getSystem(), tc);
/*
* Aff3 = MPIDR[39:32]
SERIALIZE_CONTAINER(irqGroup);
SERIALIZE_CONTAINER(irqEnabled);
SERIALIZE_CONTAINER(irqPending);
+ SERIALIZE_CONTAINER(irqPendingIspendr);
SERIALIZE_CONTAINER(irqActive);
SERIALIZE_CONTAINER(irqPriority);
SERIALIZE_CONTAINER(irqConfig);
UNSERIALIZE_CONTAINER(irqGroup);
UNSERIALIZE_CONTAINER(irqEnabled);
UNSERIALIZE_CONTAINER(irqPending);
+ UNSERIALIZE_CONTAINER(irqPendingIspendr);
UNSERIALIZE_CONTAINER(irqActive);
UNSERIALIZE_CONTAINER(irqPriority);
UNSERIALIZE_CONTAINER(irqConfig);