diff options
author | Geoffrey Blake <Geoffrey.Blake@arm.com> | 2014-05-09 18:58:47 -0400 |
---|---|---|
committer | Geoffrey Blake <Geoffrey.Blake@arm.com> | 2014-05-09 18:58:47 -0400 |
commit | 85940fd53795bd4b7b2118f4fa2a59a03bf6a8b1 (patch) | |
tree | 985e5611aa976bfe0c7e10e9458e7e5db8ed1eba | |
parent | 1028c03320c6c7822b8f5a32da2297c1be2d1487 (diff) | |
download | gem5-85940fd53795bd4b7b2118f4fa2a59a03bf6a8b1.tar.xz |
arch, arm: Preserve TLB bootUncacheability when switching CPUs
The ARM TLBs have a bootUncacheability flag used to make some loads
and stores become uncacheable when booting in FS mode. Later the
flag is cleared to let those loads and stores operate as normal. When
doing a takeOverFrom(), this flag's state is not preserved and is
momentarily reset until the CPSR is touched. On single core runs this
is a non-issue. On multi-core runs this can lead to crashes on the O3
CPU model from the following series of events:
1) takeOverFrom executed to switch from Atomic -> O3
2) All bootUncacheability flags are reset to true
3) Core2 tries to execute a load covered by bootUncacheability, it
is flagged as uncacheable
4) Core2's load needs to replay due to a pipeline flush
3) Core1 core does an action on CPSR
4) The handling code for CPSR then checks all other cores
to determine if bootUncacheability can be set to false
5) Asynchronously set bootUncacheability on all cores to false
6) Core2 replays load previously set as uncacheable and notices
it is now flagged as cacheable, leads to a panic.
This patch implements takeOverFrom() functionality for the ARM TLBs
to preserve flag values when switching from atomic -> detailed.
-rw-r--r-- | src/arch/alpha/tlb.hh | 2 | ||||
-rw-r--r-- | src/arch/arm/tlb.cc | 24 | ||||
-rw-r--r-- | src/arch/arm/tlb.hh | 2 | ||||
-rw-r--r-- | src/arch/mips/tlb.hh | 3 | ||||
-rw-r--r-- | src/arch/power/tlb.hh | 2 | ||||
-rw-r--r-- | src/arch/sparc/tlb.hh | 2 | ||||
-rw-r--r-- | src/arch/x86/tlb.hh | 2 | ||||
-rw-r--r-- | src/cpu/base.cc | 5 | ||||
-rw-r--r-- | src/sim/tlb.hh | 5 |
9 files changed, 47 insertions, 0 deletions
diff --git a/src/arch/alpha/tlb.hh b/src/arch/alpha/tlb.hh index 3300e5761..91394e972 100644 --- a/src/arch/alpha/tlb.hh +++ b/src/arch/alpha/tlb.hh @@ -87,6 +87,8 @@ class TLB : public BaseTLB TLB(const Params *p); virtual ~TLB(); + void takeOverFrom(BaseTLB *otlb) {} + virtual void regStats(); int getsize() const { return size; } diff --git a/src/arch/arm/tlb.cc b/src/arch/arm/tlb.cc index 037f7490e..37cf9b149 100644 --- a/src/arch/arm/tlb.cc +++ b/src/arch/arm/tlb.cc @@ -354,6 +354,30 @@ TLB::drainResume() } void +TLB::takeOverFrom(BaseTLB *_otlb) +{ + TLB *otlb = dynamic_cast<TLB*>(_otlb); + /* Make sure we actually have a valid type */ + if (otlb) { + _attr = otlb->_attr; + haveLPAE = otlb->haveLPAE; + directToStage2 = otlb->directToStage2; + stage2Req = otlb->stage2Req; + bootUncacheability = otlb->bootUncacheability; + + /* Sync the stage2 MMU if they exist in both + * the old CPU and the new + */ + if (!isStage2 && + stage2Tlb && otlb->stage2Tlb) { + stage2Tlb->takeOverFrom(otlb->stage2Tlb); + } + } else { + panic("Incompatible TLB type!"); + } +} + +void TLB::serialize(ostream &os) { DPRINTF(Checkpoint, "Serializing Arm TLB\n"); diff --git a/src/arch/arm/tlb.hh b/src/arch/arm/tlb.hh index ac8c672bf..b9025fa5f 100644 --- a/src/arch/arm/tlb.hh +++ b/src/arch/arm/tlb.hh @@ -155,6 +155,8 @@ class TLB : public BaseTLB virtual ~TLB(); + void takeOverFrom(BaseTLB *otlb); + /// setup all the back pointers virtual void init(); diff --git a/src/arch/mips/tlb.hh b/src/arch/mips/tlb.hh index fdd590e85..706a96ff0 100644 --- a/src/arch/mips/tlb.hh +++ b/src/arch/mips/tlb.hh @@ -87,6 +87,9 @@ class TLB : public BaseTLB int probeEntry(Addr vpn,uint8_t) const; MipsISA::PTE *getEntry(unsigned) const; virtual ~TLB(); + + void takeOverFrom(BaseTLB *otlb) {} + int smallPages; int getsize() const { return size; } diff --git a/src/arch/power/tlb.hh b/src/arch/power/tlb.hh index 753231a89..b18956b07 100644 --- a/src/arch/power/tlb.hh +++ b/src/arch/power/tlb.hh @@ -130,6 +130,8 @@ class TLB : public BaseTLB TLB(const Params *p); virtual ~TLB(); + void takeOverFrom(BaseTLB *otlb) {} + int probeEntry(Addr vpn,uint8_t) const; PowerISA::PTE *getEntry(unsigned) const; diff --git a/src/arch/sparc/tlb.hh b/src/arch/sparc/tlb.hh index e084f665c..e29c5171a 100644 --- a/src/arch/sparc/tlb.hh +++ b/src/arch/sparc/tlb.hh @@ -154,6 +154,8 @@ class TLB : public BaseTLB typedef SparcTLBParams Params; TLB(const Params *p); + void takeOverFrom(BaseTLB *otlb) {} + void demapPage(Addr vaddr, uint64_t asn) { diff --git a/src/arch/x86/tlb.hh b/src/arch/x86/tlb.hh index ea2d50ec2..6b65b6f37 100644 --- a/src/arch/x86/tlb.hh +++ b/src/arch/x86/tlb.hh @@ -75,6 +75,8 @@ namespace X86ISA typedef X86TLBParams Params; TLB(const Params *p); + void takeOverFrom(BaseTLB *otlb) {} + TlbEntry *lookup(Addr va, bool update_lru = true); void setConfigAddress(uint32_t addr); diff --git a/src/cpu/base.cc b/src/cpu/base.cc index 905785631..c8c8ac571 100644 --- a/src/cpu/base.cc +++ b/src/cpu/base.cc @@ -432,6 +432,8 @@ BaseCPU::takeOverFrom(BaseCPU *oldCPU) old_dtb_port->unbind(); new_dtb_port->bind(slavePort); } + newTC->getITBPtr()->takeOverFrom(oldTC->getITBPtr()); + newTC->getDTBPtr()->takeOverFrom(oldTC->getDTBPtr()); // Checker whether or not we have to transfer CheckerCPU // objects over in the switch @@ -447,6 +449,9 @@ BaseCPU::takeOverFrom(BaseCPU *oldCPU) BaseMasterPort *new_checker_dtb_port = newChecker->getDTBPtr()->getMasterPort(); + newChecker->getITBPtr()->takeOverFrom(oldChecker->getITBPtr()); + newChecker->getDTBPtr()->takeOverFrom(oldChecker->getDTBPtr()); + // Move over any table walker ports if they exist for checker if (new_checker_itb_port) { assert(!new_checker_itb_port->isConnected()); diff --git a/src/sim/tlb.hh b/src/sim/tlb.hh index f46c2d856..397d6e0f2 100644 --- a/src/sim/tlb.hh +++ b/src/sim/tlb.hh @@ -70,6 +70,11 @@ class BaseTLB : public SimObject virtual void flushAll() = 0; /** + * Take over from an old tlb context + */ + virtual void takeOverFrom(BaseTLB *otlb) = 0; + + /** * Get the table walker master port if present. This is used for * migrating port connections during a CPU takeOverFrom() * call. For architectures that do not have a table walker, NULL |