| /* |
| * 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 "dyngen-exec.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, |
| }; |
| |
| void HELPER(simcall)(CPUXtensaState *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) { |
| target_phys_addr_t paddr = |
| cpu_get_phys_page_debug(env, vaddr); |
| uint32_t page_left = |
| TARGET_PAGE_SIZE - (vaddr & (TARGET_PAGE_SIZE - 1)); |
| uint32_t io_sz = page_left < len ? page_left : len; |
| target_phys_addr_t 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; |
| cpu_physical_memory_unmap(buf, sz, is_write, sz); |
| if (regs[2] == -1) { |
| break; |
| } |
| } else { |
| regs[2] = -1; |
| regs[3] = 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( |
| env, 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; |
| } else { |
| regs[2] = -1; |
| regs[3] = EINVAL; |
| } |
| } |
| break; |
| |
| case TARGET_SYS_close: |
| if (regs[3] < 3) { |
| regs[2] = regs[3] = 0; |
| } else { |
| regs[2] = close(regs[3]); |
| regs[3] = errno; |
| } |
| break; |
| |
| case TARGET_SYS_lseek: |
| regs[2] = lseek(regs[3], (off_t)(int32_t)regs[4], regs[5]); |
| regs[3] = 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(env, 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; |
| } |
| 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( |
| env, 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) { |
| target_phys_addr_t 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]); |
| break; |
| } |
| } |