xref: /qemu/semihosting/syscalls.c (revision a2212474)
1 /*
2  * Syscall implementations for semihosting.
3  *
4  * Copyright (c) 2022 Linaro
5  *
6  * SPDX-License-Identifier: GPL-2.0-or-later
7  */
8 
9 #include "qemu/osdep.h"
10 #include "exec/gdbstub.h"
11 #include "semihosting/guestfd.h"
12 #include "semihosting/syscalls.h"
13 #ifdef CONFIG_USER_ONLY
14 #include "qemu.h"
15 #else
16 #include "semihosting/softmmu-uaccess.h"
17 #endif
18 
19 
20 /*
21  * Validate or compute the length of the string (including terminator).
22  */
23 static int validate_strlen(CPUState *cs, target_ulong str, target_ulong tlen)
24 {
25     CPUArchState *env G_GNUC_UNUSED = cs->env_ptr;
26     char c;
27 
28     if (tlen == 0) {
29         ssize_t slen = target_strlen(str);
30 
31         if (slen < 0) {
32             return -EFAULT;
33         }
34         if (slen >= INT32_MAX) {
35             return -ENAMETOOLONG;
36         }
37         return slen + 1;
38     }
39     if (tlen > INT32_MAX) {
40         return -ENAMETOOLONG;
41     }
42     if (get_user_u8(c, str + tlen - 1)) {
43         return -EFAULT;
44     }
45     if (c != 0) {
46         return -EINVAL;
47     }
48     return tlen;
49 }
50 
51 static int validate_lock_user_string(char **pstr, CPUState *cs,
52                                      target_ulong tstr, target_ulong tlen)
53 {
54     int ret = validate_strlen(cs, tstr, tlen);
55     CPUArchState *env G_GNUC_UNUSED = cs->env_ptr;
56     char *str = NULL;
57 
58     if (ret > 0) {
59         str = lock_user(VERIFY_READ, tstr, ret, true);
60         ret = str ? 0 : -EFAULT;
61     }
62     *pstr = str;
63     return ret;
64 }
65 
66 /*
67  * GDB semihosting syscall implementations.
68  */
69 
70 static gdb_syscall_complete_cb gdb_open_complete;
71 
72 static void gdb_open_cb(CPUState *cs, target_ulong ret, target_ulong err)
73 {
74     if (!err) {
75         int guestfd = alloc_guestfd();
76         associate_guestfd(guestfd, ret);
77         ret = guestfd;
78     }
79     gdb_open_complete(cs, ret, err);
80 }
81 
82 static void gdb_open(CPUState *cs, gdb_syscall_complete_cb complete,
83                      target_ulong fname, target_ulong fname_len,
84                      int gdb_flags, int mode)
85 {
86     int len = validate_strlen(cs, fname, fname_len);
87     if (len < 0) {
88         complete(cs, -1, -len);
89         return;
90     }
91 
92     gdb_open_complete = complete;
93     gdb_do_syscall(gdb_open_cb, "open,%s,%x,%x",
94                    fname, len, (target_ulong)gdb_flags, (target_ulong)mode);
95 }
96 
97 static void gdb_close(CPUState *cs, gdb_syscall_complete_cb complete,
98                       GuestFD *gf)
99 {
100     gdb_do_syscall(complete, "close,%x", (target_ulong)gf->hostfd);
101 }
102 
103 static void gdb_read(CPUState *cs, gdb_syscall_complete_cb complete,
104                      GuestFD *gf, target_ulong buf, target_ulong len)
105 {
106     gdb_do_syscall(complete, "read,%x,%x,%x",
107                    (target_ulong)gf->hostfd, buf, len);
108 }
109 
110 static void gdb_write(CPUState *cs, gdb_syscall_complete_cb complete,
111                       GuestFD *gf, target_ulong buf, target_ulong len)
112 {
113     gdb_do_syscall(complete, "write,%x,%x,%x",
114                    (target_ulong)gf->hostfd, buf, len);
115 }
116 
117 static void gdb_lseek(CPUState *cs, gdb_syscall_complete_cb complete,
118                       GuestFD *gf, int64_t off, int gdb_whence)
119 {
120     gdb_do_syscall(complete, "lseek,%x,%lx,%x",
121                    (target_ulong)gf->hostfd, off, (target_ulong)gdb_whence);
122 }
123 
124 static void gdb_isatty(CPUState *cs, gdb_syscall_complete_cb complete,
125                        GuestFD *gf)
126 {
127     gdb_do_syscall(complete, "isatty,%x", (target_ulong)gf->hostfd);
128 }
129 
130 /*
131  * Host semihosting syscall implementations.
132  */
133 
134 static void host_open(CPUState *cs, gdb_syscall_complete_cb complete,
135                       target_ulong fname, target_ulong fname_len,
136                       int gdb_flags, int mode)
137 {
138     CPUArchState *env G_GNUC_UNUSED = cs->env_ptr;
139     char *p;
140     int ret, host_flags;
141 
142     ret = validate_lock_user_string(&p, cs, fname, fname_len);
143     if (ret < 0) {
144         complete(cs, -1, -ret);
145         return;
146     }
147 
148     if (gdb_flags & GDB_O_WRONLY) {
149         host_flags = O_WRONLY;
150     } else if (gdb_flags & GDB_O_RDWR) {
151         host_flags = O_RDWR;
152     } else {
153         host_flags = O_RDONLY;
154     }
155     if (gdb_flags & GDB_O_CREAT) {
156         host_flags |= O_CREAT;
157     }
158     if (gdb_flags & GDB_O_TRUNC) {
159         host_flags |= O_TRUNC;
160     }
161     if (gdb_flags & GDB_O_EXCL) {
162         host_flags |= O_EXCL;
163     }
164 
165     ret = open(p, host_flags, mode);
166     if (ret < 0) {
167         complete(cs, -1, errno);
168     } else {
169         int guestfd = alloc_guestfd();
170         associate_guestfd(guestfd, ret);
171         complete(cs, guestfd, 0);
172     }
173     unlock_user(p, fname, 0);
174 }
175 
176 static void host_close(CPUState *cs, gdb_syscall_complete_cb complete,
177                        GuestFD *gf)
178 {
179     /*
180      * Only close the underlying host fd if it's one we opened on behalf
181      * of the guest in SYS_OPEN.
182      */
183     if (gf->hostfd != STDIN_FILENO &&
184         gf->hostfd != STDOUT_FILENO &&
185         gf->hostfd != STDERR_FILENO &&
186         close(gf->hostfd) < 0) {
187         complete(cs, -1, errno);
188     } else {
189         complete(cs, 0, 0);
190     }
191 }
192 
193 static void host_read(CPUState *cs, gdb_syscall_complete_cb complete,
194                       GuestFD *gf, target_ulong buf, target_ulong len)
195 {
196     CPUArchState *env G_GNUC_UNUSED = cs->env_ptr;
197     void *ptr = lock_user(VERIFY_WRITE, buf, len, 0);
198     ssize_t ret;
199 
200     if (!ptr) {
201         complete(cs, -1, EFAULT);
202         return;
203     }
204     do {
205         ret = read(gf->hostfd, ptr, len);
206     } while (ret == -1 && errno == EINTR);
207     if (ret == -1) {
208         complete(cs, -1, errno);
209         unlock_user(ptr, buf, 0);
210     } else {
211         complete(cs, ret, 0);
212         unlock_user(ptr, buf, ret);
213     }
214 }
215 
216 static void host_write(CPUState *cs, gdb_syscall_complete_cb complete,
217                        GuestFD *gf, target_ulong buf, target_ulong len)
218 {
219     CPUArchState *env G_GNUC_UNUSED = cs->env_ptr;
220     void *ptr = lock_user(VERIFY_READ, buf, len, 1);
221     ssize_t ret;
222 
223     if (!ptr) {
224         complete(cs, -1, EFAULT);
225         return;
226     }
227     ret = write(gf->hostfd, ptr, len);
228     complete(cs, ret, ret == -1 ? errno : 0);
229     unlock_user(ptr, buf, 0);
230 }
231 
232 static void host_lseek(CPUState *cs, gdb_syscall_complete_cb complete,
233                        GuestFD *gf, int64_t off, int whence)
234 {
235     /* So far, all hosts use the same values. */
236     QEMU_BUILD_BUG_ON(GDB_SEEK_SET != SEEK_SET);
237     QEMU_BUILD_BUG_ON(GDB_SEEK_CUR != SEEK_CUR);
238     QEMU_BUILD_BUG_ON(GDB_SEEK_END != SEEK_END);
239 
240     off_t ret = off;
241     int err = 0;
242 
243     if (ret == off) {
244         ret = lseek(gf->hostfd, ret, whence);
245         if (ret == -1) {
246             err = errno;
247         }
248     } else {
249         ret = -1;
250         err = EINVAL;
251     }
252     complete(cs, ret, err);
253 }
254 
255 static void host_isatty(CPUState *cs, gdb_syscall_complete_cb complete,
256                         GuestFD *gf)
257 {
258     int ret = isatty(gf->hostfd);
259     complete(cs, ret, ret ? 0 : errno);
260 }
261 
262 /*
263  * Static file semihosting syscall implementations.
264  */
265 
266 static void staticfile_read(CPUState *cs, gdb_syscall_complete_cb complete,
267                             GuestFD *gf, target_ulong buf, target_ulong len)
268 {
269     CPUArchState *env G_GNUC_UNUSED = cs->env_ptr;
270     target_ulong rest = gf->staticfile.len - gf->staticfile.off;
271     void *ptr;
272 
273     if (len > rest) {
274         len = rest;
275     }
276     ptr = lock_user(VERIFY_WRITE, buf, len, 0);
277     if (!ptr) {
278         complete(cs, -1, EFAULT);
279         return;
280     }
281     memcpy(ptr, gf->staticfile.data + gf->staticfile.off, len);
282     gf->staticfile.off += len;
283     complete(cs, len, 0);
284     unlock_user(ptr, buf, len);
285 }
286 
287 static void staticfile_lseek(CPUState *cs, gdb_syscall_complete_cb complete,
288                              GuestFD *gf, int64_t off, int gdb_whence)
289 {
290     int64_t ret;
291 
292     switch (gdb_whence) {
293     case GDB_SEEK_SET:
294         ret = off;
295         break;
296     case GDB_SEEK_CUR:
297         ret = gf->staticfile.off + off;
298         break;
299     case GDB_SEEK_END:
300         ret = gf->staticfile.len + off;
301         break;
302     default:
303         ret = -1;
304         break;
305     }
306     if (ret >= 0 && ret <= gf->staticfile.len) {
307         gf->staticfile.off = ret;
308         complete(cs, ret, 0);
309     } else {
310         complete(cs, -1, EINVAL);
311     }
312 }
313 
314 /*
315  * Syscall entry points.
316  */
317 
318 void semihost_sys_open(CPUState *cs, gdb_syscall_complete_cb complete,
319                        target_ulong fname, target_ulong fname_len,
320                        int gdb_flags, int mode)
321 {
322     if (use_gdb_syscalls()) {
323         gdb_open(cs, complete, fname, fname_len, gdb_flags, mode);
324     } else {
325         host_open(cs, complete, fname, fname_len, gdb_flags, mode);
326     }
327 }
328 
329 void semihost_sys_close(CPUState *cs, gdb_syscall_complete_cb complete, int fd)
330 {
331     GuestFD *gf = get_guestfd(fd);
332 
333     if (!gf) {
334         complete(cs, -1, EBADF);
335         return;
336     }
337     switch (gf->type) {
338     case GuestFDGDB:
339         gdb_close(cs, complete, gf);
340         break;
341     case GuestFDHost:
342         host_close(cs, complete, gf);
343         break;
344     case GuestFDStatic:
345         complete(cs, 0, 0);
346         break;
347     default:
348         g_assert_not_reached();
349     }
350     dealloc_guestfd(fd);
351 }
352 
353 void semihost_sys_read_gf(CPUState *cs, gdb_syscall_complete_cb complete,
354                           GuestFD *gf, target_ulong buf, target_ulong len)
355 {
356     /*
357      * Bound length for 64-bit guests on 32-bit hosts, not overlowing ssize_t.
358      * Note the Linux kernel does this with MAX_RW_COUNT, so it's not a bad
359      * idea to do this unconditionally.
360      */
361     if (len > INT32_MAX) {
362         len = INT32_MAX;
363     }
364     switch (gf->type) {
365     case GuestFDGDB:
366         gdb_read(cs, complete, gf, buf, len);
367         break;
368     case GuestFDHost:
369         host_read(cs, complete, gf, buf, len);
370         break;
371     case GuestFDStatic:
372         staticfile_read(cs, complete, gf, buf, len);
373         break;
374     default:
375         g_assert_not_reached();
376     }
377 }
378 
379 void semihost_sys_read(CPUState *cs, gdb_syscall_complete_cb complete,
380                        int fd, target_ulong buf, target_ulong len)
381 {
382     GuestFD *gf = get_guestfd(fd);
383 
384     if (gf) {
385         semihost_sys_read_gf(cs, complete, gf, buf, len);
386     } else {
387         complete(cs, -1, EBADF);
388     }
389 }
390 
391 void semihost_sys_write_gf(CPUState *cs, gdb_syscall_complete_cb complete,
392                            GuestFD *gf, target_ulong buf, target_ulong len)
393 {
394     /*
395      * Bound length for 64-bit guests on 32-bit hosts, not overlowing ssize_t.
396      * Note the Linux kernel does this with MAX_RW_COUNT, so it's not a bad
397      * idea to do this unconditionally.
398      */
399     if (len > INT32_MAX) {
400         len = INT32_MAX;
401     }
402     switch (gf->type) {
403     case GuestFDGDB:
404         gdb_write(cs, complete, gf, buf, len);
405         break;
406     case GuestFDHost:
407         host_write(cs, complete, gf, buf, len);
408         break;
409     case GuestFDStatic:
410         /* Static files are never open for writing: EBADF. */
411         complete(cs, -1, EBADF);
412         break;
413     default:
414         g_assert_not_reached();
415     }
416 }
417 
418 void semihost_sys_write(CPUState *cs, gdb_syscall_complete_cb complete,
419                         int fd, target_ulong buf, target_ulong len)
420 {
421     GuestFD *gf = get_guestfd(fd);
422 
423     if (gf) {
424         semihost_sys_write_gf(cs, complete, gf, buf, len);
425     } else {
426         complete(cs, -1, EBADF);
427     }
428 }
429 
430 void semihost_sys_lseek(CPUState *cs, gdb_syscall_complete_cb complete,
431                         int fd, int64_t off, int gdb_whence)
432 {
433     GuestFD *gf = get_guestfd(fd);
434 
435     if (!gf) {
436         complete(cs, -1, EBADF);
437         return;
438     }
439     switch (gf->type) {
440     case GuestFDGDB:
441         gdb_lseek(cs, complete, gf, off, gdb_whence);
442         return;
443     case GuestFDHost:
444         host_lseek(cs, complete, gf, off, gdb_whence);
445         break;
446     case GuestFDStatic:
447         staticfile_lseek(cs, complete, gf, off, gdb_whence);
448         break;
449     default:
450         g_assert_not_reached();
451     }
452 }
453 
454 void semihost_sys_isatty(CPUState *cs, gdb_syscall_complete_cb complete, int fd)
455 {
456     GuestFD *gf = get_guestfd(fd);
457 
458     if (!gf) {
459         complete(cs, 0, EBADF);
460         return;
461     }
462     switch (gf->type) {
463     case GuestFDGDB:
464         gdb_isatty(cs, complete, gf);
465         break;
466     case GuestFDHost:
467         host_isatty(cs, complete, gf);
468         break;
469     case GuestFDStatic:
470         complete(cs, 0, ENOTTY);
471         break;
472     default:
473         g_assert_not_reached();
474     }
475 }
476