master
c 208 lines 5.87 KB
Raw
1 /*
2 * m68k/ColdFire Semihosting syscall interface
3 *
4 * Copyright (c) 2005-2007 CodeSourcery.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
18 *
19 * The semihosting protocol implemented here is described in the
20 * libgloss sources:
21 * https://sourceware.org/git/?p=newlib-cygwin.git;a=blob;f=libgloss/m68k/m68k-semi.txt;hb=HEAD
22 */
23
24 #include "qemu/osdep.h"
25 #include "exec/target_long.h"
26 #include "cpu.h"
27 #include "gdbstub/syscalls.h"
28 #include "gdbstub/helpers.h"
29 #include "semihosting/syscalls.h"
30 #include "semihosting/uaccess.h"
31 #include "hw/core/boards.h"
32 #include "qemu/log.h"
33
34 #define HOSTED_EXIT 0
35 #define HOSTED_INIT_SIM 1
36 #define HOSTED_OPEN 2
37 #define HOSTED_CLOSE 3
38 #define HOSTED_READ 4
39 #define HOSTED_WRITE 5
40 #define HOSTED_LSEEK 6
41 #define HOSTED_RENAME 7
42 #define HOSTED_UNLINK 8
43 #define HOSTED_STAT 9
44 #define HOSTED_FSTAT 10
45 #define HOSTED_GETTIMEOFDAY 11
46 #define HOSTED_ISATTY 12
47 #define HOSTED_SYSTEM 13
48
49 static void m68k_semi_u32_cb(CPUState *cs, uint64_t ret, int err)
50 {
51 CPUM68KState *env = cpu_env(cs);
52
53 target_ulong args = env->dregs[1];
54 if (put_user_u32(ret, args) ||
55 put_user_u32(host_to_gdb_errno(err), args + 4)) {
56 /*
57 * The m68k semihosting ABI does not provide any way to report this
58 * error to the guest, so the best we can do is log it in qemu.
59 * It is always a guest error not to pass us a valid argument block.
60 */
61 qemu_log_mask(LOG_GUEST_ERROR, "m68k-semihosting: return value "
62 "discarded because argument block not writable\n");
63 }
64 }
65
66 static void m68k_semi_u64_cb(CPUState *cs, uint64_t ret, int err)
67 {
68 CPUM68KState *env = cpu_env(cs);
69
70 target_ulong args = env->dregs[1];
71 if (put_user_u32(ret >> 32, args) ||
72 put_user_u32(ret, args + 4) ||
73 put_user_u32(host_to_gdb_errno(err), args + 8)) {
74 /* No way to report this via m68k semihosting ABI; just log it */
75 qemu_log_mask(LOG_GUEST_ERROR, "m68k-semihosting: return value "
76 "discarded because argument block not writable\n");
77 }
78 }
79
80 /*
81 * Read the input value from the argument block; fail the semihosting
82 * call if the memory read fails.
83 */
84 #define GET_ARG(n) do { \
85 if (get_user_ual(arg ## n, args + (n) * 4)) { \
86 goto failed; \
87 } \
88 } while (0)
89
90 #define GET_ARG64(n) do { \
91 if (get_user_ual(arg ## n, args + (n) * 4)) { \
92 goto failed64; \
93 } \
94 } while (0)
95
96
97 void do_m68k_semihosting(CPUM68KState *env, int nr)
98 {
99 CPUState *cs = env_cpu(env);
100 uint32_t args;
101 target_ulong arg0, arg1, arg2, arg3;
102
103 args = env->dregs[1];
104 switch (nr) {
105 case HOSTED_EXIT:
106 gdb_exit(env->dregs[1]);
107 exit(env->dregs[1]);
108
109 case HOSTED_OPEN:
110 GET_ARG(0);
111 GET_ARG(1);
112 GET_ARG(2);
113 GET_ARG(3);
114 semihost_sys_open(cs, m68k_semi_u32_cb, arg0, arg1, arg2, arg3);
115 break;
116
117 case HOSTED_CLOSE:
118 GET_ARG(0);
119 semihost_sys_close(cs, m68k_semi_u32_cb, arg0);
120 break;
121
122 case HOSTED_READ:
123 GET_ARG(0);
124 GET_ARG(1);
125 GET_ARG(2);
126 semihost_sys_read(cs, m68k_semi_u32_cb, arg0, arg1, arg2);
127 break;
128
129 case HOSTED_WRITE:
130 GET_ARG(0);
131 GET_ARG(1);
132 GET_ARG(2);
133 semihost_sys_write(cs, m68k_semi_u32_cb, arg0, arg1, arg2);
134 break;
135
136 case HOSTED_LSEEK:
137 GET_ARG64(0);
138 GET_ARG64(1);
139 GET_ARG64(2);
140 GET_ARG64(3);
141 semihost_sys_lseek(cs, m68k_semi_u64_cb, arg0,
142 deposit64(arg2, 32, 32, arg1), arg3);
143 break;
144
145 case HOSTED_RENAME:
146 GET_ARG(0);
147 GET_ARG(1);
148 GET_ARG(2);
149 GET_ARG(3);
150 semihost_sys_rename(cs, m68k_semi_u32_cb, arg0, arg1, arg2, arg3);
151 break;
152
153 case HOSTED_UNLINK:
154 GET_ARG(0);
155 GET_ARG(1);
156 semihost_sys_remove(cs, m68k_semi_u32_cb, arg0, arg1);
157 break;
158
159 case HOSTED_STAT:
160 GET_ARG(0);
161 GET_ARG(1);
162 GET_ARG(2);
163 semihost_sys_stat(cs, m68k_semi_u32_cb, arg0, arg1, arg2);
164 break;
165
166 case HOSTED_FSTAT:
167 GET_ARG(0);
168 GET_ARG(1);
169 semihost_sys_fstat(cs, m68k_semi_u32_cb, arg0, arg1);
170 break;
171
172 case HOSTED_GETTIMEOFDAY:
173 GET_ARG(0);
174 GET_ARG(1);
175 semihost_sys_gettimeofday(cs, m68k_semi_u32_cb, arg0, arg1);
176 break;
177
178 case HOSTED_ISATTY:
179 GET_ARG(0);
180 semihost_sys_isatty(cs, m68k_semi_u32_cb, arg0);
181 break;
182
183 case HOSTED_SYSTEM:
184 GET_ARG(0);
185 GET_ARG(1);
186 semihost_sys_system(cs, m68k_semi_u32_cb, arg0, arg1);
187 break;
188
189 case HOSTED_INIT_SIM:
190 /*
191 * FIXME: This is wrong for boards where RAM does not start at
192 * address zero.
193 */
194 env->dregs[1] = current_machine->ram_size;
195 env->aregs[7] = current_machine->ram_size;
196 return;
197
198 default:
199 cpu_abort(env_cpu(env), "Unsupported semihosting syscall %d\n", nr);
200
201 failed:
202 m68k_semi_u32_cb(cs, -1, EFAULT);
203 break;
204 failed64:
205 m68k_semi_u64_cb(cs, -1, EFAULT);
206 break;
207 }
208 }