/* * Copyright (c) 2015, 2017 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall * not be construed as granting a license to any other intellectual * property including but not limited to intellectual property relating * to a hardware implementation of the functionality of the software * licensed hereunder. You may use the software subject to the license * terms below provided that you ensure that this notice is replicated * unmodified and in its entirety in all distributions of the software, * modified or unmodified, in source code or in binary form. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer; * redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution; * neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Authors: Andreas Sandberg */ #include "arch/arm/kvm/armv8_cpu.hh" #include #include "debug/KvmContext.hh" #include "params/ArmV8KvmCPU.hh" // Unlike gem5, kvm doesn't count the SP as a normal integer register, // which means we only have 31 normal integer registers. constexpr static unsigned NUM_XREGS = NUM_ARCH_INTREGS - 1; static_assert(NUM_XREGS == 31, "Unexpected number of aarch64 int. regs."); // The KVM interface accesses vector registers of 4 single precision // floats instead of individual registers. constexpr static unsigned NUM_QREGS = NumFloatV8ArchRegs / 4; static_assert(NUM_QREGS == 32, "Unexpected number of aarch64 vector regs."); #define EXTRACT_FIELD(v, name) \ (((v) & name ## _MASK) >> name ## _SHIFT) #define CORE_REG(name, size) \ (KVM_REG_ARM64 | KVM_REG_ARM_CORE | \ KVM_REG_SIZE_ ## size | \ KVM_REG_ARM_CORE_REG(name)) #define INT_REG(name) CORE_REG(name, U64) #define SIMD_REG(name) CORE_REG(name, U128) #define SYS_MPIDR_EL1 ARM64_SYS_REG(0b11, 0b000, 0b0000, 0b0000, 0b101) constexpr uint64_t kvmXReg(const int num) { return INT_REG(regs.regs[0]) + (INT_REG(regs.regs[1]) - INT_REG(regs.regs[0])) * num; } constexpr uint64_t kvmFPReg(const int num) { return SIMD_REG(fp_regs.vregs[0]) + (SIMD_REG(fp_regs.vregs[1]) - SIMD_REG(fp_regs.vregs[0])) * num; } union KvmFPReg { union { uint32_t i; float f; } s[4]; union { uint64_t i; double f; } d[2]; uint8_t data[32]; }; #define FP_REGS_PER_VFP_REG 4 static_assert(sizeof(FloatRegBits) == 4, "Unexpected float reg size"); const std::vector ArmV8KvmCPU::intRegMap = { { INT_REG(regs.sp), INTREG_SP0, "SP(EL0)" }, { INT_REG(sp_el1), INTREG_SP1, "SP(EL1)" }, }; const std::vector ArmV8KvmCPU::miscRegMap = { MiscRegInfo(INT_REG(elr_el1), MISCREG_ELR_EL1, "ELR(EL1)"), MiscRegInfo(INT_REG(spsr[KVM_SPSR_EL1]), MISCREG_SPSR_EL1, "SPSR(EL1)"), MiscRegInfo(INT_REG(spsr[KVM_SPSR_ABT]), MISCREG_SPSR_ABT, "SPSR(ABT)"), MiscRegInfo(INT_REG(spsr[KVM_SPSR_UND]), MISCREG_SPSR_UND, "SPSR(UND)"), MiscRegInfo(INT_REG(spsr[KVM_SPSR_IRQ]), MISCREG_SPSR_IRQ, "SPSR(IRQ)"), MiscRegInfo(INT_REG(spsr[KVM_SPSR_FIQ]), MISCREG_SPSR_FIQ, "SPSR(FIQ)"), MiscRegInfo(INT_REG(fp_regs.fpsr), MISCREG_FPSR, "FPSR"), MiscRegInfo(INT_REG(fp_regs.fpcr), MISCREG_FPCR, "FPCR"), }; const std::vector ArmV8KvmCPU::miscRegIdMap = { MiscRegInfo(SYS_MPIDR_EL1, MISCREG_MPIDR_EL1, "MPIDR(EL1)"), }; ArmV8KvmCPU::ArmV8KvmCPU(ArmV8KvmCPUParams *params) : BaseArmKvmCPU(params) { } ArmV8KvmCPU::~ArmV8KvmCPU() { } void ArmV8KvmCPU::startup() { BaseArmKvmCPU::startup(); // Override ID registers that KVM should "inherit" from gem5. for (const auto &ri : miscRegIdMap) { const uint64_t value(tc->readMiscReg(ri.idx)); DPRINTF(KvmContext, " %s := 0x%x\n", ri.name, value); setOneReg(ri.kvm, value); } } void ArmV8KvmCPU::dump() const { inform("Integer registers:\n"); inform(" PC: %s\n", getAndFormatOneReg(INT_REG(regs.pc))); for (int i = 0; i < NUM_XREGS; ++i) inform(" X%i: %s\n", i, getAndFormatOneReg(kvmXReg(i))); for (int i = 0; i < NUM_QREGS; ++i) inform(" Q%i: %s\n", i, getAndFormatOneReg(kvmFPReg(i))); for (const auto &ri : intRegMap) inform(" %s: %s\n", ri.name, getAndFormatOneReg(ri.kvm)); inform(" %s: %s\n", "PSTATE", getAndFormatOneReg(INT_REG(regs.pstate))); for (const auto &ri : miscRegMap) inform(" %s: %s\n", ri.name, getAndFormatOneReg(ri.kvm)); for (const auto &ri : miscRegIdMap) inform(" %s: %s\n", ri.name, getAndFormatOneReg(ri.kvm)); for (const auto ® : getRegList()) { const uint64_t arch(reg & KVM_REG_ARCH_MASK); if (arch != KVM_REG_ARM64) { inform("0x%x: %s\n", reg, getAndFormatOneReg(reg)); continue; } const uint64_t type(reg & KVM_REG_ARM_COPROC_MASK); switch (type) { case KVM_REG_ARM_CORE: // These have already been printed break; case KVM_REG_ARM64_SYSREG: { const uint64_t op0(EXTRACT_FIELD(reg, KVM_REG_ARM64_SYSREG_OP0)); const uint64_t op1(EXTRACT_FIELD(reg, KVM_REG_ARM64_SYSREG_OP1)); const uint64_t crn(EXTRACT_FIELD(reg, KVM_REG_ARM64_SYSREG_CRN)); const uint64_t crm(EXTRACT_FIELD(reg, KVM_REG_ARM64_SYSREG_CRM)); const uint64_t op2(EXTRACT_FIELD(reg, KVM_REG_ARM64_SYSREG_OP2)); const MiscRegIndex idx( decodeAArch64SysReg(op0, op1, crn, crm, op2)); inform(" %s (op0: %i, op1: %i, crn: %i, crm: %i, op2: %i): %s", miscRegName[idx], op0, op1, crn, crm, op2, getAndFormatOneReg(reg)); } break; case KVM_REG_ARM_DEMUX: { const uint64_t id(EXTRACT_FIELD(reg, KVM_REG_ARM_DEMUX_ID)); const uint64_t val(EXTRACT_FIELD(reg, KVM_REG_ARM_DEMUX_VAL)); if (id == KVM_REG_ARM_DEMUX_ID_CCSIDR) { inform(" CSSIDR[%i]: %s\n", val, getAndFormatOneReg(reg)); } else { inform(" UNKNOWN[%i:%i]: %s\n", id, val, getAndFormatOneReg(reg)); } } break; default: inform("0x%x: %s\n", reg, getAndFormatOneReg(reg)); } } } void ArmV8KvmCPU::updateKvmState() { DPRINTF(KvmContext, "In updateKvmState():\n"); // update pstate register state CPSR cpsr(tc->readMiscReg(MISCREG_CPSR)); cpsr.nz = tc->readCCReg(CCREG_NZ); cpsr.c = tc->readCCReg(CCREG_C); cpsr.v = tc->readCCReg(CCREG_V); if (cpsr.width) { cpsr.ge = tc->readCCReg(CCREG_GE); } else { cpsr.ge = 0; } DPRINTF(KvmContext, " %s := 0x%x\n", "PSTATE", cpsr); setOneReg(INT_REG(regs.pstate), cpsr); for (const auto &ri : miscRegMap) { const uint64_t value(tc->readMiscReg(ri.idx)); DPRINTF(KvmContext, " %s := 0x%x\n", ri.name, value); setOneReg(ri.kvm, value); } for (int i = 0; i < NUM_XREGS; ++i) { const uint64_t value(tc->readIntReg(INTREG_X0 + i)); DPRINTF(KvmContext, " X%i := 0x%x\n", i, value); setOneReg(kvmXReg(i), value); } for (const auto &ri : intRegMap) { const uint64_t value(tc->readIntReg(ri.idx)); DPRINTF(KvmContext, " %s := 0x%x\n", ri.name, value); setOneReg(ri.kvm, value); } for (int i = 0; i < NUM_QREGS; ++i) { const RegIndex reg_base(i * FP_REGS_PER_VFP_REG); KvmFPReg reg; for (int j = 0; j < FP_REGS_PER_VFP_REG; j++) reg.s[j].i = tc->readFloatRegBits(reg_base + j); setOneReg(kvmFPReg(i), reg.data); DPRINTF(KvmContext, " Q%i: %s\n", i, getAndFormatOneReg(kvmFPReg(i))); } for (const auto &ri : getSysRegMap()) { const uint64_t value(tc->readMiscReg(ri.idx)); DPRINTF(KvmContext, " %s := 0x%x\n", ri.name, value); setOneReg(ri.kvm, value); } setOneReg(INT_REG(regs.pc), tc->instAddr()); DPRINTF(KvmContext, " PC := 0x%x\n", tc->instAddr()); } void ArmV8KvmCPU::updateThreadContext() { DPRINTF(KvmContext, "In updateThreadContext():\n"); // Update pstate thread context const CPSR cpsr(tc->readMiscRegNoEffect(MISCREG_CPSR)); DPRINTF(KvmContext, " %s := 0x%x\n", "PSTATE", cpsr); tc->setMiscRegNoEffect(MISCREG_CPSR, cpsr); tc->setCCReg(CCREG_NZ, cpsr.nz); tc->setCCReg(CCREG_C, cpsr.c); tc->setCCReg(CCREG_V, cpsr.v); if (cpsr.width) { tc->setCCReg(CCREG_GE, cpsr.ge); } // Update core misc regs first as they // affect how other registers are mapped. for (const auto &ri : miscRegMap) { const auto value(getOneRegU64(ri.kvm)); DPRINTF(KvmContext, " %s := 0x%x\n", ri.name, value); tc->setMiscRegNoEffect(ri.idx, value); } for (int i = 0; i < NUM_XREGS; ++i) { const auto value(getOneRegU64(kvmXReg(i))); DPRINTF(KvmContext, " X%i := 0x%x\n", i, value); // KVM64 returns registers in 64-bit layout. If we are in aarch32 // mode, we need to map these to banked ARM32 registers. if (inAArch64(tc)) { tc->setIntReg(INTREG_X0 + i, value); } else { tc->setIntRegFlat(IntReg64Map[INTREG_X0 + i], value); } } for (const auto &ri : intRegMap) { const auto value(getOneRegU64(ri.kvm)); DPRINTF(KvmContext, " %s := 0x%x\n", ri.name, value); tc->setIntReg(ri.idx, value); } for (int i = 0; i < NUM_QREGS; ++i) { const RegIndex reg_base(i * FP_REGS_PER_VFP_REG); KvmFPReg reg; DPRINTF(KvmContext, " Q%i: %s\n", i, getAndFormatOneReg(kvmFPReg(i))); getOneReg(kvmFPReg(i), reg.data); for (int j = 0; j < FP_REGS_PER_VFP_REG; j++) tc->setFloatRegBits(reg_base + j, reg.s[j].i); } for (const auto &ri : getSysRegMap()) { const auto value(getOneRegU64(ri.kvm)); DPRINTF(KvmContext, " %s := 0x%x\n", ri.name, value); tc->setMiscRegNoEffect(ri.idx, value); } PCState pc(getOneRegU64(INT_REG(regs.pc))); pc.aarch64(inAArch64(tc)); pc.thumb(cpsr.t); pc.nextAArch64(inAArch64(tc)); // TODO: This is a massive assumption that will break when // switching to thumb. pc.nextThumb(cpsr.t); DPRINTF(KvmContext, " PC := 0x%x (t: %i, a64: %i)\n", pc.instAddr(), pc.thumb(), pc.aarch64()); tc->pcState(pc); } const std::vector & ArmV8KvmCPU::getSysRegMap() const { // Try to use the cached map if (!sysRegMap.empty()) return sysRegMap; for (const auto ® : getRegList()) { const uint64_t arch(reg & KVM_REG_ARCH_MASK); if (arch != KVM_REG_ARM64) continue; const uint64_t type(reg & KVM_REG_ARM_COPROC_MASK); if (type != KVM_REG_ARM64_SYSREG) continue; const uint64_t op0(EXTRACT_FIELD(reg, KVM_REG_ARM64_SYSREG_OP0)); const uint64_t op1(EXTRACT_FIELD(reg, KVM_REG_ARM64_SYSREG_OP1)); const uint64_t crn(EXTRACT_FIELD(reg, KVM_REG_ARM64_SYSREG_CRN)); const uint64_t crm(EXTRACT_FIELD(reg, KVM_REG_ARM64_SYSREG_CRM)); const uint64_t op2(EXTRACT_FIELD(reg, KVM_REG_ARM64_SYSREG_OP2)); const MiscRegIndex idx(decodeAArch64SysReg(op0, op1, crn, crm, op2)); const auto &info(miscRegInfo[idx]); const bool writeable( info[MISCREG_USR_NS_WR] || info[MISCREG_USR_S_WR] || info[MISCREG_PRI_S_WR] || info[MISCREG_PRI_NS_WR] || info[MISCREG_HYP_WR] || info[MISCREG_MON_NS0_WR] || info[MISCREG_MON_NS1_WR]); const bool implemented( info[MISCREG_IMPLEMENTED] || info[MISCREG_WARN_NOT_FAIL]); // Only add implemented registers that we are going to be able // to write. if (implemented && writeable) sysRegMap.emplace_back(reg, idx, miscRegName[idx]); } return sysRegMap; } ArmV8KvmCPU * ArmV8KvmCPUParams::create() { return new ArmV8KvmCPU(this); }