ETISS 0.11.2
ExtendableTranslatingInstructionSetSimulator(version0.11.2)
Loading...
Searching...
No Matches
semihost.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: BSD-3-Clause
2//
3// This file is part of ETISS. It is licensed under the BSD 3-Clause License; you may not use this file except in
4// compliance with the License. You should have received a copy of the license along with this project. If not, see the
5// LICENSE file.
6
7#include <cstdio>
8#include "etiss/ETISS.h"
9#include <iomanip>
10
11#include "SemihostingCalls.h"
12#include "etiss/jit/ReturnCode.h"
13
14extern "C"
15{
16#include "libsemihost.h"
17}
18
19// constant for SYS_ELAPSED and SYS_TICKFREQ
20#define TICKER_FREQ 1000 // Hz
21
22// constants for SYS_OPEN
23const char *SYS_OPEN_MODES_STRS[] = { "r", "rb", "r+", "r+b", "w", "wb", "w+", "w+b", "a", "ab", "a+", "a+b" };
24#define SYS_OPEN_MODES_TOTAL 12 // total number of modes
25#define SYS_OPEN_MODES_IN_LIMIT 4
26#define SYS_OPEN_MODES_OUT_LIMIT 8
27
28#define PS_PER_CS 10000000000 // 10 * 10^9
29
30// if var is negative set semihosting errno and return -1
31#define CHECK_NEGATIVE_RETURN(var) \
32 if ((var) < 0) \
33 { \
34 semihostingErrno = errno; \
35 return -1; \
36 }
37
38// For many semihosting calls parameter points to a data block, so this type of call is very common
39#define FIELD(fieldNo) semihostReadStructField(etissSystem, XLEN / 8, parameter, fieldNo);
40
41// forward declaration for use in extern block:
42
46etiss_int64 semihostingCall(ETISS_CPU *const cpu, ETISS_System *const etissSystem, etiss_uint32 XLEN,
47 etiss_uint64 operationNumber, etiss_uint64 parameter);
48
49extern "C"
50{
52 {
53 return etiss::cfg().get<bool>("arch.enable_semihosting", false);
54 }
55
56 int64_t etiss_semihost(ETISS_CPU *const cpu, ETISS_System *const etissSystem, void *const *const _, uint32_t XLEN,
57 uint64_t operation, uint64_t parameter)
58 {
59 return semihostingCall(cpu, etissSystem, XLEN, operation, parameter);
60 }
61}
62
67 int fieldNo)
68{
69 if (numBytes == 8)
70 {
71 etiss_uint64 field = 0;
72 etissSystem->dbg_read(etissSystem->handle, address + 8 * fieldNo, (etiss_uint8 *)&field, 8);
73 return field;
74 }
75 else if (numBytes == 4)
76 {
77 etiss_uint32 field = 0;
78 etissSystem->dbg_read(etissSystem->handle, address + 4 * fieldNo, (etiss_uint8 *)&field, 4);
79 return field;
80 }
81 else if (numBytes == 2)
82 {
83 etiss_uint16 field = 0;
84 etissSystem->dbg_read(etissSystem->handle, address + 2 * fieldNo, (etiss_uint8 *)&field, 2);
85 return field;
86 }
87 else if (numBytes == 1)
88 {
89 etiss_uint8 field = 0;
90 etissSystem->dbg_read(etissSystem->handle, address + 1 * fieldNo, &field, 1);
91 return field;
92 }
93
94 etiss::log(etiss::ERROR, "semihostReadStructField called with numBytes != 1, 2, 4 or 8");
95 return 0;
96}
97
99std::vector<etiss_uint8> semihostReadSystemMemory(ETISS_System *etissSystem, etiss_uint64 address, etiss_uint64 length)
100{
101 std::vector<etiss_uint8> buffer;
102 buffer.resize(length);
103 etissSystem->dbg_read(etissSystem->handle, address, buffer.data(), length);
104 return buffer;
105}
106
108void semihostWriteSystemMemory(ETISS_System *etissSystem, etiss_uint64 address, std::vector<etiss_uint8> data)
109{
110 etissSystem->dbg_write(etissSystem->handle, address, data.data(), data.size());
111}
112
115{
116 std::vector<etiss_uint8> buffer = semihostReadSystemMemory(etissSystem, address, length);
117 std::string str(buffer.begin(), buffer.end());
118 return str;
119}
120
122void semihostWriteString(ETISS_System *etissSystem, etiss_uint64 address, std::string str)
123{
124 etissSystem->dbg_write(etissSystem->handle, address, (etiss_uint8 *)str.c_str(), str.length() + 1);
125}
126
127bool is_std_in_out_err(FILE *file)
128{
129 return file == stdin || file == stdout || file == stderr;
130}
131
132etiss_int64 semihostingCall(ETISS_CPU *const cpu, ETISS_System *const etissSystem, etiss_uint32 XLEN,
133 etiss_uint64 operationNumber, etiss_uint64 parameter)
134{
135 // static variables to keep track of semihosting state
136
139 static std::map<etiss_uint64, FILE *> openFiles;
142 static etiss_uint64 nextFd = 0;
145 static etiss_int64 semihostingErrno;
146
147 switch (operationNumber)
148 {
149 // share code between all operations that have the
150 // file descriptor as their first argument
151 case SYS_CLOSE:
152 case SYS_WRITE:
153 case SYS_READ:
154 case SYS_ISTTY:
155 case SYS_SEEK:
156 case SYS_FLEN:
157 {
158 etiss_uint64 fd = FIELD(0);
159 if (openFiles.count(fd) == 0)
160 {
161 std::stringstream ss;
162 ss << "Semihosting: invalid file descriptor " << fd << " for semihosting call 0x" << std::hex
163 << std::setfill('0') << std::setw(2) << operationNumber;
164 etiss::log(etiss::INFO, ss.str());
165 semihostingErrno = EBADF;
166 return -1;
167 }
168 auto file = openFiles[fd];
169
170 switch (operationNumber)
171 {
172 case SYS_CLOSE:
173 {
174 std::stringstream ss;
175 ss << "Semihosting: SYS_CLOSE fd " << fd;
176 etiss::log(etiss::VERBOSE, ss.str());
177
178 openFiles.erase(fd);
179 // do not close stdin, stdout, stderr of host process
180 if (!is_std_in_out_err(file))
181 fclose(file);
182 return 0;
183 }
184 case SYS_WRITE:
185 {
186 etiss_uint64 address = FIELD(1);
187 etiss_uint64 count = FIELD(2);
188
189 std::stringstream ss;
190 ss << "Semihosting: SYS_WRITE fd " << fd;
191 etiss::log(etiss::VERBOSE, ss.str());
192
193 std::vector<etiss_uint8> buffer = semihostReadSystemMemory(etissSystem, address, count);
194
195 size_t num_written = fwrite(buffer.data(), 1, count, file);
196 return count - num_written;
197 }
198 case SYS_READ:
199 {
200 etiss_uint64 address = FIELD(1);
201 etiss_uint64 count = FIELD(2);
202
203 std::stringstream ss;
204 ss << "Semihosting: SYS_READ fd " << fd << " count " << count;
205 etiss::log(etiss::VERBOSE, ss.str());
206
207 std::vector<etiss_uint8> buffer;
208 buffer.resize(count);
209
210 size_t num_read = 0;
211 if (file == stdin)
212 {
213 // when reading from stdin: mimic behaviour from read syscall
214 // and return on newline.
215 while (num_read < count)
216 {
217 char c = fgetc(file);
218 buffer[num_read] = c;
219 num_read++;
220 if (c == '\n')
221 break;
222 }
223 }
224 else
225 {
226 num_read = fread(buffer.data(), 1, count, file);
227 }
228
229 buffer.resize(num_read);
230 semihostWriteSystemMemory(etissSystem, address, buffer);
231
232 return (etiss_int64)count - num_read;
233 }
234 case SYS_ISTTY:
235 {
236 std::stringstream ss;
237 ss << "Semihosting: SYS_ISTTY fd " << fd;
238 etiss::log(etiss::VERBOSE, ss.str());
239
240 return is_std_in_out_err(file);
241 }
242 case SYS_SEEK:
243 {
244 etiss_uint64 position = FIELD(1);
245
246 std::stringstream ss;
247 ss << "Semihosting: SYS_SEEK fd " << fd << ": " << position;
248 etiss::log(etiss::VERBOSE, ss.str());
249
250 int retval = fseek(file, position, SEEK_SET);
251 CHECK_NEGATIVE_RETURN(retval);
252 return 0;
253 }
254 case SYS_FLEN:
255 {
256 std::stringstream ss;
257 ss << "Semihosting: SYS_FLEN fd " << fd;
258 etiss::log(etiss::VERBOSE, ss.str());
259
260 size_t currentPos = ftell(file);
261 CHECK_NEGATIVE_RETURN(currentPos);
262 fseek(file, 0, SEEK_END);
263 size_t length = ftell(file);
264 fseek(file, currentPos, SEEK_SET);
265 return (etiss_int64)length;
266 }
267 }
268 }
269 case SYS_OPEN:
270 {
271 etiss_uint64 path_str_addr = FIELD(0);
272 etiss_uint64 mode = FIELD(1);
273 etiss_uint64 path_str_len = FIELD(2);
274
275 std::string path_str = semihostReadString(etissSystem, path_str_addr, path_str_len);
276
277 std::stringstream ss;
278 ss << "Semihosting: SYS_OPEN \"" << path_str << "\"";
279 etiss::log(etiss::VERBOSE, ss.str());
280
281 if (mode >= SYS_OPEN_MODES_TOTAL)
282 {
283 // invalid mode
284 semihostingErrno = EINVAL;
285 return -1;
286 }
287
288 FILE *file = nullptr;
289 if (path_str == ":tt")
290 {
291 // special file path for opening stdin, stdout and stderr
292 // open stdin, stdout or stderr depending on mode argument
293 if (mode < SYS_OPEN_MODES_IN_LIMIT) // 0 <= mode <= 3
294 file = stdin;
295 else if (mode < SYS_OPEN_MODES_OUT_LIMIT) // 4 <= mode <= 7
296 file = stdout;
297 else // 8 <= mode <= 11
298 file = stderr;
299 }
300 else
301 {
302 file = fopen(path_str.c_str(), SYS_OPEN_MODES_STRS[mode]);
303 if (file == nullptr)
304 {
305 semihostingErrno = errno;
306 return -1;
307 }
308 }
309 etiss_uint64 fd = nextFd++;
310 openFiles[fd] = file;
311
312 return (etiss_int64)fd;
313 }
314 case SYS_WRITEC:
315 {
316 etiss_uint64 character = semihostReadStructField(etissSystem, 1, parameter, 0);
317 putchar(character);
318 return 0;
319 }
320 case SYS_WRITE0:
321 {
322 etiss_uint64 address = parameter;
323 while (1)
324 {
325 etiss_uint64 character = semihostReadStructField(etissSystem, 1, address, 0);
326 if (character == 0)
327 break;
328 putchar(character);
329 address++;
330 }
331 return 0;
332 }
333 case SYS_READC:
334 {
335 return getchar();
336 }
337 case SYS_ISERROR:
338 {
339 etiss_uint64 value = FIELD(0);
340 return value != 0;
341 }
342 case SYS_TMPNAM:
343 {
344 etiss_uint64 buffer_address = FIELD(0);
345 etiss_uint64 identifier = FIELD(1);
346 etiss_uint64 buffer_len = FIELD(2);
347
348 if (identifier > 255)
349 return -1;
350
351 std::stringstream ss;
352 ss << "etiss-tmp/file-" << std::setfill('0') << std::setw(3) << identifier;
353 std::string filename = ss.str();
354
355 if (buffer_len <= filename.length() + 1)
356 return -1;
357
358 semihostWriteString(etissSystem, buffer_address, filename);
359 return 0;
360 }
361 case SYS_REMOVE:
362 {
363 etiss_uint64 path_str_addr = FIELD(0);
364 etiss_uint64 path_str_len = FIELD(1);
365
366 std::string path_str = semihostReadString(etissSystem, path_str_addr, path_str_len);
367
368 std::stringstream ss;
369 ss << "Semihosting: SYS_REMOVE \"" << path_str << "\"";
370 etiss::log(etiss::VERBOSE, ss.str());
371
372 if (remove(path_str.c_str()) < 0)
373 {
374 semihostingErrno = errno;
375 return -1;
376 }
377 return 0;
378 }
379 case SYS_RENAME:
380 {
381 etiss_uint64 old_str_addr = FIELD(0);
382 etiss_uint64 old_str_len = FIELD(1);
383 etiss_uint64 new_str_addr = FIELD(2);
384 etiss_uint64 new_str_len = FIELD(3);
385
386 std::string old_str = semihostReadString(etissSystem, old_str_addr, old_str_len);
387 std::string new_str = semihostReadString(etissSystem, new_str_addr, new_str_len);
388
389 std::stringstream ss;
390 ss << "Semihosting: SYS_RENAME \"" << old_str << "\" to \"" << new_str << "\"";
391 etiss::log(etiss::VERBOSE, ss.str());
392
393 return rename(old_str.c_str(), new_str.c_str());
394 }
395 case SYS_CLOCK:
396 {
397 // return centiseconds since some arbitrary start point
398 return cpu->cpuTime_ps / PS_PER_CS;
399 }
400 case SYS_TIME:
401 {
402 etiss::log(etiss::VERBOSE, "Semihosting: SYS_TIME");
403 etiss_int64 seconds_since_epoch = (etiss_int64)std::time(0);
404 return seconds_since_epoch;
405 }
406 case SYS_ERRNO:
407 {
408 std::stringstream ss;
409 ss << "Semihosting: SYS_ERRNO (" << semihostingErrno << ")";
410 etiss::log(etiss::VERBOSE, ss.str());
411 return semihostingErrno;
412 }
413 case SYS_EXIT:
414 {
415 etiss::log(etiss::VERBOSE, "Semihosting: SYS_EXIT -> exit simulator");
416
417 cpu->exception = ETISS_RETURNCODE_CPUFINISHED;
418 cpu->return_pending = 1;
419 return 0;
420 }
421 case SYS_ELAPSED:
422 {
423 return cpu->cpuTime_ps / TICKER_FREQ;
424 }
425 case SYS_TICKFREQ:
426 {
427 return TICKER_FREQ;
428 }
429 case SYS_SYSTEM:
430 case SYS_GET_CMDLINE:
431 case SYS_HEAPINFO:
433 {
434 std::stringstream ss;
435 ss << "Semihosting: operation not implemented: " << operationNumber;
436 etiss::log(etiss::WARNING, ss.str());
437 return 0;
438 }
439 default:
440 {
441 std::stringstream ss;
442 ss << "Semihosting: unknown operation number: " << operationNumber;
443 etiss::log(etiss::WARNING, ss.str());
444 return 0;
445 }
446 }
447}
Header file of the ETISS library.
__device__ __2f16 float c
static __inline__ uint32_t
Definition arm_cde.h:25
static __inline__ uint64_t
Definition arm_cde.h:31
static __inline__ uint8_t
Definition arm_mve.h:323
#define SYS_REMOVE
#define SYS_WRITE0
#define SYS_EXIT
#define SYS_TIME
#define SYS_ISTTY
#define SYS_ERRNO
#define SYS_READC
#define SYS_WRITE
#define SYS_READ
#define SYS_OPEN
#define SYS_ISERROR
#define SYS_FLEN
#define SYS_CLOCK
#define SYS_ELAPSED
#define SYS_SEEK
#define SYS_TMPNAM
#define SYS_WRITEC
#define SYS_SYSTEM
#define SYS_RENAME
#define SYS_GET_CMDLINE
#define SYS_CLOSE
#define SYS_TICKFREQ
#define SYS_EXIT_EXTENDED
#define SYS_HEAPINFO
uint64_t etiss_uint64
Definition types.h:58
uint32_t etiss_uint32
Definition types.h:55
int64_t etiss_int64
Definition types.h:57
uint8_t etiss_uint8
Definition types.h:49
uint16_t etiss_uint16
Definition types.h:52
T get(const std::string &key, T default_, bool *default_used=0)
template function to read the value of a configuration key.
Definition Misc.h:312
@ INFO
Definition Misc.h:87
@ VERBOSE
Definition Misc.h:88
@ WARNING
Definition Misc.h:86
@ ERROR
Definition Misc.h:85
Configuration & cfg()
Definition Misc.cpp:548
void log(Verbosity level, std::string msg)
write log message at the given level.
Definition Misc.cpp:94
float __ovld __cnfn length(float p)
Return the length of vector p, i.e., sqrt(p.x2 + p.y 2 + ...)
std::string semihostReadString(ETISS_System *etissSystem, etiss_uint64 address, etiss_uint64 length)
helper for reading a std::string by address and length from an etiss system
Definition semihost.cpp:114
#define SYS_OPEN_MODES_TOTAL
Definition semihost.cpp:24
uint8_t etiss_semihost_enabled()
Checks whether semihosting is enabled in the config.
Definition semihost.cpp:51
bool is_std_in_out_err(FILE *file)
Definition semihost.cpp:127
void semihostWriteString(ETISS_System *etissSystem, etiss_uint64 address, std::string str)
helper for writing a std::string to an etiss system
Definition semihost.cpp:122
#define TICKER_FREQ
Definition semihost.cpp:20
#define SYS_OPEN_MODES_OUT_LIMIT
Definition semihost.cpp:26
#define SYS_OPEN_MODES_IN_LIMIT
Definition semihost.cpp:25
etiss_int64 semihostingCall(ETISS_CPU *const cpu, ETISS_System *const etissSystem, etiss_uint32 XLEN, etiss_uint64 operationNumber, etiss_uint64 parameter)
Executes the semihosting call based on the operation number.
Definition semihost.cpp:132
#define CHECK_NEGATIVE_RETURN(var)
Definition semihost.cpp:31
#define FIELD(fieldNo)
Definition semihost.cpp:39
int64_t etiss_semihost(ETISS_CPU *const cpu, ETISS_System *const etissSystem, void *const *const _, uint32_t XLEN, uint64_t operation, uint64_t parameter)
Executes the semihosting call based on the operation number.
Definition semihost.cpp:56
const char * SYS_OPEN_MODES_STRS[]
Definition semihost.cpp:23
void semihostWriteSystemMemory(ETISS_System *etissSystem, etiss_uint64 address, std::vector< etiss_uint8 > data)
helper for writing a std::vector of bytes to an etiss system
Definition semihost.cpp:108
#define PS_PER_CS
Definition semihost.cpp:28
std::vector< etiss_uint8 > semihostReadSystemMemory(ETISS_System *etissSystem, etiss_uint64 address, etiss_uint64 length)
helper for reading a std::vector of bytes by address and length from an etiss system
Definition semihost.cpp:99
etiss_uint64 semihostReadStructField(ETISS_System *etissSystem, etiss_uint32 numBytes, etiss_uint64 address, int fieldNo)
Assumes there is an array of numBytes long integers at address.
Definition semihost.cpp:66
basic cpu state structure needed for execution of any cpu architecture.
Definition CPU.h:51
etiss_uint32 exception
Definition CPU.h:73
etiss_uint64 cpuTime_ps
simulation time of cpu
Definition CPU.h:59
etiss_uint32 return_pending
Definition CPU.h:74
memory access and time synchronization functions.
Definition System.h:40
void * handle
custom handle that will be passed to the functions of this structure
Definition System.h:78
etiss_int32(* dbg_write)(void *handle, etiss_uint64 addr, etiss_uint8 *buffer, etiss_uint32 length)
direct debug write
Definition System.h:70
etiss_int32(* dbg_read)(void *handle, etiss_uint64 addr, etiss_uint8 *buffer, etiss_uint32 length)
direct debug read
Definition System.h:66