2 * Copyright (c) 2018, 2019 ARM Limited
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.
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.
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.
37 * Authors: Andreas Sandberg
40 #include "arch/arm/semihosting.hh"
44 #include "arch/arm/utility.hh"
45 #include "base/logging.hh"
46 #include "base/time.hh"
47 #include "debug/Semihosting.hh"
48 #include "dev/serial/serial.hh"
49 #include "mem/physical.hh"
50 #include "mem/secure_port_proxy.hh"
51 #include "params/ArmSemihosting.hh"
52 #include "sim/byteswap.hh"
53 #include "sim/sim_exit.hh"
54 #include "sim/system.hh"
56 const std::map
<uint32_t, ArmSemihosting::SemiCall
> ArmSemihosting::calls
{
57 { 0x01, { "SYS_OPEN", &ArmSemihosting::callOpen
, 3, 3 } },
58 { 0x02, { "SYS_CLOSE", &ArmSemihosting::callClose
, 1, 1 } },
60 // Write(C|0) are special since we want to read the character
61 // manually. We therefore declare them as having 0 params.
62 { 0x03, { "SYS_WRITEC", &ArmSemihosting::callWriteC
, 0, 0 } },
63 { 0x04, { "SYS_WRITE0", &ArmSemihosting::callWrite0
, 1, 1 } },
65 { 0x05, { "SYS_WRITE", &ArmSemihosting::callWrite
, 3, 3 } },
66 { 0x06, { "SYS_READ", &ArmSemihosting::callRead
, 3, 3 } },
67 { 0x07, { "SYS_READC", &ArmSemihosting::callReadC
, 0, 0 } },
68 { 0x08, { "SYS_ISERROR", &ArmSemihosting::callIsError
, 1, 1 } },
69 { 0x09, { "SYS_ISTTY", &ArmSemihosting::callIsTTY
, 1, 1 } },
70 { 0x0A, { "SYS_SEEK", &ArmSemihosting::callSeek
, 2, 2 } },
71 { 0x0C, { "SYS_FLEN", &ArmSemihosting::callFLen
, 1, 1 } },
72 { 0x0D, { "SYS_TMPNAM", &ArmSemihosting::callTmpNam
, 3, 3 } },
73 { 0x0E, { "SYS_REMOVE", &ArmSemihosting::callRemove
, 2, 2} },
74 { 0x0F, { "SYS_RENAME", &ArmSemihosting::callRename
, 4, 4} },
75 { 0x10, { "SYS_CLOCK", &ArmSemihosting::callClock
, 0, 0} },
76 { 0x11, { "SYS_TIME", &ArmSemihosting::callTime
, 0, 0} },
77 { 0x12, { "SYS_SYSTEM", &ArmSemihosting::callSystem
, 2, 2} },
78 { 0x13, { "SYS_ERRNO", &ArmSemihosting::callErrno
, 0, 0 } },
79 { 0x15, { "SYS_GET_CMDLINE", &ArmSemihosting::callGetCmdLine
, 2, 2} },
80 { 0x16, { "SYS_HEAPINFO", &ArmSemihosting::callHeapInfo
, 1, 1} },
82 // Exit is special and requires custom handling in aarch32.
83 { 0x18, { "SYS_EXIT", &ArmSemihosting::callExit
, 0, 2 } },
84 { 0x20, { "SYS_EXIT_EXTENDED", &ArmSemihosting::callExitExtended
, 2, 2 } },
86 { 0x30, { "SYS_ELAPSED", &ArmSemihosting::callElapsed
, 0, 0 } },
87 { 0x31, { "SYS_TICKFREQ", &ArmSemihosting::callTickFreq
, 0, 0 } },
90 const std::vector
<const char *> ArmSemihosting::fmodes
{
91 "r", "rb", "r+", "r+b",
92 "w", "wb", "w+", "w+b",
93 "a", "ab", "a+", "a+b",
96 const std::map
<uint64_t, const char *> ArmSemihosting::exitCodes
{
97 { 0x20000, "semi:ADP_Stopped_BranchThroughZero" },
98 { 0x20001, "semi:ADP_Stopped_UndefinedInstr" },
99 { 0x20002, "semi:ADP_Stopped_SoftwareInterrupt" },
100 { 0x20003, "semi:ADP_Stopped_PrefetchAbort" },
101 { 0x20004, "semi:ADP_Stopped_DataAbort" },
102 { 0x20005, "semi:ADP_Stopped_AddressException" },
103 { 0x20006, "semi:ADP_Stopped_IRQ" },
104 { 0x20007, "semi:ADP_Stopped_FIQ" },
106 { 0x20020, "semi:ADP_Stopped_BreakPoint" },
107 { 0x20021, "semi:ADP_Stopped_WatchPoint" },
108 { 0x20022, "semi:ADP_Stopped_StepComplete" },
109 { 0x20023, "semi:ADP_Stopped_RunTimeErrorUnknown" },
110 { 0x20024, "semi:ADP_Stopped_InternalError" },
111 { 0x20025, "semi:ADP_Stopped_UserInterruption" },
112 { 0x20026, "semi:ADP_Stopped_ApplicationExit" },
113 { 0x20027, "semi:ADP_Stopped_StackOverflow" },
114 { 0x20028, "semi:ADP_Stopped_DivisionByZero" },
115 { 0x20029, "semi:ADP_Stopped_DivisionByZero" },
119 const std::vector
<uint8_t> ArmSemihosting::features
{
120 0x53, 0x48, 0x46, 0x42, // Magic
121 0x3, // EXT_EXIT_EXTENDED, EXT_STDOUT_STDERR
124 const std::map
<const std::string
, FILE *> ArmSemihosting::stdioMap
{
128 {"stdout", ::stdout
},
130 {"stderr", ::stderr
},
133 ArmSemihosting::ArmSemihosting(const ArmSemihostingParams
*p
)
135 cmdLine(p
->cmd_line
),
136 memReserve(p
->mem_reserve
),
137 stackSize(p
->stack_size
),
138 timeBase([p
]{ struct tm t
= p
->time
; return mkutctime(&t
); }()),
139 tickShift(calcTickShift()),
141 filesRootDir(!p
->files_root_dir
.empty() &&
142 p
->files_root_dir
.back() != '/' ?
143 p
->files_root_dir
+ '/' : p
->files_root_dir
),
144 stdin(getSTDIO("stdin", p
->stdin
, "r")),
145 stdout(getSTDIO("stdout", p
->stdout
, "w")),
146 stderr(p
->stderr
== p
->stdout
?
147 stdout
: getSTDIO("stderr", p
->stderr
, "w"))
149 // Create an empty place-holder file for position 0 as semi-hosting
150 // calls typically expect non-zero file handles.
151 files
.push_back(nullptr);
154 inform("Semihosting: Shifting elapsed ticks by %i bits.",
159 ArmSemihosting::call64(ThreadContext
*tc
, uint32_t op
, uint64_t param
)
161 const SemiCall
*call
= getCall(op
, true);
163 warn("Unknown aarch64 semihosting call: op = 0x%x, param = 0x%x",
167 } else if (!call
->implemented64()) {
168 warn("Unimplemented aarch64 semihosting call: "
169 "%s (op = 0x%x, param = 0x%x)",
170 call
->name
, op
, param
);
175 std::vector
<uint64_t> argv(call
->argc64
+ 1);
176 PortProxy
&proxy
= physProxy(tc
);
177 ByteOrder endian
= ArmISA::byteOrder(tc
);
179 DPRINTF(Semihosting
, "Semihosting call64: %s(0x%x)\n", call
->name
, param
);
181 for (int i
= 0; i
< call
->argc64
; ++i
) {
182 argv
[i
+ 1] = proxy
.read
<uint64_t>(param
+ i
* 8, endian
);
183 DPRINTF(Semihosting
, "\t: 0x%x\n", argv
[i
+ 1]);
186 auto ret_errno
= (this->*call
->call
)(tc
, true, argv
);
187 semiErrno
= ret_errno
.second
;
188 DPRINTF(Semihosting
, "\t ->: 0x%x, %i\n",
189 ret_errno
.first
, ret_errno
.second
);
190 return ret_errno
.first
;
194 ArmSemihosting::call32(ThreadContext
*tc
, uint32_t op
, uint32_t param
)
196 const SemiCall
*call
= getCall(op
, false);
198 warn("Unknown aarch32 semihosting call: op = 0x%x, param = 0x%x",
202 } else if (!call
->implemented32()) {
203 warn("Unimplemented aarch32 semihosting call: "
204 "%s (op = 0x%x, param = 0x%x)",
205 call
->name
, op
, param
);
210 std::vector
<uint64_t> argv(call
->argc32
+ 1);
211 PortProxy
&proxy
= physProxy(tc
);
212 ByteOrder endian
= ArmISA::byteOrder(tc
);
214 DPRINTF(Semihosting
, "Semihosting call32: %s(0x%x)\n", call
->name
, param
);
216 for (int i
= 0; i
< call
->argc32
; ++i
) {
217 argv
[i
+ 1] = proxy
.read
<uint32_t>(param
+ i
* 4, endian
);
218 DPRINTF(Semihosting
, "\t: 0x%x\n", argv
[i
+ 1]);
221 auto ret_errno
= (this->*call
->call
)(tc
, false, argv
);
222 semiErrno
= ret_errno
.second
;
223 DPRINTF(Semihosting
, "\t ->: 0x%x, %i\n",
224 ret_errno
.first
, ret_errno
.second
);
225 return ret_errno
.first
;
229 ArmSemihosting::serialize(CheckpointOut
&cp
) const
231 SERIALIZE_SCALAR(semiErrno
);
233 paramOut(cp
, "num_files", files
.size());
234 for (int i
= 0; i
< files
.size(); i
++) {
239 files
[i
]->serializeSection(cp
, csprintf("file%i", i
));
244 ArmSemihosting::unserialize(CheckpointIn
&cp
)
246 UNSERIALIZE_SCALAR(semiErrno
);
249 paramIn(cp
, "num_files", num_files
);
250 files
.resize(num_files
);
251 for (int i
= 0; i
< num_files
; i
++)
252 files
[i
] = FileBase::create(*this, cp
, csprintf("file%i", i
));
256 ArmSemihosting::physProxy(ThreadContext
*tc
)
258 if (ArmISA::inSecureState(tc
)) {
260 System
*sys
= tc
->getSystemPtr();
261 physProxyS
.reset(new SecurePortProxy(
262 sys
->getSystemPort(),
263 sys
->cacheLineSize()));
267 return tc
->getPhysProxy();
273 ArmSemihosting::readString(ThreadContext
*tc
, Addr ptr
, size_t len
)
275 std::vector
<char> buf(len
+ 1);
278 physProxy(tc
).readBlob(ptr
, buf
.data(), len
);
280 return std::string(buf
.data());
283 ArmSemihosting::RetErrno
284 ArmSemihosting::callOpen(ThreadContext
*tc
, bool aarch64
,
285 std::vector
<uint64_t> &argv
)
287 const Addr name_base
= argv
[1];
288 const char *mode
= argv
[2] < fmodes
.size() ? fmodes
[argv
[2]] : nullptr;
289 const Addr name_size
= argv
[3];
291 DPRINTF(Semihosting
, "Semihosting SYS_OPEN(0x%x, %i[%s], %i)\n",
292 name_base
, argv
[2], mode
? mode
: "-", name_size
);
293 if (!mode
|| !name_base
)
294 return retError(EINVAL
);
296 std::string fname
= readString(tc
, name_base
, name_size
);
297 if (!fname
.empty() && fname
.front() != '/')
298 fname
= filesRootDir
+ fname
;
300 std::unique_ptr
<ArmSemihosting::FileBase
> file
=
301 FileBase::create(*this, fname
, mode
);
302 int64_t ret
= file
->open();
303 DPRINTF(Semihosting
, "Semihosting SYS_OPEN(\"%s\", %i[%s]): %i\n",
304 fname
, argv
[2], mode
, ret
);
306 return retError(-ret
);
308 files
.push_back(std::move(file
));
309 return retOK(files
.size() - 1);
313 ArmSemihosting::RetErrno
314 ArmSemihosting::callClose(ThreadContext
*tc
, bool aarch64
,
315 std::vector
<uint64_t> &argv
)
317 if (argv
[1] > files
.size()) {
318 DPRINTF(Semihosting
, "Semihosting SYS_CLOSE(%i): Illegal file\n");
319 return retError(EBADF
);
322 std::unique_ptr
<FileBase
> &file
= files
[argv
[1]];
323 int64_t error
= file
->close();
324 DPRINTF(Semihosting
, "Semihosting SYS_CLOSE(%i[%s]): %i\n",
325 argv
[1], file
->fileName(), error
);
327 return retError(-error
);
329 // Zap the pointer and free the entry in the file table as
331 files
[argv
[1]].reset();
336 ArmSemihosting::RetErrno
337 ArmSemihosting::callWriteC(ThreadContext
*tc
, bool aarch64
,
338 std::vector
<uint64_t> &argv
)
340 const char c
= physProxy(tc
).read
<char>(argv
[0]);
342 DPRINTF(Semihosting
, "Semihosting SYS_WRITEC('%c')\n", c
);
348 ArmSemihosting::RetErrno
349 ArmSemihosting::callWrite0(ThreadContext
*tc
, bool aarch64
,
350 std::vector
<uint64_t> &argv
)
352 DPRINTF(Semihosting
, "Semihosting SYS_WRITE0(...)\n");
353 PortProxy
&proxy
= physProxy(tc
);
354 for (Addr addr
= (Addr
)argv
[0]; ; ++addr
) {
355 char data
= proxy
.read
<char>(addr
);
365 ArmSemihosting::RetErrno
366 ArmSemihosting::callWrite(ThreadContext
*tc
, bool aarch64
,
367 std::vector
<uint64_t> &argv
)
369 if (argv
[1] > files
.size() || !files
[argv
[1]])
370 return RetErrno(argv
[3], EBADF
);
372 std::vector
<uint8_t> buffer(argv
[3]);
373 physProxy(tc
).readBlob(argv
[2], buffer
.data(), buffer
.size());
375 int64_t ret
= files
[argv
[1]]->write(buffer
.data(), buffer
.size());
377 // No bytes written (we're returning the number of bytes not
379 return RetErrno(argv
[3], -ret
);
381 // Return the number of bytes not written
382 return RetErrno(argv
[3] - ret
, 0);
386 ArmSemihosting::RetErrno
387 ArmSemihosting::callRead(ThreadContext
*tc
, bool aarch64
,
388 std::vector
<uint64_t> &argv
)
390 if (argv
[1] > files
.size() || !files
[argv
[1]])
391 return RetErrno(argv
[3], EBADF
);
393 std::vector
<uint8_t> buffer(argv
[3]);
394 int64_t ret
= files
[argv
[1]]->read(buffer
.data(), buffer
.size());
396 return RetErrno(argv
[3], -ret
);
398 panic_if(ret
> buffer
.size(), "Read longer than buffer size.");
400 physProxy(tc
).writeBlob(argv
[2], buffer
.data(), ret
);
402 // Return the number of bytes not written
403 return retOK(argv
[3] - ret
);
407 ArmSemihosting::RetErrno
408 ArmSemihosting::callReadC(ThreadContext
*tc
, bool aarch64
,
409 std::vector
<uint64_t> &argv
)
411 return retOK((char)std::cin
.get());
414 ArmSemihosting::RetErrno
415 ArmSemihosting::callIsError(ThreadContext
*tc
, bool aarch64
,
416 std::vector
<uint64_t> &argv
)
418 // Sign extend from a 32 bit integer in aarch32 since the argument
419 // reader zero extends to a uint64_t.
420 const int64_t status
= (int64_t)(aarch64
? argv
[1] :sext
<32>(argv
[1]));
421 // Assume there was an error if the status value is negative.
422 return retOK(status
< 0 ? 1 : 0);
425 ArmSemihosting::RetErrno
426 ArmSemihosting::callIsTTY(ThreadContext
*tc
, bool aarch64
,
427 std::vector
<uint64_t> &argv
)
429 if (argv
[1] > files
.size() || !files
[argv
[1]])
430 return retError(EBADF
);
432 int64_t ret
= files
[argv
[1]]->isTTY();
434 return retError(-ret
);
436 return retOK(ret
? 1 : 0);
440 ArmSemihosting::RetErrno
441 ArmSemihosting::callSeek(ThreadContext
*tc
, bool aarch64
,
442 std::vector
<uint64_t> &argv
)
444 if (argv
[1] > files
.size() || !files
[argv
[1]])
445 return retError(EBADF
);
447 int64_t ret
= files
[argv
[1]]->seek(argv
[2]);
449 return retError(-ret
);
455 ArmSemihosting::RetErrno
456 ArmSemihosting::callFLen(ThreadContext
*tc
, bool aarch64
,
457 std::vector
<uint64_t> &argv
)
459 if (argv
[1] > files
.size() || !files
[argv
[1]])
460 return retError(EBADF
);
462 int64_t ret
= files
[argv
[1]]->flen();
464 return retError(-ret
);
470 ArmSemihosting::RetErrno
471 ArmSemihosting::callTmpNam(ThreadContext
*tc
, bool aarch64
,
472 std::vector
<uint64_t> &argv
)
474 const Addr guest_buf
= argv
[1];
475 //const uint64_t id = argv[2];
476 const uint64_t max_len
= argv
[3];
478 std::vector
<char> buf(L_tmpnam
);
479 char *path
= tmpnam(buf
.data());
481 return retError(EINVAL
);
483 const size_t path_len
= strlen(path
);
484 if (path_len
>= max_len
)
485 return retError(ENOSPC
);
487 physProxy(tc
).writeBlob(guest_buf
, path
, path_len
+ 1);
491 ArmSemihosting::RetErrno
492 ArmSemihosting::callRemove(ThreadContext
*tc
, bool aarch64
,
493 std::vector
<uint64_t> &argv
)
495 std::string fname
= readString(tc
, argv
[1], argv
[2]);
497 if (remove(fname
.c_str()) != 0) {
498 return retError(errno
);
504 ArmSemihosting::RetErrno
505 ArmSemihosting::callRename(ThreadContext
*tc
, bool aarch64
,
506 std::vector
<uint64_t> &argv
)
508 std::string from
= readString(tc
, argv
[1], argv
[2]);
509 std::string to
= readString(tc
, argv
[3], argv
[4]);
511 if (rename(from
.c_str(), to
.c_str()) != 0) {
512 return retError(errno
);
518 ArmSemihosting::RetErrno
519 ArmSemihosting::callClock(ThreadContext
*tc
, bool aarch64
,
520 std::vector
<uint64_t> &argv
)
522 return retOK(curTick() / (SimClock::Int::s
/ 100));
525 ArmSemihosting::RetErrno
526 ArmSemihosting::callTime(ThreadContext
*tc
, bool aarch64
,
527 std::vector
<uint64_t> &argv
)
529 return retOK(timeBase
+ round(curTick() / SimClock::Float::s
));
532 ArmSemihosting::RetErrno
533 ArmSemihosting::callSystem(ThreadContext
*tc
, bool aarch64
,
534 std::vector
<uint64_t> &argv
)
536 const std::string cmd
= readString(tc
, argv
[1], argv
[2]);
537 warn("Semihosting: SYS_SYSTEM not implemented. Guest tried to run: %s\n",
539 return retError(EINVAL
);
543 ArmSemihosting::RetErrno
544 ArmSemihosting::callErrno(ThreadContext
*tc
, bool aarch64
,
545 std::vector
<uint64_t> &argv
)
547 // Preserve errno by returning it in errno as well.
548 return RetErrno(semiErrno
, semiErrno
);
551 ArmSemihosting::RetErrno
552 ArmSemihosting::callGetCmdLine(ThreadContext
*tc
, bool aarch64
,
553 std::vector
<uint64_t> &argv
)
555 if (cmdLine
.size() + 1 < argv
[2]) {
556 PortProxy
&proxy
= physProxy(tc
);
557 ByteOrder endian
= ArmISA::byteOrder(tc
);
558 proxy
.writeBlob((Addr
)argv
[1], cmdLine
.c_str(), cmdLine
.size() + 1);
561 proxy
.write
<uint64_t>(argv
[0] + 1 * 8, cmdLine
.size(), endian
);
563 proxy
.write
<uint32_t>(argv
[0] + 1 * 4, cmdLine
.size(), endian
);
570 ArmSemihosting::RetErrno
571 ArmSemihosting::callHeapInfo(ThreadContext
*tc
, bool aarch64
,
572 std::vector
<uint64_t> &argv
)
574 const PhysicalMemory
&phys
= tc
->getSystemPtr()->getPhysMem();
575 const AddrRangeList memories
= phys
.getConfAddrRanges();
576 fatal_if(memories
.size() < 1, "No memories reported from System");
577 warn_if(memories
.size() > 1, "Multiple physical memory ranges available. "
578 "Using first range heap/stack.");
579 const AddrRange memory
= *memories
.begin();
580 const Addr mem_start
= memory
.start() + memReserve
;
581 Addr mem_end
= memory
.end();
583 // Make sure that 32-bit guests can access their memory.
585 const Addr phys_max
= (1ULL << 32) - 1;
586 panic_if(mem_start
> phys_max
,
587 "Physical memory out of range for a 32-bit guest.");
588 if (mem_end
> phys_max
) {
589 warn("Some physical memory out of range for a 32-bit guest.");
594 fatal_if(mem_start
+ stackSize
>= mem_end
,
595 "Physical memory too small to fit desired stack and a heap.");
597 const Addr heap_base
= mem_start
;
598 const Addr heap_limit
= mem_end
- stackSize
+ 1;
599 const Addr stack_base
= (mem_end
+ 1) & ~0x7ULL
; // 8 byte stack alignment
600 const Addr stack_limit
= heap_limit
;
603 inform("Reporting heap/stack info to guest:\n"
604 "\tHeap base: 0x%x\n"
605 "\tHeap limit: 0x%x\n"
606 "\tStack base: 0x%x\n"
607 "\tStack limit: 0x%x\n",
608 heap_base
, heap_limit
, stack_base
, stack_limit
);
611 PortProxy
&proxy
= physProxy(tc
);
612 ByteOrder endian
= ArmISA::byteOrder(tc
);
614 proxy
.write
<uint64_t>(base
+ 0 * 8, heap_base
, endian
);
615 proxy
.write
<uint64_t>(base
+ 1 * 8, heap_limit
, endian
);
616 proxy
.write
<uint64_t>(base
+ 2 * 8, stack_base
, endian
);
617 proxy
.write
<uint64_t>(base
+ 3 * 8, stack_limit
, endian
);
619 proxy
.write
<uint32_t>(base
+ 0 * 4, heap_base
, endian
);
620 proxy
.write
<uint32_t>(base
+ 1 * 4, heap_limit
, endian
);
621 proxy
.write
<uint32_t>(base
+ 2 * 4, stack_base
, endian
);
622 proxy
.write
<uint32_t>(base
+ 3 * 4, stack_limit
, endian
);
628 ArmSemihosting::RetErrno
629 ArmSemihosting::callExit(ThreadContext
*tc
, bool aarch64
,
630 std::vector
<uint64_t> &argv
)
633 semiExit(argv
[1], argv
[2]);
635 semiExit(argv
[0], 0);
641 ArmSemihosting::RetErrno
642 ArmSemihosting::callExitExtended(ThreadContext
*tc
, bool aarch64
,
643 std::vector
<uint64_t> &argv
)
645 semiExit(argv
[1], argv
[2]);
651 ArmSemihosting::semiExit(uint64_t code
, uint64_t subcode
)
653 auto it
= exitCodes
.find(code
);
654 if (it
!= exitCodes
.end()) {
655 exitSimLoop(it
->second
, subcode
);
657 exitSimLoop(csprintf("semi:0x%x", code
), subcode
);
662 ArmSemihosting::RetErrno
663 ArmSemihosting::callElapsed(ThreadContext
*tc
, bool aarch64
,
664 std::vector
<uint64_t> &argv
)
666 PortProxy
&proxy
= physProxy(tc
);
667 ByteOrder endian
= ArmISA::byteOrder(tc
);
668 const uint64_t tick
= semiTick(curTick());
671 proxy
.write
<uint64_t>(argv
[0], tick
, endian
);
673 proxy
.write
<uint32_t>(argv
[0] + 0 * 4, tick
, endian
);
674 proxy
.write
<uint32_t>(argv
[0] + 1 * 4, tick
>> 32, endian
);
681 ArmSemihosting::RetErrno
682 ArmSemihosting::callTickFreq(ThreadContext
*tc
, bool aarch64
,
683 std::vector
<uint64_t> &argv
)
685 return retOK(semiTick(SimClock::Frequency
));
688 const ArmSemihosting::SemiCall
*
689 ArmSemihosting::getCall(uint32_t op
, bool aarch64
)
691 auto it
= calls
.find(op
);
692 if (it
== calls
.end())
700 ArmSemihosting::getSTDIO(const char *stream_name
,
701 const std::string
&name
, const char *mode
)
703 auto it
= stdioMap
.find(name
);
704 if (it
== stdioMap
.end()) {
705 FILE *f
= fopen(name
.c_str(), mode
);
707 fatal("Failed to open %s (%s): %s\n",
708 stream_name
, name
, strerror(errno
));
716 std::unique_ptr
<ArmSemihosting::FileBase
>
717 ArmSemihosting::FileBase::create(
718 ArmSemihosting
&parent
, const std::string
&fname
, const char *mode
)
720 std::unique_ptr
<FileBase
> file
;
721 if (fname
== ":semihosting-features") {
722 file
.reset(new FileFeatures(parent
, fname
.c_str(), mode
));
724 file
.reset(new File(parent
, fname
.c_str(), mode
));
730 std::unique_ptr
<ArmSemihosting::FileBase
>
731 ArmSemihosting::FileBase::create(ArmSemihosting
&parent
,
732 CheckpointIn
&cp
, const std::string
&sec
)
734 std::unique_ptr
<FileBase
> file
;
735 ScopedCheckpointSection
_sec(cp
, sec
);
737 // Was the file open when the checkpoint was created?
738 if (!cp
.sectionExists(Serializable::currentSection()))
741 std::string fname
, mode
;
742 paramIn(cp
, "name", fname
);
743 paramIn(cp
, "mode", mode
);
744 file
= create(parent
, fname
, mode
.c_str());
746 file
->unserialize(cp
);
752 ArmSemihosting::FileBase::serialize(CheckpointOut
&cp
) const
754 paramOut(cp
, "name", _name
);
755 SERIALIZE_SCALAR(mode
);
759 ArmSemihosting::FileBase::unserialize(CheckpointIn
&cp
)
761 /* Unserialization of name and mode happens in
762 * ArmSemihosting::FileBase::create() */
766 ArmSemihosting::FileBase::read(uint8_t *buffer
, uint64_t size
)
772 ArmSemihosting::FileBase::write(const uint8_t *buffer
, uint64_t size
)
778 ArmSemihosting::FileBase::seek(uint64_t pos
)
784 ArmSemihosting::FileBase::flen()
790 ArmSemihosting::FileFeatures::FileFeatures(
791 ArmSemihosting
&_parent
, const char *_name
, const char *_mode
)
792 : FileBase(_parent
, _name
, _mode
)
797 ArmSemihosting::FileFeatures::read(uint8_t *buffer
, uint64_t size
)
801 for (; pos
< size
&& pos
< ArmSemihosting::features
.size(); pos
++)
802 buffer
[len
++] = ArmSemihosting::features
[pos
];
808 ArmSemihosting::FileFeatures::seek(uint64_t _pos
)
810 if (_pos
< ArmSemihosting::features
.size()) {
819 ArmSemihosting::FileFeatures::serialize(CheckpointOut
&cp
) const
821 FileBase::serialize(cp
);
822 SERIALIZE_SCALAR(pos
);
826 ArmSemihosting::FileFeatures::unserialize(CheckpointIn
&cp
)
828 FileBase::unserialize(cp
);
829 UNSERIALIZE_SCALAR(pos
);
834 ArmSemihosting::File::File(ArmSemihosting
&_parent
,
835 const char *_name
, const char *_perms
)
836 : FileBase(_parent
, _name
, _perms
),
841 ArmSemihosting::File::~File()
848 ArmSemihosting::File::openImpl(bool in_cpt
)
850 panic_if(file
, "Trying to open an already open file.\n");
852 if (_name
== ":tt") {
853 if (mode
[0] == 'r') {
855 } else if (mode
[0] == 'w') {
856 file
= parent
.stdout
;
857 } else if (mode
[0] == 'a') {
858 file
= parent
.stderr
;
860 warn("Unknown file mode for the ':tt' special file");
864 std::string
real_mode(this->mode
);
865 // Avoid truncating the file if we are restoring from a
867 if (in_cpt
&& real_mode
[0] == 'w')
870 file
= fopen(_name
.c_str(), real_mode
.c_str());
873 return file
? 0 : -errno
;
877 ArmSemihosting::File::close()
879 panic_if(!file
, "Trying to close an already closed file.\n");
890 ArmSemihosting::File::isTTY() const
892 return file
== parent
.stdout
||
893 file
== parent
.stderr
||
894 file
== parent
.stdin
;
898 ArmSemihosting::File::read(uint8_t *buffer
, uint64_t size
)
900 panic_if(!file
, "Trying to read from a closed file");
902 size_t ret
= fread(buffer
, 1, size
, file
);
904 // Error or EOF. Assume errors are due to invalid file
905 // operations (e.g., reading a write-only stream).
906 return ferror(file
) ? -EINVAL
: 0;
913 ArmSemihosting::File::write(const uint8_t *buffer
, uint64_t size
)
915 panic_if(!file
, "Trying to write to a closed file");
918 size_t ret
= fwrite(buffer
, 1, size
, file
);
920 // Assume errors are due to invalid file operations (e.g.,
921 // writing a read-only stream).
929 ArmSemihosting::File::seek(uint64_t _pos
)
931 panic_if(!file
, "Trying to seek in a closed file");
934 if (fseek(file
, _pos
, SEEK_SET
) == 0)
941 ArmSemihosting::File::flen()
944 long pos
= ftell(file
);
948 if (fseek(file
, 0, SEEK_END
) != 0)
951 long len
= ftell(file
);
955 if (fseek(file
, pos
, SEEK_SET
) != 0)
963 ArmSemihosting::File::serialize(CheckpointOut
&cp
) const
965 FileBase::serialize(cp
);
968 long pos
= file
? ftell(file
) : 0;
969 panic_if(pos
< 0, "Failed to get file position.");
970 SERIALIZE_SCALAR(pos
);
975 ArmSemihosting::File::unserialize(CheckpointIn
&cp
)
977 FileBase::unserialize(cp
);
979 if (openImpl(true) < 0) {
980 fatal("Failed to open file: %s", _name
);
985 UNSERIALIZE_SCALAR(pos
);
986 if (fseek(file
, pos
, SEEK_SET
) != 0) {
987 fatal("Failed seek to current position (%i) in '%s'", pos
, _name
);
994 ArmSemihostingParams::create()
996 return new ArmSemihosting(this);