From 373f7af347628151d23e6a5f81ae280f686df1f2 Mon Sep 17 00:00:00 2001 From: Phanikiran Harithas Date: Sun, 10 Jun 2018 17:45:05 +0530 Subject: [PATCH] power: Add support for Radix Translation Power ISA v3.0 introduces the Radix MMU in addition to the Hash MMU. This patch adds support in gem5 for handling the Radix based address translations when MSR[IR,DR] bits are set. It also adds an example of a radix_walk. Change-Id: I193f8d44f36b429997f7ffcb788a50544ba65a8c Signed-off-by: Phanikiran Harithas Signed-off-by: Venkatnarayan Kulkarni Signed-off-by: Gautham R. Shenoy --- configs/common/Benchmarks.py | 2 + configs/common/FSConfig.py | 2 +- src/arch/power/PowerTLB.py | 12 + src/arch/power/SConscript | 5 + src/arch/power/isa/decoder.isa | 13 +- src/arch/power/radix_walk_example.txt | 327 +++++++++++++++++++ src/arch/power/radixwalk.cc | 438 ++++++++++++++++++++++++++ src/arch/power/radixwalk.hh | 72 +++++ src/arch/power/tlb.cc | 48 ++- src/arch/power/tlb.hh | 6 + src/cpu/BaseCPU.py | 2 +- 11 files changed, 913 insertions(+), 14 deletions(-) create mode 100644 src/arch/power/radix_walk_example.txt create mode 100644 src/arch/power/radixwalk.cc create mode 100644 src/arch/power/radixwalk.hh diff --git a/configs/common/Benchmarks.py b/configs/common/Benchmarks.py index b7d10b563..755994aef 100644 --- a/configs/common/Benchmarks.py +++ b/configs/common/Benchmarks.py @@ -64,6 +64,8 @@ class SysConfig: return env.get('LINUX_IMAGE', disk('linux-aarch32-ael.img')) elif buildEnv['TARGET_ISA'] == 'sparc': return env.get('LINUX_IMAGE', disk('disk.s10hw2')) + elif buildEnv['TARGET_ISA'] == 'power': + return env.get('LINUX_IMAGE', disk('linux-latest.img')) else: print("Don't know what default disk image to use for %s ISA" % buildEnv['TARGET_ISA']) diff --git a/configs/common/FSConfig.py b/configs/common/FSConfig.py index d099d0529..e725d3101 100644 --- a/configs/common/FSConfig.py +++ b/configs/common/FSConfig.py @@ -705,7 +705,7 @@ def makeLinuxPowerSystem(mem_mode, numCPUs=1, mdesc=None, cmdline=None): self.boot_osflags = fillInCmdline(mdesc, cmdline) self.kernel = binary('vmlinux') self.dtb_filename = binary('gem5-power9-fs.dtb') -return self + return self def makeDualRoot(full_system, testSystem, driveSystem, dumpfile): diff --git a/src/arch/power/PowerTLB.py b/src/arch/power/PowerTLB.py index b12c5a8e3..9a86bb20a 100644 --- a/src/arch/power/PowerTLB.py +++ b/src/arch/power/PowerTLB.py @@ -30,6 +30,16 @@ from m5.SimObject import SimObject from m5.params import * +from m5.proxy import * + +from MemObject import MemObject + +class PowerRadixWalk(MemObject): + type = 'PowerRadixWalk' + cxx_class = 'PowerISA::RadixWalk' + cxx_header = 'arch/power/radixwalk.hh' + port = MasterPort("Port for the hardware table walker") + system = Param.System(Parent.any, "system object") from BaseTLB import BaseTLB @@ -38,3 +48,5 @@ class PowerTLB(BaseTLB): cxx_class = 'PowerISA::TLB' cxx_header = 'arch/power/tlb.hh' size = Param.Int(64, "TLB size") + walker = Param.PowerRadixWalk(\ + PowerRadixWalk(), "page table walker") diff --git a/src/arch/power/SConscript b/src/arch/power/SConscript index 473c312ca..b1d1a82b0 100644 --- a/src/arch/power/SConscript +++ b/src/arch/power/SConscript @@ -44,9 +44,12 @@ if env['TARGET_ISA'] == 'power': Source('interrupts.cc') Source('linux/linux.cc') Source('linux/process.cc') + Source('linux/system.cc') Source('isa.cc') Source('pagetable.cc') Source('process.cc') + Source('radixwalk.cc') + Source('system.cc') Source('remote_gdb.cc') Source('stacktrace.cc') Source('tlb.cc') @@ -56,7 +59,9 @@ if env['TARGET_ISA'] == 'power': SimObject('PowerInterrupts.py') SimObject('PowerISA.py') SimObject('PowerTLB.py') + SimObject('PowerSystem.py') DebugFlag('Power') + DebugFlag('RadixWalk') ISADesc('isa/main.isa') diff --git a/src/arch/power/isa/decoder.isa b/src/arch/power/isa/decoder.isa index c7e07264a..799aeb8c6 100644 --- a/src/arch/power/isa/decoder.isa +++ b/src/arch/power/isa/decoder.isa @@ -164,8 +164,11 @@ decode PO default Unknown::unknown() { } } - 17: IntOp::sc({{ xc->syscall(R0, &fault); }}, - [ IsSyscall, IsNonSpeculative, IsSerializeAfter ]); + format IntOp { + 17: sc({{ xc->syscall(R0, &fault); }}, + [ IsSyscall, IsNonSpeculative, IsSerializeAfter ]); + 2: tdi({{ }}); + } format LoadDispOp { 34: lbz({{ Rt = Mem_ub; }}); @@ -874,6 +877,12 @@ decode PO default Unknown::unknown() { 246: dcbtst({{ }}); 598: sync({{ }}, [ IsMemBarrier ]); 854: eieio({{ }}, [ IsMemBarrier ]); + 54: dcbst({{ }}); + 982: icbi({{ }}); + 306: tlbie({{ }}); + 274: tlbiel({{ }}); + 566: tlbsync({{ }}); + 498: slbia({{ }}); } // These instructions are of XO form with bit 21 as the OE bit. diff --git a/src/arch/power/radix_walk_example.txt b/src/arch/power/radix_walk_example.txt new file mode 100644 index 000000000..2e6c734f0 --- /dev/null +++ b/src/arch/power/radix_walk_example.txt @@ -0,0 +1,327 @@ +================ Radix Walk Example ================================= +SPRN_PTCR = 0x10004 : PATB = 0x10, PATS = 0x4 +===================================================================== + Memory Layout +===================================================================== +PARTITION_TABLE +0x10000 : PARTITION_TABLE_1 | PARTITION_TABLE_2 + 0xc0000000000030ad | 0x800000000100000b + HR=1 PATB_GR=1 + RTS1=0x2 PRTB=0x1000 + RPDB=0x300 PRTS=0xb + RTS2=0x5 + RPDS=0xd + +RADIX_ROOT +0x30000 : RADIX_ROOT_PTE | RADIX_ROOT_KERNEL_PTE + 0x8000000000040009 | 0x8000000000040005 + V = 1 | V = 1 + L = 0 | L = 0 + NLB = 0x400 | NLB = 0x400 + NLS = 9 | NLS = 5 + +RADIX_SECOND_LEVEL +0x40000 : RADIX_SECOND_LEVEL_PTE | RADIX_SECOND_LEVEL_KERNEL_PTE + 0xc000000000000187 | 0x8000000000050004 + V = 1 | V = 1 + L = 1 | L = 0 + SW = 0 | NLB = 0x500 + RPN = 0 | NLS = 5 + R = 1 + C = 1 + ATT = 0 + EAA 0x7 + +RADIX_THIRD_LEVEL +0x50000: RADIX_THIRD_LEVEL_KERNEL_PTE + 0xc000000000000187 + V = 1 + L = 1 + SW = 0 + RPN = 0 + R = 1 + C = 1 + ATT = 0 + EAA = 0x7 + + +PROCESS_TABLE: +0x1000000 : PROCESS_TABLE_1 | PROCESS_TABLE_2 //Hypervisor Kernel + 0x40000000000300ac | 0x0 + RTS1 = 0x2 + RPDB = 0x300 + RTS2 = 0x5 + RPDS = 12 + + PROCESS_TABLE_3 | PROCESS_TABLE_3 //Hypervisor Userspace + 0x40000000000300ad | 0x0 + RTS1 = 0x2 + RPDB = 0x300 + RTS2 = 0x5 + RPDS = 13 + +================== Example 1 : Hypervisor Userspace ======================= +MSR[HV] = 1, MSR[PR] = 1 +vaddr = 0x1000 = 0x0000000000001000 + +PTCR : PATB = 0x10 = Partition Table Base + PATS = 0x4 = Partition Table Size + +Getting the Partition Table Entry (PATE0 and PATE1) + +Partition table base address is obtained by left-shifting +PATB by 12 bits. Because the Partition table base is always aligned +to 4k which is also the minimum size of the partition table. + +patb_addr = PATB << 12 = 0x10000 + +effLPID = 0 // HV=1 + +pate1_offset = 0 * 16 + 8 = 8 // Partition Table second word is PATE1 + // for this effLPID + +pate1_addr = patb_addr + pate1_offset = 0x10008 = PARTITION_TABLE_2 +From PARTITION_TABLE_2 +PRTB = 0x1000 + +Process Table Base address is obtained by left-shifting PRTB by 12 +bits. Because the Process table is size aligned and at least is 4k. +prtb_addr = PRTB << 12 = 0x1000000 + +effPID = SPRN_PIDR = 1 // HV=1, PR=1, QUADRANT_0b00 + +prte0_offset = effPID * 16 = 16 //First double word in Process Table + //Indexed by effPID +prte0_addr = prtb_addr + prte0_offset = 0x1000010 = PROCESS_TABLE_3 + +------------------ The Walk Begins Now -------------------- +From PROCESS_TABLE_3 +RPDB = 0x300 +RPDS = 13 +RTS = RTS1 << 3 | RTS2 = 0x2 << 3 | 5 = 1 << 4 + 5 = 21 +totalSize = RTS + 31 = 21 + 31 = 52 = virtual address space used + by software + +Root Level +-------------- +nextLevelBase = RPDB << 8 = 0x30000 +nextLevelSize = RPDS = 13 + +// Call the lower totalSize bits of vaddr as the useful bits. +// The upper nextLevelSize bits of these useful bits +// has the index into the Page Table Directory +// Each entry is 8 bytes. +shift = totalSize - nextLevelSize = 52 - 13 = 39 +mask = (1 << nextLeveSize) - 1 = 0xFFF +index = (vaddr >> shift ) & mask = 0 + +entry_addr = nextLevelBase + index * 8 = 0x30000 + 0 = 0x30000 = RADIX_ROOT_PTE + +From RADIX_ROOT_PTE +V = 1 +L = 0. +NLB = 0x400 +NLS = 9 + +So this is a directory. Hence obtain NLB and NLS + +First Level +---------------- + +// We no longer need the upper nextLevelSize bits +// of the useful bits. Discard them. +// Call remaining bits of vaddr as useful bits. +totalSize = totalSize - nextLevelSize = 52 - 13 = 39 + +//Recompute the new Page Directory Base and the Size +nextLevelBase = NLB >> 8 = 0x40000 +nextLevelSize = NLS = 9 + +// The upper nextLevelSize bits of the useful bits of vaddr +// has the index into the Page Table Directory +// Each entry is 8 bytes. +shift = totalSize - nextLevelSize = 39 - 9 = 30 +mask = (1 << nextLevelSize) - 1 = 0xFF +index = (vaddr >> shift) & mask = 0 + +entry_addr = nextlevelBase + index * 8 = 0x40000 = RADIX_SECOND_LEVEL +V = 1 +L = 1 +RPN = 0 +This is a leaf node. + +Second Level +---------------- + +// We no longer need the upper nextLevelSize useful bits +// in the vaddr. Discard them. Call remaining bits of vaddr as useful +// bits. These bits will tell us precisely which location in the +// real page should we fetch the data from. +totalSize = totalSize - nextLevelSize = 39 - 9 = 30 + +//Compute the real page number base. +rpn_addr = (RPN << 12) = 0 +mask = (1ULL << totalSize) - 1 = 0x000000001fffffff +rpn_mask = ~mask = 0xffffffffe0000000 + +phys_addr = (rpn_addr & rpn_mask) | (vaddr & mask) + = (0 & 0xffffffffe0000000) | (0x1000 & 0x1fffffff) + = 0x1000 + + +Hence Virtual address = Physical Address. + +================== Example 2 : Hypervisor Kernel ======================= +Example 2: +MSR[HV] = 1, MSR[PR] = 0 +vaddr = 0xc000010800003000 + +PTCR : PATB = 0x10 = Partition Table Base (right shifted by 12) + PATS = 0x4 = Partition Table Size (add 12) + +Getting the Partition Table Entry (PATE0 and PATE1) + +Partition table base address is obtained by left-shifting +PATB by 12 bits. Because the Partition table base is always aligned +to 4k which is also the minimum size of the partition table. + +patb_addr = PATB << 12 = 0x10000 + +effLPID = 0 // Hypervisor HV=1. + +pate1_offset = 0 * 16 + 8 = 8 // Partition Table second word is PATE1 + // for this effLPID + +pate1_addr = patb_addr + pate1_offset = 0x10008 = PARTITION_TABLE_2 +From PARTITION_TABLE_2 +PRTB = 0x1000 +prtb_addr = PRTB << 12 = 0x1000000 + +effPID = SPRN_PIDR = 0 // HV=1,PR=0, Quadrant 0b11 = Hypervisor Kernel +prte0_offset = effPID * 16 = 0 //First double word in Process Table + // Indexed by effPID + +prte0_addr = prtb_addr + prte0_offset = 0x1000000 = PROCESS_TABLE_1 + +------------------ The Walk Begins Now -------------------- + +From PROCESS_TABLE_1 +RPDB = 0x30 +RPDS = 12 +RTS = RTS1 << 3 | RTS2 = 0x2 << 3 | 5 = 1 << 4 + 5 = 21 +totalSize = RTS + 31 = 21 + 31 = 52 = virtual address space used + by software + +Root Level +---------------- + +nextLevelBase = RPDB << 12 = 0x30000 +nextLevelSize = RPDS = 12 + +// The lower totalSize bits of vaddr are the useful bits. +// The upper nextLevelSize bits these useful bits +// has the index into the Page Table Directory +// Each entry is 8 bytes. +shift = totalSize - nextLevelSize = 52 - 12 = 40 +mask = (1 << nextLeveSize) - 1 = 0x1FFF +index = (vaddr >> shift ) & mask = (0xc000010800003000 >> 40) & 0xFFF + = (0b1100 0000 0000 0000 0000 0001) & 0xFFF + = (0xc000001) & 0xFFF + = 0x001 + +entry_addr = nextLevelBase + index * 8 + = 0x30000 + 1*8 = 0x30008 + = RADIX_ROOT_KERNEL_PTE +V = 1 +L = 0 +NLB = 0x400 +NLS = 5 + +This a directory. Hence obtain NLB and NLS + +First Level +---------------- + +// We no longer need the upper nextLevelSize of the useful bits +// in the vaddr. Discard them. Call remaining bits of vaddr as useful bits. +totalSize = totalSize - nextLevelSize = 52 - 12 = 40 + +//Recompute the new Page Directory Base and the Size +nextLevelBase = NLB >> 8 = 0x40000 +nextLevelSize = NLS = 5 + + +// The upper nextLevelSize bits of the useful bits +// has the index into the Page Table Directory +// Each entry is 8 bytes. +shift = totalSize - nextLevelSize = 40 - 5 = 35 +mask = (1 << nextLeveSize) - 1 = 0x1F +index = (vaddr >> shift ) & mask = (0xc000010800003000 >> 35) & 0x1F + = (0b0001 1000 0000 0000 0000 0000 0010 0001) & 0x1F + = (0x18000021) & 0x1F + = 0x01 + +entry_addr = nextLevelBase + index * 8 + = 0x40000 + 1*8 = 0x40008 + = RADIX_SECOND_LEVEL_KERNEL_PTE + +V = 1 +L = 0 +NLB = 0x500 +NLS = 5 + +Again this is a directory. Hence using the NLB and NLS go to the next +level + +Second Level +---------------- + +// We no longer need the upper nextLevelSize bits of the useful bits. +//in the vaddr. Discard them. Call remaining bits of vaddr as useful bits. +totalSize = totalSize - nextLevelSize = 40 - 5 = 35 + +//Recompute the new Page Directory Base and the Size +nextLevelBase = NLB >> 8 = 0x50000 +nextLevelSize = NLS = 5 + +// The upper nextLevelSize bits of vaddr +// has the index into the Page Table Directory +// Each entry is 8 bytes. +shift = totalSize - nextLevelSize = 35 - 5 = 30 +mask = (1 << nextLeveSize) - 1 = 0x1F + +index = (vaddr >> shift ) & mask = (0xc000010800003000 >> 30) & 0xF + = (0b0001 1000 0000 0000 0000 0000 0010 0001 0000) & 0xF + = (0x18000210) & 0xF + = 0x0 + +entry_addr = nextLevelBase + index * 8 + = 0x50000 + 0*8 = 0x50000 + = RADIX_THIRD_LEVEL_KERNEL_PTE + +V=1 +L=1 +RPN=0 + +This is the leaf level + +Third Level +---------------- + +// We no longer need the upper nextLevelSize useful bits. +// in the vaddr. Discard them. Call remaining bits of vaddr as useful +// bits. These bits will tell us precisely which location in the +// real page should we fetch the data from. +totalSize = totalSize - nextLevelSize = 35 - 5 = 30 + +//Compute the real page number base. +rpn_addr = (RPN << 12) = 0 +mask = (1ULL << totalSize) - 1 = 0x000000001fffffff +rpn_mask = ~mask = 0xffffffffe0000000 + +phys_addr = (rpn_addr & rpn_mask) | (vaddr & mask) + = (0 & 0xffffffffe0000000) | (0xc000010800003000 & 0x1fffffff) + = 0x3000 + +Hence Virtual address 0xc000010800003000 is mapped to Physical Address +0x3000. diff --git a/src/arch/power/radixwalk.cc b/src/arch/power/radixwalk.cc new file mode 100644 index 000000000..04457960e --- /dev/null +++ b/src/arch/power/radixwalk.cc @@ -0,0 +1,438 @@ +#include "arch/power/radixwalk.hh" + +#include + +#include "arch/power/miscregs.hh" +#include "arch/power/tlb.hh" +#include "base/bitfield.hh" +#include "cpu/base.hh" +#include "cpu/thread_context.hh" +#include "debug/RadixWalk.hh" +#include "mem/packet_access.hh" +#include "mem/request.hh" + +#define PRTB_SHIFT 12 +#define PRTB_MASK 0x0ffffffffffff +#define PRTB_ALIGN 4 +#define TABLE_BASE_ALIGN PRTB_SHIFT + +#define RPDB_SHIFT 8 +#define RPDB_MASK 0x0fffffffffffff + +#define RPDS_SHIFT 0 +#define RPDS_MASK 0x1f + +#define NLB_SHIFT RPDB_SHIFT +#define NLB_MASK RPDB_MASK + +#define NLS_SHIFT RPDS_SHIFT +#define NLS_MASK RPDS_MASK + +#define DIR_BASE_ALIGN RPDB_SHIFT + +#define RTS1_SHIFT 61 +#define RTS1_MASK 0x3 +#define RTS2_BITS 3 +#define RTS2_SHIFT 5 +#define RTS2_MASK ((1 << RTS2_BITS) - 1) + +#define RPN_MASK 0x01fffffffffff000 + +#define QUADRANT_MASK 0xc000000000000000 +#define QUADRANT00 0x0000000000000000 +#define QUADRANT01 0x4000000000000000 +#define QUADRANT10 0x8000000000000000 +#define QUADRANT11 0xc000000000000000 + +#define extract(x, shift, mask) ((x >> shift) & mask) +#define align(x, bits) (x << bits) + +#define getRTS(x) ((extract(x, RTS1_SHIFT, RTS1_MASK) << RTS2_BITS) | \ + (extract(x, RTS2_SHIFT, RTS2_MASK))) + +namespace PowerISA { + +uint64_t +RadixWalk::readPhysMem(uint64_t addr, uint64_t dataSize) +{ + uint64_t ret; + Request::Flags flags = Request::PHYSICAL; + + RequestPtr request = new Request(addr, dataSize, flags, this->masterId); + Packet *read = new Packet(request, MemCmd::ReadReq); + read->allocate(); + this->port.sendAtomic(read); + ret = read->get(); + + delete read->req; + + return ret; +} + +uint32_t geteffLPID(ThreadContext *tc) +{ + Msr msr = tc->readIntReg(INTREG_MSR); + + if (msr.hv) + return 0; + + return tc->readIntReg(INTREG_LPIDR); +} + +uint32_t geteffPID(ThreadContext *tc, Addr vaddr) +{ + Msr msr = tc->readIntReg(INTREG_MSR); + uint64_t quadrant = vaddr & QUADRANT_MASK; + + if (msr.hv && !msr.pr) { //Hypervisor Kernel + switch(quadrant) { + case QUADRANT11: + return 0; + case QUADRANT10: + return 0; + default: + return tc->readIntReg(INTREG_PIDR); + } + } + + //Hypervisor Userspace or Guest + switch(quadrant) { + case QUADRANT11: + return 0; + case QUADRANT00: + return tc->readIntReg(INTREG_PIDR); + default: + if (msr.hv) + panic("Errorenous hypervisor Userspace Quadrant : %lx\n", quadrant); + else + panic("Errorenous Guest Quadrant : %lx\n", quadrant); + } + + return tc->readIntReg(INTREG_PIDR); +} + +Fault +RadixWalk::start(ThreadContext * tc, RequestPtr req, BaseTLB::Mode mode) +{ + Addr vaddr = req->getVaddr(); + // prte0 ---> Process Table Entry + DPRINTF(RadixWalk,"Translating vaddr = 0x%lx\n", vaddr); + uint64_t prte0 = getRPDEntry(tc, vaddr); + // rpdb, rpds ---> Root Page Directory Base and Size + uint64_t rpdb = extract(prte0, RPDB_SHIFT, RPDB_MASK); + uint64_t rpds = extract(prte0, RPDS_SHIFT, RPDS_MASK); + // rts = Radix Tree Size - 31. + uint64_t rts = getRTS(prte0); + + uint64_t nextLevelBase = align(rpdb, DIR_BASE_ALIGN); + uint64_t nextLevelSize = rpds; + + // usefulBits ---> radix tree size + 31 + // These are the useful lower bits of vaddr used for + // address translation. + uint64_t usefulBits = rts + 31; //Typically is 52. + + DPRINTF(RadixWalk,"RPDB: 0x%lx\n RPDS: 0x%lx\n usefulBits: %ld\n\n" + ,rpdb,rpds,usefulBits); + + //TODO: + //========== + // Fault should be generated as a return value of walkTree. Perhaps + // we can pass paddr as a pointer in which the physical address can + // be returned. + // As of now we assume that there are no faults. + Addr paddr = this->walkTree(vaddr, nextLevelBase, + nextLevelSize, usefulBits); + req->setPaddr(paddr); + DPRINTF(RadixWalk,"Radix Translated %#x -> %#x\n",vaddr,paddr); + return NoFault; +} + +uint64_t +RadixWalk::getRPDEntry(ThreadContext * tc, Addr vaddr) +{ + Ptcr ptcr = tc->readIntReg(INTREG_PTCR); + uint32_t efflpid = geteffLPID(tc); + + DPRINTF(RadixWalk,"PTCR:%lx\n",(uint64_t)ptcr); + DPRINTF(RadixWalk,"effLPID: %x\n",efflpid); + + //Accessing 2nd double word of partition table (pate1) + //Ref: Power ISA Manual v3.0B, Book-III, section 5.7.6.1 + // PTCR Layout + // ==================================================== + // ----------------------------------------------- + // | /// | PATB | /// | PATS | + // ----------------------------------------------- + // 0 4 51 52 58 59 63 + // PATB[4:51] holds the base address of the Partition Table, + // right shifted by 12 bits. + // This is because the address of the Partition base is + // 4k aligned. Hence, the lower 12bits, which are always + // 0 are ommitted from the PTCR. + // + // Thus, The Partition Table Base is obtained by (PATB << 12) + // + // PATS represents the partition table size right-shifted by 12 bits. + // The minimal size of the partition table is 4k. + // Thus partition table size = (1 << PATS + 12). + // + // Partition Table + // ==================================================== + // 0 PATE0 63 PATE1 127 + // |----------------------|----------------------| + // | | | + // |----------------------|----------------------| + // | | | + // |----------------------|----------------------| + // | | | <-- effLPID + // |----------------------|----------------------| + // . + // . + // . + // |----------------------|----------------------| + // | | | + // |----------------------|----------------------| + // + // The effective LPID forms the index into the Partition Table. + // + // Each entry in the partition table contains 2 double words, PATE0, PATE1, + // corresponding to that partition. + // + // In case of Radix, The structure of PATE0 and PATE1 is as follows. + // + // PATE0 Layout + // ----------------------------------------------- + // |1|RTS1|/| RPDB | RTS2 | RPDS | + // ----------------------------------------------- + // 0 1 2 3 4 55 56 58 59 63 + // + // HR[0] : For Radix Page table, first bit should be 1. + // RTS1[1:2] : Gives one fragment of the Radix treesize + // RTS2[56:58] : Gives the second fragment of the Radix Tree size. + // RTS = (RTS1 << 3 + RTS2) + 31. + // + // RPDB[4:55] = Root Page Directory Base. + // RPDS = Logarithm of Root Page Directory Size right shifted by 3. + // Thus, Root page directory size = 1 << (RPDS + 3). + // Note: RPDS >= 5. + // + // PATE1 Layout + // ----------------------------------------------- + // |///| PRTB | // | PRTS | + // ----------------------------------------------- + // 0 3 4 51 52 58 59 63 + // + // PRTB[4:51] = Process Table Base. This is aligned to size. + // PRTS[59: 63] = Process Table Size right shifted by 12. + // Minimal size of the process table is 4k. + // Process Table Size = (1 << PRTS + 12). + // Note: PRTS <= 24. + // + // Computing the size aligned Process Table Base: + // table_base = (PRTB & ~((1 << PRTS) - 1)) << 12 + // Thus, the lower 12+PRTS bits of table_base will + // be zero. + + uint64_t pate1Addr = align(ptcr.patb, TABLE_BASE_ALIGN) + + (efflpid*sizeof(uint64_t)*2) + 8; + uint64_t dataSize = 8; + uint64_t pate1 = this->readPhysMem(pate1Addr, dataSize); + DPRINTF(RadixWalk,"2nd Double word of partition table entry: %lx\n",pate1); + + uint64_t prtb = extract(pate1, PRTB_SHIFT, PRTB_MASK); + prtb = align(prtb, TABLE_BASE_ALIGN); + + //Ref: Power ISA Manual v3.0B, Book-III, section 5.7.6.2 + // + // Process Table + // ========================== + // 0 PRTE0 63 PRTE1 127 + // |----------------------|----------------------| + // | | | + // |----------------------|----------------------| + // | | | + // |----------------------|----------------------| + // | | | <-- effPID + // |----------------------|----------------------| + // . + // . + // . + // |----------------------|----------------------| + // | | | + // |----------------------|----------------------| + // + // The effective Process id (PID) forms the index into the Process Table. + // + // Each entry in the partition table contains 2 double words, PRTE0, PRTE1, + // corresponding to that process + // + // In case of Radix, The structure of PRTE0 and PRTE1 is as follows. + // + // PRTE0 Layout + // ----------------------------------------------- + // |/|RTS1|/| RPDB | RTS2 | RPDS | + // ----------------------------------------------- + // 0 1 2 3 4 55 56 58 59 63 + // + // RTS1[1:2] : Gives one fragment of the Radix treesize + // RTS2[56:58] : Gives the second fragment of the Radix Tree size. + // RTS = (RTS1 << 3 + RTS2) << 31, + // since minimal Radix Tree size is 4G. + // + // RPDB = Root Page Directory Base. + // RPDS = Root Page Directory Size right shifted by 3. + // Thus, Root page directory size = RPDS << 3. + // Note: RPDS >= 5. + // + // PRTE1 Layout + // ----------------------------------------------- + // | /// | + // ----------------------------------------------- + // 0 63 + // All bits are reserved. + + uint32_t effPID = geteffPID(tc, vaddr); + DPRINTF(RadixWalk,"effPID=%d\n", effPID); + uint64_t prte0Addr = prtb + effPID*sizeof(prtb)*2 ; + DPRINTF(RadixWalk,"Process table base: %lx\n",prtb); + uint64_t prte0 = this->readPhysMem(prte0Addr, dataSize); + //prte0 ---> Process Table Entry + + DPRINTF(RadixWalk,"process table entry: %lx\n\n",prte0); + return prte0; +} + +Addr +RadixWalk::walkTree(Addr vaddr ,uint64_t curBase , + uint64_t curSize ,uint64_t usefulBits) +{ + uint64_t dataSize = 8; + + if (curSize < 5) { + panic("vaddr = %lx, Radix RPDS = %lx,is less than 5\n", + vaddr, curSize); + } + // vaddr 64 Bit + // vaddr |-----------------------------------------------------| + // | Unused | Used | + // |-----------|-----------------------------------------| + // | 0000000 | usefulBits = X bits (typically 52) | + // |-----------|-----------------------------------------| + // | |<--Cursize---->| | + // | | Index | | + // | | into Page | | + // | | Directory | | + // |-----------------------------------------------------| + // | | + // V | + // PDE |---------------------------| | + // |V|L|//| NLB |///|NLS| | + // |---------------------------| | + // PDE = Page Directory Entry | + // [0] = V = Valid Bit | + // [1] = L = Leaf bit. If 0, then | + // [4:55] = NLB = Next Level Base | + // right shifted by 8 | + // [59:63] = NLS = Next Level Size | + // | NLS >= 5 | + // | V + // | |--------------------------| + // | | usfulBits = X-Cursize | + // | |--------------------------| + // |---------------------><--NLS-->| | + // | Index | | + // | into | | + // | PDE | | + // |--------------------------| + // | + // If the next PDE obtained by | + // (NLB << 8 + 8 * index) is a | + // nonleaf, then repeat the above. | + // | + // If the next PDE is a leaf, | + // then Leaf PDE structure is as | + // follows | + // | + // | + // Leaf PDE | + // |------------------------------| |----------------| + // |V|L|sw|//|RPN|sw|R|C|/|ATT|EAA| | usefulBits | + // |------------------------------| |----------------| + // [0] = V = Valid Bit | + // [1] = L = Leaf Bit = 1 if leaf | + // PDE | + // [2] = Sw = Sw bit 0. | + // [7:51] = RPN = Real Page Number, V + // real_page = RPN << 12 -------------> Logical OR + // [52:54] = Sw Bits 1:3 | + // [55] = R = Reference | + // [56] = C = Change V + // [58:59] = Att = Physical Address + // 0b00 = Normal Memory + // 0b01 = SAO + // 0b10 = Non Idenmpotent + // 0b11 = Tolerant I/O + // [60:63] = Encoded Access + // Authority + // + uint64_t shift = usefulBits - curSize; + uint64_t mask = (1UL << curSize) - 1; + uint64_t index = extract(vaddr, shift, mask); + + uint64_t entryAddr = curBase + (index * sizeof(uint64_t)); + Rpde rpde = this->readPhysMem(entryAddr, dataSize); + DPRINTF(RadixWalk,"rpde:%lx\n",(uint64_t)rpde); + usefulBits = usefulBits - curSize; + //Ref: Power ISA Manual v3.0B, Book-III, section 5.7.10.2 + if (rpde.leaf == 1) + { + uint64_t realpn = rpde & RPN_MASK; + uint64_t pageMask = (1UL << usefulBits) - 1; + Addr paddr = (realpn & ~pageMask) | (vaddr & pageMask); + + //TODO: Check for permissions. + // We aren't doing that right now + // If there is a mismatch in the permissions, + // generate a fault. + DPRINTF(RadixWalk,"paddr:%lx\n",paddr); + return paddr; + } + + uint64_t nextLevelBase = align(rpde.NLB, DIR_BASE_ALIGN); + uint64_t nextLevelSize = rpde.NLS; + DPRINTF(RadixWalk,"NLB: %lx\n",(uint64_t)nextLevelBase); + DPRINTF(RadixWalk,"NLS: %lx\n",(uint64_t)nextLevelSize); + DPRINTF(RadixWalk,"usefulBits: %lx",(uint64_t)usefulBits); + return walkTree(vaddr, nextLevelBase, nextLevelSize, usefulBits); +} + +void +RadixWalk::RadixPort::recvReqRetry() +{ + +} + +bool +RadixWalk::RadixPort::recvTimingResp(PacketPtr pkt) +{ + return true; +} + +BaseMasterPort & +RadixWalk::getMasterPort(const std::string &if_name, PortID idx) +{ + if (if_name == "port") + return port; + else{ + return MemObject::getMasterPort(if_name, idx); + } +} + +/* end namespace PowerISA */ } + +PowerISA::RadixWalk * +PowerRadixWalkParams::create() +{ + return new PowerISA::RadixWalk(this); +} diff --git a/src/arch/power/radixwalk.hh b/src/arch/power/radixwalk.hh new file mode 100644 index 000000000..3a9efb924 --- /dev/null +++ b/src/arch/power/radixwalk.hh @@ -0,0 +1,72 @@ +#ifndef __ARCH_POWER_RADIX_WALK_HH__ +#define __ARCH_POWER_RADIX_WALK_HH__ + +#include "arch/power/tlb.hh" +#include "base/bitunion.hh" +#include "base/types.hh" +#include "mem/mem_object.hh" +#include "mem/packet.hh" +#include "params/PowerRadixWalk.hh" +#include "sim/faults.hh" +#include "sim/system.hh" + +class ThreadContext; + +namespace PowerISA +{ + class RadixWalk : public MemObject + { + protected: + // Port for accessing memory + class RadixPort : public MasterPort + { + public: + RadixPort(const std::string &_name, RadixWalk * _rwalk) : + MasterPort(_name, _rwalk), rwalk(_rwalk) + {} + + protected: + RadixWalk *rwalk; + bool recvTimingResp(PacketPtr pkt); + void recvReqRetry(); + }; + + friend class RadixPort; + RadixPort port; + System * sys; + MasterID masterId; + uint64_t readPhysMem(uint64_t addr, uint64_t dataSize); + + public: + + BitUnion64(Rpde) + Bitfield<63> valid; + Bitfield<62> leaf; + Bitfield<59, 8> NLB; + Bitfield<4, 0> NLS; + EndBitUnion(Rpde) + + Fault start(ThreadContext * _tc, RequestPtr req, BaseTLB::Mode mode); + BaseMasterPort &getMasterPort(const std::string &if_name, + PortID idx = InvalidPortID); + Addr getRPDEntry(ThreadContext * tc, Addr vaddr); + Addr walkTree(Addr vaddr ,uint64_t ptbase , + uint64_t ptsize ,uint64_t psize); + + typedef PowerRadixWalkParams Params; + + const Params * + params() const + { + return static_cast(_params); + } + + RadixWalk(const Params *params) : + MemObject(params), port(name() + ".port", this), + sys(params->system), + masterId(sys->getMasterId(this, name())) + { + } + }; +} +#endif // __ARCH_POWER_RADIX_WALK_HH__ diff --git a/src/arch/power/tlb.cc b/src/arch/power/tlb.cc index 88abd9407..841cb2f0b 100644 --- a/src/arch/power/tlb.cc +++ b/src/arch/power/tlb.cc @@ -34,7 +34,6 @@ * Stephen Hines * Timothy M. Jones */ - #include "arch/power/tlb.hh" #include @@ -43,6 +42,7 @@ #include "arch/power/faults.hh" #include "arch/power/miscregs.hh" #include "arch/power/pagetable.hh" +#include "arch/power/radixwalk.hh" #include "arch/power/registers.hh" #include "arch/power/utility.hh" #include "base/inifile.hh" @@ -59,6 +59,8 @@ using namespace std; using namespace PowerISA; +namespace PowerISA { + /////////////////////////////////////////////////////////////////////// // // POWER TLB @@ -72,6 +74,8 @@ TLB::TLB(const Params *p) table = new PowerISA::PTE[size]; memset(table, 0, sizeof(PowerISA::PTE[size])); smallPages = 0; + + rwalk = p->walker; } TLB::~TLB() @@ -223,6 +227,12 @@ TLB::unserialize(CheckpointIn &cp) } } +RadixWalk * +TLB::getWalker() +{ + return rwalk; +} + void TLB::regStats() { @@ -314,27 +324,36 @@ TLB::translateData(RequestPtr req, ThreadContext *tc, bool write) Fault TLB::translateAtomic(RequestPtr req, ThreadContext *tc, Mode mode) { + Addr paddr; + Addr vaddr = req->getVaddr(); + DPRINTF(TLB, "Translating vaddr %#x.\n", vaddr); + vaddr &= 0x0fffffffffffffff; if (FullSystem){ - Msr msr = tc->readIntReg(MISCREG_MSR); + Msr msr = tc->readIntReg(INTREG_MSR); if (mode == Execute){ - if (msr.ir) - fatal("Translate Atomic not Implemented for POWER"); + if (msr.ir){ + printf("MSR: %lx\n",(uint64_t)msr); + Fault fault = rwalk->start(tc,req, mode); + paddr = req->getPaddr(); + return fault; + } else{ - Addr vaddr = req->getVaddr(); DPRINTF(TLB, "Translating vaddr %#x.\n", vaddr); - Addr paddr = vaddr; + paddr = vaddr; DPRINTF(TLB, "Translated %#x -> %#x.\n", vaddr, paddr); req->setPaddr(paddr); return NoFault; } } else{ - if (msr.dr) - fatal("Translate Atomic not Implemented for POWER"); + if (msr.dr){ + Fault fault = rwalk->start(tc,req, mode); + paddr = req->getPaddr(); + return fault; + } else{ - Addr vaddr = req->getVaddr(); DPRINTF(TLB, "Translating vaddr %#x.\n", vaddr); - Addr paddr = vaddr; + paddr = vaddr; DPRINTF(TLB, "Translated %#x -> %#x.\n", vaddr, paddr); req->setPaddr(paddr); return NoFault; @@ -374,6 +393,15 @@ TLB::index(bool advance) return *pte; } +} + +BaseMasterPort * +TLB::getMasterPort() +{ + return &rwalk->getMasterPort("port"); +} + + PowerISA::TLB * PowerTLBParams::create() { diff --git a/src/arch/power/tlb.hh b/src/arch/power/tlb.hh index ca82d0b45..a73498721 100644 --- a/src/arch/power/tlb.hh +++ b/src/arch/power/tlb.hh @@ -96,6 +96,8 @@ struct TlbEntry } }; +class RadixWalk; + class TLB : public BaseTLB { protected: @@ -129,6 +131,8 @@ class TLB : public BaseTLB Stats::Formula accesses; public: + friend class RadixWalk; + RadixWalk *rwalk; typedef PowerTLBParams Params; TLB(const Params *p); virtual ~TLB(); @@ -150,6 +154,7 @@ class TLB : public BaseTLB void insert(Addr vaddr, PowerISA::PTE &pte); void insertAt(PowerISA::PTE &pte, unsigned Index, int _smallPages); void flushAll() override; + RadixWalk *getWalker(); void demapPage(Addr vaddr, uint64_t asn) override @@ -175,6 +180,7 @@ class TLB : public BaseTLB void unserialize(CheckpointIn &cp) override; void regStats() override; + BaseMasterPort *getMasterPort() override; }; } // namespace PowerISA diff --git a/src/cpu/BaseCPU.py b/src/cpu/BaseCPU.py index e02d36724..9a2506778 100644 --- a/src/cpu/BaseCPU.py +++ b/src/cpu/BaseCPU.py @@ -229,7 +229,7 @@ class BaseCPU(MemObject): dcache_port = MasterPort("Data Port") _cached_ports = ['icache_port', 'dcache_port'] - if buildEnv['TARGET_ISA'] in ['x86', 'arm']: + if buildEnv['TARGET_ISA'] in ['x86', 'arm', 'power']: _cached_ports += ["itb.walker.port", "dtb.walker.port"] _uncached_slave_ports = [] -- 2.30.2