diff options
Diffstat (limited to 'linux-user/semihost.c')
-rw-r--r-- | linux-user/semihost.c | 76 |
1 files changed, 76 insertions, 0 deletions
diff --git a/linux-user/semihost.c b/linux-user/semihost.c new file mode 100644 index 0000000000..c0015ee7f6 --- /dev/null +++ b/linux-user/semihost.c @@ -0,0 +1,76 @@ +/* + * ARM Compatible Semihosting Console Support. + * + * Copyright (c) 2019 Linaro Ltd + * + * Currently ARM and RISC-V are unique in having support for + * semihosting support in linux-user. So for now we implement the + * common console API but just for arm and risc-v linux-user. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "hw/semihosting/console.h" +#include "qemu.h" +#include <termios.h> + +int qemu_semihosting_console_outs(CPUArchState *env, target_ulong addr) +{ + int len = target_strlen(addr); + void *s; + if (len < 0){ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: passed inaccessible address " TARGET_FMT_lx, + __func__, addr); + return 0; + } + s = lock_user(VERIFY_READ, addr, (long)(len + 1), 1); + g_assert(s); /* target_strlen has already verified this will work */ + len = write(STDERR_FILENO, s, len); + unlock_user(s, addr, 0); + return len; +} + +void qemu_semihosting_console_outc(CPUArchState *env, target_ulong addr) +{ + char c; + + if (get_user_u8(c, addr)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: passed inaccessible address " TARGET_FMT_lx, + __func__, addr); + } else { + if (write(STDERR_FILENO, &c, 1) != 1) { + qemu_log_mask(LOG_UNIMP, "%s: unexpected write to stdout failure", + __func__); + } + } +} + +/* + * For linux-user we can safely block. However as we want to return as + * soon as a character is read we need to tweak the termio to disable + * line buffering. We restore the old mode afterwards in case the + * program is expecting more normal behaviour. This is slow but + * nothing using semihosting console reading is expecting to be fast. + */ +target_ulong qemu_semihosting_console_inc(CPUArchState *env) +{ + uint8_t c; + struct termios old_tio, new_tio; + + /* Disable line-buffering and echo */ + tcgetattr(STDIN_FILENO, &old_tio); + new_tio = old_tio; + new_tio.c_lflag &= (~ICANON & ~ECHO); + tcsetattr(STDIN_FILENO, TCSANOW, &new_tio); + + c = getchar(); + + /* restore config */ + tcsetattr(STDIN_FILENO, TCSANOW, &old_tio); + + return (target_ulong) c; +} |