diff --git a/src/arch/power/PowerSeWorkload.py b/src/arch/power/PowerSeWorkload.py index ef7bbb5c93..2b081f292d 100644 --- a/src/arch/power/PowerSeWorkload.py +++ b/src/arch/power/PowerSeWorkload.py @@ -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') diff --git a/src/arch/power/linux/se_workload.cc b/src/arch/power/linux/se_workload.cc index 75eb210422..815a145b76 100644 --- a/src/arch/power/linux/se_workload.cc +++ b/src/arch/power/linux/se_workload.cc @@ -50,7 +50,9 @@ class LinuxLoader : public Process::Loader Process * load(const ProcessParams ¶ms, 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(); @@ -60,7 +62,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); diff --git a/src/arch/power/process.cc b/src/arch/power/process.cc index 688cc1eb6c..8ac5946bae 100644 --- a/src/arch/power/process.cc +++ b/src/arch/power/process.cc @@ -82,7 +82,55 @@ PowerProcess::initState() { Process::initState(); - argsInit(PageBytes); + if (objFile->getArch() == loader::Power) + argsInit(PageBytes); + else + argsInit(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 @@ -91,6 +139,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> auxv; std::string filename; @@ -110,13 +160,21 @@ PowerProcess::argsInit(int pageSize) //Auxilliary vectors are loaded only for elf formatted executables. auto *elfObject = dynamic_cast(objFile); if (elfObject) { - IntType features = 0; + IntType features = HWCAP_FEATURE_32; + + // Check if running in 64-bit mode + if (is64bit) + features |= HWCAP_FEATURE_64; + + // Check if running in little endian mode + if (isLittleEndian) + features |= HWCAP_FEATURE_PPC_LE | HWCAP_FEATURE_TRUE_LE; //Bits which describe the system hardware capabilities //XXX Figure out what these should be auxv.emplace_back(gem5::auxv::Hwcap, features); //The system page size - auxv.emplace_back(gem5::auxv::Pagesz, PowerISA::PageBytes); + auxv.emplace_back(gem5::auxv::Pagesz, pageSize); //Frequency at which times() increments auxv.emplace_back(gem5::auxv::Clktck, 0x64); // For statically linked executables, this is the virtual address of @@ -282,7 +340,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; @@ -290,10 +348,13 @@ PowerProcess::argsInit(int pageSize) msr.ir = 1; msr.dr = 1; msr.ri = 1; - msr.le = (byteOrder == ByteOrder::little); + msr.le = isLittleEndian; tc->setIntReg(INTREG_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)); diff --git a/src/arch/power/regs/int.hh b/src/arch/power/regs/int.hh index 11999e590a..968b94f36a 100644 --- a/src/arch/power/regs/int.hh +++ b/src/arch/power/regs/int.hh @@ -53,6 +53,7 @@ const int ArgumentReg3 = 6; const int ArgumentReg4 = 7; const int ArgumentReg5 = 8; const int StackPointerReg = 1; +const int TOCPointerReg = 2; enum MiscIntRegNums { diff --git a/src/base/loader/elf_object.cc b/src/base/loader/elf_object.cc index 28721f5477..31390cfde1 100644 --- a/src/base/loader/elf_object.cc +++ b/src/base/loader/elf_object.cc @@ -252,15 +252,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); } @@ -269,6 +262,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: diff --git a/src/base/loader/object_file.cc b/src/base/loader/object_file.cc index 2566853e44..bfa0a1dd64 100644 --- a/src/base/loader/object_file.cc +++ b/src/base/loader/object_file.cc @@ -66,6 +66,8 @@ archToString(Arch arch) return "thumb"; case Power: return "power"; + case Power64: + return "power64"; case Riscv64: return "riscv64"; case Riscv32: @@ -84,6 +86,8 @@ opSysToString(OpSys op_sys) case Tru64: return "tru64"; case Linux: + case LinuxPower64ABIv1: + case LinuxPower64ABIv2: return "linux"; case Solaris: return "solaris"; diff --git a/src/base/loader/object_file.hh b/src/base/loader/object_file.hh index 1079f164c2..e8a96dc2b1 100644 --- a/src/base/loader/object_file.hh +++ b/src/base/loader/object_file.hh @@ -59,6 +59,7 @@ enum Arch Arm, Thumb, Power, + Power64, Riscv64, Riscv32 }; @@ -72,6 +73,8 @@ enum OpSys Linux, Solaris, LinuxArmOABI, + LinuxPower64ABIv1, + LinuxPower64ABIv2, FreeBSD };