xref: /qemu/target/mips/tcg/sysemu/mips-semi.c (revision c99fb3a5)
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/helper-proto.h"
24 #include "exec/softmmu-semi.h"
25 #include "semihosting/semihost.h"
26 #include "semihosting/console.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 /* Errno values taken from asm-mips/errno.h */
78 static const uint16_t host_to_mips_errno[] = {
79     [ENAMETOOLONG] = 78,
80 #ifdef EOVERFLOW
81     [EOVERFLOW]    = 79,
82 #endif
83 #ifdef ELOOP
84     [ELOOP]        = 90,
85 #endif
86 };
87 
88 static int errno_mips(int err)
89 {
90     if (err < 0 || err >= ARRAY_SIZE(host_to_mips_errno)) {
91         return EINVAL;
92     } else if (host_to_mips_errno[err]) {
93         return host_to_mips_errno[err];
94     } else {
95         return err;
96     }
97 }
98 
99 static int copy_stat_to_target(CPUMIPSState *env, const struct stat *src,
100                                target_ulong vaddr)
101 {
102     hwaddr len = sizeof(struct UHIStat);
103     UHIStat *dst = lock_user(VERIFY_WRITE, vaddr, len, 0);
104     if (!dst) {
105         errno = EFAULT;
106         return -1;
107     }
108 
109     dst->uhi_st_dev = tswap16(src->st_dev);
110     dst->uhi_st_ino = tswap16(src->st_ino);
111     dst->uhi_st_mode = tswap32(src->st_mode);
112     dst->uhi_st_nlink = tswap16(src->st_nlink);
113     dst->uhi_st_uid = tswap16(src->st_uid);
114     dst->uhi_st_gid = tswap16(src->st_gid);
115     dst->uhi_st_rdev = tswap16(src->st_rdev);
116     dst->uhi_st_size = tswap64(src->st_size);
117     dst->uhi_st_atime = tswap64(src->st_atime);
118     dst->uhi_st_mtime = tswap64(src->st_mtime);
119     dst->uhi_st_ctime = tswap64(src->st_ctime);
120 #ifdef _WIN32
121     dst->uhi_st_blksize = 0;
122     dst->uhi_st_blocks = 0;
123 #else
124     dst->uhi_st_blksize = tswap64(src->st_blksize);
125     dst->uhi_st_blocks = tswap64(src->st_blocks);
126 #endif
127     unlock_user(dst, vaddr, len);
128     return 0;
129 }
130 
131 static int get_open_flags(target_ulong target_flags)
132 {
133     int open_flags = 0;
134 
135     if (target_flags & UHIOpen_RDWR) {
136         open_flags |= O_RDWR;
137     } else if (target_flags & UHIOpen_WRONLY) {
138         open_flags |= O_WRONLY;
139     } else {
140         open_flags |= O_RDONLY;
141     }
142 
143     open_flags |= (target_flags & UHIOpen_APPEND) ? O_APPEND : 0;
144     open_flags |= (target_flags & UHIOpen_CREAT)  ? O_CREAT  : 0;
145     open_flags |= (target_flags & UHIOpen_TRUNC)  ? O_TRUNC  : 0;
146     open_flags |= (target_flags & UHIOpen_EXCL)   ? O_EXCL   : 0;
147 
148     return open_flags;
149 }
150 
151 static int write_to_file(CPUMIPSState *env, target_ulong fd, target_ulong vaddr,
152                          target_ulong len, target_ulong offset)
153 {
154     int num_of_bytes;
155     void *dst = lock_user(VERIFY_READ, vaddr, len, 1);
156     if (!dst) {
157         errno = EFAULT;
158         return -1;
159     }
160 
161     if (offset) {
162 #ifdef _WIN32
163         num_of_bytes = 0;
164 #else
165         num_of_bytes = pwrite(fd, dst, len, offset);
166 #endif
167     } else {
168         num_of_bytes = write(fd, dst, len);
169     }
170 
171     unlock_user(dst, vaddr, 0);
172     return num_of_bytes;
173 }
174 
175 static int read_from_file(CPUMIPSState *env, target_ulong fd,
176                           target_ulong vaddr, target_ulong len,
177                           target_ulong offset)
178 {
179     int num_of_bytes;
180     void *dst = lock_user(VERIFY_WRITE, vaddr, len, 0);
181     if (!dst) {
182         errno = EFAULT;
183         return -1;
184     }
185 
186     if (offset) {
187 #ifdef _WIN32
188         num_of_bytes = 0;
189 #else
190         num_of_bytes = pread(fd, dst, len, offset);
191 #endif
192     } else {
193         num_of_bytes = read(fd, dst, len);
194     }
195 
196     unlock_user(dst, vaddr, len);
197     return num_of_bytes;
198 }
199 
200 static int copy_argn_to_target(CPUMIPSState *env, int arg_num,
201                                target_ulong vaddr)
202 {
203     int strsize = strlen(semihosting_get_arg(arg_num)) + 1;
204     char *dst = lock_user(VERIFY_WRITE, vaddr, strsize, 0);
205     if (!dst) {
206         return -1;
207     }
208 
209     strcpy(dst, semihosting_get_arg(arg_num));
210 
211     unlock_user(dst, vaddr, strsize);
212     return 0;
213 }
214 
215 #define GET_TARGET_STRING(p, addr)              \
216     do {                                        \
217         p = lock_user_string(addr);             \
218         if (!p) {                               \
219             gpr[2] = -1;                        \
220             gpr[3] = EFAULT;                    \
221             return;                             \
222         }                                       \
223     } while (0)
224 
225 #define GET_TARGET_STRINGS_2(p, addr, p2, addr2)        \
226     do {                                                \
227         p = lock_user_string(addr);                     \
228         if (!p) {                                       \
229             gpr[2] = -1;                                \
230             gpr[3] = EFAULT;                            \
231             return;                                     \
232         }                                               \
233         p2 = lock_user_string(addr2);                   \
234         if (!p2) {                                      \
235             unlock_user(p, addr, 0);                    \
236             gpr[2] = -1;                                \
237             gpr[3] = EFAULT;                            \
238             return;                                     \
239         }                                               \
240     } while (0)
241 
242 #define FREE_TARGET_STRING(p, gpr)              \
243     do {                                        \
244         unlock_user(p, gpr, 0);                 \
245     } while (0)
246 
247 void helper_do_semihosting(CPUMIPSState *env)
248 {
249     target_ulong *gpr = env->active_tc.gpr;
250     const UHIOp op = gpr[25];
251     char *p, *p2;
252 
253     switch (op) {
254     case UHI_exit:
255         qemu_log("UHI(%d): exit(%d)\n", op, (int)gpr[4]);
256         exit(gpr[4]);
257     case UHI_open:
258         GET_TARGET_STRING(p, gpr[4]);
259         if (!strcmp("/dev/stdin", p)) {
260             gpr[2] = 0;
261         } else if (!strcmp("/dev/stdout", p)) {
262             gpr[2] = 1;
263         } else if (!strcmp("/dev/stderr", p)) {
264             gpr[2] = 2;
265         } else {
266             gpr[2] = open(p, get_open_flags(gpr[5]), gpr[6]);
267             gpr[3] = errno_mips(errno);
268         }
269         FREE_TARGET_STRING(p, gpr[4]);
270         break;
271     case UHI_close:
272         if (gpr[4] < 3) {
273             /* ignore closing stdin/stdout/stderr */
274             gpr[2] = 0;
275             return;
276         }
277         gpr[2] = close(gpr[4]);
278         gpr[3] = errno_mips(errno);
279         break;
280     case UHI_read:
281         gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6], 0);
282         gpr[3] = errno_mips(errno);
283         break;
284     case UHI_write:
285         gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6], 0);
286         gpr[3] = errno_mips(errno);
287         break;
288     case UHI_lseek:
289         gpr[2] = lseek(gpr[4], gpr[5], gpr[6]);
290         gpr[3] = errno_mips(errno);
291         break;
292     case UHI_unlink:
293         GET_TARGET_STRING(p, gpr[4]);
294         gpr[2] = remove(p);
295         gpr[3] = errno_mips(errno);
296         FREE_TARGET_STRING(p, gpr[4]);
297         break;
298     case UHI_fstat:
299         {
300             struct stat sbuf;
301             memset(&sbuf, 0, sizeof(sbuf));
302             gpr[2] = fstat(gpr[4], &sbuf);
303             gpr[3] = errno_mips(errno);
304             if (gpr[2]) {
305                 return;
306             }
307             gpr[2] = copy_stat_to_target(env, &sbuf, gpr[5]);
308             gpr[3] = errno_mips(errno);
309         }
310         break;
311     case UHI_argc:
312         gpr[2] = semihosting_get_argc();
313         break;
314     case UHI_argnlen:
315         if (gpr[4] >= semihosting_get_argc()) {
316             gpr[2] = -1;
317             return;
318         }
319         gpr[2] = strlen(semihosting_get_arg(gpr[4]));
320         break;
321     case UHI_argn:
322         if (gpr[4] >= semihosting_get_argc()) {
323             gpr[2] = -1;
324             return;
325         }
326         gpr[2] = copy_argn_to_target(env, gpr[4], gpr[5]);
327         break;
328     case UHI_plog:
329         GET_TARGET_STRING(p, gpr[4]);
330         p2 = strstr(p, "%d");
331         if (p2) {
332             int char_num = p2 - p;
333             GString *s = g_string_new_len(p, char_num);
334             g_string_append_printf(s, "%d%s", (int)gpr[5], p2 + 2);
335             gpr[2] = qemu_semihosting_log_out(s->str, s->len);
336             g_string_free(s, true);
337         } else {
338             gpr[2] = qemu_semihosting_log_out(p, strlen(p));
339         }
340         FREE_TARGET_STRING(p, gpr[4]);
341         break;
342     case UHI_assert:
343         GET_TARGET_STRINGS_2(p, gpr[4], p2, gpr[5]);
344         printf("assertion '");
345         printf("\"%s\"", p);
346         printf("': file \"%s\", line %d\n", p2, (int)gpr[6]);
347         FREE_TARGET_STRING(p2, gpr[5]);
348         FREE_TARGET_STRING(p, gpr[4]);
349         abort();
350         break;
351     case UHI_pread:
352         gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6], gpr[7]);
353         gpr[3] = errno_mips(errno);
354         break;
355     case UHI_pwrite:
356         gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6], gpr[7]);
357         gpr[3] = errno_mips(errno);
358         break;
359 #ifndef _WIN32
360     case UHI_link:
361         GET_TARGET_STRINGS_2(p, gpr[4], p2, gpr[5]);
362         gpr[2] = link(p, p2);
363         gpr[3] = errno_mips(errno);
364         FREE_TARGET_STRING(p2, gpr[5]);
365         FREE_TARGET_STRING(p, gpr[4]);
366         break;
367 #endif
368     default:
369         fprintf(stderr, "Unknown UHI operation %d\n", op);
370         abort();
371     }
372     return;
373 }
374