13b3c1694SLeon Alrae /* 23b3c1694SLeon Alrae * Unified Hosting Interface syscalls. 33b3c1694SLeon Alrae * 43b3c1694SLeon Alrae * Copyright (c) 2015 Imagination Technologies 53b3c1694SLeon Alrae * 63b3c1694SLeon Alrae * This library is free software; you can redistribute it and/or 73b3c1694SLeon Alrae * modify it under the terms of the GNU Lesser General Public 83b3c1694SLeon Alrae * License as published by the Free Software Foundation; either 989975214SChetan Pant * version 2.1 of the License, or (at your option) any later version. 103b3c1694SLeon Alrae * 113b3c1694SLeon Alrae * This library is distributed in the hope that it will be useful, 123b3c1694SLeon Alrae * but WITHOUT ANY WARRANTY; without even the implied warranty of 133b3c1694SLeon Alrae * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 143b3c1694SLeon Alrae * Lesser General Public License for more details. 153b3c1694SLeon Alrae * 163b3c1694SLeon Alrae * You should have received a copy of the GNU Lesser General Public 173b3c1694SLeon Alrae * License along with this library; if not, see <http://www.gnu.org/licenses/>. 183b3c1694SLeon Alrae */ 193b3c1694SLeon Alrae 20c684822aSPeter Maydell #include "qemu/osdep.h" 213b3c1694SLeon Alrae #include "cpu.h" 2263c91552SPaolo Bonzini #include "qemu/log.h" 2318639a28SRichard Henderson #include "exec/gdbstub.h" 24c89a14adSRichard Henderson #include "semihosting/softmmu-uaccess.h" 256b5fe137SPhilippe Mathieu-Daudé #include "semihosting/semihost.h" 266b5fe137SPhilippe Mathieu-Daudé #include "semihosting/console.h" 2718639a28SRichard Henderson #include "semihosting/syscalls.h" 288ec7e3c5SRichard Henderson #include "internal.h" 293b3c1694SLeon Alrae 303b3c1694SLeon Alrae typedef enum UHIOp { 313b3c1694SLeon Alrae UHI_exit = 1, 323b3c1694SLeon Alrae UHI_open = 2, 333b3c1694SLeon Alrae UHI_close = 3, 343b3c1694SLeon Alrae UHI_read = 4, 353b3c1694SLeon Alrae UHI_write = 5, 363b3c1694SLeon Alrae UHI_lseek = 6, 373b3c1694SLeon Alrae UHI_unlink = 7, 383b3c1694SLeon Alrae UHI_fstat = 8, 393b3c1694SLeon Alrae UHI_argc = 9, 403b3c1694SLeon Alrae UHI_argnlen = 10, 413b3c1694SLeon Alrae UHI_argn = 11, 423b3c1694SLeon Alrae UHI_plog = 13, 433b3c1694SLeon Alrae UHI_assert = 14, 443b3c1694SLeon Alrae UHI_pread = 19, 453b3c1694SLeon Alrae UHI_pwrite = 20, 463b3c1694SLeon Alrae UHI_link = 22 473b3c1694SLeon Alrae } UHIOp; 483b3c1694SLeon Alrae 493b3c1694SLeon Alrae typedef struct UHIStat { 503b3c1694SLeon Alrae int16_t uhi_st_dev; 513b3c1694SLeon Alrae uint16_t uhi_st_ino; 523b3c1694SLeon Alrae uint32_t uhi_st_mode; 533b3c1694SLeon Alrae uint16_t uhi_st_nlink; 543b3c1694SLeon Alrae uint16_t uhi_st_uid; 553b3c1694SLeon Alrae uint16_t uhi_st_gid; 563b3c1694SLeon Alrae int16_t uhi_st_rdev; 573b3c1694SLeon Alrae uint64_t uhi_st_size; 583b3c1694SLeon Alrae uint64_t uhi_st_atime; 593b3c1694SLeon Alrae uint64_t uhi_st_spare1; 603b3c1694SLeon Alrae uint64_t uhi_st_mtime; 613b3c1694SLeon Alrae uint64_t uhi_st_spare2; 623b3c1694SLeon Alrae uint64_t uhi_st_ctime; 633b3c1694SLeon Alrae uint64_t uhi_st_spare3; 643b3c1694SLeon Alrae uint64_t uhi_st_blksize; 653b3c1694SLeon Alrae uint64_t uhi_st_blocks; 663b3c1694SLeon Alrae uint64_t uhi_st_spare4[2]; 673b3c1694SLeon Alrae } UHIStat; 683b3c1694SLeon Alrae 693b3c1694SLeon Alrae enum UHIOpenFlags { 703b3c1694SLeon Alrae UHIOpen_RDONLY = 0x0, 713b3c1694SLeon Alrae UHIOpen_WRONLY = 0x1, 723b3c1694SLeon Alrae UHIOpen_RDWR = 0x2, 733b3c1694SLeon Alrae UHIOpen_APPEND = 0x8, 743b3c1694SLeon Alrae UHIOpen_CREAT = 0x200, 753b3c1694SLeon Alrae UHIOpen_TRUNC = 0x400, 763b3c1694SLeon Alrae UHIOpen_EXCL = 0x800 773b3c1694SLeon Alrae }; 783b3c1694SLeon Alrae 797ba6e53aSRichard Henderson enum UHIErrno { 807ba6e53aSRichard Henderson UHI_EACCESS = 13, 817ba6e53aSRichard Henderson UHI_EAGAIN = 11, 827ba6e53aSRichard Henderson UHI_EBADF = 9, 837ba6e53aSRichard Henderson UHI_EBADMSG = 77, 847ba6e53aSRichard Henderson UHI_EBUSY = 16, 857ba6e53aSRichard Henderson UHI_ECONNRESET = 104, 867ba6e53aSRichard Henderson UHI_EEXIST = 17, 877ba6e53aSRichard Henderson UHI_EFBIG = 27, 887ba6e53aSRichard Henderson UHI_EINTR = 4, 897ba6e53aSRichard Henderson UHI_EINVAL = 22, 907ba6e53aSRichard Henderson UHI_EIO = 5, 917ba6e53aSRichard Henderson UHI_EISDIR = 21, 927ba6e53aSRichard Henderson UHI_ELOOP = 92, 937ba6e53aSRichard Henderson UHI_EMFILE = 24, 947ba6e53aSRichard Henderson UHI_EMLINK = 31, 957ba6e53aSRichard Henderson UHI_ENAMETOOLONG = 91, 967ba6e53aSRichard Henderson UHI_ENETDOWN = 115, 977ba6e53aSRichard Henderson UHI_ENETUNREACH = 114, 987ba6e53aSRichard Henderson UHI_ENFILE = 23, 997ba6e53aSRichard Henderson UHI_ENOBUFS = 105, 1007ba6e53aSRichard Henderson UHI_ENOENT = 2, 1017ba6e53aSRichard Henderson UHI_ENOMEM = 12, 1027ba6e53aSRichard Henderson UHI_ENOSPC = 28, 1037ba6e53aSRichard Henderson UHI_ENOSR = 63, 1047ba6e53aSRichard Henderson UHI_ENOTCONN = 128, 1057ba6e53aSRichard Henderson UHI_ENOTDIR = 20, 1067ba6e53aSRichard Henderson UHI_ENXIO = 6, 1077ba6e53aSRichard Henderson UHI_EOVERFLOW = 139, 1087ba6e53aSRichard Henderson UHI_EPERM = 1, 1097ba6e53aSRichard Henderson UHI_EPIPE = 32, 1107ba6e53aSRichard Henderson UHI_ERANGE = 34, 1117ba6e53aSRichard Henderson UHI_EROFS = 30, 1127ba6e53aSRichard Henderson UHI_ESPIPE = 29, 1137ba6e53aSRichard Henderson UHI_ETIMEDOUT = 116, 1147ba6e53aSRichard Henderson UHI_ETXTBSY = 26, 1157ba6e53aSRichard Henderson UHI_EWOULDBLOCK = 11, 1167ba6e53aSRichard Henderson UHI_EXDEV = 18, 1177ba6e53aSRichard Henderson }; 1187ba6e53aSRichard Henderson 119d53a3ed4SRichard Henderson static void report_fault(CPUMIPSState *env) 120d53a3ed4SRichard Henderson { 121d53a3ed4SRichard Henderson int op = env->active_tc.gpr[25]; 122d53a3ed4SRichard Henderson error_report("Fault during UHI operation %d", op); 123d53a3ed4SRichard Henderson abort(); 124d53a3ed4SRichard Henderson } 125d53a3ed4SRichard Henderson 12618639a28SRichard Henderson static void uhi_cb(CPUState *cs, uint64_t ret, int err) 127d859a77dSPhilippe Mathieu-Daudé { 12818639a28SRichard Henderson CPUMIPSState *env = cs->env_ptr; 12918639a28SRichard Henderson 13018639a28SRichard Henderson #define E(N) case E##N: err = UHI_E##N; break 13118639a28SRichard Henderson 13218639a28SRichard Henderson switch (err) { 13318639a28SRichard Henderson case 0: 13418639a28SRichard Henderson break; 13518639a28SRichard Henderson E(PERM); 13618639a28SRichard Henderson E(NOENT); 13718639a28SRichard Henderson E(INTR); 13818639a28SRichard Henderson E(BADF); 13918639a28SRichard Henderson E(BUSY); 14018639a28SRichard Henderson E(EXIST); 14118639a28SRichard Henderson E(NOTDIR); 14218639a28SRichard Henderson E(ISDIR); 14318639a28SRichard Henderson E(INVAL); 14418639a28SRichard Henderson E(NFILE); 14518639a28SRichard Henderson E(MFILE); 14618639a28SRichard Henderson E(FBIG); 14718639a28SRichard Henderson E(NOSPC); 14818639a28SRichard Henderson E(SPIPE); 14918639a28SRichard Henderson E(ROFS); 15018639a28SRichard Henderson E(NAMETOOLONG); 15118639a28SRichard Henderson default: 15218639a28SRichard Henderson err = UHI_EINVAL; 15318639a28SRichard Henderson break; 15418639a28SRichard Henderson case EFAULT: 15518639a28SRichard Henderson report_fault(env); 1562c44b19cSLeon Alrae } 1572c44b19cSLeon Alrae 15818639a28SRichard Henderson #undef E 15918639a28SRichard Henderson 16018639a28SRichard Henderson env->active_tc.gpr[2] = ret; 16118639a28SRichard Henderson env->active_tc.gpr[3] = err; 16218639a28SRichard Henderson } 16318639a28SRichard Henderson 16418639a28SRichard Henderson static void uhi_fstat_cb(CPUState *cs, uint64_t ret, int err) 1653b3c1694SLeon Alrae { 16618639a28SRichard Henderson QEMU_BUILD_BUG_ON(sizeof(UHIStat) < sizeof(struct gdb_stat)); 16718639a28SRichard Henderson 16818639a28SRichard Henderson if (!err) { 16918639a28SRichard Henderson CPUMIPSState *env = cs->env_ptr; 17018639a28SRichard Henderson target_ulong addr = env->active_tc.gpr[5]; 17118639a28SRichard Henderson UHIStat *dst = lock_user(VERIFY_WRITE, addr, sizeof(UHIStat), 1); 17218639a28SRichard Henderson struct gdb_stat s; 17318639a28SRichard Henderson 1743b3c1694SLeon Alrae if (!dst) { 175d53a3ed4SRichard Henderson report_fault(env); 1763b3c1694SLeon Alrae } 1773b3c1694SLeon Alrae 17818639a28SRichard Henderson memcpy(&s, dst, sizeof(struct gdb_stat)); 17918639a28SRichard Henderson memset(dst, 0, sizeof(UHIStat)); 18018639a28SRichard Henderson 18118639a28SRichard Henderson dst->uhi_st_dev = tswap16(be32_to_cpu(s.gdb_st_dev)); 18218639a28SRichard Henderson dst->uhi_st_ino = tswap16(be32_to_cpu(s.gdb_st_ino)); 18318639a28SRichard Henderson dst->uhi_st_mode = tswap32(be32_to_cpu(s.gdb_st_mode)); 18418639a28SRichard Henderson dst->uhi_st_nlink = tswap16(be32_to_cpu(s.gdb_st_nlink)); 18518639a28SRichard Henderson dst->uhi_st_uid = tswap16(be32_to_cpu(s.gdb_st_uid)); 18618639a28SRichard Henderson dst->uhi_st_gid = tswap16(be32_to_cpu(s.gdb_st_gid)); 18718639a28SRichard Henderson dst->uhi_st_rdev = tswap16(be32_to_cpu(s.gdb_st_rdev)); 18818639a28SRichard Henderson dst->uhi_st_size = tswap64(be64_to_cpu(s.gdb_st_size)); 18918639a28SRichard Henderson dst->uhi_st_atime = tswap64(be32_to_cpu(s.gdb_st_atime)); 19018639a28SRichard Henderson dst->uhi_st_mtime = tswap64(be32_to_cpu(s.gdb_st_mtime)); 19118639a28SRichard Henderson dst->uhi_st_ctime = tswap64(be32_to_cpu(s.gdb_st_ctime)); 19218639a28SRichard Henderson dst->uhi_st_blksize = tswap64(be64_to_cpu(s.gdb_st_blksize)); 19318639a28SRichard Henderson dst->uhi_st_blocks = tswap64(be64_to_cpu(s.gdb_st_blocks)); 19418639a28SRichard Henderson 19518639a28SRichard Henderson unlock_user(dst, addr, sizeof(UHIStat)); 1963b3c1694SLeon Alrae } 1973b3c1694SLeon Alrae 19818639a28SRichard Henderson uhi_cb(cs, ret, err); 1993b3c1694SLeon Alrae } 2003b3c1694SLeon Alrae 2018ec7e3c5SRichard Henderson void mips_semihosting(CPUMIPSState *env) 2023b3c1694SLeon Alrae { 20318639a28SRichard Henderson CPUState *cs = env_cpu(env); 2043b3c1694SLeon Alrae target_ulong *gpr = env->active_tc.gpr; 2053b3c1694SLeon Alrae const UHIOp op = gpr[25]; 206412411b3SRichard Henderson char *p; 2073b3c1694SLeon Alrae 2083b3c1694SLeon Alrae switch (op) { 2093b3c1694SLeon Alrae case UHI_exit: 21018639a28SRichard Henderson gdb_exit(gpr[4]); 2113b3c1694SLeon Alrae exit(gpr[4]); 21218639a28SRichard Henderson 2133b3c1694SLeon Alrae case UHI_open: 21418639a28SRichard Henderson { 215b10ccec1SRichard Henderson target_ulong fname = gpr[4]; 21618639a28SRichard Henderson int ret = -1; 21718639a28SRichard Henderson 218b10ccec1SRichard Henderson p = lock_user_string(fname); 219b10ccec1SRichard Henderson if (!p) { 220b10ccec1SRichard Henderson report_fault(env); 221b10ccec1SRichard Henderson } 2223b3c1694SLeon Alrae if (!strcmp("/dev/stdin", p)) { 22318639a28SRichard Henderson ret = 0; 2243b3c1694SLeon Alrae } else if (!strcmp("/dev/stdout", p)) { 22518639a28SRichard Henderson ret = 1; 2263b3c1694SLeon Alrae } else if (!strcmp("/dev/stderr", p)) { 22718639a28SRichard Henderson ret = 2; 2283b3c1694SLeon Alrae } 229b10ccec1SRichard Henderson unlock_user(p, fname, 0); 23018639a28SRichard Henderson 23118639a28SRichard Henderson /* FIXME: reusing a guest fd doesn't seem correct. */ 23218639a28SRichard Henderson if (ret >= 0) { 23318639a28SRichard Henderson gpr[2] = ret; 2343b3c1694SLeon Alrae break; 2353b3c1694SLeon Alrae } 23618639a28SRichard Henderson 237b10ccec1SRichard Henderson semihost_sys_open(cs, uhi_cb, fname, 0, gpr[5], gpr[6]); 23818639a28SRichard Henderson } 23918639a28SRichard Henderson break; 24018639a28SRichard Henderson 24118639a28SRichard Henderson case UHI_close: 24218639a28SRichard Henderson semihost_sys_close(cs, uhi_cb, gpr[4]); 2433b3c1694SLeon Alrae break; 2443b3c1694SLeon Alrae case UHI_read: 24518639a28SRichard Henderson semihost_sys_read(cs, uhi_cb, gpr[4], gpr[5], gpr[6]); 2463b3c1694SLeon Alrae break; 2473b3c1694SLeon Alrae case UHI_write: 24818639a28SRichard Henderson semihost_sys_write(cs, uhi_cb, gpr[4], gpr[5], gpr[6]); 2493b3c1694SLeon Alrae break; 2503b3c1694SLeon Alrae case UHI_lseek: 25118639a28SRichard Henderson semihost_sys_lseek(cs, uhi_cb, gpr[4], gpr[5], gpr[6]); 2523b3c1694SLeon Alrae break; 2533b3c1694SLeon Alrae case UHI_unlink: 25418639a28SRichard Henderson semihost_sys_remove(cs, uhi_cb, gpr[4], 0); 2553b3c1694SLeon Alrae break; 2563b3c1694SLeon Alrae case UHI_fstat: 25718639a28SRichard Henderson semihost_sys_fstat(cs, uhi_fstat_cb, gpr[4], gpr[5]); 2583b3c1694SLeon Alrae break; 25918639a28SRichard Henderson 2603b3c1694SLeon Alrae case UHI_argc: 2613b3c1694SLeon Alrae gpr[2] = semihosting_get_argc(); 2623b3c1694SLeon Alrae break; 2633b3c1694SLeon Alrae case UHI_argnlen: 2643bb45bbcSRichard Henderson { 2653bb45bbcSRichard Henderson const char *s = semihosting_get_arg(gpr[4]); 2663bb45bbcSRichard Henderson gpr[2] = s ? strlen(s) : -1; 2673b3c1694SLeon Alrae } 2683b3c1694SLeon Alrae break; 2693b3c1694SLeon Alrae case UHI_argn: 2703bb45bbcSRichard Henderson { 2713bb45bbcSRichard Henderson const char *s = semihosting_get_arg(gpr[4]); 2723bb45bbcSRichard Henderson target_ulong addr; 2733bb45bbcSRichard Henderson size_t len; 2743bb45bbcSRichard Henderson 2753bb45bbcSRichard Henderson if (!s) { 2763b3c1694SLeon Alrae gpr[2] = -1; 2773bb45bbcSRichard Henderson break; 2783b3c1694SLeon Alrae } 2793bb45bbcSRichard Henderson len = strlen(s) + 1; 2803bb45bbcSRichard Henderson addr = gpr[5]; 2813bb45bbcSRichard Henderson p = lock_user(VERIFY_WRITE, addr, len, 0); 2823bb45bbcSRichard Henderson if (!p) { 2833bb45bbcSRichard Henderson report_fault(env); 2843bb45bbcSRichard Henderson } 2853bb45bbcSRichard Henderson memcpy(p, s, len); 2863bb45bbcSRichard Henderson unlock_user(p, addr, len); 2873bb45bbcSRichard Henderson gpr[2] = 0; 2883bb45bbcSRichard Henderson } 2893b3c1694SLeon Alrae break; 290ea421060SRichard Henderson 2913b3c1694SLeon Alrae case UHI_plog: 292ea421060SRichard Henderson { 293ea421060SRichard Henderson target_ulong addr = gpr[4]; 294ea421060SRichard Henderson ssize_t len = target_strlen(addr); 295ea421060SRichard Henderson GString *str; 296ea421060SRichard Henderson char *pct_d; 297ea421060SRichard Henderson 298ea421060SRichard Henderson if (len < 0) { 299ea421060SRichard Henderson report_fault(env); 3003b3c1694SLeon Alrae } 301ea421060SRichard Henderson p = lock_user(VERIFY_READ, addr, len, 1); 302ea421060SRichard Henderson if (!p) { 303ea421060SRichard Henderson report_fault(env); 304ea421060SRichard Henderson } 305ea421060SRichard Henderson 306ea421060SRichard Henderson pct_d = strstr(p, "%d"); 307ea421060SRichard Henderson if (!pct_d) { 308b10ccec1SRichard Henderson unlock_user(p, addr, 0); 309ea421060SRichard Henderson semihost_sys_write(cs, uhi_cb, 2, addr, len); 3103b3c1694SLeon Alrae break; 311ea421060SRichard Henderson } 312ea421060SRichard Henderson 313ea421060SRichard Henderson str = g_string_new_len(p, pct_d - p); 314ea421060SRichard Henderson g_string_append_printf(str, "%d%s", (int)gpr[5], pct_d + 2); 315b10ccec1SRichard Henderson unlock_user(p, addr, 0); 316ea421060SRichard Henderson 317ea421060SRichard Henderson /* 318ea421060SRichard Henderson * When we're using gdb, we need a guest address, so 319ea421060SRichard Henderson * drop the string onto the stack below the stack pointer. 320ea421060SRichard Henderson */ 321ea421060SRichard Henderson if (use_gdb_syscalls()) { 322ea421060SRichard Henderson addr = gpr[29] - str->len; 323ea421060SRichard Henderson p = lock_user(VERIFY_WRITE, addr, str->len, 0); 324*8809baf4SPeter Maydell if (!p) { 325*8809baf4SPeter Maydell report_fault(env); 326*8809baf4SPeter Maydell } 327ea421060SRichard Henderson memcpy(p, str->str, str->len); 328ea421060SRichard Henderson unlock_user(p, addr, str->len); 329ea421060SRichard Henderson semihost_sys_write(cs, uhi_cb, 2, addr, str->len); 330ea421060SRichard Henderson } else { 331ea421060SRichard Henderson gpr[2] = qemu_semihosting_console_write(str->str, str->len); 332ea421060SRichard Henderson } 333ea421060SRichard Henderson g_string_free(str, true); 334ea421060SRichard Henderson } 335ea421060SRichard Henderson break; 336ea421060SRichard Henderson 3373b3c1694SLeon Alrae case UHI_assert: 338412411b3SRichard Henderson { 339412411b3SRichard Henderson const char *msg, *file; 340412411b3SRichard Henderson 341412411b3SRichard Henderson msg = lock_user_string(gpr[4]); 342412411b3SRichard Henderson if (!msg) { 343412411b3SRichard Henderson msg = "<EFAULT>"; 344412411b3SRichard Henderson } 345412411b3SRichard Henderson file = lock_user_string(gpr[5]); 346412411b3SRichard Henderson if (!file) { 347412411b3SRichard Henderson file = "<EFAULT>"; 348412411b3SRichard Henderson } 349412411b3SRichard Henderson 350412411b3SRichard Henderson error_report("UHI assertion \"%s\": file \"%s\", line %d", 351412411b3SRichard Henderson msg, file, (int)gpr[6]); 3523b3c1694SLeon Alrae abort(); 353412411b3SRichard Henderson } 354412411b3SRichard Henderson 3553b3c1694SLeon Alrae default: 356d53a3ed4SRichard Henderson error_report("Unknown UHI operation %d", op); 3573b3c1694SLeon Alrae abort(); 3583b3c1694SLeon Alrae } 3593b3c1694SLeon Alrae return; 3603b3c1694SLeon Alrae } 361