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 memcpy(p, str->str, str->len); 325 unlock_user(p, addr, str->len); 326 semihost_sys_write(cs, uhi_cb, 2, addr, str->len); 327 } else { 328 gpr[2] = qemu_semihosting_console_write(str->str, str->len); 329 } 330 g_string_free(str, true); 331 } 332 break; 333 334 case UHI_assert: 335 { 336 const char *msg, *file; 337 338 msg = lock_user_string(gpr[4]); 339 if (!msg) { 340 msg = "<EFAULT>"; 341 } 342 file = lock_user_string(gpr[5]); 343 if (!file) { 344 file = "<EFAULT>"; 345 } 346 347 error_report("UHI assertion \"%s\": file \"%s\", line %d", 348 msg, file, (int)gpr[6]); 349 abort(); 350 } 351 352 default: 353 error_report("Unknown UHI operation %d", op); 354 abort(); 355 } 356 return; 357 } 358