xref: /openbmc/qemu/target/mips/tcg/sysemu/mips-semi.c (revision 4e245a9e)
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 "semihosting/softmmu-uaccess.h"
24 #include "semihosting/semihost.h"
25 #include "semihosting/console.h"
26 #include "internal.h"
27 
28 typedef enum UHIOp {
29     UHI_exit = 1,
30     UHI_open = 2,
31     UHI_close = 3,
32     UHI_read = 4,
33     UHI_write = 5,
34     UHI_lseek = 6,
35     UHI_unlink = 7,
36     UHI_fstat = 8,
37     UHI_argc = 9,
38     UHI_argnlen = 10,
39     UHI_argn = 11,
40     UHI_plog = 13,
41     UHI_assert = 14,
42     UHI_pread = 19,
43     UHI_pwrite = 20,
44     UHI_link = 22
45 } UHIOp;
46 
47 typedef struct UHIStat {
48     int16_t uhi_st_dev;
49     uint16_t uhi_st_ino;
50     uint32_t uhi_st_mode;
51     uint16_t uhi_st_nlink;
52     uint16_t uhi_st_uid;
53     uint16_t uhi_st_gid;
54     int16_t uhi_st_rdev;
55     uint64_t uhi_st_size;
56     uint64_t uhi_st_atime;
57     uint64_t uhi_st_spare1;
58     uint64_t uhi_st_mtime;
59     uint64_t uhi_st_spare2;
60     uint64_t uhi_st_ctime;
61     uint64_t uhi_st_spare3;
62     uint64_t uhi_st_blksize;
63     uint64_t uhi_st_blocks;
64     uint64_t uhi_st_spare4[2];
65 } UHIStat;
66 
67 enum UHIOpenFlags {
68     UHIOpen_RDONLY = 0x0,
69     UHIOpen_WRONLY = 0x1,
70     UHIOpen_RDWR   = 0x2,
71     UHIOpen_APPEND = 0x8,
72     UHIOpen_CREAT  = 0x200,
73     UHIOpen_TRUNC  = 0x400,
74     UHIOpen_EXCL   = 0x800
75 };
76 
77 enum UHIErrno {
78     UHI_EACCESS         = 13,
79     UHI_EAGAIN          = 11,
80     UHI_EBADF           = 9,
81     UHI_EBADMSG         = 77,
82     UHI_EBUSY           = 16,
83     UHI_ECONNRESET      = 104,
84     UHI_EEXIST          = 17,
85     UHI_EFBIG           = 27,
86     UHI_EINTR           = 4,
87     UHI_EINVAL          = 22,
88     UHI_EIO             = 5,
89     UHI_EISDIR          = 21,
90     UHI_ELOOP           = 92,
91     UHI_EMFILE          = 24,
92     UHI_EMLINK          = 31,
93     UHI_ENAMETOOLONG    = 91,
94     UHI_ENETDOWN        = 115,
95     UHI_ENETUNREACH     = 114,
96     UHI_ENFILE          = 23,
97     UHI_ENOBUFS         = 105,
98     UHI_ENOENT          = 2,
99     UHI_ENOMEM          = 12,
100     UHI_ENOSPC          = 28,
101     UHI_ENOSR           = 63,
102     UHI_ENOTCONN        = 128,
103     UHI_ENOTDIR         = 20,
104     UHI_ENXIO           = 6,
105     UHI_EOVERFLOW       = 139,
106     UHI_EPERM           = 1,
107     UHI_EPIPE           = 32,
108     UHI_ERANGE          = 34,
109     UHI_EROFS           = 30,
110     UHI_ESPIPE          = 29,
111     UHI_ETIMEDOUT       = 116,
112     UHI_ETXTBSY         = 26,
113     UHI_EWOULDBLOCK     = 11,
114     UHI_EXDEV           = 18,
115 };
116 
117 static int errno_mips(int host_errno)
118 {
119     /* Errno values taken from asm-mips/errno.h */
120     switch (host_errno) {
121     case 0:             return 0;
122     case ENAMETOOLONG:  return 78;
123 #ifdef EOVERFLOW
124     case EOVERFLOW:     return 79;
125 #endif
126 #ifdef ELOOP
127     case ELOOP:         return 90;
128 #endif
129     default:            return EINVAL;
130     }
131 }
132 
133 static int copy_stat_to_target(CPUMIPSState *env, const struct stat *src,
134                                target_ulong vaddr)
135 {
136     hwaddr len = sizeof(struct UHIStat);
137     UHIStat *dst = lock_user(VERIFY_WRITE, vaddr, len, 0);
138     if (!dst) {
139         errno = EFAULT;
140         return -1;
141     }
142 
143     dst->uhi_st_dev = tswap16(src->st_dev);
144     dst->uhi_st_ino = tswap16(src->st_ino);
145     dst->uhi_st_mode = tswap32(src->st_mode);
146     dst->uhi_st_nlink = tswap16(src->st_nlink);
147     dst->uhi_st_uid = tswap16(src->st_uid);
148     dst->uhi_st_gid = tswap16(src->st_gid);
149     dst->uhi_st_rdev = tswap16(src->st_rdev);
150     dst->uhi_st_size = tswap64(src->st_size);
151     dst->uhi_st_atime = tswap64(src->st_atime);
152     dst->uhi_st_mtime = tswap64(src->st_mtime);
153     dst->uhi_st_ctime = tswap64(src->st_ctime);
154 #ifdef _WIN32
155     dst->uhi_st_blksize = 0;
156     dst->uhi_st_blocks = 0;
157 #else
158     dst->uhi_st_blksize = tswap64(src->st_blksize);
159     dst->uhi_st_blocks = tswap64(src->st_blocks);
160 #endif
161     unlock_user(dst, vaddr, len);
162     return 0;
163 }
164 
165 static int get_open_flags(target_ulong target_flags)
166 {
167     int open_flags = 0;
168 
169     if (target_flags & UHIOpen_RDWR) {
170         open_flags |= O_RDWR;
171     } else if (target_flags & UHIOpen_WRONLY) {
172         open_flags |= O_WRONLY;
173     } else {
174         open_flags |= O_RDONLY;
175     }
176 
177     open_flags |= (target_flags & UHIOpen_APPEND) ? O_APPEND : 0;
178     open_flags |= (target_flags & UHIOpen_CREAT)  ? O_CREAT  : 0;
179     open_flags |= (target_flags & UHIOpen_TRUNC)  ? O_TRUNC  : 0;
180     open_flags |= (target_flags & UHIOpen_EXCL)   ? O_EXCL   : 0;
181 
182     return open_flags;
183 }
184 
185 static int write_to_file(CPUMIPSState *env, target_ulong fd,
186                          target_ulong vaddr, target_ulong len)
187 {
188     int num_of_bytes;
189     void *dst = lock_user(VERIFY_READ, vaddr, len, 1);
190     if (!dst) {
191         errno = EFAULT;
192         return -1;
193     }
194 
195     num_of_bytes = write(fd, dst, len);
196 
197     unlock_user(dst, vaddr, 0);
198     return num_of_bytes;
199 }
200 
201 static int read_from_file(CPUMIPSState *env, target_ulong fd,
202                           target_ulong vaddr, target_ulong len)
203 {
204     int num_of_bytes;
205     void *dst = lock_user(VERIFY_WRITE, vaddr, len, 0);
206     if (!dst) {
207         errno = EFAULT;
208         return -1;
209     }
210 
211     num_of_bytes = read(fd, dst, len);
212 
213     unlock_user(dst, vaddr, len);
214     return num_of_bytes;
215 }
216 
217 static int copy_argn_to_target(CPUMIPSState *env, int arg_num,
218                                target_ulong vaddr)
219 {
220     int strsize = strlen(semihosting_get_arg(arg_num)) + 1;
221     char *dst = lock_user(VERIFY_WRITE, vaddr, strsize, 0);
222     if (!dst) {
223         return -1;
224     }
225 
226     strcpy(dst, semihosting_get_arg(arg_num));
227 
228     unlock_user(dst, vaddr, strsize);
229     return 0;
230 }
231 
232 #define GET_TARGET_STRING(p, addr)              \
233     do {                                        \
234         p = lock_user_string(addr);             \
235         if (!p) {                               \
236             gpr[2] = -1;                        \
237             gpr[3] = EFAULT;                    \
238             return;                             \
239         }                                       \
240     } while (0)
241 
242 #define GET_TARGET_STRINGS_2(p, addr, p2, addr2)        \
243     do {                                                \
244         p = lock_user_string(addr);                     \
245         if (!p) {                                       \
246             gpr[2] = -1;                                \
247             gpr[3] = EFAULT;                            \
248             return;                                     \
249         }                                               \
250         p2 = lock_user_string(addr2);                   \
251         if (!p2) {                                      \
252             unlock_user(p, addr, 0);                    \
253             gpr[2] = -1;                                \
254             gpr[3] = EFAULT;                            \
255             return;                                     \
256         }                                               \
257     } while (0)
258 
259 #define FREE_TARGET_STRING(p, gpr)              \
260     do {                                        \
261         unlock_user(p, gpr, 0);                 \
262     } while (0)
263 
264 void mips_semihosting(CPUMIPSState *env)
265 {
266     target_ulong *gpr = env->active_tc.gpr;
267     const UHIOp op = gpr[25];
268     char *p, *p2;
269 
270     switch (op) {
271     case UHI_exit:
272         qemu_log("UHI(%d): exit(%d)\n", op, (int)gpr[4]);
273         exit(gpr[4]);
274     case UHI_open:
275         GET_TARGET_STRING(p, gpr[4]);
276         if (!strcmp("/dev/stdin", p)) {
277             gpr[2] = 0;
278         } else if (!strcmp("/dev/stdout", p)) {
279             gpr[2] = 1;
280         } else if (!strcmp("/dev/stderr", p)) {
281             gpr[2] = 2;
282         } else {
283             gpr[2] = open(p, get_open_flags(gpr[5]), gpr[6]);
284             gpr[3] = errno_mips(errno);
285         }
286         FREE_TARGET_STRING(p, gpr[4]);
287         break;
288     case UHI_close:
289         if (gpr[4] < 3) {
290             /* ignore closing stdin/stdout/stderr */
291             gpr[2] = 0;
292             return;
293         }
294         gpr[2] = close(gpr[4]);
295         gpr[3] = errno_mips(errno);
296         break;
297     case UHI_read:
298         gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6]);
299         gpr[3] = errno_mips(errno);
300         break;
301     case UHI_write:
302         gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6]);
303         gpr[3] = errno_mips(errno);
304         break;
305     case UHI_lseek:
306         gpr[2] = lseek(gpr[4], gpr[5], gpr[6]);
307         gpr[3] = errno_mips(errno);
308         break;
309     case UHI_unlink:
310         GET_TARGET_STRING(p, gpr[4]);
311         gpr[2] = remove(p);
312         gpr[3] = errno_mips(errno);
313         FREE_TARGET_STRING(p, gpr[4]);
314         break;
315     case UHI_fstat:
316         {
317             struct stat sbuf;
318             memset(&sbuf, 0, sizeof(sbuf));
319             gpr[2] = fstat(gpr[4], &sbuf);
320             gpr[3] = errno_mips(errno);
321             if (gpr[2]) {
322                 return;
323             }
324             gpr[2] = copy_stat_to_target(env, &sbuf, gpr[5]);
325             gpr[3] = errno_mips(errno);
326         }
327         break;
328     case UHI_argc:
329         gpr[2] = semihosting_get_argc();
330         break;
331     case UHI_argnlen:
332         if (gpr[4] >= semihosting_get_argc()) {
333             gpr[2] = -1;
334             return;
335         }
336         gpr[2] = strlen(semihosting_get_arg(gpr[4]));
337         break;
338     case UHI_argn:
339         if (gpr[4] >= semihosting_get_argc()) {
340             gpr[2] = -1;
341             return;
342         }
343         gpr[2] = copy_argn_to_target(env, gpr[4], gpr[5]);
344         break;
345     case UHI_plog:
346         GET_TARGET_STRING(p, gpr[4]);
347         p2 = strstr(p, "%d");
348         if (p2) {
349             int char_num = p2 - p;
350             GString *s = g_string_new_len(p, char_num);
351             g_string_append_printf(s, "%d%s", (int)gpr[5], p2 + 2);
352             gpr[2] = qemu_semihosting_log_out(s->str, s->len);
353             g_string_free(s, true);
354         } else {
355             gpr[2] = qemu_semihosting_log_out(p, strlen(p));
356         }
357         FREE_TARGET_STRING(p, gpr[4]);
358         break;
359     case UHI_assert:
360         GET_TARGET_STRINGS_2(p, gpr[4], p2, gpr[5]);
361         printf("assertion '");
362         printf("\"%s\"", p);
363         printf("': file \"%s\", line %d\n", p2, (int)gpr[6]);
364         FREE_TARGET_STRING(p2, gpr[5]);
365         FREE_TARGET_STRING(p, gpr[4]);
366         abort();
367         break;
368 #ifndef _WIN32
369     case UHI_link:
370         GET_TARGET_STRINGS_2(p, gpr[4], p2, gpr[5]);
371         gpr[2] = link(p, p2);
372         gpr[3] = errno_mips(errno);
373         FREE_TARGET_STRING(p2, gpr[5]);
374         FREE_TARGET_STRING(p, gpr[4]);
375         break;
376 #endif
377     default:
378         fprintf(stderr, "Unknown UHI operation %d\n", op);
379         abort();
380     }
381     return;
382 }
383