arm: Optionally enable gem5 extended semihosting calls.
[gem5.git] / src / arch / arm / semihosting.cc
1 /*
2 * Copyright (c) 2018, 2019 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
38 #include "arch/arm/semihosting.hh"
39
40 #include <cstdio>
41
42 #include "arch/arm/utility.hh"
43 #include "base/logging.hh"
44 #include "base/time.hh"
45 #include "debug/Semihosting.hh"
46 #include "dev/serial/serial.hh"
47 #include "mem/physical.hh"
48 #include "mem/secure_port_proxy.hh"
49 #include "params/ArmSemihosting.hh"
50 #include "sim/byteswap.hh"
51 #include "sim/sim_exit.hh"
52 #include "sim/system.hh"
53
54 const std::map<uint32_t, ArmSemihosting::SemiCall> ArmSemihosting::calls{
55 { SYS_OPEN, { "SYS_OPEN", &ArmSemihosting::callOpen } },
56 { SYS_CLOSE, { "SYS_CLOSE", &ArmSemihosting::callClose } },
57 { SYS_WRITEC, { "SYS_WRITEC", &ArmSemihosting::callWriteC } },
58 { SYS_WRITE0, { "SYS_WRITE0", &ArmSemihosting::callWrite0 } },
59 { SYS_WRITE, { "SYS_WRITE", &ArmSemihosting::callWrite } },
60 { SYS_READ, { "SYS_READ", &ArmSemihosting::callRead } },
61 { SYS_READC, { "SYS_READC", &ArmSemihosting::callReadC } },
62 { SYS_ISERROR, { "SYS_ISERROR", &ArmSemihosting::callIsError } },
63 { SYS_ISTTY, { "SYS_ISTTY", &ArmSemihosting::callIsTTY } },
64 { SYS_SEEK, { "SYS_SEEK", &ArmSemihosting::callSeek } },
65 { SYS_FLEN, { "SYS_FLEN", &ArmSemihosting::callFLen } },
66 { SYS_TMPNAM, { "SYS_TMPNAM", &ArmSemihosting::callTmpNam } },
67 { SYS_REMOVE, { "SYS_REMOVE", &ArmSemihosting::callRemove } },
68 { SYS_RENAME, { "SYS_RENAME", &ArmSemihosting::callRename } },
69 { SYS_CLOCK, { "SYS_CLOCK", &ArmSemihosting::callClock } },
70 { SYS_TIME, { "SYS_TIME", &ArmSemihosting::callTime } },
71 { SYS_SYSTEM, { "SYS_SYSTEM", &ArmSemihosting::callSystem } },
72 { SYS_ERRNO, { "SYS_ERRNO", &ArmSemihosting::callErrno } },
73 { SYS_GET_CMDLINE,
74 { "SYS_GET_CMDLINE", &ArmSemihosting::callGetCmdLine } },
75 { SYS_HEAPINFO, { "SYS_HEAPINFO", &ArmSemihosting::callHeapInfo32,
76 &ArmSemihosting::callHeapInfo64 } },
77
78 { SYS_EXIT, { "SYS_EXIT", &ArmSemihosting::callExit32,
79 &ArmSemihosting::callExit64} },
80 { SYS_EXIT_EXTENDED,
81 { "SYS_EXIT_EXTENDED", &ArmSemihosting::callExitExtended } },
82
83 { SYS_ELAPSED, { "SYS_ELAPSED", &ArmSemihosting::callElapsed32,
84 &ArmSemihosting::callElapsed64 } },
85 { SYS_TICKFREQ, { "SYS_TICKFREQ", &ArmSemihosting::callTickFreq } },
86 };
87
88 const std::vector<const char *> ArmSemihosting::fmodes{
89 "r", "rb", "r+", "r+b",
90 "w", "wb", "w+", "w+b",
91 "a", "ab", "a+", "a+b",
92 };
93
94 const std::map<uint64_t, const char *> ArmSemihosting::exitCodes{
95 { 0x20000, "semi:ADP_Stopped_BranchThroughZero" },
96 { 0x20001, "semi:ADP_Stopped_UndefinedInstr" },
97 { 0x20002, "semi:ADP_Stopped_SoftwareInterrupt" },
98 { 0x20003, "semi:ADP_Stopped_PrefetchAbort" },
99 { 0x20004, "semi:ADP_Stopped_DataAbort" },
100 { 0x20005, "semi:ADP_Stopped_AddressException" },
101 { 0x20006, "semi:ADP_Stopped_IRQ" },
102 { 0x20007, "semi:ADP_Stopped_FIQ" },
103
104 { 0x20020, "semi:ADP_Stopped_BreakPoint" },
105 { 0x20021, "semi:ADP_Stopped_WatchPoint" },
106 { 0x20022, "semi:ADP_Stopped_StepComplete" },
107 { 0x20023, "semi:ADP_Stopped_RunTimeErrorUnknown" },
108 { 0x20024, "semi:ADP_Stopped_InternalError" },
109 { 0x20025, "semi:ADP_Stopped_UserInterruption" },
110 { 0x20026, "semi:ADP_Stopped_ApplicationExit" },
111 { 0x20027, "semi:ADP_Stopped_StackOverflow" },
112 { 0x20028, "semi:ADP_Stopped_DivisionByZero" },
113 { 0x20029, "semi:ADP_Stopped_DivisionByZero" },
114 };
115
116
117 const std::vector<uint8_t> ArmSemihosting::features{
118 0x53, 0x48, 0x46, 0x42, // Magic
119 0x3, // EXT_EXIT_EXTENDED, EXT_STDOUT_STDERR
120 };
121
122 const std::map<const std::string, FILE *> ArmSemihosting::stdioMap{
123 {"cin", ::stdin},
124 {"stdin", ::stdin},
125 {"cout", ::stdout},
126 {"stdout", ::stdout},
127 {"cerr", ::stderr},
128 {"stderr", ::stderr},
129 };
130
131 ArmSemihosting::ArmSemihosting(const ArmSemihostingParams *p)
132 : SimObject(p),
133 cmdLine(p->cmd_line),
134 memReserve(p->mem_reserve),
135 stackSize(p->stack_size),
136 timeBase([p]{ struct tm t = p->time; return mkutctime(&t); }()),
137 tickShift(calcTickShift()),
138 semiErrno(0),
139 filesRootDir(!p->files_root_dir.empty() &&
140 p->files_root_dir.back() != '/' ?
141 p->files_root_dir + '/' : p->files_root_dir),
142 stdin(getSTDIO("stdin", p->stdin, "r")),
143 stdout(getSTDIO("stdout", p->stdout, "w")),
144 stderr(p->stderr == p->stdout ?
145 stdout : getSTDIO("stderr", p->stderr, "w"))
146 {
147 // Create an empty place-holder file for position 0 as semi-hosting
148 // calls typically expect non-zero file handles.
149 files.push_back(nullptr);
150
151 if (tickShift > 0)
152 inform("Semihosting: Shifting elapsed ticks by %i bits.",
153 tickShift);
154 }
155
156 void
157 ArmSemihosting::call64(ThreadContext *tc, bool gem5_ops)
158 {
159 RegVal op = tc->readIntReg(ArmISA::INTREG_X0 & mask(32));
160 if (op > MaxStandardOp && !gem5_ops) {
161 unrecognizedCall<Abi64>(
162 tc, "Gem5 semihosting op (0x%x) disabled from here.", op);
163 return;
164 }
165
166 auto it = calls.find(op);
167 if (it == calls.end()) {
168 unrecognizedCall<Abi64>(
169 tc, "Unknown aarch64 semihosting call: op = 0x%x", op);
170 return;
171 }
172 const SemiCall &call = it->second;
173
174 DPRINTF(Semihosting, "Semihosting call64: %s\n", call.dump64(tc));
175 auto err = call.call64(this, tc);
176 semiErrno = err.second;
177 DPRINTF(Semihosting, "\t ->: 0x%x, %i\n", err.first, err.second);
178 }
179
180 void
181 ArmSemihosting::call32(ThreadContext *tc, bool gem5_ops)
182 {
183 RegVal op = tc->readIntReg(ArmISA::INTREG_R0);
184 if (op > MaxStandardOp && !gem5_ops) {
185 unrecognizedCall<Abi32>(
186 tc, "Gem5 semihosting op (0x%x) disabled from here.", op);
187 return;
188 }
189
190 auto it = calls.find(op);
191 if (it == calls.end()) {
192 unrecognizedCall<Abi32>(
193 tc, "Unknown aarch32 semihosting call: op = 0x%x", op);
194 return;
195 }
196 const SemiCall &call = it->second;
197
198 DPRINTF(Semihosting, "Semihosting call32: %s\n", call.dump32(tc));
199 auto err = call.call32(this, tc);
200 semiErrno = err.second;
201 DPRINTF(Semihosting, "\t ->: 0x%x, %i\n", err.first, err.second);
202 }
203
204 void
205 ArmSemihosting::serialize(CheckpointOut &cp) const
206 {
207 SERIALIZE_SCALAR(semiErrno);
208
209 paramOut(cp, "num_files", files.size());
210 for (int i = 0; i < files.size(); i++) {
211 // File closed?
212 if (!files[i])
213 continue;
214
215 files[i]->serializeSection(cp, csprintf("file%i", i));
216 }
217 }
218
219 void
220 ArmSemihosting::unserialize(CheckpointIn &cp)
221 {
222 UNSERIALIZE_SCALAR(semiErrno);
223
224 size_t num_files;
225 paramIn(cp, "num_files", num_files);
226 files.resize(num_files);
227 for (int i = 0; i < num_files; i++)
228 files[i] = FileBase::create(*this, cp, csprintf("file%i", i));
229 }
230
231 PortProxy &
232 ArmSemihosting::physProxy(ThreadContext *tc)
233 {
234 static std::unique_ptr<PortProxy> phys_proxy_s;
235 static System *secure_sys = nullptr;
236
237 if (ArmISA::inSecureState(tc)) {
238 System *sys = tc->getSystemPtr();
239 if (sys != secure_sys) {
240 phys_proxy_s.reset(new SecurePortProxy(sys->getSystemPort(),
241 sys->cacheLineSize()));
242 }
243 secure_sys = sys;
244 return *phys_proxy_s;
245 } else {
246 return tc->getPhysProxy();
247 }
248 }
249
250
251 std::string
252 ArmSemihosting::readString(ThreadContext *tc, Addr ptr, size_t len)
253 {
254 std::vector<char> buf(len + 1);
255
256 buf[len] = '\0';
257 physProxy(tc).readBlob(ptr, buf.data(), len);
258
259 return std::string(buf.data());
260 }
261
262 ArmSemihosting::RetErrno
263 ArmSemihosting::callOpen(ThreadContext *tc, const Addr name_base,
264 int fmode, size_t name_size)
265 {
266 const char *mode = fmode < fmodes.size() ? fmodes[fmode] : nullptr;
267
268 DPRINTF(Semihosting, "Semihosting SYS_OPEN(0x%x, %i[%s], %i)\n",
269 name_base, fmode, mode ? mode : "-", name_size);
270 if (!mode || !name_base)
271 return retError(EINVAL);
272
273 std::string fname = readString(tc, name_base, name_size);
274 if (!fname.empty() && fname.front() != '/')
275 fname = filesRootDir + fname;
276
277 std::unique_ptr<ArmSemihosting::FileBase> file =
278 FileBase::create(*this, fname, mode);
279 int64_t ret = file->open();
280 DPRINTF(Semihosting, "Semihosting SYS_OPEN(\"%s\", %i[%s]): %i\n",
281 fname, fmode, mode, ret);
282 if (ret < 0) {
283 return retError(-ret);
284 } else {
285 files.push_back(std::move(file));
286 return retOK(files.size() - 1);
287 }
288 }
289
290 ArmSemihosting::RetErrno
291 ArmSemihosting::callClose(ThreadContext *tc, uint64_t handle)
292 {
293 if (handle > files.size()) {
294 DPRINTF(Semihosting, "Semihosting SYS_CLOSE(%i): Illegal file\n");
295 return retError(EBADF);
296 }
297
298 std::unique_ptr<FileBase> &file = files[handle];
299 int64_t error = file->close();
300 DPRINTF(Semihosting, "Semihosting SYS_CLOSE(%i[%s]): %i\n",
301 handle, file->fileName(), error);
302 if (error < 0) {
303 return retError(-error);
304 } else {
305 // Zap the pointer and free the entry in the file table as
306 // well.
307 files[handle].reset();
308 return retOK(0);
309 }
310 }
311
312 ArmSemihosting::RetErrno
313 ArmSemihosting::callWriteC(ThreadContext *tc, InPlaceArg arg)
314 {
315 const char c = physProxy(tc).read<char>(arg.addr);
316
317 DPRINTF(Semihosting, "Semihosting SYS_WRITEC('%c')\n", c);
318 std::cout.put(c);
319
320 return retOK(0);
321 }
322
323 ArmSemihosting::RetErrno
324 ArmSemihosting::callWrite0(ThreadContext *tc, InPlaceArg arg)
325 {
326 DPRINTF(Semihosting, "Semihosting SYS_WRITE0(...)\n");
327 PortProxy &proxy = physProxy(tc);
328 std::string str;
329 proxy.readString(str, arg.addr);
330 std::cout.write(str.c_str(), str.size());
331
332 return retOK(0);
333 }
334
335 ArmSemihosting::RetErrno
336 ArmSemihosting::callWrite(ThreadContext *tc, uint64_t handle, Addr addr,
337 size_t size)
338 {
339 if (handle > files.size() || !files[handle])
340 return RetErrno(size, EBADF);
341
342 std::vector<uint8_t> buffer(size);
343 physProxy(tc).readBlob(addr, buffer.data(), buffer.size());
344
345 int64_t ret = files[handle]->write(buffer.data(), buffer.size());
346 if (ret < 0) {
347 // No bytes written (we're returning the number of bytes not
348 // written)
349 return RetErrno(size, -ret);
350 } else {
351 // Return the number of bytes not written
352 return RetErrno(size - ret, 0);
353 }
354 }
355
356 ArmSemihosting::RetErrno
357 ArmSemihosting::callRead(ThreadContext *tc, uint64_t handle, Addr addr,
358 size_t size)
359 {
360 if (handle > files.size() || !files[handle])
361 return RetErrno(size, EBADF);
362
363 std::vector<uint8_t> buffer(size);
364 int64_t ret = files[handle]->read(buffer.data(), buffer.size());
365 if (ret < 0) {
366 return RetErrno(size, -ret);
367 } else {
368 panic_if(ret > buffer.size(), "Read longer than buffer size.");
369
370 physProxy(tc).writeBlob(addr, buffer.data(), ret);
371
372 // Return the number of bytes not written
373 return retOK(size - ret);
374 }
375 }
376
377 ArmSemihosting::RetErrno
378 ArmSemihosting::callReadC(ThreadContext *tc)
379 {
380 return retOK((char)std::cin.get());
381 }
382
383 ArmSemihosting::RetErrno
384 ArmSemihosting::callIsError(ThreadContext *tc, int64_t status)
385 {
386 return retOK(status < 0 ? 1 : 0);
387 }
388
389 ArmSemihosting::RetErrno
390 ArmSemihosting::callIsTTY(ThreadContext *tc, uint64_t handle)
391 {
392 if (handle > files.size() || !files[handle])
393 return retError(EBADF);
394
395 int64_t ret = files[handle]->isTTY();
396 if (ret < 0) {
397 return retError(-ret);
398 } else {
399 return retOK(ret ? 1 : 0);
400 }
401 }
402
403 ArmSemihosting::RetErrno
404 ArmSemihosting::callSeek(ThreadContext *tc, uint64_t handle, uint64_t pos)
405 {
406 if (handle > files.size() || !files[handle])
407 return retError(EBADF);
408
409 int64_t ret = files[handle]->seek(pos);
410 if (ret < 0) {
411 return retError(-ret);
412 } else {
413 return retOK(0);
414 }
415 }
416
417 ArmSemihosting::RetErrno
418 ArmSemihosting::callFLen(ThreadContext *tc, uint64_t handle)
419 {
420 if (handle > files.size() || !files[handle])
421 return retError(EBADF);
422
423 int64_t ret = files[handle]->flen();
424 if (ret < 0) {
425 return retError(-ret);
426 } else {
427 return retOK(ret);
428 }
429 }
430
431 ArmSemihosting::RetErrno
432 ArmSemihosting::callTmpNam(ThreadContext *tc, Addr addr, uint64_t id,
433 size_t size)
434 {
435 std::vector<char> buf(L_tmpnam);
436 char *path = tmpnam(buf.data());
437 if (!path)
438 return retError(EINVAL);
439
440 const size_t path_len = strlen(path);
441 if (path_len >= size)
442 return retError(ENOSPC);
443
444 physProxy(tc).writeBlob(addr, path, path_len + 1);
445 return retOK(0);
446 }
447
448 ArmSemihosting::RetErrno
449 ArmSemihosting::callRemove(ThreadContext *tc, Addr name_base, size_t name_size)
450 {
451 std::string fname = readString(tc, name_base, name_size);
452
453 if (remove(fname.c_str()) != 0) {
454 return retError(errno);
455 } else {
456 return retOK(0);
457 }
458 }
459
460 ArmSemihosting::RetErrno
461 ArmSemihosting::callRename(ThreadContext *tc, Addr from_addr, size_t from_size,
462 Addr to_addr, size_t to_size)
463 {
464 std::string from = readString(tc, from_addr, from_size);
465 std::string to = readString(tc, to_addr, to_size);
466
467 if (rename(from.c_str(), to.c_str()) != 0) {
468 return retError(errno);
469 } else {
470 return retOK(0);
471 }
472 }
473
474 ArmSemihosting::RetErrno
475 ArmSemihosting::callClock(ThreadContext *tc)
476 {
477 return retOK(curTick() / (SimClock::Int::s / 100));
478 }
479
480 ArmSemihosting::RetErrno
481 ArmSemihosting::callTime(ThreadContext *tc)
482 {
483 return retOK(timeBase + round(curTick() / SimClock::Float::s));
484 }
485
486 ArmSemihosting::RetErrno
487 ArmSemihosting::callSystem(ThreadContext *tc, Addr cmd_addr, size_t cmd_size)
488 {
489 const std::string cmd = readString(tc, cmd_addr, cmd_size);
490 warn("Semihosting: SYS_SYSTEM not implemented. Guest tried to run: %s\n",
491 cmd);
492 return retError(EINVAL);
493
494 }
495
496 ArmSemihosting::RetErrno
497 ArmSemihosting::callErrno(ThreadContext *tc)
498 {
499 // Preserve errno by returning it in errno as well.
500 return RetErrno(semiErrno, semiErrno);
501 }
502
503 ArmSemihosting::RetErrno
504 ArmSemihosting::callGetCmdLine(ThreadContext *tc, Addr addr,
505 InPlaceArg size_arg)
506 {
507 PortProxy &proxy = physProxy(tc);
508 ByteOrder endian = ArmISA::byteOrder(tc);
509 size_t size = size_arg.read(tc, endian);
510
511 if (cmdLine.size() + 1 < size) {
512 proxy.writeBlob(addr, cmdLine.c_str(), cmdLine.size() + 1);
513 size_arg.write(tc, cmdLine.size(), endian);
514 return retOK(0);
515 } else {
516 return retError(0);
517 }
518 }
519
520 void
521 ArmSemihosting::gatherHeapInfo(ThreadContext *tc, bool aarch64,
522 Addr &heap_base, Addr &heap_limit,
523 Addr &stack_base, Addr &stack_limit)
524 {
525 const PhysicalMemory &phys = tc->getSystemPtr()->getPhysMem();
526 const AddrRangeList memories = phys.getConfAddrRanges();
527 fatal_if(memories.size() < 1, "No memories reported from System");
528 warn_if(memories.size() > 1, "Multiple physical memory ranges available. "
529 "Using first range heap/stack.");
530 const AddrRange memory = *memories.begin();
531 const Addr mem_start = memory.start() + memReserve;
532 Addr mem_end = memory.end();
533
534 // Make sure that 32-bit guests can access their memory.
535 if (!aarch64) {
536 const Addr phys_max = (1ULL << 32) - 1;
537 panic_if(mem_start > phys_max,
538 "Physical memory out of range for a 32-bit guest.");
539 if (mem_end > phys_max) {
540 warn("Some physical memory out of range for a 32-bit guest.");
541 mem_end = phys_max;
542 }
543 }
544
545 fatal_if(mem_start + stackSize >= mem_end,
546 "Physical memory too small to fit desired stack and a heap.");
547
548 heap_base = mem_start;
549 heap_limit = mem_end - stackSize + 1;
550 stack_base = (mem_end + 1) & ~0x7ULL; // 8 byte stack alignment
551 stack_limit = heap_limit;
552
553 inform("Reporting heap/stack info to guest:\n"
554 "\tHeap base: 0x%x\n"
555 "\tHeap limit: 0x%x\n"
556 "\tStack base: 0x%x\n"
557 "\tStack limit: 0x%x\n",
558 heap_base, heap_limit, stack_base, stack_limit);
559 }
560
561 ArmSemihosting::RetErrno
562 ArmSemihosting::callHeapInfo32(ThreadContext *tc, Addr block_addr)
563 {
564 uint64_t heap_base, heap_limit, stack_base, stack_limit;
565 gatherHeapInfo(tc, false, heap_base, heap_limit, stack_base, stack_limit);
566
567 std::array<uint32_t, 4> block = {
568 (uint32_t)heap_base, (uint32_t)heap_limit,
569 (uint32_t)stack_base, (uint32_t)stack_limit
570 };
571 physProxy(tc).write(block_addr, block, ArmISA::byteOrder(tc));
572
573 return retOK(0);
574 }
575
576 ArmSemihosting::RetErrno
577 ArmSemihosting::callHeapInfo64(ThreadContext *tc, Addr block_addr)
578 {
579 uint64_t heap_base, heap_limit, stack_base, stack_limit;
580 gatherHeapInfo(tc, true, heap_base, heap_limit, stack_base, stack_limit);
581
582 std::array<uint64_t, 4> block = {
583 heap_base, heap_limit, stack_base, stack_limit
584 };
585 physProxy(tc).write(block_addr, block, ArmISA::byteOrder(tc));
586
587 return retOK(0);
588 }
589
590 ArmSemihosting::RetErrno
591 ArmSemihosting::callExit32(ThreadContext *tc, InPlaceArg code)
592 {
593 semiExit(code.addr, 0);
594 return retOK(0);
595 }
596
597 ArmSemihosting::RetErrno
598 ArmSemihosting::callExit64(ThreadContext *tc, uint64_t code, uint64_t subcode)
599 {
600 semiExit(code, subcode);
601 return retOK(0);
602 }
603
604 ArmSemihosting::RetErrno
605 ArmSemihosting::callExitExtended(ThreadContext *tc,
606 uint64_t code, uint64_t subcode)
607 {
608 semiExit(code, subcode);
609 return retOK(0);
610 }
611
612 void
613 ArmSemihosting::semiExit(uint64_t code, uint64_t subcode)
614 {
615 auto it = exitCodes.find(code);
616 if (it != exitCodes.end()) {
617 exitSimLoop(it->second, subcode);
618 } else {
619 exitSimLoop(csprintf("semi:0x%x", code), subcode);
620 }
621 }
622
623
624 ArmSemihosting::RetErrno
625 ArmSemihosting::callElapsed32(ThreadContext *tc, InPlaceArg low,
626 InPlaceArg high)
627 {
628 ByteOrder endian = ArmISA::byteOrder(tc);
629 uint64_t tick = semiTick(curTick());
630
631 low.write(tc, tick, endian);
632 high.write(tc, tick >> 32, endian);
633
634 return retOK(0);
635 }
636
637
638 ArmSemihosting::RetErrno
639 ArmSemihosting::callElapsed64(ThreadContext *tc, InPlaceArg ticks)
640 {
641 ticks.write(tc, semiTick(curTick()), ArmISA::byteOrder(tc));
642 return retOK(0);
643 }
644
645
646 ArmSemihosting::RetErrno
647 ArmSemihosting::callTickFreq(ThreadContext *tc)
648 {
649 return retOK(semiTick(SimClock::Frequency));
650 }
651
652 FILE *
653 ArmSemihosting::getSTDIO(const char *stream_name,
654 const std::string &name, const char *mode)
655 {
656 auto it = stdioMap.find(name);
657 if (it == stdioMap.end()) {
658 FILE *f = fopen(name.c_str(), mode);
659 if (!f) {
660 fatal("Failed to open %s (%s): %s\n",
661 stream_name, name, strerror(errno));
662 }
663 return f;
664 } else {
665 return it->second;
666 }
667 }
668
669 std::unique_ptr<ArmSemihosting::FileBase>
670 ArmSemihosting::FileBase::create(
671 ArmSemihosting &parent, const std::string &fname, const char *mode)
672 {
673 std::unique_ptr<FileBase> file;
674 if (fname == ":semihosting-features") {
675 file.reset(new FileFeatures(parent, fname.c_str(), mode));
676 } else {
677 file.reset(new File(parent, fname.c_str(), mode));
678 }
679
680 return file;
681 }
682
683 std::unique_ptr<ArmSemihosting::FileBase>
684 ArmSemihosting::FileBase::create(ArmSemihosting &parent,
685 CheckpointIn &cp, const std::string &sec)
686 {
687 std::unique_ptr<FileBase> file;
688 ScopedCheckpointSection _sec(cp, sec);
689
690 // Was the file open when the checkpoint was created?
691 if (!cp.sectionExists(Serializable::currentSection()))
692 return file;
693
694 std::string fname, mode;
695 paramIn(cp, "name", fname);
696 paramIn(cp, "mode", mode);
697 file = create(parent, fname, mode.c_str());
698 assert(file);
699 file->unserialize(cp);
700
701 return file;
702 }
703
704 void
705 ArmSemihosting::FileBase::serialize(CheckpointOut &cp) const
706 {
707 paramOut(cp, "name", _name);
708 SERIALIZE_SCALAR(mode);
709 }
710
711 void
712 ArmSemihosting::FileBase::unserialize(CheckpointIn &cp)
713 {
714 /* Unserialization of name and mode happens in
715 * ArmSemihosting::FileBase::create() */
716 }
717
718 int64_t
719 ArmSemihosting::FileBase::read(uint8_t *buffer, uint64_t size)
720 {
721 return -EINVAL;
722 }
723
724 int64_t
725 ArmSemihosting::FileBase::write(const uint8_t *buffer, uint64_t size)
726 {
727 return -EINVAL;
728 }
729
730 int64_t
731 ArmSemihosting::FileBase::seek(uint64_t pos)
732 {
733 return -EINVAL;
734 }
735
736 int64_t
737 ArmSemihosting::FileBase::flen()
738 {
739 return -EINVAL;
740 }
741
742
743 ArmSemihosting::FileFeatures::FileFeatures(
744 ArmSemihosting &_parent, const char *_name, const char *_mode)
745 : FileBase(_parent, _name, _mode)
746 {
747 }
748
749 int64_t
750 ArmSemihosting::FileFeatures::read(uint8_t *buffer, uint64_t size)
751 {
752 int64_t len = 0;
753
754 for (; pos < size && pos < ArmSemihosting::features.size(); pos++)
755 buffer[len++] = ArmSemihosting::features[pos];
756
757 return len;
758 }
759
760 int64_t
761 ArmSemihosting::FileFeatures::seek(uint64_t _pos)
762 {
763 if (_pos < ArmSemihosting::features.size()) {
764 pos = _pos;
765 return 0;
766 } else {
767 return -ENXIO;
768 }
769 }
770
771 void
772 ArmSemihosting::FileFeatures::serialize(CheckpointOut &cp) const
773 {
774 FileBase::serialize(cp);
775 SERIALIZE_SCALAR(pos);
776 }
777
778 void
779 ArmSemihosting::FileFeatures::unserialize(CheckpointIn &cp)
780 {
781 FileBase::unserialize(cp);
782 UNSERIALIZE_SCALAR(pos);
783 }
784
785
786
787 ArmSemihosting::File::File(ArmSemihosting &_parent,
788 const char *_name, const char *_perms)
789 : FileBase(_parent, _name, _perms),
790 file(nullptr)
791 {
792 }
793
794 ArmSemihosting::File::~File()
795 {
796 if (file)
797 close();
798 }
799
800 int64_t
801 ArmSemihosting::File::openImpl(bool in_cpt)
802 {
803 panic_if(file, "Trying to open an already open file.\n");
804
805 if (_name == ":tt") {
806 if (mode[0] == 'r') {
807 file = parent.stdin;
808 } else if (mode[0] == 'w') {
809 file = parent.stdout;
810 } else if (mode[0] == 'a') {
811 file = parent.stderr;
812 } else {
813 warn("Unknown file mode for the ':tt' special file");
814 return -EINVAL;
815 }
816 } else {
817 std::string real_mode(this->mode);
818 // Avoid truncating the file if we are restoring from a
819 // checkpoint.
820 if (in_cpt && real_mode[0] == 'w')
821 real_mode[0] = 'a';
822
823 file = fopen(_name.c_str(), real_mode.c_str());
824 }
825
826 return file ? 0 : -errno;
827 }
828
829 int64_t
830 ArmSemihosting::File::close()
831 {
832 panic_if(!file, "Trying to close an already closed file.\n");
833
834 if (needClose()) {
835 fclose(file);
836 }
837 file = nullptr;
838
839 return 0;
840 }
841
842 bool
843 ArmSemihosting::File::isTTY() const
844 {
845 return file == parent.stdout ||
846 file == parent.stderr ||
847 file == parent.stdin;
848 }
849
850 int64_t
851 ArmSemihosting::File::read(uint8_t *buffer, uint64_t size)
852 {
853 panic_if(!file, "Trying to read from a closed file");
854
855 size_t ret = fread(buffer, 1, size, file);
856 if (ret == 0) {
857 // Error or EOF. Assume errors are due to invalid file
858 // operations (e.g., reading a write-only stream).
859 return ferror(file) ? -EINVAL : 0;
860 } else {
861 return ret;
862 }
863 }
864
865 int64_t
866 ArmSemihosting::File::write(const uint8_t *buffer, uint64_t size)
867 {
868 panic_if(!file, "Trying to write to a closed file");
869
870
871 size_t ret = fwrite(buffer, 1, size, file);
872 if (ret == 0) {
873 // Assume errors are due to invalid file operations (e.g.,
874 // writing a read-only stream).
875 return -EINVAL;
876 } else {
877 return ret;
878 }
879 }
880
881 int64_t
882 ArmSemihosting::File::seek(uint64_t _pos)
883 {
884 panic_if(!file, "Trying to seek in a closed file");
885
886 errno = 0;
887 if (fseek(file, _pos, SEEK_SET) == 0)
888 return 0;
889 else
890 return -errno;
891 }
892
893 int64_t
894 ArmSemihosting::File::flen()
895 {
896 errno = 0;
897 long pos = ftell(file);
898 if (pos < 0)
899 return -errno;
900
901 if (fseek(file, 0, SEEK_END) != 0)
902 return -errno;
903
904 long len = ftell(file);
905 if (len < 0)
906 return -errno;
907
908 if (fseek(file, pos, SEEK_SET) != 0)
909 return -errno;
910
911 return len;
912 }
913
914
915 void
916 ArmSemihosting::File::serialize(CheckpointOut &cp) const
917 {
918 FileBase::serialize(cp);
919
920 if (!isTTY()) {
921 long pos = file ? ftell(file) : 0;
922 panic_if(pos < 0, "Failed to get file position.");
923 SERIALIZE_SCALAR(pos);
924 }
925 }
926
927 void
928 ArmSemihosting::File::unserialize(CheckpointIn &cp)
929 {
930 FileBase::unserialize(cp);
931
932 if (openImpl(true) < 0) {
933 fatal("Failed to open file: %s", _name);
934 }
935
936 if (!isTTY()) {
937 long pos = 0;
938 UNSERIALIZE_SCALAR(pos);
939 if (fseek(file, pos, SEEK_SET) != 0) {
940 fatal("Failed seek to current position (%i) in '%s'", pos, _name);
941 }
942 }
943 }
944
945 std::ostream &
946 operator << (std::ostream &os, const ArmSemihosting::InPlaceArg &ipa)
947 {
948 ccprintf(os, "[%#x-%#x)", ipa.addr, ipa.addr + ipa.size - 1);
949 return os;
950 }
951
952
953 ArmSemihosting *
954 ArmSemihostingParams::create()
955 {
956 return new ArmSemihosting(this);
957 }