ARMEBS4  revision-26.06.2015
retarget_semihosting.c
Go to the documentation of this file.
1 /************************************************************************//**
2  * \file heivs/retarget_semihosting.c
3  * \author marc dot pignat at hevs dot ch
4  *
5  * \defgroup stdio_redir stdio redirection
6  * \ingroup stdio_h
7  * @{
8  *
9  * \brief stdio redirection for newlib
10  *
11  * Here is the low-level part of stdio (printf/scanf, puts, getc, ...) functions
12  *
13  * \see ARM DUI 0203J for the specification
14  *
15  ***************************************************************************/
16 
17 #include "heivs/config.h"
18 #if !defined(USE_STDIO_USART)
19 
20 #include "heivs/retarget.h"
21 #include "heivs/stm32_usart.h"
22 #include "heivs/config.h"
23 #include <stdio.h>
24 #include <unistd.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <errno.h>
28 
29 static uint32_t use_stdio_nobuf = USE_STDIO_NOBUF;
30 static uint32_t use_stdio_echo = USE_STDIO_ECHO;
31 
32 void retarget_init(void)
33 {
34  if (use_stdio_nobuf)
35  {
36  setvbuf(stdout, NULL, _IONBF, 0);
37  }
38 }
39 
40 /**
41  * \brief Semihosting communication
42  * \param syscall the semihosting system call
43  * \param *param the parameter
44  *
45  */
46 static inline uint32_t _semi(uint32_t syscall, void *param)
47 {
48  uint32_t result;
49 
50  if (!debugger_is_connected())
51  {
52  return -1;
53  }
54 
55  asm volatile
56  (
57  // Set the syscall # in r0
58  "mov r0, %1\n"
59 
60  // Set the parameter in r1
61  "mov r1, %2\n"
62 
63  // Call the debugger
64  "bkpt 0xab\n"
65 
66  // Get the result of the operation
67  "mov %0, r0\n"
68  // Output
69  : "=r" (result)
70 
71  // Input
72  : "r" (syscall), "r" (param)
73 
74  // Clobbers (list of modified registers + memory + flags)
75  : "r0", "r1", "r2", "r3", "ip", "lr", "memory", "cc"
76  );
77 
78  return result;
79 }
80 
81 enum semi_syscalls_e
82 {
83  SEMI_SYS_WRITE = 0x05,
84  SEMI_SYS_WRITEC = 0x03,
85  SEMI_SYS_WRITE0 = 0x04,
86  SEMI_SYS_READC = 0x07,
87  SEMI_SYS_READ = 0x06,
88  SEMI_SYS_OPEN = 0x01,
89  SEMI_SYS_CLOSE = 0x02,
90  SEMI_SYS_SEEK = 0x0a,
91  SEMI_SYS_FLEN = 0x0c,
92 };
93 
94 struct semi_3_params
95 {
96  uint32_t w1;
97  uint32_t w2;
98  uint32_t w3;
99 };
100 
101 struct semi_2_params
102 {
103  uint32_t w1;
104  uint32_t w2;
105 };
106 
107 static int semi_write(int fd, const void *ptr, size_t len)
108 {
109  struct semi_3_params params =
110  {
111  .w1 = fd,
112  .w2 = (uint32_t)ptr,
113  .w3 = len,
114  };
115 
116  return _semi(SEMI_SYS_WRITE, &params);
117 }
118 
119 static int semi_read(int fd, void *ptr, size_t len)
120 {
121  struct semi_3_params params =
122  {
123  .w1 = fd,
124  .w2 = (uint32_t)ptr,
125  .w3 = len,
126  };
127 
128  int status = _semi(SEMI_SYS_READ, &params);
129 
130  /* status ok ? */
131  if (status == 0)
132  {
133  return len;
134  }
135 
136  /* EOF ? */
137  if (status == len)
138  {
139  return 0;
140  }
141 
142  /* Partial read */
143  return status;
144 }
145 
146 int _open(const char *pathname, int flags)
147 {
148  /**
149  * Convert from flags to ARM semihosting mode
150  *
151  * only use binary file access
152  */
153 
154  flags &= ~_FBINARY;
155 
156  uint32_t mode;
157  switch (flags)
158  {
159  case O_RDONLY:
160  /* rb */
161  mode = 1;
162  break;
163 
164  case O_WRONLY|O_CREAT|O_TRUNC:
165  /* wb */
166  mode = 5;
167  break;
168 
169  case O_WRONLY|O_CREAT|O_APPEND:
170  /* ab */
171  mode = 9;
172  break;
173 
174  case O_RDWR:
175  /* rb+ */
176  mode = 3;
177  break;
178 
179  case O_RDWR|O_CREAT|O_TRUNC:
180  /* wb+ */
181  mode = 7;
182  break;
183 
184  case O_RDWR|O_CREAT|O_APPEND:
185  /* ab+ */
186  mode = 11;
187  break;
188 
189  default:
190  return -EINVAL;
191  break;
192  }
193 
194  struct semi_3_params params =
195  {
196  .w1 = (uint32_t)pathname,
197  .w2 = mode,
198  .w3 = strlen(pathname),
199  };
200 
201  return _semi(SEMI_SYS_OPEN, &params);
202 }
203 
204 int _close(int fd)
205 {
206  return _semi(SEMI_SYS_CLOSE, &fd);
207 }
208 
209 int _lseek(int fd, long offset, int whence)
210 {
211  switch (whence)
212  {
213  case SEEK_SET:
214  break;
215 
216  case SEEK_CUR:
217  return -EINVAL;
218  break;
219 
220  case SEEK_END:
221  return -EINVAL;
222  break;
223 
224  default:
225  return -EINVAL;
226  }
227 
228  struct semi_3_params params =
229  {
230  .w1 = fd,
231  .w2 = offset,
232  };
233 
234  return _semi(SEMI_SYS_SEEK, &params);
235 }
236 
237 int _fstat(int fd, struct stat *sbuf)
238 {
239  memset(sbuf, 0x0, sizeof(*sbuf));
240  sbuf->st_size = _semi(SEMI_SYS_FLEN, &fd);
241 
242  if (sbuf->st_size < 0)
243  {
244  sbuf->st_size = 0;
245  return -1;
246  }
247 
248  return 0;
249 }
250 
251 int _write(int fd, const char *ptr, size_t len)
252 {
253  const uint32_t auto_crlf = USE_STDIO_AUTO_CR_BEFORE_LF;
254 
255  int i;
256 
257  if ( (fd == STDOUT_FILENO || fd == STDERR_FILENO) && auto_crlf)
258  {
259  for (i = 0 ; i < len ; i++)
260  {
261  char c = ptr[i];
262 
263  if (c == '\n')
264  {
265  char cr = '\r';
266  _semi(SEMI_SYS_WRITEC, &cr);
267  }
268  _semi(SEMI_SYS_WRITEC, &c);
269  }
270  }
271  else
272  {
273  semi_write(fd, ptr, len);
274  }
275 
276  return len;
277 }
278 
279 int _read(int fd, char *ptr, int len)
280 {
281  int result;
282  if (fd == STDIN_FILENO)
283  {
284  *ptr = _semi(SEMI_SYS_READC, NULL);
285  if (use_stdio_echo)
286  {
287  _write(STDOUT_FILENO, ptr, 1);
288  }
289  return 1;
290  }
291 
292  result = semi_read(fd, ptr, len);
293 
294  return result;
295 }
296 
297 void _ttywrch(int ci)
298 {
299  char c = ci;
300  _write(STDOUT_FILENO, &c, 1);
301 }
302 
303 #endif /* !defined(USE_STDIO_USART) */
304 
305 /************************************************************************//**
306  * @}
307  ***************************************************************************/
stdio redirection
static uint32_t _semi(uint32_t syscall, void *param)
Semihosting communication.
void * memset(void *dest, int n, size_t n)
libheivs configuration file
#define USE_STDIO_AUTO_CR_BEFORE_LF
Put a CR ('') before every LF (' ') on stdout (and stderr)
Definition: config.h:244
#define USE_STDIO_ECHO
Enable echo for stdio.
Definition: config.h:227
#define debugger_is_connected()
Detect is debugger is connected.
Definition: utils.h:67
usart bus
size_t strlen(const char *str)
#define USE_STDIO_NOBUF
Disable stdio buffering.
Definition: config.h:199