123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983 |
- /*
- * Syscall implementations for semihosting.
- *
- * Copyright (c) 2022 Linaro
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
- */
- #include "qemu/osdep.h"
- #include "cpu.h"
- #include "gdbstub/syscalls.h"
- #include "semihosting/guestfd.h"
- #include "semihosting/syscalls.h"
- #include "semihosting/console.h"
- #ifdef CONFIG_USER_ONLY
- #include "qemu.h"
- #else
- #include "semihosting/uaccess.h"
- #endif
- /*
- * Validate or compute the length of the string (including terminator).
- */
- static int validate_strlen(CPUState *cs, target_ulong str, target_ulong tlen)
- {
- CPUArchState *env G_GNUC_UNUSED = cpu_env(cs);
- char c;
- if (tlen == 0) {
- ssize_t slen = target_strlen(str);
- if (slen < 0) {
- return -EFAULT;
- }
- if (slen >= INT32_MAX) {
- return -ENAMETOOLONG;
- }
- return slen + 1;
- }
- if (tlen > INT32_MAX) {
- return -ENAMETOOLONG;
- }
- if (get_user_u8(c, str + tlen - 1)) {
- return -EFAULT;
- }
- if (c != 0) {
- return -EINVAL;
- }
- return tlen;
- }
- static int validate_lock_user_string(char **pstr, CPUState *cs,
- target_ulong tstr, target_ulong tlen)
- {
- int ret = validate_strlen(cs, tstr, tlen);
- CPUArchState *env G_GNUC_UNUSED = cpu_env(cs);
- char *str = NULL;
- if (ret > 0) {
- str = lock_user(VERIFY_READ, tstr, ret, true);
- ret = str ? 0 : -EFAULT;
- }
- *pstr = str;
- return ret;
- }
- /*
- * TODO: Note that gdb always stores the stat structure big-endian.
- * So far, that's ok, as the only two targets using this are also
- * big-endian. Until we do something with gdb, also produce the
- * same big-endian result from the host.
- */
- static int copy_stat_to_user(CPUState *cs, target_ulong addr,
- const struct stat *s)
- {
- CPUArchState *env G_GNUC_UNUSED = cpu_env(cs);
- struct gdb_stat *p;
- if (s->st_dev != (uint32_t)s->st_dev ||
- s->st_ino != (uint32_t)s->st_ino) {
- return -EOVERFLOW;
- }
- p = lock_user(VERIFY_WRITE, addr, sizeof(struct gdb_stat), 0);
- if (!p) {
- return -EFAULT;
- }
- p->gdb_st_dev = cpu_to_be32(s->st_dev);
- p->gdb_st_ino = cpu_to_be32(s->st_ino);
- p->gdb_st_mode = cpu_to_be32(s->st_mode);
- p->gdb_st_nlink = cpu_to_be32(s->st_nlink);
- p->gdb_st_uid = cpu_to_be32(s->st_uid);
- p->gdb_st_gid = cpu_to_be32(s->st_gid);
- p->gdb_st_rdev = cpu_to_be32(s->st_rdev);
- p->gdb_st_size = cpu_to_be64(s->st_size);
- #ifdef _WIN32
- /* Windows stat is missing some fields. */
- p->gdb_st_blksize = 0;
- p->gdb_st_blocks = 0;
- #else
- p->gdb_st_blksize = cpu_to_be64(s->st_blksize);
- p->gdb_st_blocks = cpu_to_be64(s->st_blocks);
- #endif
- p->gdb_st_atime = cpu_to_be32(s->st_atime);
- p->gdb_st_mtime = cpu_to_be32(s->st_mtime);
- p->gdb_st_ctime = cpu_to_be32(s->st_ctime);
- unlock_user(p, addr, sizeof(struct gdb_stat));
- return 0;
- }
- /*
- * GDB semihosting syscall implementations.
- */
- static gdb_syscall_complete_cb gdb_open_complete;
- static void gdb_open_cb(CPUState *cs, uint64_t ret, int err)
- {
- if (!err) {
- int guestfd = alloc_guestfd();
- associate_guestfd(guestfd, ret);
- ret = guestfd;
- }
- gdb_open_complete(cs, ret, err);
- }
- static void gdb_open(CPUState *cs, gdb_syscall_complete_cb complete,
- target_ulong fname, target_ulong fname_len,
- int gdb_flags, int mode)
- {
- int len = validate_strlen(cs, fname, fname_len);
- if (len < 0) {
- complete(cs, -1, -len);
- return;
- }
- gdb_open_complete = complete;
- gdb_do_syscall(gdb_open_cb, "open,%s,%x,%x",
- (uint64_t)fname, (uint32_t)len,
- (uint32_t)gdb_flags, (uint32_t)mode);
- }
- static void gdb_close(CPUState *cs, gdb_syscall_complete_cb complete,
- GuestFD *gf)
- {
- gdb_do_syscall(complete, "close,%x", (uint32_t)gf->hostfd);
- }
- static void gdb_read(CPUState *cs, gdb_syscall_complete_cb complete,
- GuestFD *gf, target_ulong buf, target_ulong len)
- {
- gdb_do_syscall(complete, "read,%x,%lx,%lx",
- (uint32_t)gf->hostfd, (uint64_t)buf, (uint64_t)len);
- }
- static void gdb_write(CPUState *cs, gdb_syscall_complete_cb complete,
- GuestFD *gf, target_ulong buf, target_ulong len)
- {
- gdb_do_syscall(complete, "write,%x,%lx,%lx",
- (uint32_t)gf->hostfd, (uint64_t)buf, (uint64_t)len);
- }
- static void gdb_lseek(CPUState *cs, gdb_syscall_complete_cb complete,
- GuestFD *gf, int64_t off, int gdb_whence)
- {
- gdb_do_syscall(complete, "lseek,%x,%lx,%x",
- (uint32_t)gf->hostfd, off, (uint32_t)gdb_whence);
- }
- static void gdb_isatty(CPUState *cs, gdb_syscall_complete_cb complete,
- GuestFD *gf)
- {
- gdb_do_syscall(complete, "isatty,%x", (uint32_t)gf->hostfd);
- }
- static void gdb_fstat(CPUState *cs, gdb_syscall_complete_cb complete,
- GuestFD *gf, target_ulong addr)
- {
- gdb_do_syscall(complete, "fstat,%x,%lx",
- (uint32_t)gf->hostfd, (uint64_t)addr);
- }
- static void gdb_stat(CPUState *cs, gdb_syscall_complete_cb complete,
- target_ulong fname, target_ulong fname_len,
- target_ulong addr)
- {
- int len = validate_strlen(cs, fname, fname_len);
- if (len < 0) {
- complete(cs, -1, -len);
- return;
- }
- gdb_do_syscall(complete, "stat,%s,%lx",
- (uint64_t)fname, (uint32_t)len, (uint64_t)addr);
- }
- static void gdb_remove(CPUState *cs, gdb_syscall_complete_cb complete,
- target_ulong fname, target_ulong fname_len)
- {
- int len = validate_strlen(cs, fname, fname_len);
- if (len < 0) {
- complete(cs, -1, -len);
- return;
- }
- gdb_do_syscall(complete, "unlink,%s", (uint64_t)fname, (uint32_t)len);
- }
- static void gdb_rename(CPUState *cs, gdb_syscall_complete_cb complete,
- target_ulong oname, target_ulong oname_len,
- target_ulong nname, target_ulong nname_len)
- {
- int olen, nlen;
- olen = validate_strlen(cs, oname, oname_len);
- if (olen < 0) {
- complete(cs, -1, -olen);
- return;
- }
- nlen = validate_strlen(cs, nname, nname_len);
- if (nlen < 0) {
- complete(cs, -1, -nlen);
- return;
- }
- gdb_do_syscall(complete, "rename,%s,%s",
- (uint64_t)oname, (uint32_t)olen,
- (uint64_t)nname, (uint32_t)nlen);
- }
- static void gdb_system(CPUState *cs, gdb_syscall_complete_cb complete,
- target_ulong cmd, target_ulong cmd_len)
- {
- int len = validate_strlen(cs, cmd, cmd_len);
- if (len < 0) {
- complete(cs, -1, -len);
- return;
- }
- gdb_do_syscall(complete, "system,%s", (uint64_t)cmd, (uint32_t)len);
- }
- static void gdb_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete,
- target_ulong tv_addr, target_ulong tz_addr)
- {
- gdb_do_syscall(complete, "gettimeofday,%lx,%lx",
- (uint64_t)tv_addr, (uint64_t)tz_addr);
- }
- /*
- * Host semihosting syscall implementations.
- */
- static void host_open(CPUState *cs, gdb_syscall_complete_cb complete,
- target_ulong fname, target_ulong fname_len,
- int gdb_flags, int mode)
- {
- CPUArchState *env G_GNUC_UNUSED = cpu_env(cs);
- char *p;
- int ret, host_flags = O_BINARY;
- ret = validate_lock_user_string(&p, cs, fname, fname_len);
- if (ret < 0) {
- complete(cs, -1, -ret);
- return;
- }
- if (gdb_flags & GDB_O_WRONLY) {
- host_flags |= O_WRONLY;
- } else if (gdb_flags & GDB_O_RDWR) {
- host_flags |= O_RDWR;
- } else {
- host_flags |= O_RDONLY;
- }
- if (gdb_flags & GDB_O_CREAT) {
- host_flags |= O_CREAT;
- }
- if (gdb_flags & GDB_O_TRUNC) {
- host_flags |= O_TRUNC;
- }
- if (gdb_flags & GDB_O_EXCL) {
- host_flags |= O_EXCL;
- }
- ret = open(p, host_flags, mode);
- if (ret < 0) {
- complete(cs, -1, errno);
- } else {
- int guestfd = alloc_guestfd();
- associate_guestfd(guestfd, ret);
- complete(cs, guestfd, 0);
- }
- unlock_user(p, fname, 0);
- }
- static void host_close(CPUState *cs, gdb_syscall_complete_cb complete,
- GuestFD *gf)
- {
- /*
- * Only close the underlying host fd if it's one we opened on behalf
- * of the guest in SYS_OPEN.
- */
- if (gf->hostfd != STDIN_FILENO &&
- gf->hostfd != STDOUT_FILENO &&
- gf->hostfd != STDERR_FILENO &&
- close(gf->hostfd) < 0) {
- complete(cs, -1, errno);
- } else {
- complete(cs, 0, 0);
- }
- }
- static void host_read(CPUState *cs, gdb_syscall_complete_cb complete,
- GuestFD *gf, target_ulong buf, target_ulong len)
- {
- CPUArchState *env G_GNUC_UNUSED = cpu_env(cs);
- void *ptr = lock_user(VERIFY_WRITE, buf, len, 0);
- ssize_t ret;
- if (!ptr) {
- complete(cs, -1, EFAULT);
- return;
- }
- ret = RETRY_ON_EINTR(read(gf->hostfd, ptr, len));
- if (ret == -1) {
- unlock_user(ptr, buf, 0);
- complete(cs, -1, errno);
- } else {
- unlock_user(ptr, buf, ret);
- complete(cs, ret, 0);
- }
- }
- static void host_write(CPUState *cs, gdb_syscall_complete_cb complete,
- GuestFD *gf, target_ulong buf, target_ulong len)
- {
- CPUArchState *env G_GNUC_UNUSED = cpu_env(cs);
- void *ptr = lock_user(VERIFY_READ, buf, len, 1);
- ssize_t ret;
- if (!ptr) {
- complete(cs, -1, EFAULT);
- return;
- }
- ret = write(gf->hostfd, ptr, len);
- unlock_user(ptr, buf, 0);
- complete(cs, ret, ret == -1 ? errno : 0);
- }
- static void host_lseek(CPUState *cs, gdb_syscall_complete_cb complete,
- GuestFD *gf, int64_t off, int whence)
- {
- /* So far, all hosts use the same values. */
- QEMU_BUILD_BUG_ON(GDB_SEEK_SET != SEEK_SET);
- QEMU_BUILD_BUG_ON(GDB_SEEK_CUR != SEEK_CUR);
- QEMU_BUILD_BUG_ON(GDB_SEEK_END != SEEK_END);
- off_t ret = off;
- int err = 0;
- if (ret == off) {
- ret = lseek(gf->hostfd, ret, whence);
- if (ret == -1) {
- err = errno;
- }
- } else {
- ret = -1;
- err = EINVAL;
- }
- complete(cs, ret, err);
- }
- static void host_isatty(CPUState *cs, gdb_syscall_complete_cb complete,
- GuestFD *gf)
- {
- int ret = isatty(gf->hostfd);
- complete(cs, ret, ret ? 0 : errno);
- }
- static void host_flen(CPUState *cs, gdb_syscall_complete_cb complete,
- GuestFD *gf)
- {
- struct stat buf;
- if (fstat(gf->hostfd, &buf) < 0) {
- complete(cs, -1, errno);
- } else {
- complete(cs, buf.st_size, 0);
- }
- }
- static void host_fstat(CPUState *cs, gdb_syscall_complete_cb complete,
- GuestFD *gf, target_ulong addr)
- {
- struct stat buf;
- int ret;
- ret = fstat(gf->hostfd, &buf);
- if (ret) {
- complete(cs, -1, errno);
- return;
- }
- ret = copy_stat_to_user(cs, addr, &buf);
- complete(cs, ret ? -1 : 0, ret ? -ret : 0);
- }
- static void host_stat(CPUState *cs, gdb_syscall_complete_cb complete,
- target_ulong fname, target_ulong fname_len,
- target_ulong addr)
- {
- CPUArchState *env G_GNUC_UNUSED = cpu_env(cs);
- struct stat buf;
- char *name;
- int ret, err;
- ret = validate_lock_user_string(&name, cs, fname, fname_len);
- if (ret < 0) {
- complete(cs, -1, -ret);
- return;
- }
- ret = stat(name, &buf);
- if (ret) {
- err = errno;
- } else {
- ret = copy_stat_to_user(cs, addr, &buf);
- err = 0;
- if (ret < 0) {
- err = -ret;
- ret = -1;
- }
- }
- unlock_user(name, fname, 0);
- complete(cs, ret, err);
- }
- static void host_remove(CPUState *cs, gdb_syscall_complete_cb complete,
- target_ulong fname, target_ulong fname_len)
- {
- CPUArchState *env G_GNUC_UNUSED = cpu_env(cs);
- char *p;
- int ret;
- ret = validate_lock_user_string(&p, cs, fname, fname_len);
- if (ret < 0) {
- complete(cs, -1, -ret);
- return;
- }
- ret = remove(p);
- unlock_user(p, fname, 0);
- complete(cs, ret, ret ? errno : 0);
- }
- static void host_rename(CPUState *cs, gdb_syscall_complete_cb complete,
- target_ulong oname, target_ulong oname_len,
- target_ulong nname, target_ulong nname_len)
- {
- CPUArchState *env G_GNUC_UNUSED = cpu_env(cs);
- char *ostr, *nstr;
- int ret;
- ret = validate_lock_user_string(&ostr, cs, oname, oname_len);
- if (ret < 0) {
- complete(cs, -1, -ret);
- return;
- }
- ret = validate_lock_user_string(&nstr, cs, nname, nname_len);
- if (ret < 0) {
- unlock_user(ostr, oname, 0);
- complete(cs, -1, -ret);
- return;
- }
- ret = rename(ostr, nstr);
- unlock_user(ostr, oname, 0);
- unlock_user(nstr, nname, 0);
- complete(cs, ret, ret ? errno : 0);
- }
- static void host_system(CPUState *cs, gdb_syscall_complete_cb complete,
- target_ulong cmd, target_ulong cmd_len)
- {
- CPUArchState *env G_GNUC_UNUSED = cpu_env(cs);
- char *p;
- int ret;
- ret = validate_lock_user_string(&p, cs, cmd, cmd_len);
- if (ret < 0) {
- complete(cs, -1, -ret);
- return;
- }
- ret = system(p);
- unlock_user(p, cmd, 0);
- complete(cs, ret, ret == -1 ? errno : 0);
- }
- static void host_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete,
- target_ulong tv_addr, target_ulong tz_addr)
- {
- CPUArchState *env G_GNUC_UNUSED = cpu_env(cs);
- struct gdb_timeval *p;
- int64_t rt;
- /* GDB fails on non-null TZ, so be consistent. */
- if (tz_addr != 0) {
- complete(cs, -1, EINVAL);
- return;
- }
- p = lock_user(VERIFY_WRITE, tv_addr, sizeof(struct gdb_timeval), 0);
- if (!p) {
- complete(cs, -1, EFAULT);
- return;
- }
- /* TODO: Like stat, gdb always produces big-endian results; match it. */
- rt = g_get_real_time();
- p->tv_sec = cpu_to_be32(rt / G_USEC_PER_SEC);
- p->tv_usec = cpu_to_be64(rt % G_USEC_PER_SEC);
- unlock_user(p, tv_addr, sizeof(struct gdb_timeval));
- }
- #ifndef CONFIG_USER_ONLY
- static void host_poll_one(CPUState *cs, gdb_syscall_complete_cb complete,
- GuestFD *gf, GIOCondition cond, int timeout)
- {
- /*
- * Since this is only used by xtensa in system mode, and stdio is
- * handled through GuestFDConsole, and there are no semihosting
- * system calls for sockets and the like, that means this descriptor
- * must be a normal file. Normal files never block and are thus
- * always ready.
- */
- complete(cs, cond & (G_IO_IN | G_IO_OUT), 0);
- }
- #endif
- /*
- * Static file semihosting syscall implementations.
- */
- static void staticfile_read(CPUState *cs, gdb_syscall_complete_cb complete,
- GuestFD *gf, target_ulong buf, target_ulong len)
- {
- CPUArchState *env G_GNUC_UNUSED = cpu_env(cs);
- target_ulong rest = gf->staticfile.len - gf->staticfile.off;
- void *ptr;
- if (len > rest) {
- len = rest;
- }
- ptr = lock_user(VERIFY_WRITE, buf, len, 0);
- if (!ptr) {
- complete(cs, -1, EFAULT);
- return;
- }
- memcpy(ptr, gf->staticfile.data + gf->staticfile.off, len);
- gf->staticfile.off += len;
- unlock_user(ptr, buf, len);
- complete(cs, len, 0);
- }
- static void staticfile_lseek(CPUState *cs, gdb_syscall_complete_cb complete,
- GuestFD *gf, int64_t off, int gdb_whence)
- {
- int64_t ret;
- switch (gdb_whence) {
- case GDB_SEEK_SET:
- ret = off;
- break;
- case GDB_SEEK_CUR:
- ret = gf->staticfile.off + off;
- break;
- case GDB_SEEK_END:
- ret = gf->staticfile.len + off;
- break;
- default:
- ret = -1;
- break;
- }
- if (ret >= 0 && ret <= gf->staticfile.len) {
- gf->staticfile.off = ret;
- complete(cs, ret, 0);
- } else {
- complete(cs, -1, EINVAL);
- }
- }
- static void staticfile_flen(CPUState *cs, gdb_syscall_complete_cb complete,
- GuestFD *gf)
- {
- complete(cs, gf->staticfile.len, 0);
- }
- /*
- * Console semihosting syscall implementations.
- */
- static void console_read(CPUState *cs, gdb_syscall_complete_cb complete,
- GuestFD *gf, target_ulong buf, target_ulong len)
- {
- CPUArchState *env G_GNUC_UNUSED = cpu_env(cs);
- char *ptr;
- int ret;
- ptr = lock_user(VERIFY_WRITE, buf, len, 0);
- if (!ptr) {
- complete(cs, -1, EFAULT);
- return;
- }
- ret = qemu_semihosting_console_read(cs, ptr, len);
- unlock_user(ptr, buf, ret);
- complete(cs, ret, 0);
- }
- static void console_write(CPUState *cs, gdb_syscall_complete_cb complete,
- GuestFD *gf, target_ulong buf, target_ulong len)
- {
- CPUArchState *env G_GNUC_UNUSED = cpu_env(cs);
- char *ptr = lock_user(VERIFY_READ, buf, len, 1);
- int ret;
- if (!ptr) {
- complete(cs, -1, EFAULT);
- return;
- }
- ret = qemu_semihosting_console_write(ptr, len);
- unlock_user(ptr, buf, 0);
- complete(cs, ret ? ret : -1, ret ? 0 : EIO);
- }
- static void console_fstat(CPUState *cs, gdb_syscall_complete_cb complete,
- GuestFD *gf, target_ulong addr)
- {
- static const struct stat tty_buf = {
- .st_mode = 020666, /* S_IFCHR, ugo+rw */
- .st_rdev = 5, /* makedev(5, 0) -- linux /dev/tty */
- };
- int ret;
- ret = copy_stat_to_user(cs, addr, &tty_buf);
- complete(cs, ret ? -1 : 0, ret ? -ret : 0);
- }
- #ifndef CONFIG_USER_ONLY
- static void console_poll_one(CPUState *cs, gdb_syscall_complete_cb complete,
- GuestFD *gf, GIOCondition cond, int timeout)
- {
- /* The semihosting console does not support urgent data or errors. */
- cond &= G_IO_IN | G_IO_OUT;
- /*
- * Since qemu_semihosting_console_write never blocks, we can
- * consider output always ready -- leave G_IO_OUT alone.
- * All that remains is to conditionally signal input ready.
- * Since output ready causes an immediate return, only block
- * for G_IO_IN alone.
- *
- * TODO: Implement proper timeout. For now, only support
- * indefinite wait or immediate poll.
- */
- if (cond == G_IO_IN && timeout < 0) {
- qemu_semihosting_console_block_until_ready(cs);
- /* We returned -- input must be ready. */
- } else if ((cond & G_IO_IN) && !qemu_semihosting_console_ready()) {
- cond &= ~G_IO_IN;
- }
- complete(cs, cond, 0);
- }
- #endif
- /*
- * Syscall entry points.
- */
- void semihost_sys_open(CPUState *cs, gdb_syscall_complete_cb complete,
- target_ulong fname, target_ulong fname_len,
- int gdb_flags, int mode)
- {
- if (use_gdb_syscalls()) {
- gdb_open(cs, complete, fname, fname_len, gdb_flags, mode);
- } else {
- host_open(cs, complete, fname, fname_len, gdb_flags, mode);
- }
- }
- void semihost_sys_close(CPUState *cs, gdb_syscall_complete_cb complete, int fd)
- {
- GuestFD *gf = get_guestfd(fd);
- if (!gf) {
- complete(cs, -1, EBADF);
- return;
- }
- switch (gf->type) {
- case GuestFDGDB:
- gdb_close(cs, complete, gf);
- break;
- case GuestFDHost:
- host_close(cs, complete, gf);
- break;
- case GuestFDStatic:
- case GuestFDConsole:
- complete(cs, 0, 0);
- break;
- default:
- g_assert_not_reached();
- }
- dealloc_guestfd(fd);
- }
- void semihost_sys_read_gf(CPUState *cs, gdb_syscall_complete_cb complete,
- GuestFD *gf, target_ulong buf, target_ulong len)
- {
- /*
- * Bound length for 64-bit guests on 32-bit hosts, not overflowing ssize_t.
- * Note the Linux kernel does this with MAX_RW_COUNT, so it's not a bad
- * idea to do this unconditionally.
- */
- if (len > INT32_MAX) {
- len = INT32_MAX;
- }
- switch (gf->type) {
- case GuestFDGDB:
- gdb_read(cs, complete, gf, buf, len);
- break;
- case GuestFDHost:
- host_read(cs, complete, gf, buf, len);
- break;
- case GuestFDStatic:
- staticfile_read(cs, complete, gf, buf, len);
- break;
- case GuestFDConsole:
- console_read(cs, complete, gf, buf, len);
- break;
- default:
- g_assert_not_reached();
- }
- }
- void semihost_sys_read(CPUState *cs, gdb_syscall_complete_cb complete,
- int fd, target_ulong buf, target_ulong len)
- {
- GuestFD *gf = get_guestfd(fd);
- if (gf) {
- semihost_sys_read_gf(cs, complete, gf, buf, len);
- } else {
- complete(cs, -1, EBADF);
- }
- }
- void semihost_sys_write_gf(CPUState *cs, gdb_syscall_complete_cb complete,
- GuestFD *gf, target_ulong buf, target_ulong len)
- {
- /*
- * Bound length for 64-bit guests on 32-bit hosts, not overflowing ssize_t.
- * Note the Linux kernel does this with MAX_RW_COUNT, so it's not a bad
- * idea to do this unconditionally.
- */
- if (len > INT32_MAX) {
- len = INT32_MAX;
- }
- switch (gf->type) {
- case GuestFDGDB:
- gdb_write(cs, complete, gf, buf, len);
- break;
- case GuestFDHost:
- host_write(cs, complete, gf, buf, len);
- break;
- case GuestFDConsole:
- console_write(cs, complete, gf, buf, len);
- break;
- case GuestFDStatic:
- /* Static files are never open for writing: EBADF. */
- complete(cs, -1, EBADF);
- break;
- default:
- g_assert_not_reached();
- }
- }
- void semihost_sys_write(CPUState *cs, gdb_syscall_complete_cb complete,
- int fd, target_ulong buf, target_ulong len)
- {
- GuestFD *gf = get_guestfd(fd);
- if (gf) {
- semihost_sys_write_gf(cs, complete, gf, buf, len);
- } else {
- complete(cs, -1, EBADF);
- }
- }
- void semihost_sys_lseek(CPUState *cs, gdb_syscall_complete_cb complete,
- int fd, int64_t off, int gdb_whence)
- {
- GuestFD *gf = get_guestfd(fd);
- if (!gf) {
- complete(cs, -1, EBADF);
- return;
- }
- switch (gf->type) {
- case GuestFDGDB:
- gdb_lseek(cs, complete, gf, off, gdb_whence);
- return;
- case GuestFDHost:
- host_lseek(cs, complete, gf, off, gdb_whence);
- break;
- case GuestFDStatic:
- staticfile_lseek(cs, complete, gf, off, gdb_whence);
- break;
- case GuestFDConsole:
- complete(cs, -1, ESPIPE);
- break;
- default:
- g_assert_not_reached();
- }
- }
- void semihost_sys_isatty(CPUState *cs, gdb_syscall_complete_cb complete, int fd)
- {
- GuestFD *gf = get_guestfd(fd);
- if (!gf) {
- complete(cs, 0, EBADF);
- return;
- }
- switch (gf->type) {
- case GuestFDGDB:
- gdb_isatty(cs, complete, gf);
- break;
- case GuestFDHost:
- host_isatty(cs, complete, gf);
- break;
- case GuestFDStatic:
- complete(cs, 0, ENOTTY);
- break;
- case GuestFDConsole:
- complete(cs, 1, 0);
- break;
- default:
- g_assert_not_reached();
- }
- }
- void semihost_sys_flen(CPUState *cs, gdb_syscall_complete_cb fstat_cb,
- gdb_syscall_complete_cb flen_cb, int fd,
- target_ulong fstat_addr)
- {
- GuestFD *gf = get_guestfd(fd);
- if (!gf) {
- flen_cb(cs, -1, EBADF);
- return;
- }
- switch (gf->type) {
- case GuestFDGDB:
- gdb_fstat(cs, fstat_cb, gf, fstat_addr);
- break;
- case GuestFDHost:
- host_flen(cs, flen_cb, gf);
- break;
- case GuestFDStatic:
- staticfile_flen(cs, flen_cb, gf);
- break;
- case GuestFDConsole:
- default:
- g_assert_not_reached();
- }
- }
- void semihost_sys_fstat(CPUState *cs, gdb_syscall_complete_cb complete,
- int fd, target_ulong addr)
- {
- GuestFD *gf = get_guestfd(fd);
- if (!gf) {
- complete(cs, -1, EBADF);
- return;
- }
- switch (gf->type) {
- case GuestFDGDB:
- gdb_fstat(cs, complete, gf, addr);
- break;
- case GuestFDHost:
- host_fstat(cs, complete, gf, addr);
- break;
- case GuestFDConsole:
- console_fstat(cs, complete, gf, addr);
- break;
- case GuestFDStatic:
- default:
- g_assert_not_reached();
- }
- }
- void semihost_sys_stat(CPUState *cs, gdb_syscall_complete_cb complete,
- target_ulong fname, target_ulong fname_len,
- target_ulong addr)
- {
- if (use_gdb_syscalls()) {
- gdb_stat(cs, complete, fname, fname_len, addr);
- } else {
- host_stat(cs, complete, fname, fname_len, addr);
- }
- }
- void semihost_sys_remove(CPUState *cs, gdb_syscall_complete_cb complete,
- target_ulong fname, target_ulong fname_len)
- {
- if (use_gdb_syscalls()) {
- gdb_remove(cs, complete, fname, fname_len);
- } else {
- host_remove(cs, complete, fname, fname_len);
- }
- }
- void semihost_sys_rename(CPUState *cs, gdb_syscall_complete_cb complete,
- target_ulong oname, target_ulong oname_len,
- target_ulong nname, target_ulong nname_len)
- {
- if (use_gdb_syscalls()) {
- gdb_rename(cs, complete, oname, oname_len, nname, nname_len);
- } else {
- host_rename(cs, complete, oname, oname_len, nname, nname_len);
- }
- }
- void semihost_sys_system(CPUState *cs, gdb_syscall_complete_cb complete,
- target_ulong cmd, target_ulong cmd_len)
- {
- if (use_gdb_syscalls()) {
- gdb_system(cs, complete, cmd, cmd_len);
- } else {
- host_system(cs, complete, cmd, cmd_len);
- }
- }
- void semihost_sys_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete,
- target_ulong tv_addr, target_ulong tz_addr)
- {
- if (use_gdb_syscalls()) {
- gdb_gettimeofday(cs, complete, tv_addr, tz_addr);
- } else {
- host_gettimeofday(cs, complete, tv_addr, tz_addr);
- }
- }
- #ifndef CONFIG_USER_ONLY
- void semihost_sys_poll_one(CPUState *cs, gdb_syscall_complete_cb complete,
- int fd, GIOCondition cond, int timeout)
- {
- GuestFD *gf = get_guestfd(fd);
- if (!gf) {
- complete(cs, G_IO_NVAL, 1);
- return;
- }
- switch (gf->type) {
- case GuestFDGDB:
- complete(cs, G_IO_NVAL, 1);
- break;
- case GuestFDHost:
- host_poll_one(cs, complete, gf, cond, timeout);
- break;
- case GuestFDConsole:
- console_poll_one(cs, complete, gf, cond, timeout);
- break;
- case GuestFDStatic:
- default:
- g_assert_not_reached();
- }
- }
- #endif
|