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
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"
27 #include "exec/gdbstub.h"
28 #if defined(CONFIG_USER_ONLY)
31 #include "semihosting/softmmu-uaccess.h"
36 #define HOSTED_INIT_SIM 1
38 #define HOSTED_CLOSE 3
40 #define HOSTED_WRITE 5
41 #define HOSTED_LSEEK 6
42 #define HOSTED_RENAME 7
43 #define HOSTED_UNLINK 8
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
)
54 if (flags
& GDB_O_WRONLY
) {
56 } else if (flags
& GDB_O_RDWR
) {
62 if (flags
& GDB_O_APPEND
) {
65 if (flags
& GDB_O_CREAT
) {
68 if (flags
& GDB_O_TRUNC
) {
71 if (flags
& GDB_O_EXCL
) {
78 static bool translate_stat(CPUNios2State
*env
, target_ulong addr
,
83 p
= lock_user(VERIFY_WRITE
, addr
, sizeof(struct gdb_stat
), 0);
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
);
97 /* Windows stat is missing some fields. */
98 p
->gdb_st_blksize
= 0;
101 p
->gdb_st_blksize
= cpu_to_be64(s
->st_blksize
);
102 p
->gdb_st_blocks
= cpu_to_be64(s
->st_blocks
);
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
));
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)) { \
156 void do_nios2_semihosting(CPUNios2State
*env
)
158 CPUState
*cs
= env_cpu(env
);
161 target_ulong arg0
, arg1
, arg2
, arg3
;
167 nr
= env
->regs
[R_ARG0
];
168 args
= env
->regs
[R_ARG1
];
171 gdb_exit(env
->regs
[R_ARG0
]);
172 exit(env
->regs
[R_ARG0
]);
178 if (use_gdb_syscalls()) {
179 gdb_do_syscall(nios2_semi_u32_cb
, "open,%s,%x,%x", arg0
, (int)arg1
,
183 p
= lock_user_string(arg0
);
188 result
= open(p
, translate_openflags(arg2
), arg3
);
189 unlock_user(p
, arg0
, 0);
195 /* Ignore attempts to close stdin/out/err. */
199 if (use_gdb_syscalls()) {
200 gdb_do_syscall(nios2_semi_u32_cb
, "close,%x", arg0
);
215 if (use_gdb_syscalls()) {
216 gdb_do_syscall(nios2_semi_u32_cb
, "read,%x,%x,%x",
220 p
= lock_user(VERIFY_WRITE
, arg1
, len
, 0);
225 result
= read(arg0
, p
, len
);
226 unlock_user(p
, arg1
, len
);
235 if (use_gdb_syscalls()) {
236 gdb_do_syscall(nios2_semi_u32_cb
, "write,%x,%x,%x",
240 p
= lock_user(VERIFY_READ
, arg1
, len
, 1);
245 result
= write(arg0
, p
, len
);
246 unlock_user(p
, arg0
, 0);
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",
262 off
= lseek(arg0
, off
, arg3
);
263 nios2_semi_u64_cb(cs
, off
, errno
);
272 if (use_gdb_syscalls()) {
273 gdb_do_syscall(nios2_semi_u32_cb
, "rename,%s,%s",
274 arg0
, (int)arg1
, arg2
, (int)arg3
);
277 p
= lock_user_string(arg0
);
278 q
= lock_user_string(arg2
);
283 result
= rename(p
, q
);
285 unlock_user(p
, arg0
, 0);
286 unlock_user(q
, arg2
, 0);
292 if (use_gdb_syscalls()) {
293 gdb_do_syscall(nios2_semi_u32_cb
, "unlink,%s",
297 p
= lock_user_string(arg0
);
303 unlock_user(p
, arg0
, 0);
311 if (use_gdb_syscalls()) {
312 gdb_do_syscall(nios2_semi_u32_cb
, "stat,%s,%x",
313 arg0
, (int)arg1
, arg2
);
317 p
= lock_user_string(arg0
);
322 result
= stat(p
, &s
);
323 unlock_user(p
, arg0
, 0);
325 if (result
== 0 && !translate_stat(env
, arg2
, &s
)) {
334 if (use_gdb_syscalls()) {
335 gdb_do_syscall(nios2_semi_u32_cb
, "fstat,%x,%x",
340 result
= fstat(arg0
, &s
);
341 if (result
== 0 && !translate_stat(env
, arg1
, &s
)) {
347 case HOSTED_GETTIMEOFDAY
:
348 /* Only the tv parameter is used. tz is assumed NULL. */
350 if (use_gdb_syscalls()) {
351 gdb_do_syscall(nios2_semi_u32_cb
, "gettimeofday,%x,%x",
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);
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
));
371 if (use_gdb_syscalls()) {
372 gdb_do_syscall(nios2_semi_u32_cb
, "isatty,%x", arg0
);
375 result
= isatty(arg0
);
381 if (use_gdb_syscalls()) {
382 gdb_do_syscall(nios2_semi_u32_cb
, "system,%s",
386 p
= lock_user_string(arg0
);
392 unlock_user(p
, arg0
, 0);
397 qemu_log_mask(LOG_GUEST_ERROR
, "nios2-semihosting: unsupported "
398 "semihosting syscall %d\n", nr
);
402 nios2_semi_u32_cb(cs
, result
, errno
);