|  | /* | 
|  | * Copyright (c) 2011, Max Filippov, Open Source and Linux Lab. | 
|  | * All rights reserved. | 
|  | * | 
|  | * Redistribution and use in source and binary forms, with or without | 
|  | * modification, are permitted provided that the following conditions are met: | 
|  | *     * Redistributions of source code must retain the above copyright | 
|  | *       notice, this list of conditions and the following disclaimer. | 
|  | *     * Redistributions in binary form must reproduce the above copyright | 
|  | *       notice, this list of conditions and the following disclaimer in the | 
|  | *       documentation and/or other materials provided with the distribution. | 
|  | *     * Neither the name of the Open Source and Linux Lab nor the | 
|  | *       names of its contributors may be used to endorse or promote products | 
|  | *       derived from this software without specific prior written permission. | 
|  | * | 
|  | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | 
|  | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
|  | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 
|  | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | 
|  | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | 
|  | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | 
|  | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | 
|  | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
|  | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | 
|  | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  | */ | 
|  |  | 
|  | #include <errno.h> | 
|  | #include <unistd.h> | 
|  | #include <string.h> | 
|  | #include <stddef.h> | 
|  | #include "cpu.h" | 
|  | #include "helper.h" | 
|  | #include "qemu/log.h" | 
|  |  | 
|  | enum { | 
|  | TARGET_SYS_exit = 1, | 
|  | TARGET_SYS_read = 3, | 
|  | TARGET_SYS_write = 4, | 
|  | TARGET_SYS_open = 5, | 
|  | TARGET_SYS_close = 6, | 
|  | TARGET_SYS_lseek = 19, | 
|  | TARGET_SYS_select_one = 29, | 
|  |  | 
|  | TARGET_SYS_argc = 1000, | 
|  | TARGET_SYS_argv_sz = 1001, | 
|  | TARGET_SYS_argv = 1002, | 
|  | TARGET_SYS_memset = 1004, | 
|  | }; | 
|  |  | 
|  | enum { | 
|  | SELECT_ONE_READ   = 1, | 
|  | SELECT_ONE_WRITE  = 2, | 
|  | SELECT_ONE_EXCEPT = 3, | 
|  | }; | 
|  |  | 
|  | enum { | 
|  | TARGET_EPERM        =  1, | 
|  | TARGET_ENOENT       =  2, | 
|  | TARGET_ESRCH        =  3, | 
|  | TARGET_EINTR        =  4, | 
|  | TARGET_EIO          =  5, | 
|  | TARGET_ENXIO        =  6, | 
|  | TARGET_E2BIG        =  7, | 
|  | TARGET_ENOEXEC      =  8, | 
|  | TARGET_EBADF        =  9, | 
|  | TARGET_ECHILD       = 10, | 
|  | TARGET_EAGAIN       = 11, | 
|  | TARGET_ENOMEM       = 12, | 
|  | TARGET_EACCES       = 13, | 
|  | TARGET_EFAULT       = 14, | 
|  | TARGET_ENOTBLK      = 15, | 
|  | TARGET_EBUSY        = 16, | 
|  | TARGET_EEXIST       = 17, | 
|  | TARGET_EXDEV        = 18, | 
|  | TARGET_ENODEV       = 19, | 
|  | TARGET_ENOTDIR      = 20, | 
|  | TARGET_EISDIR       = 21, | 
|  | TARGET_EINVAL       = 22, | 
|  | TARGET_ENFILE       = 23, | 
|  | TARGET_EMFILE       = 24, | 
|  | TARGET_ENOTTY       = 25, | 
|  | TARGET_ETXTBSY      = 26, | 
|  | TARGET_EFBIG        = 27, | 
|  | TARGET_ENOSPC       = 28, | 
|  | TARGET_ESPIPE       = 29, | 
|  | TARGET_EROFS        = 30, | 
|  | TARGET_EMLINK       = 31, | 
|  | TARGET_EPIPE        = 32, | 
|  | TARGET_EDOM         = 33, | 
|  | TARGET_ERANGE       = 34, | 
|  | TARGET_ENOSYS       = 88, | 
|  | TARGET_ELOOP        = 92, | 
|  | }; | 
|  |  | 
|  | static uint32_t errno_h2g(int host_errno) | 
|  | { | 
|  | static const uint32_t guest_errno[] = { | 
|  | [EPERM]         = TARGET_EPERM, | 
|  | [ENOENT]        = TARGET_ENOENT, | 
|  | [ESRCH]         = TARGET_ESRCH, | 
|  | [EINTR]         = TARGET_EINTR, | 
|  | [EIO]           = TARGET_EIO, | 
|  | [ENXIO]         = TARGET_ENXIO, | 
|  | [E2BIG]         = TARGET_E2BIG, | 
|  | [ENOEXEC]       = TARGET_ENOEXEC, | 
|  | [EBADF]         = TARGET_EBADF, | 
|  | [ECHILD]        = TARGET_ECHILD, | 
|  | [EAGAIN]        = TARGET_EAGAIN, | 
|  | [ENOMEM]        = TARGET_ENOMEM, | 
|  | [EACCES]        = TARGET_EACCES, | 
|  | [EFAULT]        = TARGET_EFAULT, | 
|  | #ifdef ENOTBLK | 
|  | [ENOTBLK]       = TARGET_ENOTBLK, | 
|  | #endif | 
|  | [EBUSY]         = TARGET_EBUSY, | 
|  | [EEXIST]        = TARGET_EEXIST, | 
|  | [EXDEV]         = TARGET_EXDEV, | 
|  | [ENODEV]        = TARGET_ENODEV, | 
|  | [ENOTDIR]       = TARGET_ENOTDIR, | 
|  | [EISDIR]        = TARGET_EISDIR, | 
|  | [EINVAL]        = TARGET_EINVAL, | 
|  | [ENFILE]        = TARGET_ENFILE, | 
|  | [EMFILE]        = TARGET_EMFILE, | 
|  | [ENOTTY]        = TARGET_ENOTTY, | 
|  | #ifdef ETXTBSY | 
|  | [ETXTBSY]       = TARGET_ETXTBSY, | 
|  | #endif | 
|  | [EFBIG]         = TARGET_EFBIG, | 
|  | [ENOSPC]        = TARGET_ENOSPC, | 
|  | [ESPIPE]        = TARGET_ESPIPE, | 
|  | [EROFS]         = TARGET_EROFS, | 
|  | [EMLINK]        = TARGET_EMLINK, | 
|  | [EPIPE]         = TARGET_EPIPE, | 
|  | [EDOM]          = TARGET_EDOM, | 
|  | [ERANGE]        = TARGET_ERANGE, | 
|  | [ENOSYS]        = TARGET_ENOSYS, | 
|  | #ifdef ELOOP | 
|  | [ELOOP]         = TARGET_ELOOP, | 
|  | #endif | 
|  | }; | 
|  |  | 
|  | if (host_errno == 0) { | 
|  | return 0; | 
|  | } else if (host_errno > 0 && host_errno < ARRAY_SIZE(guest_errno) && | 
|  | guest_errno[host_errno]) { | 
|  | return guest_errno[host_errno]; | 
|  | } else { | 
|  | return TARGET_EINVAL; | 
|  | } | 
|  | } | 
|  |  | 
|  | void HELPER(simcall)(CPUXtensaState *env) | 
|  | { | 
|  | CPUState *cs = CPU(xtensa_env_get_cpu(env)); | 
|  | uint32_t *regs = env->regs; | 
|  |  | 
|  | switch (regs[2]) { | 
|  | case TARGET_SYS_exit: | 
|  | qemu_log("exit(%d) simcall\n", regs[3]); | 
|  | exit(regs[3]); | 
|  | break; | 
|  |  | 
|  | case TARGET_SYS_read: | 
|  | case TARGET_SYS_write: | 
|  | { | 
|  | bool is_write = regs[2] == TARGET_SYS_write; | 
|  | uint32_t fd = regs[3]; | 
|  | uint32_t vaddr = regs[4]; | 
|  | uint32_t len = regs[5]; | 
|  |  | 
|  | while (len > 0) { | 
|  | hwaddr paddr = cpu_get_phys_page_debug(cs, vaddr); | 
|  | uint32_t page_left = | 
|  | TARGET_PAGE_SIZE - (vaddr & (TARGET_PAGE_SIZE - 1)); | 
|  | uint32_t io_sz = page_left < len ? page_left : len; | 
|  | hwaddr sz = io_sz; | 
|  | void *buf = cpu_physical_memory_map(paddr, &sz, is_write); | 
|  |  | 
|  | if (buf) { | 
|  | vaddr += io_sz; | 
|  | len -= io_sz; | 
|  | regs[2] = is_write ? | 
|  | write(fd, buf, io_sz) : | 
|  | read(fd, buf, io_sz); | 
|  | regs[3] = errno_h2g(errno); | 
|  | cpu_physical_memory_unmap(buf, sz, is_write, sz); | 
|  | if (regs[2] == -1) { | 
|  | break; | 
|  | } | 
|  | } else { | 
|  | regs[2] = -1; | 
|  | regs[3] = TARGET_EINVAL; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | break; | 
|  |  | 
|  | case TARGET_SYS_open: | 
|  | { | 
|  | char name[1024]; | 
|  | int rc; | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < ARRAY_SIZE(name); ++i) { | 
|  | rc = cpu_memory_rw_debug(cs, regs[3] + i, | 
|  | (uint8_t *)name + i, 1, 0); | 
|  | if (rc != 0 || name[i] == 0) { | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (rc == 0 && i < ARRAY_SIZE(name)) { | 
|  | regs[2] = open(name, regs[4], regs[5]); | 
|  | regs[3] = errno_h2g(errno); | 
|  | } else { | 
|  | regs[2] = -1; | 
|  | regs[3] = TARGET_EINVAL; | 
|  | } | 
|  | } | 
|  | break; | 
|  |  | 
|  | case TARGET_SYS_close: | 
|  | if (regs[3] < 3) { | 
|  | regs[2] = regs[3] = 0; | 
|  | } else { | 
|  | regs[2] = close(regs[3]); | 
|  | regs[3] = errno_h2g(errno); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case TARGET_SYS_lseek: | 
|  | regs[2] = lseek(regs[3], (off_t)(int32_t)regs[4], regs[5]); | 
|  | regs[3] = errno_h2g(errno); | 
|  | break; | 
|  |  | 
|  | case TARGET_SYS_select_one: | 
|  | { | 
|  | uint32_t fd = regs[3]; | 
|  | uint32_t rq = regs[4]; | 
|  | uint32_t target_tv = regs[5]; | 
|  | uint32_t target_tvv[2]; | 
|  |  | 
|  | struct timeval tv = {0}; | 
|  | fd_set fdset; | 
|  |  | 
|  | FD_ZERO(&fdset); | 
|  | FD_SET(fd, &fdset); | 
|  |  | 
|  | if (target_tv) { | 
|  | cpu_memory_rw_debug(cs, target_tv, | 
|  | (uint8_t *)target_tvv, sizeof(target_tvv), 0); | 
|  | tv.tv_sec = (int32_t)tswap32(target_tvv[0]); | 
|  | tv.tv_usec = (int32_t)tswap32(target_tvv[1]); | 
|  | } | 
|  | regs[2] = select(fd + 1, | 
|  | rq == SELECT_ONE_READ   ? &fdset : NULL, | 
|  | rq == SELECT_ONE_WRITE  ? &fdset : NULL, | 
|  | rq == SELECT_ONE_EXCEPT ? &fdset : NULL, | 
|  | target_tv ? &tv : NULL); | 
|  | regs[3] = errno_h2g(errno); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case TARGET_SYS_argc: | 
|  | regs[2] = 1; | 
|  | regs[3] = 0; | 
|  | break; | 
|  |  | 
|  | case TARGET_SYS_argv_sz: | 
|  | regs[2] = 128; | 
|  | regs[3] = 0; | 
|  | break; | 
|  |  | 
|  | case TARGET_SYS_argv: | 
|  | { | 
|  | struct Argv { | 
|  | uint32_t argptr[2]; | 
|  | char text[120]; | 
|  | } argv = { | 
|  | {0, 0}, | 
|  | "test" | 
|  | }; | 
|  |  | 
|  | argv.argptr[0] = tswap32(regs[3] + offsetof(struct Argv, text)); | 
|  | cpu_memory_rw_debug(cs, | 
|  | regs[3], (uint8_t *)&argv, sizeof(argv), 1); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case TARGET_SYS_memset: | 
|  | { | 
|  | uint32_t base = regs[3]; | 
|  | uint32_t sz = regs[5]; | 
|  |  | 
|  | while (sz) { | 
|  | hwaddr len = sz; | 
|  | void *buf = cpu_physical_memory_map(base, &len, 1); | 
|  |  | 
|  | if (buf && len) { | 
|  | memset(buf, regs[4], len); | 
|  | cpu_physical_memory_unmap(buf, len, 1, len); | 
|  | } else { | 
|  | len = 1; | 
|  | } | 
|  | base += len; | 
|  | sz -= len; | 
|  | } | 
|  | regs[2] = regs[3]; | 
|  | regs[3] = 0; | 
|  | } | 
|  | break; | 
|  |  | 
|  | default: | 
|  | qemu_log("%s(%d): not implemented\n", __func__, regs[2]); | 
|  | regs[2] = -1; | 
|  | regs[3] = TARGET_ENOSYS; | 
|  | break; | 
|  | } | 
|  | } |