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