1*56b5170cSKeith Packard /* 2*56b5170cSKeith Packard * ARM Semihosting Console Support 3*56b5170cSKeith Packard * 4*56b5170cSKeith Packard * Copyright (c) 2019 Linaro Ltd 5*56b5170cSKeith Packard * 6*56b5170cSKeith Packard * Currently ARM is unique in having support for semihosting support 7*56b5170cSKeith Packard * in linux-user. So for now we implement the common console API but 8*56b5170cSKeith Packard * just for arm linux-user. 9*56b5170cSKeith Packard * 10*56b5170cSKeith Packard * SPDX-License-Identifier: GPL-2.0-or-later 11*56b5170cSKeith Packard */ 12*56b5170cSKeith Packard 13*56b5170cSKeith Packard #include "qemu/osdep.h" 14*56b5170cSKeith Packard #include "cpu.h" 15*56b5170cSKeith Packard #include "hw/semihosting/console.h" 16*56b5170cSKeith Packard #include "qemu.h" 17*56b5170cSKeith Packard #include <termios.h> 18*56b5170cSKeith Packard 19*56b5170cSKeith Packard int qemu_semihosting_console_outs(CPUArchState *env, target_ulong addr) 20*56b5170cSKeith Packard { 21*56b5170cSKeith Packard int len = target_strlen(addr); 22*56b5170cSKeith Packard void *s; 23*56b5170cSKeith Packard if (len < 0){ 24*56b5170cSKeith Packard qemu_log_mask(LOG_GUEST_ERROR, 25*56b5170cSKeith Packard "%s: passed inaccessible address " TARGET_FMT_lx, 26*56b5170cSKeith Packard __func__, addr); 27*56b5170cSKeith Packard return 0; 28*56b5170cSKeith Packard } 29*56b5170cSKeith Packard s = lock_user(VERIFY_READ, addr, (long)(len + 1), 1); 30*56b5170cSKeith Packard g_assert(s); /* target_strlen has already verified this will work */ 31*56b5170cSKeith Packard len = write(STDERR_FILENO, s, len); 32*56b5170cSKeith Packard unlock_user(s, addr, 0); 33*56b5170cSKeith Packard return len; 34*56b5170cSKeith Packard } 35*56b5170cSKeith Packard 36*56b5170cSKeith Packard void qemu_semihosting_console_outc(CPUArchState *env, target_ulong addr) 37*56b5170cSKeith Packard { 38*56b5170cSKeith Packard char c; 39*56b5170cSKeith Packard 40*56b5170cSKeith Packard if (get_user_u8(c, addr)) { 41*56b5170cSKeith Packard qemu_log_mask(LOG_GUEST_ERROR, 42*56b5170cSKeith Packard "%s: passed inaccessible address " TARGET_FMT_lx, 43*56b5170cSKeith Packard __func__, addr); 44*56b5170cSKeith Packard } else { 45*56b5170cSKeith Packard if (write(STDERR_FILENO, &c, 1) != 1) { 46*56b5170cSKeith Packard qemu_log_mask(LOG_UNIMP, "%s: unexpected write to stdout failure", 47*56b5170cSKeith Packard __func__); 48*56b5170cSKeith Packard } 49*56b5170cSKeith Packard } 50*56b5170cSKeith Packard } 51*56b5170cSKeith Packard 52*56b5170cSKeith Packard /* 53*56b5170cSKeith Packard * For linux-user we can safely block. However as we want to return as 54*56b5170cSKeith Packard * soon as a character is read we need to tweak the termio to disable 55*56b5170cSKeith Packard * line buffering. We restore the old mode afterwards in case the 56*56b5170cSKeith Packard * program is expecting more normal behaviour. This is slow but 57*56b5170cSKeith Packard * nothing using semihosting console reading is expecting to be fast. 58*56b5170cSKeith Packard */ 59*56b5170cSKeith Packard target_ulong qemu_semihosting_console_inc(CPUArchState *env) 60*56b5170cSKeith Packard { 61*56b5170cSKeith Packard uint8_t c; 62*56b5170cSKeith Packard struct termios old_tio, new_tio; 63*56b5170cSKeith Packard 64*56b5170cSKeith Packard /* Disable line-buffering and echo */ 65*56b5170cSKeith Packard tcgetattr(STDIN_FILENO, &old_tio); 66*56b5170cSKeith Packard new_tio = old_tio; 67*56b5170cSKeith Packard new_tio.c_lflag &= (~ICANON & ~ECHO); 68*56b5170cSKeith Packard tcsetattr(STDIN_FILENO, TCSANOW, &new_tio); 69*56b5170cSKeith Packard 70*56b5170cSKeith Packard c = getchar(); 71*56b5170cSKeith Packard 72*56b5170cSKeith Packard /* restore config */ 73*56b5170cSKeith Packard tcsetattr(STDIN_FILENO, TCSANOW, &old_tio); 74*56b5170cSKeith Packard 75*56b5170cSKeith Packard return (target_ulong) c; 76*56b5170cSKeith Packard } 77