1 /* 2 * Copyright (c) 2011, Max Filippov, Open Source and Linux Lab. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * * Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * * Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * * Neither the name of the Open Source and Linux Lab nor the 13 * names of its contributors may be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include "qemu/osdep.h" 29 #include "cpu.h" 30 #include "chardev/char-fe.h" 31 #include "exec/helper-proto.h" 32 #include "exec/target_page.h" 33 #include "semihosting/semihost.h" 34 #include "semihosting/uaccess.h" 35 #include "system/memory.h" 36 #include "qapi/error.h" 37 #include "qemu/log.h" 38 #include "qemu/plugin.h" 39 40 enum { 41 TARGET_SYS_exit = 1, 42 TARGET_SYS_read = 3, 43 TARGET_SYS_write = 4, 44 TARGET_SYS_open = 5, 45 TARGET_SYS_close = 6, 46 TARGET_SYS_lseek = 19, 47 TARGET_SYS_select_one = 29, 48 49 TARGET_SYS_argc = 1000, 50 TARGET_SYS_argv_sz = 1001, 51 TARGET_SYS_argv = 1002, 52 TARGET_SYS_memset = 1004, 53 }; 54 55 enum { 56 SELECT_ONE_READ = 1, 57 SELECT_ONE_WRITE = 2, 58 SELECT_ONE_EXCEPT = 3, 59 }; 60 61 enum { 62 TARGET_EPERM = 1, 63 TARGET_ENOENT = 2, 64 TARGET_ESRCH = 3, 65 TARGET_EINTR = 4, 66 TARGET_EIO = 5, 67 TARGET_ENXIO = 6, 68 TARGET_E2BIG = 7, 69 TARGET_ENOEXEC = 8, 70 TARGET_EBADF = 9, 71 TARGET_ECHILD = 10, 72 TARGET_EAGAIN = 11, 73 TARGET_ENOMEM = 12, 74 TARGET_EACCES = 13, 75 TARGET_EFAULT = 14, 76 TARGET_ENOTBLK = 15, 77 TARGET_EBUSY = 16, 78 TARGET_EEXIST = 17, 79 TARGET_EXDEV = 18, 80 TARGET_ENODEV = 19, 81 TARGET_ENOTDIR = 20, 82 TARGET_EISDIR = 21, 83 TARGET_EINVAL = 22, 84 TARGET_ENFILE = 23, 85 TARGET_EMFILE = 24, 86 TARGET_ENOTTY = 25, 87 TARGET_ETXTBSY = 26, 88 TARGET_EFBIG = 27, 89 TARGET_ENOSPC = 28, 90 TARGET_ESPIPE = 29, 91 TARGET_EROFS = 30, 92 TARGET_EMLINK = 31, 93 TARGET_EPIPE = 32, 94 TARGET_EDOM = 33, 95 TARGET_ERANGE = 34, 96 TARGET_ENOSYS = 88, 97 TARGET_ELOOP = 92, 98 }; 99 100 static uint32_t errno_h2g(int host_errno) 101 { 102 switch (host_errno) { 103 case 0: return 0; 104 case EPERM: return TARGET_EPERM; 105 case ENOENT: return TARGET_ENOENT; 106 case ESRCH: return TARGET_ESRCH; 107 case EINTR: return TARGET_EINTR; 108 case EIO: return TARGET_EIO; 109 case ENXIO: return TARGET_ENXIO; 110 case E2BIG: return TARGET_E2BIG; 111 case ENOEXEC: return TARGET_ENOEXEC; 112 case EBADF: return TARGET_EBADF; 113 case ECHILD: return TARGET_ECHILD; 114 case EAGAIN: return TARGET_EAGAIN; 115 case ENOMEM: return TARGET_ENOMEM; 116 case EACCES: return TARGET_EACCES; 117 case EFAULT: return TARGET_EFAULT; 118 #ifdef ENOTBLK 119 case ENOTBLK: return TARGET_ENOTBLK; 120 #endif 121 case EBUSY: return TARGET_EBUSY; 122 case EEXIST: return TARGET_EEXIST; 123 case EXDEV: return TARGET_EXDEV; 124 case ENODEV: return TARGET_ENODEV; 125 case ENOTDIR: return TARGET_ENOTDIR; 126 case EISDIR: return TARGET_EISDIR; 127 case EINVAL: return TARGET_EINVAL; 128 case ENFILE: return TARGET_ENFILE; 129 case EMFILE: return TARGET_EMFILE; 130 case ENOTTY: return TARGET_ENOTTY; 131 #ifdef ETXTBSY 132 case ETXTBSY: return TARGET_ETXTBSY; 133 #endif 134 case EFBIG: return TARGET_EFBIG; 135 case ENOSPC: return TARGET_ENOSPC; 136 case ESPIPE: return TARGET_ESPIPE; 137 case EROFS: return TARGET_EROFS; 138 case EMLINK: return TARGET_EMLINK; 139 case EPIPE: return TARGET_EPIPE; 140 case EDOM: return TARGET_EDOM; 141 case ERANGE: return TARGET_ERANGE; 142 case ENOSYS: return TARGET_ENOSYS; 143 #ifdef ELOOP 144 case ELOOP: return TARGET_ELOOP; 145 #endif 146 }; 147 148 return TARGET_EINVAL; 149 } 150 151 typedef struct XtensaSimConsole { 152 CharFrontend fe; 153 struct { 154 char buffer[16]; 155 size_t offset; 156 } input; 157 } XtensaSimConsole; 158 159 static XtensaSimConsole *sim_console; 160 161 static IOCanReadHandler sim_console_can_read; 162 static int sim_console_can_read(void *opaque) 163 { 164 XtensaSimConsole *p = opaque; 165 166 return sizeof(p->input.buffer) - p->input.offset; 167 } 168 169 static IOReadHandler sim_console_read; 170 static void sim_console_read(void *opaque, const uint8_t *buf, int size) 171 { 172 XtensaSimConsole *p = opaque; 173 size_t copy = sizeof(p->input.buffer) - p->input.offset; 174 175 if (size < copy) { 176 copy = size; 177 } 178 memcpy(p->input.buffer + p->input.offset, buf, copy); 179 p->input.offset += copy; 180 } 181 182 void xtensa_sim_open_console(Chardev *chr) 183 { 184 static XtensaSimConsole console; 185 186 qemu_chr_fe_init(&console.fe, chr, &error_abort); 187 qemu_chr_fe_set_handlers(&console.fe, 188 sim_console_can_read, 189 sim_console_read, 190 NULL, NULL, &console, 191 NULL, true); 192 sim_console = &console; 193 } 194 195 void HELPER(simcall)(CPUXtensaState *env) 196 { 197 const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; 198 CPUState *cs = env_cpu(env); 199 AddressSpace *as = cs->as; 200 uint32_t *regs = env->regs; 201 uint64_t last_pc = env->pc; 202 203 switch (regs[2]) { 204 case TARGET_SYS_exit: 205 exit(regs[3]); 206 break; 207 208 case TARGET_SYS_read: 209 case TARGET_SYS_write: 210 { 211 bool is_write = regs[2] == TARGET_SYS_write; 212 uint32_t fd = regs[3]; 213 uint32_t vaddr = regs[4]; 214 uint32_t len = regs[5]; 215 uint32_t len_done = 0; 216 217 while (len > 0) { 218 hwaddr paddr = cpu_get_phys_page_debug(cs, vaddr); 219 uint32_t page_left = 220 TARGET_PAGE_SIZE - (vaddr & (TARGET_PAGE_SIZE - 1)); 221 uint32_t io_sz = page_left < len ? page_left : len; 222 hwaddr sz = io_sz; 223 void *buf = address_space_map(as, paddr, &sz, !is_write, attrs); 224 uint32_t io_done; 225 bool error = false; 226 227 if (buf) { 228 vaddr += io_sz; 229 len -= io_sz; 230 if (fd < 3 && sim_console) { 231 if (is_write && (fd == 1 || fd == 2)) { 232 io_done = qemu_chr_fe_write_all(&sim_console->fe, 233 buf, io_sz); 234 regs[3] = errno_h2g(errno); 235 } else if (!is_write && fd == 0) { 236 if (sim_console->input.offset) { 237 io_done = sim_console->input.offset; 238 if (io_sz < io_done) { 239 io_done = io_sz; 240 } 241 memcpy(buf, sim_console->input.buffer, io_done); 242 memmove(sim_console->input.buffer, 243 sim_console->input.buffer + io_done, 244 sim_console->input.offset - io_done); 245 sim_console->input.offset -= io_done; 246 qemu_chr_fe_accept_input(&sim_console->fe); 247 } else { 248 io_done = -1; 249 regs[3] = TARGET_EAGAIN; 250 } 251 } else { 252 qemu_log_mask(LOG_GUEST_ERROR, 253 "%s fd %d is not supported with chardev console\n", 254 is_write ? 255 "writing to" : "reading from", fd); 256 io_done = -1; 257 regs[3] = TARGET_EBADF; 258 } 259 } else { 260 io_done = is_write ? 261 write(fd, buf, io_sz) : 262 read(fd, buf, io_sz); 263 regs[3] = errno_h2g(errno); 264 } 265 if (io_done == -1) { 266 error = true; 267 io_done = 0; 268 } 269 address_space_unmap(as, buf, sz, !is_write, io_done); 270 } else { 271 error = true; 272 regs[3] = TARGET_EINVAL; 273 break; 274 } 275 if (error) { 276 if (!len_done) { 277 len_done = -1; 278 } 279 break; 280 } 281 len_done += io_done; 282 if (io_done < io_sz) { 283 break; 284 } 285 } 286 regs[2] = len_done; 287 } 288 break; 289 290 case TARGET_SYS_open: 291 { 292 char name[1024]; 293 int rc; 294 int i; 295 296 for (i = 0; i < ARRAY_SIZE(name); ++i) { 297 rc = cpu_memory_rw_debug(cs, regs[3] + i, 298 (uint8_t *)name + i, 1, 0); 299 if (rc != 0 || name[i] == 0) { 300 break; 301 } 302 } 303 304 if (rc == 0 && i < ARRAY_SIZE(name)) { 305 regs[2] = open(name, regs[4], regs[5]); 306 regs[3] = errno_h2g(errno); 307 } else { 308 regs[2] = -1; 309 regs[3] = TARGET_EINVAL; 310 } 311 } 312 break; 313 314 case TARGET_SYS_close: 315 if (regs[3] < 3) { 316 regs[2] = regs[3] = 0; 317 } else { 318 regs[2] = close(regs[3]); 319 regs[3] = errno_h2g(errno); 320 } 321 break; 322 323 case TARGET_SYS_lseek: 324 regs[2] = lseek(regs[3], (off_t)(int32_t)regs[4], regs[5]); 325 regs[3] = errno_h2g(errno); 326 break; 327 328 case TARGET_SYS_select_one: 329 { 330 uint32_t fd = regs[3]; 331 uint32_t rq = regs[4]; 332 uint32_t target_tv = regs[5]; 333 334 struct timeval tv = {0}; 335 336 if (target_tv) { 337 get_user_u32(tv.tv_sec, target_tv); 338 get_user_u32(tv.tv_sec, target_tv + 4); 339 } 340 if (fd < 3 && sim_console) { 341 if ((fd == 1 || fd == 2) && rq == SELECT_ONE_WRITE) { 342 regs[2] = 1; 343 } else if (fd == 0 && rq == SELECT_ONE_READ) { 344 regs[2] = sim_console->input.offset > 0; 345 } else { 346 regs[2] = 0; 347 } 348 regs[3] = 0; 349 } else { 350 fd_set fdset; 351 352 FD_ZERO(&fdset); 353 FD_SET(fd, &fdset); 354 regs[2] = select(fd + 1, 355 rq == SELECT_ONE_READ ? &fdset : NULL, 356 rq == SELECT_ONE_WRITE ? &fdset : NULL, 357 rq == SELECT_ONE_EXCEPT ? &fdset : NULL, 358 target_tv ? &tv : NULL); 359 regs[3] = errno_h2g(errno); 360 } 361 } 362 break; 363 364 case TARGET_SYS_argc: 365 regs[2] = semihosting_get_argc(); 366 regs[3] = 0; 367 break; 368 369 case TARGET_SYS_argv_sz: 370 { 371 int argc = semihosting_get_argc(); 372 int sz = (argc + 1) * sizeof(uint32_t); 373 int i; 374 375 for (i = 0; i < argc; ++i) { 376 sz += 1 + strlen(semihosting_get_arg(i)); 377 } 378 regs[2] = sz; 379 regs[3] = 0; 380 } 381 break; 382 383 case TARGET_SYS_argv: 384 { 385 int argc = semihosting_get_argc(); 386 int str_offset = (argc + 1) * sizeof(uint32_t); 387 int i; 388 uint32_t argptr; 389 390 for (i = 0; i < argc; ++i) { 391 const char *str = semihosting_get_arg(i); 392 int str_size = strlen(str) + 1; 393 394 put_user_u32(regs[3] + str_offset, 395 regs[3] + i * sizeof(uint32_t)); 396 cpu_memory_rw_debug(cs, 397 regs[3] + str_offset, 398 (uint8_t *)str, str_size, 1); 399 str_offset += str_size; 400 } 401 argptr = 0; 402 cpu_memory_rw_debug(cs, 403 regs[3] + i * sizeof(uint32_t), 404 (uint8_t *)&argptr, sizeof(argptr), 1); 405 regs[3] = 0; 406 } 407 break; 408 409 case TARGET_SYS_memset: 410 { 411 uint32_t base = regs[3]; 412 uint32_t sz = regs[5]; 413 414 while (sz) { 415 hwaddr len = sz; 416 void *buf = address_space_map(as, base, &len, true, attrs); 417 418 if (buf && len) { 419 memset(buf, regs[4], len); 420 address_space_unmap(as, buf, len, true, len); 421 } else { 422 len = 1; 423 } 424 base += len; 425 sz -= len; 426 } 427 regs[2] = regs[3]; 428 regs[3] = 0; 429 } 430 break; 431 432 default: 433 qemu_log_mask(LOG_GUEST_ERROR, "%s(%d): not implemented\n", __func__, regs[2]); 434 regs[2] = -1; 435 regs[3] = TARGET_ENOSYS; 436 break; 437 } 438 qemu_plugin_vcpu_hostcall_cb(cs, last_pc); 439 } 440