| /* | |
| * SPDX-License-Identifier: BSD-3-Clause | |
| * | |
| * Copyright (c) 2010-2020, The Regents of the University of California | |
| * (Regents). All Rights Reserved. | |
| */ | |
| #include <sbi/riscv_locks.h> | |
| #include <sbi/sbi_console.h> | |
| #include <sbi/sbi_error.h> | |
| #include <sbi/sbi_system.h> | |
| #include <sbi_utils/sys/htif.h> | |
| #define HTIF_DATA_BITS 48 | |
| #define HTIF_DATA_MASK ((1ULL << HTIF_DATA_BITS) - 1) | |
| #define HTIF_DATA_SHIFT 0 | |
| #define HTIF_CMD_BITS 8 | |
| #define HTIF_CMD_MASK ((1ULL << HTIF_CMD_BITS) - 1) | |
| #define HTIF_CMD_SHIFT 48 | |
| #define HTIF_DEV_BITS 8 | |
| #define HTIF_DEV_MASK ((1ULL << HTIF_DEV_BITS) - 1) | |
| #define HTIF_DEV_SHIFT 56 | |
| #define HTIF_DEV_SYSTEM 0 | |
| #define HTIF_DEV_CONSOLE 1 | |
| #define HTIF_CONSOLE_CMD_GETC 0 | |
| #define HTIF_CONSOLE_CMD_PUTC 1 | |
| #if __riscv_xlen == 64 | |
| # define TOHOST_CMD(dev, cmd, payload) \ | |
| (((uint64_t)(dev) << HTIF_DEV_SHIFT) | \ | |
| ((uint64_t)(cmd) << HTIF_CMD_SHIFT) | \ | |
| (uint64_t)(payload)) | |
| #else | |
| # define TOHOST_CMD(dev, cmd, payload) ({ \ | |
| if ((dev) || (cmd)) __builtin_trap(); \ | |
| (payload); }) | |
| #endif | |
| #define FROMHOST_DEV(fromhost_value) \ | |
| ((uint64_t)((fromhost_value) >> HTIF_DEV_SHIFT) & HTIF_DEV_MASK) | |
| #define FROMHOST_CMD(fromhost_value) \ | |
| ((uint64_t)((fromhost_value) >> HTIF_CMD_SHIFT) & HTIF_CMD_MASK) | |
| #define FROMHOST_DATA(fromhost_value) \ | |
| ((uint64_t)((fromhost_value) >> HTIF_DATA_SHIFT) & HTIF_DATA_MASK) | |
| #define PK_SYS_write 64 | |
| volatile uint64_t tohost __attribute__((section(".htif"))); | |
| volatile uint64_t fromhost __attribute__((section(".htif"))); | |
| static uint64_t *htif_fromhost = NULL; | |
| static uint64_t *htif_tohost = NULL; | |
| static bool htif_custom = false; | |
| static int htif_console_buf; | |
| static spinlock_t htif_lock = SPIN_LOCK_INITIALIZER; | |
| static inline uint64_t __read_tohost(void) | |
| { | |
| return (htif_custom) ? *htif_tohost : tohost; | |
| } | |
| static inline void __write_tohost(uint64_t val) | |
| { | |
| if (htif_custom) | |
| *htif_tohost = val; | |
| else | |
| tohost = val; | |
| } | |
| static inline uint64_t __read_fromhost(void) | |
| { | |
| return (htif_custom) ? *htif_fromhost : fromhost; | |
| } | |
| static inline void __write_fromhost(uint64_t val) | |
| { | |
| if (htif_custom) | |
| *htif_fromhost = val; | |
| else | |
| fromhost = val; | |
| } | |
| static void __check_fromhost() | |
| { | |
| uint64_t fh = __read_fromhost(); | |
| if (!fh) | |
| return; | |
| __write_fromhost(0); | |
| /* this should be from the console */ | |
| if (FROMHOST_DEV(fh) != HTIF_DEV_CONSOLE) | |
| __builtin_trap(); | |
| switch (FROMHOST_CMD(fh)) { | |
| case HTIF_CONSOLE_CMD_GETC: | |
| htif_console_buf = 1 + (uint8_t)FROMHOST_DATA(fh); | |
| break; | |
| case HTIF_CONSOLE_CMD_PUTC: | |
| break; | |
| default: | |
| __builtin_trap(); | |
| } | |
| } | |
| static void __set_tohost(uint64_t dev, uint64_t cmd, uint64_t data) | |
| { | |
| while (__read_tohost()) | |
| __check_fromhost(); | |
| __write_tohost(TOHOST_CMD(dev, cmd, data)); | |
| } | |
| static int set_custom_addr(bool custom_addr, | |
| unsigned long custom_fromhost_addr, | |
| unsigned long custom_tohost_addr) | |
| { | |
| if (custom_addr) { | |
| if (htif_custom && | |
| ((custom_fromhost_addr != (unsigned long)htif_fromhost) || | |
| (custom_tohost_addr != (unsigned long)htif_tohost))) | |
| return SBI_EINVAL; | |
| htif_fromhost = (uint64_t *)custom_fromhost_addr; | |
| htif_tohost = (uint64_t *)custom_tohost_addr; | |
| htif_custom = true; | |
| } | |
| return 0; | |
| } | |
| #if __riscv_xlen == 32 | |
| static void do_tohost_fromhost(uint64_t dev, uint64_t cmd, uint64_t data) | |
| { | |
| spin_lock(&htif_lock); | |
| __set_tohost(HTIF_DEV_SYSTEM, cmd, data); | |
| while (1) { | |
| uint64_t fh = __read_fromhost(); | |
| if (fh) { | |
| if (FROMHOST_DEV(fh) == HTIF_DEV_SYSTEM && | |
| FROMHOST_CMD(fh) == cmd) { | |
| __write_fromhost(0); | |
| break; | |
| } | |
| __check_fromhost(); | |
| } | |
| } | |
| spin_unlock(&htif_lock); | |
| } | |
| static void htif_putc(char ch) | |
| { | |
| /* HTIF devices are not supported on RV32, so do a proxy write call */ | |
| volatile uint64_t magic_mem[8]; | |
| magic_mem[0] = PK_SYS_write; | |
| magic_mem[1] = HTIF_DEV_CONSOLE; | |
| magic_mem[2] = (uint64_t)(uintptr_t)&ch; | |
| magic_mem[3] = HTIF_CONSOLE_CMD_PUTC; | |
| do_tohost_fromhost(HTIF_DEV_SYSTEM, 0, (uint64_t)(uintptr_t)magic_mem); | |
| } | |
| #else | |
| static void htif_putc(char ch) | |
| { | |
| spin_lock(&htif_lock); | |
| __set_tohost(HTIF_DEV_CONSOLE, HTIF_CONSOLE_CMD_PUTC, ch); | |
| spin_unlock(&htif_lock); | |
| } | |
| #endif | |
| static int htif_getc(void) | |
| { | |
| int ch; | |
| #if __riscv_xlen == 32 | |
| /* HTIF devices are not supported on RV32 */ | |
| return -1; | |
| #endif | |
| spin_lock(&htif_lock); | |
| __check_fromhost(); | |
| ch = htif_console_buf; | |
| if (ch >= 0) { | |
| htif_console_buf = -1; | |
| __set_tohost(HTIF_DEV_CONSOLE, HTIF_CONSOLE_CMD_GETC, 0); | |
| } | |
| spin_unlock(&htif_lock); | |
| return ch - 1; | |
| } | |
| static struct sbi_console_device htif_console = { | |
| .name = "htif", | |
| .console_putc = htif_putc, | |
| .console_getc = htif_getc | |
| }; | |
| int htif_serial_init(bool custom_addr, | |
| unsigned long custom_fromhost_addr, | |
| unsigned long custom_tohost_addr) | |
| { | |
| int rc; | |
| rc = set_custom_addr(custom_addr, custom_fromhost_addr, | |
| custom_tohost_addr); | |
| if (rc) | |
| return rc; | |
| sbi_console_set_device(&htif_console); | |
| return 0; | |
| } | |
| static int htif_system_reset_check(u32 type, u32 reason) | |
| { | |
| return 1; | |
| } | |
| static void htif_system_reset(u32 type, u32 reason) | |
| { | |
| while (1) { | |
| __write_fromhost(0); | |
| __write_tohost(1); | |
| } | |
| } | |
| static struct sbi_system_reset_device htif_reset = { | |
| .name = "htif", | |
| .system_reset_check = htif_system_reset_check, | |
| .system_reset = htif_system_reset | |
| }; | |
| int htif_system_reset_init(bool custom_addr, | |
| unsigned long custom_fromhost_addr, | |
| unsigned long custom_tohost_addr) | |
| { | |
| int rc; | |
| rc = set_custom_addr(custom_addr, custom_fromhost_addr, | |
| custom_tohost_addr); | |
| if (rc) | |
| return rc; | |
| sbi_system_reset_add_device(&htif_reset); | |
| return 0; | |
| } |