xref: /openbmc/qemu/target/nios2/nios2-semi.c (revision f14eced5)
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 #include "cpu.h"
26 #include "gdbstub/syscalls.h"
27 #include "gdbstub/helpers.h"
28 #include "semihosting/syscalls.h"
29 #include "semihosting/uaccess.h"
30 #include "qemu/log.h"
31 
32 #define HOSTED_EXIT  0
33 #define HOSTED_INIT_SIM 1
34 #define HOSTED_OPEN 2
35 #define HOSTED_CLOSE 3
36 #define HOSTED_READ 4
37 #define HOSTED_WRITE 5
38 #define HOSTED_LSEEK 6
39 #define HOSTED_RENAME 7
40 #define HOSTED_UNLINK 8
41 #define HOSTED_STAT 9
42 #define HOSTED_FSTAT 10
43 #define HOSTED_GETTIMEOFDAY 11
44 #define HOSTED_ISATTY 12
45 #define HOSTED_SYSTEM 13
46 
host_to_gdb_errno(int err)47 static int host_to_gdb_errno(int err)
48 {
49 #define E(X)  case E##X: return GDB_E##X
50     switch (err) {
51     E(PERM);
52     E(NOENT);
53     E(INTR);
54     E(BADF);
55     E(ACCES);
56     E(FAULT);
57     E(BUSY);
58     E(EXIST);
59     E(NODEV);
60     E(NOTDIR);
61     E(ISDIR);
62     E(INVAL);
63     E(NFILE);
64     E(MFILE);
65     E(FBIG);
66     E(NOSPC);
67     E(SPIPE);
68     E(ROFS);
69     E(NAMETOOLONG);
70     default:
71         return GDB_EUNKNOWN;
72     }
73 #undef E
74 }
75 
nios2_semi_u32_cb(CPUState * cs,uint64_t ret,int err)76 static void nios2_semi_u32_cb(CPUState *cs, uint64_t ret, int err)
77 {
78     Nios2CPU *cpu = NIOS2_CPU(cs);
79     CPUNios2State *env = &cpu->env;
80     target_ulong args = env->regs[R_ARG1];
81 
82     if (put_user_u32(ret, args) ||
83         put_user_u32(host_to_gdb_errno(err), args + 4)) {
84         /*
85          * The nios2 semihosting ABI does not provide any way to report this
86          * error to the guest, so the best we can do is log it in qemu.
87          * It is always a guest error not to pass us a valid argument block.
88          */
89         qemu_log_mask(LOG_GUEST_ERROR, "nios2-semihosting: return value "
90                       "discarded because argument block not writable\n");
91     }
92 }
93 
nios2_semi_u64_cb(CPUState * cs,uint64_t ret,int err)94 static void nios2_semi_u64_cb(CPUState *cs, uint64_t ret, int err)
95 {
96     Nios2CPU *cpu = NIOS2_CPU(cs);
97     CPUNios2State *env = &cpu->env;
98     target_ulong args = env->regs[R_ARG1];
99 
100     if (put_user_u32(ret >> 32, args) ||
101         put_user_u32(ret, args + 4) ||
102         put_user_u32(host_to_gdb_errno(err), args + 8)) {
103         /* No way to report this via nios2 semihosting ABI; just log it */
104         qemu_log_mask(LOG_GUEST_ERROR, "nios2-semihosting: return value "
105                       "discarded because argument block not writable\n");
106     }
107 }
108 
109 /*
110  * Read the input value from the argument block; fail the semihosting
111  * call if the memory read fails.
112  */
113 #define GET_ARG(n) do {                                 \
114     if (get_user_ual(arg ## n, args + (n) * 4)) {       \
115         goto failed;                                    \
116     }                                                   \
117 } while (0)
118 
119 #define GET_ARG64(n) do {                               \
120     if (get_user_ual(arg ## n, args + (n) * 4)) {       \
121         goto failed64;                                  \
122     }                                                   \
123 } while (0)
124 
do_nios2_semihosting(CPUNios2State * env)125 void do_nios2_semihosting(CPUNios2State *env)
126 {
127     CPUState *cs = env_cpu(env);
128     int nr;
129     uint32_t args;
130     target_ulong arg0, arg1, arg2, arg3;
131 
132     nr = env->regs[R_ARG0];
133     args = env->regs[R_ARG1];
134     switch (nr) {
135     case HOSTED_EXIT:
136         gdb_exit(env->regs[R_ARG1]);
137         exit(env->regs[R_ARG1]);
138 
139     case HOSTED_OPEN:
140         GET_ARG(0);
141         GET_ARG(1);
142         GET_ARG(2);
143         GET_ARG(3);
144         semihost_sys_open(cs, nios2_semi_u32_cb, arg0, arg1, arg2, arg3);
145         break;
146 
147     case HOSTED_CLOSE:
148         GET_ARG(0);
149         semihost_sys_close(cs, nios2_semi_u32_cb, arg0);
150         break;
151 
152     case HOSTED_READ:
153         GET_ARG(0);
154         GET_ARG(1);
155         GET_ARG(2);
156         semihost_sys_read(cs, nios2_semi_u32_cb, arg0, arg1, arg2);
157         break;
158 
159     case HOSTED_WRITE:
160         GET_ARG(0);
161         GET_ARG(1);
162         GET_ARG(2);
163         semihost_sys_write(cs, nios2_semi_u32_cb, arg0, arg1, arg2);
164         break;
165 
166     case HOSTED_LSEEK:
167         GET_ARG64(0);
168         GET_ARG64(1);
169         GET_ARG64(2);
170         GET_ARG64(3);
171         semihost_sys_lseek(cs, nios2_semi_u64_cb, arg0,
172                            deposit64(arg2, 32, 32, arg1), arg3);
173         break;
174 
175     case HOSTED_RENAME:
176         GET_ARG(0);
177         GET_ARG(1);
178         GET_ARG(2);
179         GET_ARG(3);
180         semihost_sys_rename(cs, nios2_semi_u32_cb, arg0, arg1, arg2, arg3);
181         break;
182 
183     case HOSTED_UNLINK:
184         GET_ARG(0);
185         GET_ARG(1);
186         semihost_sys_remove(cs, nios2_semi_u32_cb, arg0, arg1);
187         break;
188 
189     case HOSTED_STAT:
190         GET_ARG(0);
191         GET_ARG(1);
192         GET_ARG(2);
193         semihost_sys_stat(cs, nios2_semi_u32_cb, arg0, arg1, arg2);
194         break;
195 
196     case HOSTED_FSTAT:
197         GET_ARG(0);
198         GET_ARG(1);
199         semihost_sys_fstat(cs, nios2_semi_u32_cb, arg0, arg1);
200         break;
201 
202     case HOSTED_GETTIMEOFDAY:
203         GET_ARG(0);
204         GET_ARG(1);
205         semihost_sys_gettimeofday(cs, nios2_semi_u32_cb, arg0, arg1);
206         break;
207 
208     case HOSTED_ISATTY:
209         GET_ARG(0);
210         semihost_sys_isatty(cs, nios2_semi_u32_cb, arg0);
211         break;
212 
213     case HOSTED_SYSTEM:
214         GET_ARG(0);
215         GET_ARG(1);
216         semihost_sys_system(cs, nios2_semi_u32_cb, arg0, arg1);
217         break;
218 
219     default:
220         qemu_log_mask(LOG_GUEST_ERROR, "nios2-semihosting: unsupported "
221                       "semihosting syscall %d\n", nr);
222         nios2_semi_u32_cb(cs, -1, ENOSYS);
223         break;
224 
225     failed:
226         nios2_semi_u32_cb(cs, -1, EFAULT);
227         break;
228     failed64:
229         nios2_semi_u64_cb(cs, -1, EFAULT);
230         break;
231     }
232 }
233