HTIF devices are currently used for the console and signaling test completion for tests in riscv-tests. These are not part of any RISC-V standard and will be phased out once better device support is available.
Signed-off-by: Sagar Karandikar <sag...@eecs.berkeley.edu> --- hw/riscv/Makefile.objs | 2 + hw/riscv/htif/elf_symb.c | 286 +++++++++++++++++++++++++++++ hw/riscv/htif/elf_symb.h | 80 ++++++++ hw/riscv/htif/htif.c | 423 +++++++++++++++++++++++++++++++++++++++++++ include/hw/riscv/htif/htif.h | 61 +++++++ 5 files changed, 852 insertions(+) create mode 100644 hw/riscv/htif/elf_symb.c create mode 100644 hw/riscv/htif/elf_symb.h create mode 100644 hw/riscv/htif/htif.c create mode 100644 include/hw/riscv/htif/htif.h diff --git a/hw/riscv/Makefile.objs b/hw/riscv/Makefile.objs index 79b4553..d830e5d 100644 --- a/hw/riscv/Makefile.objs +++ b/hw/riscv/Makefile.objs @@ -1 +1,3 @@ obj-y += riscv_rtc.o +obj-y += htif/elf_symb.o +obj-y += htif/htif.o diff --git a/hw/riscv/htif/elf_symb.c b/hw/riscv/htif/elf_symb.c new file mode 100644 index 0000000..dc8efd6 --- /dev/null +++ b/hw/riscv/htif/elf_symb.c @@ -0,0 +1,286 @@ +/* + * elf.c - A simple package for manipulating symbol tables in elf binaries. + * + * Taken from + * https://www.cs.cmu.edu/afs/cs.cmu.edu/academic/class/15213-f03/www/ + * ftrace/elf.c + * + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/mman.h> +#include <errno.h> +#include <fcntl.h> +#include <glib.h> + +#include "elf_symb.h" + +/* + * elf_open - Map a binary into the address space and extract the + * locations of the static and dynamic symbol tables and their string + * tables. Return this information in a Elf object file handle that will + * be passed to all of the other elf functions. + */ +Elf_obj *elf_open(const char *filename) +{ + int i, fd; + struct stat sbuf; + Elf_obj *ep; + Elf64_Shdr *shdr; + + ep = g_new(Elf_obj, 1); + + /* Do some consistency checks on the binary */ + fd = open(filename, O_RDONLY); + if (fd == -1) { + fprintf(stderr, "Can't open \"%s\": %s\n", filename, strerror(errno)); + exit(1); + } + if (fstat(fd, &sbuf) == -1) { + fprintf(stderr, "Can't stat \"%s\": %s\n", filename, strerror(errno)); + exit(1); + } + if (sbuf.st_size < sizeof(Elf64_Ehdr)) { + fprintf(stderr, "\"%s\" is not an ELF binary object\n", filename); + exit(1); + } + + /* It looks OK, so map the Elf binary into our address space */ + ep->mlen = sbuf.st_size; + ep->maddr = mmap(NULL, ep->mlen, PROT_READ, MAP_SHARED, fd, 0); + if (ep->maddr == (void *)-1) { + fprintf(stderr, "Can't mmap \"%s\": %s\n", filename, strerror(errno)); + exit(1); + } + close(fd); + + /* The Elf binary begins with the Elf header */ + ep->ehdr = ep->maddr; + + /* Make sure that this is an Elf binary */ + /* if (strncmp(ep->ehdr->e_ident, ELFMAG, SELFMAG)) { + fprintf(stderr, "\"%s\" is not an ELF binary object\n", filename); + exit(1); + }*/ + + /* + * Find the static and dynamic symbol tables and their string + * tables in the the mapped binary. The sh_link field in symbol + * table section headers gives the section index of the string + * table for that symbol table. + */ + shdr = (Elf64_Shdr *)(ep->maddr + ep->ehdr->e_shoff); + for (i = 0; i < ep->ehdr->e_shnum; i++) { + if (shdr[i].sh_type == SHT_SYMTAB) { /* Static symbol table */ + ep->symtab = (Elf64_Sym *)(ep->maddr + shdr[i].sh_offset); + ep->symtab_end = (Elf64_Sym *)((char *)ep->symtab + + shdr[i].sh_size); + ep->strtab = (char *)(ep->maddr + shdr[shdr[i].sh_link].sh_offset); + } + if (shdr[i].sh_type == SHT_DYNSYM) { /* Dynamic symbol table */ + ep->dsymtab = (Elf64_Sym *)(ep->maddr + shdr[i].sh_offset); + ep->dsymtab_end = (Elf64_Sym *)((char *)ep->dsymtab + + shdr[i].sh_size); + ep->dstrtab = (char *)(ep->maddr + shdr[shdr[i].sh_link].sh_offset); + } + } + return ep; +} + +/* + * elf_open - Map a binary into the address space and extract the + * locations of the static and dynamic symbol tables and their string + * tables. Return this information in a Elf object file handle that will + * be passed to all of the other elf functions. + */ +Elf_obj32 *elf_open32(const char *filename) +{ + int i, fd; + struct stat sbuf; + Elf_obj32 *ep; + Elf32_Shdr *shdr; + + ep = g_new(Elf_obj32, 1); + + /* Do some consistency checks on the binary */ + fd = open(filename, O_RDONLY); + if (fd == -1) { + fprintf(stderr, "Can't open \"%s\": %s\n", filename, strerror(errno)); + exit(1); + } + if (fstat(fd, &sbuf) == -1) { + fprintf(stderr, "Can't stat \"%s\": %s\n", filename, strerror(errno)); + exit(1); + } + if (sbuf.st_size < sizeof(Elf32_Ehdr)) { + fprintf(stderr, "\"%s\" is not an ELF binary object\n", filename); + exit(1); + } + + /* It looks OK, so map the Elf binary into our address space */ + ep->mlen = sbuf.st_size; + ep->maddr = mmap(NULL, ep->mlen, PROT_READ, MAP_SHARED, fd, 0); + if (ep->maddr == (void *)-1) { + fprintf(stderr, "Can't mmap \"%s\": %s\n", filename, strerror(errno)); + exit(1); + } + close(fd); + + /* The Elf binary begins with the Elf header */ + ep->ehdr = ep->maddr; + + /* Make sure that this is an Elf binary */ +/* if (strncmp(ep->ehdr->e_ident, ELFMAG, SELFMAG)) { + fprintf(stderr, "\"%s\" is not an ELF binary object\n", filename); + exit(1); + }*/ + + /* + * Find the static and dynamic symbol tables and their string + * tables in the the mapped binary. The sh_link field in symbol + * table section headers gives the section index of the string + * table for that symbol table. + */ + shdr = (Elf32_Shdr *)(ep->maddr + ep->ehdr->e_shoff); + for (i = 0; i < ep->ehdr->e_shnum; i++) { + if (shdr[i].sh_type == SHT_SYMTAB) { /* Static symbol table */ + ep->symtab = (Elf32_Sym *)(ep->maddr + shdr[i].sh_offset); + ep->symtab_end = (Elf32_Sym *)((char *)ep->symtab + + shdr[i].sh_size); + ep->strtab = (char *)(ep->maddr + shdr[shdr[i].sh_link].sh_offset); + } + if (shdr[i].sh_type == SHT_DYNSYM) { /* Dynamic symbol table */ + ep->dsymtab = (Elf32_Sym *)(ep->maddr + shdr[i].sh_offset); + ep->dsymtab_end = (Elf32_Sym *)((char *)ep->dsymtab + + shdr[i].sh_size); + ep->dstrtab = (char *)(ep->maddr + shdr[shdr[i].sh_link].sh_offset); + } + } + return ep; +} + +/* + * elf_close - Free up the resources of an elf object + */ +void elf_close(Elf_obj *ep) +{ + if (munmap(ep->maddr, ep->mlen) < 0) { + perror("munmap"); + exit(1); + } + free(ep); +} + +/* + * elf_symname - Return ASCII name of a static symbol + */ +char *elf_symname(Elf_obj *ep, Elf64_Sym *sym) +{ + return &ep->strtab[sym->st_name]; +} + +/* + * elf_symname - Return ASCII name of a static symbol + */ +char *elf_symname32(Elf_obj32 *ep, Elf32_Sym *sym) +{ + return &ep->strtab[sym->st_name]; +} + + +/* + * elf_dsymname - Return ASCII name of a dynamic symbol + */ +char *elf_dsymname(Elf_obj *ep, Elf64_Sym *sym) +{ + return &ep->dstrtab[sym->st_name]; +} + +/* + * elf_firstsym - Return ptr to first symbol in static symbol table + */ +Elf64_Sym *elf_firstsym(Elf_obj *ep) +{ + return ep->symtab; +} + +/* + * elf_firstsym - Return ptr to first symbol in static symbol table + */ +Elf32_Sym *elf_firstsym32(Elf_obj32 *ep) +{ + return ep->symtab; +} + + +/* + * elf_nextsym - Return ptr to next symbol in static symbol table, + * or NULL if no more symbols. + */ +Elf64_Sym *elf_nextsym(Elf_obj *ep, Elf64_Sym *sym) +{ + sym++; + if (sym < ep->symtab_end) { + return sym; + } else { + return NULL; + } +} + +/* + * elf_nextsym - Return ptr to next symbol in static symbol table, + * or NULL if no more symbols. + */ +Elf32_Sym *elf_nextsym32(Elf_obj32 *ep, Elf32_Sym *sym) +{ + sym++; + if (sym < ep->symtab_end) { + return sym; + } else { + return NULL; + } +} + + +/* + * elf_firstdsym - Return ptr to first symbol in dynamic symbol table + */ +Elf64_Sym *elf_firstdsym(Elf_obj *ep) +{ + return ep->dsymtab; +} + +/* + * elf_nextdsym - Return ptr to next symbol in dynamic symbol table, + * of NULL if no more symbols. + */ +Elf64_Sym *elf_nextdsym(Elf_obj *ep, Elf64_Sym *sym) +{ + sym++; + if (sym < ep->dsymtab_end) { + return sym; + } else { + return NULL; + } +} + +/* + * elf_isfunc - Return true if symbol is a static function + */ +int elf_isfunc(Elf_obj *ep, Elf64_Sym *sym) +{ + return ((ELF32_ST_TYPE(sym->st_info) == STT_FUNC) && + (sym->st_shndx != SHT_NULL)); +} + +/* + * elf_isdfunc - Return true if symbol is a dynamic function + */ +int elf_isdfunc(Elf_obj *ep, Elf64_Sym *sym) +{ + return ((ELF32_ST_TYPE(sym->st_info) == STT_FUNC)); +} diff --git a/hw/riscv/htif/elf_symb.h b/hw/riscv/htif/elf_symb.h new file mode 100644 index 0000000..a438c60 --- /dev/null +++ b/hw/riscv/htif/elf_symb.h @@ -0,0 +1,80 @@ +/* + * elf.h - A package for manipulating Elf binaries + * + * Taken from: + * https://www.cs.cmu.edu/afs/cs.cmu.edu/academic/class/15213-f03/www/ftrace/ + * elf.h + * + */ + +#ifndef ELF_H +#define ELF_H + +#include <stdint.h> +#include <elf.h> + +/* + * This is a handle that is created by elf_open and then used by every + * other function in the elf package +*/ +typedef struct { + void *maddr; /* Start of mapped Elf binary segment in memory */ + int mlen; /* Length in bytes of the mapped Elf segment */ + Elf64_Ehdr *ehdr; /* Start of main Elf header (same as maddr) */ + Elf64_Sym *symtab; /* Start of symbol table */ + Elf64_Sym *symtab_end; /* End of symbol table (symtab + size) */ + char *strtab; /* Address of string table */ + Elf64_Sym *dsymtab; /* Start of dynamic symbol table */ + Elf64_Sym *dsymtab_end; /* End of dynamic symbol table (dsymtab + size) */ + char *dstrtab; /* Address of dynamic string table */ +} Elf_obj; + +typedef struct { + void *maddr; /* Start of mapped Elf binary segment in memory */ + int mlen; /* Length in bytes of the mapped Elf segment */ + Elf32_Ehdr *ehdr; /* Start of main Elf header (same as maddr) */ + Elf32_Sym *symtab; /* Start of symbol table */ + Elf32_Sym *symtab_end; /* End of symbol table (symtab + size) */ + char *strtab; /* Address of string table */ + Elf32_Sym *dsymtab; /* Start of dynamic symbol table */ + Elf32_Sym *dsymtab_end; /* End of dynamic symbol table (dsymtab + size) */ + char *dstrtab; /* Address of dynamic string table */ +} Elf_obj32; + +/* + * Create and destroy Elf object handles + */ +Elf_obj *elf_open(const char *filename); +Elf_obj32 *elf_open32(const char *filename); + +void elf_close(Elf_obj *ep); + +/* + * Functions for manipulating static symbols + */ + +/* Returns pointer to the first symbol */ +Elf64_Sym *elf_firstsym(Elf_obj *ep); +Elf32_Sym *elf_firstsym32(Elf_obj32 *ep); + +/* Returns pointer to the next symbol, or NULL if no more symbols */ +Elf64_Sym *elf_nextsym(Elf_obj *ep, Elf64_Sym *sym); +Elf32_Sym *elf_nextsym32(Elf_obj32 *ep, Elf32_Sym *sym); + +/* Return symbol string name */ +char *elf_symname(Elf_obj *ep, Elf64_Sym *sym); +char *elf_symname32(Elf_obj32 *ep, Elf32_Sym *sym); + +/* True if symbol is a function */ +int elf_isfunc(Elf_obj *ep, Elf64_Sym *sym); + +/* + * Corresponding functions for manipulating dynamic symbols + */ +Elf64_Sym *elf_firstdsym(Elf_obj *ep); +Elf64_Sym *elf_nextdsym(Elf_obj *ep, Elf64_Sym *sym); +char *elf_dsymname(Elf_obj *ep, Elf64_Sym *sym); +int elf_isdfunc(Elf_obj *ep, Elf64_Sym *sym); + + +#endif diff --git a/hw/riscv/htif/htif.c b/hw/riscv/htif/htif.c new file mode 100644 index 0000000..6093ebd --- /dev/null +++ b/hw/riscv/htif/htif.c @@ -0,0 +1,423 @@ +/* + * QEMU RISC-V Host Target Interface (HTIF) Emulation + * + * Author: Sagar Karandikar, sag...@eecs.berkeley.edu + * + * This provides HTIF device emulation for QEMU. At the moment this allows + * for identical copies of bbl/linux to run on both spike and QEMU. + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "hw/char/serial.h" +#include "sysemu/char.h" +#include "hw/riscv/htif/htif.h" +#include "qemu/timer.h" +#include "exec/address-spaces.h" +#include "qemu/error-report.h" +#include <fcntl.h> +#include <unistd.h> +#include <sys/stat.h> +#include <inttypes.h> +#include "elf_symb.h" + +#define ENABLE_CHARDEV +/*#define DEBUG_CHARDEV */ +/*#define DEBUG_HTIF */ + +#ifdef ENABLE_CHARDEV +/* + * Called by the char dev to see if HTIF is ready to accept input. + */ +static int htif_can_recv(void *opaque) +{ + return 1; +} + +/* + * Called by the char dev to supply input to HTIF console. + * We assume that we will receive one character at a time. + */ +static void htif_recv(void *opaque, const uint8_t *buf, int size) +{ + if (size != 1) { + return; + } + + HTIFState *htifstate = opaque; + + #ifdef DEBUG_CHARDEV + if (htifstate->env->mfromhost != 0x0) { + fprintf(stderr, "recv handler: fromhost was not ready to \ + accept input\n"); + fprintf(stderr, "recv handler: prev value was: %016lx\n", + htifstate->env->mfromhost); + } + #endif + + uint64_t val_written = htifstate->pending_read; + uint64_t resp = 0x100 | *buf; + + htifstate->env->mfromhost = (val_written >> 48 << 48) | (resp << 16 >> 16); + qemu_irq_raise(htifstate->irq); +} + +/* + * Called by the char dev to supply special events to the HTIF console. + * Not used for HTIF. + */ +static void htif_event(void *opaque, int event) +{ + #ifdef DEBUG_CHARDEV + fprintf(stderr, "GOT EVENT: %d\n", event); + #endif +} +#endif + +static void htif_pre_save(void *opaque) +{ + return; +} + +static int htif_post_load(void *opaque, int version_id) +{ + return 0; +} + +const VMStateDescription vmstate_htif = { + .name = "htif", + .version_id = 1, + .minimum_version_id = 1, + .pre_save = htif_pre_save, + .post_load = htif_post_load, + .fields = (VMStateField []) { /* TODO what */ + VMSTATE_UINT64(tohost_offset, HTIFState), + VMSTATE_UINT64(fromhost_offset, HTIFState), + VMSTATE_UINT64(tohost_size, HTIFState), + VMSTATE_UINT64(fromhost_size, HTIFState), + VMSTATE_END_OF_LIST() + }, +}; + +static void dma_strcopy(HTIFState *htifstate, char *str, hwaddr phys_addr) +{ + int i = 0; + void *base_copy_addr = htifstate->main_mem_ram_ptr + phys_addr; + while (*(str + i)) { + stb_p((void *)(base_copy_addr + i), *(str + i)); + i++; + } + stb_p((void *)(base_copy_addr + i), 0); /* store null term */ +} + +static void htif_handle_tohost_write(HTIFState *htifstate, uint64_t val_written) +{ + #ifdef DEBUG_HTIF + fprintf(stderr, "TOHOST WRITE WITH val 0x%016lx\n", val_written); + #endif + + uint8_t device = val_written >> 56; + uint8_t cmd = val_written >> 48; + uint64_t payload = val_written & 0xFFFFFFFFFFFFULL; + + uint64_t addr = payload >> 8; + hwaddr real_addr = (hwaddr)addr; + uint8_t what = payload & 0xFF; + int resp; + + resp = 0; /* stop gcc complaining */ + #ifdef DEBUG_HTIF + fprintf(stderr, "mtohost write:\n-device: %d\n-cmd: %d\n-what: %02lx\n\ + -payload: %016lx\n", device, cmd, payload & 0xFF, payload); + #endif + + /* + * Currently, there is a fixed mapping of devices: + * 0: riscv-tests Pass/Fail Reporting Only (no syscall proxy) + * 1: Console + */ + if (unlikely(device == 0x0)) { + /* frontend syscall handler, only test pass/fail support */ + if (cmd == 0x0) { + #ifdef DEBUG_HTIF + fprintf(stderr, "frontend syscall handler\n"); + #endif + if (payload & 0x1) { + /* test result */ + if (payload >> 1) { + printf("*** FAILED *** (exitcode = %016lx)\n", + payload >> 1); + } else { + printf("TEST PASSED\n"); + } + exit(payload >> 1); + } + fprintf(stderr, "pk syscall proxy not supported\n"); + } else if (cmd == 0xFF) { + /* use what */ + if (what == 0xFF) { + #ifdef DEBUG_HTIF + fprintf(stderr, "registering name\n"); + #endif + dma_strcopy(htifstate, (char *)"syscall_proxy", real_addr); + } else if (what == 0x0) { + #ifdef DEBUG_HTIF + fprintf(stderr, "registering syscall cmd\n"); + #endif + dma_strcopy(htifstate, (char *)"syscall", real_addr); + } else { + #ifdef DEBUG_HTIF + fprintf(stderr, "registering end of cmds list\n"); + #endif + dma_strcopy(htifstate, (char *)"", real_addr); + } + resp = 0x1; /* write to indicate device name placed */ + } else { + fprintf(stderr, "HTIF device %d: UNKNOWN COMMAND\n", device); + exit(1); + } + } else if (likely(device == 0x1)) { + /* HTIF Console */ + if (cmd == 0x0) { + /* this should be a queue, but not yet implemented as such */ + htifstate->pending_read = val_written; + htifstate->env->mtohost = 0; /* clear to indicate we read */ + return; + } else if (cmd == 0x1) { + #ifdef ENABLE_CHARDEV + qemu_chr_fe_write(htifstate->chr, (uint8_t *)&payload, 1); + #endif + resp = 0x100 | (uint8_t)payload; + } else if (cmd == 0xFF) { + /* use what */ + if (what == 0xFF) { + #ifdef DEBUG_HTIF + fprintf(stderr, "registering name\n"); + #endif + dma_strcopy(htifstate, (char *)"bcd", real_addr); + } else if (what == 0x0) { + #ifdef DEBUG_HTIF + fprintf(stderr, "registering read cmd\n"); + #endif + dma_strcopy(htifstate, (char *)"read", real_addr); + } else if (what == 0x1) { + #ifdef DEBUG_HTIF + fprintf(stderr, "registering write cmd\n"); + #endif + dma_strcopy(htifstate, (char *)"write", real_addr); + } else { + #ifdef DEBUG_HTIF + fprintf(stderr, "registering end of cmds list\n"); + #endif + dma_strcopy(htifstate, (char *)"", real_addr); + } + resp = 0x1; /* write to indicate device name placed */ + } else { + fprintf(stderr, "HTIF device %d: UNKNOWN COMMAND\n", device); + exit(1); + } + /* all other devices */ + } else if (device == 0x2 && cmd == 0xFF && what == 0xFF) { + #ifdef DEBUG_HTIF + fprintf(stderr, "registering no device as last\n"); + #endif + stb_p((void *)(htifstate->main_mem_ram_ptr + real_addr), 0); + resp = 0x1; /* write to indicate device name placed */ + } else { + fprintf(stderr, "HTIF UNKNOWN DEVICE OR COMMAND!\n"); + fprintf(stderr, "-device: %d\n-cmd: %d\n-what: %02lx\n-payload: \ + %016lx\n", device, cmd, payload & 0xFF, payload); + exit(1); + } + while (!htifstate->fromhost_inprogress && + htifstate->env->mfromhost != 0x0) { + /* wait */ + } + htifstate->env->mfromhost = (val_written >> 48 << 48) | (resp << 16 >> 16); + htifstate->env->mtohost = 0; /* clear to indicate we read */ + if (htifstate->env->mfromhost != 0) { + /* raise HTIF interrupt */ + qemu_irq_raise(htifstate->irq); + } +} + +#define TOHOST_OFFSET1 (htifstate->tohost_offset) +#define TOHOST_OFFSET2 (htifstate->tohost_offset + 4) +#define FROMHOST_OFFSET1 (htifstate->fromhost_offset) +#define FROMHOST_OFFSET2 (htifstate->fromhost_offset + 4) + +/* CPU wants to read an HTIF register */ +static uint64_t htif_mm_read(void *opaque, hwaddr addr, unsigned size) +{ + HTIFState *htifstate = opaque; + if (addr == TOHOST_OFFSET1) { + return htifstate->env->mtohost & 0xFFFFFFFF; + } else if (addr == TOHOST_OFFSET2) { + return (htifstate->env->mtohost >> 32) & 0xFFFFFFFF; + } else if (addr == FROMHOST_OFFSET1) { + return htifstate->env->mfromhost & 0xFFFFFFFF; + } else if (addr == FROMHOST_OFFSET2) { + return (htifstate->env->mfromhost >> 32) & 0xFFFFFFFF; + } else { + printf("Invalid htif register address %016lx\n", (uint64_t)addr); + exit(1); + } +} + +/* CPU wrote to an HTIF register */ +static void htif_mm_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + HTIFState *htifstate = opaque; + if (addr == TOHOST_OFFSET1) { + if (htifstate->env->mtohost == 0x0) { + htifstate->allow_tohost = 1; + htifstate->env->mtohost = value & 0xFFFFFFFF; + if (unlikely(htifstate->tohost_size != 8)) { +#ifdef DEBUG_HTIF + fprintf(stderr, "Using non-8 htif width\n"); +#endif + /* tests have a zero tohost size in elf symb tab and they + use sw to write to mm_write, so TOHOST_OFFSET2 will never + be written to. Thus, initiate side effects here. */ + htif_handle_tohost_write(htifstate, htifstate->env->mtohost); + } + } else { + htifstate->allow_tohost = 0; + } + } else if (addr == TOHOST_OFFSET2) { + if (htifstate->allow_tohost) { + htifstate->env->mtohost |= value << 32; + htif_handle_tohost_write(htifstate, htifstate->env->mtohost); + } + } else if (addr == FROMHOST_OFFSET1) { + htifstate->fromhost_inprogress = 1; + htifstate->env->mfromhost = value & 0xFFFFFFFF; + } else if (addr == FROMHOST_OFFSET2) { + htifstate->env->mfromhost |= value << 32; + if (htifstate->env->mfromhost == 0x0) { + qemu_irq_lower(htifstate->irq); + } + htifstate->fromhost_inprogress = 0; + } else { + printf("Invalid htif register address %016lx\n", (uint64_t)addr); + exit(1); + } +} + +static const MemoryRegionOps htif_mm_ops[3] = { + [DEVICE_LITTLE_ENDIAN] = { + .read = htif_mm_read, + .write = htif_mm_write, + .endianness = DEVICE_LITTLE_ENDIAN, + }, +}; + +HTIFState *htif_mm_init(MemoryRegion *address_space, + const char *kernel_filename, qemu_irq irq, MemoryRegion *main_mem, + CPURISCVState *env, CharDriverState *chr) +{ + uint64_t fromhost_addr = 0; + uint64_t fromhost_size = 0; /* for pk vs tests */ + uint64_t tohost_addr = 0; + uint64_t tohost_size = 0; /* for pk vs tests */ + + /* get fromhost/tohost addresses from the ELF, as spike/fesvr do */ + if (NULL != kernel_filename) { +#if defined(TARGET_RISCV64) + Elf_obj *e = elf_open(kernel_filename); +#else + Elf_obj32 *e = elf_open32(kernel_filename); +#endif + + const char *fromhost = "fromhost"; + const char *tohost = "tohost"; + +#if defined(TARGET_RISCV64) + Elf64_Sym *curr_sym = elf_firstsym(e); +#else + Elf32_Sym *curr_sym = elf_firstsym32(e); +#endif + while (curr_sym) { +#if defined(TARGET_RISCV64) + char *symname = elf_symname(e, curr_sym); +#else + char *symname = elf_symname32(e, curr_sym); +#endif + + if (strcmp(fromhost, symname) == 0) { + /* get fromhost addr */ + fromhost_addr = curr_sym->st_value; + fromhost_size = curr_sym->st_size; /* this is correctly set to 8 + by pk */ + } else if (strcmp(tohost, symname) == 0) { + /* get tohost addr */ + tohost_addr = curr_sym->st_value; + tohost_size = curr_sym->st_size; /* this is correctly set to 8 + by pk */ + } +#if defined(TARGET_RISCV64) + curr_sym = elf_nextsym(e, curr_sym); +#else + curr_sym = elf_nextsym32(e, curr_sym); +#endif + } + } + + /* now setup HTIF device */ + HTIFState *htifstate; + + htifstate = g_malloc0(sizeof(HTIFState)); + htifstate->irq = irq; + htifstate->address_space = address_space; + htifstate->main_mem = main_mem; + htifstate->main_mem_ram_ptr = memory_region_get_ram_ptr(main_mem); + htifstate->env = env; + htifstate->chr = chr; + htifstate->pending_read = 0; + htifstate->allow_tohost = 0; + htifstate->fromhost_inprogress = 0; + htifstate->fromhost_size = fromhost_size; + htifstate->tohost_size = tohost_size; + +#ifdef ENABLE_CHARDEV + qemu_chr_add_handlers(htifstate->chr, htif_can_recv, htif_recv, htif_event, + htifstate); +#endif + + uint64_t base = tohost_addr < fromhost_addr ? tohost_addr : fromhost_addr; + uint64_t second = tohost_addr < fromhost_addr ? fromhost_addr : tohost_addr; + uint64_t regionwidth = second - base + 8; + + htifstate->tohost_offset = base == tohost_addr ? 0 : tohost_addr - + fromhost_addr; + htifstate->fromhost_offset = base == fromhost_addr ? 0 : fromhost_addr - + tohost_addr; + + vmstate_register(NULL, base, &vmstate_htif, htifstate); + + memory_region_init_io(&htifstate->io, NULL, + &htif_mm_ops[DEVICE_LITTLE_ENDIAN], + htifstate, "htif", regionwidth); + memory_region_add_subregion(address_space, base, &htifstate->io); + + return htifstate; +} diff --git a/include/hw/riscv/htif/htif.h b/include/hw/riscv/htif/htif.h new file mode 100644 index 0000000..3499947 --- /dev/null +++ b/include/hw/riscv/htif/htif.h @@ -0,0 +1,61 @@ +/* + * QEMU RISCV Host Target Interface (HTIF) Emulation + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef HW_RISCV_HTIF_H +#define HW_RISCV_HTIF_H 1 + +#include "hw/hw.h" +#include "sysemu/sysemu.h" +#include "exec/memory.h" +#include "target-riscv/cpu.h" + +typedef struct HTIFState HTIFState; + +struct HTIFState { + int allow_tohost; + int fromhost_inprogress; + + hwaddr tohost_offset; + hwaddr fromhost_offset; + uint64_t tohost_size; + uint64_t fromhost_size; + qemu_irq irq; /* host interrupt line */ + MemoryRegion io; + MemoryRegion *address_space; + MemoryRegion *main_mem; + void *main_mem_ram_ptr; + + CPURISCVState *env; + CharDriverState *chr; + uint64_t pending_read; +}; + +extern const VMStateDescription vmstate_htif; +extern const MemoryRegionOps htif_io_ops; + +/* legacy pre qom */ +HTIFState *htif_mm_init(MemoryRegion *address_space, + const char *kernel_filename, qemu_irq irq, + MemoryRegion *main_mem, + CPURISCVState *env, CharDriverState *chr); + +#endif -- 2.9.3