aboutsummaryrefslogtreecommitdiff
path: root/target
diff options
context:
space:
mode:
authorMax Filippov <jcmvbkbc@gmail.com>2017-05-12 11:09:14 -0700
committerMax Filippov <jcmvbkbc@gmail.com>2017-06-06 02:40:48 -0700
commit8128b3e0798e6c7e5742fa9f85bf8a4bf90af94a (patch)
tree068e7f48cbe3422f7ff79a6ee339657bb6221f4f /target
parent347ec03093f9668a379ef6b7fa1feb332fff039c (diff)
target/xtensa: support output to chardev console
In semihosting mode QEMU allows guest to read and write host file descriptors directly, including descriptors 0..2, a.k.a. stdin, stdout and stderr. Sometimes it's desirable to have semihosting console controlled by -serial option, e.g. to connect it to network. Add semihosting console to xtensa-semi.c, open it in the 'sim' machine in the presence of -serial option and direct stdout and stderr to it when it's present. Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
Diffstat (limited to 'target')
-rw-r--r--target/xtensa/cpu.h1
-rw-r--r--target/xtensa/xtensa-semi.c66
2 files changed, 53 insertions, 14 deletions
diff --git a/target/xtensa/cpu.h b/target/xtensa/cpu.h
index ecca17d45d..ee29fb1a14 100644
--- a/target/xtensa/cpu.h
+++ b/target/xtensa/cpu.h
@@ -483,6 +483,7 @@ void xtensa_translate_init(void);
void xtensa_breakpoint_handler(CPUState *cs);
void xtensa_finalize_config(XtensaConfig *config);
void xtensa_register_core(XtensaConfigList *node);
+void xtensa_sim_open_console(Chardev *chr);
void check_interrupts(CPUXtensaState *s);
void xtensa_irq_init(CPUXtensaState *env);
void *xtensa_get_extint(CPUXtensaState *env, unsigned extint);
diff --git a/target/xtensa/xtensa-semi.c b/target/xtensa/xtensa-semi.c
index ffcaf8d0ac..32e2bd7f1d 100644
--- a/target/xtensa/xtensa-semi.c
+++ b/target/xtensa/xtensa-semi.c
@@ -27,9 +27,14 @@
#include "qemu/osdep.h"
#include "cpu.h"
+#include "chardev/char-fe.h"
#include "exec/helper-proto.h"
#include "exec/semihost.h"
+#include "qapi/error.h"
#include "qemu/log.h"
+#include "sysemu/sysemu.h"
+
+static CharBackend *xtensa_sim_console;
enum {
TARGET_SYS_exit = 1,
@@ -148,6 +153,15 @@ static uint32_t errno_h2g(int host_errno)
}
}
+void xtensa_sim_open_console(Chardev *chr)
+{
+ static CharBackend console;
+
+ qemu_chr_fe_init(&console, chr, &error_abort);
+ qemu_chr_fe_set_handlers(&console, NULL, NULL, NULL, NULL, NULL, true);
+ xtensa_sim_console = &console;
+}
+
void HELPER(simcall)(CPUXtensaState *env)
{
CPUState *cs = CPU(xtensa_env_get_cpu(env));
@@ -181,10 +195,25 @@ void HELPER(simcall)(CPUXtensaState *env)
if (buf) {
vaddr += io_sz;
len -= io_sz;
- io_done = is_write ?
- write(fd, buf, io_sz) :
- read(fd, buf, io_sz);
- regs[3] = errno_h2g(errno);
+ if (fd < 3 && xtensa_sim_console) {
+ if (is_write && (fd == 1 || fd == 2)) {
+ io_done = qemu_chr_fe_write_all(xtensa_sim_console,
+ buf, io_sz);
+ regs[3] = errno_h2g(errno);
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s fd %d is not supported with chardev console\n",
+ is_write ?
+ "writing to" : "reading from", fd);
+ io_done = -1;
+ regs[3] = TARGET_EBADF;
+ }
+ } else {
+ io_done = is_write ?
+ write(fd, buf, io_sz) :
+ read(fd, buf, io_sz);
+ regs[3] = errno_h2g(errno);
+ }
if (io_done == -1) {
error = true;
io_done = 0;
@@ -256,10 +285,6 @@ void HELPER(simcall)(CPUXtensaState *env)
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,
@@ -267,12 +292,25 @@ void HELPER(simcall)(CPUXtensaState *env)
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);
+ if (fd < 3 && xtensa_sim_console) {
+ if ((fd == 1 || fd == 2) && rq == SELECT_ONE_WRITE) {
+ regs[2] = 1;
+ } else {
+ regs[2] = 0;
+ }
+ regs[3] = 0;
+ } else {
+ fd_set fdset;
+
+ FD_ZERO(&fdset);
+ FD_SET(fd, &fdset);
+ 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;