target/nios2: Eliminate nios2_semi_is_lseek
[qemu/ar7.git] / target / nios2 / nios2-semi.c
blobbdf88496895ff90f1f397fae7f8adedb57c879ea
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
8 * Copyright (c) 2017-2019 Mentor Graphics
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.
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.
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/>.
24 #include "qemu/osdep.h"
26 #include "cpu.h"
27 #include "exec/gdbstub.h"
28 #if defined(CONFIG_USER_ONLY)
29 #include "qemu.h"
30 #else
31 #include "semihosting/softmmu-uaccess.h"
32 #endif
33 #include "qemu/log.h"
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
50 static int translate_openflags(int flags)
52 int hf;
54 if (flags & GDB_O_WRONLY) {
55 hf = O_WRONLY;
56 } else if (flags & GDB_O_RDWR) {
57 hf = O_RDWR;
58 } else {
59 hf = O_RDONLY;
62 if (flags & GDB_O_APPEND) {
63 hf |= O_APPEND;
65 if (flags & GDB_O_CREAT) {
66 hf |= O_CREAT;
68 if (flags & GDB_O_TRUNC) {
69 hf |= O_TRUNC;
71 if (flags & GDB_O_EXCL) {
72 hf |= O_EXCL;
75 return hf;
78 static bool translate_stat(CPUNios2State *env, target_ulong addr,
79 struct stat *s)
81 struct gdb_stat *p;
83 p = lock_user(VERIFY_WRITE, addr, sizeof(struct gdb_stat), 0);
85 if (!p) {
86 return false;
88 p->gdb_st_dev = cpu_to_be32(s->st_dev);
89 p->gdb_st_ino = cpu_to_be32(s->st_ino);
90 p->gdb_st_mode = cpu_to_be32(s->st_mode);
91 p->gdb_st_nlink = cpu_to_be32(s->st_nlink);
92 p->gdb_st_uid = cpu_to_be32(s->st_uid);
93 p->gdb_st_gid = cpu_to_be32(s->st_gid);
94 p->gdb_st_rdev = cpu_to_be32(s->st_rdev);
95 p->gdb_st_size = cpu_to_be64(s->st_size);
96 #ifdef _WIN32
97 /* Windows stat is missing some fields. */
98 p->gdb_st_blksize = 0;
99 p->gdb_st_blocks = 0;
100 #else
101 p->gdb_st_blksize = cpu_to_be64(s->st_blksize);
102 p->gdb_st_blocks = cpu_to_be64(s->st_blocks);
103 #endif
104 p->gdb_st_atime = cpu_to_be32(s->st_atime);
105 p->gdb_st_mtime = cpu_to_be32(s->st_mtime);
106 p->gdb_st_ctime = cpu_to_be32(s->st_ctime);
107 unlock_user(p, addr, sizeof(struct gdb_stat));
108 return true;
111 static void nios2_semi_u32_cb(CPUState *cs, uint64_t ret, int err)
113 Nios2CPU *cpu = NIOS2_CPU(cs);
114 CPUNios2State *env = &cpu->env;
115 target_ulong args = env->regs[R_ARG1];
117 if (put_user_u32(ret, args) ||
118 put_user_u32(err, args + 4)) {
120 * The nios2 semihosting ABI does not provide any way to report this
121 * error to the guest, so the best we can do is log it in qemu.
122 * It is always a guest error not to pass us a valid argument block.
124 qemu_log_mask(LOG_GUEST_ERROR, "nios2-semihosting: return value "
125 "discarded because argument block not writable\n");
129 static void nios2_semi_u64_cb(CPUState *cs, uint64_t ret, int err)
131 Nios2CPU *cpu = NIOS2_CPU(cs);
132 CPUNios2State *env = &cpu->env;
133 target_ulong args = env->regs[R_ARG1];
135 if (put_user_u32(ret >> 32, args) ||
136 put_user_u32(ret, args + 4) ||
137 put_user_u32(err, args + 8)) {
138 /* No way to report this via nios2 semihosting ABI; just log it */
139 qemu_log_mask(LOG_GUEST_ERROR, "nios2-semihosting: return value "
140 "discarded because argument block not writable\n");
145 * Read the input value from the argument block; fail the semihosting
146 * call if the memory read fails.
148 #define GET_ARG(n) do { \
149 if (get_user_ual(arg ## n, args + (n) * 4)) { \
150 result = -1; \
151 errno = EFAULT; \
152 goto failed; \
154 } while (0)
156 void do_nios2_semihosting(CPUNios2State *env)
158 CPUState *cs = env_cpu(env);
159 int nr;
160 uint32_t args;
161 target_ulong arg0, arg1, arg2, arg3;
162 void *p;
163 void *q;
164 uint32_t len;
165 uint32_t result;
167 nr = env->regs[R_ARG0];
168 args = env->regs[R_ARG1];
169 switch (nr) {
170 case HOSTED_EXIT:
171 gdb_exit(env->regs[R_ARG0]);
172 exit(env->regs[R_ARG0]);
173 case HOSTED_OPEN:
174 GET_ARG(0);
175 GET_ARG(1);
176 GET_ARG(2);
177 GET_ARG(3);
178 if (use_gdb_syscalls()) {
179 gdb_do_syscall(nios2_semi_u32_cb, "open,%s,%x,%x", arg0, (int)arg1,
180 arg2, arg3);
181 return;
182 } else {
183 p = lock_user_string(arg0);
184 if (!p) {
185 result = -1;
186 errno = EFAULT;
187 } else {
188 result = open(p, translate_openflags(arg2), arg3);
189 unlock_user(p, arg0, 0);
192 break;
193 case HOSTED_CLOSE:
195 /* Ignore attempts to close stdin/out/err. */
196 GET_ARG(0);
197 int fd = arg0;
198 if (fd > 2) {
199 if (use_gdb_syscalls()) {
200 gdb_do_syscall(nios2_semi_u32_cb, "close,%x", arg0);
201 return;
202 } else {
203 result = close(fd);
205 } else {
206 result = 0;
208 break;
210 case HOSTED_READ:
211 GET_ARG(0);
212 GET_ARG(1);
213 GET_ARG(2);
214 len = arg2;
215 if (use_gdb_syscalls()) {
216 gdb_do_syscall(nios2_semi_u32_cb, "read,%x,%x,%x",
217 arg0, arg1, len);
218 return;
219 } else {
220 p = lock_user(VERIFY_WRITE, arg1, len, 0);
221 if (!p) {
222 result = -1;
223 errno = EFAULT;
224 } else {
225 result = read(arg0, p, len);
226 unlock_user(p, arg1, len);
229 break;
230 case HOSTED_WRITE:
231 GET_ARG(0);
232 GET_ARG(1);
233 GET_ARG(2);
234 len = arg2;
235 if (use_gdb_syscalls()) {
236 gdb_do_syscall(nios2_semi_u32_cb, "write,%x,%x,%x",
237 arg0, arg1, len);
238 return;
239 } else {
240 p = lock_user(VERIFY_READ, arg1, len, 1);
241 if (!p) {
242 result = -1;
243 errno = EFAULT;
244 } else {
245 result = write(arg0, p, len);
246 unlock_user(p, arg0, 0);
249 break;
250 case HOSTED_LSEEK:
252 uint64_t off;
253 GET_ARG(0);
254 GET_ARG(1);
255 GET_ARG(2);
256 GET_ARG(3);
257 off = (uint32_t)arg2 | ((uint64_t)arg1 << 32);
258 if (use_gdb_syscalls()) {
259 gdb_do_syscall(nios2_semi_u64_cb, "lseek,%x,%lx,%x",
260 arg0, off, arg3);
261 } else {
262 off = lseek(arg0, off, arg3);
263 nios2_semi_u64_cb(cs, off, errno);
265 return;
267 case HOSTED_RENAME:
268 GET_ARG(0);
269 GET_ARG(1);
270 GET_ARG(2);
271 GET_ARG(3);
272 if (use_gdb_syscalls()) {
273 gdb_do_syscall(nios2_semi_u32_cb, "rename,%s,%s",
274 arg0, (int)arg1, arg2, (int)arg3);
275 return;
276 } else {
277 p = lock_user_string(arg0);
278 q = lock_user_string(arg2);
279 if (!p || !q) {
280 result = -1;
281 errno = EFAULT;
282 } else {
283 result = rename(p, q);
285 unlock_user(p, arg0, 0);
286 unlock_user(q, arg2, 0);
288 break;
289 case HOSTED_UNLINK:
290 GET_ARG(0);
291 GET_ARG(1);
292 if (use_gdb_syscalls()) {
293 gdb_do_syscall(nios2_semi_u32_cb, "unlink,%s",
294 arg0, (int)arg1);
295 return;
296 } else {
297 p = lock_user_string(arg0);
298 if (!p) {
299 result = -1;
300 errno = EFAULT;
301 } else {
302 result = unlink(p);
303 unlock_user(p, arg0, 0);
306 break;
307 case HOSTED_STAT:
308 GET_ARG(0);
309 GET_ARG(1);
310 GET_ARG(2);
311 if (use_gdb_syscalls()) {
312 gdb_do_syscall(nios2_semi_u32_cb, "stat,%s,%x",
313 arg0, (int)arg1, arg2);
314 return;
315 } else {
316 struct stat s;
317 p = lock_user_string(arg0);
318 if (!p) {
319 result = -1;
320 errno = EFAULT;
321 } else {
322 result = stat(p, &s);
323 unlock_user(p, arg0, 0);
325 if (result == 0 && !translate_stat(env, arg2, &s)) {
326 result = -1;
327 errno = EFAULT;
330 break;
331 case HOSTED_FSTAT:
332 GET_ARG(0);
333 GET_ARG(1);
334 if (use_gdb_syscalls()) {
335 gdb_do_syscall(nios2_semi_u32_cb, "fstat,%x,%x",
336 arg0, arg1);
337 return;
338 } else {
339 struct stat s;
340 result = fstat(arg0, &s);
341 if (result == 0 && !translate_stat(env, arg1, &s)) {
342 result = -1;
343 errno = EFAULT;
346 break;
347 case HOSTED_GETTIMEOFDAY:
348 /* Only the tv parameter is used. tz is assumed NULL. */
349 GET_ARG(0);
350 if (use_gdb_syscalls()) {
351 gdb_do_syscall(nios2_semi_u32_cb, "gettimeofday,%x,%x",
352 arg0, 0);
353 return;
354 } else {
355 struct gdb_timeval *p;
356 int64_t rt = g_get_real_time();
357 p = lock_user(VERIFY_WRITE, arg0, sizeof(struct gdb_timeval), 0);
358 if (!p) {
359 result = -1;
360 errno = EFAULT;
361 } else {
362 result = 0;
363 p->tv_sec = cpu_to_be32(rt / G_USEC_PER_SEC);
364 p->tv_usec = cpu_to_be64(rt % G_USEC_PER_SEC);
365 unlock_user(p, arg0, sizeof(struct gdb_timeval));
368 break;
369 case HOSTED_ISATTY:
370 GET_ARG(0);
371 if (use_gdb_syscalls()) {
372 gdb_do_syscall(nios2_semi_u32_cb, "isatty,%x", arg0);
373 return;
374 } else {
375 result = isatty(arg0);
377 break;
378 case HOSTED_SYSTEM:
379 GET_ARG(0);
380 GET_ARG(1);
381 if (use_gdb_syscalls()) {
382 gdb_do_syscall(nios2_semi_u32_cb, "system,%s",
383 arg0, (int)arg1);
384 return;
385 } else {
386 p = lock_user_string(arg0);
387 if (!p) {
388 result = -1;
389 errno = EFAULT;
390 } else {
391 result = system(p);
392 unlock_user(p, arg0, 0);
395 break;
396 default:
397 qemu_log_mask(LOG_GUEST_ERROR, "nios2-semihosting: unsupported "
398 "semihosting syscall %d\n", nr);
399 result = 0;
401 failed:
402 nios2_semi_u32_cb(cs, result, errno);