xref: /openbmc/qemu/target/nios2/nios2-semi.c (revision ca61e750)
1 /*
2  *  Nios II Semihosting syscall interface.
3  *  This code is derived from m68k-semi.c.
4  *  The semihosting protocol implemented here is described in the
5  *  libgloss sources:
6  *  https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=libgloss/nios2/nios2-semi.txt;hb=HEAD
7  *
8  *  Copyright (c) 2017-2019 Mentor Graphics
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2 of the License, or
13  *  (at your option) any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
22  */
23 
24 #include "qemu/osdep.h"
25 
26 #include "cpu.h"
27 #include "exec/gdbstub.h"
28 #if defined(CONFIG_USER_ONLY)
29 #include "qemu.h"
30 #else
31 #include "exec/softmmu-semi.h"
32 #endif
33 #include "qemu/log.h"
34 
35 #define HOSTED_EXIT  0
36 #define HOSTED_INIT_SIM 1
37 #define HOSTED_OPEN 2
38 #define HOSTED_CLOSE 3
39 #define HOSTED_READ 4
40 #define HOSTED_WRITE 5
41 #define HOSTED_LSEEK 6
42 #define HOSTED_RENAME 7
43 #define HOSTED_UNLINK 8
44 #define HOSTED_STAT 9
45 #define HOSTED_FSTAT 10
46 #define HOSTED_GETTIMEOFDAY 11
47 #define HOSTED_ISATTY 12
48 #define HOSTED_SYSTEM 13
49 
50 typedef uint32_t gdb_mode_t;
51 typedef uint32_t gdb_time_t;
52 
53 struct nios2_gdb_stat {
54   uint32_t    gdb_st_dev;     /* device */
55   uint32_t    gdb_st_ino;     /* inode */
56   gdb_mode_t  gdb_st_mode;    /* protection */
57   uint32_t    gdb_st_nlink;   /* number of hard links */
58   uint32_t    gdb_st_uid;     /* user ID of owner */
59   uint32_t    gdb_st_gid;     /* group ID of owner */
60   uint32_t    gdb_st_rdev;    /* device type (if inode device) */
61   uint64_t    gdb_st_size;    /* total size, in bytes */
62   uint64_t    gdb_st_blksize; /* blocksize for filesystem I/O */
63   uint64_t    gdb_st_blocks;  /* number of blocks allocated */
64   gdb_time_t  gdb_st_atime;   /* time of last access */
65   gdb_time_t  gdb_st_mtime;   /* time of last modification */
66   gdb_time_t  gdb_st_ctime;   /* time of last change */
67 } QEMU_PACKED;
68 
69 struct gdb_timeval {
70   gdb_time_t tv_sec;  /* second */
71   uint64_t tv_usec;   /* microsecond */
72 } QEMU_PACKED;
73 
74 #define GDB_O_RDONLY   0x0
75 #define GDB_O_WRONLY   0x1
76 #define GDB_O_RDWR     0x2
77 #define GDB_O_APPEND   0x8
78 #define GDB_O_CREAT  0x200
79 #define GDB_O_TRUNC  0x400
80 #define GDB_O_EXCL   0x800
81 
82 static int translate_openflags(int flags)
83 {
84     int hf;
85 
86     if (flags & GDB_O_WRONLY) {
87         hf = O_WRONLY;
88     } else if (flags & GDB_O_RDWR) {
89         hf = O_RDWR;
90     } else {
91         hf = O_RDONLY;
92     }
93 
94     if (flags & GDB_O_APPEND) {
95         hf |= O_APPEND;
96     }
97     if (flags & GDB_O_CREAT) {
98         hf |= O_CREAT;
99     }
100     if (flags & GDB_O_TRUNC) {
101         hf |= O_TRUNC;
102     }
103     if (flags & GDB_O_EXCL) {
104         hf |= O_EXCL;
105     }
106 
107     return hf;
108 }
109 
110 static bool translate_stat(CPUNios2State *env, target_ulong addr,
111                            struct stat *s)
112 {
113     struct nios2_gdb_stat *p;
114 
115     p = lock_user(VERIFY_WRITE, addr, sizeof(struct nios2_gdb_stat), 0);
116 
117     if (!p) {
118         return false;
119     }
120     p->gdb_st_dev = cpu_to_be32(s->st_dev);
121     p->gdb_st_ino = cpu_to_be32(s->st_ino);
122     p->gdb_st_mode = cpu_to_be32(s->st_mode);
123     p->gdb_st_nlink = cpu_to_be32(s->st_nlink);
124     p->gdb_st_uid = cpu_to_be32(s->st_uid);
125     p->gdb_st_gid = cpu_to_be32(s->st_gid);
126     p->gdb_st_rdev = cpu_to_be32(s->st_rdev);
127     p->gdb_st_size = cpu_to_be64(s->st_size);
128 #ifdef _WIN32
129     /* Windows stat is missing some fields.  */
130     p->gdb_st_blksize = 0;
131     p->gdb_st_blocks = 0;
132 #else
133     p->gdb_st_blksize = cpu_to_be64(s->st_blksize);
134     p->gdb_st_blocks = cpu_to_be64(s->st_blocks);
135 #endif
136     p->gdb_st_atime = cpu_to_be32(s->st_atime);
137     p->gdb_st_mtime = cpu_to_be32(s->st_mtime);
138     p->gdb_st_ctime = cpu_to_be32(s->st_ctime);
139     unlock_user(p, addr, sizeof(struct nios2_gdb_stat));
140     return true;
141 }
142 
143 static void nios2_semi_return_u32(CPUNios2State *env, uint32_t ret,
144                                   uint32_t err)
145 {
146     target_ulong args = env->regs[R_ARG1];
147     if (put_user_u32(ret, args) ||
148         put_user_u32(err, args + 4)) {
149         /*
150          * The nios2 semihosting ABI does not provide any way to report this
151          * error to the guest, so the best we can do is log it in qemu.
152          * It is always a guest error not to pass us a valid argument block.
153          */
154         qemu_log_mask(LOG_GUEST_ERROR, "nios2-semihosting: return value "
155                       "discarded because argument block not writable\n");
156     }
157 }
158 
159 static void nios2_semi_return_u64(CPUNios2State *env, uint64_t ret,
160                                   uint32_t err)
161 {
162     target_ulong args = env->regs[R_ARG1];
163     if (put_user_u32(ret >> 32, args) ||
164         put_user_u32(ret, args + 4) ||
165         put_user_u32(err, args + 8)) {
166         /* No way to report this via nios2 semihosting ABI; just log it */
167         qemu_log_mask(LOG_GUEST_ERROR, "nios2-semihosting: return value "
168                       "discarded because argument block not writable\n");
169     }
170 }
171 
172 static int nios2_semi_is_lseek;
173 
174 static void nios2_semi_cb(CPUState *cs, target_ulong ret, target_ulong err)
175 {
176     Nios2CPU *cpu = NIOS2_CPU(cs);
177     CPUNios2State *env = &cpu->env;
178 
179     if (nios2_semi_is_lseek) {
180         /*
181          * FIXME: We've already lost the high bits of the lseek
182          * return value.
183          */
184         nios2_semi_return_u64(env, ret, err);
185         nios2_semi_is_lseek = 0;
186     } else {
187         nios2_semi_return_u32(env, ret, err);
188     }
189 }
190 
191 /*
192  * Read the input value from the argument block; fail the semihosting
193  * call if the memory read fails.
194  */
195 #define GET_ARG(n) do {                                 \
196     if (get_user_ual(arg ## n, args + (n) * 4)) {       \
197         result = -1;                                    \
198         errno = EFAULT;                                 \
199         goto failed;                                    \
200     }                                                   \
201 } while (0)
202 
203 void do_nios2_semihosting(CPUNios2State *env)
204 {
205     int nr;
206     uint32_t args;
207     target_ulong arg0, arg1, arg2, arg3;
208     void *p;
209     void *q;
210     uint32_t len;
211     uint32_t result;
212 
213     nr = env->regs[R_ARG0];
214     args = env->regs[R_ARG1];
215     switch (nr) {
216     case HOSTED_EXIT:
217         gdb_exit(env->regs[R_ARG0]);
218         exit(env->regs[R_ARG0]);
219     case HOSTED_OPEN:
220         GET_ARG(0);
221         GET_ARG(1);
222         GET_ARG(2);
223         GET_ARG(3);
224         if (use_gdb_syscalls()) {
225             gdb_do_syscall(nios2_semi_cb, "open,%s,%x,%x", arg0, (int)arg1,
226                            arg2, arg3);
227             return;
228         } else {
229             p = lock_user_string(arg0);
230             if (!p) {
231                 result = -1;
232                 errno = EFAULT;
233             } else {
234                 result = open(p, translate_openflags(arg2), arg3);
235                 unlock_user(p, arg0, 0);
236             }
237         }
238         break;
239     case HOSTED_CLOSE:
240         {
241             /* Ignore attempts to close stdin/out/err.  */
242             GET_ARG(0);
243             int fd = arg0;
244             if (fd > 2) {
245                 if (use_gdb_syscalls()) {
246                     gdb_do_syscall(nios2_semi_cb, "close,%x", arg0);
247                     return;
248                 } else {
249                     result = close(fd);
250                 }
251             } else {
252                 result = 0;
253             }
254             break;
255         }
256     case HOSTED_READ:
257         GET_ARG(0);
258         GET_ARG(1);
259         GET_ARG(2);
260         len = arg2;
261         if (use_gdb_syscalls()) {
262             gdb_do_syscall(nios2_semi_cb, "read,%x,%x,%x",
263                            arg0, arg1, len);
264             return;
265         } else {
266             p = lock_user(VERIFY_WRITE, arg1, len, 0);
267             if (!p) {
268                 result = -1;
269                 errno = EFAULT;
270             } else {
271                 result = read(arg0, p, len);
272                 unlock_user(p, arg1, len);
273             }
274         }
275         break;
276     case HOSTED_WRITE:
277         GET_ARG(0);
278         GET_ARG(1);
279         GET_ARG(2);
280         len = arg2;
281         if (use_gdb_syscalls()) {
282             gdb_do_syscall(nios2_semi_cb, "write,%x,%x,%x",
283                            arg0, arg1, len);
284             return;
285         } else {
286             p = lock_user(VERIFY_READ, arg1, len, 1);
287             if (!p) {
288                 result = -1;
289                 errno = EFAULT;
290             } else {
291                 result = write(arg0, p, len);
292                 unlock_user(p, arg0, 0);
293             }
294         }
295         break;
296     case HOSTED_LSEEK:
297         {
298             uint64_t off;
299             GET_ARG(0);
300             GET_ARG(1);
301             GET_ARG(2);
302             GET_ARG(3);
303             off = (uint32_t)arg2 | ((uint64_t)arg1 << 32);
304             if (use_gdb_syscalls()) {
305                 nios2_semi_is_lseek = 1;
306                 gdb_do_syscall(nios2_semi_cb, "lseek,%x,%lx,%x",
307                                arg0, off, arg3);
308             } else {
309                 off = lseek(arg0, off, arg3);
310                 nios2_semi_return_u64(env, off, errno);
311             }
312             return;
313         }
314     case HOSTED_RENAME:
315         GET_ARG(0);
316         GET_ARG(1);
317         GET_ARG(2);
318         GET_ARG(3);
319         if (use_gdb_syscalls()) {
320             gdb_do_syscall(nios2_semi_cb, "rename,%s,%s",
321                            arg0, (int)arg1, arg2, (int)arg3);
322             return;
323         } else {
324             p = lock_user_string(arg0);
325             q = lock_user_string(arg2);
326             if (!p || !q) {
327                 result = -1;
328                 errno = EFAULT;
329             } else {
330                 result = rename(p, q);
331             }
332             unlock_user(p, arg0, 0);
333             unlock_user(q, arg2, 0);
334         }
335         break;
336     case HOSTED_UNLINK:
337         GET_ARG(0);
338         GET_ARG(1);
339         if (use_gdb_syscalls()) {
340             gdb_do_syscall(nios2_semi_cb, "unlink,%s",
341                            arg0, (int)arg1);
342             return;
343         } else {
344             p = lock_user_string(arg0);
345             if (!p) {
346                 result = -1;
347                 errno = EFAULT;
348             } else {
349                 result = unlink(p);
350                 unlock_user(p, arg0, 0);
351             }
352         }
353         break;
354     case HOSTED_STAT:
355         GET_ARG(0);
356         GET_ARG(1);
357         GET_ARG(2);
358         if (use_gdb_syscalls()) {
359             gdb_do_syscall(nios2_semi_cb, "stat,%s,%x",
360                            arg0, (int)arg1, arg2);
361             return;
362         } else {
363             struct stat s;
364             p = lock_user_string(arg0);
365             if (!p) {
366                 result = -1;
367                 errno = EFAULT;
368             } else {
369                 result = stat(p, &s);
370                 unlock_user(p, arg0, 0);
371             }
372             if (result == 0 && !translate_stat(env, arg2, &s)) {
373                 result = -1;
374                 errno = EFAULT;
375             }
376         }
377         break;
378     case HOSTED_FSTAT:
379         GET_ARG(0);
380         GET_ARG(1);
381         if (use_gdb_syscalls()) {
382             gdb_do_syscall(nios2_semi_cb, "fstat,%x,%x",
383                            arg0, arg1);
384             return;
385         } else {
386             struct stat s;
387             result = fstat(arg0, &s);
388             if (result == 0 && !translate_stat(env, arg1, &s)) {
389                 result = -1;
390                 errno = EFAULT;
391             }
392         }
393         break;
394     case HOSTED_GETTIMEOFDAY:
395         /* Only the tv parameter is used.  tz is assumed NULL.  */
396         GET_ARG(0);
397         if (use_gdb_syscalls()) {
398             gdb_do_syscall(nios2_semi_cb, "gettimeofday,%x,%x",
399                            arg0, 0);
400             return;
401         } else {
402             struct gdb_timeval *p;
403             int64_t rt = g_get_real_time();
404             p = lock_user(VERIFY_WRITE, arg0, sizeof(struct gdb_timeval), 0);
405             if (!p) {
406                 result = -1;
407                 errno = EFAULT;
408             } else {
409                 result = 0;
410                 p->tv_sec = cpu_to_be32(rt / G_USEC_PER_SEC);
411                 p->tv_usec = cpu_to_be64(rt % G_USEC_PER_SEC);
412                 unlock_user(p, arg0, sizeof(struct gdb_timeval));
413             }
414         }
415         break;
416     case HOSTED_ISATTY:
417         GET_ARG(0);
418         if (use_gdb_syscalls()) {
419             gdb_do_syscall(nios2_semi_cb, "isatty,%x", arg0);
420             return;
421         } else {
422             result = isatty(arg0);
423         }
424         break;
425     case HOSTED_SYSTEM:
426         GET_ARG(0);
427         GET_ARG(1);
428         if (use_gdb_syscalls()) {
429             gdb_do_syscall(nios2_semi_cb, "system,%s",
430                            arg0, (int)arg1);
431             return;
432         } else {
433             p = lock_user_string(arg0);
434             if (!p) {
435                 result = -1;
436                 errno = EFAULT;
437             } else {
438                 result = system(p);
439                 unlock_user(p, arg0, 0);
440             }
441         }
442         break;
443     default:
444         qemu_log_mask(LOG_GUEST_ERROR, "nios2-semihosting: unsupported "
445                       "semihosting syscall %d\n", nr);
446         result = 0;
447     }
448 failed:
449     nios2_semi_return_u32(env, result, errno);
450 }
451