misc,sim: Fixed std::array bracket compiler error
[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 // Ignore return values since those will be handled by semihosting.
700 template <typename T>
701 struct Result<SemiPseudoAbi32, T>
702 {
703 static void store(ThreadContext *tc, const T &ret) {}
704 };
705 template <typename T>
706 struct Result<SemiPseudoAbi64, T>
707 {
708 static void store(ThreadContext *tc, const T &ret) {}
709 };
710
711 // Handle arguments the same as for semihosting operations. Skipping the first
712 // slot is handled internally by the State type.
713 template <typename T>
714 struct Argument<SemiPseudoAbi32, T> :
715 public Argument<ArmSemihosting::Abi32, T>
716 {};
717 template <typename T>
718 struct Argument<SemiPseudoAbi64, T> :
719 public Argument<ArmSemihosting::Abi64, T>
720 {};
721
722 } // namespace GuestABI
723
724 ArmSemihosting::RetErrno
725 ArmSemihosting::callGem5PseudoOp32(ThreadContext *tc, uint32_t encoded_func)
726 {
727 uint8_t func;
728 PseudoInst::decodeAddrOffset(encoded_func, func);
729
730 uint64_t ret;
731 if (PseudoInst::pseudoInst<SemiPseudoAbi32>(tc, func, ret))
732 return retOK(ret);
733 else
734 return retError(EINVAL);
735 }
736
737 ArmSemihosting::RetErrno
738 ArmSemihosting::callGem5PseudoOp64(ThreadContext *tc, uint64_t encoded_func)
739 {
740 uint8_t func;
741 PseudoInst::decodeAddrOffset(encoded_func, func);
742
743 uint64_t ret;
744 if (PseudoInst::pseudoInst<SemiPseudoAbi64>(tc, func, ret))
745 return retOK(ret);
746 else
747 return retError(EINVAL);
748 }
749
750 FILE *
751 ArmSemihosting::getSTDIO(const char *stream_name,
752 const std::string &name, const char *mode)
753 {
754 auto it = stdioMap.find(name);
755 if (it == stdioMap.end()) {
756 FILE *f = fopen(name.c_str(), mode);
757 if (!f) {
758 fatal("Failed to open %s (%s): %s\n",
759 stream_name, name, strerror(errno));
760 }
761 return f;
762 } else {
763 return it->second;
764 }
765 }
766
767 std::unique_ptr<ArmSemihosting::FileBase>
768 ArmSemihosting::FileBase::create(
769 ArmSemihosting &parent, const std::string &fname, const char *mode)
770 {
771 std::unique_ptr<FileBase> file;
772 if (fname == ":semihosting-features") {
773 file.reset(new FileFeatures(parent, fname.c_str(), mode));
774 } else {
775 file.reset(new File(parent, fname.c_str(), mode));
776 }
777
778 return file;
779 }
780
781 std::unique_ptr<ArmSemihosting::FileBase>
782 ArmSemihosting::FileBase::create(ArmSemihosting &parent,
783 CheckpointIn &cp, const std::string &sec)
784 {
785 std::unique_ptr<FileBase> file;
786 ScopedCheckpointSection _sec(cp, sec);
787
788 // Was the file open when the checkpoint was created?
789 if (!cp.sectionExists(Serializable::currentSection()))
790 return file;
791
792 std::string fname, mode;
793 paramIn(cp, "name", fname);
794 paramIn(cp, "mode", mode);
795 file = create(parent, fname, mode.c_str());
796 assert(file);
797 file->unserialize(cp);
798
799 return file;
800 }
801
802 void
803 ArmSemihosting::FileBase::serialize(CheckpointOut &cp) const
804 {
805 paramOut(cp, "name", _name);
806 SERIALIZE_SCALAR(mode);
807 }
808
809 void
810 ArmSemihosting::FileBase::unserialize(CheckpointIn &cp)
811 {
812 /* Unserialization of name and mode happens in
813 * ArmSemihosting::FileBase::create() */
814 }
815
816 int64_t
817 ArmSemihosting::FileBase::read(uint8_t *buffer, uint64_t size)
818 {
819 return -EINVAL;
820 }
821
822 int64_t
823 ArmSemihosting::FileBase::write(const uint8_t *buffer, uint64_t size)
824 {
825 return -EINVAL;
826 }
827
828 int64_t
829 ArmSemihosting::FileBase::seek(uint64_t pos)
830 {
831 return -EINVAL;
832 }
833
834 int64_t
835 ArmSemihosting::FileBase::flen()
836 {
837 return -EINVAL;
838 }
839
840
841 ArmSemihosting::FileFeatures::FileFeatures(
842 ArmSemihosting &_parent, const char *_name, const char *_mode)
843 : FileBase(_parent, _name, _mode)
844 {
845 }
846
847 int64_t
848 ArmSemihosting::FileFeatures::read(uint8_t *buffer, uint64_t size)
849 {
850 int64_t len = 0;
851
852 for (; pos < size && pos < ArmSemihosting::features.size(); pos++)
853 buffer[len++] = ArmSemihosting::features[pos];
854
855 return len;
856 }
857
858 int64_t
859 ArmSemihosting::FileFeatures::seek(uint64_t _pos)
860 {
861 if (_pos < ArmSemihosting::features.size()) {
862 pos = _pos;
863 return 0;
864 } else {
865 return -ENXIO;
866 }
867 }
868
869 void
870 ArmSemihosting::FileFeatures::serialize(CheckpointOut &cp) const
871 {
872 FileBase::serialize(cp);
873 SERIALIZE_SCALAR(pos);
874 }
875
876 void
877 ArmSemihosting::FileFeatures::unserialize(CheckpointIn &cp)
878 {
879 FileBase::unserialize(cp);
880 UNSERIALIZE_SCALAR(pos);
881 }
882
883
884
885 ArmSemihosting::File::File(ArmSemihosting &_parent,
886 const char *_name, const char *_perms)
887 : FileBase(_parent, _name, _perms),
888 file(nullptr)
889 {
890 }
891
892 ArmSemihosting::File::~File()
893 {
894 if (file)
895 close();
896 }
897
898 int64_t
899 ArmSemihosting::File::openImpl(bool in_cpt)
900 {
901 panic_if(file, "Trying to open an already open file.\n");
902
903 if (_name == ":tt") {
904 if (mode[0] == 'r') {
905 file = parent.stdin;
906 } else if (mode[0] == 'w') {
907 file = parent.stdout;
908 } else if (mode[0] == 'a') {
909 file = parent.stderr;
910 } else {
911 warn("Unknown file mode for the ':tt' special file");
912 return -EINVAL;
913 }
914 } else {
915 std::string real_mode(this->mode);
916 // Avoid truncating the file if we are restoring from a
917 // checkpoint.
918 if (in_cpt && real_mode[0] == 'w')
919 real_mode[0] = 'a';
920
921 file = fopen(_name.c_str(), real_mode.c_str());
922 }
923
924 return file ? 0 : -errno;
925 }
926
927 int64_t
928 ArmSemihosting::File::close()
929 {
930 panic_if(!file, "Trying to close an already closed file.\n");
931
932 if (needClose()) {
933 fclose(file);
934 }
935 file = nullptr;
936
937 return 0;
938 }
939
940 bool
941 ArmSemihosting::File::isTTY() const
942 {
943 return file == parent.stdout ||
944 file == parent.stderr ||
945 file == parent.stdin;
946 }
947
948 int64_t
949 ArmSemihosting::File::read(uint8_t *buffer, uint64_t size)
950 {
951 panic_if(!file, "Trying to read from a closed file");
952
953 size_t ret = fread(buffer, 1, size, file);
954 if (ret == 0) {
955 // Error or EOF. Assume errors are due to invalid file
956 // operations (e.g., reading a write-only stream).
957 return ferror(file) ? -EINVAL : 0;
958 } else {
959 return ret;
960 }
961 }
962
963 int64_t
964 ArmSemihosting::File::write(const uint8_t *buffer, uint64_t size)
965 {
966 panic_if(!file, "Trying to write to a closed file");
967
968
969 size_t ret = fwrite(buffer, 1, size, file);
970 if (ret == 0) {
971 // Assume errors are due to invalid file operations (e.g.,
972 // writing a read-only stream).
973 return -EINVAL;
974 } else {
975 return ret;
976 }
977 }
978
979 int64_t
980 ArmSemihosting::File::seek(uint64_t _pos)
981 {
982 panic_if(!file, "Trying to seek in a closed file");
983
984 errno = 0;
985 if (fseek(file, _pos, SEEK_SET) == 0)
986 return 0;
987 else
988 return -errno;
989 }
990
991 int64_t
992 ArmSemihosting::File::flen()
993 {
994 errno = 0;
995 long pos = ftell(file);
996 if (pos < 0)
997 return -errno;
998
999 if (fseek(file, 0, SEEK_END) != 0)
1000 return -errno;
1001
1002 long len = ftell(file);
1003 if (len < 0)
1004 return -errno;
1005
1006 if (fseek(file, pos, SEEK_SET) != 0)
1007 return -errno;
1008
1009 return len;
1010 }
1011
1012
1013 void
1014 ArmSemihosting::File::serialize(CheckpointOut &cp) const
1015 {
1016 FileBase::serialize(cp);
1017
1018 if (!isTTY()) {
1019 long pos = file ? ftell(file) : 0;
1020 panic_if(pos < 0, "Failed to get file position.");
1021 SERIALIZE_SCALAR(pos);
1022 }
1023 }
1024
1025 void
1026 ArmSemihosting::File::unserialize(CheckpointIn &cp)
1027 {
1028 FileBase::unserialize(cp);
1029
1030 if (openImpl(true) < 0) {
1031 fatal("Failed to open file: %s", _name);
1032 }
1033
1034 if (!isTTY()) {
1035 long pos = 0;
1036 UNSERIALIZE_SCALAR(pos);
1037 if (fseek(file, pos, SEEK_SET) != 0) {
1038 fatal("Failed seek to current position (%i) in '%s'", pos, _name);
1039 }
1040 }
1041 }
1042
1043 std::ostream &
1044 operator << (std::ostream &os, const ArmSemihosting::InPlaceArg &ipa)
1045 {
1046 ccprintf(os, "[%#x-%#x)", ipa.addr, ipa.addr + ipa.size - 1);
1047 return os;
1048 }
1049
1050
1051 ArmSemihosting *
1052 ArmSemihostingParams::create()
1053 {
1054 return new ArmSemihosting(this);
1055 }