target-lm32: add semihosting support
Intercept certain system calls if semihosting is enabled. This should behave like the GDB simulator. Signed-off-by: Michael Walle <michael@walle.cc>
This commit is contained in:
parent
a946ce8020
commit
f7bbcfb5c3
@ -3071,7 +3071,8 @@ STEXI
|
|||||||
Set OpenBIOS nvram @var{variable} to given @var{value} (PPC, SPARC only).
|
Set OpenBIOS nvram @var{variable} to given @var{value} (PPC, SPARC only).
|
||||||
ETEXI
|
ETEXI
|
||||||
DEF("semihosting", 0, QEMU_OPTION_semihosting,
|
DEF("semihosting", 0, QEMU_OPTION_semihosting,
|
||||||
"-semihosting semihosting mode\n", QEMU_ARCH_ARM | QEMU_ARCH_M68K | QEMU_ARCH_XTENSA)
|
"-semihosting semihosting mode\n",
|
||||||
|
QEMU_ARCH_ARM | QEMU_ARCH_M68K | QEMU_ARCH_XTENSA | QEMU_ARCH_LM32)
|
||||||
STEXI
|
STEXI
|
||||||
@item -semihosting
|
@item -semihosting
|
||||||
@findex -semihosting
|
@findex -semihosting
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
obj-y += translate.o op_helper.o helper.o cpu.o
|
obj-y += translate.o op_helper.o helper.o cpu.o
|
||||||
obj-y += gdbstub.o
|
obj-y += gdbstub.o
|
||||||
|
obj-y += lm32-semi.o
|
||||||
obj-$(CONFIG_SOFTMMU) += machine.o
|
obj-$(CONFIG_SOFTMMU) += machine.o
|
||||||
|
@ -26,6 +26,15 @@ first BSP which instantiate this model. A (32 bit) write to 0xfff0000
|
|||||||
causes a vm shutdown.
|
causes a vm shutdown.
|
||||||
|
|
||||||
|
|
||||||
|
Semihosting
|
||||||
|
-----------
|
||||||
|
Semihosting on this target is supported. Some system calls like read, write
|
||||||
|
and exit are executed on the host if semihosting is enabled. See
|
||||||
|
target/lm32-semi.c for all supported system calls. Emulation aware programs
|
||||||
|
can use this mechanism to shut down the virtual machine and print to the
|
||||||
|
host console. See the tcg tests for an example.
|
||||||
|
|
||||||
|
|
||||||
Special instructions
|
Special instructions
|
||||||
--------------------
|
--------------------
|
||||||
The translation recognizes one special instruction to halt the cpu:
|
The translation recognizes one special instruction to halt the cpu:
|
||||||
|
@ -217,6 +217,7 @@ void lm32_breakpoint_remove(CPULM32State *env, int index);
|
|||||||
void lm32_watchpoint_insert(CPULM32State *env, int index, target_ulong address,
|
void lm32_watchpoint_insert(CPULM32State *env, int index, target_ulong address,
|
||||||
lm32_wp_t wp_type);
|
lm32_wp_t wp_type);
|
||||||
void lm32_watchpoint_remove(CPULM32State *env, int index);
|
void lm32_watchpoint_remove(CPULM32State *env, int index);
|
||||||
|
bool lm32_cpu_do_semihosting(CPUState *cs);
|
||||||
|
|
||||||
static inline CPULM32State *cpu_init(const char *cpu_model)
|
static inline CPULM32State *cpu_init(const char *cpu_model)
|
||||||
{
|
{
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* LatticeMico32 helper routines.
|
* LatticeMico32 helper routines.
|
||||||
*
|
*
|
||||||
* Copyright (c) 2010 Michael Walle <michael@walle.cc>
|
* Copyright (c) 2010-2014 Michael Walle <michael@walle.cc>
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "qemu/host-utils.h"
|
#include "qemu/host-utils.h"
|
||||||
|
#include "sysemu/sysemu.h"
|
||||||
|
|
||||||
int lm32_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw,
|
int lm32_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw,
|
||||||
int mmu_idx)
|
int mmu_idx)
|
||||||
@ -159,11 +160,20 @@ void lm32_cpu_do_interrupt(CPUState *cs)
|
|||||||
"exception at pc=%x type=%x\n", env->pc, cs->exception_index);
|
"exception at pc=%x type=%x\n", env->pc, cs->exception_index);
|
||||||
|
|
||||||
switch (cs->exception_index) {
|
switch (cs->exception_index) {
|
||||||
|
case EXCP_SYSTEMCALL:
|
||||||
|
if (unlikely(semihosting_enabled)) {
|
||||||
|
/* do_semicall() returns true if call was handled. Otherwise
|
||||||
|
* do the normal exception handling. */
|
||||||
|
if (lm32_cpu_do_semihosting(cs)) {
|
||||||
|
env->pc += 4;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* fall through */
|
||||||
case EXCP_INSN_BUS_ERROR:
|
case EXCP_INSN_BUS_ERROR:
|
||||||
case EXCP_DATA_BUS_ERROR:
|
case EXCP_DATA_BUS_ERROR:
|
||||||
case EXCP_DIVIDE_BY_ZERO:
|
case EXCP_DIVIDE_BY_ZERO:
|
||||||
case EXCP_IRQ:
|
case EXCP_IRQ:
|
||||||
case EXCP_SYSTEMCALL:
|
|
||||||
/* non-debug exceptions */
|
/* non-debug exceptions */
|
||||||
env->regs[R_EA] = env->pc;
|
env->regs[R_EA] = env->pc;
|
||||||
env->ie |= (env->ie & IE_IE) ? IE_EIE : 0;
|
env->ie |= (env->ie & IE_IE) ? IE_EIE : 0;
|
||||||
|
215
target-lm32/lm32-semi.c
Normal file
215
target-lm32/lm32-semi.c
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
/*
|
||||||
|
* Lattice Mico32 semihosting syscall interface
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014 Michael Walle <michael@walle.cc>
|
||||||
|
*
|
||||||
|
* Based on target-m68k/m68k-semi.c, which is
|
||||||
|
* Copyright (c) 2005-2007 CodeSourcery.
|
||||||
|
*
|
||||||
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||||
|
* See the COPYING file in the top-level directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "cpu.h"
|
||||||
|
#include "helper.h"
|
||||||
|
#include "qemu/log.h"
|
||||||
|
#include "exec/softmmu-semi.h"
|
||||||
|
|
||||||
|
enum {
|
||||||
|
TARGET_SYS_exit = 1,
|
||||||
|
TARGET_SYS_open = 2,
|
||||||
|
TARGET_SYS_close = 3,
|
||||||
|
TARGET_SYS_read = 4,
|
||||||
|
TARGET_SYS_write = 5,
|
||||||
|
TARGET_SYS_lseek = 6,
|
||||||
|
TARGET_SYS_fstat = 10,
|
||||||
|
TARGET_SYS_stat = 15,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
NEWLIB_O_RDONLY = 0x0,
|
||||||
|
NEWLIB_O_WRONLY = 0x1,
|
||||||
|
NEWLIB_O_RDWR = 0x2,
|
||||||
|
NEWLIB_O_APPEND = 0x8,
|
||||||
|
NEWLIB_O_CREAT = 0x200,
|
||||||
|
NEWLIB_O_TRUNC = 0x400,
|
||||||
|
NEWLIB_O_EXCL = 0x800,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int translate_openflags(int flags)
|
||||||
|
{
|
||||||
|
int hf;
|
||||||
|
|
||||||
|
if (flags & NEWLIB_O_WRONLY) {
|
||||||
|
hf = O_WRONLY;
|
||||||
|
} else if (flags & NEWLIB_O_RDWR) {
|
||||||
|
hf = O_RDWR;
|
||||||
|
} else {
|
||||||
|
hf = O_RDONLY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & NEWLIB_O_APPEND) {
|
||||||
|
hf |= O_APPEND;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & NEWLIB_O_CREAT) {
|
||||||
|
hf |= O_CREAT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & NEWLIB_O_TRUNC) {
|
||||||
|
hf |= O_TRUNC;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & NEWLIB_O_EXCL) {
|
||||||
|
hf |= O_EXCL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hf;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct newlib_stat {
|
||||||
|
int16_t newlib_st_dev; /* device */
|
||||||
|
uint16_t newlib_st_ino; /* inode */
|
||||||
|
uint16_t newlib_st_mode; /* protection */
|
||||||
|
uint16_t newlib_st_nlink; /* number of hard links */
|
||||||
|
uint16_t newlib_st_uid; /* user ID of owner */
|
||||||
|
uint16_t newlib_st_gid; /* group ID of owner */
|
||||||
|
int16_t newlib_st_rdev; /* device type (if inode device) */
|
||||||
|
int32_t newlib_st_size; /* total size, in bytes */
|
||||||
|
int32_t newlib_st_atime; /* time of last access */
|
||||||
|
uint32_t newlib_st_spare1;
|
||||||
|
int32_t newlib_st_mtime; /* time of last modification */
|
||||||
|
uint32_t newlib_st_spare2;
|
||||||
|
int32_t newlib_st_ctime; /* time of last change */
|
||||||
|
uint32_t newlib_st_spare3;
|
||||||
|
} QEMU_PACKED;
|
||||||
|
|
||||||
|
static int translate_stat(CPULM32State *env, target_ulong addr,
|
||||||
|
struct stat *s)
|
||||||
|
{
|
||||||
|
struct newlib_stat *p;
|
||||||
|
|
||||||
|
p = lock_user(VERIFY_WRITE, addr, sizeof(struct newlib_stat), 0);
|
||||||
|
if (!p) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
p->newlib_st_dev = cpu_to_be16(s->st_dev);
|
||||||
|
p->newlib_st_ino = cpu_to_be16(s->st_ino);
|
||||||
|
p->newlib_st_mode = cpu_to_be16(s->st_mode);
|
||||||
|
p->newlib_st_nlink = cpu_to_be16(s->st_nlink);
|
||||||
|
p->newlib_st_uid = cpu_to_be16(s->st_uid);
|
||||||
|
p->newlib_st_gid = cpu_to_be16(s->st_gid);
|
||||||
|
p->newlib_st_rdev = cpu_to_be16(s->st_rdev);
|
||||||
|
p->newlib_st_size = cpu_to_be32(s->st_size);
|
||||||
|
p->newlib_st_atime = cpu_to_be32(s->st_atime);
|
||||||
|
p->newlib_st_mtime = cpu_to_be32(s->st_mtime);
|
||||||
|
p->newlib_st_ctime = cpu_to_be32(s->st_ctime);
|
||||||
|
unlock_user(p, addr, sizeof(struct newlib_stat));
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lm32_cpu_do_semihosting(CPUState *cs)
|
||||||
|
{
|
||||||
|
LM32CPU *cpu = LM32_CPU(cs);
|
||||||
|
CPULM32State *env = &cpu->env;
|
||||||
|
|
||||||
|
int ret = -1;
|
||||||
|
target_ulong nr, arg0, arg1, arg2;
|
||||||
|
void *p;
|
||||||
|
struct stat s;
|
||||||
|
|
||||||
|
nr = env->regs[R_R8];
|
||||||
|
arg0 = env->regs[R_R1];
|
||||||
|
arg1 = env->regs[R_R2];
|
||||||
|
arg2 = env->regs[R_R3];
|
||||||
|
|
||||||
|
switch (nr) {
|
||||||
|
case TARGET_SYS_exit:
|
||||||
|
/* void _exit(int rc) */
|
||||||
|
exit(arg0);
|
||||||
|
|
||||||
|
case TARGET_SYS_open:
|
||||||
|
/* int open(const char *pathname, int flags) */
|
||||||
|
p = lock_user_string(arg0);
|
||||||
|
if (!p) {
|
||||||
|
ret = -1;
|
||||||
|
} else {
|
||||||
|
ret = open(p, translate_openflags(arg2));
|
||||||
|
unlock_user(p, arg0, 0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TARGET_SYS_read:
|
||||||
|
/* ssize_t read(int fd, const void *buf, size_t count) */
|
||||||
|
p = lock_user(VERIFY_WRITE, arg1, arg2, 0);
|
||||||
|
if (!p) {
|
||||||
|
ret = -1;
|
||||||
|
} else {
|
||||||
|
ret = read(arg0, p, arg2);
|
||||||
|
unlock_user(p, arg1, arg2);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TARGET_SYS_write:
|
||||||
|
/* ssize_t write(int fd, const void *buf, size_t count) */
|
||||||
|
p = lock_user(VERIFY_READ, arg1, arg2, 1);
|
||||||
|
if (!p) {
|
||||||
|
ret = -1;
|
||||||
|
} else {
|
||||||
|
ret = write(arg0, p, arg2);
|
||||||
|
unlock_user(p, arg1, 0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TARGET_SYS_close:
|
||||||
|
/* int close(int fd) */
|
||||||
|
/* don't close stdin/stdout/stderr */
|
||||||
|
if (arg0 > 2) {
|
||||||
|
ret = close(arg0);
|
||||||
|
} else {
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TARGET_SYS_lseek:
|
||||||
|
/* off_t lseek(int fd, off_t offset, int whence */
|
||||||
|
ret = lseek(arg0, arg1, arg2);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TARGET_SYS_stat:
|
||||||
|
/* int stat(const char *path, struct stat *buf) */
|
||||||
|
p = lock_user_string(arg0);
|
||||||
|
if (!p) {
|
||||||
|
ret = -1;
|
||||||
|
} else {
|
||||||
|
ret = stat(p, &s);
|
||||||
|
unlock_user(p, arg0, 0);
|
||||||
|
if (translate_stat(env, arg1, &s) == 0) {
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TARGET_SYS_fstat:
|
||||||
|
/* int stat(int fd, struct stat *buf) */
|
||||||
|
ret = fstat(arg0, &s);
|
||||||
|
if (ret == 0) {
|
||||||
|
if (translate_stat(env, arg1, &s) == 0) {
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
/* unhandled */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
env->regs[R_R1] = ret;
|
||||||
|
return true;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user