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 "semihosting/softmmu-uaccess.h" 24 #include "semihosting/semihost.h" 25 #include "semihosting/console.h" 26 #include "internal.h" 27 28 typedef enum UHIOp { 29 UHI_exit = 1, 30 UHI_open = 2, 31 UHI_close = 3, 32 UHI_read = 4, 33 UHI_write = 5, 34 UHI_lseek = 6, 35 UHI_unlink = 7, 36 UHI_fstat = 8, 37 UHI_argc = 9, 38 UHI_argnlen = 10, 39 UHI_argn = 11, 40 UHI_plog = 13, 41 UHI_assert = 14, 42 UHI_pread = 19, 43 UHI_pwrite = 20, 44 UHI_link = 22 45 } UHIOp; 46 47 typedef struct UHIStat { 48 int16_t uhi_st_dev; 49 uint16_t uhi_st_ino; 50 uint32_t uhi_st_mode; 51 uint16_t uhi_st_nlink; 52 uint16_t uhi_st_uid; 53 uint16_t uhi_st_gid; 54 int16_t uhi_st_rdev; 55 uint64_t uhi_st_size; 56 uint64_t uhi_st_atime; 57 uint64_t uhi_st_spare1; 58 uint64_t uhi_st_mtime; 59 uint64_t uhi_st_spare2; 60 uint64_t uhi_st_ctime; 61 uint64_t uhi_st_spare3; 62 uint64_t uhi_st_blksize; 63 uint64_t uhi_st_blocks; 64 uint64_t uhi_st_spare4[2]; 65 } UHIStat; 66 67 enum UHIOpenFlags { 68 UHIOpen_RDONLY = 0x0, 69 UHIOpen_WRONLY = 0x1, 70 UHIOpen_RDWR = 0x2, 71 UHIOpen_APPEND = 0x8, 72 UHIOpen_CREAT = 0x200, 73 UHIOpen_TRUNC = 0x400, 74 UHIOpen_EXCL = 0x800 75 }; 76 77 enum UHIErrno { 78 UHI_EACCESS = 13, 79 UHI_EAGAIN = 11, 80 UHI_EBADF = 9, 81 UHI_EBADMSG = 77, 82 UHI_EBUSY = 16, 83 UHI_ECONNRESET = 104, 84 UHI_EEXIST = 17, 85 UHI_EFBIG = 27, 86 UHI_EINTR = 4, 87 UHI_EINVAL = 22, 88 UHI_EIO = 5, 89 UHI_EISDIR = 21, 90 UHI_ELOOP = 92, 91 UHI_EMFILE = 24, 92 UHI_EMLINK = 31, 93 UHI_ENAMETOOLONG = 91, 94 UHI_ENETDOWN = 115, 95 UHI_ENETUNREACH = 114, 96 UHI_ENFILE = 23, 97 UHI_ENOBUFS = 105, 98 UHI_ENOENT = 2, 99 UHI_ENOMEM = 12, 100 UHI_ENOSPC = 28, 101 UHI_ENOSR = 63, 102 UHI_ENOTCONN = 128, 103 UHI_ENOTDIR = 20, 104 UHI_ENXIO = 6, 105 UHI_EOVERFLOW = 139, 106 UHI_EPERM = 1, 107 UHI_EPIPE = 32, 108 UHI_ERANGE = 34, 109 UHI_EROFS = 30, 110 UHI_ESPIPE = 29, 111 UHI_ETIMEDOUT = 116, 112 UHI_ETXTBSY = 26, 113 UHI_EWOULDBLOCK = 11, 114 UHI_EXDEV = 18, 115 }; 116 117 static int errno_mips(int host_errno) 118 { 119 /* Errno values taken from asm-mips/errno.h */ 120 switch (host_errno) { 121 case 0: return 0; 122 case ENAMETOOLONG: return 78; 123 #ifdef EOVERFLOW 124 case EOVERFLOW: return 79; 125 #endif 126 #ifdef ELOOP 127 case ELOOP: return 90; 128 #endif 129 default: return EINVAL; 130 } 131 } 132 133 static int copy_stat_to_target(CPUMIPSState *env, const struct stat *src, 134 target_ulong vaddr) 135 { 136 hwaddr len = sizeof(struct UHIStat); 137 UHIStat *dst = lock_user(VERIFY_WRITE, vaddr, len, 0); 138 if (!dst) { 139 errno = EFAULT; 140 return -1; 141 } 142 143 dst->uhi_st_dev = tswap16(src->st_dev); 144 dst->uhi_st_ino = tswap16(src->st_ino); 145 dst->uhi_st_mode = tswap32(src->st_mode); 146 dst->uhi_st_nlink = tswap16(src->st_nlink); 147 dst->uhi_st_uid = tswap16(src->st_uid); 148 dst->uhi_st_gid = tswap16(src->st_gid); 149 dst->uhi_st_rdev = tswap16(src->st_rdev); 150 dst->uhi_st_size = tswap64(src->st_size); 151 dst->uhi_st_atime = tswap64(src->st_atime); 152 dst->uhi_st_mtime = tswap64(src->st_mtime); 153 dst->uhi_st_ctime = tswap64(src->st_ctime); 154 #ifdef _WIN32 155 dst->uhi_st_blksize = 0; 156 dst->uhi_st_blocks = 0; 157 #else 158 dst->uhi_st_blksize = tswap64(src->st_blksize); 159 dst->uhi_st_blocks = tswap64(src->st_blocks); 160 #endif 161 unlock_user(dst, vaddr, len); 162 return 0; 163 } 164 165 static int get_open_flags(target_ulong target_flags) 166 { 167 int open_flags = 0; 168 169 if (target_flags & UHIOpen_RDWR) { 170 open_flags |= O_RDWR; 171 } else if (target_flags & UHIOpen_WRONLY) { 172 open_flags |= O_WRONLY; 173 } else { 174 open_flags |= O_RDONLY; 175 } 176 177 open_flags |= (target_flags & UHIOpen_APPEND) ? O_APPEND : 0; 178 open_flags |= (target_flags & UHIOpen_CREAT) ? O_CREAT : 0; 179 open_flags |= (target_flags & UHIOpen_TRUNC) ? O_TRUNC : 0; 180 open_flags |= (target_flags & UHIOpen_EXCL) ? O_EXCL : 0; 181 182 return open_flags; 183 } 184 185 static int write_to_file(CPUMIPSState *env, target_ulong fd, 186 target_ulong vaddr, target_ulong len) 187 { 188 int num_of_bytes; 189 void *dst = lock_user(VERIFY_READ, vaddr, len, 1); 190 if (!dst) { 191 errno = EFAULT; 192 return -1; 193 } 194 195 num_of_bytes = write(fd, dst, len); 196 197 unlock_user(dst, vaddr, 0); 198 return num_of_bytes; 199 } 200 201 static int read_from_file(CPUMIPSState *env, target_ulong fd, 202 target_ulong vaddr, target_ulong len) 203 { 204 int num_of_bytes; 205 void *dst = lock_user(VERIFY_WRITE, vaddr, len, 0); 206 if (!dst) { 207 errno = EFAULT; 208 return -1; 209 } 210 211 num_of_bytes = read(fd, dst, len); 212 213 unlock_user(dst, vaddr, len); 214 return num_of_bytes; 215 } 216 217 static int copy_argn_to_target(CPUMIPSState *env, int arg_num, 218 target_ulong vaddr) 219 { 220 int strsize = strlen(semihosting_get_arg(arg_num)) + 1; 221 char *dst = lock_user(VERIFY_WRITE, vaddr, strsize, 0); 222 if (!dst) { 223 return -1; 224 } 225 226 strcpy(dst, semihosting_get_arg(arg_num)); 227 228 unlock_user(dst, vaddr, strsize); 229 return 0; 230 } 231 232 #define GET_TARGET_STRING(p, addr) \ 233 do { \ 234 p = lock_user_string(addr); \ 235 if (!p) { \ 236 gpr[2] = -1; \ 237 gpr[3] = EFAULT; \ 238 return; \ 239 } \ 240 } while (0) 241 242 #define GET_TARGET_STRINGS_2(p, addr, p2, addr2) \ 243 do { \ 244 p = lock_user_string(addr); \ 245 if (!p) { \ 246 gpr[2] = -1; \ 247 gpr[3] = EFAULT; \ 248 return; \ 249 } \ 250 p2 = lock_user_string(addr2); \ 251 if (!p2) { \ 252 unlock_user(p, addr, 0); \ 253 gpr[2] = -1; \ 254 gpr[3] = EFAULT; \ 255 return; \ 256 } \ 257 } while (0) 258 259 #define FREE_TARGET_STRING(p, gpr) \ 260 do { \ 261 unlock_user(p, gpr, 0); \ 262 } while (0) 263 264 void mips_semihosting(CPUMIPSState *env) 265 { 266 target_ulong *gpr = env->active_tc.gpr; 267 const UHIOp op = gpr[25]; 268 char *p, *p2; 269 270 switch (op) { 271 case UHI_exit: 272 qemu_log("UHI(%d): exit(%d)\n", op, (int)gpr[4]); 273 exit(gpr[4]); 274 case UHI_open: 275 GET_TARGET_STRING(p, gpr[4]); 276 if (!strcmp("/dev/stdin", p)) { 277 gpr[2] = 0; 278 } else if (!strcmp("/dev/stdout", p)) { 279 gpr[2] = 1; 280 } else if (!strcmp("/dev/stderr", p)) { 281 gpr[2] = 2; 282 } else { 283 gpr[2] = open(p, get_open_flags(gpr[5]), gpr[6]); 284 gpr[3] = errno_mips(errno); 285 } 286 FREE_TARGET_STRING(p, gpr[4]); 287 break; 288 case UHI_close: 289 if (gpr[4] < 3) { 290 /* ignore closing stdin/stdout/stderr */ 291 gpr[2] = 0; 292 return; 293 } 294 gpr[2] = close(gpr[4]); 295 gpr[3] = errno_mips(errno); 296 break; 297 case UHI_read: 298 gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6]); 299 gpr[3] = errno_mips(errno); 300 break; 301 case UHI_write: 302 gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6]); 303 gpr[3] = errno_mips(errno); 304 break; 305 case UHI_lseek: 306 gpr[2] = lseek(gpr[4], gpr[5], gpr[6]); 307 gpr[3] = errno_mips(errno); 308 break; 309 case UHI_unlink: 310 GET_TARGET_STRING(p, gpr[4]); 311 gpr[2] = remove(p); 312 gpr[3] = errno_mips(errno); 313 FREE_TARGET_STRING(p, gpr[4]); 314 break; 315 case UHI_fstat: 316 { 317 struct stat sbuf; 318 memset(&sbuf, 0, sizeof(sbuf)); 319 gpr[2] = fstat(gpr[4], &sbuf); 320 gpr[3] = errno_mips(errno); 321 if (gpr[2]) { 322 return; 323 } 324 gpr[2] = copy_stat_to_target(env, &sbuf, gpr[5]); 325 gpr[3] = errno_mips(errno); 326 } 327 break; 328 case UHI_argc: 329 gpr[2] = semihosting_get_argc(); 330 break; 331 case UHI_argnlen: 332 if (gpr[4] >= semihosting_get_argc()) { 333 gpr[2] = -1; 334 return; 335 } 336 gpr[2] = strlen(semihosting_get_arg(gpr[4])); 337 break; 338 case UHI_argn: 339 if (gpr[4] >= semihosting_get_argc()) { 340 gpr[2] = -1; 341 return; 342 } 343 gpr[2] = copy_argn_to_target(env, gpr[4], gpr[5]); 344 break; 345 case UHI_plog: 346 GET_TARGET_STRING(p, gpr[4]); 347 p2 = strstr(p, "%d"); 348 if (p2) { 349 int char_num = p2 - p; 350 GString *s = g_string_new_len(p, char_num); 351 g_string_append_printf(s, "%d%s", (int)gpr[5], p2 + 2); 352 gpr[2] = qemu_semihosting_log_out(s->str, s->len); 353 g_string_free(s, true); 354 } else { 355 gpr[2] = qemu_semihosting_log_out(p, strlen(p)); 356 } 357 FREE_TARGET_STRING(p, gpr[4]); 358 break; 359 case UHI_assert: 360 GET_TARGET_STRINGS_2(p, gpr[4], p2, gpr[5]); 361 printf("assertion '"); 362 printf("\"%s\"", p); 363 printf("': file \"%s\", line %d\n", p2, (int)gpr[6]); 364 FREE_TARGET_STRING(p2, gpr[5]); 365 FREE_TARGET_STRING(p, gpr[4]); 366 abort(); 367 break; 368 #ifndef _WIN32 369 case UHI_link: 370 GET_TARGET_STRINGS_2(p, gpr[4], p2, gpr[5]); 371 gpr[2] = link(p, p2); 372 gpr[3] = errno_mips(errno); 373 FREE_TARGET_STRING(p2, gpr[5]); 374 FREE_TARGET_STRING(p, gpr[4]); 375 break; 376 #endif 377 default: 378 fprintf(stderr, "Unknown UHI operation %d\n", op); 379 abort(); 380 } 381 return; 382 } 383