armv8_cpu.cc revision 11891:5886cd7ec57b
1/*
2 * Copyright (c) 2015 ARM Limited
3 * All rights reserved
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder.  You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions are
16 * met: redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer;
18 * redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution;
21 * neither the name of the copyright holders nor the names of its
22 * contributors may be used to endorse or promote products derived from
23 * this software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 *
37 * Authors: Andreas Sandberg
38 */
39
40#include "arch/arm/kvm/armv8_cpu.hh"
41
42#include <linux/kvm.h>
43
44#include "debug/KvmContext.hh"
45#include "params/ArmV8KvmCPU.hh"
46
47// Unlike gem5, kvm doesn't count the SP as a normal integer register,
48// which means we only have 31 normal integer registers.
49constexpr static unsigned NUM_XREGS = NUM_ARCH_INTREGS - 1;
50static_assert(NUM_XREGS == 31, "Unexpected number of aarch64 int. regs.");
51
52// The KVM interface accesses vector registers of 4 single precision
53// floats instead of individual registers.
54constexpr static unsigned NUM_QREGS = NumFloatV8ArchRegs / 4;
55static_assert(NUM_QREGS == 32, "Unexpected number of aarch64 vector regs.");
56
57#define EXTRACT_FIELD(v, name) \
58    (((v) & name ## _MASK) >> name ## _SHIFT)
59
60#define CORE_REG(name, size)                               \
61    (KVM_REG_ARM64 | KVM_REG_ARM_CORE |                    \
62     KVM_REG_SIZE_ ## size |                               \
63     KVM_REG_ARM_CORE_REG(name))
64
65#define INT_REG(name) CORE_REG(name, U64)
66#define SIMD_REG(name) CORE_REG(name, U128)
67
68constexpr uint64_t
69kvmXReg(const int num)
70{
71    return INT_REG(regs.regs[0]) +
72        (INT_REG(regs.regs[1]) - INT_REG(regs.regs[0])) * num;
73}
74
75constexpr uint64_t
76kvmFPReg(const int num)
77{
78    return SIMD_REG(fp_regs.vregs[0]) +
79        (SIMD_REG(fp_regs.vregs[1]) - SIMD_REG(fp_regs.vregs[0])) * num;
80}
81
82union KvmFPReg {
83    union {
84        uint32_t i;
85        float f;
86    } s[4];
87
88    union {
89        uint64_t i;
90        double f;
91    } d[2];
92
93    uint8_t data[32];
94};
95
96#define FP_REGS_PER_VFP_REG 4
97static_assert(sizeof(FloatRegBits) == 4, "Unexpected float reg size");
98
99const std::vector<ArmV8KvmCPU::IntRegInfo> ArmV8KvmCPU::intRegMap = {
100    { INT_REG(regs.sp), INTREG_SP0, "SP(EL0)" },
101    { INT_REG(sp_el1), INTREG_SP1, "SP(EL1)" },
102};
103
104const std::vector<ArmV8KvmCPU::MiscRegInfo> ArmV8KvmCPU::miscRegMap = {
105    MiscRegInfo(INT_REG(elr_el1), MISCREG_ELR_EL1, "ELR(EL1)"),
106    MiscRegInfo(INT_REG(spsr[KVM_SPSR_EL1]), MISCREG_SPSR_EL1, "SPSR(EL1)"),
107    MiscRegInfo(INT_REG(spsr[KVM_SPSR_ABT]), MISCREG_SPSR_ABT, "SPSR(ABT)"),
108    MiscRegInfo(INT_REG(spsr[KVM_SPSR_UND]), MISCREG_SPSR_UND, "SPSR(UND)"),
109    MiscRegInfo(INT_REG(spsr[KVM_SPSR_IRQ]), MISCREG_SPSR_IRQ, "SPSR(IRQ)"),
110    MiscRegInfo(INT_REG(spsr[KVM_SPSR_FIQ]), MISCREG_SPSR_FIQ, "SPSR(FIQ)"),
111    MiscRegInfo(INT_REG(fp_regs.fpsr), MISCREG_FPSR, "FPSR"),
112    MiscRegInfo(INT_REG(fp_regs.fpcr), MISCREG_FPCR, "FPCR"),
113};
114
115ArmV8KvmCPU::ArmV8KvmCPU(ArmV8KvmCPUParams *params)
116    : BaseArmKvmCPU(params)
117{
118}
119
120ArmV8KvmCPU::~ArmV8KvmCPU()
121{
122}
123
124void
125ArmV8KvmCPU::dump() const
126{
127    inform("Integer registers:\n");
128    inform("  PC: %s\n", getAndFormatOneReg(INT_REG(regs.pc)));
129    for (int i = 0; i < NUM_XREGS; ++i)
130        inform("  X%i: %s\n", i, getAndFormatOneReg(kvmXReg(i)));
131
132    for (int i = 0; i < NUM_QREGS; ++i)
133        inform("  Q%i: %s\n", i, getAndFormatOneReg(kvmFPReg(i)));
134
135    for (const auto &ri : intRegMap)
136        inform("  %s: %s\n", ri.name, getAndFormatOneReg(ri.kvm));
137
138    inform("  %s: %s\n", "PSTATE", getAndFormatOneReg(INT_REG(regs.pstate)));
139
140    for (const auto &ri : miscRegMap)
141        inform("  %s: %s\n", ri.name, getAndFormatOneReg(ri.kvm));
142
143    for (const auto &reg : getRegList()) {
144        const uint64_t arch(reg & KVM_REG_ARCH_MASK);
145        if (arch != KVM_REG_ARM64) {
146            inform("0x%x: %s\n", reg, getAndFormatOneReg(reg));
147            continue;
148        }
149
150        const uint64_t type(reg & KVM_REG_ARM_COPROC_MASK);
151        switch (type) {
152          case KVM_REG_ARM_CORE:
153            // These have already been printed
154            break;
155
156          case KVM_REG_ARM64_SYSREG: {
157              const uint64_t op0(EXTRACT_FIELD(reg, KVM_REG_ARM64_SYSREG_OP0));
158              const uint64_t op1(EXTRACT_FIELD(reg, KVM_REG_ARM64_SYSREG_OP1));
159              const uint64_t crn(EXTRACT_FIELD(reg, KVM_REG_ARM64_SYSREG_CRN));
160              const uint64_t crm(EXTRACT_FIELD(reg, KVM_REG_ARM64_SYSREG_CRM));
161              const uint64_t op2(EXTRACT_FIELD(reg, KVM_REG_ARM64_SYSREG_OP2));
162              const MiscRegIndex idx(
163                  decodeAArch64SysReg(op0, op1, crn, crm, op2));
164
165              inform("  %s (op0: %i, op1: %i, crn: %i, crm: %i, op2: %i): %s",
166                     miscRegName[idx], op0, op1, crn, crm, op2,
167                     getAndFormatOneReg(reg));
168          } break;
169
170          case KVM_REG_ARM_DEMUX: {
171              const uint64_t id(EXTRACT_FIELD(reg, KVM_REG_ARM_DEMUX_ID));
172              const uint64_t val(EXTRACT_FIELD(reg, KVM_REG_ARM_DEMUX_VAL));
173              if (id == KVM_REG_ARM_DEMUX_ID_CCSIDR) {
174                  inform("  CSSIDR[%i]: %s\n", val,
175                         getAndFormatOneReg(reg));
176              } else {
177                  inform("  UNKNOWN[%i:%i]: %s\n", id, val,
178                         getAndFormatOneReg(reg));
179              }
180          } break;
181
182          default:
183            inform("0x%x: %s\n", reg, getAndFormatOneReg(reg));
184        }
185    }
186}
187
188void
189ArmV8KvmCPU::updateKvmState()
190{
191    DPRINTF(KvmContext, "In updateKvmState():\n");
192
193    // update pstate register state
194    CPSR cpsr(tc->readMiscReg(MISCREG_CPSR));
195    cpsr.nz = tc->readCCReg(CCREG_NZ);
196    cpsr.c = tc->readCCReg(CCREG_C);
197    cpsr.v = tc->readCCReg(CCREG_V);
198    if (cpsr.width) {
199        cpsr.ge = tc->readCCReg(CCREG_GE);
200    } else {
201        cpsr.ge = 0;
202    }
203    DPRINTF(KvmContext, "  %s := 0x%x\n", "PSTATE", cpsr);
204    setOneReg(INT_REG(regs.pstate), cpsr);
205
206    for (const auto &ri : miscRegMap) {
207        const uint64_t value(tc->readMiscReg(ri.idx));
208        DPRINTF(KvmContext, "  %s := 0x%x\n", ri.name, value);
209        setOneReg(ri.kvm, value);
210    }
211
212    for (int i = 0; i < NUM_XREGS; ++i) {
213        const uint64_t value(tc->readIntReg(INTREG_X0 + i));
214        DPRINTF(KvmContext, "  X%i := 0x%x\n", i, value);
215        setOneReg(kvmXReg(i), value);
216    }
217
218    for (const auto &ri : intRegMap) {
219        const uint64_t value(tc->readIntReg(ri.idx));
220        DPRINTF(KvmContext, "  %s := 0x%x\n", ri.name, value);
221        setOneReg(ri.kvm, value);
222    }
223
224    for (int i = 0; i < NUM_QREGS; ++i) {
225        const RegIndex reg_base(i * FP_REGS_PER_VFP_REG);
226        KvmFPReg reg;
227        for (int j = 0; j < FP_REGS_PER_VFP_REG; j++)
228            reg.s[j].i = tc->readFloatRegBits(reg_base + j);
229
230        setOneReg(kvmFPReg(i), reg.data);
231        DPRINTF(KvmContext, "  Q%i: %s\n", i, getAndFormatOneReg(kvmFPReg(i)));
232    }
233
234    for (const auto &ri : getSysRegMap()) {
235        const uint64_t value(tc->readMiscReg(ri.idx));
236        DPRINTF(KvmContext, "  %s := 0x%x\n", ri.name, value);
237        setOneReg(ri.kvm, value);
238    }
239
240    setOneReg(INT_REG(regs.pc), tc->instAddr());
241    DPRINTF(KvmContext, "  PC := 0x%x\n", tc->instAddr());
242}
243
244void
245ArmV8KvmCPU::updateThreadContext()
246{
247    DPRINTF(KvmContext, "In updateThreadContext():\n");
248
249    // Update pstate thread context
250    const CPSR cpsr(tc->readMiscRegNoEffect(MISCREG_CPSR));
251    DPRINTF(KvmContext, "  %s := 0x%x\n", "PSTATE", cpsr);
252    tc->setMiscRegNoEffect(MISCREG_CPSR, cpsr);
253    tc->setCCReg(CCREG_NZ, cpsr.nz);
254    tc->setCCReg(CCREG_C, cpsr.c);
255    tc->setCCReg(CCREG_V, cpsr.v);
256    if (cpsr.width) {
257        tc->setCCReg(CCREG_GE, cpsr.ge);
258    }
259
260    // Update core misc regs first as they
261    // affect how other registers are mapped.
262    for (const auto &ri : miscRegMap) {
263        const auto value(getOneRegU64(ri.kvm));
264        DPRINTF(KvmContext, "  %s := 0x%x\n", ri.name, value);
265        tc->setMiscRegNoEffect(ri.idx, value);
266    }
267
268    for (int i = 0; i < NUM_XREGS; ++i) {
269        const auto value(getOneRegU64(kvmXReg(i)));
270        DPRINTF(KvmContext, "  X%i := 0x%x\n", i, value);
271        // KVM64 returns registers in 64-bit layout. If we are in aarch32
272        // mode, we need to map these to banked ARM32 registers.
273        if (inAArch64(tc)) {
274            tc->setIntReg(INTREG_X0 + i, value);
275        } else {
276            tc->setIntRegFlat(IntReg64Map[INTREG_X0 + i], value);
277        }
278    }
279
280    for (const auto &ri : intRegMap) {
281        const auto value(getOneRegU64(ri.kvm));
282        DPRINTF(KvmContext, "  %s := 0x%x\n", ri.name, value);
283        tc->setIntReg(ri.idx, value);
284    }
285
286    for (int i = 0; i < NUM_QREGS; ++i) {
287        const RegIndex reg_base(i * FP_REGS_PER_VFP_REG);
288        KvmFPReg reg;
289        DPRINTF(KvmContext, "  Q%i: %s\n", i, getAndFormatOneReg(kvmFPReg(i)));
290        getOneReg(kvmFPReg(i), reg.data);
291        for (int j = 0; j < FP_REGS_PER_VFP_REG; j++)
292            tc->setFloatRegBits(reg_base + j, reg.s[j].i);
293    }
294
295    for (const auto &ri : getSysRegMap()) {
296        const auto value(getOneRegU64(ri.kvm));
297        DPRINTF(KvmContext, "  %s := 0x%x\n", ri.name, value);
298        tc->setMiscRegNoEffect(ri.idx, value);
299    }
300
301    PCState pc(getOneRegU64(INT_REG(regs.pc)));
302    pc.aarch64(inAArch64(tc));
303    pc.thumb(cpsr.t);
304    pc.nextAArch64(inAArch64(tc));
305    // TODO: This is a massive assumption that will break when
306    // switching to thumb.
307    pc.nextThumb(cpsr.t);
308    DPRINTF(KvmContext, "  PC := 0x%x (t: %i, a64: %i)\n",
309            pc.instAddr(), pc.thumb(), pc.aarch64());
310    tc->pcState(pc);
311}
312
313const std::vector<ArmV8KvmCPU::MiscRegInfo> &
314ArmV8KvmCPU::getSysRegMap() const
315{
316    // Try to use the cached map
317    if (!sysRegMap.empty())
318        return sysRegMap;
319
320    for (const auto &reg : getRegList()) {
321        const uint64_t arch(reg & KVM_REG_ARCH_MASK);
322        if (arch != KVM_REG_ARM64)
323            continue;
324
325        const uint64_t type(reg & KVM_REG_ARM_COPROC_MASK);
326        if (type != KVM_REG_ARM64_SYSREG)
327            continue;
328
329        const uint64_t op0(EXTRACT_FIELD(reg, KVM_REG_ARM64_SYSREG_OP0));
330        const uint64_t op1(EXTRACT_FIELD(reg, KVM_REG_ARM64_SYSREG_OP1));
331        const uint64_t crn(EXTRACT_FIELD(reg, KVM_REG_ARM64_SYSREG_CRN));
332        const uint64_t crm(EXTRACT_FIELD(reg, KVM_REG_ARM64_SYSREG_CRM));
333        const uint64_t op2(EXTRACT_FIELD(reg, KVM_REG_ARM64_SYSREG_OP2));
334        const MiscRegIndex idx(decodeAArch64SysReg(op0, op1, crn, crm, op2));
335        const auto &info(miscRegInfo[idx]);
336        const bool writeable(
337            info[MISCREG_USR_NS_WR] || info[MISCREG_USR_S_WR] ||
338            info[MISCREG_PRI_S_WR] || info[MISCREG_PRI_NS_WR] ||
339            info[MISCREG_HYP_WR] ||
340            info[MISCREG_MON_NS0_WR] || info[MISCREG_MON_NS1_WR]);
341        const bool implemented(
342            info[MISCREG_IMPLEMENTED] || info[MISCREG_WARN_NOT_FAIL]);
343
344        // Only add implemented registers that we are going to be able
345        // to write.
346        if (implemented && writeable)
347            sysRegMap.emplace_back(reg, idx, miscRegName[idx]);
348    }
349
350    return sysRegMap;
351}
352
353ArmV8KvmCPU *
354ArmV8KvmCPUParams::create()
355{
356    return new ArmV8KvmCPU(this);
357}
358