ARM: Fix some subtle bugs in the GIC
The GIC code can write to the registers with 8, 16, or 32 byte accesses which could set/clear different numbers of interrupts.
This commit is contained in:
parent
521d68c82a
commit
9792bbc324
3 changed files with 136 additions and 31 deletions
|
@ -51,3 +51,4 @@ if env['FULL_SYSTEM'] and env['TARGET_ISA'] == 'arm':
|
||||||
Source('realview.cc')
|
Source('realview.cc')
|
||||||
|
|
||||||
TraceFlag('AMBA')
|
TraceFlag('AMBA')
|
||||||
|
TraceFlag('GIC')
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*
|
*
|
||||||
* Authors: Ali Saidi
|
* Authors: Ali Saidi
|
||||||
|
* Prakash Ramrakhyani
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "base/trace.hh"
|
#include "base/trace.hh"
|
||||||
|
@ -59,6 +60,8 @@ Gic::Gic(const Params *p)
|
||||||
cpuEnabled[x] = false;
|
cpuEnabled[x] = false;
|
||||||
cpuPriority[x] = 0;
|
cpuPriority[x] = 0;
|
||||||
cpuBpr[x] = 0;
|
cpuBpr[x] = 0;
|
||||||
|
// Initialize cpu highest int
|
||||||
|
cpuHighestInt[x] = SPURIOUS_INT;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int x = 0; x < 32; x++) {
|
for (int x = 0; x < 32; x++) {
|
||||||
|
@ -146,13 +149,26 @@ Gic::readDistributor(PacketPtr pkt)
|
||||||
|
|
||||||
if (daddr >= ICDIPR_ST && daddr < ICDIPR_ED + 4) {
|
if (daddr >= ICDIPR_ST && daddr < ICDIPR_ED + 4) {
|
||||||
Addr int_num;
|
Addr int_num;
|
||||||
int_num = (daddr-ICDIPR_ST) << 2;
|
int_num = daddr - ICDIPR_ST;
|
||||||
assert(int_num < 1020);
|
assert(int_num < 1020);
|
||||||
|
DPRINTF(Interrupt, "Reading interrupt priority at int# %#x \n",int_num);
|
||||||
|
switch(pkt->getSize()){
|
||||||
|
case 1:
|
||||||
|
pkt->set<uint8_t>(intPriority[int_num]);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
pkt->set<uint16_t>(intPriority[int_num] |
|
||||||
|
intPriority[int_num+1] << 8);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
pkt->set<uint32_t>(intPriority[int_num] |
|
pkt->set<uint32_t>(intPriority[int_num] |
|
||||||
intPriority[int_num+1] << 8 |
|
intPriority[int_num+1] << 8 |
|
||||||
intPriority[int_num+2] << 16 |
|
intPriority[int_num+2] << 16 |
|
||||||
intPriority[int_num+3] << 24) ;
|
intPriority[int_num+3] << 24);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
panic("Invalid access size while reading, priority registers in Gic: %d", pkt->getSize());
|
||||||
|
}
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,18 +239,23 @@ Gic::readCpu(PacketPtr pkt)
|
||||||
break;
|
break;
|
||||||
case ICCIAR:
|
case ICCIAR:
|
||||||
DPRINTF(Interrupt, "CPU reading IAR = %d\n", cpuHighestInt[0]);
|
DPRINTF(Interrupt, "CPU reading IAR = %d\n", cpuHighestInt[0]);
|
||||||
|
if(enabled && cpuEnabled[0]){
|
||||||
pkt->set<uint32_t>(cpuHighestInt[0]);
|
pkt->set<uint32_t>(cpuHighestInt[0]);
|
||||||
activeInt[intNumToWord(cpuHighestInt[0])] |=
|
activeInt[intNumToWord(cpuHighestInt[0])] |=
|
||||||
1 << intNumToBit(cpuHighestInt[0]);
|
1 << intNumToBit(cpuHighestInt[0]);
|
||||||
|
updateRunPri();
|
||||||
pendingInt[intNumToWord(cpuHighestInt[0])] &=
|
pendingInt[intNumToWord(cpuHighestInt[0])] &=
|
||||||
~(1 << intNumToBit(cpuHighestInt[0]));
|
~(1 << intNumToBit(cpuHighestInt[0]));
|
||||||
cpuHighestInt[0] = SPURIOUS_INT;
|
cpuHighestInt[0] = SPURIOUS_INT;
|
||||||
updateIntState(-1);
|
updateIntState(-1);
|
||||||
platform->intrctrl->clear(0, ArmISA::INT_IRQ, 0);
|
platform->intrctrl->clear(0, ArmISA::INT_IRQ, 0);
|
||||||
|
} else {
|
||||||
|
pkt->set<uint32_t>(SPURIOUS_INT);
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case ICCRPR:
|
case ICCRPR:
|
||||||
pkt->set<uint32_t>(0);
|
pkt->set<uint32_t>(iccrpr[0]);
|
||||||
panic("Need to implement RPR");
|
|
||||||
break;
|
break;
|
||||||
case ICCHPIR:
|
case ICCHPIR:
|
||||||
pkt->set<uint32_t>(0);
|
pkt->set<uint32_t>(0);
|
||||||
|
@ -255,8 +276,8 @@ Gic::writeDistributor(PacketPtr pkt)
|
||||||
Addr daddr = pkt->getAddr() - distAddr;
|
Addr daddr = pkt->getAddr() - distAddr;
|
||||||
pkt->allocate();
|
pkt->allocate();
|
||||||
|
|
||||||
DPRINTF(Interrupt, "gic distributor write register %#x val: %#x\n",
|
DPRINTF(Interrupt, "gic distributor write register %#x size %#x\n",
|
||||||
daddr, pkt->get<uint32_t>());
|
daddr, pkt->getSize());
|
||||||
|
|
||||||
if (daddr >= ICDISER_ST && daddr < ICDISER_ED + 4) {
|
if (daddr >= ICDISER_ST && daddr < ICDISER_ED + 4) {
|
||||||
assert((daddr-ICDISER_ST) >> 2 < 32);
|
assert((daddr-ICDISER_ST) >> 2 < 32);
|
||||||
|
@ -285,14 +306,32 @@ Gic::writeDistributor(PacketPtr pkt)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (daddr >= ICDIPR_ST && daddr < ICDIPR_ED + 4) {
|
if (daddr >= ICDIPR_ST && daddr < ICDIPR_ED + 4) {
|
||||||
Addr int_num = (daddr-ICDIPR_ST) << 2;
|
Addr int_num = daddr - ICDIPR_ST;
|
||||||
assert(int_num < 1020);
|
assert(int_num < 1020);
|
||||||
uint32_t tmp = pkt->get<uint32_t>();
|
uint32_t tmp;
|
||||||
|
switch(pkt->getSize()){
|
||||||
|
case 1:
|
||||||
|
tmp = pkt->get<uint8_t>();
|
||||||
intPriority[int_num] = tmp & 0xff;
|
intPriority[int_num] = tmp & 0xff;
|
||||||
intPriority[int_num+1] = (tmp >> 8) & 0xff;
|
break;
|
||||||
intPriority[int_num+2] = (tmp >> 16) & 0xff;
|
case 2:
|
||||||
intPriority[int_num+3] = (tmp >> 24) & 0xff;
|
tmp = pkt->get<uint16_t>();
|
||||||
updateIntState((daddr-ICDIPR_ST)>>2);
|
intPriority[int_num] = tmp & 0xff;
|
||||||
|
intPriority[int_num + 1] = (tmp >> 8) & 0xff;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
tmp = pkt->get<uint32_t>();
|
||||||
|
intPriority[int_num] = tmp & 0xff;
|
||||||
|
intPriority[int_num + 1] = (tmp >> 8) & 0xff;
|
||||||
|
intPriority[int_num + 2] = (tmp >> 16) & 0xff;
|
||||||
|
intPriority[int_num + 3] = (tmp >> 24) & 0xff;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
panic("Invalid access size while writing to, priority registers in Gic: %d", pkt->getSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
updateIntState(-1);
|
||||||
|
updateRunPri();
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,6 +360,7 @@ Gic::writeDistributor(PacketPtr pkt)
|
||||||
switch(daddr) {
|
switch(daddr) {
|
||||||
case ICDDCR:
|
case ICDDCR:
|
||||||
enabled = pkt->get<uint32_t>();
|
enabled = pkt->get<uint32_t>();
|
||||||
|
DPRINTF(Interrupt, "Distributor enable flag set to = %d\n", enabled);
|
||||||
break;
|
break;
|
||||||
case ICDSGIR:
|
case ICDSGIR:
|
||||||
softInt(pkt->get<uint32_t>());
|
softInt(pkt->get<uint32_t>());
|
||||||
|
@ -363,6 +403,7 @@ Gic::writeCpu(PacketPtr pkt)
|
||||||
if (!(activeInt[intNumToWord(tmp)] & (1 << intNumToBit(tmp))))
|
if (!(activeInt[intNumToWord(tmp)] & (1 << intNumToBit(tmp))))
|
||||||
panic("Done handling interrupt that isn't active?\n");
|
panic("Done handling interrupt that isn't active?\n");
|
||||||
activeInt[intNumToWord(tmp)] &= ~(1 << intNumToBit(tmp));
|
activeInt[intNumToWord(tmp)] &= ~(1 << intNumToBit(tmp));
|
||||||
|
updateRunPri();
|
||||||
DPRINTF(Interrupt, "CPU done handling interrupt IAR = %d\n", tmp);
|
DPRINTF(Interrupt, "CPU done handling interrupt IAR = %d\n", tmp);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -383,10 +424,11 @@ void
|
||||||
Gic::updateIntState(int hint)
|
Gic::updateIntState(int hint)
|
||||||
{
|
{
|
||||||
/*@todo use hint to do less work. */
|
/*@todo use hint to do less work. */
|
||||||
int highest_int = -1;
|
int highest_int = SPURIOUS_INT;
|
||||||
uint8_t highest_pri = 0xff;
|
// Priorities below that set in ICCPMR can be ignored
|
||||||
|
uint8_t highest_pri = cpuPriority[0];
|
||||||
|
|
||||||
for (int x = 0; x < itLinesLog2; x++) {
|
for (int x = 0; x < (itLines/32) ; x++) {
|
||||||
if (intEnabled[x] & pendingInt[x]) {
|
if (intEnabled[x] & pendingInt[x]) {
|
||||||
for (int y = 0; y < 32; y++) {
|
for (int y = 0; y < 32; y++) {
|
||||||
if (bits(intEnabled[x], y) & bits(pendingInt[x], y))
|
if (bits(intEnabled[x], y) & bits(pendingInt[x], y))
|
||||||
|
@ -398,7 +440,7 @@ Gic::updateIntState(int hint)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (highest_int == -1)
|
if (highest_int == SPURIOUS_INT)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
cpuHighestInt[0] = highest_int;
|
cpuHighestInt[0] = highest_int;
|
||||||
|
@ -406,7 +448,7 @@ Gic::updateIntState(int hint)
|
||||||
|
|
||||||
/* @todo make this work for more than one cpu, need to handle 1:N, N:N
|
/* @todo make this work for more than one cpu, need to handle 1:N, N:N
|
||||||
* models */
|
* models */
|
||||||
if (cpuEnabled[0] && highest_pri < cpuPriority[0]) {
|
if (enabled && cpuEnabled[0] && (highest_pri < cpuPriority[0])) {
|
||||||
/* @todo delay interrupt by some time to deal with calculation delay */
|
/* @todo delay interrupt by some time to deal with calculation delay */
|
||||||
/* @todo only interrupt if we've haven't already interrupted for this
|
/* @todo only interrupt if we've haven't already interrupted for this
|
||||||
* int !!!!!!!!!! */
|
* int !!!!!!!!!! */
|
||||||
|
@ -415,7 +457,17 @@ Gic::updateIntState(int hint)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Gic::updateRunPri()
|
||||||
|
{
|
||||||
|
uint8_t maxPriority = 0xff;
|
||||||
|
for (int i = 0 ; i < itLines ; i++){
|
||||||
|
if ( activeInt[intNumToWord(i)] & (1 << intNumToBit(i))){
|
||||||
|
if (intPriority[i] < maxPriority) maxPriority = intPriority[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iccrpr[0] = maxPriority;
|
||||||
|
}
|
||||||
void
|
void
|
||||||
Gic::sendInt(uint32_t num)
|
Gic::sendInt(uint32_t num)
|
||||||
{
|
{
|
||||||
|
@ -457,3 +509,40 @@ GicParams::create()
|
||||||
{
|
{
|
||||||
return new Gic(this);
|
return new Gic(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Functions for debugging and testing */
|
||||||
|
void
|
||||||
|
Gic::driveSPI(unsigned int spiVect)
|
||||||
|
{
|
||||||
|
DPRINTF(GIC, "Received SPI Vector:%x Enable: %d\n", spiVect, irqEnable);
|
||||||
|
if( irqEnable && enabled ){
|
||||||
|
pendingInt[1] |= spiVect;
|
||||||
|
updateIntState(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Gic::driveIrqEn( bool state)
|
||||||
|
{
|
||||||
|
irqEnable = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Gic::driveLegIRQ(bool state)
|
||||||
|
{
|
||||||
|
if (irqEnable && !(!enabled && cpuEnabled[0])){
|
||||||
|
if(state){
|
||||||
|
DPRINTF(GIC, "Driving Legacy Irq\n");
|
||||||
|
platform->intrctrl->post(0, ArmISA::INT_IRQ, 0);
|
||||||
|
}
|
||||||
|
else platform->intrctrl->clear(0, ArmISA::INT_IRQ, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Gic::driveLegFIQ(bool state)
|
||||||
|
{
|
||||||
|
if (state)
|
||||||
|
platform->intrctrl->post(0, ArmISA::INT_FIQ, 0);
|
||||||
|
else platform->intrctrl->clear(0, ArmISA::INT_FIQ, 0);
|
||||||
|
}
|
||||||
|
|
|
@ -138,6 +138,9 @@ class Gic : public PioDevice
|
||||||
* one bit per interrupt, 32 bit per word = 32 words */
|
* one bit per interrupt, 32 bit per word = 32 words */
|
||||||
uint32_t activeInt[32];
|
uint32_t activeInt[32];
|
||||||
|
|
||||||
|
/** read only running priroity register, 1 per cpu*/
|
||||||
|
uint32_t iccrpr[8];
|
||||||
|
|
||||||
/** an 8 bit priority (lower is higher priority) for each
|
/** an 8 bit priority (lower is higher priority) for each
|
||||||
* of the 1020 possible supported interrupts.
|
* of the 1020 possible supported interrupts.
|
||||||
*/
|
*/
|
||||||
|
@ -164,6 +167,9 @@ class Gic : public PioDevice
|
||||||
/** highest interrupt that is interrupting CPU */
|
/** highest interrupt that is interrupting CPU */
|
||||||
uint32_t cpuHighestInt[8];
|
uint32_t cpuHighestInt[8];
|
||||||
|
|
||||||
|
/** IRQ Enable Used for debug */
|
||||||
|
bool irqEnable;
|
||||||
|
|
||||||
/** software generated interrupt
|
/** software generated interrupt
|
||||||
* @param data data to decode that indicates which cpus to interrupt
|
* @param data data to decode that indicates which cpus to interrupt
|
||||||
*/
|
*/
|
||||||
|
@ -174,6 +180,10 @@ class Gic : public PioDevice
|
||||||
*/
|
*/
|
||||||
void updateIntState(int hint);
|
void updateIntState(int hint);
|
||||||
|
|
||||||
|
/** Update the register that records priority of the highest priority
|
||||||
|
* active interrupt*/
|
||||||
|
void updateRunPri();
|
||||||
|
|
||||||
int intNumToWord(int num) const { return num >> 5; }
|
int intNumToWord(int num) const { return num >> 5; }
|
||||||
int intNumToBit(int num) const { return num % 32; }
|
int intNumToBit(int num) const { return num % 32; }
|
||||||
|
|
||||||
|
@ -232,6 +242,11 @@ class Gic : public PioDevice
|
||||||
* @param number number of interrupt to send */
|
* @param number number of interrupt to send */
|
||||||
void clearInt(uint32_t number);
|
void clearInt(uint32_t number);
|
||||||
|
|
||||||
|
/* Various functions fer testing and debugging */
|
||||||
|
void driveSPI(uint32_t spi);
|
||||||
|
void driveLegIRQ(bool state);
|
||||||
|
void driveLegFIQ(bool state);
|
||||||
|
void driveIrqEn(bool state);
|
||||||
|
|
||||||
virtual void serialize(std::ostream &os);
|
virtual void serialize(std::ostream &os);
|
||||||
virtual void unserialize(Checkpoint *cp, const std::string §ion);
|
virtual void unserialize(Checkpoint *cp, const std::string §ion);
|
||||||
|
|
Loading…
Reference in a new issue