150336067SMichael Clark /* 250336067SMichael Clark * QEMU RISC-V Host Target Interface (HTIF) Emulation 350336067SMichael Clark * 450336067SMichael Clark * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu 550336067SMichael Clark * Copyright (c) 2017-2018 SiFive, Inc. 650336067SMichael Clark * 750336067SMichael Clark * This provides HTIF device emulation for QEMU. At the moment this allows 850336067SMichael Clark * for identical copies of bbl/linux to run on both spike and QEMU. 950336067SMichael Clark * 1050336067SMichael Clark * This program is free software; you can redistribute it and/or modify it 1150336067SMichael Clark * under the terms and conditions of the GNU General Public License, 1250336067SMichael Clark * version 2 or later, as published by the Free Software Foundation. 1350336067SMichael Clark * 1450336067SMichael Clark * This program is distributed in the hope it will be useful, but WITHOUT 1550336067SMichael Clark * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1650336067SMichael Clark * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 1750336067SMichael Clark * more details. 1850336067SMichael Clark * 1950336067SMichael Clark * You should have received a copy of the GNU General Public License along with 2050336067SMichael Clark * this program. If not, see <http://www.gnu.org/licenses/>. 2150336067SMichael Clark */ 2250336067SMichael Clark 2350336067SMichael Clark #include "qemu/osdep.h" 2450336067SMichael Clark #include "qapi/error.h" 2550336067SMichael Clark #include "qemu/log.h" 2670eb9f9cSBin Meng #include "hw/char/riscv_htif.h" 2750336067SMichael Clark #include "chardev/char.h" 2850336067SMichael Clark #include "chardev/char-fe.h" 2950336067SMichael Clark #include "qemu/timer.h" 3050336067SMichael Clark #include "qemu/error-report.h" 3166247edcSWeiwei Li #include "exec/address-spaces.h" 32058096f1SThomas Huth #include "exec/tswap.h" 33*32cad1ffSPhilippe Mathieu-Daudé #include "system/dma.h" 34*32cad1ffSPhilippe Mathieu-Daudé #include "system/runstate.h" 3550336067SMichael Clark 3650336067SMichael Clark #define RISCV_DEBUG_HTIF 0 3750336067SMichael Clark #define HTIF_DEBUG(fmt, ...) \ 3850336067SMichael Clark do { \ 3950336067SMichael Clark if (RISCV_DEBUG_HTIF) { \ 4050336067SMichael Clark qemu_log_mask(LOG_TRACE, "%s: " fmt "\n", __func__, ##__VA_ARGS__);\ 4150336067SMichael Clark } \ 4250336067SMichael Clark } while (0) 4350336067SMichael Clark 44753ae97aSBin Meng #define HTIF_DEV_SHIFT 56 45753ae97aSBin Meng #define HTIF_CMD_SHIFT 48 46753ae97aSBin Meng 47753ae97aSBin Meng #define HTIF_DEV_SYSTEM 0 48753ae97aSBin Meng #define HTIF_DEV_CONSOLE 1 49753ae97aSBin Meng 50753ae97aSBin Meng #define HTIF_SYSTEM_CMD_SYSCALL 0 51753ae97aSBin Meng #define HTIF_CONSOLE_CMD_GETC 0 52753ae97aSBin Meng #define HTIF_CONSOLE_CMD_PUTC 1 53753ae97aSBin Meng 54a6e13e31SBin Meng /* PK system call number */ 55a6e13e31SBin Meng #define PK_SYS_WRITE 64 56a6e13e31SBin Meng 5766247edcSWeiwei Li const char *sig_file; 5866247edcSWeiwei Li uint8_t line_size = 16; 5966247edcSWeiwei Li 6066247edcSWeiwei Li static uint64_t fromhost_addr, tohost_addr, begin_sig_addr, end_sig_addr; 6150336067SMichael Clark 6250336067SMichael Clark void htif_symbol_callback(const char *st_name, int st_info, uint64_t st_value, 6350336067SMichael Clark uint64_t st_size) 6450336067SMichael Clark { 6550336067SMichael Clark if (strcmp("fromhost", st_name) == 0) { 6650336067SMichael Clark fromhost_addr = st_value; 6750336067SMichael Clark if (st_size != 8) { 6850336067SMichael Clark error_report("HTIF fromhost must be 8 bytes"); 6950336067SMichael Clark exit(1); 7050336067SMichael Clark } 7150336067SMichael Clark } else if (strcmp("tohost", st_name) == 0) { 7250336067SMichael Clark tohost_addr = st_value; 7350336067SMichael Clark if (st_size != 8) { 7450336067SMichael Clark error_report("HTIF tohost must be 8 bytes"); 7550336067SMichael Clark exit(1); 7650336067SMichael Clark } 7766247edcSWeiwei Li } else if (strcmp("begin_signature", st_name) == 0) { 7866247edcSWeiwei Li begin_sig_addr = st_value; 7966247edcSWeiwei Li } else if (strcmp("end_signature", st_name) == 0) { 8066247edcSWeiwei Li end_sig_addr = st_value; 8150336067SMichael Clark } 8250336067SMichael Clark } 8350336067SMichael Clark 8450336067SMichael Clark /* 8550336067SMichael Clark * Called by the char dev to see if HTIF is ready to accept input. 8650336067SMichael Clark */ 8750336067SMichael Clark static int htif_can_recv(void *opaque) 8850336067SMichael Clark { 8950336067SMichael Clark return 1; 9050336067SMichael Clark } 9150336067SMichael Clark 9250336067SMichael Clark /* 9350336067SMichael Clark * Called by the char dev to supply input to HTIF console. 9450336067SMichael Clark * We assume that we will receive one character at a time. 9550336067SMichael Clark */ 9650336067SMichael Clark static void htif_recv(void *opaque, const uint8_t *buf, int size) 9750336067SMichael Clark { 98dadee9e3SBin Meng HTIFState *s = opaque; 9950336067SMichael Clark 10050336067SMichael Clark if (size != 1) { 10150336067SMichael Clark return; 10250336067SMichael Clark } 10350336067SMichael Clark 104753ae97aSBin Meng /* 105753ae97aSBin Meng * TODO - we need to check whether mfromhost is zero which indicates 106753ae97aSBin Meng * the device is ready to receive. The current implementation 107753ae97aSBin Meng * will drop characters 108753ae97aSBin Meng */ 10950336067SMichael Clark 110dadee9e3SBin Meng uint64_t val_written = s->pending_read; 11150336067SMichael Clark uint64_t resp = 0x100 | *buf; 11250336067SMichael Clark 1131237c2d6SBin Meng s->fromhost = (val_written >> 48 << 48) | (resp << 16 >> 16); 11450336067SMichael Clark } 11550336067SMichael Clark 11650336067SMichael Clark /* 11750336067SMichael Clark * Called by the char dev to supply special events to the HTIF console. 11850336067SMichael Clark * Not used for HTIF. 11950336067SMichael Clark */ 120083b266fSPhilippe Mathieu-Daudé static void htif_event(void *opaque, QEMUChrEvent event) 12150336067SMichael Clark { 12250336067SMichael Clark 12350336067SMichael Clark } 12450336067SMichael Clark 12550336067SMichael Clark static int htif_be_change(void *opaque) 12650336067SMichael Clark { 12750336067SMichael Clark HTIFState *s = opaque; 12850336067SMichael Clark 12950336067SMichael Clark qemu_chr_fe_set_handlers(&s->chr, htif_can_recv, htif_recv, htif_event, 13050336067SMichael Clark htif_be_change, s, NULL, true); 13150336067SMichael Clark 13250336067SMichael Clark return 0; 13350336067SMichael Clark } 13450336067SMichael Clark 135753ae97aSBin Meng /* 136753ae97aSBin Meng * See below the tohost register format. 137753ae97aSBin Meng * 138753ae97aSBin Meng * Bits 63:56 indicate the "device". 139753ae97aSBin Meng * Bits 55:48 indicate the "command". 140753ae97aSBin Meng * 141753ae97aSBin Meng * Device 0 is the syscall device, which is used to emulate Unixy syscalls. 142753ae97aSBin Meng * It only implements command 0, which has two subfunctions: 143753ae97aSBin Meng * - If bit 0 is clear, then bits 47:0 represent a pointer to a struct 144753ae97aSBin Meng * describing the syscall. 145753ae97aSBin Meng * - If bit 1 is set, then bits 47:1 represent an exit code, with a zero 146753ae97aSBin Meng * value indicating success and other values indicating failure. 147753ae97aSBin Meng * 148753ae97aSBin Meng * Device 1 is the blocking character device. 149753ae97aSBin Meng * - Command 0 reads a character 150753ae97aSBin Meng * - Command 1 writes a character from the 8 LSBs of tohost 151753ae97aSBin Meng * 152753ae97aSBin Meng * For RV32, the tohost register is zero-extended, so only device=0 and 153753ae97aSBin Meng * command=0 (i.e. HTIF syscalls/exit codes) are supported. 154753ae97aSBin Meng */ 155dadee9e3SBin Meng static void htif_handle_tohost_write(HTIFState *s, uint64_t val_written) 15650336067SMichael Clark { 157753ae97aSBin Meng uint8_t device = val_written >> HTIF_DEV_SHIFT; 158753ae97aSBin Meng uint8_t cmd = val_written >> HTIF_CMD_SHIFT; 15950336067SMichael Clark uint64_t payload = val_written & 0xFFFFFFFFFFFFULL; 16050336067SMichael Clark int resp = 0; 16150336067SMichael Clark 16250336067SMichael Clark HTIF_DEBUG("mtohost write: device: %d cmd: %d what: %02" PRIx64 16350336067SMichael Clark " -payload: %016" PRIx64 "\n", device, cmd, payload & 0xFF, payload); 16450336067SMichael Clark 16550336067SMichael Clark /* 16650336067SMichael Clark * Currently, there is a fixed mapping of devices: 16750336067SMichael Clark * 0: riscv-tests Pass/Fail Reporting Only (no syscall proxy) 16850336067SMichael Clark * 1: Console 16950336067SMichael Clark */ 170753ae97aSBin Meng if (unlikely(device == HTIF_DEV_SYSTEM)) { 17150336067SMichael Clark /* frontend syscall handler, shutdown and exit code support */ 172753ae97aSBin Meng if (cmd == HTIF_SYSTEM_CMD_SYSCALL) { 17350336067SMichael Clark if (payload & 0x1) { 17450336067SMichael Clark /* exit code */ 17550336067SMichael Clark int exit_code = payload >> 1; 17666247edcSWeiwei Li 17766247edcSWeiwei Li /* 17866247edcSWeiwei Li * Dump signature data if sig_file is specified and 17966247edcSWeiwei Li * begin/end_signature symbols exist. 18066247edcSWeiwei Li */ 18166247edcSWeiwei Li if (sig_file && begin_sig_addr && end_sig_addr) { 18266247edcSWeiwei Li uint64_t sig_len = end_sig_addr - begin_sig_addr; 18366247edcSWeiwei Li char *sig_data = g_malloc(sig_len); 18466247edcSWeiwei Li dma_memory_read(&address_space_memory, begin_sig_addr, 18566247edcSWeiwei Li sig_data, sig_len, MEMTXATTRS_UNSPECIFIED); 18666247edcSWeiwei Li FILE *signature = fopen(sig_file, "w"); 18766247edcSWeiwei Li if (signature == NULL) { 18866247edcSWeiwei Li error_report("Unable to open %s with error %s", 18966247edcSWeiwei Li sig_file, strerror(errno)); 19066247edcSWeiwei Li exit(1); 19166247edcSWeiwei Li } 19266247edcSWeiwei Li 19366247edcSWeiwei Li for (int i = 0; i < sig_len; i += line_size) { 19466247edcSWeiwei Li for (int j = line_size; j > 0; j--) { 19566247edcSWeiwei Li if (i + j <= sig_len) { 19666247edcSWeiwei Li fprintf(signature, "%02x", 19766247edcSWeiwei Li sig_data[i + j - 1] & 0xff); 19866247edcSWeiwei Li } else { 19966247edcSWeiwei Li fprintf(signature, "%02x", 0); 20066247edcSWeiwei Li } 20166247edcSWeiwei Li } 20266247edcSWeiwei Li fprintf(signature, "\n"); 20366247edcSWeiwei Li } 20466247edcSWeiwei Li 20566247edcSWeiwei Li fclose(signature); 20666247edcSWeiwei Li g_free(sig_data); 20766247edcSWeiwei Li } 20866247edcSWeiwei Li 209354c9606SClément Chigot qemu_system_shutdown_request_with_code( 210354c9606SClément Chigot SHUTDOWN_CAUSE_GUEST_SHUTDOWN, exit_code); 211354c9606SClément Chigot return; 21250336067SMichael Clark } else { 213a6e13e31SBin Meng uint64_t syscall[8]; 214a6e13e31SBin Meng cpu_physical_memory_read(payload, syscall, sizeof(syscall)); 215058096f1SThomas Huth if (tswap64(syscall[0]) == PK_SYS_WRITE && 216058096f1SThomas Huth tswap64(syscall[1]) == HTIF_DEV_CONSOLE && 217058096f1SThomas Huth tswap64(syscall[3]) == HTIF_CONSOLE_CMD_PUTC) { 218a6e13e31SBin Meng uint8_t ch; 219058096f1SThomas Huth cpu_physical_memory_read(tswap64(syscall[2]), &ch, 1); 2204a0e8ca3SAlistair Francis /* 2214a0e8ca3SAlistair Francis * XXX this blocks entire thread. Rewrite to use 2224a0e8ca3SAlistair Francis * qemu_chr_fe_write and background I/O callbacks 2234a0e8ca3SAlistair Francis */ 2244a0e8ca3SAlistair Francis qemu_chr_fe_write_all(&s->chr, &ch, 1); 225a6e13e31SBin Meng resp = 0x100 | (uint8_t)payload; 226a6e13e31SBin Meng } else { 227a6e13e31SBin Meng qemu_log_mask(LOG_UNIMP, 228a6e13e31SBin Meng "pk syscall proxy not supported\n"); 229a6e13e31SBin Meng } 23050336067SMichael Clark } 23150336067SMichael Clark } else { 23250336067SMichael Clark qemu_log("HTIF device %d: unknown command\n", device); 23350336067SMichael Clark } 234753ae97aSBin Meng } else if (likely(device == HTIF_DEV_CONSOLE)) { 23550336067SMichael Clark /* HTIF Console */ 236753ae97aSBin Meng if (cmd == HTIF_CONSOLE_CMD_GETC) { 23750336067SMichael Clark /* this should be a queue, but not yet implemented as such */ 238dadee9e3SBin Meng s->pending_read = val_written; 2391237c2d6SBin Meng s->tohost = 0; /* clear to indicate we read */ 24050336067SMichael Clark return; 241753ae97aSBin Meng } else if (cmd == HTIF_CONSOLE_CMD_PUTC) { 242c255946eSThomas Huth uint8_t ch = (uint8_t)payload; 2434a0e8ca3SAlistair Francis /* 2444a0e8ca3SAlistair Francis * XXX this blocks entire thread. Rewrite to use 2454a0e8ca3SAlistair Francis * qemu_chr_fe_write and background I/O callbacks 2464a0e8ca3SAlistair Francis */ 2474a0e8ca3SAlistair Francis qemu_chr_fe_write_all(&s->chr, &ch, 1); 24850336067SMichael Clark resp = 0x100 | (uint8_t)payload; 24950336067SMichael Clark } else { 25050336067SMichael Clark qemu_log("HTIF device %d: unknown command\n", device); 25150336067SMichael Clark } 25250336067SMichael Clark } else { 25350336067SMichael Clark qemu_log("HTIF unknown device or command\n"); 25450336067SMichael Clark HTIF_DEBUG("device: %d cmd: %d what: %02" PRIx64 25550336067SMichael Clark " payload: %016" PRIx64, device, cmd, payload & 0xFF, payload); 25650336067SMichael Clark } 25750336067SMichael Clark /* 258753ae97aSBin Meng * Latest bbl does not set fromhost to 0 if there is a value in tohost. 259753ae97aSBin Meng * With this code enabled, qemu hangs waiting for fromhost to go to 0. 260753ae97aSBin Meng * With this code disabled, qemu works with bbl priv v1.9.1 and v1.10. 261753ae97aSBin Meng * HTIF needs protocol documentation and a more complete state machine. 262753ae97aSBin Meng * 263dadee9e3SBin Meng * while (!s->fromhost_inprogress && 2641237c2d6SBin Meng * s->fromhost != 0x0) { 265753ae97aSBin Meng * } 26650336067SMichael Clark */ 2671237c2d6SBin Meng s->fromhost = (val_written >> 48 << 48) | (resp << 16 >> 16); 2681237c2d6SBin Meng s->tohost = 0; /* clear to indicate we read */ 26950336067SMichael Clark } 27050336067SMichael Clark 271dadee9e3SBin Meng #define TOHOST_OFFSET1 (s->tohost_offset) 272dadee9e3SBin Meng #define TOHOST_OFFSET2 (s->tohost_offset + 4) 273dadee9e3SBin Meng #define FROMHOST_OFFSET1 (s->fromhost_offset) 274dadee9e3SBin Meng #define FROMHOST_OFFSET2 (s->fromhost_offset + 4) 27550336067SMichael Clark 27650336067SMichael Clark /* CPU wants to read an HTIF register */ 27750336067SMichael Clark static uint64_t htif_mm_read(void *opaque, hwaddr addr, unsigned size) 27850336067SMichael Clark { 279dadee9e3SBin Meng HTIFState *s = opaque; 28050336067SMichael Clark if (addr == TOHOST_OFFSET1) { 2811237c2d6SBin Meng return s->tohost & 0xFFFFFFFF; 28250336067SMichael Clark } else if (addr == TOHOST_OFFSET2) { 2831237c2d6SBin Meng return (s->tohost >> 32) & 0xFFFFFFFF; 28450336067SMichael Clark } else if (addr == FROMHOST_OFFSET1) { 2851237c2d6SBin Meng return s->fromhost & 0xFFFFFFFF; 28650336067SMichael Clark } else if (addr == FROMHOST_OFFSET2) { 2871237c2d6SBin Meng return (s->fromhost >> 32) & 0xFFFFFFFF; 28850336067SMichael Clark } else { 28950336067SMichael Clark qemu_log("Invalid htif read: address %016" PRIx64 "\n", 29050336067SMichael Clark (uint64_t)addr); 29150336067SMichael Clark return 0; 29250336067SMichael Clark } 29350336067SMichael Clark } 29450336067SMichael Clark 29550336067SMichael Clark /* CPU wrote to an HTIF register */ 29650336067SMichael Clark static void htif_mm_write(void *opaque, hwaddr addr, 29750336067SMichael Clark uint64_t value, unsigned size) 29850336067SMichael Clark { 299dadee9e3SBin Meng HTIFState *s = opaque; 30050336067SMichael Clark if (addr == TOHOST_OFFSET1) { 3011237c2d6SBin Meng if (s->tohost == 0x0) { 302dadee9e3SBin Meng s->allow_tohost = 1; 3031237c2d6SBin Meng s->tohost = value & 0xFFFFFFFF; 30450336067SMichael Clark } else { 305dadee9e3SBin Meng s->allow_tohost = 0; 30650336067SMichael Clark } 30750336067SMichael Clark } else if (addr == TOHOST_OFFSET2) { 308dadee9e3SBin Meng if (s->allow_tohost) { 3091237c2d6SBin Meng s->tohost |= value << 32; 3101237c2d6SBin Meng htif_handle_tohost_write(s, s->tohost); 31150336067SMichael Clark } 31250336067SMichael Clark } else if (addr == FROMHOST_OFFSET1) { 313dadee9e3SBin Meng s->fromhost_inprogress = 1; 3141237c2d6SBin Meng s->fromhost = value & 0xFFFFFFFF; 31550336067SMichael Clark } else if (addr == FROMHOST_OFFSET2) { 3161237c2d6SBin Meng s->fromhost |= value << 32; 317dadee9e3SBin Meng s->fromhost_inprogress = 0; 31850336067SMichael Clark } else { 31950336067SMichael Clark qemu_log("Invalid htif write: address %016" PRIx64 "\n", 32050336067SMichael Clark (uint64_t)addr); 32150336067SMichael Clark } 32250336067SMichael Clark } 32350336067SMichael Clark 32450336067SMichael Clark static const MemoryRegionOps htif_mm_ops = { 32550336067SMichael Clark .read = htif_mm_read, 32650336067SMichael Clark .write = htif_mm_write, 32750336067SMichael Clark }; 32850336067SMichael Clark 3291237c2d6SBin Meng HTIFState *htif_mm_init(MemoryRegion *address_space, Chardev *chr, 33071d68c48SBin Meng uint64_t nonelf_base, bool custom_base) 3318d8897acSAnup Patel { 3328d8897acSAnup Patel uint64_t base, size, tohost_offset, fromhost_offset; 3338d8897acSAnup Patel 33471d68c48SBin Meng if (custom_base) { 3358d8897acSAnup Patel fromhost_addr = nonelf_base; 3368d8897acSAnup Patel tohost_addr = nonelf_base + 8; 33771d68c48SBin Meng } else { 33871d68c48SBin Meng if (!fromhost_addr || !tohost_addr) { 33971d68c48SBin Meng error_report("Invalid HTIF fromhost or tohost address"); 34071d68c48SBin Meng exit(1); 34171d68c48SBin Meng } 3428d8897acSAnup Patel } 3438d8897acSAnup Patel 3448d8897acSAnup Patel base = MIN(tohost_addr, fromhost_addr); 3458d8897acSAnup Patel size = MAX(tohost_addr + 8, fromhost_addr + 8) - base; 3468d8897acSAnup Patel tohost_offset = tohost_addr - base; 3478d8897acSAnup Patel fromhost_offset = fromhost_addr - base; 34850336067SMichael Clark 349b21e2380SMarkus Armbruster HTIFState *s = g_new0(HTIFState, 1); 35050336067SMichael Clark s->tohost_offset = tohost_offset; 35150336067SMichael Clark s->fromhost_offset = fromhost_offset; 35250336067SMichael Clark s->pending_read = 0; 35350336067SMichael Clark s->allow_tohost = 0; 35450336067SMichael Clark s->fromhost_inprogress = 0; 35550336067SMichael Clark qemu_chr_fe_init(&s->chr, chr, &error_abort); 35650336067SMichael Clark qemu_chr_fe_set_handlers(&s->chr, htif_can_recv, htif_recv, htif_event, 35750336067SMichael Clark htif_be_change, s, NULL, true); 3588d8897acSAnup Patel 35950336067SMichael Clark memory_region_init_io(&s->mmio, NULL, &htif_mm_ops, s, 36050336067SMichael Clark TYPE_HTIF_UART, size); 3616fad7d18SKONRAD Frederic memory_region_add_subregion_overlap(address_space, base, 3626fad7d18SKONRAD Frederic &s->mmio, 1); 36350336067SMichael Clark 36450336067SMichael Clark return s; 36550336067SMichael Clark } 366