arch-power: Add multi-mode support
authorSandipan Das <sandipan@linux.ibm.com>
Sat, 6 Feb 2021 12:09:51 +0000 (17:39 +0530)
committerSandipan Das <sandipan@linux.ibm.com>
Mon, 15 Feb 2021 08:32:38 +0000 (14:02 +0530)
This adds multi-mode support and allows the simulator to
read, interpret and execute 32bit and 64-bit, big and
little endian binaries in syscall emulation mode.

During process initialization, a minimal set of hardware
capabilities are also advertised by the simulator to show
support for 64-bit mode and little endian byte order.
This also adds some fixups specific to 64-bit ELF ABI v1
that readjust the entry point and symbol table due to the
use of function descriptors.

Change-Id: I124339eff7b70dbd14e50ff970340c88c13bd0ad
Signed-off-by: Sandipan Das <sandipan@linux.ibm.com>
src/arch/power/PowerSeWorkload.py
src/arch/power/linux/se_workload.cc
src/arch/power/process.cc
src/arch/power/registers.hh
src/base/loader/elf_object.cc
src/base/loader/object_file.cc
src/base/loader/object_file.hh

index 2d3d3cb7fa47dd2e882e32d75858df64c47e0d26..40e989c0ef4176e46b3481a87b70cf90b2251039 100644 (file)
@@ -40,5 +40,5 @@ class PowerEmuLinux(PowerSEWorkload):
 
     @classmethod
     def _is_compatible_with(cls, obj):
-        return obj.get_arch() == 'power' and \
+        return obj.get_arch() in ('power', 'power64') and  \
                 obj.get_op_sys() in ('linux', 'unknown')
index 864468ff6f72353608544da109ddfa69aa6cd64c..091fd35b802aeab9aaedfe817361904d334e36a7 100644 (file)
@@ -47,7 +47,9 @@ class LinuxLoader : public Process::Loader
     Process *
     load(const ProcessParams &params, ::Loader::ObjectFile *obj) override
     {
-        if (obj->getArch() != ::Loader::Power)
+        auto arch = obj->getArch();
+
+        if (arch != ::Loader::Power && arch != ::Loader::Power64)
             return nullptr;
 
         auto opsys = obj->getOpSys();
@@ -57,7 +59,10 @@ class LinuxLoader : public Process::Loader
             opsys = ::Loader::Linux;
         }
 
-        if (opsys != ::Loader::Linux)
+        if ((arch == ::Loader::Power && opsys != ::Loader::Linux) ||
+            (arch == ::Loader::Power64 &&
+             opsys != ::Loader::LinuxPower64ABIv1 &&
+             opsys != ::Loader::LinuxPower64ABIv2))
             return nullptr;
 
         return new PowerProcess(params, obj);
index 409f980ae55370c32419694917d0ab75bb3b3b5e..735b3d9de0f7d4d8054795f67c5c305eb5903931 100644 (file)
@@ -76,7 +76,55 @@ PowerProcess::initState()
 {
     Process::initState();
 
-    argsInit<uint32_t>(PageBytes);
+    if (objFile->getArch() == ::Loader::Power)
+        argsInit<uint32_t>(PageBytes);
+    else
+        argsInit<uint64_t>(PageBytes);
+
+    // Fix up entry point and symbol table for 64-bit ELF ABI v1
+    if (objFile->getOpSys() != ::Loader::LinuxPower64ABIv1)
+        return;
+
+    // Fix entry point address and the base TOC pointer by looking the
+    // the function descriptor in the .opd section
+    Addr entryPoint, tocBase;
+    ByteOrder byteOrder = objFile->getByteOrder();
+    ThreadContext *tc = system->threads[contextIds[0]];
+
+    // The first doubleword of the descriptor contains the address of the
+    // entry point of the function
+    initVirtMem->readBlob(getStartPC(), &entryPoint, sizeof(Addr));
+
+    // Update the PC state
+    auto pc = tc->pcState();
+    pc.byteOrder(byteOrder);
+    pc.set(gtoh(entryPoint, byteOrder));
+    tc->pcState(pc);
+
+    // The second doubleword of the descriptor contains the TOC base
+    // address for the function
+    initVirtMem->readBlob(getStartPC() + 8, &tocBase, sizeof(Addr));
+    tc->setIntReg(TOCPointerReg, gtoh(tocBase, byteOrder));
+
+    // Fix symbol table entries as they would otherwise point to the
+    // function descriptor rather than the actual entry point address
+    auto *symbolTable = new ::Loader::SymbolTable;
+
+    for (auto sym : ::Loader::debugSymbolTable) {
+        Addr entry;
+        ::Loader::Symbol symbol = sym;
+
+        // Try to read entry point from function descriptor
+        if (initVirtMem->tryReadBlob(sym.address, &entry, sizeof(Addr)))
+            symbol.address = gtoh(entry, byteOrder);
+
+        symbolTable->insert(symbol);
+    }
+
+    // Replace the current debug symbol table
+    ::Loader::debugSymbolTable.clear();
+    ::Loader::debugSymbolTable.insert(*symbolTable);
+    delete symbolTable;
 }
 
 template <typename IntType>
@@ -85,6 +133,8 @@ PowerProcess::argsInit(int pageSize)
 {
     int intSize = sizeof(IntType);
     ByteOrder byteOrder = objFile->getByteOrder();
+    bool is64bit = (objFile->getArch() == ::Loader::Power64);
+    bool isLittleEndian = (byteOrder == ByteOrder::little);
     std::vector<AuxVector<IntType>> auxv;
 
     std::string filename;
@@ -104,13 +154,21 @@ PowerProcess::argsInit(int pageSize)
     //Auxilliary vectors are loaded only for elf formatted executables.
     auto *elfObject = dynamic_cast<::Loader::ElfObject *>(objFile);
     if (elfObject) {
-        IntType features = 0;
+        IntType features = PPC_FEATURE_32;
+
+        // Check if running in 64-bit mode
+        if (is64bit)
+            features |= PPC_FEATURE_64;
+
+        // Check if running in little endian mode
+        if (isLittleEndian)
+            features |= PPC_FEATURE_PPC_LE | PPC_FEATURE_TRUE_LE;
 
         //Bits which describe the system hardware capabilities
         //XXX Figure out what these should be
         auxv.emplace_back(M5_AT_HWCAP, features);
         //The system page size
-        auxv.emplace_back(M5_AT_PAGESZ, PowerISA::PageBytes);
+        auxv.emplace_back(M5_AT_PAGESZ, pageSize);
         //Frequency at which times() increments
         auxv.emplace_back(M5_AT_CLKTCK, 0x64);
         // For statically linked executables, this is the virtual address of
@@ -276,7 +334,7 @@ PowerProcess::argsInit(int pageSize)
 
     //Set the machine status for a typical userspace
     Msr msr = 0;
-    msr.sf = (intSize == 8);
+    msr.sf = is64bit;
     msr.hv = 1;
     msr.ee = 1;
     msr.pr = 1;
@@ -284,10 +342,13 @@ PowerProcess::argsInit(int pageSize)
     msr.ir = 1;
     msr.dr = 1;
     msr.ri = 1;
-    msr.le = (byteOrder == ByteOrder::little);
+    msr.le = isLittleEndian;
     tc->setMiscReg(MISCREG_MSR, msr);
 
-    tc->pcState(getStartPC());
+    auto pc = tc->pcState();
+    pc.set(getStartPC());
+    pc.byteOrder(byteOrder);
+    tc->pcState(pc);
 
     //Align the "stack_min" to a page boundary.
     memState->setStackMin(roundDown(stack_min, pageSize));
index c9bdf1c8bdf35edc59097c583a3a5c83a39c29eb..2745a92c22f45d6671e069fa02c5bdf371875666 100644 (file)
@@ -85,6 +85,7 @@ const int ArgumentReg3 = 6;
 const int ArgumentReg4 = 7;
 const int ArgumentReg5 = 8;
 const int StackPointerReg = 1;
+const int TOCPointerReg = 2;
 
 // There isn't one in Power, but we need to define one somewhere
 const int ZeroReg = NumIntRegs - 1;
index 4198875361a6b900c9ff76add03af1e81f69ac37..66bd066f24a498443b698a9f0098e355cdc9e2ab 100644 (file)
@@ -247,15 +247,8 @@ ElfObject::determineArch()
         arch = (eclass == ELFCLASS64) ? Riscv64 : Riscv32;
     } else if (emach == EM_PPC && eclass == ELFCLASS32) {
         arch = Power;
-        if (edata != ELFDATA2MSB) {
-            fatal("The binary you're trying to load is compiled for "
-                  "little endian Power.\ngem5 only supports big "
-                  "endian Power. Please recompile your binary.\n");
-        }
-    } else if (emach == EM_PPC64) {
-        fatal("The binary you're trying to load is compiled for 64-bit "
-              "Power. M5\n only supports 32-bit Power. Please "
-              "recompile your binary.\n");
+    } else if (emach == EM_PPC64 && eclass == ELFCLASS64) {
+        arch = Power64;
     } else {
         warn("Unknown architecture: %d\n", emach);
     }
@@ -264,6 +257,21 @@ ElfObject::determineArch()
 void
 ElfObject::determineOpSys()
 {
+    // For 64-bit Power, EI_OSABI and EI_ABIVERSION cannot be used to
+    // determine the ABI version used by the ELF object
+    if (ehdr.e_machine == EM_PPC64) {
+        switch (ehdr.e_flags & 0x3) {
+            case 0x1: opSys = LinuxPower64ABIv1; return;
+            case 0x2: opSys = LinuxPower64ABIv2; return;
+            default:
+                if (ehdr.e_ident[EI_DATA] == ELFDATA2MSB)
+                    opSys = LinuxPower64ABIv1;
+                if (ehdr.e_ident[EI_DATA] == ELFDATA2LSB)
+                    opSys = LinuxPower64ABIv2;
+                return;
+        }
+    }
+
     // Detect the operating system
     switch (ehdr.e_ident[EI_OSABI]) {
       case ELFOSABI_LINUX:
index 6fdf228d4f3e17dae4ec6ea0590c6e3834cfaf2c..d8075a520947cd61ac019429399805b9a47f88d2 100644 (file)
@@ -62,6 +62,8 @@ archToString(Arch arch)
         return "thumb";
       case Power:
         return "power";
+      case Power64:
+        return "power64";
       case Riscv64:
         return "riscv64";
       case Riscv32:
@@ -80,6 +82,8 @@ opSysToString(OpSys op_sys)
       case Tru64:
         return "tru64";
       case Linux:
+      case LinuxPower64ABIv1:
+      case LinuxPower64ABIv2:
         return "linux";
       case Solaris:
         return "solaris";
index 7d542a98e3c6413510c5161d39cdcb9c4a2afe59..19d9bd6fe067d9f14a12b85dd82c463f1bc78ea2 100644 (file)
@@ -53,6 +53,7 @@ enum Arch {
     Arm,
     Thumb,
     Power,
+    Power64,
     Riscv64,
     Riscv32
 };
@@ -65,6 +66,8 @@ enum OpSys {
     Linux,
     Solaris,
     LinuxArmOABI,
+    LinuxPower64ABIv1,
+    LinuxPower64ABIv2,
     FreeBSD
 };