pseudo_inst.cc revision 9659
1/*
2 * Copyright (c) 2010-2012 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 * Copyright (c) 2011 Advanced Micro Devices, Inc.
15 * Copyright (c) 2003-2006 The Regents of The University of Michigan
16 * All rights reserved.
17 *
18 * Redistribution and use in source and binary forms, with or without
19 * modification, are permitted provided that the following conditions are
20 * met: redistributions of source code must retain the above copyright
21 * notice, this list of conditions and the following disclaimer;
22 * redistributions in binary form must reproduce the above copyright
23 * notice, this list of conditions and the following disclaimer in the
24 * documentation and/or other materials provided with the distribution;
25 * neither the name of the copyright holders nor the names of its
26 * contributors may be used to endorse or promote products derived from
27 * this software without specific prior written permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40 *
41 * Authors: Nathan Binkert
42 */
43
44#include <fcntl.h>
45#include <unistd.h>
46
47#include <cerrno>
48#include <fstream>
49#include <string>
50#include <vector>
51
52#include "arch/kernel_stats.hh"
53#include "arch/utility.hh"
54#include "arch/vtophys.hh"
55#include "base/debug.hh"
56#include "base/output.hh"
57#include "config/the_isa.hh"
58#include "cpu/base.hh"
59#include "cpu/quiesce_event.hh"
60#include "cpu/thread_context.hh"
61#include "debug/Loader.hh"
62#include "debug/Quiesce.hh"
63#include "debug/WorkItems.hh"
64#include "params/BaseCPU.hh"
65#include "sim/full_system.hh"
66#include "sim/pseudo_inst.hh"
67#include "sim/serialize.hh"
68#include "sim/sim_events.hh"
69#include "sim/sim_exit.hh"
70#include "sim/stat_control.hh"
71#include "sim/stats.hh"
72#include "sim/system.hh"
73#include "sim/vptr.hh"
74
75using namespace std;
76
77using namespace Stats;
78using namespace TheISA;
79
80namespace PseudoInst {
81
82static inline void
83panicFsOnlyPseudoInst(const char *name)
84{
85    panic("Pseudo inst \"%s\" is only available in Full System mode.");
86}
87
88uint64_t
89pseudoInst(ThreadContext *tc, uint8_t func, uint8_t subfunc)
90{
91    uint64_t args[4];
92
93    // We need to do this in a slightly convoluted way since
94    // getArgument() might have side-effects on arg_num. We could have
95    // used the Argument class, but due to the possible side effects
96    // from getArgument, it'd most likely break.
97    int arg_num(0);
98    for (int i = 0; i < sizeof(args) / sizeof(*args); ++i)
99        args[arg_num++] = getArgument(tc, arg_num, sizeof(uint64_t), false);
100
101    switch (func) {
102      case 0x00: // arm_func
103        arm(tc);
104        break;
105
106      case 0x01: // quiesce_func
107        quiesce(tc);
108        break;
109
110      case 0x02: // quiescens_func
111        quiesceSkip(tc);
112        break;
113
114      case 0x03: // quiescecycle_func
115        quiesceNs(tc, args[0]);
116        break;
117
118      case 0x04: // quiescetime_func
119        return quiesceTime(tc);
120
121      case 0x07: // rpns_func
122        return rpns(tc);
123
124      case 0x09: // wakecpu_func
125        wakeCPU(tc, args[0]);
126        break;
127
128      case 0x21: // exit_func
129        m5exit(tc, args[0]);
130        break;
131
132      case 0x30: // initparam_func
133        return initParam(tc);
134
135      case 0x31: // loadsymbol_func
136        loadsymbol(tc);
137        break;
138
139      case 0x40: // resetstats_func
140        resetstats(tc, args[0], args[1]);
141        break;
142
143      case 0x41: // dumpstats_func
144        dumpstats(tc, args[0], args[1]);
145        break;
146
147      case 0x42: // dumprststats_func
148        dumpresetstats(tc, args[0], args[1]);
149        break;
150
151      case 0x43: // ckpt_func
152        m5checkpoint(tc, args[0], args[1]);
153        break;
154
155      case 0x4f: // writefile_func
156        return writefile(tc, args[0], args[1], args[2], args[3]);
157
158      case 0x50: // readfile_func
159        return readfile(tc, args[0], args[1], args[2]);
160
161      case 0x51: // debugbreak_func
162        debugbreak(tc);
163        break;
164
165      case 0x52: // switchcpu_func
166        switchcpu(tc);
167        break;
168
169      case 0x53: // addsymbol_func
170        addsymbol(tc, args[0], args[1]);
171        break;
172
173      case 0x54: // panic_func
174        panic("M5 panic instruction called at %s\n", tc->pcState());
175
176      case 0x5a: // work_begin_func
177        workbegin(tc, args[0], args[1]);
178        break;
179
180      case 0x5b: // work_end_func
181        workend(tc, args[0], args[1]);
182        break;
183
184      case 0x55: // annotate_func
185      case 0x56: // reserved2_func
186      case 0x57: // reserved3_func
187      case 0x58: // reserved4_func
188      case 0x59: // reserved5_func
189        warn("Unimplemented m5 op (0x%x)\n", func);
190        break;
191
192      default:
193        warn("Unhandled m5 op: 0x%x\n", func);
194        break;
195    }
196
197    return 0;
198}
199
200void
201arm(ThreadContext *tc)
202{
203    if (!FullSystem)
204        panicFsOnlyPseudoInst("arm");
205
206    if (tc->getKernelStats())
207        tc->getKernelStats()->arm();
208}
209
210void
211quiesce(ThreadContext *tc)
212{
213    if (!FullSystem)
214        panicFsOnlyPseudoInst("quiesce");
215
216    if (!tc->getCpuPtr()->params()->do_quiesce)
217        return;
218
219    DPRINTF(Quiesce, "%s: quiesce()\n", tc->getCpuPtr()->name());
220
221    tc->suspend();
222    if (tc->getKernelStats())
223        tc->getKernelStats()->quiesce();
224}
225
226void
227quiesceSkip(ThreadContext *tc)
228{
229    if (!FullSystem)
230        panicFsOnlyPseudoInst("quiesceSkip");
231
232    BaseCPU *cpu = tc->getCpuPtr();
233
234    if (!cpu->params()->do_quiesce)
235        return;
236
237    EndQuiesceEvent *quiesceEvent = tc->getQuiesceEvent();
238
239    Tick resume = curTick() + 1;
240
241    cpu->reschedule(quiesceEvent, resume, true);
242
243    DPRINTF(Quiesce, "%s: quiesceSkip() until %d\n",
244            cpu->name(), resume);
245
246    tc->suspend();
247    if (tc->getKernelStats())
248        tc->getKernelStats()->quiesce();
249}
250
251void
252quiesceNs(ThreadContext *tc, uint64_t ns)
253{
254    if (!FullSystem)
255        panicFsOnlyPseudoInst("quiesceNs");
256
257    BaseCPU *cpu = tc->getCpuPtr();
258
259    if (!cpu->params()->do_quiesce || ns == 0)
260        return;
261
262    EndQuiesceEvent *quiesceEvent = tc->getQuiesceEvent();
263
264    Tick resume = curTick() + SimClock::Int::ns * ns;
265
266    cpu->reschedule(quiesceEvent, resume, true);
267
268    DPRINTF(Quiesce, "%s: quiesceNs(%d) until %d\n",
269            cpu->name(), ns, resume);
270
271    tc->suspend();
272    if (tc->getKernelStats())
273        tc->getKernelStats()->quiesce();
274}
275
276void
277quiesceCycles(ThreadContext *tc, uint64_t cycles)
278{
279    if (!FullSystem)
280        panicFsOnlyPseudoInst("quiesceCycles");
281
282    BaseCPU *cpu = tc->getCpuPtr();
283
284    if (!cpu->params()->do_quiesce || cycles == 0)
285        return;
286
287    EndQuiesceEvent *quiesceEvent = tc->getQuiesceEvent();
288
289    Tick resume = cpu->clockEdge(Cycles(cycles));
290
291    cpu->reschedule(quiesceEvent, resume, true);
292
293    DPRINTF(Quiesce, "%s: quiesceCycles(%d) until %d\n",
294            cpu->name(), cycles, resume);
295
296    tc->suspend();
297    if (tc->getKernelStats())
298        tc->getKernelStats()->quiesce();
299}
300
301uint64_t
302quiesceTime(ThreadContext *tc)
303{
304    if (!FullSystem) {
305        panicFsOnlyPseudoInst("quiesceTime");
306        return 0;
307    }
308
309    return (tc->readLastActivate() - tc->readLastSuspend()) /
310        SimClock::Int::ns;
311}
312
313uint64_t
314rpns(ThreadContext *tc)
315{
316    return curTick() / SimClock::Int::ns;
317}
318
319void
320wakeCPU(ThreadContext *tc, uint64_t cpuid)
321{
322    System *sys = tc->getSystemPtr();
323    ThreadContext *other_tc = sys->threadContexts[cpuid];
324    if (other_tc->status() == ThreadContext::Suspended)
325        other_tc->activate();
326}
327
328void
329m5exit(ThreadContext *tc, Tick delay)
330{
331    Tick when = curTick() + delay * SimClock::Int::ns;
332    exitSimLoop("m5_exit instruction encountered", 0, when);
333}
334
335void
336m5fail(ThreadContext *tc, Tick delay, uint64_t code)
337{
338    Tick when = curTick() + delay * SimClock::Int::ns;
339    exitSimLoop("m5_fail instruction encountered", code, when);
340}
341
342void
343loadsymbol(ThreadContext *tc)
344{
345    if (!FullSystem)
346        panicFsOnlyPseudoInst("loadsymbol");
347
348    const string &filename = tc->getCpuPtr()->system->params()->symbolfile;
349    if (filename.empty()) {
350        return;
351    }
352
353    std::string buffer;
354    ifstream file(filename.c_str());
355
356    if (!file)
357        fatal("file error: Can't open symbol table file %s\n", filename);
358
359    while (!file.eof()) {
360        getline(file, buffer);
361
362        if (buffer.empty())
363            continue;
364
365        string::size_type idx = buffer.find(' ');
366        if (idx == string::npos)
367            continue;
368
369        string address = "0x" + buffer.substr(0, idx);
370        eat_white(address);
371        if (address.empty())
372            continue;
373
374        // Skip over letter and space
375        string symbol = buffer.substr(idx + 3);
376        eat_white(symbol);
377        if (symbol.empty())
378            continue;
379
380        Addr addr;
381        if (!to_number(address, addr))
382            continue;
383
384        if (!tc->getSystemPtr()->kernelSymtab->insert(addr, symbol))
385            continue;
386
387
388        DPRINTF(Loader, "Loaded symbol: %s @ %#llx\n", symbol, addr);
389    }
390    file.close();
391}
392
393void
394addsymbol(ThreadContext *tc, Addr addr, Addr symbolAddr)
395{
396    if (!FullSystem)
397        panicFsOnlyPseudoInst("addSymbol");
398
399    char symb[100];
400    CopyStringOut(tc, symb, symbolAddr, 100);
401    std::string symbol(symb);
402
403    DPRINTF(Loader, "Loaded symbol: %s @ %#llx\n", symbol, addr);
404
405    tc->getSystemPtr()->kernelSymtab->insert(addr,symbol);
406    debugSymbolTable->insert(addr,symbol);
407}
408
409uint64_t
410initParam(ThreadContext *tc)
411{
412    if (!FullSystem) {
413        panicFsOnlyPseudoInst("initParam");
414        return 0;
415    }
416
417    return tc->getCpuPtr()->system->init_param;
418}
419
420
421void
422resetstats(ThreadContext *tc, Tick delay, Tick period)
423{
424    if (!tc->getCpuPtr()->params()->do_statistics_insts)
425        return;
426
427
428    Tick when = curTick() + delay * SimClock::Int::ns;
429    Tick repeat = period * SimClock::Int::ns;
430
431    Stats::schedStatEvent(false, true, when, repeat);
432}
433
434void
435dumpstats(ThreadContext *tc, Tick delay, Tick period)
436{
437    if (!tc->getCpuPtr()->params()->do_statistics_insts)
438        return;
439
440
441    Tick when = curTick() + delay * SimClock::Int::ns;
442    Tick repeat = period * SimClock::Int::ns;
443
444    Stats::schedStatEvent(true, false, when, repeat);
445}
446
447void
448dumpresetstats(ThreadContext *tc, Tick delay, Tick period)
449{
450    if (!tc->getCpuPtr()->params()->do_statistics_insts)
451        return;
452
453
454    Tick when = curTick() + delay * SimClock::Int::ns;
455    Tick repeat = period * SimClock::Int::ns;
456
457    Stats::schedStatEvent(true, true, when, repeat);
458}
459
460void
461m5checkpoint(ThreadContext *tc, Tick delay, Tick period)
462{
463    if (!tc->getCpuPtr()->params()->do_checkpoint_insts)
464        return;
465
466    Tick when = curTick() + delay * SimClock::Int::ns;
467    Tick repeat = period * SimClock::Int::ns;
468
469    exitSimLoop("checkpoint", 0, when, repeat);
470}
471
472uint64_t
473readfile(ThreadContext *tc, Addr vaddr, uint64_t len, uint64_t offset)
474{
475    if (!FullSystem) {
476        panicFsOnlyPseudoInst("readfile");
477        return 0;
478    }
479
480    const string &file = tc->getSystemPtr()->params()->readfile;
481    if (file.empty()) {
482        return ULL(0);
483    }
484
485    uint64_t result = 0;
486
487    int fd = ::open(file.c_str(), O_RDONLY, 0);
488    if (fd < 0)
489        panic("could not open file %s\n", file);
490
491    if (::lseek(fd, offset, SEEK_SET) < 0)
492        panic("could not seek: %s", strerror(errno));
493
494    char *buf = new char[len];
495    char *p = buf;
496    while (len > 0) {
497        int bytes = ::read(fd, p, len);
498        if (bytes <= 0)
499            break;
500
501        p += bytes;
502        result += bytes;
503        len -= bytes;
504    }
505
506    close(fd);
507    CopyIn(tc, vaddr, buf, result);
508    delete [] buf;
509    return result;
510}
511
512uint64_t
513writefile(ThreadContext *tc, Addr vaddr, uint64_t len, uint64_t offset,
514            Addr filename_addr)
515{
516    ostream *os;
517
518    // copy out target filename
519    char fn[100];
520    std::string filename;
521    CopyStringOut(tc, fn, filename_addr, 100);
522    filename = std::string(fn);
523
524    if (offset == 0) {
525        // create a new file (truncate)
526        os = simout.create(filename, true);
527    } else {
528        // do not truncate file if offset is non-zero
529        // (ios::in flag is required as well to keep the existing data
530        //  intact, otherwise existing data will be zeroed out.)
531        os = simout.openFile(simout.directory() + filename,
532                            ios::in | ios::out | ios::binary);
533    }
534    if (!os)
535        panic("could not open file %s\n", filename);
536
537    // seek to offset
538    os->seekp(offset);
539
540    // copy out data and write to file
541    char *buf = new char[len];
542    CopyOut(tc, buf, vaddr, len);
543    os->write(buf, len);
544    if (os->fail() || os->bad())
545        panic("Error while doing writefile!\n");
546
547    simout.close(os);
548
549    delete [] buf;
550
551    return len;
552}
553
554void
555debugbreak(ThreadContext *tc)
556{
557    Debug::breakpoint();
558}
559
560void
561switchcpu(ThreadContext *tc)
562{
563    exitSimLoop("switchcpu");
564}
565
566//
567// This function is executed when annotated work items begin.  Depending on
568// what the user specified at the command line, the simulation may exit and/or
569// take a checkpoint when a certain work item begins.
570//
571void
572workbegin(ThreadContext *tc, uint64_t workid, uint64_t threadid)
573{
574    tc->getCpuPtr()->workItemBegin();
575    System *sys = tc->getSystemPtr();
576    const System::Params *params = sys->params();
577    sys->workItemBegin(threadid, workid);
578
579    DPRINTF(WorkItems, "Work Begin workid: %d, threadid %d\n", workid,
580            threadid);
581
582    //
583    // If specified, determine if this is the specific work item the user
584    // identified
585    //
586    if (params->work_item_id == -1 || params->work_item_id == workid) {
587
588        uint64_t systemWorkBeginCount = sys->incWorkItemsBegin();
589        int cpuId = tc->getCpuPtr()->cpuId();
590
591        if (params->work_cpus_ckpt_count != 0 &&
592            sys->markWorkItem(cpuId) >= params->work_cpus_ckpt_count) {
593            //
594            // If active cpus equals checkpoint count, create checkpoint
595            //
596            exitSimLoop("checkpoint");
597        }
598
599        if (systemWorkBeginCount == params->work_begin_ckpt_count) {
600            //
601            // Note: the string specified as the cause of the exit event must
602            // exactly equal "checkpoint" inorder to create a checkpoint
603            //
604            exitSimLoop("checkpoint");
605        }
606
607        if (systemWorkBeginCount == params->work_begin_exit_count) {
608            //
609            // If a certain number of work items started, exit simulation
610            //
611            exitSimLoop("work started count reach");
612        }
613
614        if (cpuId == params->work_begin_cpu_id_exit) {
615            //
616            // If work started on the cpu id specified, exit simulation
617            //
618            exitSimLoop("work started on specific cpu");
619        }
620    }
621}
622
623//
624// This function is executed when annotated work items end.  Depending on
625// what the user specified at the command line, the simulation may exit and/or
626// take a checkpoint when a certain work item ends.
627//
628void
629workend(ThreadContext *tc, uint64_t workid, uint64_t threadid)
630{
631    tc->getCpuPtr()->workItemEnd();
632    System *sys = tc->getSystemPtr();
633    const System::Params *params = sys->params();
634    sys->workItemEnd(threadid, workid);
635
636    DPRINTF(WorkItems, "Work End workid: %d, threadid %d\n", workid, threadid);
637
638    //
639    // If specified, determine if this is the specific work item the user
640    // identified
641    //
642    if (params->work_item_id == -1 || params->work_item_id == workid) {
643
644        uint64_t systemWorkEndCount = sys->incWorkItemsEnd();
645        int cpuId = tc->getCpuPtr()->cpuId();
646
647        if (params->work_cpus_ckpt_count != 0 &&
648            sys->markWorkItem(cpuId) >= params->work_cpus_ckpt_count) {
649            //
650            // If active cpus equals checkpoint count, create checkpoint
651            //
652            exitSimLoop("checkpoint");
653        }
654
655        if (params->work_end_ckpt_count != 0 &&
656            systemWorkEndCount == params->work_end_ckpt_count) {
657            //
658            // If total work items completed equals checkpoint count, create
659            // checkpoint
660            //
661            exitSimLoop("checkpoint");
662        }
663
664        if (params->work_end_exit_count != 0 &&
665            systemWorkEndCount == params->work_end_exit_count) {
666            //
667            // If total work items completed equals exit count, exit simulation
668            //
669            exitSimLoop("work items exit count reached");
670        }
671    }
672}
673
674} // namespace PseudoInst
675