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