├── .gitignore ├── LICENSE ├── Makefile ├── README ├── TODO └── src ├── cpp ├── mini_c_spike.c ├── mini_spike.cc ├── tv_spike.cc ├── tv_spike.h ├── tv_spike_intf.c └── tv_spike_intf.h ├── l3 ├── riscv-print.spec └── riscv.spec └── sml ├── l3riscv.h ├── l3riscv.mlb ├── lib ├── Elf.sig └── Elf.sml ├── mlton_run.sml ├── model.sml ├── oracle.sig ├── poly_model.sml ├── poly_run.sml └── poly_spike.sml /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2015-2018 SRI International. 2 | 3 | This software was developed by SRI International and the University of 4 | Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-11-C-0249 5 | ("MRC2"), as part of the DARPA MRC research programme, and under 6 | DARPA/AFRL contract FA8750-10-C-0237 ("CTSRD"), as part of the DARPA 7 | CRASH research programme, and under DARPA/AFRL contract FA8650-18-C-7809 8 | ("CIFV"), and under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of 9 | the DARPA SSITH research programme. 10 | 11 | 12 | -------------------------------------------------------------------- 13 | The L3 MIPS simulator from which this RISCV port was derived has the 14 | following copyright notice: 15 | 16 | -- (c) Anthony Fox, University of Cambridge 17 | -- (c) Alexandre Joannou, University of Cambridge 18 | 19 | Redistribution and use in source and binary forms, with or without 20 | modification, are permitted provided that the following conditions are 21 | met: 22 | 23 | * Redistributions of source code must retain the above copyright 24 | notice, this list of conditions and the following disclaimer. 25 | 26 | * Redistributions in binary form must reproduce the above copyright 27 | notice, this list of conditions and the following disclaimer in 28 | the documentation and/or other materials provided with the 29 | distribution. 30 | 31 | * The names of the copyright holders and contributors may not be 32 | used to endorse or promote products derived from this software 33 | without specific prior written permission. 34 | 35 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 36 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 37 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 38 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 39 | HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 40 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 41 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 42 | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 43 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 44 | TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 45 | USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 46 | DAMAGE. 47 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ######################################## 2 | # Makefile for the L3 RISCV simulator ## 3 | ######################################## 4 | 5 | TOPDIR=$(shell realpath .) 6 | 7 | L3SRCDIR=src/l3 8 | L3SRCBASE+=riscv-print.spec 9 | L3SRCBASE+=riscv.spec 10 | L3SRC=$(patsubst %, $(L3SRCDIR)/%, $(L3SRCBASE)) 11 | L3LIBDIR?=$(shell l3 --lib-path) 12 | 13 | # sml lib sources 14 | ####################################### 15 | SMLSRCDIR=src/sml 16 | SMLLIBDIR=src/sml/lib 17 | SMLLIBSRC=Elf.sig Elf.sml 18 | SMLLIB=$(patsubst %, $(SMLLIBDIR)/%, $(SMLLIBSRC)) 19 | 20 | # generating the sml source list 21 | ####################################### 22 | SMLSRCBASE+=riscv.sig riscv.sml 23 | SMLSRCBASE+=model.sml mlton_run.sml poly_run.sml oracle.sig poly_spike.sml poly_model.sml 24 | SMLSRCBASE+=l3riscv.mlb 25 | MLBFILE=l3riscv.mlb 26 | SMLSRC=$(patsubst %, $(SMLSRCDIR)/%, $(SMLSRCBASE)) 27 | 28 | # generating the IL source 29 | ####################################### 30 | ILSRCDIR=src/il 31 | 32 | # generating the HOL source 33 | ####################################### 34 | HOLSRCDIR=src/hol 35 | 36 | # MLton compiler options 37 | ####################################### 38 | MLTON = mlton 39 | MLTON_OPTS = -inline 1000 -default-type intinf -verbose 1 40 | MLTON_OPTS += -default-ann 'allowFFI true' -export-header ${SMLSRCDIR}/riscv_ffi.h 41 | MLTON_LIB_OPTS = -mlb-path-var 'L3LIBDIR '$(L3LIBDIR) 42 | 43 | # PolyML compiler options 44 | ####################################### 45 | POLYC = polyc 46 | 47 | # Spike-based Tandem Verification library 48 | ####################################### 49 | # Set ENABLE_TVSPIKE to 1 to enable verification against Spike. 50 | # Ensure that you have the RISCV environment variable correctly 51 | # set as when working with riscv-tools. 52 | ENABLE_TVSPIKE = 1 53 | CSRCDIR=src/cpp 54 | TVSPIKE_SRCBASE = tv_spike_intf.h tv_spike_intf.c tv_spike.h tv_spike.cc 55 | TVSPIKE_SRC = $(patsubst %, $(CSRCDIR)/%, $(TVSPIKE_SRCBASE)) 56 | TVSPIKE_INC = -I $(CSRCDIR) 57 | TVSPIKE_INC += -I $(RISCV)/include 58 | TVSPIKE_LIBS = -L $(RISCV)/lib -lfesvr -lriscv -Wl,-rpath=$(RISCV)/lib 59 | CSPIKE_INC = -I $(CSRCDIR) 60 | CSPIKE_LIBS = -L $(TOPDIR) -ltv_spike -Wl,-rpath=$(TOPDIR) 61 | CFLAGS = -g -Wall -Wextra -Wno-unused-parameter 62 | # One of the below options may be needed on some platforms 63 | #CCOPTS = 64 | CCOPTS = -std=c++11 65 | #CCOPTS = -std=gnu++11 66 | 67 | # make targets 68 | ####################################### 69 | 70 | all: l3riscv.poly ilspec holspec c 71 | c: libtv_spike.so mini_spike mini_c_spike 72 | 73 | .PHONY: c c_clean clean 74 | 75 | ifeq ($(ENABLE_TVSPIKE),1) 76 | all: libtv_spike.so mini_spike mini_c_spike 77 | endif 78 | 79 | ${SMLSRCDIR}/riscv.sig ${SMLSRCDIR}/riscv.sml: ${L3SRC} 80 | echo 'SMLExport.spec ("${L3SRC}", "${SMLSRCDIR}/riscv intinf")' | l3 81 | 82 | ifeq ($(ENABLE_TVSPIKE),1) 83 | l3riscv.poly: libtv_spike.so 84 | endif 85 | l3riscv.poly: ${SMLLIB} ${SMLSRC} Makefile 86 | $(POLYC) -o $@ ${SMLSRCDIR}/poly_run.sml 87 | 88 | # l3riscv.mlton: ${SMLLIB} ${SMLSRC} Makefile 89 | # $(MLTON) $(MLTON_OPTS) \ 90 | # $(MLTON_LIB_OPTS) \ 91 | # -output $@ ${SMLSRCDIR}/$(MLBFILE) $(L3LIBDIR)/sse_float.c $(L3LIBDIR)/mlton_sse_float.c 92 | 93 | libtv_spike.so: ${TVSPIKE_SRC} Makefile 94 | g++ ${CFLAGS} ${CCOPTS} -o $@ -shared -fPIC ${TVSPIKE_INC} ${TVSPIKE_LIBS} ${TVSPIKE_SRC} 95 | 96 | mini_spike: ${CSRCDIR}/mini_spike.cc ${TVSPIKE_SRC} Makefile 97 | g++ ${CFLAGS} ${CCOPTS} ${TVSPIKE_INC} -o $@ $< ${TVSPIKE_SRC} ${TVSPIKE_LIBS} 98 | 99 | mini_c_spike: ${CSRCDIR}/mini_c_spike.c ${TVSPIKE_SRC} libtv_spike.so Makefile 100 | gcc ${CFLAGS} -o $@ $< ${CSPIKE_INC} ${CSPIKE_LIBS} ${TVSPIKE_LIBS} 101 | 102 | #libl3riscv.so: ${SMLLIB} ${SMLSRC} Makefile 103 | # $(MLTON) $(MLTON_OPTS) \ 104 | # $(MLTON_LIB_OPTS) \ 105 | # -format library \ 106 | # -output $@ ${SMLSRCDIR}/$(MLBFILE) ${SMLSRCDIR}/riscv_cissr.c ${SMLSRCDIR}/riscv_oracle.c $(L3LIBDIR)/sse_float.c $(L3LIBDIR)/mlton_sse_float.c 107 | 108 | ilspec: ${L3SRC} 109 | mkdir -p $(ILSRCDIR) 110 | echo 'ILExport.spec ("${L3SRC}", "${ILSRCDIR}/riscv")' | l3 111 | 112 | holspec: ${L3SRC} 113 | mkdir -p $(HOLSRCDIR) 114 | echo 'HolExport.spec ("${L3SRC}", "${HOLSRCDIR}/riscv")' | l3 115 | 116 | c_clean: 117 | rm -f libtv_spike.so mini_spike mini_c_spike libl3riscv.so 118 | 119 | clean: c_clean 120 | rm -f l3riscv.poly l3riscv.mlton 121 | rm -f ${SMLSRCDIR}/riscv.sig ${SMLSRCDIR}/riscv.sml 122 | rm -f ${ILSRCDIR}/riscv.l3 123 | rm -f ${HOLSRCDIR}/riscvLib.sig ${HOLSRCDIR}/riscvLib.sml ${HOLSRCDIR}/riscvScript.sml 124 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | L3 Specification of the RISC-V ISA 2 | ---------------------------------- 3 | 4 | For more on RISC-V, see http://riscv.org. 5 | 6 | This L3 specification can be used to generate an executable interpreter of 7 | RISC-V binaries, similar to the Spike simulator for RISC-V. 8 | 9 | It can also be used to generate a machine-readable specification in a simple 10 | sexpr-based syntax. 11 | 12 | Dependencies 13 | ------------ 14 | 15 | This model needs a recent Poly/ML compiler (at least version 5.7.2 if released, 16 | or built from the latest source at github.com/polyml), and L3 itself. 17 | 18 | L3 is available from: http://www.cl.cam.ac.uk/~acjf3/l3 19 | Ensure that you are using a recent version (L3-2018-02-09 or later). 20 | 21 | The tandem-verification to Spike needs a recent version of Spike installed, and 22 | the RISCV environment variable set to its installation directory. 23 | 24 | The model supports the following combinations of Spike configuration flags: 25 | --[en|dis]able-dirty --[en|dis]able-misaligned 26 | 27 | 28 | Building 29 | -------- 30 | 31 | - Ensure that l3 is in your path. 32 | 33 | - Ensure that the RISCV environment variable is correctly defined. 34 | 35 | - $ make 36 | 37 | This will build the interpreter in 'l3riscv.poly' when compiled with Poly/ML. 38 | 39 | Using the interpreter 40 | --------------------- 41 | 42 | - Execute a RISC-V ELF binary: 43 | 44 | $ ./l3riscv --trace 1 45 | 46 | To execute this in lock-step with a Spike back-end: 47 | 48 | $ ./l3riscv --trace 1 --check true 49 | 50 | The execution will stop with a failure at the first discrepancy between the 51 | execution of the model and that of Spike. 52 | 53 | - Disassemble a RISC-V ELF binary: 54 | 55 | $ ./l3riscv --dis true 56 | 57 | Note that the interpreter cannot currently boot an OS since the SBI spec for 58 | RISC-V has not yet been released. 59 | 60 | Generating a machine-readable specification 61 | ------------------------------------------- 62 | 63 | $ make ilspec 64 | 65 | The specification generated will be in src/il/riscv.l3. 66 | 67 | Known Issues 68 | ------------ 69 | 70 | - It passes most 64-bit M-, S-, and U-mode tests, and the 32-bit M- and U-mode 71 | tests, in Spike-checked mode. 72 | 73 | It does not implement the debug-module registers, and hence fails the 74 | rv{32,64}mi-p-breakpoint tests. 75 | 76 | - It does not yet boot a full OS since it has no device models. 77 | 78 | Credits 79 | ------- 80 | 81 | This model started out as a port of the L3 MIPS model to RISC-V. The original 82 | l3mips model is at https://github.com/acjf3/l3mips 83 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | - hpm counter and event csrs 2 | 3 | - hypervisor mode 4 | 5 | - MMIO 6 | 7 | - PMA/PMP 8 | 9 | - PLIC 10 | 11 | -------------------------------------------------------------------------------- /src/cpp/mini_c_spike.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 SRI International. 5 | * All rights reserved. 6 | * 7 | * This software was developed by SRI International and the University of 8 | * Cambridge Computer Laboratory (Department of Computer Science and 9 | * Technology) under DARPA/AFRL contract FA8650-18-C-7809 ("CIFV"). 10 | * 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions 13 | * are met: 14 | * 1. Redistributions of source code must retain the above copyright 15 | * notice, this list of conditions and the following disclaimer. 16 | * 2. Redistributions in binary form must reproduce the above copyright 17 | * notice, this list of conditions and the following disclaimer in the 18 | * documentation and/or other materials provided with the distribution. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 | * SUCH DAMAGE. 31 | */ 32 | 33 | /* A lightweight C-based spike wrapper to test the C bindings. */ 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #include "tv_spike_intf.h" 43 | 44 | uint64_t ram_size = (64LL << 20); 45 | 46 | void run_elf(const char *isa, const char *filename) 47 | { 48 | struct tv_spike_t *s = tv_init(isa, ram_size, 1); 49 | tv_set_verbose(s, 1); 50 | tv_set_dtb_in_rom(s, 1); 51 | tv_load_elf(s, filename); 52 | tv_reset(s); 53 | 54 | { size_t insns_per_tick = tv_get_insns_per_tick(s); 55 | size_t cnt = 0; 56 | while (!tv_is_done(s)) { 57 | tv_step(s); 58 | cnt++; 59 | if (cnt == insns_per_tick) { 60 | tv_tick_clock(s); 61 | tv_step_io(s); 62 | } 63 | } 64 | } 65 | tv_free(s); 66 | } 67 | 68 | void print_usage(const char *argv0) 69 | { 70 | fprintf(stderr, "Usage: %s \n", argv0); 71 | exit(0); 72 | } 73 | 74 | void print_dts(void) 75 | { 76 | size_t dts_len = 0; 77 | struct tv_spike_t *s = tv_init("RV64IMAC", ram_size, 0); 78 | tv_get_dts(s, NULL, &dts_len); 79 | if (dts_len > 0) { 80 | unsigned char *dts = (unsigned char *)malloc(dts_len + 1); 81 | dts[dts_len] = '\0'; 82 | tv_get_dts(s, dts, &dts_len); 83 | fprintf(stdout, "%s\n", dts); 84 | } 85 | exit(0); 86 | } 87 | 88 | int is_file(const char *f) 89 | { 90 | struct stat st; 91 | return (!stat(f, &st) && (st.st_mode & S_IFMT) == S_IFREG); 92 | } 93 | 94 | int main(int argc, const char **argv) 95 | { 96 | if (argc != 2) print_usage(argv[0]); 97 | if (!is_file(argv[1])) print_usage(argv[0]); 98 | 99 | run_elf("RV64IMAFDC", argv[1]); 100 | } 101 | -------------------------------------------------------------------------------- /src/cpp/mini_spike.cc: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 SRI International. 5 | * All rights reserved. 6 | * 7 | * This software was developed by SRI International and the University of 8 | * Cambridge Computer Laboratory (Department of Computer Science and 9 | * Technology) under DARPA/AFRL contract FA8650-18-C-7809 ("CIFV"). 10 | * 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions 13 | * are met: 14 | * 1. Redistributions of source code must retain the above copyright 15 | * notice, this list of conditions and the following disclaimer. 16 | * 2. Redistributions in binary form must reproduce the above copyright 17 | * notice, this list of conditions and the following disclaimer in the 18 | * documentation and/or other materials provided with the distribution. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 | * SUCH DAMAGE. 31 | */ 32 | 33 | /* A simple Spike-based RISC-V simulator. */ 34 | 35 | #include 36 | #include 37 | #include "tv_spike.h" 38 | 39 | static int run(tv_spike_t *tv) 40 | { 41 | int code; 42 | tv->reset(); 43 | while (!tv->exited(code)) { 44 | tv->step(tv->INSNS_PER_RTC_TICK); 45 | if (!tv->exited(code)) { 46 | tv->tick(1); 47 | tv->step_io(); 48 | } 49 | } 50 | fprintf(stderr, "Exited with code %d.\n", code); 51 | return code; 52 | } 53 | 54 | static void run_elf(const char *isa, const char *file, uint64_t ram_size, bool debug) 55 | { 56 | tv_spike_t s(isa, ram_size, debug); 57 | s.dtb_in_rom(true); 58 | s.init_elf(file); 59 | exit(run(&s)); 60 | } 61 | 62 | static void help() 63 | { 64 | fprintf(stderr, "Usage: mini_spike [--isa=] [--ram-size=MB] [--dump-dts] [--dump-dtb] [--show-config] [--debug-log] \n"); 65 | exit(0); 66 | } 67 | 68 | static void print_dts(const char *isa, uint64_t ram_size) 69 | { 70 | tv_spike_t s(isa, ram_size, false); 71 | fprintf(stdout, "%s", s.get_dts().c_str()); 72 | } 73 | 74 | static void print_dtb(const char *isa, uint64_t ram_size) 75 | { 76 | tv_spike_t s(isa, ram_size, false); 77 | std::string dtb = s.get_dtb(); 78 | write(1, dtb.c_str(), dtb.length()); 79 | } 80 | 81 | static void print_cfg(const char *isa, uint64_t ram_size) 82 | { 83 | tv_spike_t s(isa, ram_size, false); 84 | fprintf(stdout, "\nisa: %s\n", isa); 85 | fprintf(stdout, "--enable-dirty: %s\n", s.is_dirty_enabled() ? "on" : "off"); 86 | fprintf(stdout, "--enable-misaligned: %s\n", s.is_misaligned_enabled() ? "on" : "off"); 87 | fprintf(stdout, "--ram-size: %ld (0x%lx) MB (%ld)\n", s.ram_size() >> 20, s.ram_size() >> 20, s.ram_size()); 88 | } 89 | 90 | int main(int argc, char **argv) 91 | { 92 | bool dump_dts = false; 93 | bool dump_dtb = false; 94 | bool show_cfg = false; 95 | 96 | bool debug_log = false; 97 | uint64_t ram_size = 64LL << 20; 98 | 99 | const char *isa = "RV64IMAFDC"; 100 | option_parser_t parser; 101 | parser.help(&help); 102 | 103 | parser.option('h', 0, 0, [&](const char* s){help();}); 104 | parser.option(0, "dump-dts", 0, [&](const char *s){dump_dts = true;}); 105 | parser.option(0, "dump-dtb", 0, [&](const char *s){dump_dtb = true;}); 106 | parser.option(0, "show-config", 0, [&](const char *s){show_cfg = true;}); 107 | parser.option(0, "ram-size", 1, [&](const char *s){ram_size = atol(s) * 1024 * 1024;}); 108 | parser.option(0, "debug-log", 0, [&](const char *s){debug_log = true;}); 109 | parser.option(0, "isa", 1, [&](const char* s){isa = s;}); 110 | const char* const* file = parser.parse(argv); 111 | 112 | if (dump_dts) print_dts(isa, ram_size); 113 | else if (dump_dtb) print_dtb(isa, ram_size); 114 | else if (show_cfg) print_cfg(isa, ram_size); 115 | else if (file && file[0] && file[0][0]) 116 | run_elf(isa, file[0], ram_size, debug_log); 117 | else help(); 118 | } 119 | -------------------------------------------------------------------------------- /src/cpp/tv_spike.cc: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 SRI International. 5 | * All rights reserved. 6 | * 7 | * This software was developed by SRI International and the University of 8 | * Cambridge Computer Laboratory (Department of Computer Science and 9 | * Technology) under DARPA/AFRL contract FA8650-18-C-7809 ("CIFV"). 10 | * 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions 13 | * are met: 14 | * 1. Redistributions of source code must retain the above copyright 15 | * notice, this list of conditions and the following disclaimer. 16 | * 2. Redistributions in binary form must reproduce the above copyright 17 | * notice, this list of conditions and the following disclaimer in the 18 | * documentation and/or other materials provided with the distribution. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 | * SUCH DAMAGE. 31 | */ 32 | 33 | #include "tv_spike.h" 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | tv_spike_t::tv_spike_t(const char *isa, uint64_t ram_size, bool debug) 40 | : memif(this), tohost_addr(0), fromhost_addr(0), dtb_inited(false), 41 | entry(-1), has_exited(false), exit_code(0), 42 | verbose_verify(true), insert_dts(false), debug_log(debug) 43 | { 44 | cpu = new processor_t(isa, this, /*hartid*/ 0, /*halted*/ false); 45 | procs = std::vector(1, cpu); 46 | 47 | debug_mmu = new mmu_t(this, /*processor_t**/NULL); 48 | dram = new mem_t(ram_size); 49 | mem_regions = std::vector>(1, std::make_pair(reg_t(DRAM_BASE), dram)); 50 | 51 | for (auto& x : mem_regions) { 52 | if (debug_log) 53 | std::cerr << "Adding mem device @0x" << std::hex << x.first 54 | << " size:0x" << x.second->size() << std::endl; 55 | bus.add_device(x.first, x.second); 56 | } 57 | /* MMIO */ 58 | clint.reset(new clint_t(procs)); 59 | bus.add_device(CLINT_BASE, clint.get()); 60 | if (debug_log) 61 | std::cerr << "Adding clint device @0x" << std::hex << CLINT_BASE 62 | << " size:0x" << clint.get()->size() << std::endl; 63 | 64 | /* HTIF: devices need to be registered in the same order as Spike, since htif 65 | * device identifiers in commands are based on the registration index. Use a 66 | * null-device for now instead of the syscall-proxy. 67 | */ 68 | device_list.register_device(&nulld); 69 | device_list.register_device(&bcd); 70 | } 71 | 72 | tv_spike_t::~tv_spike_t() 73 | { 74 | delete dram; 75 | delete debug_mmu; 76 | delete cpu; 77 | } 78 | 79 | int tv_spike_t::is_dirty_enabled() 80 | { 81 | return cpu->get_mmu()->is_dirty_enabled(); 82 | } 83 | 84 | int tv_spike_t::is_misaligned_enabled() 85 | { 86 | return cpu->get_mmu()->is_misaligned_enabled(); 87 | } 88 | 89 | uint64_t tv_spike_t::ram_size() 90 | { 91 | // Needs revision if we have multiple ram regions. 92 | return dram->size(); 93 | } 94 | 95 | void tv_spike_t::dtb_in_rom(bool enable) 96 | { 97 | insert_dts = enable; 98 | } 99 | 100 | reg_t tv_spike_t::init_elf(const char *elf_file) 101 | { 102 | std::map symbols = load_elf(elf_file, &memif, &entry); 103 | if (debug_log) std::cerr << " loading " << elf_file << std::endl; 104 | if (symbols.count("tohost") && symbols.count("fromhost")) { 105 | tohost_addr = symbols["tohost"]; 106 | fromhost_addr = symbols["fromhost"]; 107 | if (debug_log) { 108 | std::cerr << "tohost <- 0x" << std::hex << tohost_addr << std::endl; 109 | std::cerr << "fromhost <- 0x" << std::hex << fromhost_addr << std::endl; 110 | } 111 | } else { 112 | std::cerr << "warning: tohost and fromhost symbols not in ELF;" 113 | << "can't communicate with target" << std::endl; 114 | } 115 | debug_mmu->set_debug(debug_log); 116 | return entry; 117 | } 118 | 119 | void tv_spike_t::set_pc_reg(uint64_t pc) 120 | { 121 | cpu->get_state()->pc = pc; 122 | } 123 | 124 | void tv_spike_t::setup_dtb() 125 | { 126 | dtb_inited = true; 127 | dts = make_dts(INSNS_PER_RTC_TICK, CPU_HZ, procs, mem_regions); 128 | dtb = dts_compile(dts); 129 | } 130 | 131 | const std::string tv_spike_t::get_dts() 132 | { 133 | if (!dtb_inited) setup_dtb(); 134 | return dts; 135 | } 136 | 137 | const std::string tv_spike_t::get_dtb() 138 | { 139 | if (!dtb_inited) setup_dtb(); 140 | return dtb; 141 | } 142 | 143 | void tv_spike_t::reset() 144 | { 145 | reg_t start_pc = get_entry_point(); 146 | const int reset_vec_size = 8; 147 | 148 | uint32_t reset_vec[reset_vec_size] = { 149 | 0x297, // auipc t0,0x0 150 | 0x28593 + (reset_vec_size * 4 << 20), // addi a1, t0, &dtb 151 | 0xf1402573, // csrr a0, mhartid 152 | cpu->get_xlen() == 32 ? 153 | 0x0182a283u : // lw t0,24(t0) 154 | 0x0182b283u, // ld t0,24(t0) 155 | 0x28067, // jr t0 156 | 0, 157 | (uint32_t) (start_pc & 0xffffffff), 158 | (uint32_t) (start_pc >> 32) 159 | }; 160 | 161 | std::vector rom((char*)reset_vec, (char*)reset_vec + sizeof(reset_vec)); 162 | 163 | if (insert_dts) { 164 | /* Imitate the spike platform. */ 165 | std::string dt = get_dtb(); 166 | rom.insert(rom.end(), dt.begin(), dt.end()); 167 | if (debug_log) 168 | std::cerr << "Inserted platform dtb into rom." << std::endl; 169 | } 170 | const int align = 0x1000; 171 | int old_rom_size = rom.size(); 172 | rom.resize((rom.size() + align - 1) / align * align); 173 | 174 | boot_rom.reset(new rom_device_t(rom)); 175 | if (debug_log) { 176 | std::cerr << "Adding rom device @0x" << std::hex << DEFAULT_RSTVEC 177 | << " size:0x" << boot_rom.get()->contents().size() 178 | << " (resized from 0x" << old_rom_size << ")" 179 | << std::endl; 180 | } 181 | 182 | bus.add_device(DEFAULT_RSTVEC, boot_rom.get()); 183 | cpu->set_debug(debug_log); 184 | } 185 | 186 | void tv_spike_t::step(size_t steps) 187 | { 188 | for (size_t i = 0; (i < steps) && !has_exited; i++) { 189 | cpu->step(1); 190 | check_exited(); 191 | } 192 | } 193 | 194 | char* tv_spike_t::addr_to_mem(reg_t addr) 195 | { 196 | auto desc = bus.find_device(addr); 197 | if (auto mem = dynamic_cast(desc.second)) { 198 | if (addr - desc.first < mem->size()) 199 | return mem->contents() + (addr - desc.first); 200 | } 201 | return NULL; 202 | } 203 | 204 | bool tv_spike_t::mmio_load(reg_t addr, size_t len, uint8_t* bytes) 205 | { 206 | if (addr + len < addr) 207 | return false; 208 | bool mmio = bus.load(addr, len, bytes); 209 | return mmio; 210 | } 211 | 212 | bool tv_spike_t::mmio_store(reg_t addr, size_t len, const uint8_t* bytes) 213 | { 214 | if (addr + len < addr) 215 | return false; 216 | bool mmio = bus.store(addr, len, bytes); 217 | return mmio; 218 | } 219 | 220 | void tv_spike_t::read_chunk(addr_t taddr, size_t len, void* dst) 221 | { 222 | assert(len == 8); 223 | auto data = debug_mmu->load_uint64(taddr); 224 | memcpy(dst, &data, sizeof data); 225 | } 226 | 227 | void tv_spike_t::write_chunk(addr_t taddr, size_t len, const void* src) 228 | { 229 | assert(len == 8); 230 | uint64_t data; 231 | memcpy(&data, src, sizeof data); 232 | debug_mmu->store_uint64(taddr, data); 233 | } 234 | 235 | void tv_spike_t::clear_chunk(addr_t taddr, size_t len) 236 | { 237 | char zeros[chunk_max_size()]; 238 | memset(zeros, 0, chunk_max_size()); 239 | 240 | for (size_t pos = 0; pos < len; pos += chunk_max_size()) 241 | write_chunk(taddr + pos, std::min(len - pos, chunk_max_size()), zeros); 242 | } 243 | 244 | void tv_spike_t::step_io(void) 245 | { 246 | uint64_t tohost = memif.read_uint64(tohost_addr); 247 | if (debug_log) std::cerr << "htif::tick 0x" << std::hex << tohost << std::endl; 248 | if (tohost) { 249 | auto enq_func = [](std::queue* q, uint64_t x) { q->push(x); }; 250 | std::function fromhost_callback = 251 | std::bind(enq_func, &fromhost_queue, std::placeholders::_1); 252 | 253 | command_t cmd(memif, tohost, fromhost_callback); 254 | memif.write_uint64(tohost_addr, 0); 255 | device_list.handle_command(cmd); 256 | { /* Special handling of the exit code for riscv-tests, since this is 257 | * normally handled by the syscall-proxy device, and we have a 258 | * null-device instead. 259 | */ 260 | if (cmd.device() == 0 && cmd.payload() & 1) { 261 | has_exited = true; 262 | exit_code = cmd.payload() >> 1; 263 | } 264 | } 265 | } 266 | device_list.tick(); 267 | } 268 | 269 | void tv_spike_t::tick(reg_t inc) 270 | { 271 | clint->increment(inc); 272 | } 273 | 274 | void tv_spike_t::check_exited(void) 275 | { 276 | uint64_t tohost = memif.read_uint64(tohost_addr); 277 | uint8_t dev_cmd = tohost >> 48; 278 | uint64_t payload = tohost << 16 >> 16; 279 | if ((dev_cmd == 0) && (payload & 1)) { 280 | has_exited = true; 281 | exit_code = payload >> 1; 282 | } 283 | } 284 | 285 | bool tv_spike_t::exited(int& code) 286 | { 287 | if (has_exited) code = exit_code; 288 | return has_exited; 289 | } 290 | 291 | bool tv_spike_t::set_verbose(bool enable) 292 | { 293 | bool prev = verbose_verify; 294 | verbose_verify = enable; 295 | return prev; 296 | } 297 | 298 | uint64_t tv_spike_t::check_mask(void) 299 | { 300 | return (cpu->get_xlen() == 32) ? 0x00000000FFFFFFFF : -1; 301 | } 302 | 303 | bool tv_spike_t::check_pc(uint64_t val) 304 | { 305 | uint64_t model_val = cpu->get_state()->pc; 306 | uint64_t mask = check_mask(); 307 | bool chk = (model_val & mask) == (val & mask); 308 | 309 | if (verbose_verify && !chk) 310 | fprintf(stderr, " PC: expected %0" PRIx64 " got %" PRIx64 "\n", 311 | model_val, val); 312 | return chk; 313 | } 314 | 315 | bool tv_spike_t::check_gpr(size_t regno, uint64_t val) 316 | { 317 | uint64_t model_val = uint64_t(-1); 318 | uint64_t mask = check_mask(); 319 | bool chk = false; 320 | if (regno < NXPR) { 321 | model_val = cpu->get_state()->XPR[regno]; 322 | chk = (model_val & mask) == (val & mask); 323 | } 324 | if (verbose_verify && !chk) 325 | fprintf(stderr, " GPR reg %ld: expected %0" PRIx64 " got %" PRIx64 "\n", 326 | regno, model_val, val); 327 | return chk; 328 | } 329 | 330 | static bool isCSR64(size_t regno) 331 | { 332 | // The following CSRs are always 64-bit: mcycle, mtime, minstret. 333 | return (regno == CSR_TIME || regno == CSR_INSTRET || regno == CSR_CYCLE); 334 | } 335 | 336 | bool tv_spike_t::check_csr(size_t regno, uint64_t val) 337 | { 338 | uint64_t model_val = read_csr(regno); 339 | uint64_t mask = isCSR64(regno) ? uint64_t(-1) : check_mask(); 340 | bool chk = (model_val & mask) == (val & mask); 341 | if (verbose_verify && !chk) 342 | fprintf(stderr, " CSR reg %lx (%s): expected %0" PRIx64 " got %" PRIx64 "\n", 343 | regno, csr_name(regno), model_val, val); 344 | return chk; 345 | } 346 | 347 | bool tv_spike_t::check_priv(uint8_t val) 348 | { 349 | uint8_t model_val = cpu->get_state()->prv; 350 | bool chk = model_val == val; 351 | if (verbose_verify && !chk) 352 | fprintf(stderr, " PRIV: expected %0x got %0x\n", 353 | model_val, val); 354 | return chk; 355 | } 356 | 357 | 358 | // This will need to be kept manually in sync with upstream, until we can get a 359 | // suitable api/patch accepted. 360 | reg_t tv_spike_t::read_csr(size_t which) 361 | { 362 | unsigned xlen = cpu->get_xlen(); 363 | unsigned max_xlen = cpu->get_max_xlen(); 364 | state_t* state = cpu->get_state(); 365 | reg_t isa = state->misa; 366 | 367 | if (which >= CSR_HPMCOUNTER3 && which <= CSR_HPMCOUNTER31) 368 | return 0; 369 | if (xlen == 32 && which >= CSR_HPMCOUNTER3H && which <= CSR_HPMCOUNTER31H) 370 | return 0; 371 | if (which >= CSR_MHPMCOUNTER3 && which <= CSR_MHPMCOUNTER31) 372 | return 0; 373 | if (cpu->get_xlen() == 32 && which >= CSR_MHPMCOUNTER3H && which <= CSR_MHPMCOUNTER31H) 374 | return 0; 375 | if (which >= CSR_MHPMEVENT3 && which <= CSR_MHPMEVENT31) 376 | return 0; 377 | switch (which) { 378 | case CSR_FFLAGS: 379 | return state->fflags; 380 | case CSR_FRM: 381 | return state->frm; 382 | case CSR_FCSR: 383 | return (state->fflags << FSR_AEXC_SHIFT) | (state->frm << FSR_RD_SHIFT); 384 | case CSR_INSTRET: 385 | case CSR_CYCLE: 386 | return state->minstret; 387 | case CSR_MINSTRET: 388 | case CSR_MCYCLE: 389 | return state->minstret; 390 | case CSR_INSTRETH: 391 | case CSR_CYCLEH: 392 | return state->minstret >> 32; 393 | case CSR_MINSTRETH: 394 | case CSR_MCYCLEH: 395 | return state->minstret >> 32; 396 | case CSR_SCOUNTEREN: return state->scounteren; 397 | case CSR_MCOUNTEREN: return state->mcounteren; 398 | case CSR_SSTATUS: { 399 | reg_t mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_SPP | SSTATUS_FS 400 | | SSTATUS_XS | SSTATUS_SUM | SSTATUS_UXL; 401 | reg_t sstatus = state->mstatus & mask; 402 | if ((sstatus & SSTATUS_FS) == SSTATUS_FS || 403 | (sstatus & SSTATUS_XS) == SSTATUS_XS) 404 | sstatus |= (xlen == 32 ? SSTATUS32_SD : SSTATUS64_SD); 405 | return sstatus; 406 | } 407 | case CSR_SIP: return state->mip & state->mideleg; 408 | case CSR_SIE: return state->mie & state->mideleg; 409 | case CSR_SEPC: return state->sepc; 410 | case CSR_STVAL: return state->stval; 411 | case CSR_STVEC: return state->stvec; 412 | case CSR_SCAUSE: 413 | if (max_xlen > xlen) 414 | return state->scause | ((state->scause >> (max_xlen-1)) << (xlen-1)); 415 | return state->scause; 416 | case CSR_SATP: 417 | return state->satp; 418 | case CSR_SSCRATCH: return state->sscratch; 419 | case CSR_MSTATUS: return state->mstatus; 420 | case CSR_MIP: return state->mip; 421 | case CSR_MIE: return state->mie; 422 | case CSR_MEPC: return state->mepc; 423 | case CSR_MSCRATCH: return state->mscratch; 424 | case CSR_MCAUSE: return state->mcause; 425 | case CSR_MTVAL: return state->mtval; 426 | case CSR_MISA: return isa; 427 | case CSR_MARCHID: return 0; 428 | case CSR_MIMPID: return 0; 429 | case CSR_MVENDORID: return 0; 430 | case CSR_MHARTID: return 0; 431 | case CSR_MTVEC: return state->mtvec; 432 | case CSR_MEDELEG: return state->medeleg; 433 | case CSR_MIDELEG: return state->mideleg; 434 | case CSR_TSELECT: return state->tselect; 435 | case CSR_TDATA1: 436 | if (state->tselect < state->num_triggers) { 437 | reg_t v = 0; 438 | mcontrol_t *mc = &state->mcontrol[state->tselect]; 439 | v = set_field(v, MCONTROL_TYPE(xlen), mc->type); 440 | v = set_field(v, MCONTROL_DMODE(xlen), mc->dmode); 441 | v = set_field(v, MCONTROL_MASKMAX(xlen), mc->maskmax); 442 | v = set_field(v, MCONTROL_SELECT, mc->select); 443 | v = set_field(v, MCONTROL_TIMING, mc->timing); 444 | v = set_field(v, MCONTROL_ACTION, mc->action); 445 | v = set_field(v, MCONTROL_CHAIN, mc->chain); 446 | v = set_field(v, MCONTROL_MATCH, mc->match); 447 | v = set_field(v, MCONTROL_M, mc->m); 448 | v = set_field(v, MCONTROL_H, mc->h); 449 | v = set_field(v, MCONTROL_S, mc->s); 450 | v = set_field(v, MCONTROL_U, mc->u); 451 | v = set_field(v, MCONTROL_EXECUTE, mc->execute); 452 | v = set_field(v, MCONTROL_STORE, mc->store); 453 | v = set_field(v, MCONTROL_LOAD, mc->load); 454 | return v; 455 | } else { 456 | return 0; 457 | } 458 | break; 459 | case CSR_TDATA2: 460 | if (state->tselect < state->num_triggers) { 461 | return state->tdata2[state->tselect]; 462 | } else { 463 | return 0; 464 | } 465 | break; 466 | case CSR_TDATA3: return 0; 467 | case CSR_DCSR: 468 | { 469 | uint32_t v = 0; 470 | v = set_field(v, DCSR_XDEBUGVER, 1); 471 | v = set_field(v, DCSR_EBREAKM, state->dcsr.ebreakm); 472 | v = set_field(v, DCSR_EBREAKH, state->dcsr.ebreakh); 473 | v = set_field(v, DCSR_EBREAKS, state->dcsr.ebreaks); 474 | v = set_field(v, DCSR_EBREAKU, state->dcsr.ebreaku); 475 | v = set_field(v, DCSR_STOPCYCLE, 0); 476 | v = set_field(v, DCSR_STOPTIME, 0); 477 | v = set_field(v, DCSR_CAUSE, state->dcsr.cause); 478 | v = set_field(v, DCSR_STEP, state->dcsr.step); 479 | v = set_field(v, DCSR_PRV, state->dcsr.prv); 480 | return v; 481 | } 482 | case CSR_DPC: 483 | return state->dpc; 484 | case CSR_DSCRATCH: 485 | return state->dscratch; 486 | } 487 | return reg_t(-1); 488 | } 489 | -------------------------------------------------------------------------------- /src/cpp/tv_spike.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 SRI International. 5 | * All rights reserved. 6 | * 7 | * This software was developed by SRI International and the University of 8 | * Cambridge Computer Laboratory (Department of Computer Science and 9 | * Technology) under DARPA/AFRL contract FA8650-18-C-7809 ("CIFV"). 10 | * 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions 13 | * are met: 14 | * 1. Redistributions of source code must retain the above copyright 15 | * notice, this list of conditions and the following disclaimer. 16 | * 2. Redistributions in binary form must reproduce the above copyright 17 | * notice, this list of conditions and the following disclaimer in the 18 | * documentation and/or other materials provided with the distribution. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 | * SUCH DAMAGE. 31 | */ 32 | 33 | #ifndef __TV_SPIKE_H_ 34 | #define __TV_SPIKE_H_ 35 | 36 | #include 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | 47 | /* 48 | * This class implements a single-CPU in-order RISC-V system, using the Spike 49 | * reference simulator as a backend. 50 | */ 51 | 52 | class tv_spike_t : public simif_t, public chunked_memif_t 53 | { 54 | public: 55 | tv_spike_t(const char *isa, size_t ram_size, bool debug_log); 56 | virtual ~tv_spike_t(); 57 | 58 | /* initialization */ 59 | reg_t init_elf(const char *elf_file); 60 | void set_pc_reg(uint64_t pc); 61 | void reset(void); 62 | reg_t get_entry_point(void) { return entry; } 63 | 64 | /* config queries */ 65 | int is_dirty_enabled(); 66 | int is_misaligned_enabled(); 67 | uint64_t ram_size(); 68 | 69 | /* execution */ 70 | void step(size_t steps); 71 | void step_io(void); 72 | void tick(reg_t inc); 73 | 74 | /* riscv-fesvr chunked_memif_t interface */ 75 | size_t chunk_align() { return 8; } 76 | size_t chunk_max_size() { return 8; } 77 | void read_chunk(addr_t taddr, size_t len, void* dst); 78 | void write_chunk(addr_t taddr, size_t len, const void* src); 79 | void clear_chunk(addr_t taddr, size_t len); 80 | 81 | /* riscv-isa-sim simif_t interface */ 82 | 83 | // physical memory mapping: returns a pointer to the byte in the physical 84 | // memory model addressed by the physical address 'addr'. NULL is returned if 85 | // 'addr' does not map to a valid physical memory range. 86 | char* addr_to_mem(reg_t addr); 87 | 88 | // MMIO operations: 89 | bool mmio_load(reg_t addr, size_t len, uint8_t* bytes); 90 | bool mmio_store(reg_t addr, size_t len, const uint8_t* bytes); 91 | // callbacks 92 | void proc_reset(unsigned id) { } 93 | 94 | // misc 95 | bool exited(int& exit_code); 96 | 97 | // verification API 98 | bool set_verbose(bool enable); 99 | 100 | bool check_pc(uint64_t pc); 101 | bool check_gpr(size_t regno, uint64_t val); 102 | bool check_csr(size_t regno, uint64_t val); 103 | bool check_priv(uint8_t prv); 104 | //bool check_fpr(size_t regno, uint64_t val); 105 | 106 | /* platform info */ 107 | static const size_t INSNS_PER_RTC_TICK = 100; // 10 MHz clock for 1 BIPS core 108 | static const size_t CPU_HZ = 1000000000; // 1GHz CPU 109 | const std::string get_dts(void); 110 | const std::string get_dtb(void); 111 | void dtb_in_rom(bool enable); 112 | 113 | private: 114 | mem_t *dram; 115 | std::vector> mem_regions; 116 | mmu_t* debug_mmu; // used for initialization of memory regions 117 | processor_t *cpu; 118 | std::vector procs; // contains the above singleton cpu 119 | 120 | // memory access and MMIO 121 | memif_t memif; // used by ELF loader 122 | std::unique_ptr boot_rom; // holds reset vector 123 | std::unique_ptr clint; // clock interface 124 | bus_t bus; 125 | 126 | // htif access and devices 127 | addr_t tohost_addr; 128 | bcd_t bcd; 129 | null_device_t nulld; 130 | device_list_t device_list; 131 | addr_t fromhost_addr; 132 | std::queue fromhost_queue; 133 | 134 | // device tree info 135 | bool dtb_inited; 136 | std::string dts; 137 | std::string dtb; 138 | 139 | // execution 140 | reg_t entry; 141 | bool has_exited; 142 | int exit_code; 143 | 144 | // verification API 145 | bool verbose_verify; 146 | bool insert_dts; 147 | 148 | // miscellaneous 149 | bool debug_log; 150 | 151 | // internal 152 | void setup_dtb(void); 153 | reg_t read_csr(size_t which); 154 | void check_exited(void); 155 | uint64_t check_mask(void); 156 | }; 157 | 158 | #endif // __TV_SPIKE_H_ 159 | -------------------------------------------------------------------------------- /src/cpp/tv_spike_intf.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 SRI International. 5 | * All rights reserved. 6 | * 7 | * This software was developed by SRI International and the University of 8 | * Cambridge Computer Laboratory (Department of Computer Science and 9 | * Technology) under DARPA/AFRL contract FA8650-18-C-7809 ("CIFV"). 10 | * 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions 13 | * are met: 14 | * 1. Redistributions of source code must retain the above copyright 15 | * notice, this list of conditions and the following disclaimer. 16 | * 2. Redistributions in binary form must reproduce the above copyright 17 | * notice, this list of conditions and the following disclaimer in the 18 | * documentation and/or other materials provided with the distribution. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 | * SUCH DAMAGE. 31 | */ 32 | 33 | #include 34 | #include "tv_spike_intf.h" 35 | #include "tv_spike.h" 36 | 37 | static int debug = 1; 38 | struct tv_spike_t* tv_init(const char *isa, uint64_t ram_size, int _debug) 39 | { 40 | if (_debug) fprintf(stderr, "%s(%s, %d)\n", __func__, isa, _debug); 41 | debug = _debug; 42 | tv_spike_t *tvs = new tv_spike_t(isa, ram_size, bool(debug)); 43 | return tvs; 44 | } 45 | 46 | int tv_get_dts(struct tv_spike_t *tvs, unsigned char *dts_buf, size_t *len_p) 47 | { 48 | if (!len_p) return -1; 49 | 50 | const std::string dts = tvs->get_dts(); 51 | if (*len_p < dts.length() || dts_buf == NULL) { 52 | *len_p = dts.length(); 53 | return -1; 54 | } 55 | 56 | *len_p = dts.length(); 57 | if (dts_buf) memcpy(dts_buf, (const unsigned char *)dts.c_str(), *len_p); 58 | return 0; 59 | } 60 | 61 | int tv_get_dtb(struct tv_spike_t *tvs, unsigned char *dtb_buf, size_t *len_p) 62 | { 63 | if (!len_p) return -1; 64 | 65 | const std::string dtb = tvs->get_dtb(); 66 | if (*len_p < dtb.length() || dtb_buf == NULL) { 67 | *len_p = dtb.length(); 68 | return -1; 69 | } 70 | 71 | *len_p = dtb.length(); 72 | if (dtb_buf) memcpy(dtb_buf, (const unsigned char *)dtb.c_str(), *len_p); 73 | return 0; 74 | } 75 | 76 | size_t tv_get_insns_per_tick(struct tv_spike_t* tvs) 77 | { 78 | return tvs->INSNS_PER_RTC_TICK; 79 | } 80 | 81 | void tv_set_verbose(struct tv_spike_t* tvs, int enable) 82 | { 83 | if (debug) fprintf(stderr, "%s(%d)\n", __func__, enable); 84 | tvs->set_verbose(enable); 85 | } 86 | 87 | void tv_set_dtb_in_rom(struct tv_spike_t* tvs, int enable) 88 | { 89 | tvs->dtb_in_rom(enable); 90 | } 91 | 92 | int tv_is_dirty_enabled(struct tv_spike_t* tvs) 93 | { 94 | return tvs->is_dirty_enabled(); 95 | } 96 | 97 | int tv_is_misaligned_enabled(struct tv_spike_t* tvs) 98 | { 99 | return tvs->is_misaligned_enabled(); 100 | } 101 | 102 | uint64_t tv_ram_size(tv_spike_t *tvs) 103 | { 104 | return tvs->ram_size(); 105 | } 106 | 107 | void tv_load_elf(struct tv_spike_t* tvs, const char *filename) 108 | { 109 | reg_t entry = tvs->init_elf(filename); 110 | if (debug) fprintf(stderr, "%s(%s): %0" PRIx64 "\n", 111 | __func__, filename, entry); 112 | } 113 | 114 | void tv_reset(struct tv_spike_t* tvs) 115 | { 116 | if (debug) fprintf(stderr, "%s()\n", __func__); 117 | tvs->reset(); 118 | } 119 | 120 | void tv_set_pc(struct tv_spike_t *tvs, uint64_t pc) 121 | { 122 | if (debug) fprintf(stderr, "%s()\n", __func__); 123 | tvs->set_pc_reg(pc); 124 | } 125 | 126 | void tv_step(struct tv_spike_t* tvs) 127 | { 128 | if (debug) fprintf(stderr, "%s()\n", __func__); 129 | tvs->step(1); 130 | } 131 | 132 | void tv_step_io(struct tv_spike_t* tvs) 133 | { 134 | if (debug) fprintf(stderr, "%s()\n", __func__); 135 | tvs->step_io(); 136 | } 137 | 138 | void tv_tick_clock(struct tv_spike_t* tvs) 139 | { 140 | if (debug) fprintf(stderr, "%s()\n", __func__); 141 | tvs->tick(1); 142 | } 143 | 144 | int tv_is_done(struct tv_spike_t* tvs) 145 | { 146 | int exit_code; 147 | return tvs->exited(exit_code); 148 | } 149 | 150 | int tv_check_priv(struct tv_spike_t* tvs, uint8_t priv) 151 | { 152 | return tvs->check_priv(priv); 153 | } 154 | 155 | int tv_check_pc(struct tv_spike_t* tvs, uint64_t val) 156 | { 157 | return tvs->check_pc(val); 158 | } 159 | 160 | int tv_check_gpr(struct tv_spike_t* tvs, size_t regno, uint64_t val) 161 | { 162 | return tvs->check_gpr(regno, val); 163 | } 164 | 165 | int tv_check_csr(struct tv_spike_t* tvs, size_t regno, uint64_t val) 166 | { 167 | return tvs->check_csr(regno, val); 168 | } 169 | 170 | void tv_free(struct tv_spike_t *tvs) 171 | { 172 | if (debug) fprintf(stderr, "%s(%p)\n", __func__, tvs); 173 | delete tvs; 174 | } 175 | -------------------------------------------------------------------------------- /src/cpp/tv_spike_intf.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (c) 2018 SRI International. 5 | * All rights reserved. 6 | * 7 | * This software was developed by SRI International and the University of 8 | * Cambridge Computer Laboratory (Department of Computer Science and 9 | * Technology) under DARPA/AFRL contract FA8650-18-C-7809 ("CIFV"). 10 | * 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions 13 | * are met: 14 | * 1. Redistributions of source code must retain the above copyright 15 | * notice, this list of conditions and the following disclaimer. 16 | * 2. Redistributions in binary form must reproduce the above copyright 17 | * notice, this list of conditions and the following disclaimer in the 18 | * documentation and/or other materials provided with the distribution. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 | * SUCH DAMAGE. 31 | */ 32 | 33 | 34 | #ifndef _TV_SPIKE_INTF_H_ 35 | #define _TV_SPIKE_INTF_H_ 36 | 37 | #include 38 | #include 39 | 40 | #ifdef __cplusplus 41 | extern "C" { 42 | #endif 43 | 44 | struct tv_spike_t; 45 | 46 | struct tv_spike_t* tv_init(const char *isa, uint64_t ram_size, int debug); 47 | 48 | int tv_get_dts(struct tv_spike_t* tvs, unsigned char *dts_buf, size_t *len_p); 49 | 50 | int tv_get_dtb(struct tv_spike_t* tvs, unsigned char *dtb_buf, size_t *len_p); 51 | 52 | size_t tv_get_insns_per_tick(struct tv_spike_t* tvs); 53 | 54 | int tv_is_dirty_enabled(struct tv_spike_t* tvs); 55 | 56 | int tv_is_misaligned_enabled(struct tv_spike_t* tvs); 57 | 58 | uint64_t tv_ram_size(struct tv_spike_t *tvs); 59 | 60 | void tv_set_verbose(struct tv_spike_t* tvs, int enable); 61 | 62 | void tv_set_dtb_in_rom(struct tv_spike_t* tvs, int enable); 63 | 64 | void tv_load_elf(struct tv_spike_t* tvs, const char *filename); 65 | 66 | void tv_reset(struct tv_spike_t *tvs); 67 | 68 | void tv_set_pc(struct tv_spike_t *tvs, uint64_t pc); 69 | 70 | void tv_step(struct tv_spike_t *tvs); 71 | 72 | void tv_step_io(struct tv_spike_t *tvs); 73 | 74 | void tv_tick_clock(struct tv_spike_t *tvs); 75 | 76 | int tv_is_done(struct tv_spike_t *tvs); 77 | 78 | int tv_check_priv(struct tv_spike_t *tvs, uint8_t prv); 79 | 80 | int tv_check_pc(struct tv_spike_t *tvs, uint64_t val); 81 | 82 | int tv_check_gpr(struct tv_spike_t *tvs, size_t regno, uint64_t val); 83 | 84 | int tv_check_csr(struct tv_spike_t *tvs, size_t regno, uint64_t val); 85 | 86 | void tv_free(struct tv_spike_t *tvs); 87 | 88 | #ifdef __cplusplus 89 | } 90 | #endif 91 | 92 | #endif /* _TV_SPIKE_INTF_H_ */ 93 | -------------------------------------------------------------------------------- /src/l3/riscv-print.spec: -------------------------------------------------------------------------------- 1 | declare print::string->unit 2 | declare println::string->unit 3 | -------------------------------------------------------------------------------- /src/sml/l3riscv.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #ifdef __APPLE__ 5 | #include // For Mac OS X 6 | #else 7 | #include // For Linux 8 | #endif 9 | 10 | /* This should be called before any other calls into l3riscv. */ 11 | void l3riscv_init(); 12 | 13 | /* This should be called at the end of the program. */ 14 | void l3riscv_done(); 15 | 16 | /* The filename is specified using the SIM_ELF_FILENAME environment variable. */ 17 | void l3riscv_mem_load_elf(); 18 | 19 | /* Use these interfaces to retrieve the memory ranges and contents of 20 | the loaded ELF file. 21 | */ 22 | uint64_t l3riscv_mem_get_min_addr(); 23 | uint64_t l3riscv_mem_get_max_addr(); 24 | uint32_t l3riscv_mem_read_32(uint64_t addr); 25 | 26 | /* Currently undocumented interfaces to support debuggers and other uses. */ 27 | 28 | void l3riscv_cpu_write_pc(uint64_t pc); 29 | void l3riscv_cpu_write_gpr(uint8_t gpr, uint64_t val); 30 | void l3riscv_cpu_write_csr(uint16_t csr, uint64_t val); 31 | void l3riscv_cpu_write_fpr(uint8_t fpr, uint64_t val); 32 | 33 | /* This function should be called with information about every retired 34 | instruction. The arguments are as follows: 35 | 36 | - cpu: the identifier for the cpu 37 | -- currently unused, but will support multi-core verification 38 | 39 | - cmd: the command to the verifier 40 | -- the only supported value is 0 to verify a retired instruction 41 | 42 | - exc_taken: non-zero when the retired instruction caused an exception, zero otherwise 43 | 44 | - pc: PC for the retired instruction 45 | 46 | - addr: address argument for instruction: 47 | -- new control flow target for jump, exception branch, ERET 48 | -- memory address for memory ops and AMOs 49 | -- CSR register address for CSR instructions 50 | 51 | - data1: data result for instruction: 52 | -- new value for rd for ALU ops, LOAD, LOAD_FP, LR, SC, CSR ops 53 | -- new csr_status for exceptions and ERET 54 | 55 | - data2: data argument for instruction: 56 | -- new csr_cause for exceptions 57 | -- new memory value for STORE, STORE_FP, SC, AMOs 58 | -- argument for CSR ops 59 | 60 | - fp_data: floating point value, currently unused 61 | 62 | - verbosity: for any generated tracing, currently unused 63 | 64 | Note that some of the state arguments are often undefined. For e.g., after a 65 | fetch exception, only exc_taken and pc will be checked. 66 | 67 | */ 68 | uint32_t l3riscv_verify(uint64_t cpu, 69 | uint32_t cmd, 70 | uint32_t exc_taken, 71 | uint64_t pc, 72 | uint64_t addr, 73 | uint64_t data1, 74 | uint64_t data2, 75 | uint64_t data3, 76 | uint64_t fpdata, 77 | uint32_t verbosity); 78 | -------------------------------------------------------------------------------- /src/sml/l3riscv.mlb: -------------------------------------------------------------------------------- 1 | $(SML_LIB)/basis/basis.mlb 2 | $(L3LIBDIR)/Runtime.sig 3 | $(L3LIBDIR)/Runtime.sml 4 | $(L3LIBDIR)/IntExtra.sig 5 | $(L3LIBDIR)/IntExtra.sml 6 | $(L3LIBDIR)/Nat.sig 7 | $(L3LIBDIR)/Nat.sml 8 | $(L3LIBDIR)/Set.sig 9 | $(L3LIBDIR)/Set.sml 10 | $(L3LIBDIR)/L3.sig 11 | $(L3LIBDIR)/L3.sml 12 | $(L3LIBDIR)/Bitstring.sig 13 | $(L3LIBDIR)/Bitstring.sml 14 | $(L3LIBDIR)/BitsN.sig 15 | $(L3LIBDIR)/BitsN.sml 16 | $(L3LIBDIR)/SSE.sig 17 | $(L3LIBDIR)/MLtonSSE.sml 18 | $(L3LIBDIR)/FP.sig 19 | $(L3LIBDIR)/FP32.sml 20 | $(L3LIBDIR)/FP64.sml 21 | $(L3LIBDIR)/FPConvert.sig 22 | $(L3LIBDIR)/FPConvert.sml 23 | $(L3LIBDIR)/Ptree.sig 24 | $(L3LIBDIR)/Ptree.sml 25 | $(L3LIBDIR)/Map.sml 26 | $(L3LIBDIR)/MutableMapFunctor.sml 27 | $(L3LIBDIR)/MutableMap24.sml 28 | ./lib/Elf.sig 29 | ./lib/Elf.sml 30 | riscv.sig 31 | ann "nonexhaustiveMatch ignore" in 32 | riscv.sml 33 | end 34 | model.sml 35 | mlton_run.sml 36 | -------------------------------------------------------------------------------- /src/sml/lib/Elf.sig: -------------------------------------------------------------------------------- 1 | (* Copyright (C) 2015-2018 SRI International. 2 | * 3 | * This software was developed by SRI International and the University 4 | * of Cambridge Computer Laboratory under DARPA/AFRL contract 5 | * FA8750-11-C-0249 ("MRC2"), as part of the DARPA MRC research 6 | * programme. 7 | * 8 | * See the LICENSE file for details. 9 | *) 10 | 11 | signature Elf = 12 | sig 13 | datatype ElfType = ET_NONE | ET_REL | ET_EXEC | ET_DYN | ET_CORE | ET_OTHER 14 | datatype Endian = BIG | LITTLE 15 | datatype Class = BIT_32 | BIT_64 16 | datatype PType = PT_NULL | PT_LOAD | PT_DYNAMIC | PT_INTERP | PT_NOTE | PT_SHLIB | PT_PHDR | PT_OTHER 17 | datatype SType = SHT_NULL | SHT_PROGBITS | SHT_SYMTAB | SHT_STRTAB | SHT_RELA | SHT_HASH | SHT_DYNAMIC | SHT_NOTE | SHT_NOBITS | SHT_REL | SHT_SHLIB | SHT_DYNSYM | SHT_OTHER 18 | 19 | type hdr = 20 | { etype: ElfType 21 | , endian: Endian 22 | , class: Class 23 | , entry: LargeInt.int 24 | , phoff: LargeInt.int 25 | , phesz: LargeInt.int 26 | , phnum: LargeInt.int 27 | , shoff: LargeInt.int 28 | , shesz: LargeInt.int 29 | , shnum: LargeInt.int 30 | , shsndx: LargeInt.int 31 | } 32 | 33 | type segm = 34 | { ptype: PType 35 | , offset: LargeInt.int 36 | , paddr: LargeInt.int 37 | , vaddr: LargeInt.int 38 | , memsz: LargeInt.int 39 | , bytes: Word8Vector.vector 40 | } 41 | 42 | type sect = 43 | { stype: SType 44 | , snmidx: LargeInt.int 45 | , saddr: LargeInt.int 46 | , soffs: LargeInt.int 47 | , ssize: LargeInt.int 48 | , sentsz: LargeInt.int 49 | } 50 | 51 | type named_sect = Substring.substring option * sect 52 | 53 | type symb = 54 | { syname: Substring.substring option 55 | , syshndx: LargeInt.int 56 | , syvalue: LargeInt.int 57 | , sysize: LargeInt.int 58 | , syinfo: LargeInt.int 59 | } 60 | 61 | type elf_file 62 | 63 | val openElf : string -> elf_file 64 | 65 | val isELFFile : elf_file -> bool 66 | val getHeader : elf_file -> hdr 67 | val getSegments : elf_file -> hdr -> segm list 68 | val getSections : elf_file -> hdr -> sect list 69 | val getSymbols : elf_file -> hdr -> named_sect list -> symb list 70 | 71 | val printHeader : hdr -> unit 72 | val printSegment : segm -> unit 73 | val printSection : sect -> unit 74 | val printSymbol : symb -> unit 75 | 76 | val getNamedSections : elf_file -> hdr -> sect list -> named_sect list 77 | val printNamedSection: named_sect -> unit 78 | end 79 | -------------------------------------------------------------------------------- /src/sml/lib/Elf.sml: -------------------------------------------------------------------------------- 1 | (* Copyright (C) 2015-2018 SRI International. 2 | * 3 | * This software was developed by SRI International and the University 4 | * of Cambridge Computer Laboratory under DARPA/AFRL contract 5 | * FA8750-11-C-0249 ("MRC2"), as part of the DARPA MRC research 6 | * programme. 7 | * 8 | * See the LICENSE file for details. 9 | *) 10 | 11 | structure Elf : Elf = struct 12 | 13 | datatype ElfType = ET_NONE | ET_REL | ET_EXEC | ET_DYN | ET_CORE | ET_OTHER 14 | datatype Endian = BIG | LITTLE 15 | datatype Class = BIT_32 | BIT_64 16 | datatype PType = PT_NULL | PT_LOAD | PT_DYNAMIC | PT_INTERP | PT_NOTE | PT_SHLIB | PT_PHDR | PT_OTHER 17 | datatype SType = SHT_NULL | SHT_PROGBITS | SHT_SYMTAB | SHT_STRTAB | SHT_RELA | SHT_HASH | SHT_DYNAMIC | SHT_NOTE | SHT_NOBITS | SHT_REL | SHT_SHLIB | SHT_DYNSYM | SHT_OTHER 18 | 19 | type hdr = 20 | { etype: ElfType 21 | , endian: Endian 22 | , class: Class 23 | , entry: LargeInt.int 24 | , phoff: LargeInt.int 25 | , phesz: LargeInt.int 26 | , phnum: LargeInt.int 27 | , shoff: LargeInt.int 28 | , shesz: LargeInt.int 29 | , shnum: LargeInt.int 30 | , shsndx: LargeInt.int 31 | } 32 | 33 | type segm = 34 | { ptype: PType 35 | , offset: LargeInt.int 36 | , paddr: LargeInt.int 37 | , vaddr: LargeInt.int 38 | , memsz: LargeInt.int 39 | , bytes: Word8Vector.vector 40 | } 41 | 42 | type sect = 43 | { stype: SType 44 | , snmidx: LargeInt.int 45 | , saddr: LargeInt.int 46 | , soffs: LargeInt.int 47 | , ssize: LargeInt.int 48 | , sentsz: LargeInt.int 49 | } 50 | 51 | type named_sect = Substring.substring option * sect 52 | 53 | type symb = 54 | { syname: Substring.substring option 55 | , syshndx: LargeInt.int 56 | , syvalue: LargeInt.int 57 | , sysize: LargeInt.int 58 | , syinfo: LargeInt.int 59 | } 60 | 61 | type elf_file = Posix.IO.file_desc 62 | 63 | (* type conversion utilities *) 64 | 65 | fun toType t = 66 | case t of 67 | 0 => ET_NONE 68 | | 1 => ET_REL 69 | | 2 => ET_EXEC 70 | | 3 => ET_DYN 71 | | 4 => ET_CORE 72 | | _ => ET_OTHER 73 | 74 | fun typeToString t = 75 | case t of 76 | ET_NONE => "NONE" 77 | | ET_REL => "REL" 78 | | ET_EXEC => "EXEC" 79 | | ET_DYN => "DYN" 80 | | ET_CORE => "CORE" 81 | | ET_OTHER => "OTHER" 82 | 83 | fun toClass t = 84 | case t of 85 | 1 => BIT_32 86 | | 2 => BIT_64 87 | | _ => raise Fail ("Invalid ELF Class " ^ IntInf.toString t) 88 | 89 | fun classToString t = 90 | case t of 91 | BIT_32 => "32-bit" 92 | | BIT_64 => "64-bit" 93 | 94 | fun toEndian t = 95 | case t of 96 | 1 => LITTLE 97 | | 2 => BIG 98 | | _ => raise Fail ("Invalid ELF endian " ^ IntInf.toString t) 99 | 100 | fun endianToString t = 101 | case t of 102 | LITTLE => "little-endian" 103 | | BIG => "big-endian" 104 | 105 | fun toPType t = 106 | case t of 107 | 0 => PT_NULL 108 | | 1 => PT_LOAD 109 | | 2 => PT_DYNAMIC 110 | | 3 => PT_INTERP 111 | | 4 => PT_NOTE 112 | | 5 => PT_SHLIB 113 | | 6 => PT_PHDR 114 | | _ => PT_OTHER 115 | 116 | fun ptypeToString t = 117 | case t of 118 | PT_NULL => "NULL" 119 | | PT_LOAD => "LOAD" 120 | | PT_DYNAMIC => "DYNAMIC" 121 | | PT_INTERP => "INTERP" 122 | | PT_NOTE => "NOTE" 123 | | PT_SHLIB => "SHLIB" 124 | | PT_PHDR => "PHDR" 125 | | PT_OTHER => "OTHER" 126 | 127 | fun toSType t = 128 | case t of 129 | 0 => SHT_NULL 130 | | 1 => SHT_PROGBITS 131 | | 2 => SHT_SYMTAB 132 | | 3 => SHT_STRTAB 133 | | 4 => SHT_RELA 134 | | 5 => SHT_HASH 135 | | 6 => SHT_DYNAMIC 136 | | 7 => SHT_NOTE 137 | | 8 => SHT_NOBITS 138 | | 9 => SHT_REL 139 | | 10 => SHT_SHLIB 140 | | 11 => SHT_DYNSYM 141 | | _ => SHT_OTHER 142 | 143 | fun stypeToString t = 144 | case t of 145 | SHT_NULL => "NULL" 146 | | SHT_PROGBITS => "PROGBITS" 147 | | SHT_SYMTAB => "SYMTAB" 148 | | SHT_STRTAB => "STRTAB" 149 | | SHT_RELA => "RELA" 150 | | SHT_HASH => "HASH" 151 | | SHT_DYNAMIC => "DYNAMIC" 152 | | SHT_NOTE => "NOTE" 153 | | SHT_NOBITS => "NOBITS" 154 | | SHT_REL => "REL" 155 | | SHT_SHLIB => "SHLIB" 156 | | SHT_DYNSYM => "DYNSYM" 157 | | SHT_OTHER => "OTHER" 158 | 159 | 160 | (* binary processing utilities *) 161 | 162 | fun extract_bin fd (ofs : Position.int) (width : int) : Word8Vector.vector = 163 | let val res = Posix.IO.lseek(fd, ofs, Posix.IO.SEEK_SET) 164 | in Posix.IO.readVec(fd, width) (* TODO: handle under-reads. *) 165 | end 166 | 167 | fun toInt endian v : IntInf.int = 168 | (case endian of 169 | BIG => Word8Vector.foldli 170 | | LITTLE => Word8Vector.foldri 171 | ) (fn (i, v, acc) => 172 | IntInf.orb (IntInf.<< (acc, Word.fromInt 8), IntInf.fromInt (Word8.toInt v)) 173 | ) (IntInf.fromInt 0) v 174 | 175 | (* debugging *) 176 | 177 | fun show_bin name v = 178 | (print ("showing " ^ name ^ "\n"); 179 | Vector.appi (fn (i,e) => 180 | print("\t " ^ (Int.toString i) ^ ": " ^ (Word8.toString e) ^ "\n") 181 | ) v) 182 | 183 | (* C-string utilities *) 184 | 185 | fun locateCStringEnd buf ofs = 186 | let val len = String.size buf 187 | fun at_null idx = String.sub(buf, idx) = #"\000" 188 | fun search idx = if idx >= len 189 | then NONE 190 | else if at_null idx 191 | then SOME idx 192 | else search (idx + 1) 193 | in if ofs < 0 then NONE else search ofs 194 | end 195 | 196 | fun extractCString buf (ofs : int) = 197 | case locateCStringEnd buf ofs of 198 | NONE => NONE 199 | | SOME idx => SOME (Substring.substring(buf, ofs, idx - ofs)) 200 | 201 | (* api *) 202 | 203 | fun openElf fname = 204 | Posix.FileSys.openf (fname, Posix.FileSys.O_RDONLY, Posix.FileSys.O.sync) 205 | 206 | val ELF_MAGIC = Word8Vector.fromList [ 0wx7F, 0wx45, 0wx4c, 0wx46 ] 207 | fun isELFFile fd = 208 | let val magic = extract_bin fd (Position.fromInt 0) 4 209 | in Word8Vector.foldli 210 | (fn (idx, v, acc) => 211 | acc andalso (Word8Vector.sub (ELF_MAGIC, idx) = v) 212 | ) true magic 213 | end 214 | 215 | (* elf header *) 216 | 217 | fun entry_loc c = if c = BIT_32 then (24,4) else (24,8) 218 | fun phoff_loc c = if c = BIT_32 then (28,4) else (32,8) 219 | fun shoff_loc c = if c = BIT_32 then (32,4) else (40,8) 220 | fun phesz_loc c = if c = BIT_32 then (42,2) else (54,2) 221 | fun phnum_loc c = if c = BIT_32 then (44,2) else (56,2) 222 | fun shesz_loc c = if c = BIT_32 then (46,2) else (58,2) 223 | fun shnum_loc c = if c = BIT_32 then (48,2) else (60,2) 224 | fun shsndx_loc c = if c = BIT_32 then (50,2) else (62,2) 225 | 226 | fun getHeader fd = 227 | let val class = toClass (toInt LITTLE (extract_bin fd 0x04 1)) 228 | val endian = toEndian (toInt LITTLE (extract_bin fd 0x05 1)) 229 | val etype = toType (toInt LITTLE (extract_bin fd 0x10 2)) 230 | 231 | fun ex_field f = let val (off, wd) = f 232 | in extract_bin fd off wd 233 | end 234 | fun int_field loc = IntInf.toLarge (toInt endian (ex_field (loc class))) 235 | in 236 | { etype = etype 237 | , endian = endian 238 | , class = class 239 | , entry = int_field entry_loc 240 | , phoff = int_field phoff_loc 241 | , phesz = int_field phesz_loc 242 | , phnum = int_field phnum_loc 243 | , shoff = int_field shoff_loc 244 | , shesz = int_field shesz_loc 245 | , shnum = int_field shnum_loc 246 | , shsndx = int_field shsndx_loc 247 | } 248 | end 249 | 250 | fun printHeader (hdr : hdr) = 251 | ( print ("ELF header\n") 252 | ; print ("\tType: " ^ (typeToString (#etype hdr)) ^ "\n") 253 | ; print ("\tEndian: " ^ (endianToString (#endian hdr)) ^ "\n") 254 | ; print ("\tClass: " ^ (classToString (#class hdr)) ^ "\n") 255 | ; print ("\tEntry: " ^ (IntInf.fmt StringCvt.HEX (#entry hdr)) ^ "\n") 256 | ; print ("\tphoff: " ^ (IntInf.fmt StringCvt.HEX (#phoff hdr)) ^ "\n") 257 | ; print ("\tphesz: " ^ (IntInf.fmt StringCvt.HEX (#phesz hdr)) ^ "\n") 258 | ; print ("\tphnum: " ^ (IntInf.fmt StringCvt.HEX (#phnum hdr)) ^ "\n") 259 | ; print ("\tshoff: " ^ (IntInf.fmt StringCvt.HEX (#shoff hdr)) ^ "\n") 260 | ; print ("\tshesz: " ^ (IntInf.fmt StringCvt.HEX (#shesz hdr)) ^ "\n") 261 | ; print ("\tshnum: " ^ (IntInf.fmt StringCvt.HEX (#shnum hdr)) ^ "\n") 262 | ; print ("\tshsndx: " ^ (IntInf.fmt StringCvt.HEX (#shsndx hdr)) ^ "\n") 263 | ) 264 | 265 | (* segments *) 266 | 267 | (* Elf32_Phdr and Elf64_Phdr have different layouts for alignment! *) 268 | fun ptype_loc c = ( 0,4) 269 | fun flags_loc c = if c = BIT_32 then (24,4) else ( 4,4) 270 | fun offset_loc c = if c = BIT_32 then ( 4,4) else ( 8,8) 271 | fun vaddr_loc c = if c = BIT_32 then ( 8,4) else (16,8) 272 | fun paddr_loc c = if c = BIT_32 then (12,4) else (24,8) 273 | fun filesz_loc c = if c = BIT_32 then (16,4) else (32,8) 274 | fun memsz_loc c = if c = BIT_32 then (20,4) else (40,8) 275 | fun align_loc c = if c = BIT_32 then (28,4) else (48,8) 276 | fun getPhdr (fd : elf_file) (hdr : hdr) i = 277 | let val c = #class hdr 278 | val endian = #endian hdr 279 | fun ex_field f = let val (off, wd) = f 280 | val ent_skip = LargeInt.* (i, (#phesz hdr)) 281 | val ent_off = LargeInt.+ ((#phoff hdr), ent_skip) 282 | val fld_off = LargeInt.+ (ent_off, off) 283 | in extract_bin fd (Position.fromLarge fld_off) wd 284 | end 285 | fun int_field loc = toInt endian (ex_field (loc c)) 286 | val p_flags = int_field flags_loc 287 | val p_offset = int_field offset_loc 288 | val p_filesz = int_field filesz_loc 289 | val offset = Position.fromLarge (IntInf.toLarge p_offset) 290 | val nbytes = IntInf.toInt p_filesz 291 | in { ptype = toPType (int_field ptype_loc) 292 | , offset = p_offset 293 | , paddr = int_field paddr_loc 294 | , vaddr = int_field vaddr_loc 295 | , memsz = int_field memsz_loc 296 | , bytes = extract_bin fd offset nbytes 297 | } 298 | end 299 | 300 | fun printSegment (segm : segm) = 301 | ( print ("Segment:\n") 302 | ; print ("\tType: " ^ (ptypeToString (#ptype segm)) ^ "\n") 303 | ; print ("\tOffset: " ^ (IntInf.fmt StringCvt.HEX (#offset segm)) ^ "\n") 304 | ; print ("\tVirtAddr: " ^ (IntInf.fmt StringCvt.HEX (#vaddr segm)) ^ "\n") 305 | ; print ("\tPhysAddr: " ^ (IntInf.fmt StringCvt.HEX (#paddr segm)) ^ "\n") 306 | ; print ("\tMemSize: " 307 | ^ (IntInf.fmt StringCvt.HEX (#memsz segm)) 308 | ^ " (" ^ (IntInf.toString (#memsz segm)) ^ ")" 309 | ^ "\n") 310 | ; print ("\tFileSize: " 311 | ^ (Int.fmt StringCvt.HEX (Word8Vector.length (#bytes segm))) 312 | ^ " (" ^ (Int.toString (Word8Vector.length (#bytes segm))) ^ ")" 313 | ^ "\n") 314 | ) 315 | 316 | fun getSegments fd (hdr : hdr) = 317 | let val nsegs = IntInf.toInt (#phnum hdr) 318 | val segnlist = List.tabulate (nsegs, (fn i => Int.toLarge i)) 319 | in List.map (getPhdr fd hdr) segnlist 320 | end 321 | 322 | (* sections *) 323 | 324 | (* TODO: helpers needed for symbol resolution: 325 | - parse symbol table entries from symtab (32/64-bit) 326 | - collect symbol names and values 327 | *) 328 | 329 | fun extractSection fd (sect : sect) = 330 | case #stype sect of 331 | SHT_NOBITS => "" 332 | | _ => 333 | ( let val ofs = Posix.IO.lseek(fd, Position.fromLarge (#soffs sect), Posix.IO.SEEK_SET) 334 | val fvec = Posix.IO.readVec(fd, LargeInt.toInt (#ssize sect)) 335 | val flen = Word8Vector.length fvec 336 | val elen = LargeInt.toInt (#ssize sect) 337 | in if flen <> elen 338 | then raise Fail ("Error extracting contents from " 339 | ^ (stypeToString (#stype sect)) 340 | ^ " section: got " ^ (Int.toString flen) ^ " bytes " 341 | ^ " when " ^ (Int.toString elen) ^ " were expected." 342 | ) 343 | else Byte.bytesToString fvec 344 | end 345 | ) 346 | 347 | (* Elf32_Shdr and Elf64_Shdr have different layouts. *) 348 | fun snmidx_loc c = (0,4) 349 | fun stype_loc c = (4,4) 350 | fun saddr_loc c = if c = BIT_32 then (12,4) else (16,8) 351 | fun soffs_loc c = if c = BIT_32 then (16,4) else (24,8) 352 | fun ssize_loc c = if c = BIT_32 then (20,4) else (32,8) 353 | fun sentsz_loc c = if c = BIT_32 then (36,4) else (56,8) 354 | fun getShdr (fd : elf_file) (hdr : hdr) i = 355 | let val c = #class hdr 356 | val endian = #endian hdr 357 | fun ex_field f = let val (off, wd) = f 358 | val ent_skip = LargeInt.* (i, (#shesz hdr)) 359 | val ent_off = LargeInt.+ ((#shoff hdr), ent_skip) 360 | val fld_off = LargeInt.+ (ent_off, off) 361 | in extract_bin fd (Position.fromLarge fld_off) wd 362 | end 363 | fun int_field loc = toInt endian (ex_field (loc c)) 364 | in { snmidx = int_field snmidx_loc 365 | , stype = toSType (int_field stype_loc) 366 | , saddr = int_field saddr_loc 367 | , soffs = int_field soffs_loc 368 | , ssize = int_field ssize_loc 369 | , sentsz = int_field sentsz_loc 370 | } 371 | end 372 | 373 | fun printSection (sect : sect) = 374 | ( print ("Section:\n") 375 | ; print ("\tType: " ^ (stypeToString (#stype sect)) ^ "\n") 376 | ; print ("\tNameIdx: " ^ (IntInf.fmt StringCvt.HEX (#snmidx sect)) ^ "\n") 377 | ; print ("\tAddress: " ^ (IntInf.fmt StringCvt.HEX (#saddr sect)) ^ "\n") 378 | ; print ("\tOffset: " ^ (IntInf.fmt StringCvt.HEX (#soffs sect)) ^ "\n") 379 | ; print ("\tSize: " ^ (IntInf.fmt StringCvt.HEX (#ssize sect)) ^ "\n") 380 | ; print ("\tEntSize: " ^ (IntInf.fmt StringCvt.HEX (#sentsz sect)) ^ "\n") 381 | ) 382 | 383 | fun getSections fd (hdr : hdr) = 384 | let val nsects = IntInf.toInt (#shnum hdr) 385 | val sectnlist = List.tabulate (nsects, (fn i => Int.toLarge i)) 386 | in List.map (getShdr fd hdr) sectnlist 387 | end 388 | 389 | fun getNamedSections fd (hdr : hdr) (sects : sect list) = 390 | let val shsndx = LargeInt.toInt (#shsndx hdr) 391 | val shs_sect = List.nth(sects, shsndx) 392 | handle Subscript => raise Fail ("Out-of-bounds shstrndx" 393 | ^ (Int.toString shsndx) 394 | ^ "in Elf-header") 395 | val shs_buf = extractSection fd shs_sect 396 | fun nm sect = extractCString shs_buf (LargeInt.toInt (#snmidx sect)) 397 | in List.map (fn s => (nm s, s)) sects 398 | end 399 | 400 | fun printNamedSection (nm, (sect : sect)) = 401 | ( (case nm of 402 | NONE => print ("Unnamed Section: \n") 403 | | SOME s => print ("Section: " ^ Substring.string s ^ "\n") 404 | ) 405 | ; print ("\tType: " ^ (stypeToString (#stype sect)) ^ "\n") 406 | ; print ("\tNameIdx: " ^ (IntInf.fmt StringCvt.HEX (#snmidx sect)) ^ "\n") 407 | ; print ("\tAddress: " ^ (IntInf.fmt StringCvt.HEX (#saddr sect)) ^ "\n") 408 | ; print ("\tOffset: " ^ (IntInf.fmt StringCvt.HEX (#soffs sect)) ^ "\n") 409 | ; print ("\tSize: " ^ (IntInf.fmt StringCvt.HEX (#ssize sect)) ^ "\n") 410 | ; print ("\tEntSize: " ^ (IntInf.fmt StringCvt.HEX (#sentsz sect)) ^ "\n") 411 | ) 412 | 413 | (* symbols *) 414 | 415 | fun getSymbolSections nsects = 416 | let fun pred name stype (nm, (s : sect)) = 417 | (case (nm, (#stype s)) of 418 | (SOME s, t) => Substring.string s = name andalso t = stype 419 | | (_, _) => false 420 | ) 421 | val symtab = List.find (pred ".symtab" SHT_SYMTAB) nsects 422 | val strtab = List.find (pred ".strtab" SHT_STRTAB) nsects 423 | in (symtab, strtab) 424 | end 425 | 426 | (* Elf32_Sym and Elf64_Sym have different layouts. *) 427 | fun syname_loc c = (0, 4) 428 | fun syvalue_loc c = if c = BIT_32 then ( 4,4) else ( 8,8) 429 | fun sysize_loc c = if c = BIT_32 then ( 8,4) else (16,8) 430 | fun syinfo_loc c = if c = BIT_32 then (12,1) else ( 4,1) 431 | fun syshndx_loc c = if c = BIT_32 then (14,2) else ( 6,2) 432 | fun getSym (fd : elf_file) (hdr : hdr) (symtab : sect) str_buf (i : int) = 433 | let val c = #class hdr 434 | val endian = #endian hdr 435 | fun ex_field f = let val (off, wd) = f 436 | val ent_skip = LargeInt.* (LargeInt.fromInt i, (#sentsz symtab)) 437 | val ent_off = LargeInt.+ ((#soffs symtab), ent_skip) 438 | val fld_off = LargeInt.+ (ent_off, off) 439 | in extract_bin fd (Position.fromLarge fld_off) wd 440 | end 441 | fun int_field loc = toInt endian (ex_field (loc c)) 442 | val name_ofs = IntInf.toInt (int_field syname_loc) 443 | in { syname = extractCString str_buf name_ofs 444 | , syshndx = int_field syshndx_loc 445 | , syvalue = int_field syvalue_loc 446 | , sysize = int_field sysize_loc 447 | , syinfo = int_field syinfo_loc 448 | } 449 | end 450 | 451 | fun printSymbol (symb : symb) = 452 | ( print ("Symbol:\n") 453 | ; (case (#syname symb) of 454 | SOME s => 455 | print ("\tName: " ^ (Substring.string s) ^ "\n") 456 | | NONE => 457 | print ("\tNo Name\n") 458 | ) 459 | ; print ("\tSectIdx: " ^ (IntInf.fmt StringCvt.HEX (#syshndx symb)) ^ "\n") 460 | ; print ("\tValue: " ^ (IntInf.fmt StringCvt.HEX (#syvalue symb)) ^ "\n") 461 | ; print ("\tSize: " ^ (IntInf.fmt StringCvt.HEX (#sysize symb)) ^ "\n") 462 | ; print ("\tInfo: " ^ (IntInf.fmt StringCvt.HEX (#syinfo symb)) ^ "\n") 463 | ) 464 | 465 | fun getSyms fd hdr (symtab : sect) (strtab : sect) = 466 | let val syment_sz = #sentsz symtab 467 | val symtab_sz = #ssize symtab 468 | val nsyms = if syment_sz = LargeInt.toLarge 0 469 | then 0 470 | else LargeInt.quot (symtab_sz, syment_sz) 471 | val sym_idxs = List.tabulate (LargeInt.toInt nsyms, (fn i => i)) 472 | val str_buf = extractSection fd strtab 473 | in List.map (getSym fd hdr symtab str_buf) sym_idxs 474 | end 475 | 476 | fun getSyms_opt fd hdr symtab_opt strtab_opt = 477 | case (symtab_opt, strtab_opt) of 478 | (SOME (_, symtab), SOME (_, strtab)) => getSyms fd hdr symtab strtab 479 | | (_, _) => [] 480 | 481 | fun getSymbols fd hdr nsects = 482 | let val (symtab, strtab) = getSymbolSections nsects 483 | in getSyms_opt fd hdr symtab strtab 484 | end 485 | end (* struct *) 486 | -------------------------------------------------------------------------------- /src/sml/mlton_run.sml: -------------------------------------------------------------------------------- 1 | (* Copyright (C) 2018 SRI International. 2 | * 3 | * This software was developed by SRI International and the University of 4 | * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-11-C-0249 5 | * ("MRC2"), as part of the DARPA MRC research programme, and under 6 | * DARPA/AFRL contract FA8750-10-C-0237 ("CTSRD"), as part of the DARPA 7 | * CRASH research programme. 8 | * 9 | * See the LICENSE file for details. 10 | *) 11 | 12 | (* Outer shell for Mlton *) 13 | 14 | val () = main () 15 | -------------------------------------------------------------------------------- /src/sml/model.sml: -------------------------------------------------------------------------------- 1 | (* Copyright (C) 2014, 2015 Anthony Fox, University of Cambridge 2 | * Copyright (C) 2014, 2015 Alexandre Joannou, University of Cambridge 3 | * Copyright (C) 2015-2018 SRI International. 4 | * 5 | * This software was developed by SRI International and the University of 6 | * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-11-C-0249 7 | * ("MRC2"), as part of the DARPA MRC research programme, and under 8 | * DARPA/AFRL contract FA8750-10-C-0237 ("CTSRD"), as part of the DARPA 9 | * CRASH research programme, and under DARPA/AFRL contract FA8650-18-C-7809 10 | * ("CIFV"), and under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of 11 | * the DARPA SSITH research programme. 12 | * 13 | * See the LICENSE file for details. 14 | *) 15 | 16 | (* -------------------------------------------------------------------------- 17 | RISCV emulator 18 | -------------------------------------------------------------------------- *) 19 | 20 | (* Default Configuration *) 21 | 22 | val mem_base_addr = ref (IntInf.fromInt 0x80000000) (* default used in spike *) 23 | val mem_size = ref (IntInf.fromInt 0) 24 | 25 | (* Platform settings; defaults match Spike's default config. *) 26 | val enable_misaligned_access = ref false 27 | val enable_dirty_update = ref false 28 | 29 | (* Execution parameters *) 30 | 31 | (* true -> init starting PC to reset vector 32 | false -> use start offset from ELF *) 33 | val boot = ref false 34 | val reset_addr = 0x1000 (* default used in spike *) 35 | 36 | val be = ref false (* little-endian *) 37 | val time_run = ref true 38 | 39 | val trace_lvl = ref (0 : int) 40 | val trace_elf = ref false 41 | 42 | val check = ref false 43 | val checker = ref (NONE : Oracle.t option) 44 | val checker_exit_pc = ref (Word64.fromInt (~1)) 45 | 46 | val verifier_mode = ref false 47 | val verifier_exe_name = "SIM_ELF_FILENAME" 48 | val verifier_trace_lvl = ref 1 49 | 50 | (* Utilities *) 51 | 52 | fun hex s = L3.lowercase (BitsN.toHexString s) 53 | fun phex n = StringCvt.padLeft #"0" (n div 4) o hex 54 | val hex16 = phex 16 55 | val hex32 = phex 32 56 | val hex64 = phex 64 57 | 58 | fun hx32 n = Word32.fmt StringCvt.HEX n 59 | fun hx64 n = Word64.fmt StringCvt.HEX n 60 | fun hxi n = Int.fmt StringCvt.HEX n 61 | fun hxi64 n = IntInf.fmt StringCvt.HEX n 62 | 63 | fun failExit s = ( print (s ^ "\n"); OS.Process.exit OS.Process.failure ) 64 | fun err e s = failExit ("Failed to " ^ e ^ " file \"" ^ s ^ "\"") 65 | 66 | fun debugPrint s = print("==DEBUG== "^s) 67 | fun debugPrintln s = print("==DEBUG== "^s^"\n") 68 | 69 | fun mkMask64 w = 70 | Word64.-(Word64.<<(Word64.fromInt 0x1, 71 | Word.fromInt (IntInf.toInt (BitsN.toUInt w))), 72 | Word64.fromInt 0x1) 73 | 74 | (* Bit vector utilities *) 75 | 76 | fun word8ToBits8 word8 = 77 | BitsN.fromInt (Word8.toLargeInt word8, 8) 78 | 79 | fun getByte v i = 80 | if i < Word8Vector.length v 81 | then word8ToBits8 (Word8Vector.sub (v, i)) 82 | else BitsN.zero 8 83 | 84 | (* Memory utilities *) 85 | 86 | (* TODO: this might be broken for big-endian code, but RISCV is 87 | little-endian by default. *) 88 | fun storeVecInMemHelper vec (base : int) (i : int) = 89 | let val j = 8*i; 90 | val bytes0 = List.tabulate (8, fn inner => getByte vec (j+inner)); 91 | val bytes1 = if !be then bytes0 else rev bytes0 92 | val bits64 = BitsN.concat bytes1 93 | val addr = IntInf.fromInt (base + j) 94 | val vlen = Word8Vector.length vec 95 | in if j < vlen 96 | then ( riscv.extRawWriteMem (BitsN.fromInt (addr, IntInf.fromInt 64), bits64) 97 | ; storeVecInMemHelper vec base (i+1) 98 | ) 99 | else 100 | if !trace_elf 101 | then print (Int.toString (Word8Vector.length vec) ^ " words.\n") 102 | else () 103 | end 104 | 105 | fun storeVecInMem (base : int, memsz : int, vec) = 106 | let val vlen = Word8Vector.length vec 107 | val padded = if memsz <= vlen then vec 108 | else ( 109 | let val pad = Word8Vector.tabulate 110 | (memsz - vlen, (fn _ => Word8.fromInt 0)) 111 | in Word8Vector.concat (vec :: pad :: []) 112 | end 113 | ) 114 | in storeVecInMemHelper padded base 0 115 | end 116 | 117 | (* Physical memory model: match Spike for now, but needs to be configurable. *) 118 | 119 | val RSTVEC_BASE = 0x1000 120 | val RSTVEC_SIZE = 0x20 121 | val DRAM_BASE = 0x80000000 122 | val DRAM_SIZE = 2048 * 1024 * 1024 123 | 124 | val physMemRanges : (IntInf.int * IntInf.int) list ref = 125 | ref [ (RSTVEC_BASE, RSTVEC_SIZE) 126 | , (DRAM_BASE, DRAM_SIZE) 127 | ] 128 | 129 | fun isMemoryAddr intAddr = 130 | List.exists (fn (s, r) => 131 | s <= intAddr andalso intAddr < (s + r) 132 | ) (!physMemRanges) 133 | 134 | (* Multi-core utilities *) 135 | 136 | fun currentCore () = 137 | BitsN.toInt (!riscv.procID) 138 | 139 | fun nextCoreToSchedule () = 140 | (1 + currentCore ()) mod !riscv.totalCore 141 | 142 | fun isLastCore () = 143 | BitsN.toInt (!riscv.procID) + 1 = !riscv.totalCore 144 | 145 | (* Printing utilities *) 146 | 147 | fun printLog () = 148 | ( List.app (fn (n, l) => 149 | if IntInf.toInt n <= !trace_lvl 150 | then print (l ^ "\n") 151 | else () 152 | ) (List.rev (!riscv.log)) 153 | ; riscv.clear_logs () 154 | ) 155 | 156 | local 157 | fun readReg i = hex64 (riscv.GPR (BitsN.fromNat (i, 5))) 158 | in 159 | fun dumpRegisters core = 160 | let val savedCore = currentCore () 161 | val pc = riscv.Map.lookup(!riscv.c_PC, core) 162 | in riscv.scheduleCore core 163 | ; printLog () 164 | ; print "====== Registers ======\n" 165 | ; print ("Core = " ^ IntInf.toString core 166 | ^ " at privilege " ^ riscv.privName (riscv.curPrivilege ()) 167 | ^ "\n") 168 | ; let val w = #instr (riscv.Delta ()) 169 | val i = riscv.Decode w 170 | in print ("Faulting instruction: (0x" ^ hex32 w ^ ") " 171 | ^ riscv.instructionToString i 172 | ^ "\n\n") 173 | end 174 | 175 | ; print ("PC " ^ hex64 pc ^ "\n") 176 | ; L3.for 177 | (IntInf.fromInt 0, IntInf.fromInt 31, 178 | fn i => 179 | print ("reg " ^ (if IntInf.< (i, 10) then " " else "") ^ 180 | IntInf.toString i ^ " " ^ readReg i ^ "\n")) 181 | ; riscv.scheduleCore savedCore 182 | end 183 | end 184 | 185 | local 186 | fun disRVC arch addr ilo = 187 | let val inst = riscv.DecodeRVC (arch, ilo) 188 | in print ("0x" ^ (L3.padLeftString(#"0", (10, BitsN.toHexString addr))) 189 | ^ ": 0x" ^ hex16 ilo 190 | ^ ": " ^ riscv.instructionToString inst 191 | ^ "\n" 192 | ) 193 | end 194 | fun disBase addr ilo ihi = 195 | let val word = BitsN.concat [ihi, ilo] 196 | val inst = riscv.Decode word 197 | in print ("0x" ^ (L3.padLeftString(#"0", (10, BitsN.toHexString addr))) 198 | ^ ": 0x" ^ hex32 word 199 | ^ ": " ^ riscv.instructionToString inst 200 | ^ "\n" 201 | ) 202 | end 203 | in 204 | fun disassemble arch pc range = 205 | if range <= 0 then () 206 | else let val addr = BitsN.fromInt (pc, IntInf.fromInt 64) 207 | val ilo = riscv.rawReadInstGranule (addr) 208 | val is_rvc = riscv.isRVC ilo 209 | in if is_rvc 210 | then ( disRVC arch addr ilo 211 | ; disassemble arch (pc + 2) (range - 2) 212 | ) 213 | else ( let val pc_plus_2 = BitsN.+(addr, BitsN.fromInt(2, 64)) 214 | in disBase addr ilo (riscv.rawReadInstGranule pc_plus_2) 215 | ; disassemble arch (pc + 4) (range - 4) 216 | end 217 | ) 218 | end 219 | end 220 | 221 | fun verifierTrace (lvl, str) = 222 | if lvl <= !verifier_trace_lvl 223 | then print (String.concat ["L3RISCV: ", str, "\n"]) 224 | else () 225 | 226 | (* Tandem verification: 227 | client interface: we are a client of an external oracle. *) 228 | 229 | fun setChecker hndl = 230 | checker := SOME hndl 231 | 232 | fun getChecker () = 233 | case !checker of 234 | NONE => failExit "Verification oracle not initialized!" 235 | | SOME h => h 236 | 237 | local 238 | fun check_pc t pc = 239 | Oracle.checkPC (t, BitsN.toUInt pc) 240 | fun check_reg t rdb rvb = 241 | let val (rd, rv) = (IntInf.toInt (BitsN.toUInt rdb), BitsN.toUInt rvb) 242 | in Oracle.checkGPR (t, rd, rv) end 243 | fun check_csr_int t rn rvb = 244 | let val rv = BitsN.toUInt rvb 245 | in Oracle.checkCSR (t, rn, rv) end 246 | fun check_csr t rnb rvb = 247 | let val rn = IntInf.toInt (BitsN.toUInt rnb) 248 | in check_csr_int t rn rvb end 249 | 250 | fun check_mstatus t rvb = check_csr_int t 0x300 rvb 251 | fun check_misa t rvb = check_csr_int t 0x301 rvb 252 | fun check_mepc t rvb = check_csr_int t 0x341 rvb 253 | fun check_mcause t rvb = check_csr_int t 0x342 rvb 254 | fun check_mtval t rvb = check_csr_int t 0x343 rvb 255 | fun check_sepc t rvb = check_csr_int t 0x141 rvb 256 | fun check_scause t rvb = check_csr_int t 0x142 rvb 257 | fun check_stval t rvb = check_csr_int t 0x143 rvb 258 | 259 | fun check_failed msg (chks, vals) = 260 | ( ListPair.app (fn (c, v) => if v then () 261 | else print (" mis-matched " ^ c ^ "\n") 262 | ) (chks, vals) 263 | ; if List.exists (fn b => not b) vals 264 | then ( print (msg ^ " verification error.\n") 265 | ; dumpRegisters (currentCore ()) 266 | ; failExit "Verification FAILED!\n" 267 | ) 268 | else () 269 | ) 270 | in 271 | 272 | fun doInitCheck () = 273 | let val t = getChecker () 274 | val priv_ok = Oracle.checkPriv (t, riscv.curPrivilege ()) 275 | val pc_ok = Oracle.checkPC (t, BitsN.toUInt (riscv.PC ())) 276 | val isa_ok = check_misa t (riscv.reg'misa (#misa (riscv.MCSR ()))) 277 | val ms_ok = check_mstatus t (riscv.reg'mstatus (#mstatus (riscv.MCSR ()))) 278 | val regs_ok = ref [] 279 | in check_failed "Initialization" ( ["privilege", "pc", "misa", "mstatus"] 280 | , [priv_ok, pc_ok, isa_ok, ms_ok] 281 | ) 282 | ; L3.for 283 | (IntInf.fromInt 0, IntInf.fromInt 31, 284 | fn i => 285 | let val rd = BitsN.fromNat (i, 5) 286 | val rv = riscv.GPR rd 287 | in regs_ok := (check_reg t rd rv) :: !regs_ok 288 | end 289 | ) 290 | ; check_failed "Initialization" ( (List.map (fn _ => "reg") (!regs_ok)) 291 | , !regs_ok 292 | ) 293 | ; print ("Initialization state passed checks.\n") 294 | end 295 | fun doCheck () = 296 | let val delta = riscv.Delta () 297 | val t = getChecker () 298 | val priv_ok = Oracle.checkPriv (t, (#priv delta)) 299 | val pc_ok = Oracle.checkPC (t, BitsN.toUInt (#pc delta)) 300 | (* TODO: inst check *) 301 | val reg_ok = case #reg_effect delta of 302 | NONE => true 303 | | SOME (rd, rv) => check_reg t rd rv 304 | val csr_ok = case #csr_effect delta of 305 | NONE => true 306 | | SOME (rd, rv) => check_csr t rd rv 307 | val ms_ok = check_mstatus t (#mstatus delta) 308 | 309 | val mpc_ok = case #mepc delta of 310 | NONE => true 311 | | SOME rv => check_mepc t rv 312 | val mc_ok = case #mcause delta of 313 | NONE => true 314 | | SOME rv => check_mcause t rv 315 | val mtv_ok = case #mtval delta of 316 | NONE => true 317 | | SOME rv => check_mtval t rv 318 | 319 | val spc_ok = case #sepc delta of 320 | NONE => true 321 | | SOME rv => check_sepc t rv 322 | val sc_ok = case #scause delta of 323 | NONE => true 324 | | SOME rv => check_scause t rv 325 | val stv_ok = case #stval delta of 326 | NONE => true 327 | | SOME rv => check_stval t rv 328 | 329 | in check_failed "State" ( [ "privilege", "pc", "reg", "csr", "mstatus" 330 | , "mepc", "mcause", "mtval" 331 | , "sepc", "scause", "stval" 332 | ] 333 | , [ priv_ok, pc_ok, reg_ok, csr_ok, ms_ok 334 | , mpc_ok, mc_ok, mtv_ok 335 | , spc_ok, sc_ok, stv_ok 336 | ] 337 | ) 338 | end 339 | end 340 | 341 | fun isCheckerDone () = 342 | Oracle.isDone (getChecker ()) 343 | 344 | (* Code execution *) 345 | 346 | fun logLoop mx i = 347 | ( riscv.scheduleCore (nextCoreToSchedule ()) 348 | ; riscv.Next () 349 | ; print ("\n") 350 | ; printLog () 351 | ; if !check 352 | then ( Oracle.step (getChecker ()) 353 | ; doCheck() 354 | ) 355 | else () 356 | ; if !riscv.done orelse i = mx orelse (!check andalso isCheckerDone ()) 357 | then ( print ("ExitCode: " ^ Nat.toString (riscv.exitCode ()) ^ "\n") 358 | ; print ("Completed " ^ Int.toString (i + 1) ^ " instructions.\n") 359 | ) 360 | else logLoop mx (if isLastCore () then (i + 1) else i) 361 | ) 362 | 363 | local 364 | fun t f x = if !time_run then Runtime.time f x else f x 365 | in 366 | fun run mx = 367 | ( t (logLoop mx) 0 368 | ; if !riscv.done 369 | then Unix.exit (Word8.fromInt (IntInf.toInt (Nat.toInt (riscv.exitCode ())))) 370 | else () 371 | ) 372 | 373 | fun runWrapped mx = 374 | run mx 375 | handle riscv.UNDEFINED s => 376 | ( dumpRegisters (currentCore ()) 377 | ; failExit ("UNDEFINED \"" ^ s ^ "\"\n") 378 | ) 379 | | riscv.INTERNAL_ERROR s => 380 | ( dumpRegisters (currentCore ()) 381 | ; failExit ("INTERNAL_ERROR \"" ^ s ^ "\"\n") 382 | ) 383 | end 384 | 385 | (* Platform initialization *) 386 | 387 | fun physAddrIsMemory (a : BitsN.nbit, n : IntInf.int) = 388 | let val addr = BitsN.toInt a 389 | val nend = IntInf.-(n, 1) 390 | in isMemoryAddr addr andalso isMemoryAddr (IntInf.+(addr, nend)) 391 | end 392 | 393 | fun initPlatform cores = 394 | ( riscv.print := debugPrint 395 | ; riscv.println := debugPrintln 396 | ; riscv.procID := BitsN.B(0, BitsN.size(!riscv.procID)) 397 | ; riscv.totalCore := cores 398 | ; riscv.initMem (BitsN.fromInt 399 | ((if !check then 0xaaaaaaaaAAAAAAAA else 0x0) 400 | , 64)) 401 | ; riscv.validMemAddrPred := physAddrIsMemory 402 | 403 | (* Select platform implementation choices. *) 404 | ; riscv.enable_dirty_update := !enable_dirty_update 405 | ; riscv.enable_misaligned_access := !enable_misaligned_access 406 | 407 | (* Set up external oracle if needed. *) 408 | ; if !check 409 | then ( let val t = Oracle.init ("RV64IMAFDC") 410 | in setChecker t 411 | (* override platform choices with those of oracle. *) 412 | ; riscv.enable_dirty_update := Oracle.isDirtyEnabled t 413 | ; riscv.enable_misaligned_access := Oracle.isMisalignedEnabled t 414 | end 415 | ) 416 | else () 417 | ) 418 | 419 | (* assumes riscv.procID is 0 *) 420 | fun initCores (arch, pc) = 421 | ( riscv.initIdent arch 422 | ; riscv.initMachine (!riscv.procID) 423 | ; riscv.initRegs pc 424 | ; if isLastCore () 425 | then () (* core scheduler will wrap back to first core *) 426 | else ( riscv.scheduleCore (nextCoreToSchedule ()) 427 | ; initCores (arch, pc) 428 | ) 429 | ) 430 | 431 | (* Spike-compatible reset vector *) 432 | 433 | fun insertResetVec pc = 434 | (* reset vector from Spike, sim.cc:make_dtb() *) 435 | let val rst_vec_sz = 8 * 4 436 | val auipc_imm = BitsN.B(0x0, 20) 437 | val rvsz_imm = BitsN.fromInt (rst_vec_sz, 12) 438 | val mhartid_imm = BitsN.B((0x0F14: IntInf.int), 12) 439 | val boot_code = 440 | [ (* auipc t0, 0x0 *) 441 | riscv.ArithI(riscv.AUIPC(BitsN.fromNat (5, 5), auipc_imm)) 442 | , (* addi a1, t0, reset_vec_size *) 443 | riscv.ArithI(riscv.ADDI(BitsN.fromNat (11, 5), (BitsN.fromInt (5, 5), rvsz_imm))) 444 | , (* csrr a0, mhartid *) 445 | riscv.System(riscv.CSRRS(BitsN.fromNat (10, 5), (BitsN.fromNat (0, 5), mhartid_imm))) 446 | , (* ld t0, 24(t0) TODO: xlen=32 => lw t0, 24(t0) *) 447 | riscv.Load(riscv.LD(BitsN.fromNat (5, 5), (BitsN.fromNat (5, 5), BitsN.fromNat (24, 12)))) 448 | , (* jr t0 *) 449 | riscv.Branch(riscv.JALR(BitsN.fromNat (0, 5), (BitsN.fromNat (5, 5), BitsN.zero 12))) 450 | ] 451 | val pc_int = IntInf.fromInt pc 452 | val pc_lo = IntInf.andb (pc_int, 0xFFFFFFFF) 453 | val pc_hi = IntInf.~>> (pc_int, Word.fromInt 32) 454 | fun insert (addr : IntInf.int) words = 455 | case words of 456 | w :: tl => ( riscv.rawWriteMem (BitsN.fromInt (addr, 64), (w, 4)) 457 | ; insert (IntInf.+ (addr, 4)) tl 458 | ) 459 | | [] => addr 460 | in print ("L3RISCV: Loading reset code at " ^ hxi reset_addr ^ "\n") 461 | ; let val pc_addr = 462 | insert (IntInf.fromInt reset_addr) (List.map riscv.Encode boot_code) 463 | in printLog () 464 | (* the reset-vector terminates with a 64-bit aligned start-address. *) 465 | ; insert pc_addr [ BitsN.fromInt ( 0x0, 32) (* align *) 466 | , BitsN.fromInt (pc_lo, 32) 467 | , BitsN.fromInt (pc_hi, 32) 468 | ] 469 | ; printLog () 470 | end 471 | end 472 | 473 | 474 | (* Program load *) 475 | 476 | fun loadElf arch segms dis = 477 | List.app (fn s => 478 | if (#ptype s) = Elf.PT_LOAD 479 | then (let val vaddr = Int.fromLarge (#vaddr s) 480 | val memsz = Int.fromLarge (#memsz s) 481 | val mem_end = IntInf.toInt (IntInf.+ (!mem_base_addr, !mem_size)) 482 | in 483 | if !trace_elf 484 | then ( print ("Loading segment @ vaddr=" ^ hxi vaddr 485 | ^ ", " ^ Int.toString memsz ^ " bytes ...\n") 486 | ; Elf.printSegment s 487 | ) 488 | else () 489 | ; storeVecInMem (vaddr, memsz, (#bytes s)) 490 | (* update memory range *) 491 | ; if vaddr < IntInf.toInt (!mem_base_addr) 492 | then mem_base_addr := IntInf.fromInt vaddr 493 | else () 494 | ; if vaddr + memsz > mem_end 495 | then mem_size := IntInf.fromInt (vaddr + memsz - mem_end) 496 | else () 497 | (* TODO: should check flags for executable segment *) 498 | ; if dis then disassemble arch (#vaddr s) (#memsz s) 499 | else () 500 | end) 501 | else ( print ("Skipping segment ...\n") 502 | ; Elf.printSegment s 503 | ) 504 | ) segms 505 | 506 | fun match_symb (name : string) (s : Elf.symb) = 507 | case (#syname s) of 508 | NONE => false 509 | | SOME nm => Substring.string nm = name 510 | 511 | fun set_tohost (tohost : Elf.symb option) = 512 | case tohost of 513 | NONE => 514 | print "L3RISCV: no tohost symbol found!\n" 515 | | SOME s => 516 | let val addr = Int.fromLarge (#syvalue s) 517 | in print ("L3RISCV: tohost mapped to 0x" ^ (hxi addr) ^ "\n") 518 | ; riscv.htif_tohost_addr := BitsN.fromInt(IntInf.fromInt addr, IntInf.fromInt 64) 519 | end 520 | 521 | fun setupElf file dis = 522 | let val elf = Elf.openElf file 523 | val hdr = Elf.getHeader elf 524 | val segms = Elf.getSegments elf hdr 525 | val sects = Elf.getSections elf hdr 526 | val nsects = Elf.getNamedSections elf hdr sects 527 | val symbs = Elf.getSymbols elf hdr nsects 528 | val pc = if !boot then reset_addr else (LargeInt.toInt (#entry hdr)) 529 | val tohost = List.find (match_symb "tohost") symbs 530 | val arch = if (#class hdr) = Elf.BIT_32 then riscv.RV32 else riscv.RV64 531 | in set_tohost tohost 532 | (* FIXME: instead of presenting as RV64 in misa/mstatus for RV32 ELF 533 | * files, we should support presenting as RV32 using writable xXLs. This 534 | * should be made to support tandem-verification with spike. 535 | *) 536 | ; initCores ( riscv.RV64 (* TODO: use arch instead? *) 537 | , IntInf.fromInt pc 538 | ) 539 | ; print ("L3RISCV: pc set to 0x" ^ (hx64 (Word64.fromInt pc)) 540 | ^ (if !boot then " [boot-entry] " else " [elf-entry]") 541 | ^ " in file " ^ file ^ "\n") 542 | ; if !trace_elf 543 | then ( print "Loading elf file ...\n" 544 | ; Elf.printHeader hdr 545 | ; List.app Elf.printNamedSection nsects 546 | ; List.app Elf.printSymbol symbs 547 | ) 548 | else () 549 | ; be := (if (#endian hdr = Elf.BIG) then true else false) 550 | ; loadElf arch segms dis 551 | ; if !trace_elf 552 | then ( print ("\nMem base: " ^ (hxi64 (!mem_base_addr))) 553 | ; print ("\nMem size: " ^ (hxi64 (!mem_size)) 554 | ^ " (" ^ (IntInf.fmt StringCvt.DEC (!mem_size)) ^ ")\n") 555 | ) 556 | else () 557 | ; printLog () 558 | (* FIXME: this spike-specific behaviour of loading a reset-vector in a debug-rom 559 | * should be conditioned by an appropriate flag. *) 560 | ; if !boot 561 | then insertResetVec (LargeInt.toInt (#entry hdr)) 562 | else () 563 | end 564 | 565 | fun doElf cycles file dis = 566 | ( setupElf file dis 567 | ; if !check 568 | then ( Oracle.loadElf (getChecker (), file) 569 | ; Oracle.reset (getChecker ()) 570 | ; doInitCheck () 571 | ) 572 | else () 573 | ; if dis 574 | then printLog () 575 | else runWrapped cycles 576 | ) 577 | 578 | (* Tandem verification: 579 | server interface: verify against model *) 580 | 581 | (* TODO *) 582 | fun initModel () = 583 | () 584 | 585 | (* Command line interface *) 586 | 587 | fun printUsage () = 588 | print 589 | ("\nRISCV emulator (based on an L3 specification).\n\ 590 | \http://www.cl.cam.ac.uk/~acjf3/l3\n\n\ 591 | \usage: " ^ OS.Path.file (CommandLine.name ()) ^ " [arguments] file\n\n\ 592 | \Arguments:\n\ 593 | \ --dis only disassemble loaded code\n\ 594 | \ --cycles upper bound on instruction cycles\n\ 595 | \ --trace verbosity level (0 default, 4 maximum)\n\ 596 | \ --multi <#cores> number of cores (1 default)\n\ 597 | \ --check check execution against external verifier\n\ 598 | \ --boot set starting pc to reset address x1000 (false default)\n\ 599 | \ --pte-update update PTE on page-table walks (false default, not used with --check)\n\ 600 | \ --misaligned enable non-trapping misaligned accesses (false default, not used with --check)\n\ 601 | \ -h or --help print this message\n\n") 602 | 603 | fun getNumber s = 604 | case IntExtra.fromString s of 605 | SOME n => n 606 | | NONE => failExit ("Bad number: " ^ s) 607 | 608 | fun getBool s = 609 | case Bool.fromString s of 610 | SOME b => b 611 | | NONE => failExit ("Bad bool: " ^ s) 612 | 613 | fun getArguments () = 614 | List.map 615 | (fn "-c" => "--cycles" 616 | | "-t" => "--trace" 617 | | "-d" => "--dis" 618 | | "-h" => "--help" 619 | | "-k" => "--check" 620 | | "-m" => "--multi" 621 | | "-v" => "--verifier" 622 | | "-b" => "--boot" 623 | | "-pu" => "--pte-update" 624 | | "-ma" => "--misaligned" 625 | | s => s 626 | ) (CommandLine.arguments ()) 627 | 628 | fun processOption (s: string) = 629 | let fun loop acc = 630 | fn a :: b :: r => 631 | if a = s 632 | then (SOME b, List.rev acc @ r) 633 | else loop (a :: acc) (b :: r) 634 | | r => (NONE, List.rev acc @ r) 635 | in loop [] 636 | end 637 | 638 | fun model_main () = 639 | case getArguments () of 640 | ["--help"] => printUsage () 641 | | ["-h"] => printUsage () 642 | | l => 643 | let val (c, l) = processOption "--cycles" l 644 | val (t, l) = processOption "--trace" l 645 | val (d, l) = processOption "--dis" l 646 | val (k, l) = processOption "--check" l 647 | val (m, l) = processOption "--multi" l 648 | val (v, l) = processOption "--verifier" l 649 | val (b, l) = processOption "--boot" l 650 | val (p, l) = processOption "--pte-update" l 651 | val (a, l) = processOption "--misaligned" l 652 | 653 | val c = Option.getOpt (Option.map getNumber c, ~1) 654 | val d = Option.getOpt (Option.map getBool d, !trace_elf) 655 | val t = Option.getOpt (Option.map getNumber t, 656 | (IntInf.fromInt (!trace_lvl))) 657 | 658 | val m = Option.getOpt (Option.map getNumber m, 1) 659 | val k = Option.getOpt (Option.map getBool k, !check) 660 | val v = Option.getOpt (Option.map getBool v, !verifier_mode) 661 | val b = Option.getOpt (Option.map getBool b, !boot) 662 | 663 | val p = Option.getOpt (Option.map getBool p, !enable_dirty_update) 664 | val a = Option.getOpt (Option.map getBool a, !enable_misaligned_access) 665 | 666 | val () = trace_lvl := Int.max (0, IntInf.toInt t) 667 | val () = check := k 668 | val () = trace_elf := d 669 | val () = verifier_mode := v 670 | val () = boot := b 671 | 672 | val () = enable_dirty_update := p 673 | val () = enable_misaligned_access := a 674 | 675 | in if List.null l andalso not (!verifier_mode) 676 | then printUsage () 677 | else ( initPlatform (m) 678 | ; if !verifier_mode 679 | then initModel () 680 | else doElf (IntInf.toInt c) (List.hd l) d 681 | ) 682 | end 683 | -------------------------------------------------------------------------------- /src/sml/oracle.sig: -------------------------------------------------------------------------------- 1 | (* Copyright (C) 2018 SRI International. 2 | * 3 | * This software was developed by SRI International and the University of 4 | * Cambridge Computer Laboratory under DARPA/AFRL contract FA8650-18-C-7809 5 | * ("CIFV"), and under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of 6 | * the DARPA SSITH research programme. 7 | * 8 | * See the LICENSE file for details. 9 | *) 10 | 11 | (* 12 | * This interface allows checking the model against an external oracle like the 13 | * Spike simulator. 14 | *) 15 | 16 | signature Oracle = 17 | sig 18 | type t 19 | 20 | (* initialization and lifecycle *) 21 | val init : string -> t 22 | val setVerbose : t * bool -> unit 23 | val loadElf : t * string -> unit (* TODO: cross-check the parsed entry point? *) 24 | val reset : t -> unit 25 | val step : t -> unit 26 | val isDone : t -> bool 27 | 28 | (* query the oracle's mmu configuration *) 29 | val isDirtyEnabled : t -> bool 30 | val isMisalignedEnabled : t -> bool 31 | 32 | (* checks *) 33 | val checkPC : t * IntInf.int -> bool 34 | val checkPriv : t * riscv.Privilege -> bool 35 | val checkGPR : t * int * IntInf.int -> bool 36 | val checkCSR : t * int * IntInf.int -> bool 37 | end 38 | -------------------------------------------------------------------------------- /src/sml/poly_model.sml: -------------------------------------------------------------------------------- 1 | (* Copyright (C) 2018 SRI International. 2 | * 3 | * This software was developed by SRI International and the University of 4 | * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-11-C-0249 5 | * ("MRC2"), as part of the DARPA MRC research programme, and under 6 | * DARPA/AFRL contract FA8750-10-C-0237 ("CTSRD"), as part of the DARPA 7 | * CRASH research programme, and under DARPA/AFRL contract FA8650-18-C-7809 8 | * ("CIFV"), and under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of 9 | * the DARPA SSITH research programme. 10 | * 11 | * See the LICENSE file for details. 12 | *) 13 | 14 | (* Wrapper around Model.main() to catch runtime-specific FFI exceptions. *) 15 | 16 | open Foreign 17 | 18 | fun main () = 19 | model_main () 20 | handle Foreign msg => 21 | ( print ("FFI error: " ^ msg ^ "\n") 22 | ; print ("Either Spike support has not been built (see ENABLE_TVSPIKE in the Makefile), " 23 | ^ "or is out of date.\n") 24 | ) 25 | | e => print ("Exception error:" ^ (exnMessage e) ^ "\n") 26 | -------------------------------------------------------------------------------- /src/sml/poly_run.sml: -------------------------------------------------------------------------------- 1 | (* Copyright (C) 2014, 2015 Anthony Fox, University of Cambridge 2 | * Copyright (C) 2014, 2015 Alexandre Joannou, University of Cambridge 3 | * Copyright (C) 2015-2018 SRI International. 4 | * 5 | * This software was developed by SRI International and the University of 6 | * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-11-C-0249 7 | * ("MRC2"), as part of the DARPA MRC research programme, and under 8 | * DARPA/AFRL contract FA8750-10-C-0237 ("CTSRD"), as part of the DARPA 9 | * CRASH research programme, under DARPA/AFRL contract FA8650-18-C-7809 10 | * ("CIFV"), and under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of 11 | * the DARPA SSITH research programme. 12 | * 13 | * See the LICENSE file for details. 14 | *) 15 | 16 | (* Outer shell for Poly/ML. *) 17 | 18 | val L3_LIB_PATH = 19 | let 20 | val tmp = OS.FileSys.tmpName () 21 | in 22 | if OS.Process.isSuccess (OS.Process.system ("l3 --lib-path > " ^ tmp)) 23 | then let 24 | val strm = TextIO.openIn tmp 25 | val s = TextIO.inputAll strm 26 | before (TextIO.closeIn strm; OS.FileSys.remove tmp) 27 | in 28 | OS.Path.fromString (String.substring (s, 0, String.size s - 1)) 29 | end 30 | else raise Fail "Failed to get L3_LIB_PATH" 31 | end 32 | 33 | local 34 | fun path {vol, isAbs, arcs} s e = 35 | let 36 | val f = OS.Path.joinBaseExt {base = s, ext = SOME e} 37 | val s = OS.Path.toString {vol = vol, isAbs = isAbs, arcs = arcs @ [f]} 38 | in 39 | if OS.FileSys.access (s, [OS.FileSys.A_READ]) then ( 40 | (* print ("Using: " ^ s ^ "\n"); *) 41 | PolyML.use s 42 | ) else ( 43 | (* print ("Not using: " ^ s ^ "\n"); *) 44 | () 45 | ) 46 | end 47 | in 48 | fun useSigSml a s = List.app (path a s) ["sig", "sml"] 49 | end 50 | 51 | structure Real64 = LargeReal 52 | structure PackReal64Little = PackRealLittle 53 | 54 | val () = 55 | List.app (useSigSml L3_LIB_PATH) 56 | ["IntExtra", "Nat", "Set", "L3", "Bitstring", "BitsN", "Ptree", "Map", 57 | "MutableMapFunctor", "MutableMap16", "Runtime"]; 58 | 59 | val () = L3.setLibDir (OS.Path.toString L3_LIB_PATH); 60 | 61 | val () = 62 | List.app (useSigSml L3_LIB_PATH) 63 | ["SSE", "PolySSE", "FP", "FP32", "FP64", "FPConvert"]; 64 | 65 | val () = 66 | let 67 | val cur_file = PolyML.getUseFileName () 68 | val cur_dir = (case cur_file of 69 | NONE => "." 70 | | SOME p => OS.Path.dir p) 71 | val cur_path = OS.Path.fromString cur_dir 72 | val lib_dir = OS.Path.joinDirFile {dir = cur_dir, file = "lib"} 73 | val lib_path = OS.Path.fromString lib_dir 74 | in 75 | (* (case cur_file of 76 | NONE => print ("No use-file found for main module\n") 77 | | SOME p => print ("Cur file: '" ^ p ^ "'\n")); *) 78 | useSigSml lib_path "Elf"; 79 | List.app (useSigSml cur_path) ["riscv", "oracle", "poly_spike", "model", "poly_model"] 80 | end 81 | -------------------------------------------------------------------------------- /src/sml/poly_spike.sml: -------------------------------------------------------------------------------- 1 | (* Copyright (C) 2018 SRI International. 2 | * 3 | * This software was developed by SRI International and the University of 4 | * Cambridge Computer Laboratory under DARPA/AFRL contract FA8650-18-C-7809 5 | * ("CIFV"), and under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of 6 | * the DARPA SSITH research programme. 7 | * 8 | * See the LICENSE file for details. 9 | *) 10 | 11 | structure Oracle :> Oracle = 12 | struct 13 | 14 | open Foreign 15 | 16 | type t = Memory.voidStar 17 | 18 | (* 19 | * The code in this library is executed at compile-time, due to the 20 | * way Poly/ML compiler works. So, loadLibrary of libtv_spike.so is actually 21 | * performed during compilation. In l3riscv/Makefile, we ensure that 22 | * libtv_spike.so has already been built and placed in the proper location, 23 | * so that the loadLibrary succeeds when this file is compiled. 24 | * We are assuming that getDir() returns the location of the Makefile, 25 | * which works even when built using 'make -C ...'. 26 | *) 27 | fun getLib () = 28 | loadLibrary (OS.Path.joinDirFile {dir = OS.FileSys.getDir (), 29 | file = "libtv_spike.so"}) 30 | 31 | val getSym = getSymbol (getLib ()) 32 | 33 | fun toBool i = if i = 0 then false else true 34 | fun fromBool b = if b then 1 else 0 35 | 36 | val init : string -> t = 37 | buildCall2 (getSym "tv_init", (cString, cInt), cPointer) o (fn s => (s, 0)) 38 | 39 | val setVerbose : (t * bool) -> unit = 40 | buildCall2 (getSym "tv_set_verbose", (cPointer, cInt), cVoid) 41 | o (fn (t, b) => (t, fromBool b)) 42 | 43 | val isDirtyEnabled : t -> bool = 44 | toBool o buildCall1 (getSym "tv_is_dirty_enabled", cPointer, cInt) 45 | 46 | val isMisalignedEnabled : t -> bool = 47 | toBool o buildCall1 (getSym "tv_is_misaligned_enabled", cPointer, cInt) 48 | 49 | val isDone : t -> bool = 50 | toBool o buildCall1 (getSym "tv_is_done", cPointer, cInt) 51 | 52 | val loadElf : (t * string) -> unit = 53 | buildCall2 (getSym "tv_load_elf", (cPointer, cString), cVoid) 54 | 55 | val reset : t -> unit = 56 | buildCall1 (getSym "tv_reset", cPointer, cVoid) 57 | 58 | val step : t -> unit = 59 | buildCall1 (getSym "tv_step", cPointer, cVoid) 60 | 61 | val isDone : t -> bool = 62 | toBool o buildCall1 (getSym "tv_is_done", cPointer, cInt) 63 | 64 | val checkPC : (t * IntInf.int) -> bool = 65 | toBool o buildCall2 (getSym "tv_check_pc", (cPointer, cUint64Large), cInt) 66 | o (fn (t, i) => (t, IntInf.toLarge i)) 67 | 68 | val checkPriv : (t * riscv.Privilege) -> bool = 69 | toBool o buildCall2 (getSym "tv_check_priv", (cPointer, cUint8), cInt) 70 | o (fn (t, p) => (t, case p of riscv.User => 0 71 | | riscv.Supervisor => 1 72 | | riscv.Machine => 3)) 73 | 74 | val checkGPR : (t * int * IntInf.int) -> bool = 75 | toBool o buildCall3 (getSym "tv_check_gpr", (cPointer, cUint64, cUint64Large), cInt) 76 | o (fn (t, rno, rval) => (t, rno, IntInf.toLarge rval)) 77 | 78 | val checkCSR : (t * int * IntInf.int) -> bool = 79 | toBool o buildCall3 (getSym "tv_check_csr", (cPointer, cUint64, cUint64Large), cInt) 80 | o (fn (t, rno, rval) => (t, rno, IntInf.toLarge rval)) 81 | 82 | end 83 | --------------------------------------------------------------------------------