xref: /openbmc/qemu/target/mips/tcg/sysemu/mips-semi.c (revision b14df228)
1 /*
2  * Unified Hosting Interface syscalls.
3  *
4  * Copyright (c) 2015 Imagination Technologies
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library 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 GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "qemu/osdep.h"
21 #include "cpu.h"
22 #include "qemu/log.h"
23 #include "exec/gdbstub.h"
24 #include "semihosting/softmmu-uaccess.h"
25 #include "semihosting/semihost.h"
26 #include "semihosting/console.h"
27 #include "semihosting/syscalls.h"
28 #include "internal.h"
29 
30 typedef enum UHIOp {
31     UHI_exit = 1,
32     UHI_open = 2,
33     UHI_close = 3,
34     UHI_read = 4,
35     UHI_write = 5,
36     UHI_lseek = 6,
37     UHI_unlink = 7,
38     UHI_fstat = 8,
39     UHI_argc = 9,
40     UHI_argnlen = 10,
41     UHI_argn = 11,
42     UHI_plog = 13,
43     UHI_assert = 14,
44     UHI_pread = 19,
45     UHI_pwrite = 20,
46     UHI_link = 22
47 } UHIOp;
48 
49 typedef struct UHIStat {
50     int16_t uhi_st_dev;
51     uint16_t uhi_st_ino;
52     uint32_t uhi_st_mode;
53     uint16_t uhi_st_nlink;
54     uint16_t uhi_st_uid;
55     uint16_t uhi_st_gid;
56     int16_t uhi_st_rdev;
57     uint64_t uhi_st_size;
58     uint64_t uhi_st_atime;
59     uint64_t uhi_st_spare1;
60     uint64_t uhi_st_mtime;
61     uint64_t uhi_st_spare2;
62     uint64_t uhi_st_ctime;
63     uint64_t uhi_st_spare3;
64     uint64_t uhi_st_blksize;
65     uint64_t uhi_st_blocks;
66     uint64_t uhi_st_spare4[2];
67 } UHIStat;
68 
69 enum UHIOpenFlags {
70     UHIOpen_RDONLY = 0x0,
71     UHIOpen_WRONLY = 0x1,
72     UHIOpen_RDWR   = 0x2,
73     UHIOpen_APPEND = 0x8,
74     UHIOpen_CREAT  = 0x200,
75     UHIOpen_TRUNC  = 0x400,
76     UHIOpen_EXCL   = 0x800
77 };
78 
79 enum UHIErrno {
80     UHI_EACCESS         = 13,
81     UHI_EAGAIN          = 11,
82     UHI_EBADF           = 9,
83     UHI_EBADMSG         = 77,
84     UHI_EBUSY           = 16,
85     UHI_ECONNRESET      = 104,
86     UHI_EEXIST          = 17,
87     UHI_EFBIG           = 27,
88     UHI_EINTR           = 4,
89     UHI_EINVAL          = 22,
90     UHI_EIO             = 5,
91     UHI_EISDIR          = 21,
92     UHI_ELOOP           = 92,
93     UHI_EMFILE          = 24,
94     UHI_EMLINK          = 31,
95     UHI_ENAMETOOLONG    = 91,
96     UHI_ENETDOWN        = 115,
97     UHI_ENETUNREACH     = 114,
98     UHI_ENFILE          = 23,
99     UHI_ENOBUFS         = 105,
100     UHI_ENOENT          = 2,
101     UHI_ENOMEM          = 12,
102     UHI_ENOSPC          = 28,
103     UHI_ENOSR           = 63,
104     UHI_ENOTCONN        = 128,
105     UHI_ENOTDIR         = 20,
106     UHI_ENXIO           = 6,
107     UHI_EOVERFLOW       = 139,
108     UHI_EPERM           = 1,
109     UHI_EPIPE           = 32,
110     UHI_ERANGE          = 34,
111     UHI_EROFS           = 30,
112     UHI_ESPIPE          = 29,
113     UHI_ETIMEDOUT       = 116,
114     UHI_ETXTBSY         = 26,
115     UHI_EWOULDBLOCK     = 11,
116     UHI_EXDEV           = 18,
117 };
118 
119 static void report_fault(CPUMIPSState *env)
120 {
121     int op = env->active_tc.gpr[25];
122     error_report("Fault during UHI operation %d", op);
123     abort();
124 }
125 
126 static void uhi_cb(CPUState *cs, uint64_t ret, int err)
127 {
128     CPUMIPSState *env = cs->env_ptr;
129 
130 #define E(N) case E##N: err = UHI_E##N; break
131 
132     switch (err) {
133     case 0:
134         break;
135     E(PERM);
136     E(NOENT);
137     E(INTR);
138     E(BADF);
139     E(BUSY);
140     E(EXIST);
141     E(NOTDIR);
142     E(ISDIR);
143     E(INVAL);
144     E(NFILE);
145     E(MFILE);
146     E(FBIG);
147     E(NOSPC);
148     E(SPIPE);
149     E(ROFS);
150     E(NAMETOOLONG);
151     default:
152         err = UHI_EINVAL;
153         break;
154     case EFAULT:
155         report_fault(env);
156     }
157 
158 #undef E
159 
160     env->active_tc.gpr[2] = ret;
161     env->active_tc.gpr[3] = err;
162 }
163 
164 static void uhi_fstat_cb(CPUState *cs, uint64_t ret, int err)
165 {
166     QEMU_BUILD_BUG_ON(sizeof(UHIStat) < sizeof(struct gdb_stat));
167 
168     if (!err) {
169         CPUMIPSState *env = cs->env_ptr;
170         target_ulong addr = env->active_tc.gpr[5];
171         UHIStat *dst = lock_user(VERIFY_WRITE, addr, sizeof(UHIStat), 1);
172         struct gdb_stat s;
173 
174         if (!dst) {
175             report_fault(env);
176         }
177 
178         memcpy(&s, dst, sizeof(struct gdb_stat));
179         memset(dst, 0, sizeof(UHIStat));
180 
181         dst->uhi_st_dev = tswap16(be32_to_cpu(s.gdb_st_dev));
182         dst->uhi_st_ino = tswap16(be32_to_cpu(s.gdb_st_ino));
183         dst->uhi_st_mode = tswap32(be32_to_cpu(s.gdb_st_mode));
184         dst->uhi_st_nlink = tswap16(be32_to_cpu(s.gdb_st_nlink));
185         dst->uhi_st_uid = tswap16(be32_to_cpu(s.gdb_st_uid));
186         dst->uhi_st_gid = tswap16(be32_to_cpu(s.gdb_st_gid));
187         dst->uhi_st_rdev = tswap16(be32_to_cpu(s.gdb_st_rdev));
188         dst->uhi_st_size = tswap64(be64_to_cpu(s.gdb_st_size));
189         dst->uhi_st_atime = tswap64(be32_to_cpu(s.gdb_st_atime));
190         dst->uhi_st_mtime = tswap64(be32_to_cpu(s.gdb_st_mtime));
191         dst->uhi_st_ctime = tswap64(be32_to_cpu(s.gdb_st_ctime));
192         dst->uhi_st_blksize = tswap64(be64_to_cpu(s.gdb_st_blksize));
193         dst->uhi_st_blocks = tswap64(be64_to_cpu(s.gdb_st_blocks));
194 
195         unlock_user(dst, addr, sizeof(UHIStat));
196     }
197 
198     uhi_cb(cs, ret, err);
199 }
200 
201 void mips_semihosting(CPUMIPSState *env)
202 {
203     CPUState *cs = env_cpu(env);
204     target_ulong *gpr = env->active_tc.gpr;
205     const UHIOp op = gpr[25];
206     char *p;
207 
208     switch (op) {
209     case UHI_exit:
210         gdb_exit(gpr[4]);
211         exit(gpr[4]);
212 
213     case UHI_open:
214         {
215             target_ulong fname = gpr[4];
216             int ret = -1;
217 
218             p = lock_user_string(fname);
219             if (!p) {
220                 report_fault(env);
221             }
222             if (!strcmp("/dev/stdin", p)) {
223                 ret = 0;
224             } else if (!strcmp("/dev/stdout", p)) {
225                 ret = 1;
226             } else if (!strcmp("/dev/stderr", p)) {
227                 ret = 2;
228             }
229             unlock_user(p, fname, 0);
230 
231             /* FIXME: reusing a guest fd doesn't seem correct. */
232             if (ret >= 0) {
233                 gpr[2] = ret;
234                 break;
235             }
236 
237             semihost_sys_open(cs, uhi_cb, fname, 0, gpr[5], gpr[6]);
238         }
239         break;
240 
241     case UHI_close:
242         semihost_sys_close(cs, uhi_cb, gpr[4]);
243         break;
244     case UHI_read:
245         semihost_sys_read(cs, uhi_cb, gpr[4], gpr[5], gpr[6]);
246         break;
247     case UHI_write:
248         semihost_sys_write(cs, uhi_cb, gpr[4], gpr[5], gpr[6]);
249         break;
250     case UHI_lseek:
251         semihost_sys_lseek(cs, uhi_cb, gpr[4], gpr[5], gpr[6]);
252         break;
253     case UHI_unlink:
254         semihost_sys_remove(cs, uhi_cb, gpr[4], 0);
255         break;
256     case UHI_fstat:
257         semihost_sys_fstat(cs, uhi_fstat_cb, gpr[4], gpr[5]);
258         break;
259 
260     case UHI_argc:
261         gpr[2] = semihosting_get_argc();
262         break;
263     case UHI_argnlen:
264         {
265             const char *s = semihosting_get_arg(gpr[4]);
266             gpr[2] = s ? strlen(s) : -1;
267         }
268         break;
269     case UHI_argn:
270         {
271             const char *s = semihosting_get_arg(gpr[4]);
272             target_ulong addr;
273             size_t len;
274 
275             if (!s) {
276                 gpr[2] = -1;
277                 break;
278             }
279             len = strlen(s) + 1;
280             addr = gpr[5];
281             p = lock_user(VERIFY_WRITE, addr, len, 0);
282             if (!p) {
283                 report_fault(env);
284             }
285             memcpy(p, s, len);
286             unlock_user(p, addr, len);
287             gpr[2] = 0;
288         }
289         break;
290 
291     case UHI_plog:
292         {
293             target_ulong addr = gpr[4];
294             ssize_t len = target_strlen(addr);
295             GString *str;
296             char *pct_d;
297 
298             if (len < 0) {
299                 report_fault(env);
300             }
301             p = lock_user(VERIFY_READ, addr, len, 1);
302             if (!p) {
303                 report_fault(env);
304             }
305 
306             pct_d = strstr(p, "%d");
307             if (!pct_d) {
308                 unlock_user(p, addr, 0);
309                 semihost_sys_write(cs, uhi_cb, 2, addr, len);
310                 break;
311             }
312 
313             str = g_string_new_len(p, pct_d - p);
314             g_string_append_printf(str, "%d%s", (int)gpr[5], pct_d + 2);
315             unlock_user(p, addr, 0);
316 
317             /*
318              * When we're using gdb, we need a guest address, so
319              * drop the string onto the stack below the stack pointer.
320              */
321             if (use_gdb_syscalls()) {
322                 addr = gpr[29] - str->len;
323                 p = lock_user(VERIFY_WRITE, addr, str->len, 0);
324                 if (!p) {
325                     report_fault(env);
326                 }
327                 memcpy(p, str->str, str->len);
328                 unlock_user(p, addr, str->len);
329                 semihost_sys_write(cs, uhi_cb, 2, addr, str->len);
330             } else {
331                 gpr[2] = qemu_semihosting_console_write(str->str, str->len);
332             }
333             g_string_free(str, true);
334         }
335         break;
336 
337     case UHI_assert:
338         {
339             const char *msg, *file;
340 
341             msg = lock_user_string(gpr[4]);
342             if (!msg) {
343                 msg = "<EFAULT>";
344             }
345             file = lock_user_string(gpr[5]);
346             if (!file) {
347                 file = "<EFAULT>";
348             }
349 
350             error_report("UHI assertion \"%s\": file \"%s\", line %d",
351                          msg, file, (int)gpr[6]);
352             abort();
353         }
354 
355     default:
356         error_report("Unknown UHI operation %d", op);
357         abort();
358     }
359     return;
360 }
361