1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24#include "qemu/osdep.h"
25#include "cpu.h"
26#include "exec/gdbstub.h"
27#include "semihosting/softmmu-uaccess.h"
28#include "qemu/log.h"
29
30#define HOSTED_EXIT 0
31#define HOSTED_INIT_SIM 1
32#define HOSTED_OPEN 2
33#define HOSTED_CLOSE 3
34#define HOSTED_READ 4
35#define HOSTED_WRITE 5
36#define HOSTED_LSEEK 6
37#define HOSTED_RENAME 7
38#define HOSTED_UNLINK 8
39#define HOSTED_STAT 9
40#define HOSTED_FSTAT 10
41#define HOSTED_GETTIMEOFDAY 11
42#define HOSTED_ISATTY 12
43#define HOSTED_SYSTEM 13
44
45static int translate_openflags(int flags)
46{
47 int hf;
48
49 if (flags & GDB_O_WRONLY) {
50 hf = O_WRONLY;
51 } else if (flags & GDB_O_RDWR) {
52 hf = O_RDWR;
53 } else {
54 hf = O_RDONLY;
55 }
56
57 if (flags & GDB_O_APPEND) {
58 hf |= O_APPEND;
59 }
60 if (flags & GDB_O_CREAT) {
61 hf |= O_CREAT;
62 }
63 if (flags & GDB_O_TRUNC) {
64 hf |= O_TRUNC;
65 }
66 if (flags & GDB_O_EXCL) {
67 hf |= O_EXCL;
68 }
69
70 return hf;
71}
72
73static bool translate_stat(CPUNios2State *env, target_ulong addr,
74 struct stat *s)
75{
76 struct gdb_stat *p;
77
78 p = lock_user(VERIFY_WRITE, addr, sizeof(struct gdb_stat), 0);
79
80 if (!p) {
81 return false;
82 }
83 p->gdb_st_dev = cpu_to_be32(s->st_dev);
84 p->gdb_st_ino = cpu_to_be32(s->st_ino);
85 p->gdb_st_mode = cpu_to_be32(s->st_mode);
86 p->gdb_st_nlink = cpu_to_be32(s->st_nlink);
87 p->gdb_st_uid = cpu_to_be32(s->st_uid);
88 p->gdb_st_gid = cpu_to_be32(s->st_gid);
89 p->gdb_st_rdev = cpu_to_be32(s->st_rdev);
90 p->gdb_st_size = cpu_to_be64(s->st_size);
91#ifdef _WIN32
92
93 p->gdb_st_blksize = 0;
94 p->gdb_st_blocks = 0;
95#else
96 p->gdb_st_blksize = cpu_to_be64(s->st_blksize);
97 p->gdb_st_blocks = cpu_to_be64(s->st_blocks);
98#endif
99 p->gdb_st_atime = cpu_to_be32(s->st_atime);
100 p->gdb_st_mtime = cpu_to_be32(s->st_mtime);
101 p->gdb_st_ctime = cpu_to_be32(s->st_ctime);
102 unlock_user(p, addr, sizeof(struct gdb_stat));
103 return true;
104}
105
106static void nios2_semi_u32_cb(CPUState *cs, uint64_t ret, int err)
107{
108 Nios2CPU *cpu = NIOS2_CPU(cs);
109 CPUNios2State *env = &cpu->env;
110 target_ulong args = env->regs[R_ARG1];
111
112 if (put_user_u32(ret, args) ||
113 put_user_u32(err, args + 4)) {
114
115
116
117
118
119 qemu_log_mask(LOG_GUEST_ERROR, "nios2-semihosting: return value "
120 "discarded because argument block not writable\n");
121 }
122}
123
124static void nios2_semi_u64_cb(CPUState *cs, uint64_t ret, int err)
125{
126 Nios2CPU *cpu = NIOS2_CPU(cs);
127 CPUNios2State *env = &cpu->env;
128 target_ulong args = env->regs[R_ARG1];
129
130 if (put_user_u32(ret >> 32, args) ||
131 put_user_u32(ret, args + 4) ||
132 put_user_u32(err, args + 8)) {
133
134 qemu_log_mask(LOG_GUEST_ERROR, "nios2-semihosting: return value "
135 "discarded because argument block not writable\n");
136 }
137}
138
139
140
141
142
143#define GET_ARG(n) do { \
144 if (get_user_ual(arg ## n, args + (n) * 4)) { \
145 result = -1; \
146 errno = EFAULT; \
147 goto failed; \
148 } \
149} while (0)
150
151void do_nios2_semihosting(CPUNios2State *env)
152{
153 CPUState *cs = env_cpu(env);
154 int nr;
155 uint32_t args;
156 target_ulong arg0, arg1, arg2, arg3;
157 void *p;
158 void *q;
159 uint32_t len;
160 uint32_t result;
161
162 nr = env->regs[R_ARG0];
163 args = env->regs[R_ARG1];
164 switch (nr) {
165 case HOSTED_EXIT:
166 gdb_exit(env->regs[R_ARG0]);
167 exit(env->regs[R_ARG0]);
168 case HOSTED_OPEN:
169 GET_ARG(0);
170 GET_ARG(1);
171 GET_ARG(2);
172 GET_ARG(3);
173 if (use_gdb_syscalls()) {
174 gdb_do_syscall(nios2_semi_u32_cb, "open,%s,%x,%x", arg0, (int)arg1,
175 arg2, arg3);
176 return;
177 } else {
178 p = lock_user_string(arg0);
179 if (!p) {
180 result = -1;
181 errno = EFAULT;
182 } else {
183 result = open(p, translate_openflags(arg2), arg3);
184 unlock_user(p, arg0, 0);
185 }
186 }
187 break;
188 case HOSTED_CLOSE:
189 {
190
191 GET_ARG(0);
192 int fd = arg0;
193 if (fd > 2) {
194 if (use_gdb_syscalls()) {
195 gdb_do_syscall(nios2_semi_u32_cb, "close,%x", arg0);
196 return;
197 } else {
198 result = close(fd);
199 }
200 } else {
201 result = 0;
202 }
203 break;
204 }
205 case HOSTED_READ:
206 GET_ARG(0);
207 GET_ARG(1);
208 GET_ARG(2);
209 len = arg2;
210 if (use_gdb_syscalls()) {
211 gdb_do_syscall(nios2_semi_u32_cb, "read,%x,%x,%x",
212 arg0, arg1, len);
213 return;
214 } else {
215 p = lock_user(VERIFY_WRITE, arg1, len, 0);
216 if (!p) {
217 result = -1;
218 errno = EFAULT;
219 } else {
220 result = read(arg0, p, len);
221 unlock_user(p, arg1, len);
222 }
223 }
224 break;
225 case HOSTED_WRITE:
226 GET_ARG(0);
227 GET_ARG(1);
228 GET_ARG(2);
229 len = arg2;
230 if (use_gdb_syscalls()) {
231 gdb_do_syscall(nios2_semi_u32_cb, "write,%x,%x,%x",
232 arg0, arg1, len);
233 return;
234 } else {
235 p = lock_user(VERIFY_READ, arg1, len, 1);
236 if (!p) {
237 result = -1;
238 errno = EFAULT;
239 } else {
240 result = write(arg0, p, len);
241 unlock_user(p, arg0, 0);
242 }
243 }
244 break;
245 case HOSTED_LSEEK:
246 {
247 uint64_t off;
248 GET_ARG(0);
249 GET_ARG(1);
250 GET_ARG(2);
251 GET_ARG(3);
252 off = (uint32_t)arg2 | ((uint64_t)arg1 << 32);
253 if (use_gdb_syscalls()) {
254 gdb_do_syscall(nios2_semi_u64_cb, "lseek,%x,%lx,%x",
255 arg0, off, arg3);
256 } else {
257 off = lseek(arg0, off, arg3);
258 nios2_semi_u64_cb(cs, off, errno);
259 }
260 return;
261 }
262 case HOSTED_RENAME:
263 GET_ARG(0);
264 GET_ARG(1);
265 GET_ARG(2);
266 GET_ARG(3);
267 if (use_gdb_syscalls()) {
268 gdb_do_syscall(nios2_semi_u32_cb, "rename,%s,%s",
269 arg0, (int)arg1, arg2, (int)arg3);
270 return;
271 } else {
272 p = lock_user_string(arg0);
273 q = lock_user_string(arg2);
274 if (!p || !q) {
275 result = -1;
276 errno = EFAULT;
277 } else {
278 result = rename(p, q);
279 }
280 unlock_user(p, arg0, 0);
281 unlock_user(q, arg2, 0);
282 }
283 break;
284 case HOSTED_UNLINK:
285 GET_ARG(0);
286 GET_ARG(1);
287 if (use_gdb_syscalls()) {
288 gdb_do_syscall(nios2_semi_u32_cb, "unlink,%s",
289 arg0, (int)arg1);
290 return;
291 } else {
292 p = lock_user_string(arg0);
293 if (!p) {
294 result = -1;
295 errno = EFAULT;
296 } else {
297 result = unlink(p);
298 unlock_user(p, arg0, 0);
299 }
300 }
301 break;
302 case HOSTED_STAT:
303 GET_ARG(0);
304 GET_ARG(1);
305 GET_ARG(2);
306 if (use_gdb_syscalls()) {
307 gdb_do_syscall(nios2_semi_u32_cb, "stat,%s,%x",
308 arg0, (int)arg1, arg2);
309 return;
310 } else {
311 struct stat s;
312 p = lock_user_string(arg0);
313 if (!p) {
314 result = -1;
315 errno = EFAULT;
316 } else {
317 result = stat(p, &s);
318 unlock_user(p, arg0, 0);
319 }
320 if (result == 0 && !translate_stat(env, arg2, &s)) {
321 result = -1;
322 errno = EFAULT;
323 }
324 }
325 break;
326 case HOSTED_FSTAT:
327 GET_ARG(0);
328 GET_ARG(1);
329 if (use_gdb_syscalls()) {
330 gdb_do_syscall(nios2_semi_u32_cb, "fstat,%x,%x",
331 arg0, arg1);
332 return;
333 } else {
334 struct stat s;
335 result = fstat(arg0, &s);
336 if (result == 0 && !translate_stat(env, arg1, &s)) {
337 result = -1;
338 errno = EFAULT;
339 }
340 }
341 break;
342 case HOSTED_GETTIMEOFDAY:
343
344 GET_ARG(0);
345 if (use_gdb_syscalls()) {
346 gdb_do_syscall(nios2_semi_u32_cb, "gettimeofday,%x,%x",
347 arg0, 0);
348 return;
349 } else {
350 struct gdb_timeval *p;
351 int64_t rt = g_get_real_time();
352 p = lock_user(VERIFY_WRITE, arg0, sizeof(struct gdb_timeval), 0);
353 if (!p) {
354 result = -1;
355 errno = EFAULT;
356 } else {
357 result = 0;
358 p->tv_sec = cpu_to_be32(rt / G_USEC_PER_SEC);
359 p->tv_usec = cpu_to_be64(rt % G_USEC_PER_SEC);
360 unlock_user(p, arg0, sizeof(struct gdb_timeval));
361 }
362 }
363 break;
364 case HOSTED_ISATTY:
365 GET_ARG(0);
366 if (use_gdb_syscalls()) {
367 gdb_do_syscall(nios2_semi_u32_cb, "isatty,%x", arg0);
368 return;
369 } else {
370 result = isatty(arg0);
371 }
372 break;
373 case HOSTED_SYSTEM:
374 GET_ARG(0);
375 GET_ARG(1);
376 if (use_gdb_syscalls()) {
377 gdb_do_syscall(nios2_semi_u32_cb, "system,%s",
378 arg0, (int)arg1);
379 return;
380 } else {
381 p = lock_user_string(arg0);
382 if (!p) {
383 result = -1;
384 errno = EFAULT;
385 } else {
386 result = system(p);
387 unlock_user(p, arg0, 0);
388 }
389 }
390 break;
391 default:
392 qemu_log_mask(LOG_GUEST_ERROR, "nios2-semihosting: unsupported "
393 "semihosting syscall %d\n", nr);
394 result = 0;
395 }
396failed:
397 nios2_semi_u32_cb(cs, result, errno);
398}
399