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