arch-arm: Implement Armv8.2-LPA
This is enabled by setting the ArmSystem.phys_addr_range64 to 52. This will automatically set the ID_AA64MMFR0_EL1.PARange to 0b0110 which encodes the presence of Armv8.2-LPA Change-Id: If9b36e26cd2a72e55c8e929a632b7b50d909b282 Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/35956 Reviewed-by: Giacomo Travaglini <giacomo.travaglini@arm.com> Maintainer: Giacomo Travaglini <giacomo.travaglini@arm.com> Tested-by: kokoro <noreply+kokoro@google.com>
This commit is contained in:
@@ -51,7 +51,7 @@ namespace ArmISA
|
||||
{
|
||||
|
||||
// Max. physical address range in bits supported by the architecture
|
||||
const unsigned MaxPhysAddrRange = 48;
|
||||
const unsigned MaxPhysAddrRange = 52;
|
||||
|
||||
// ITB/DTB page table entry
|
||||
struct PTE
|
||||
|
||||
@@ -95,7 +95,7 @@ ArmSystem::ArmSystem(Params *p)
|
||||
|
||||
if (_highestELIs64 && (
|
||||
_physAddrRange64 < 32 ||
|
||||
_physAddrRange64 > 48 ||
|
||||
_physAddrRange64 > MaxPhysAddrRange ||
|
||||
(_physAddrRange64 % 4 != 0 && _physAddrRange64 != 42))) {
|
||||
fatal("Invalid physical address range (%d)\n", _physAddrRange64);
|
||||
}
|
||||
|
||||
@@ -81,12 +81,12 @@ TableWalker::TableWalker(const Params *p)
|
||||
haveSecurity = armSys->haveSecurity();
|
||||
_haveLPAE = armSys->haveLPAE();
|
||||
_haveVirtualization = armSys->haveVirtualization();
|
||||
physAddrRange = armSys->physAddrRange();
|
||||
_physAddrRange = armSys->physAddrRange();
|
||||
_haveLargeAsid64 = armSys->haveLargeAsid64();
|
||||
} else {
|
||||
haveSecurity = _haveLPAE = _haveVirtualization = false;
|
||||
_haveLargeAsid64 = false;
|
||||
physAddrRange = 32;
|
||||
_physAddrRange = 48;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -252,7 +252,7 @@ TableWalker::walk(const RequestPtr &_req, ThreadContext *_tc, uint16_t _asid,
|
||||
currState->mode = _mode;
|
||||
currState->tranType = tranType;
|
||||
currState->isSecure = secure;
|
||||
currState->physAddrRange = physAddrRange;
|
||||
currState->physAddrRange = _physAddrRange;
|
||||
|
||||
/** @todo These should be cached or grabbed from cached copies in
|
||||
the TLB, all these miscreg reads are expensive */
|
||||
@@ -764,10 +764,10 @@ TableWalker::checkVAddrSizeFaultAArch64(Addr addr, int top_bit,
|
||||
}
|
||||
|
||||
bool
|
||||
TableWalker::checkAddrSizeFaultAArch64(Addr addr, int currPhysAddrRange)
|
||||
TableWalker::checkAddrSizeFaultAArch64(Addr addr, int pa_range)
|
||||
{
|
||||
return (currPhysAddrRange != MaxPhysAddrRange &&
|
||||
bits(addr, MaxPhysAddrRange - 1, currPhysAddrRange));
|
||||
return (pa_range != _physAddrRange &&
|
||||
bits(addr, _physAddrRange - 1, pa_range));
|
||||
}
|
||||
|
||||
Fault
|
||||
@@ -1041,20 +1041,29 @@ TableWalker::processWalkAArch64()
|
||||
"Table walker couldn't find lookup level\n");
|
||||
}
|
||||
|
||||
int stride = tg - 3;
|
||||
// Clamp to lower limit
|
||||
int pa_range = decodePhysAddrRange64(ps);
|
||||
if (pa_range > _physAddrRange) {
|
||||
currState->physAddrRange = _physAddrRange;
|
||||
} else {
|
||||
currState->physAddrRange = pa_range;
|
||||
}
|
||||
|
||||
// Determine table base address
|
||||
int stride = tg - 3;
|
||||
int base_addr_lo = 3 + tsz - stride * (3 - start_lookup_level) - tg;
|
||||
Addr base_addr = mbits(ttbr, 47, base_addr_lo);
|
||||
Addr base_addr = 0;
|
||||
|
||||
if (pa_range == 52) {
|
||||
int z = (base_addr_lo < 6) ? 6 : base_addr_lo;
|
||||
base_addr = mbits(ttbr, 47, z);
|
||||
base_addr |= (bits(ttbr, 5, 2) << 48);
|
||||
} else {
|
||||
base_addr = mbits(ttbr, 47, base_addr_lo);
|
||||
}
|
||||
|
||||
// Determine physical address size and raise an Address Size Fault if
|
||||
// necessary
|
||||
int pa_range = decodePhysAddrRange64(ps);
|
||||
// Clamp to lower limit
|
||||
if (pa_range > physAddrRange)
|
||||
currState->physAddrRange = physAddrRange;
|
||||
else
|
||||
currState->physAddrRange = pa_range;
|
||||
if (checkAddrSizeFaultAArch64(base_addr, currState->physAddrRange)) {
|
||||
DPRINTF(TLB, "Address size fault before any lookup\n");
|
||||
Fault f;
|
||||
@@ -1084,7 +1093,7 @@ TableWalker::processWalkAArch64()
|
||||
}
|
||||
return f;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Determine descriptor address
|
||||
Addr desc_addr = base_addr |
|
||||
@@ -1119,6 +1128,7 @@ TableWalker::processWalkAArch64()
|
||||
currState->longDesc.lookupLevel = start_lookup_level;
|
||||
currState->longDesc.aarch64 = true;
|
||||
currState->longDesc.grainSize = tg;
|
||||
currState->longDesc.physAddrRange = _physAddrRange;
|
||||
|
||||
if (currState->timing) {
|
||||
fetchDescriptor(desc_addr, (uint8_t*) &currState->longDesc.data,
|
||||
@@ -1745,10 +1755,8 @@ TableWalker::doLongDescriptor()
|
||||
{
|
||||
auto fault_source = ArmFault::FaultSourceInvalid;
|
||||
// Check for address size fault
|
||||
if (checkAddrSizeFaultAArch64(
|
||||
mbits(currState->longDesc.data, MaxPhysAddrRange - 1,
|
||||
currState->longDesc.offsetBits()),
|
||||
currState->physAddrRange)) {
|
||||
if (checkAddrSizeFaultAArch64(currState->longDesc.paddr(),
|
||||
currState->physAddrRange)) {
|
||||
|
||||
DPRINTF(TLB, "L%d descriptor causing Address Size Fault\n",
|
||||
currState->longDesc.lookupLevel);
|
||||
@@ -2305,6 +2313,7 @@ TableWalker::pageSizeNtoStatBin(uint8_t N)
|
||||
case 25: return 6; // 32M (using 16K granule in v8-64)
|
||||
case 29: return 7; // 512M (using 64K granule in v8-64)
|
||||
case 30: return 8; // 1G-LPAE
|
||||
case 42: return 9; // 1G-LPAE
|
||||
default:
|
||||
panic("unknown page size");
|
||||
return 255;
|
||||
@@ -2374,7 +2383,7 @@ TableWalker::TableWalkerStats::TableWalkerStats(Stats::Group *parent)
|
||||
.flags(Stats::pdf | Stats::dist | Stats::nozero | Stats::nonan);
|
||||
|
||||
pageSizes // see DDI 0487A D4-1661
|
||||
.init(9)
|
||||
.init(10)
|
||||
.flags(Stats::total | Stats::pdf | Stats::dist | Stats::nozero);
|
||||
pageSizes.subname(0, "4K");
|
||||
pageSizes.subname(1, "16K");
|
||||
@@ -2385,6 +2394,7 @@ TableWalker::TableWalkerStats::TableWalkerStats(Stats::Group *parent)
|
||||
pageSizes.subname(6, "32M");
|
||||
pageSizes.subname(7, "512M");
|
||||
pageSizes.subname(8, "1G");
|
||||
pageSizes.subname(9, "4TB");
|
||||
|
||||
requestOrigin
|
||||
.init(2,2) // Instruction/Data, requests/completed
|
||||
|
||||
@@ -382,7 +382,10 @@ class TableWalker : public ClockedObject
|
||||
Page
|
||||
};
|
||||
|
||||
LongDescriptor() : data(0), _dirty(false) {}
|
||||
LongDescriptor()
|
||||
: data(0), _dirty(false), aarch64(false), grainSize(Grain4KB),
|
||||
physAddrRange(0)
|
||||
{}
|
||||
|
||||
/** The raw bits of the entry */
|
||||
uint64_t data;
|
||||
@@ -391,6 +394,15 @@ class TableWalker : public ClockedObject
|
||||
* written back to memory */
|
||||
bool _dirty;
|
||||
|
||||
/** True if the current lookup is performed in AArch64 state */
|
||||
bool aarch64;
|
||||
|
||||
/** Width of the granule size in bits */
|
||||
GrainSize grainSize;
|
||||
|
||||
uint8_t physAddrRange;
|
||||
|
||||
|
||||
virtual uint64_t getRawData() const
|
||||
{
|
||||
return (data);
|
||||
@@ -417,12 +429,6 @@ class TableWalker : public ClockedObject
|
||||
return have_security && (currState->secureLookup && !bits(data, 5));
|
||||
}
|
||||
|
||||
/** True if the current lookup is performed in AArch64 state */
|
||||
bool aarch64;
|
||||
|
||||
/** Width of the granule size in bits */
|
||||
GrainSize grainSize;
|
||||
|
||||
/** Return the descriptor type */
|
||||
EntryType type() const
|
||||
{
|
||||
@@ -430,9 +436,31 @@ class TableWalker : public ClockedObject
|
||||
case 0x1:
|
||||
// In AArch64 blocks are not allowed at L0 for the 4 KB granule
|
||||
// and at L1 for 16/64 KB granules
|
||||
if (grainSize > Grain4KB)
|
||||
return lookupLevel == L2 ? Block : Invalid;
|
||||
return lookupLevel == L0 || lookupLevel == L3 ? Invalid : Block;
|
||||
switch (grainSize) {
|
||||
case Grain4KB:
|
||||
if (lookupLevel == L0 || lookupLevel == L3)
|
||||
return Invalid;
|
||||
else
|
||||
return Block;
|
||||
|
||||
case Grain16KB:
|
||||
if (lookupLevel == L2)
|
||||
return Block;
|
||||
else
|
||||
return Invalid;
|
||||
|
||||
case Grain64KB:
|
||||
// With Armv8.2-LPA (52bit PA) L1 Block descriptors
|
||||
// are allowed for 64KB granule
|
||||
if ((lookupLevel == L1 && physAddrRange == 52) ||
|
||||
lookupLevel == L2)
|
||||
return Block;
|
||||
else
|
||||
return Invalid;
|
||||
|
||||
default:
|
||||
return Invalid;
|
||||
}
|
||||
case 0x3:
|
||||
return lookupLevel == L3 ? Page : Table;
|
||||
default:
|
||||
@@ -451,7 +479,8 @@ class TableWalker : public ClockedObject
|
||||
case Grain16KB:
|
||||
return 25 /* 32 MB */;
|
||||
case Grain64KB:
|
||||
return 29 /* 512 MB */;
|
||||
return lookupLevel == L1 ? 42 /* 4TB MB */
|
||||
: 29 /* 512 MB */;
|
||||
default:
|
||||
panic("Invalid AArch64 VM granule size\n");
|
||||
}
|
||||
@@ -472,36 +501,39 @@ class TableWalker : public ClockedObject
|
||||
/** Return the physical frame, bits shifted right */
|
||||
Addr pfn() const
|
||||
{
|
||||
if (aarch64)
|
||||
return bits(data, 47, offsetBits());
|
||||
return bits(data, 39, offsetBits());
|
||||
}
|
||||
|
||||
/** Return the complete physical address given a VA */
|
||||
Addr paddr(Addr va) const
|
||||
{
|
||||
int n = offsetBits();
|
||||
if (aarch64)
|
||||
return mbits(data, 47, n) | mbits(va, n - 1, 0);
|
||||
return mbits(data, 39, n) | mbits(va, n - 1, 0);
|
||||
return paddr() >> offsetBits();
|
||||
}
|
||||
|
||||
/** Return the physical address of the entry */
|
||||
Addr paddr() const
|
||||
{
|
||||
if (aarch64)
|
||||
return mbits(data, 47, offsetBits());
|
||||
return mbits(data, 39, offsetBits());
|
||||
Addr addr = 0;
|
||||
if (aarch64) {
|
||||
addr = mbits(data, 47, offsetBits());
|
||||
if (physAddrRange == 52 && grainSize == Grain64KB) {
|
||||
addr |= bits(data, 15, 12) << 48;
|
||||
}
|
||||
} else {
|
||||
addr = mbits(data, 39, offsetBits());
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
/** Return the address of the next page table */
|
||||
Addr nextTableAddr() const
|
||||
{
|
||||
assert(type() == Table);
|
||||
if (aarch64)
|
||||
return mbits(data, 47, grainSize);
|
||||
else
|
||||
return mbits(data, 39, 12);
|
||||
Addr table_address = 0;
|
||||
if (aarch64) {
|
||||
table_address = mbits(data, 47, grainSize);
|
||||
// Using 52bit if Armv8.2-LPA is implemented
|
||||
if (physAddrRange == 52 && grainSize == Grain64KB)
|
||||
table_address |= bits(data, 15, 12) << 48;
|
||||
} else {
|
||||
table_address = mbits(data, 39, 12);
|
||||
}
|
||||
|
||||
return table_address;
|
||||
}
|
||||
|
||||
/** Return the address of the next descriptor */
|
||||
@@ -854,7 +886,7 @@ class TableWalker : public ClockedObject
|
||||
bool haveSecurity;
|
||||
bool _haveLPAE;
|
||||
bool _haveVirtualization;
|
||||
uint8_t physAddrRange;
|
||||
uint8_t _physAddrRange;
|
||||
bool _haveLargeAsid64;
|
||||
|
||||
/** Statistics */
|
||||
@@ -896,6 +928,7 @@ class TableWalker : public ClockedObject
|
||||
bool haveLPAE() const { return _haveLPAE; }
|
||||
bool haveVirtualization() const { return _haveVirtualization; }
|
||||
bool haveLargeAsid64() const { return _haveLargeAsid64; }
|
||||
uint8_t physAddrRange() const { return _physAddrRange; }
|
||||
/** Checks if all state is cleared and if so, completes drain */
|
||||
void completeDrain();
|
||||
DrainState drain() override;
|
||||
@@ -962,7 +995,8 @@ class TableWalker : public ClockedObject
|
||||
|
||||
/// Returns true if the address exceeds the range permitted by the
|
||||
/// system-wide setting or by the TCR_ELx IPS/PS setting
|
||||
static bool checkAddrSizeFaultAArch64(Addr addr, int currPhysAddrRange);
|
||||
bool checkAddrSizeFaultAArch64(Addr addr, int pa_range);
|
||||
|
||||
Fault processWalkAArch64();
|
||||
void processWalkWrapper();
|
||||
EventFunctionWrapper doProcessEvent;
|
||||
|
||||
@@ -89,6 +89,7 @@ TLB::TLB(const ArmTLBParams *p)
|
||||
haveLPAE = tableWalker->haveLPAE();
|
||||
haveVirtualization = tableWalker->haveVirtualization();
|
||||
haveLargeAsid64 = tableWalker->haveLargeAsid64();
|
||||
physAddrRange = tableWalker->physAddrRange();
|
||||
|
||||
if (sys)
|
||||
m5opRange = sys->m5opRange();
|
||||
@@ -949,7 +950,7 @@ TLB::translateMmuOff(ThreadContext *tc, const RequestPtr &req, Mode mode,
|
||||
bool selbit = bits(vaddr, 55);
|
||||
TCR tcr1 = tc->readMiscReg(MISCREG_TCR_EL1);
|
||||
int topbit = computeAddrTop(tc, selbit, is_fetch, tcr1, currEL(tc));
|
||||
int addr_sz = bits(vaddr, topbit, MaxPhysAddrRange);
|
||||
int addr_sz = bits(vaddr, topbit, physAddrRange);
|
||||
if (addr_sz != 0){
|
||||
Fault f;
|
||||
if (is_fetch)
|
||||
|
||||
@@ -431,6 +431,7 @@ protected:
|
||||
bool haveLPAE;
|
||||
bool haveVirtualization;
|
||||
bool haveLargeAsid64;
|
||||
uint8_t physAddrRange;
|
||||
|
||||
AddrRange m5opRange;
|
||||
|
||||
|
||||
@@ -1396,9 +1396,9 @@ decodePhysAddrRange64(uint8_t pa_enc)
|
||||
case 0x4:
|
||||
return 44;
|
||||
case 0x5:
|
||||
case 0x6:
|
||||
case 0x7:
|
||||
return 48;
|
||||
case 0x6:
|
||||
return 52;
|
||||
default:
|
||||
panic("Invalid phys. address range encoding");
|
||||
}
|
||||
@@ -1420,6 +1420,8 @@ encodePhysAddrRange64(int pa_size)
|
||||
return 0x4;
|
||||
case 48:
|
||||
return 0x5;
|
||||
case 52:
|
||||
return 0x6;
|
||||
default:
|
||||
panic("Invalid phys. address range");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user