ETISS 0.8.0
Extendable Translating Instruction Set Simulator (version 0.8.0)
semihost.cpp
Go to the documentation of this file.
1 #include <cstdio>
2 #include "etiss/ETISS.h"
3 
4 #include "SemihostingCalls.h"
5 
6 extern "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
15 const 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 
38 etiss_int64 semihostingCall(ETISS_CPU *const cpu, ETISS_System *const etissSystem, etiss_uint32 XLEN,
39  etiss_uint64 operationNumber, etiss_uint64 parameter);
40 
41 extern "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 
91 std::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 
100 void 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 
114 void 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 
119 bool is_std_in_out_err(FILE *file)
120 {
121  return file == stdin || file == stdout || file == stderr;
122 }
123 
124 etiss_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:
424  case SYS_EXIT_EXTENDED:
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(const std::string &cfgName)
Get reference of the global ETISS configuration object.
Definition: Misc.cpp:560
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
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
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
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