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"
31*dfc56946SRichard Henderson #include "system/address-spaces.h"
32058096f1SThomas Huth #include "exec/tswap.h"
3332cad1ffSPhilippe Mathieu-Daudé #include "system/dma.h"
3432cad1ffSPhilippe Mathieu-Daudé #include "system/runstate.h"
35f04cac4fSPhilippe Mathieu-Daudé #include "trace.h"
3650336067SMichael Clark
37753ae97aSBin Meng #define HTIF_DEV_SHIFT 56
38753ae97aSBin Meng #define HTIF_CMD_SHIFT 48
39753ae97aSBin Meng
40753ae97aSBin Meng #define HTIF_DEV_SYSTEM 0
41753ae97aSBin Meng #define HTIF_DEV_CONSOLE 1
42753ae97aSBin Meng
43753ae97aSBin Meng #define HTIF_SYSTEM_CMD_SYSCALL 0
44753ae97aSBin Meng #define HTIF_CONSOLE_CMD_GETC 0
45753ae97aSBin Meng #define HTIF_CONSOLE_CMD_PUTC 1
46753ae97aSBin Meng
47a6e13e31SBin Meng /* PK system call number */
48a6e13e31SBin Meng #define PK_SYS_WRITE 64
49a6e13e31SBin Meng
5066247edcSWeiwei Li const char *sig_file;
5166247edcSWeiwei Li uint8_t line_size = 16;
5266247edcSWeiwei Li
5366247edcSWeiwei Li static uint64_t fromhost_addr, tohost_addr, begin_sig_addr, end_sig_addr;
5450336067SMichael Clark
htif_symbol_callback(const char * st_name,int st_info,uint64_t st_value,uint64_t st_size)5550336067SMichael Clark void htif_symbol_callback(const char *st_name, int st_info, uint64_t st_value,
5650336067SMichael Clark uint64_t st_size)
5750336067SMichael Clark {
5850336067SMichael Clark if (strcmp("fromhost", st_name) == 0) {
5950336067SMichael Clark fromhost_addr = st_value;
6050336067SMichael Clark if (st_size != 8) {
6150336067SMichael Clark error_report("HTIF fromhost must be 8 bytes");
6250336067SMichael Clark exit(1);
6350336067SMichael Clark }
6450336067SMichael Clark } else if (strcmp("tohost", st_name) == 0) {
6550336067SMichael Clark tohost_addr = st_value;
6650336067SMichael Clark if (st_size != 8) {
6750336067SMichael Clark error_report("HTIF tohost must be 8 bytes");
6850336067SMichael Clark exit(1);
6950336067SMichael Clark }
7066247edcSWeiwei Li } else if (strcmp("begin_signature", st_name) == 0) {
7166247edcSWeiwei Li begin_sig_addr = st_value;
7266247edcSWeiwei Li } else if (strcmp("end_signature", st_name) == 0) {
7366247edcSWeiwei Li end_sig_addr = st_value;
7450336067SMichael Clark }
7550336067SMichael Clark }
7650336067SMichael Clark
7750336067SMichael Clark /*
7850336067SMichael Clark * Called by the char dev to see if HTIF is ready to accept input.
7950336067SMichael Clark */
htif_can_recv(void * opaque)8050336067SMichael Clark static int htif_can_recv(void *opaque)
8150336067SMichael Clark {
8250336067SMichael Clark return 1;
8350336067SMichael Clark }
8450336067SMichael Clark
8550336067SMichael Clark /*
8650336067SMichael Clark * Called by the char dev to supply input to HTIF console.
8750336067SMichael Clark * We assume that we will receive one character at a time.
8850336067SMichael Clark */
htif_recv(void * opaque,const uint8_t * buf,int size)8950336067SMichael Clark static void htif_recv(void *opaque, const uint8_t *buf, int size)
9050336067SMichael Clark {
91dadee9e3SBin Meng HTIFState *s = opaque;
9250336067SMichael Clark
9350336067SMichael Clark if (size != 1) {
9450336067SMichael Clark return;
9550336067SMichael Clark }
9650336067SMichael Clark
97753ae97aSBin Meng /*
98753ae97aSBin Meng * TODO - we need to check whether mfromhost is zero which indicates
99753ae97aSBin Meng * the device is ready to receive. The current implementation
100753ae97aSBin Meng * will drop characters
101753ae97aSBin Meng */
10250336067SMichael Clark
103dadee9e3SBin Meng uint64_t val_written = s->pending_read;
10450336067SMichael Clark uint64_t resp = 0x100 | *buf;
10550336067SMichael Clark
1061237c2d6SBin Meng s->fromhost = (val_written >> 48 << 48) | (resp << 16 >> 16);
10750336067SMichael Clark }
10850336067SMichael Clark
10950336067SMichael Clark /*
11050336067SMichael Clark * Called by the char dev to supply special events to the HTIF console.
11150336067SMichael Clark * Not used for HTIF.
11250336067SMichael Clark */
htif_event(void * opaque,QEMUChrEvent event)113083b266fSPhilippe Mathieu-Daudé static void htif_event(void *opaque, QEMUChrEvent event)
11450336067SMichael Clark {
11550336067SMichael Clark
11650336067SMichael Clark }
11750336067SMichael Clark
htif_be_change(void * opaque)11850336067SMichael Clark static int htif_be_change(void *opaque)
11950336067SMichael Clark {
12050336067SMichael Clark HTIFState *s = opaque;
12150336067SMichael Clark
12250336067SMichael Clark qemu_chr_fe_set_handlers(&s->chr, htif_can_recv, htif_recv, htif_event,
12350336067SMichael Clark htif_be_change, s, NULL, true);
12450336067SMichael Clark
12550336067SMichael Clark return 0;
12650336067SMichael Clark }
12750336067SMichael Clark
128753ae97aSBin Meng /*
129753ae97aSBin Meng * See below the tohost register format.
130753ae97aSBin Meng *
131753ae97aSBin Meng * Bits 63:56 indicate the "device".
132753ae97aSBin Meng * Bits 55:48 indicate the "command".
133753ae97aSBin Meng *
134753ae97aSBin Meng * Device 0 is the syscall device, which is used to emulate Unixy syscalls.
135753ae97aSBin Meng * It only implements command 0, which has two subfunctions:
136753ae97aSBin Meng * - If bit 0 is clear, then bits 47:0 represent a pointer to a struct
137753ae97aSBin Meng * describing the syscall.
138753ae97aSBin Meng * - If bit 1 is set, then bits 47:1 represent an exit code, with a zero
139753ae97aSBin Meng * value indicating success and other values indicating failure.
140753ae97aSBin Meng *
141753ae97aSBin Meng * Device 1 is the blocking character device.
142753ae97aSBin Meng * - Command 0 reads a character
143753ae97aSBin Meng * - Command 1 writes a character from the 8 LSBs of tohost
144753ae97aSBin Meng *
145753ae97aSBin Meng * For RV32, the tohost register is zero-extended, so only device=0 and
146753ae97aSBin Meng * command=0 (i.e. HTIF syscalls/exit codes) are supported.
147753ae97aSBin Meng */
htif_handle_tohost_write(HTIFState * s,uint64_t val_written)148dadee9e3SBin Meng static void htif_handle_tohost_write(HTIFState *s, uint64_t val_written)
14950336067SMichael Clark {
150753ae97aSBin Meng uint8_t device = val_written >> HTIF_DEV_SHIFT;
151753ae97aSBin Meng uint8_t cmd = val_written >> HTIF_CMD_SHIFT;
15250336067SMichael Clark uint64_t payload = val_written & 0xFFFFFFFFFFFFULL;
15350336067SMichael Clark int resp = 0;
15450336067SMichael Clark
155f04cac4fSPhilippe Mathieu-Daudé trace_htif_uart_write_to_host(device, cmd, payload);
15650336067SMichael Clark
15750336067SMichael Clark /*
15850336067SMichael Clark * Currently, there is a fixed mapping of devices:
15950336067SMichael Clark * 0: riscv-tests Pass/Fail Reporting Only (no syscall proxy)
16050336067SMichael Clark * 1: Console
16150336067SMichael Clark */
162753ae97aSBin Meng if (unlikely(device == HTIF_DEV_SYSTEM)) {
16350336067SMichael Clark /* frontend syscall handler, shutdown and exit code support */
164753ae97aSBin Meng if (cmd == HTIF_SYSTEM_CMD_SYSCALL) {
16550336067SMichael Clark if (payload & 0x1) {
16650336067SMichael Clark /* exit code */
16750336067SMichael Clark int exit_code = payload >> 1;
16866247edcSWeiwei Li
16966247edcSWeiwei Li /*
17066247edcSWeiwei Li * Dump signature data if sig_file is specified and
17166247edcSWeiwei Li * begin/end_signature symbols exist.
17266247edcSWeiwei Li */
17366247edcSWeiwei Li if (sig_file && begin_sig_addr && end_sig_addr) {
17466247edcSWeiwei Li uint64_t sig_len = end_sig_addr - begin_sig_addr;
17566247edcSWeiwei Li char *sig_data = g_malloc(sig_len);
17666247edcSWeiwei Li dma_memory_read(&address_space_memory, begin_sig_addr,
17766247edcSWeiwei Li sig_data, sig_len, MEMTXATTRS_UNSPECIFIED);
17866247edcSWeiwei Li FILE *signature = fopen(sig_file, "w");
17966247edcSWeiwei Li if (signature == NULL) {
18066247edcSWeiwei Li error_report("Unable to open %s with error %s",
18166247edcSWeiwei Li sig_file, strerror(errno));
18266247edcSWeiwei Li exit(1);
18366247edcSWeiwei Li }
18466247edcSWeiwei Li
18566247edcSWeiwei Li for (int i = 0; i < sig_len; i += line_size) {
18666247edcSWeiwei Li for (int j = line_size; j > 0; j--) {
18766247edcSWeiwei Li if (i + j <= sig_len) {
18866247edcSWeiwei Li fprintf(signature, "%02x",
18966247edcSWeiwei Li sig_data[i + j - 1] & 0xff);
19066247edcSWeiwei Li } else {
19166247edcSWeiwei Li fprintf(signature, "%02x", 0);
19266247edcSWeiwei Li }
19366247edcSWeiwei Li }
19466247edcSWeiwei Li fprintf(signature, "\n");
19566247edcSWeiwei Li }
19666247edcSWeiwei Li
19766247edcSWeiwei Li fclose(signature);
19866247edcSWeiwei Li g_free(sig_data);
19966247edcSWeiwei Li }
20066247edcSWeiwei Li
201354c9606SClément Chigot qemu_system_shutdown_request_with_code(
202354c9606SClément Chigot SHUTDOWN_CAUSE_GUEST_SHUTDOWN, exit_code);
203354c9606SClément Chigot return;
20450336067SMichael Clark } else {
205a6e13e31SBin Meng uint64_t syscall[8];
206a6e13e31SBin Meng cpu_physical_memory_read(payload, syscall, sizeof(syscall));
207be0a70b9SPhilippe Mathieu-Daudé if (le64_to_cpu(syscall[0]) == PK_SYS_WRITE &&
208be0a70b9SPhilippe Mathieu-Daudé le64_to_cpu(syscall[1]) == HTIF_DEV_CONSOLE &&
209be0a70b9SPhilippe Mathieu-Daudé le64_to_cpu(syscall[3]) == HTIF_CONSOLE_CMD_PUTC) {
210a6e13e31SBin Meng uint8_t ch;
211be0a70b9SPhilippe Mathieu-Daudé cpu_physical_memory_read(le64_to_cpu(syscall[2]), &ch, 1);
2124a0e8ca3SAlistair Francis /*
2134a0e8ca3SAlistair Francis * XXX this blocks entire thread. Rewrite to use
2144a0e8ca3SAlistair Francis * qemu_chr_fe_write and background I/O callbacks
2154a0e8ca3SAlistair Francis */
2164a0e8ca3SAlistair Francis qemu_chr_fe_write_all(&s->chr, &ch, 1);
217a6e13e31SBin Meng resp = 0x100 | (uint8_t)payload;
218a6e13e31SBin Meng } else {
219a6e13e31SBin Meng qemu_log_mask(LOG_UNIMP,
220a6e13e31SBin Meng "pk syscall proxy not supported\n");
221a6e13e31SBin Meng }
22250336067SMichael Clark }
22350336067SMichael Clark } else {
22450336067SMichael Clark qemu_log("HTIF device %d: unknown command\n", device);
22550336067SMichael Clark }
226753ae97aSBin Meng } else if (likely(device == HTIF_DEV_CONSOLE)) {
22750336067SMichael Clark /* HTIF Console */
228753ae97aSBin Meng if (cmd == HTIF_CONSOLE_CMD_GETC) {
22950336067SMichael Clark /* this should be a queue, but not yet implemented as such */
230dadee9e3SBin Meng s->pending_read = val_written;
2311237c2d6SBin Meng s->tohost = 0; /* clear to indicate we read */
23250336067SMichael Clark return;
233753ae97aSBin Meng } else if (cmd == HTIF_CONSOLE_CMD_PUTC) {
234c255946eSThomas Huth uint8_t ch = (uint8_t)payload;
2354a0e8ca3SAlistair Francis /*
2364a0e8ca3SAlistair Francis * XXX this blocks entire thread. Rewrite to use
2374a0e8ca3SAlistair Francis * qemu_chr_fe_write and background I/O callbacks
2384a0e8ca3SAlistair Francis */
2394a0e8ca3SAlistair Francis qemu_chr_fe_write_all(&s->chr, &ch, 1);
24050336067SMichael Clark resp = 0x100 | (uint8_t)payload;
24150336067SMichael Clark } else {
24250336067SMichael Clark qemu_log("HTIF device %d: unknown command\n", device);
24350336067SMichael Clark }
24450336067SMichael Clark } else {
24550336067SMichael Clark qemu_log("HTIF unknown device or command\n");
246f04cac4fSPhilippe Mathieu-Daudé trace_htif_uart_unknown_device_command(device, cmd, payload);
24750336067SMichael Clark }
24850336067SMichael Clark /*
249753ae97aSBin Meng * Latest bbl does not set fromhost to 0 if there is a value in tohost.
250753ae97aSBin Meng * With this code enabled, qemu hangs waiting for fromhost to go to 0.
251753ae97aSBin Meng * With this code disabled, qemu works with bbl priv v1.9.1 and v1.10.
252753ae97aSBin Meng * HTIF needs protocol documentation and a more complete state machine.
253753ae97aSBin Meng *
254dadee9e3SBin Meng * while (!s->fromhost_inprogress &&
2551237c2d6SBin Meng * s->fromhost != 0x0) {
256753ae97aSBin Meng * }
25750336067SMichael Clark */
2581237c2d6SBin Meng s->fromhost = (val_written >> 48 << 48) | (resp << 16 >> 16);
2591237c2d6SBin Meng s->tohost = 0; /* clear to indicate we read */
26050336067SMichael Clark }
26150336067SMichael Clark
262dadee9e3SBin Meng #define TOHOST_OFFSET1 (s->tohost_offset)
263dadee9e3SBin Meng #define TOHOST_OFFSET2 (s->tohost_offset + 4)
264dadee9e3SBin Meng #define FROMHOST_OFFSET1 (s->fromhost_offset)
265dadee9e3SBin Meng #define FROMHOST_OFFSET2 (s->fromhost_offset + 4)
26650336067SMichael Clark
26750336067SMichael Clark /* CPU wants to read an HTIF register */
htif_mm_read(void * opaque,hwaddr addr,unsigned size)26850336067SMichael Clark static uint64_t htif_mm_read(void *opaque, hwaddr addr, unsigned size)
26950336067SMichael Clark {
270dadee9e3SBin Meng HTIFState *s = opaque;
27150336067SMichael Clark if (addr == TOHOST_OFFSET1) {
2721237c2d6SBin Meng return s->tohost & 0xFFFFFFFF;
27350336067SMichael Clark } else if (addr == TOHOST_OFFSET2) {
2741237c2d6SBin Meng return (s->tohost >> 32) & 0xFFFFFFFF;
27550336067SMichael Clark } else if (addr == FROMHOST_OFFSET1) {
2761237c2d6SBin Meng return s->fromhost & 0xFFFFFFFF;
27750336067SMichael Clark } else if (addr == FROMHOST_OFFSET2) {
2781237c2d6SBin Meng return (s->fromhost >> 32) & 0xFFFFFFFF;
27950336067SMichael Clark } else {
28050336067SMichael Clark qemu_log("Invalid htif read: address %016" PRIx64 "\n",
28150336067SMichael Clark (uint64_t)addr);
28250336067SMichael Clark return 0;
28350336067SMichael Clark }
28450336067SMichael Clark }
28550336067SMichael Clark
28650336067SMichael Clark /* CPU wrote to an HTIF register */
htif_mm_write(void * opaque,hwaddr addr,uint64_t value,unsigned size)28750336067SMichael Clark static void htif_mm_write(void *opaque, hwaddr addr,
28850336067SMichael Clark uint64_t value, unsigned size)
28950336067SMichael Clark {
290dadee9e3SBin Meng HTIFState *s = opaque;
29150336067SMichael Clark if (addr == TOHOST_OFFSET1) {
2921237c2d6SBin Meng if (s->tohost == 0x0) {
293dadee9e3SBin Meng s->allow_tohost = 1;
2941237c2d6SBin Meng s->tohost = value & 0xFFFFFFFF;
29550336067SMichael Clark } else {
296dadee9e3SBin Meng s->allow_tohost = 0;
29750336067SMichael Clark }
29850336067SMichael Clark } else if (addr == TOHOST_OFFSET2) {
299dadee9e3SBin Meng if (s->allow_tohost) {
3001237c2d6SBin Meng s->tohost |= value << 32;
3011237c2d6SBin Meng htif_handle_tohost_write(s, s->tohost);
30250336067SMichael Clark }
30350336067SMichael Clark } else if (addr == FROMHOST_OFFSET1) {
304dadee9e3SBin Meng s->fromhost_inprogress = 1;
3051237c2d6SBin Meng s->fromhost = value & 0xFFFFFFFF;
30650336067SMichael Clark } else if (addr == FROMHOST_OFFSET2) {
3071237c2d6SBin Meng s->fromhost |= value << 32;
308dadee9e3SBin Meng s->fromhost_inprogress = 0;
30950336067SMichael Clark } else {
31050336067SMichael Clark qemu_log("Invalid htif write: address %016" PRIx64 "\n",
31150336067SMichael Clark (uint64_t)addr);
31250336067SMichael Clark }
31350336067SMichael Clark }
31450336067SMichael Clark
31550336067SMichael Clark static const MemoryRegionOps htif_mm_ops = {
31650336067SMichael Clark .read = htif_mm_read,
31750336067SMichael Clark .write = htif_mm_write,
318be0a70b9SPhilippe Mathieu-Daudé .endianness = DEVICE_LITTLE_ENDIAN,
319d2ed9fffSPhilippe Mathieu-Daudé .impl = {
320d2ed9fffSPhilippe Mathieu-Daudé .min_access_size = 4,
321d2ed9fffSPhilippe Mathieu-Daudé .max_access_size = 4,
322d2ed9fffSPhilippe Mathieu-Daudé },
32350336067SMichael Clark };
32450336067SMichael Clark
htif_mm_init(MemoryRegion * address_space,Chardev * chr,uint64_t nonelf_base,bool custom_base)3251237c2d6SBin Meng HTIFState *htif_mm_init(MemoryRegion *address_space, Chardev *chr,
32671d68c48SBin Meng uint64_t nonelf_base, bool custom_base)
3278d8897acSAnup Patel {
3288d8897acSAnup Patel uint64_t base, size, tohost_offset, fromhost_offset;
3298d8897acSAnup Patel
33071d68c48SBin Meng if (custom_base) {
3318d8897acSAnup Patel fromhost_addr = nonelf_base;
3328d8897acSAnup Patel tohost_addr = nonelf_base + 8;
33371d68c48SBin Meng } else {
33471d68c48SBin Meng if (!fromhost_addr || !tohost_addr) {
33571d68c48SBin Meng error_report("Invalid HTIF fromhost or tohost address");
33671d68c48SBin Meng exit(1);
33771d68c48SBin Meng }
3388d8897acSAnup Patel }
3398d8897acSAnup Patel
3408d8897acSAnup Patel base = MIN(tohost_addr, fromhost_addr);
3418d8897acSAnup Patel size = MAX(tohost_addr + 8, fromhost_addr + 8) - base;
3428d8897acSAnup Patel tohost_offset = tohost_addr - base;
3438d8897acSAnup Patel fromhost_offset = fromhost_addr - base;
34450336067SMichael Clark
345b21e2380SMarkus Armbruster HTIFState *s = g_new0(HTIFState, 1);
34650336067SMichael Clark s->tohost_offset = tohost_offset;
34750336067SMichael Clark s->fromhost_offset = fromhost_offset;
34850336067SMichael Clark s->pending_read = 0;
34950336067SMichael Clark s->allow_tohost = 0;
35050336067SMichael Clark s->fromhost_inprogress = 0;
35150336067SMichael Clark qemu_chr_fe_init(&s->chr, chr, &error_abort);
35250336067SMichael Clark qemu_chr_fe_set_handlers(&s->chr, htif_can_recv, htif_recv, htif_event,
35350336067SMichael Clark htif_be_change, s, NULL, true);
3548d8897acSAnup Patel
35550336067SMichael Clark memory_region_init_io(&s->mmio, NULL, &htif_mm_ops, s,
35650336067SMichael Clark TYPE_HTIF_UART, size);
3576fad7d18SKONRAD Frederic memory_region_add_subregion_overlap(address_space, base,
3586fad7d18SKONRAD Frederic &s->mmio, 1);
35950336067SMichael Clark
36050336067SMichael Clark return s;
36150336067SMichael Clark }
362