]>
Commit | Line | Data |
---|---|---|
a87295e8 PB |
1 | /* |
2 | * m68k/ColdFire Semihosting syscall interface | |
5fafdf24 | 3 | * |
a87295e8 PB |
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 | |
8167ee88 | 17 | * along with this program; if not, see <http://www.gnu.org/licenses/>. |
1321d844 PM |
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 | |
a87295e8 PB |
22 | */ |
23 | ||
d8416665 | 24 | #include "qemu/osdep.h" |
a87295e8 PB |
25 | |
26 | #include "cpu.h" | |
c566080c | 27 | #include "gdbstub/syscalls.h" |
4ea5fe99 | 28 | #include "gdbstub/helpers.h" |
95027250 | 29 | #include "semihosting/syscalls.h" |
f14eced5 | 30 | #include "semihosting/uaccess.h" |
5601d241 | 31 | #include "hw/boards.h" |
63c91552 | 32 | #include "qemu/log.h" |
a87295e8 PB |
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 | ||
7327e602 RH |
49 | static int host_to_gdb_errno(int err) |
50 | { | |
51 | #define E(X) case E##X: return GDB_E##X | |
52 | switch (err) { | |
53 | E(PERM); | |
54 | E(NOENT); | |
55 | E(INTR); | |
56 | E(BADF); | |
57 | E(ACCES); | |
58 | E(FAULT); | |
59 | E(BUSY); | |
60 | E(EXIST); | |
61 | E(NODEV); | |
62 | E(NOTDIR); | |
63 | E(ISDIR); | |
64 | E(INVAL); | |
65 | E(NFILE); | |
66 | E(MFILE); | |
67 | E(FBIG); | |
68 | E(NOSPC); | |
69 | E(SPIPE); | |
70 | E(ROFS); | |
71 | E(NAMETOOLONG); | |
72 | default: | |
73 | return GDB_EUNKNOWN; | |
74 | } | |
75 | #undef E | |
76 | } | |
77 | ||
ab294b6c | 78 | static void m68k_semi_u32_cb(CPUState *cs, uint64_t ret, int err) |
1073bfd8 | 79 | { |
e22a4560 | 80 | CPUM68KState *env = cpu_env(cs); |
ab294b6c | 81 | |
1073bfd8 PM |
82 | target_ulong args = env->dregs[1]; |
83 | if (put_user_u32(ret, args) || | |
7327e602 | 84 | put_user_u32(host_to_gdb_errno(err), args + 4)) { |
808d77bc LMP |
85 | /* |
86 | * The m68k semihosting ABI does not provide any way to report this | |
1073bfd8 PM |
87 | * error to the guest, so the best we can do is log it in qemu. |
88 | * It is always a guest error not to pass us a valid argument block. | |
89 | */ | |
90 | qemu_log_mask(LOG_GUEST_ERROR, "m68k-semihosting: return value " | |
91 | "discarded because argument block not writable\n"); | |
92 | } | |
93 | } | |
94 | ||
ab294b6c | 95 | static void m68k_semi_u64_cb(CPUState *cs, uint64_t ret, int err) |
1073bfd8 | 96 | { |
e22a4560 | 97 | CPUM68KState *env = cpu_env(cs); |
ab294b6c | 98 | |
1073bfd8 PM |
99 | target_ulong args = env->dregs[1]; |
100 | if (put_user_u32(ret >> 32, args) || | |
101 | put_user_u32(ret, args + 4) || | |
7327e602 | 102 | put_user_u32(host_to_gdb_errno(err), args + 8)) { |
1073bfd8 PM |
103 | /* No way to report this via m68k semihosting ABI; just log it */ |
104 | qemu_log_mask(LOG_GUEST_ERROR, "m68k-semihosting: return value " | |
105 | "discarded because argument block not writable\n"); | |
106 | } | |
107 | } | |
108 | ||
808d77bc LMP |
109 | /* |
110 | * Read the input value from the argument block; fail the semihosting | |
7ba6c104 PM |
111 | * call if the memory read fails. |
112 | */ | |
113 | #define GET_ARG(n) do { \ | |
114 | if (get_user_ual(arg ## n, args + (n) * 4)) { \ | |
7ba6c104 PM |
115 | goto failed; \ |
116 | } \ | |
117 | } while (0) | |
118 | ||
95027250 RH |
119 | #define GET_ARG64(n) do { \ |
120 | if (get_user_ual(arg ## n, args + (n) * 4)) { \ | |
121 | goto failed64; \ | |
122 | } \ | |
123 | } while (0) | |
124 | ||
125 | ||
a87295e8 PB |
126 | void do_m68k_semihosting(CPUM68KState *env, int nr) |
127 | { | |
ab294b6c | 128 | CPUState *cs = env_cpu(env); |
a87295e8 | 129 | uint32_t args; |
7ba6c104 | 130 | target_ulong arg0, arg1, arg2, arg3; |
a87295e8 PB |
131 | |
132 | args = env->dregs[1]; | |
133 | switch (nr) { | |
134 | case HOSTED_EXIT: | |
5fae5110 KP |
135 | gdb_exit(env->dregs[1]); |
136 | exit(env->dregs[1]); | |
95027250 | 137 | |
a87295e8 | 138 | case HOSTED_OPEN: |
7ba6c104 PM |
139 | GET_ARG(0); |
140 | GET_ARG(1); | |
141 | GET_ARG(2); | |
142 | GET_ARG(3); | |
95027250 | 143 | semihost_sys_open(cs, m68k_semi_u32_cb, arg0, arg1, arg2, arg3); |
a87295e8 | 144 | break; |
95027250 | 145 | |
a87295e8 | 146 | case HOSTED_CLOSE: |
95027250 RH |
147 | GET_ARG(0); |
148 | semihost_sys_close(cs, m68k_semi_u32_cb, arg0); | |
149 | break; | |
150 | ||
a87295e8 | 151 | case HOSTED_READ: |
7ba6c104 PM |
152 | GET_ARG(0); |
153 | GET_ARG(1); | |
154 | GET_ARG(2); | |
95027250 | 155 | semihost_sys_read(cs, m68k_semi_u32_cb, arg0, arg1, arg2); |
a87295e8 | 156 | break; |
95027250 | 157 | |
a87295e8 | 158 | case HOSTED_WRITE: |
7ba6c104 PM |
159 | GET_ARG(0); |
160 | GET_ARG(1); | |
161 | GET_ARG(2); | |
95027250 | 162 | semihost_sys_write(cs, m68k_semi_u32_cb, arg0, arg1, arg2); |
a87295e8 | 163 | break; |
95027250 | 164 | |
a87295e8 | 165 | case HOSTED_LSEEK: |
95027250 RH |
166 | GET_ARG64(0); |
167 | GET_ARG64(1); | |
168 | GET_ARG64(2); | |
169 | GET_ARG64(3); | |
170 | semihost_sys_lseek(cs, m68k_semi_u64_cb, arg0, | |
8caaae73 | 171 | deposit64(arg2, 32, 32, arg1), arg3); |
95027250 RH |
172 | break; |
173 | ||
a87295e8 | 174 | case HOSTED_RENAME: |
7ba6c104 PM |
175 | GET_ARG(0); |
176 | GET_ARG(1); | |
177 | GET_ARG(2); | |
178 | GET_ARG(3); | |
95027250 | 179 | semihost_sys_rename(cs, m68k_semi_u32_cb, arg0, arg1, arg2, arg3); |
a87295e8 | 180 | break; |
95027250 | 181 | |
a87295e8 | 182 | case HOSTED_UNLINK: |
7ba6c104 PM |
183 | GET_ARG(0); |
184 | GET_ARG(1); | |
95027250 | 185 | semihost_sys_remove(cs, m68k_semi_u32_cb, arg0, arg1); |
a87295e8 | 186 | break; |
95027250 | 187 | |
a87295e8 | 188 | case HOSTED_STAT: |
7ba6c104 PM |
189 | GET_ARG(0); |
190 | GET_ARG(1); | |
191 | GET_ARG(2); | |
95027250 | 192 | semihost_sys_stat(cs, m68k_semi_u32_cb, arg0, arg1, arg2); |
a87295e8 | 193 | break; |
95027250 | 194 | |
a87295e8 | 195 | case HOSTED_FSTAT: |
7ba6c104 PM |
196 | GET_ARG(0); |
197 | GET_ARG(1); | |
95027250 | 198 | semihost_sys_fstat(cs, m68k_semi_u32_cb, arg0, arg1); |
a87295e8 | 199 | break; |
95027250 | 200 | |
a87295e8 | 201 | case HOSTED_GETTIMEOFDAY: |
7ba6c104 PM |
202 | GET_ARG(0); |
203 | GET_ARG(1); | |
95027250 | 204 | semihost_sys_gettimeofday(cs, m68k_semi_u32_cb, arg0, arg1); |
a87295e8 | 205 | break; |
95027250 | 206 | |
a87295e8 | 207 | case HOSTED_ISATTY: |
7ba6c104 | 208 | GET_ARG(0); |
95027250 | 209 | semihost_sys_isatty(cs, m68k_semi_u32_cb, arg0); |
a87295e8 | 210 | break; |
95027250 | 211 | |
a87295e8 | 212 | case HOSTED_SYSTEM: |
7ba6c104 PM |
213 | GET_ARG(0); |
214 | GET_ARG(1); | |
95027250 | 215 | semihost_sys_system(cs, m68k_semi_u32_cb, arg0, arg1); |
a87295e8 | 216 | break; |
95027250 | 217 | |
a87295e8 | 218 | case HOSTED_INIT_SIM: |
808d77bc LMP |
219 | /* |
220 | * FIXME: This is wrong for boards where RAM does not start at | |
221 | * address zero. | |
222 | */ | |
5601d241 PB |
223 | env->dregs[1] = current_machine->ram_size; |
224 | env->aregs[7] = current_machine->ram_size; | |
a87295e8 | 225 | return; |
95027250 | 226 | |
a87295e8 | 227 | default: |
a8d92fd8 | 228 | cpu_abort(env_cpu(env), "Unsupported semihosting syscall %d\n", nr); |
95027250 RH |
229 | |
230 | failed: | |
231 | m68k_semi_u32_cb(cs, -1, EFAULT); | |
232 | break; | |
233 | failed64: | |
234 | m68k_semi_u64_cb(cs, -1, EFAULT); | |
235 | break; | |
a87295e8 | 236 | } |
a87295e8 | 237 | } |