/*
* Arm "Angel" semihosting syscalls
- *
+ *
* Copyright (c) 2005, 2007 CodeSourcery.
* Written by Paul Brook.
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <sys/types.h>
#define ARM_ANGEL_HEAP_SIZE (128 * 1024 * 1024)
#else
-#include "vl.h"
+#include "qemu-common.h"
+#include "gdbstub.h"
#endif
#define SYS_OPEN 0x01
return code;
}
-static uint32_t softmmu_tget32(CPUState *env, uint32_t addr)
-{
- uint32_t val;
-
- cpu_memory_rw_debug(env, addr, (uint8_t *)&val, 4, 0);
- return tswap32(val);
-}
-static uint32_t softmmu_tget8(CPUState *env, uint32_t addr)
-{
- uint8_t val;
-
- cpu_memory_rw_debug(env, addr, &val, 1, 0);
- return val;
-}
-#define tget32(p) softmmu_tget32(env, p)
-#define tget8(p) softmmu_tget8(env, p)
-
-static void *softmmu_lock_user(CPUState *env, uint32_t addr, uint32_t len,
- int copy)
-{
- char *p;
- /* TODO: Make this something that isn't fixed size. */
- p = malloc(len);
- if (copy)
- cpu_memory_rw_debug(env, addr, p, len, 0);
- return p;
-}
-#define lock_user(p, len, copy) softmmu_lock_user(env, p, len, copy)
-static char *softmmu_lock_user_string(CPUState *env, uint32_t addr)
-{
- char *p;
- char *s;
- uint8_t c;
- /* TODO: Make this something that isn't fixed size. */
- s = p = malloc(1024);
- do {
- cpu_memory_rw_debug(env, addr, &c, 1, 0);
- addr++;
- *(p++) = c;
- } while (c);
- return s;
-}
-#define lock_user_string(p) softmmu_lock_user_string(env, p)
-static void softmmu_unlock_user(CPUState *env, void *p, target_ulong addr,
- target_ulong len)
-{
- if (len)
- cpu_memory_rw_debug(env, addr, p, len, 1);
- free(p);
-}
-#define unlock_user(s, args, len) softmmu_unlock_user(env, s, args, len)
+#include "softmmu-semi.h"
#endif
static target_ulong arm_semi_syscall_len;
+#if !defined(CONFIG_USER_ONLY)
+static target_ulong syscall_err;
+#endif
+
static void arm_semi_cb(CPUState *env, target_ulong ret, target_ulong err)
{
#ifdef CONFIG_USER_ONLY
TaskState *ts = env->opaque;
#endif
+
if (ret == (target_ulong)-1) {
#ifdef CONFIG_USER_ONLY
ts->swi_errno = err;
+#else
+ syscall_err = err;
#endif
env->regs[0] = ret;
} else {
}
}
-#define ARG(n) tget32(args + (n) * 4)
-#define SET_ARG(n, val) tput32(args + (n) * 4,val)
+static void arm_semi_flen_cb(CPUState *env, target_ulong ret, target_ulong err)
+{
+ /* The size is always stored in big-endian order, extract
+ the value. We assume the size always fit in 32 bits. */
+ uint32_t size;
+ cpu_memory_rw_debug(env, env->regs[13]-64+32, (uint8_t *)&size, 4, 0);
+ env->regs[0] = be32_to_cpu(size);
+#ifdef CONFIG_USER_ONLY
+ ((TaskState *)env->opaque)->swi_errno = err;
+#else
+ syscall_err = err;
+#endif
+}
+
+#define ARG(n) \
+({ \
+ target_ulong __arg; \
+ /* FIXME - handle get_user() failure */ \
+ get_user_ual(__arg, args + (n) * 4); \
+ __arg; \
+})
+#define SET_ARG(n, val) put_user_ual(val, args + (n) * 4)
uint32_t do_arm_semihosting(CPUState *env)
{
target_ulong args;
args = env->regs[1];
switch (nr) {
case SYS_OPEN:
- s = lock_user_string(ARG(0));
+ if (!(s = lock_user_string(ARG(0))))
+ /* FIXME - should this error code be -TARGET_EFAULT ? */
+ return (uint32_t)-1;
if (ARG(1) >= 12)
- return (uint32_t)-1;
+ return (uint32_t)-1;
if (strcmp(s, ":tt") == 0) {
if (ARG(1) < 4)
return STDIN_FILENO;
return STDOUT_FILENO;
}
if (use_gdb_syscalls()) {
- gdb_do_syscall(arm_semi_cb, "open,%s,%x,1a4", ARG(0), (int)ARG(2),
- gdb_open_modeflags[ARG(1)]);
+ gdb_do_syscall(arm_semi_cb, "open,%s,%x,1a4", ARG(0),
+ (int)ARG(2)+1, gdb_open_modeflags[ARG(1)]);
return env->regs[0];
} else {
ret = set_swi_errno(ts, open(s, open_modeflags[ARG(1)], 0644));
}
case SYS_WRITEC:
{
- char c = tget8(args);
+ char c;
+
+ if (get_user_u8(c, args))
+ /* FIXME - should this error code be -TARGET_EFAULT ? */
+ return (uint32_t)-1;
/* Write to debug console. stderr is near enough. */
if (use_gdb_syscalls()) {
gdb_do_syscall(arm_semi_cb, "write,2,%x,1", args);
}
}
case SYS_WRITE0:
- s = lock_user_string(args);
+ if (!(s = lock_user_string(args)))
+ /* FIXME - should this error code be -TARGET_EFAULT ? */
+ return (uint32_t)-1;
len = strlen(s);
if (use_gdb_syscalls()) {
gdb_do_syscall(arm_semi_cb, "write,2,%x,%x\n", args, len);
gdb_do_syscall(arm_semi_cb, "write,%x,%x,%x", ARG(0), ARG(1), len);
return env->regs[0];
} else {
- s = lock_user(ARG(1), len, 1);
+ if (!(s = lock_user(VERIFY_READ, ARG(1), len, 1)))
+ /* FIXME - should this error code be -TARGET_EFAULT ? */
+ return (uint32_t)-1;
ret = set_swi_errno(ts, write(ARG(0), s, len));
unlock_user(s, ARG(1), 0);
if (ret == (uint32_t)-1)
gdb_do_syscall(arm_semi_cb, "read,%x,%x,%x", ARG(0), ARG(1), len);
return env->regs[0];
} else {
- s = lock_user(ARG(1), len, 0);
+ if (!(s = lock_user(VERIFY_WRITE, ARG(1), len, 0)))
+ /* FIXME - should this error code be -TARGET_EFAULT ? */
+ return (uint32_t)-1;
do
ret = set_swi_errno(ts, read(ARG(0), s, len));
while (ret == -1 && errno == EINTR);
}
case SYS_SEEK:
if (use_gdb_syscalls()) {
- gdb_do_syscall(arm_semi_cb, "fseek,%x,%x,0", ARG(0), ARG(1));
+ gdb_do_syscall(arm_semi_cb, "lseek,%x,%x,0", ARG(0), ARG(1));
return env->regs[0];
} else {
ret = set_swi_errno(ts, lseek(ARG(0), ARG(1), SEEK_SET));
}
case SYS_FLEN:
if (use_gdb_syscalls()) {
- /* TODO: Use stat syscall. */
- return -1;
+ gdb_do_syscall(arm_semi_flen_cb, "fstat,%x,%x",
+ ARG(0), env->regs[13]-64);
+ return env->regs[0];
} else {
struct stat buf;
ret = set_swi_errno(ts, fstat(ARG(0), &buf));
return -1;
case SYS_REMOVE:
if (use_gdb_syscalls()) {
- gdb_do_syscall(arm_semi_cb, "unlink,%s", ARG(0), (int)ARG(1));
+ gdb_do_syscall(arm_semi_cb, "unlink,%s", ARG(0), (int)ARG(1)+1);
ret = env->regs[0];
} else {
- s = lock_user_string(ARG(0));
+ if (!(s = lock_user_string(ARG(0))))
+ /* FIXME - should this error code be -TARGET_EFAULT ? */
+ return (uint32_t)-1;
ret = set_swi_errno(ts, remove(s));
unlock_user(s, ARG(0), 0);
}
case SYS_RENAME:
if (use_gdb_syscalls()) {
gdb_do_syscall(arm_semi_cb, "rename,%s,%s",
- ARG(0), (int)ARG(1), ARG(2), (int)ARG(3));
+ ARG(0), (int)ARG(1)+1, ARG(2), (int)ARG(3)+1);
return env->regs[0];
} else {
char *s2;
s = lock_user_string(ARG(0));
s2 = lock_user_string(ARG(2));
- ret = set_swi_errno(ts, rename(s, s2));
- unlock_user(s2, ARG(2), 0);
- unlock_user(s, ARG(0), 0);
+ if (!s || !s2)
+ /* FIXME - should this error code be -TARGET_EFAULT ? */
+ ret = (uint32_t)-1;
+ else
+ ret = set_swi_errno(ts, rename(s, s2));
+ if (s2)
+ unlock_user(s2, ARG(2), 0);
+ if (s)
+ unlock_user(s, ARG(0), 0);
return ret;
}
case SYS_CLOCK:
return set_swi_errno(ts, time(NULL));
case SYS_SYSTEM:
if (use_gdb_syscalls()) {
- gdb_do_syscall(arm_semi_cb, "system,%s", ARG(0), (int)ARG(1));
+ gdb_do_syscall(arm_semi_cb, "system,%s", ARG(0), (int)ARG(1)+1);
return env->regs[0];
} else {
- s = lock_user_string(ARG(0));
+ if (!(s = lock_user_string(ARG(0))))
+ /* FIXME - should this error code be -TARGET_EFAULT ? */
+ return (uint32_t)-1;
ret = set_swi_errno(ts, system(s));
unlock_user(s, ARG(0), 0);
+ return ret;
}
case SYS_ERRNO:
#ifdef CONFIG_USER_ONLY
return ts->swi_errno;
#else
- return 0;
+ return syscall_err;
#endif
case SYS_GET_CMDLINE:
#ifdef CONFIG_USER_ONLY
/* Build a commandline from the original argv. */
{
- char **arg = ts->info->host_argv;
- int len = ARG(1);
- /* lock the buffer on the ARM side */
- char *cmdline_buffer = (char*)lock_user(ARG(0), len, 0);
+ char *arm_cmdline_buffer;
+ const char *host_cmdline_buffer;
- s = cmdline_buffer;
- while (*arg && len > 2) {
- int n = strlen(*arg);
+ unsigned int i;
+ unsigned int arm_cmdline_len = ARG(1);
+ unsigned int host_cmdline_len =
+ ts->info->arg_end-ts->info->arg_start;
- if (s != cmdline_buffer) {
- *(s++) = ' ';
- len--;
- }
- if (n >= len)
- n = len - 1;
- memcpy(s, *arg, n);
- s += n;
- len -= n;
- arg++;
+ if (!arm_cmdline_len || host_cmdline_len > arm_cmdline_len) {
+ return -1; /* not enough space to store command line */
}
- /* Null terminate the string. */
- *s = 0;
- len = s - cmdline_buffer;
- /* Unlock the buffer on the ARM side. */
- unlock_user(cmdline_buffer, ARG(0), len);
+ if (!host_cmdline_len) {
+ /* We special-case the "empty command line" case (argc==0).
+ Just provide the terminating 0. */
+ arm_cmdline_buffer = lock_user(VERIFY_WRITE, ARG(0), 1, 0);
+ arm_cmdline_buffer[0] = 0;
+ unlock_user(arm_cmdline_buffer, ARG(0), 1);
- /* Adjust the commandline length argument. */
- SET_ARG(1, len);
+ /* Adjust the commandline length argument. */
+ SET_ARG(1, 0);
+ return 0;
+ }
+
+ /* lock the buffers on the ARM side */
+ arm_cmdline_buffer =
+ lock_user(VERIFY_WRITE, ARG(0), host_cmdline_len, 0);
+ host_cmdline_buffer =
+ lock_user(VERIFY_READ, ts->info->arg_start,
+ host_cmdline_len, 1);
+
+ if (arm_cmdline_buffer && host_cmdline_buffer)
+ {
+ /* the last argument is zero-terminated;
+ no need for additional termination */
+ memcpy(arm_cmdline_buffer, host_cmdline_buffer,
+ host_cmdline_len);
+
+ /* separate arguments by white spaces */
+ for (i = 0; i < host_cmdline_len-1; i++) {
+ if (arm_cmdline_buffer[i] == 0) {
+ arm_cmdline_buffer[i] = ' ';
+ }
+ }
- /* Return success if commandline fit into buffer. */
- return *arg ? -1 : 0;
+ /* Adjust the commandline length argument. */
+ SET_ARG(1, host_cmdline_len-1);
+ }
+
+ /* Unlock the buffers on the ARM side. */
+ unlock_user(arm_cmdline_buffer, ARG(0), host_cmdline_len);
+ unlock_user((void*)host_cmdline_buffer, ts->info->arg_start, 0);
+
+ /* Return success if we could return a commandline. */
+ return (arm_cmdline_buffer && host_cmdline_buffer) ? 0 : -1;
}
#else
- return -1;
+ return -1;
#endif
case SYS_HEAPINFO:
{
}
ts->heap_limit = limit;
}
-
- ptr = lock_user(ARG(0), 16, 0);
+
+ if (!(ptr = lock_user(VERIFY_WRITE, ARG(0), 16, 0)))
+ /* FIXME - should this error code be -TARGET_EFAULT ? */
+ return (uint32_t)-1;
ptr[0] = tswap32(ts->heap_base);
ptr[1] = tswap32(ts->heap_limit);
ptr[2] = tswap32(ts->stack_base);
unlock_user(ptr, ARG(0), 16);
#else
limit = ram_size;
- ptr = lock_user(ARG(0), 16, 0);
+ if (!(ptr = lock_user(VERIFY_WRITE, ARG(0), 16, 0)))
+ /* FIXME - should this error code be -TARGET_EFAULT ? */
+ return (uint32_t)-1;
/* TODO: Make this use the limit of the loaded application. */
ptr[0] = tswap32(limit / 2);
ptr[1] = tswap32(limit);
return 0;
}
case SYS_EXIT:
+ gdb_exit(env, 0);
exit(0);
default:
fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr);