qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

mips-semi.c (9722B)


      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/gdbstub.h"
     24 #include "semihosting/softmmu-uaccess.h"
     25 #include "semihosting/semihost.h"
     26 #include "semihosting/console.h"
     27 #include "semihosting/syscalls.h"
     28 #include "internal.h"
     29 
     30 typedef enum UHIOp {
     31     UHI_exit = 1,
     32     UHI_open = 2,
     33     UHI_close = 3,
     34     UHI_read = 4,
     35     UHI_write = 5,
     36     UHI_lseek = 6,
     37     UHI_unlink = 7,
     38     UHI_fstat = 8,
     39     UHI_argc = 9,
     40     UHI_argnlen = 10,
     41     UHI_argn = 11,
     42     UHI_plog = 13,
     43     UHI_assert = 14,
     44     UHI_pread = 19,
     45     UHI_pwrite = 20,
     46     UHI_link = 22
     47 } UHIOp;
     48 
     49 typedef struct UHIStat {
     50     int16_t uhi_st_dev;
     51     uint16_t uhi_st_ino;
     52     uint32_t uhi_st_mode;
     53     uint16_t uhi_st_nlink;
     54     uint16_t uhi_st_uid;
     55     uint16_t uhi_st_gid;
     56     int16_t uhi_st_rdev;
     57     uint64_t uhi_st_size;
     58     uint64_t uhi_st_atime;
     59     uint64_t uhi_st_spare1;
     60     uint64_t uhi_st_mtime;
     61     uint64_t uhi_st_spare2;
     62     uint64_t uhi_st_ctime;
     63     uint64_t uhi_st_spare3;
     64     uint64_t uhi_st_blksize;
     65     uint64_t uhi_st_blocks;
     66     uint64_t uhi_st_spare4[2];
     67 } UHIStat;
     68 
     69 enum UHIOpenFlags {
     70     UHIOpen_RDONLY = 0x0,
     71     UHIOpen_WRONLY = 0x1,
     72     UHIOpen_RDWR   = 0x2,
     73     UHIOpen_APPEND = 0x8,
     74     UHIOpen_CREAT  = 0x200,
     75     UHIOpen_TRUNC  = 0x400,
     76     UHIOpen_EXCL   = 0x800
     77 };
     78 
     79 enum UHIErrno {
     80     UHI_EACCESS         = 13,
     81     UHI_EAGAIN          = 11,
     82     UHI_EBADF           = 9,
     83     UHI_EBADMSG         = 77,
     84     UHI_EBUSY           = 16,
     85     UHI_ECONNRESET      = 104,
     86     UHI_EEXIST          = 17,
     87     UHI_EFBIG           = 27,
     88     UHI_EINTR           = 4,
     89     UHI_EINVAL          = 22,
     90     UHI_EIO             = 5,
     91     UHI_EISDIR          = 21,
     92     UHI_ELOOP           = 92,
     93     UHI_EMFILE          = 24,
     94     UHI_EMLINK          = 31,
     95     UHI_ENAMETOOLONG    = 91,
     96     UHI_ENETDOWN        = 115,
     97     UHI_ENETUNREACH     = 114,
     98     UHI_ENFILE          = 23,
     99     UHI_ENOBUFS         = 105,
    100     UHI_ENOENT          = 2,
    101     UHI_ENOMEM          = 12,
    102     UHI_ENOSPC          = 28,
    103     UHI_ENOSR           = 63,
    104     UHI_ENOTCONN        = 128,
    105     UHI_ENOTDIR         = 20,
    106     UHI_ENXIO           = 6,
    107     UHI_EOVERFLOW       = 139,
    108     UHI_EPERM           = 1,
    109     UHI_EPIPE           = 32,
    110     UHI_ERANGE          = 34,
    111     UHI_EROFS           = 30,
    112     UHI_ESPIPE          = 29,
    113     UHI_ETIMEDOUT       = 116,
    114     UHI_ETXTBSY         = 26,
    115     UHI_EWOULDBLOCK     = 11,
    116     UHI_EXDEV           = 18,
    117 };
    118 
    119 static void report_fault(CPUMIPSState *env)
    120 {
    121     int op = env->active_tc.gpr[25];
    122     error_report("Fault during UHI operation %d", op);
    123     abort();
    124 }
    125 
    126 static void uhi_cb(CPUState *cs, uint64_t ret, int err)
    127 {
    128     CPUMIPSState *env = cs->env_ptr;
    129 
    130 #define E(N) case E##N: err = UHI_E##N; break
    131 
    132     switch (err) {
    133     case 0:
    134         break;
    135     E(PERM);
    136     E(NOENT);
    137     E(INTR);
    138     E(BADF);
    139     E(BUSY);
    140     E(EXIST);
    141     E(NOTDIR);
    142     E(ISDIR);
    143     E(INVAL);
    144     E(NFILE);
    145     E(MFILE);
    146     E(FBIG);
    147     E(NOSPC);
    148     E(SPIPE);
    149     E(ROFS);
    150     E(NAMETOOLONG);
    151     default:
    152         err = UHI_EINVAL;
    153         break;
    154     case EFAULT:
    155         report_fault(env);
    156     }
    157 
    158 #undef E
    159 
    160     env->active_tc.gpr[2] = ret;
    161     env->active_tc.gpr[3] = err;
    162 }
    163 
    164 static void uhi_fstat_cb(CPUState *cs, uint64_t ret, int err)
    165 {
    166     QEMU_BUILD_BUG_ON(sizeof(UHIStat) < sizeof(struct gdb_stat));
    167 
    168     if (!err) {
    169         CPUMIPSState *env = cs->env_ptr;
    170         target_ulong addr = env->active_tc.gpr[5];
    171         UHIStat *dst = lock_user(VERIFY_WRITE, addr, sizeof(UHIStat), 1);
    172         struct gdb_stat s;
    173 
    174         if (!dst) {
    175             report_fault(env);
    176         }
    177 
    178         memcpy(&s, dst, sizeof(struct gdb_stat));
    179         memset(dst, 0, sizeof(UHIStat));
    180 
    181         dst->uhi_st_dev = tswap16(be32_to_cpu(s.gdb_st_dev));
    182         dst->uhi_st_ino = tswap16(be32_to_cpu(s.gdb_st_ino));
    183         dst->uhi_st_mode = tswap32(be32_to_cpu(s.gdb_st_mode));
    184         dst->uhi_st_nlink = tswap16(be32_to_cpu(s.gdb_st_nlink));
    185         dst->uhi_st_uid = tswap16(be32_to_cpu(s.gdb_st_uid));
    186         dst->uhi_st_gid = tswap16(be32_to_cpu(s.gdb_st_gid));
    187         dst->uhi_st_rdev = tswap16(be32_to_cpu(s.gdb_st_rdev));
    188         dst->uhi_st_size = tswap64(be64_to_cpu(s.gdb_st_size));
    189         dst->uhi_st_atime = tswap64(be32_to_cpu(s.gdb_st_atime));
    190         dst->uhi_st_mtime = tswap64(be32_to_cpu(s.gdb_st_mtime));
    191         dst->uhi_st_ctime = tswap64(be32_to_cpu(s.gdb_st_ctime));
    192         dst->uhi_st_blksize = tswap64(be64_to_cpu(s.gdb_st_blksize));
    193         dst->uhi_st_blocks = tswap64(be64_to_cpu(s.gdb_st_blocks));
    194 
    195         unlock_user(dst, addr, sizeof(UHIStat));
    196     }
    197 
    198     uhi_cb(cs, ret, err);
    199 }
    200 
    201 void mips_semihosting(CPUMIPSState *env)
    202 {
    203     CPUState *cs = env_cpu(env);
    204     target_ulong *gpr = env->active_tc.gpr;
    205     const UHIOp op = gpr[25];
    206     char *p;
    207 
    208     switch (op) {
    209     case UHI_exit:
    210         gdb_exit(gpr[4]);
    211         exit(gpr[4]);
    212 
    213     case UHI_open:
    214         {
    215             target_ulong fname = gpr[4];
    216             int ret = -1;
    217 
    218             p = lock_user_string(fname);
    219             if (!p) {
    220                 report_fault(env);
    221             }
    222             if (!strcmp("/dev/stdin", p)) {
    223                 ret = 0;
    224             } else if (!strcmp("/dev/stdout", p)) {
    225                 ret = 1;
    226             } else if (!strcmp("/dev/stderr", p)) {
    227                 ret = 2;
    228             }
    229             unlock_user(p, fname, 0);
    230 
    231             /* FIXME: reusing a guest fd doesn't seem correct. */
    232             if (ret >= 0) {
    233                 gpr[2] = ret;
    234                 break;
    235             }
    236 
    237             semihost_sys_open(cs, uhi_cb, fname, 0, gpr[5], gpr[6]);
    238         }
    239         break;
    240 
    241     case UHI_close:
    242         semihost_sys_close(cs, uhi_cb, gpr[4]);
    243         break;
    244     case UHI_read:
    245         semihost_sys_read(cs, uhi_cb, gpr[4], gpr[5], gpr[6]);
    246         break;
    247     case UHI_write:
    248         semihost_sys_write(cs, uhi_cb, gpr[4], gpr[5], gpr[6]);
    249         break;
    250     case UHI_lseek:
    251         semihost_sys_lseek(cs, uhi_cb, gpr[4], gpr[5], gpr[6]);
    252         break;
    253     case UHI_unlink:
    254         semihost_sys_remove(cs, uhi_cb, gpr[4], 0);
    255         break;
    256     case UHI_fstat:
    257         semihost_sys_fstat(cs, uhi_fstat_cb, gpr[4], gpr[5]);
    258         break;
    259 
    260     case UHI_argc:
    261         gpr[2] = semihosting_get_argc();
    262         break;
    263     case UHI_argnlen:
    264         {
    265             const char *s = semihosting_get_arg(gpr[4]);
    266             gpr[2] = s ? strlen(s) : -1;
    267         }
    268         break;
    269     case UHI_argn:
    270         {
    271             const char *s = semihosting_get_arg(gpr[4]);
    272             target_ulong addr;
    273             size_t len;
    274 
    275             if (!s) {
    276                 gpr[2] = -1;
    277                 break;
    278             }
    279             len = strlen(s) + 1;
    280             addr = gpr[5];
    281             p = lock_user(VERIFY_WRITE, addr, len, 0);
    282             if (!p) {
    283                 report_fault(env);
    284             }
    285             memcpy(p, s, len);
    286             unlock_user(p, addr, len);
    287             gpr[2] = 0;
    288         }
    289         break;
    290 
    291     case UHI_plog:
    292         {
    293             target_ulong addr = gpr[4];
    294             ssize_t len = target_strlen(addr);
    295             GString *str;
    296             char *pct_d;
    297 
    298             if (len < 0) {
    299                 report_fault(env);
    300             }
    301             p = lock_user(VERIFY_READ, addr, len, 1);
    302             if (!p) {
    303                 report_fault(env);
    304             }
    305 
    306             pct_d = strstr(p, "%d");
    307             if (!pct_d) {
    308                 unlock_user(p, addr, 0);
    309                 semihost_sys_write(cs, uhi_cb, 2, addr, len);
    310                 break;
    311             }
    312 
    313             str = g_string_new_len(p, pct_d - p);
    314             g_string_append_printf(str, "%d%s", (int)gpr[5], pct_d + 2);
    315             unlock_user(p, addr, 0);
    316 
    317             /*
    318              * When we're using gdb, we need a guest address, so
    319              * drop the string onto the stack below the stack pointer.
    320              */
    321             if (use_gdb_syscalls()) {
    322                 addr = gpr[29] - str->len;
    323                 p = lock_user(VERIFY_WRITE, addr, str->len, 0);
    324                 if (!p) {
    325                     report_fault(env);
    326                 }
    327                 memcpy(p, str->str, str->len);
    328                 unlock_user(p, addr, str->len);
    329                 semihost_sys_write(cs, uhi_cb, 2, addr, str->len);
    330             } else {
    331                 gpr[2] = qemu_semihosting_console_write(str->str, str->len);
    332             }
    333             g_string_free(str, true);
    334         }
    335         break;
    336 
    337     case UHI_assert:
    338         {
    339             const char *msg, *file;
    340 
    341             msg = lock_user_string(gpr[4]);
    342             if (!msg) {
    343                 msg = "<EFAULT>";
    344             }
    345             file = lock_user_string(gpr[5]);
    346             if (!file) {
    347                 file = "<EFAULT>";
    348             }
    349 
    350             error_report("UHI assertion \"%s\": file \"%s\", line %d",
    351                          msg, file, (int)gpr[6]);
    352             abort();
    353         }
    354 
    355     default:
    356         error_report("Unknown UHI operation %d", op);
    357         abort();
    358     }
    359     return;
    360 }