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