1/*
2 * Copyright (c) 2017-2018 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: Giacomo Travaglini
38 */
39
40#include "arch/arm/tracers/tarmac_record.hh"
41
42#include "arch/arm/insts/static_inst.hh"
43#include "tarmac_tracer.hh"
44
45namespace Trace {
46
47// TARMAC Instruction Record static variables
48uint64_t TarmacTracerRecord::TraceInstEntry::instCount = 0;
49
50std::string
51iSetStateToStr(TarmacBaseRecord::ISetState isetstate)
52{
53    switch (isetstate) {
54      case TarmacBaseRecord::ISET_ARM:
55        return "A";
56      case TarmacBaseRecord::ISET_THUMB:
57        return "T";
58      case TarmacBaseRecord::ISET_A64:
59        return "O";
60      default:
61        return "Unsupported";
62    }
63}
64
65std::string
66opModeToStr(OperatingMode opMode)
67{
68    switch (opMode) {
69      case MODE_EL0T:
70        return "EL0t";
71      case MODE_EL1T:
72        return "EL1t";
73      case MODE_EL1H:
74        return "EL1h";
75      case MODE_EL2T:
76        return "EL2t";
77      case MODE_EL2H:
78        return "EL2h";
79      case MODE_EL3T:
80        return "EL3t";
81      case MODE_EL3H:
82        return "EL3h";
83      case MODE_USER:
84        return "usr";
85      case MODE_FIQ:
86        return "fiq";
87      case MODE_IRQ:
88        return "irq";
89      case MODE_SVC:
90        return "svc";
91      case MODE_MON:
92        return "mon";
93      case MODE_ABORT:
94        return "abt";
95      case MODE_HYP:
96        return "hyp";
97      case MODE_UNDEFINED:
98        return "und";
99      case MODE_SYSTEM:
100        return "sys";
101      default:
102        return "Unsupported";
103    }
104}
105
106// TarmacTracerRecord ctor
107TarmacTracerRecord::TarmacTracerRecord(Tick _when, ThreadContext *_thread,
108                                     const StaticInstPtr _staticInst,
109                                     PCState _pc,
110                                     TarmacTracer& _tracer,
111                                     const StaticInstPtr _macroStaticInst)
112    : TarmacBaseRecord(_when, _thread, _staticInst,
113                       _pc,  _macroStaticInst),
114      tracer(_tracer)
115{
116}
117
118TarmacTracerRecord::TraceInstEntry::TraceInstEntry(
119    const TarmacContext& tarmCtx,
120    bool predicate)
121      : InstEntry(tarmCtx.thread, tarmCtx.pc, tarmCtx.staticInst, predicate)
122{
123    secureMode = inSecureState(tarmCtx.thread);
124
125    auto arm_inst = static_cast<const ArmStaticInst*>(
126        tarmCtx.staticInst.get()
127    );
128
129    // Get the instruction size as a number of bits:
130    // (multiply byte size by 8)
131    instSize = (arm_inst->instSize() << 3);
132
133    // Mask the opcode using the instruction size: the
134    // opcode field will otherwise be 32 bit wide even
135    // for 16bit (Thumb) instruction.
136    opcode = arm_inst->encoding();
137
138    // Update the instruction count: number of executed
139    // instructions.
140    instCount++;
141}
142
143TarmacTracerRecord::TraceMemEntry::TraceMemEntry(
144    const TarmacContext& tarmCtx,
145    uint8_t _size, Addr _addr, uint64_t _data)
146      :  MemEntry(_size, _addr, _data),
147         loadAccess(tarmCtx.staticInst->isLoad())
148{
149}
150
151TarmacTracerRecord::TraceRegEntry::TraceRegEntry(
152    const TarmacContext& tarmCtx,
153    const RegId& reg)
154      : RegEntry(tarmCtx.pc),
155        regValid(false),
156        regClass(reg.classValue()),
157        regRel(reg.index())
158{
159}
160
161void
162TarmacTracerRecord::TraceRegEntry::update(
163    const TarmacContext& tarmCtx
164)
165{
166    // Fill the register entry data, according to register
167    // class.
168    switch (regClass) {
169      case CCRegClass:
170        updateCC(tarmCtx, regRel);
171        break;
172      case FloatRegClass:
173        updateFloat(tarmCtx, regRel);
174        break;
175      case IntRegClass:
176        updateInt(tarmCtx, regRel);
177        break;
178      case MiscRegClass:
179        updateMisc(tarmCtx, regRel);
180        break;
181      default:
182        // If unsupported format, do nothing: non updating
183        // the register will prevent it to be printed.
184        break;
185    }
186}
187
188void
189TarmacTracerRecord::TraceRegEntry::updateMisc(
190    const TarmacContext& tarmCtx,
191    RegIndex regRelIdx
192)
193{
194    auto thread = tarmCtx.thread;
195
196    regValid = true;
197    regName = miscRegName[regRelIdx];
198    valueLo = thread->readMiscRegNoEffect(regRelIdx);
199
200    // If it is the CPSR:
201    // update the value of the CPSR register and add
202    // the CC flags on top of the value
203    if (regRelIdx == MISCREG_CPSR) {
204        CPSR cpsr = thread->readMiscRegNoEffect(MISCREG_CPSR);
205        cpsr.nz = thread->readCCReg(CCREG_NZ);
206        cpsr.c = thread->readCCReg(CCREG_C);
207        cpsr.v = thread->readCCReg(CCREG_V);
208        cpsr.ge = thread->readCCReg(CCREG_GE);
209
210        // update the entry value
211        valueLo = cpsr;
212    }
213}
214
215void
216TarmacTracerRecord::TraceRegEntry::updateCC(
217    const TarmacContext& tarmCtx,
218    RegIndex regRelIdx
219)
220{
221    auto thread = tarmCtx.thread;
222
223    regValid = true;
224    regName = ccRegName[regRelIdx];
225    valueLo = thread->readCCReg(regRelIdx);
226}
227
228void
229TarmacTracerRecord::TraceRegEntry::updateFloat(
230    const TarmacContext& tarmCtx,
231    RegIndex regRelIdx
232)
233{
234    auto thread = tarmCtx.thread;
235
236    regValid = true;
237    regName  = "f" + std::to_string(regRelIdx);
238    valueLo = bitsToFloat32(thread->readFloatReg(regRelIdx));
239}
240
241void
242TarmacTracerRecord::TraceRegEntry::updateInt(
243    const TarmacContext& tarmCtx,
244    RegIndex regRelIdx
245)
246{
247    auto thread = tarmCtx.thread;
248
249    // Reading operating mode from CPSR.
250    // This is needed when printing the register name in case
251    // of banked register (e.g. lr_svc)
252    CPSR cpsr = thread->readMiscRegNoEffect(MISCREG_CPSR);
253    OperatingMode mode = (OperatingMode)(uint8_t)cpsr.mode;
254
255    std::string reg_suffix;
256    if (mode != MODE_USER) {
257        reg_suffix = "_"  + opModeToStr(mode);
258    }
259
260    regValid = true;
261    switch (regRelIdx) {
262      case PCReg:
263        regName = "pc";
264        break;
265      case StackPointerReg:
266        regName = "sp" + reg_suffix ;
267        break;
268      case FramePointerReg:
269        regName = "fp" + reg_suffix;
270        break;
271      case ReturnAddressReg:
272        regName = "lr" + reg_suffix;
273        break;
274      default:
275        regName  = "r" + std::to_string(regRelIdx);
276        break;
277    }
278    valueLo = thread->readIntReg(regRelIdx);
279}
280
281void
282TarmacTracerRecord::addInstEntry(std::vector<InstPtr>& queue,
283                                 const TarmacContext& tarmCtx)
284{
285    // Generate an instruction entry in the record and
286    // add it to the Instruction Queue
287    queue.push_back(
288        m5::make_unique<TraceInstEntry>(tarmCtx, predicate)
289    );
290}
291
292void
293TarmacTracerRecord::addMemEntry(std::vector<MemPtr>& queue,
294                                const TarmacContext& tarmCtx)
295{
296    // Generate a memory entry in the record if the record
297    // implies a valid memory access, and add it to the
298    // Memory Queue
299    if (getMemValid()) {
300        queue.push_back(
301            m5::make_unique<TraceMemEntry>(tarmCtx,
302                                           static_cast<uint8_t>(getSize()),
303                                           getAddr(), getIntData())
304        );
305    }
306}
307
308void
309TarmacTracerRecord::addRegEntry(std::vector<RegPtr>& queue,
310                                const TarmacContext& tarmCtx)
311{
312    // Generate an entry for every ARM register being
313    // written by the current instruction
314    for (auto reg = 0; reg < staticInst->numDestRegs(); ++reg) {
315
316        RegId reg_id = staticInst->destRegIdx(reg);
317
318        // Creating a single register change entry
319        auto single_reg = genRegister<TraceRegEntry>(tarmCtx, reg_id);
320
321        // Copying the entry and adding it to the "list"
322        // of entries to be dumped to trace.
323        queue.push_back(
324            m5::make_unique<TraceRegEntry>(single_reg)
325        );
326    }
327
328    // Gem5 is treating CPSR flags as separate registers (CC registers),
329    // in contrast with Tarmac specification: we need to merge the gem5 CC
330    // entries altogether with the CPSR register and produce a single entry.
331    mergeCCEntry<TraceRegEntry>(queue, tarmCtx);
332}
333
334void
335TarmacTracerRecord::dump()
336{
337    // Generate and print all the record's entries.
338    auto &instQueue = tracer.instQueue;
339    auto &memQueue = tracer.memQueue;
340    auto &regQueue = tracer.regQueue;
341
342    const TarmacContext tarmCtx(
343        thread,
344        staticInst->isMicroop()? macroStaticInst : staticInst,
345        pc
346    );
347
348    if (!staticInst->isMicroop()) {
349        // Current instruction is NOT a micro-instruction:
350        // Generate Tarmac entries and dump them immediately
351
352        // Generate Tarmac entries and add them to the respective
353        // queues.
354        addInstEntry(instQueue, tarmCtx);
355        addMemEntry(memQueue, tarmCtx);
356        addRegEntry(regQueue, tarmCtx);
357
358        // Flush (print) any queued entry.
359        flushQueues(instQueue, memQueue, regQueue);
360
361    } else {
362        // Current instruction is a micro-instruction:
363        // save micro entries into dedicated queues and flush them
364        // into the tracefile only when the MACRO-instruction
365        // has completed.
366
367        if (staticInst->isFirstMicroop()) {
368            addInstEntry(instQueue, tarmCtx);
369        }
370
371        addRegEntry(regQueue, tarmCtx);
372        addMemEntry(memQueue, tarmCtx);
373
374        if (staticInst->isLastMicroop()) {
375            // Flush (print) any queued entry.
376            flushQueues(instQueue, memQueue, regQueue);
377        }
378    }
379}
380
381template<typename Queue>
382void
383TarmacTracerRecord::flushQueues(Queue& queue)
384{
385    std::ostream &outs = Trace::output();
386
387    for (const auto &single_entry : queue) {
388        single_entry->print(outs);
389    }
390
391    queue.clear();
392}
393
394template<typename Queue, typename... Args>
395void
396TarmacTracerRecord::flushQueues(Queue& queue, Args & ... args)
397{
398    flushQueues(queue);
399    flushQueues(args...);
400}
401
402void
403TarmacTracerRecord::TraceInstEntry::print(
404    std::ostream& outs,
405    int verbosity,
406    const std::string &prefix) const
407{
408    // Pad the opcode
409    std::string opcode_str = csprintf("%0*x", instSize >> 2, opcode);
410
411    // Print the instruction record formatted according
412    // to the Tarmac specification
413    ccprintf(outs, "%s clk %s (%u) %08x %s %s %s_%s : %s\n",
414             curTick(),                   /* Tick time */
415             taken? "IT" : "IS",          /* Instruction taken/skipped */
416             instCount,                   /* Instruction count */
417             addr,                        /* Instruction address */
418             opcode_str,                  /* Instruction opcode */
419             iSetStateToStr(isetstate),   /* Instruction Set */
420             opModeToStr(mode),           /* Exception level */
421             secureMode? "s" : "ns",      /* Security */
422             disassemble);                /* Instruction disass */
423}
424
425void
426TarmacTracerRecord::TraceMemEntry::print(
427    std::ostream& outs,
428    int verbosity,
429    const std::string &prefix) const
430{
431    // Print the memory record formatted according
432    // to the Tarmac specification
433    ccprintf(outs, "%s clk M%s%d %08x %0*x\n",
434             curTick(),                 /* Tick time */
435             loadAccess? "R" : "W",     /* Access type */
436             size,                      /* Access size */
437             addr,                      /* Memory address */
438             size*2,                    /* Padding with access size */
439             data);                     /* Memory data */
440}
441
442void
443TarmacTracerRecord::TraceRegEntry::print(
444    std::ostream& outs,
445    int verbosity,
446    const std::string &prefix) const
447{
448    // Print the register record formatted according
449    // to the Tarmac specification
450    if (regValid)
451        ccprintf(outs, "%s clk R %s %08x\n",
452                 curTick(),                 /* Tick time */
453                 regName,                   /* Register name */
454                 valueLo);                  /* Register value */
455}
456
457} // namespace Trace
458