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/helper-proto.h" 24 #include "exec/softmmu-semi.h" 25 #include "semihosting/semihost.h" 26 #include "semihosting/console.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 /* Errno values taken from asm-mips/errno.h */ 78 static uint16_t host_to_mips_errno[] = { 79 [ENAMETOOLONG] = 78, 80 #ifdef EOVERFLOW 81 [EOVERFLOW] = 79, 82 #endif 83 #ifdef ELOOP 84 [ELOOP] = 90, 85 #endif 86 }; 87 88 static int errno_mips(int err) 89 { 90 if (err < 0 || err >= ARRAY_SIZE(host_to_mips_errno)) { 91 return EINVAL; 92 } else if (host_to_mips_errno[err]) { 93 return host_to_mips_errno[err]; 94 } else { 95 return err; 96 } 97 } 98 99 static int copy_stat_to_target(CPUMIPSState *env, const struct stat *src, 100 target_ulong vaddr) 101 { 102 hwaddr len = sizeof(struct UHIStat); 103 UHIStat *dst = lock_user(VERIFY_WRITE, vaddr, len, 0); 104 if (!dst) { 105 errno = EFAULT; 106 return -1; 107 } 108 109 dst->uhi_st_dev = tswap16(src->st_dev); 110 dst->uhi_st_ino = tswap16(src->st_ino); 111 dst->uhi_st_mode = tswap32(src->st_mode); 112 dst->uhi_st_nlink = tswap16(src->st_nlink); 113 dst->uhi_st_uid = tswap16(src->st_uid); 114 dst->uhi_st_gid = tswap16(src->st_gid); 115 dst->uhi_st_rdev = tswap16(src->st_rdev); 116 dst->uhi_st_size = tswap64(src->st_size); 117 dst->uhi_st_atime = tswap64(src->st_atime); 118 dst->uhi_st_mtime = tswap64(src->st_mtime); 119 dst->uhi_st_ctime = tswap64(src->st_ctime); 120 #ifdef _WIN32 121 dst->uhi_st_blksize = 0; 122 dst->uhi_st_blocks = 0; 123 #else 124 dst->uhi_st_blksize = tswap64(src->st_blksize); 125 dst->uhi_st_blocks = tswap64(src->st_blocks); 126 #endif 127 unlock_user(dst, vaddr, len); 128 return 0; 129 } 130 131 static int get_open_flags(target_ulong target_flags) 132 { 133 int open_flags = 0; 134 135 if (target_flags & UHIOpen_RDWR) { 136 open_flags |= O_RDWR; 137 } else if (target_flags & UHIOpen_WRONLY) { 138 open_flags |= O_WRONLY; 139 } else { 140 open_flags |= O_RDONLY; 141 } 142 143 open_flags |= (target_flags & UHIOpen_APPEND) ? O_APPEND : 0; 144 open_flags |= (target_flags & UHIOpen_CREAT) ? O_CREAT : 0; 145 open_flags |= (target_flags & UHIOpen_TRUNC) ? O_TRUNC : 0; 146 open_flags |= (target_flags & UHIOpen_EXCL) ? O_EXCL : 0; 147 148 return open_flags; 149 } 150 151 static int write_to_file(CPUMIPSState *env, target_ulong fd, target_ulong vaddr, 152 target_ulong len, target_ulong offset) 153 { 154 int num_of_bytes; 155 void *dst = lock_user(VERIFY_READ, vaddr, len, 1); 156 if (!dst) { 157 errno = EFAULT; 158 return -1; 159 } 160 161 if (offset) { 162 #ifdef _WIN32 163 num_of_bytes = 0; 164 #else 165 num_of_bytes = pwrite(fd, dst, len, offset); 166 #endif 167 } else { 168 num_of_bytes = write(fd, dst, len); 169 } 170 171 unlock_user(dst, vaddr, 0); 172 return num_of_bytes; 173 } 174 175 static int read_from_file(CPUMIPSState *env, target_ulong fd, 176 target_ulong vaddr, target_ulong len, 177 target_ulong offset) 178 { 179 int num_of_bytes; 180 void *dst = lock_user(VERIFY_WRITE, vaddr, len, 0); 181 if (!dst) { 182 errno = EFAULT; 183 return -1; 184 } 185 186 if (offset) { 187 #ifdef _WIN32 188 num_of_bytes = 0; 189 #else 190 num_of_bytes = pread(fd, dst, len, offset); 191 #endif 192 } else { 193 num_of_bytes = read(fd, dst, len); 194 } 195 196 unlock_user(dst, vaddr, len); 197 return num_of_bytes; 198 } 199 200 static int copy_argn_to_target(CPUMIPSState *env, int arg_num, 201 target_ulong vaddr) 202 { 203 int strsize = strlen(semihosting_get_arg(arg_num)) + 1; 204 char *dst = lock_user(VERIFY_WRITE, vaddr, strsize, 0); 205 if (!dst) { 206 return -1; 207 } 208 209 strcpy(dst, semihosting_get_arg(arg_num)); 210 211 unlock_user(dst, vaddr, strsize); 212 return 0; 213 } 214 215 #define GET_TARGET_STRING(p, addr) \ 216 do { \ 217 p = lock_user_string(addr); \ 218 if (!p) { \ 219 gpr[2] = -1; \ 220 gpr[3] = EFAULT; \ 221 return; \ 222 } \ 223 } while (0) 224 225 #define GET_TARGET_STRINGS_2(p, addr, p2, addr2) \ 226 do { \ 227 p = lock_user_string(addr); \ 228 if (!p) { \ 229 gpr[2] = -1; \ 230 gpr[3] = EFAULT; \ 231 return; \ 232 } \ 233 p2 = lock_user_string(addr2); \ 234 if (!p2) { \ 235 unlock_user(p, addr, 0); \ 236 gpr[2] = -1; \ 237 gpr[3] = EFAULT; \ 238 return; \ 239 } \ 240 } while (0) 241 242 #define FREE_TARGET_STRING(p, gpr) \ 243 do { \ 244 unlock_user(p, gpr, 0); \ 245 } while (0) 246 247 void helper_do_semihosting(CPUMIPSState *env) 248 { 249 target_ulong *gpr = env->active_tc.gpr; 250 const UHIOp op = gpr[25]; 251 char *p, *p2; 252 253 switch (op) { 254 case UHI_exit: 255 qemu_log("UHI(%d): exit(%d)\n", op, (int)gpr[4]); 256 exit(gpr[4]); 257 case UHI_open: 258 GET_TARGET_STRING(p, gpr[4]); 259 if (!strcmp("/dev/stdin", p)) { 260 gpr[2] = 0; 261 } else if (!strcmp("/dev/stdout", p)) { 262 gpr[2] = 1; 263 } else if (!strcmp("/dev/stderr", p)) { 264 gpr[2] = 2; 265 } else { 266 gpr[2] = open(p, get_open_flags(gpr[5]), gpr[6]); 267 gpr[3] = errno_mips(errno); 268 } 269 FREE_TARGET_STRING(p, gpr[4]); 270 break; 271 case UHI_close: 272 if (gpr[4] < 3) { 273 /* ignore closing stdin/stdout/stderr */ 274 gpr[2] = 0; 275 return; 276 } 277 gpr[2] = close(gpr[4]); 278 gpr[3] = errno_mips(errno); 279 break; 280 case UHI_read: 281 gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6], 0); 282 gpr[3] = errno_mips(errno); 283 break; 284 case UHI_write: 285 gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6], 0); 286 gpr[3] = errno_mips(errno); 287 break; 288 case UHI_lseek: 289 gpr[2] = lseek(gpr[4], gpr[5], gpr[6]); 290 gpr[3] = errno_mips(errno); 291 break; 292 case UHI_unlink: 293 GET_TARGET_STRING(p, gpr[4]); 294 gpr[2] = remove(p); 295 gpr[3] = errno_mips(errno); 296 FREE_TARGET_STRING(p, gpr[4]); 297 break; 298 case UHI_fstat: 299 { 300 struct stat sbuf; 301 memset(&sbuf, 0, sizeof(sbuf)); 302 gpr[2] = fstat(gpr[4], &sbuf); 303 gpr[3] = errno_mips(errno); 304 if (gpr[2]) { 305 return; 306 } 307 gpr[2] = copy_stat_to_target(env, &sbuf, gpr[5]); 308 gpr[3] = errno_mips(errno); 309 } 310 break; 311 case UHI_argc: 312 gpr[2] = semihosting_get_argc(); 313 break; 314 case UHI_argnlen: 315 if (gpr[4] >= semihosting_get_argc()) { 316 gpr[2] = -1; 317 return; 318 } 319 gpr[2] = strlen(semihosting_get_arg(gpr[4])); 320 break; 321 case UHI_argn: 322 if (gpr[4] >= semihosting_get_argc()) { 323 gpr[2] = -1; 324 return; 325 } 326 gpr[2] = copy_argn_to_target(env, gpr[4], gpr[5]); 327 break; 328 case UHI_plog: 329 GET_TARGET_STRING(p, gpr[4]); 330 p2 = strstr(p, "%d"); 331 if (p2) { 332 int char_num = p2 - p; 333 GString *s = g_string_new_len(p, char_num); 334 g_string_append_printf(s, "%d%s", (int)gpr[5], p2 + 2); 335 gpr[2] = qemu_semihosting_log_out(s->str, s->len); 336 g_string_free(s, true); 337 } else { 338 gpr[2] = qemu_semihosting_log_out(p, strlen(p)); 339 } 340 FREE_TARGET_STRING(p, gpr[4]); 341 break; 342 case UHI_assert: 343 GET_TARGET_STRINGS_2(p, gpr[4], p2, gpr[5]); 344 printf("assertion '"); 345 printf("\"%s\"", p); 346 printf("': file \"%s\", line %d\n", p2, (int)gpr[6]); 347 FREE_TARGET_STRING(p2, gpr[5]); 348 FREE_TARGET_STRING(p, gpr[4]); 349 abort(); 350 break; 351 case UHI_pread: 352 gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6], gpr[7]); 353 gpr[3] = errno_mips(errno); 354 break; 355 case UHI_pwrite: 356 gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6], gpr[7]); 357 gpr[3] = errno_mips(errno); 358 break; 359 #ifndef _WIN32 360 case UHI_link: 361 GET_TARGET_STRINGS_2(p, gpr[4], p2, gpr[5]); 362 gpr[2] = link(p, p2); 363 gpr[3] = errno_mips(errno); 364 FREE_TARGET_STRING(p2, gpr[5]); 365 FREE_TARGET_STRING(p, gpr[4]); 366 break; 367 #endif 368 default: 369 fprintf(stderr, "Unknown UHI operation %d\n", op); 370 abort(); 371 } 372 return; 373 } 374