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