diff --git a/c_emulator/riscv_callbacks.cpp b/c_emulator/riscv_callbacks.cpp index dff3c209a..bb97d8b64 100644 --- a/c_emulator/riscv_callbacks.cpp +++ b/c_emulator/riscv_callbacks.cpp @@ -133,3 +133,37 @@ unit trap_callback(bool is_interrupt, fbits cause) } return UNIT; } +// Page table walk callback +unit ptw_start_callback(uint64_t vpn, struct zMemoryAccessTypezIuzK access_type, + enum zPrivilege privilege) +{ + for (auto c : callbacks) { + c->ptw_start_callback(vpn, access_type, privilege); + } + return UNIT; +} + +unit ptw_step_callback(sail_int level, sbits pte_addr, uint64_t pte) +{ + for (auto c : callbacks) { + c->ptw_step_callback(level, pte_addr, pte); + } + return UNIT; +} + +unit ptw_success_callback(uint64_t final_ppn, sail_int level) +{ + for (auto c : callbacks) { + c->ptw_success_callback(final_ppn, level); + } + return UNIT; +} + +unit ptw_fail_callback(struct zPTW_Error error_type, sail_int level, + sbits pte_addr) +{ + for (auto c : callbacks) { + c->ptw_fail_callback(error_type, level, pte_addr); + } + return UNIT; +} diff --git a/c_emulator/riscv_callbacks.h b/c_emulator/riscv_callbacks.h index de03ef75e..6d8bc7476 100644 --- a/c_emulator/riscv_callbacks.h +++ b/c_emulator/riscv_callbacks.h @@ -1,5 +1,6 @@ #pragma once #include "sail.h" +#include "sail_riscv_model.h" #ifdef __cplusplus extern "C" { @@ -25,6 +26,13 @@ unit vreg_write_callback(unsigned reg, lbits value); unit pc_write_callback(sbits new_pc); unit redirect_callback(sbits new_pc); unit trap_callback(bool is_interrupt, fbits cause); +// Page table walk callbacks +unit ptw_start_callback(uint64_t vpn, struct zMemoryAccessTypezIuzK access_type, + enum zPrivilege privilege); +unit ptw_step_callback(sail_int level, sbits pte_addr, uint64_t pte); +unit ptw_success_callback(uint64_t final_ppn, sail_int level); +unit ptw_fail_callback(struct zPTW_Error error_type, sail_int level, + sbits pte_addr); #ifdef __cplusplus } diff --git a/c_emulator/riscv_callbacks_if.h b/c_emulator/riscv_callbacks_if.h index 40ff9377a..9e00635bc 100644 --- a/c_emulator/riscv_callbacks_if.h +++ b/c_emulator/riscv_callbacks_if.h @@ -1,6 +1,7 @@ #pragma once #include "sail.h" +#include "sail_riscv_model.h" // This class is not declared in "riscv_callbacks.h" as that file is // included in the C model generated by Sail. @@ -66,6 +67,27 @@ class callbacks_if { [[maybe_unused]] fbits cause) { } + // Page table walk callbacks + virtual void + ptw_start_callback([[maybe_unused]] uint64_t vpn, + [[maybe_unused]] struct zMemoryAccessTypezIuzK access_type, + [[maybe_unused]] enum zPrivilege privilege) + { + } + virtual void ptw_step_callback([[maybe_unused]] sail_int level, + [[maybe_unused]] sbits pte_addr, + [[maybe_unused]] uint64_t pte) + { + } + virtual void ptw_success_callback([[maybe_unused]] uint64_t final_ppn, + [[maybe_unused]] sail_int level) + { + } + virtual void ptw_fail_callback([[maybe_unused]] struct zPTW_Error error_type, + [[maybe_unused]] sail_int level, + [[maybe_unused]] sbits pte_addr) + { + } }; void register_callback(callbacks_if *cb); diff --git a/c_emulator/riscv_callbacks_log.cpp b/c_emulator/riscv_callbacks_log.cpp index 1637668c0..160f052b2 100644 --- a/c_emulator/riscv_callbacks_log.cpp +++ b/c_emulator/riscv_callbacks_log.cpp @@ -20,10 +20,12 @@ void print_lbits_hex(FILE *trace_log, lbits val, int length = 0) log_callbacks::log_callbacks(bool config_print_reg, bool config_print_mem_access, - bool config_use_abi_names, FILE *trace_log) + bool config_print_ptw, bool config_use_abi_names, + FILE *trace_log) : config_print_reg(config_print_reg) , config_print_mem_access(config_print_mem_access) , config_use_abi_names(config_use_abi_names) + , config_print_ptw(config_print_ptw) , trace_log(trace_log) { } @@ -101,3 +103,56 @@ void log_callbacks::vreg_write_callback(unsigned reg, lbits value) print_lbits_hex(trace_log, value); } } + +// Page table walk callback +void log_callbacks::ptw_start_callback( + uint64_t vpn, struct zMemoryAccessTypezIuzK access_type, + enum zPrivilege privilege) +{ + if (trace_log != nullptr && config_print_ptw) { + sail_string str_ac, str_pr; + CREATE(sail_string)(&str_ac); + CREATE(sail_string)(&str_pr); + zaccessType_to_str(&str_ac, access_type); + zprivLevel_to_str(&str_pr, privilege); + fprintf(trace_log, + "PTW: Start, vpn=0x%" PRIx64 ", access_type=%s, privilege=%s", vpn, + str_ac, str_pr); + KILL(sail_string)(&str_ac); + KILL(sail_string)(&str_pr); + } +} + +void log_callbacks::ptw_step_callback(sail_int level, sbits pte_addr, + uint64_t pte) +{ + if (trace_log != nullptr && config_print_ptw) { + gmp_fprintf(trace_log, + "PTW: Step, level=0x%ZX, pte=0x%" PRIX64 ", pte_addr=0x%" PRIX64 + "\n", + level, pte, pte_addr.bits); + } +} + +void log_callbacks::ptw_success_callback(uint64_t final_ppn, sail_int level) +{ + if (trace_log != nullptr && config_print_ptw) { + gmp_fprintf(trace_log, "PTW: Success, final_ppn=0x%" PRIx64 ", level=%ZX", + final_ppn, level); + } +} + +void log_callbacks::ptw_fail_callback(struct zPTW_Error error_type, + sail_int level, sbits pte_addr) +{ + // failed trace is always available + if (trace_log != nullptr) { + sail_string str_et; + CREATE(sail_string)(&str_et); + zptw_error_to_str(&str_et, error_type); + gmp_fprintf(trace_log, + "PTW: failed, error=%s, level=%ZX, pte_addr=0x%" PRIX64 "\n", + str_et, level, pte_addr.bits); + KILL(sail_string)(&str_et); + } +} diff --git a/c_emulator/riscv_callbacks_log.h b/c_emulator/riscv_callbacks_log.h index fa5c24836..8bb68884d 100644 --- a/c_emulator/riscv_callbacks_log.h +++ b/c_emulator/riscv_callbacks_log.h @@ -7,7 +7,8 @@ class log_callbacks : public callbacks_if { public: log_callbacks(bool config_print_reg = true, bool config_print_mem_access = true, - bool config_use_abi_names = false, FILE *trace_log = nullptr); + bool config_print_ptw = true, bool config_use_abi_names = false, + FILE *trace_log = nullptr); // callbacks_if void mem_write_callback(const char *type, sbits paddr, uint64_t width, @@ -22,10 +23,19 @@ class log_callbacks : public callbacks_if { void csr_full_read_callback(const_sail_string csr_name, unsigned reg, sbits value) override; void vreg_write_callback(unsigned reg, lbits value) override; + // Page table walk callback + void ptw_start_callback(uint64_t vpn, + struct zMemoryAccessTypezIuzK access_type, + enum zPrivilege privilege) override; + void ptw_step_callback(sail_int level, sbits pte_addr, uint64_t pte) override; + void ptw_success_callback(uint64_t final_ppn, sail_int level) override; + void ptw_fail_callback(struct zPTW_Error error_type, sail_int /*level*/, + sbits pte_addr) override; private: bool config_print_reg; bool config_print_mem_access; bool config_use_abi_names; + bool config_print_ptw; FILE *trace_log; }; diff --git a/c_emulator/riscv_sim.cpp b/c_emulator/riscv_sim.cpp index 5d5faac07..1fa3bbc75 100644 --- a/c_emulator/riscv_sim.cpp +++ b/c_emulator/riscv_sim.cpp @@ -75,6 +75,7 @@ bool config_print_htif = false; bool config_print_pma = false; bool config_print_rvfi = false; bool config_print_step = false; +bool config_print_ptw = false; bool config_use_abi_names = false; bool config_enable_rvfi = false; @@ -170,6 +171,8 @@ static void setup_options(CLI::App &app) app.add_flag("--trace-instr", config_print_instr, "Enable trace output for instruction execution"); + app.add_flag("--trace-ptw", config_print_ptw, + "Enable trace output for Page Table walk"); app.add_flag("--trace-reg", config_print_reg, "Enable trace output for register access"); app.add_flag("--trace-mem", config_print_mem_access, @@ -215,6 +218,7 @@ static void setup_options(CLI::App &app) config_print_htif = true; config_print_pma = true; config_print_step = true; + config_print_ptw = true; config_print_reservation = true; }, "Enable all trace output"); @@ -644,7 +648,7 @@ int inner_main(int argc, char **argv) init_logs(); log_callbacks log_cbs(config_print_reg, config_print_mem_access, - config_use_abi_names, trace_log); + config_print_ptw, config_use_abi_names, trace_log); register_callback(&log_cbs); if (gettimeofday(&init_start, nullptr) < 0) { diff --git a/model/CMakeLists.txt b/model/CMakeLists.txt index f80fe6e05..8c40273a2 100644 --- a/model/CMakeLists.txt +++ b/model/CMakeLists.txt @@ -121,6 +121,8 @@ add_custom_command( --c-preserve rvfi_mem_exception --c-preserve rvfi_wX --c-preserve rvfi_trap + # Preserve to_str functions. + --c-preserve ptw_error_to_str # Input files. ${SAIL_MODULES} ${project_file} diff --git a/model/riscv.sail_project b/model/riscv.sail_project index 1bdda62ad..c4e92a84d 100644 --- a/model/riscv.sail_project +++ b/model/riscv.sail_project @@ -69,6 +69,7 @@ sys { sys/mem.sail, sys/vmem_pte.sail, sys/vmem_ptw.sail, + sys/callbacks.sail, sys/vmem_tlb.sail, sys/vmem.sail, sys/insts_begin.sail, diff --git a/model/sys/callbacks.sail b/model/sys/callbacks.sail new file mode 100644 index 000000000..cdb3f0e7b --- /dev/null +++ b/model/sys/callbacks.sail @@ -0,0 +1,12 @@ +// Page table walk callback +val ptw_start_callback = pure {c: "ptw_start_callback"} : (/* vpn */ bits(64), /* access type */ MemoryAccessType(ext_access_type), /* privilege */ Privilege) -> unit +function ptw_start_callback(_) = () + +val ptw_step_callback = pure {c: "ptw_step_callback"} : (/* level */ int, /* pte_addr */ physaddrbits, /* pte */ bits(64)) -> unit +function ptw_step_callback(_) = () + +val ptw_success_callback = pure {c: "ptw_success_callback"} : (/* final_ppn */ bits(64), /* level */ int) -> unit +function ptw_success_callback(_) = () + +val ptw_fail_callback = pure {c: "ptw_fail_callback"} : (/* error_type */ PTW_Error, /* level */ int, /* pte_addr */ physaddrbits) -> unit +function ptw_fail_callback(_) = () diff --git a/model/sys/vmem.sail b/model/sys/vmem.sail index 875ba7ae5..854a11f6b 100644 --- a/model/sys/vmem.sail +++ b/model/sys/vmem.sail @@ -87,6 +87,8 @@ function pt_walk( global, ext_ptw, ) = { + ptw_start_callback(zero_extend(vpn), ac, priv); + // Extract the PPN component for this level; 10 bits on Sv32, otherwise 9. let 'vpn_i_size = if 'v == 32 then 10 else 9; let vpn_i = vpn[(level + 1) * vpn_i_size - 1 .. level * vpn_i_size]; @@ -105,14 +107,21 @@ function pt_walk( // Read this-level PTE from mem (Step 2 of VATP) match read_pte(pte_addr, 2 ^ log_pte_size_bytes) { - Err(_) => Err(PTW_No_Access(), ext_ptw), + Err(_) => { + ptw_fail_callback(PTW_No_Access(), level, bits_of(pte_addr)); + Err(PTW_No_Access(), ext_ptw) + }, Ok(pte) => { + ptw_step_callback(level, bits_of(pte_addr), zero_extend(pte)); + let pte_flags = Mk_PTE_Flags(pte[7 .. 0]); let pte_ext = ext_bits_of_PTE(pte); - if pte_is_invalid(pte_flags, pte_ext) then + if pte_is_invalid(pte_flags, pte_ext) then { // Step 3 of VATP. + ptw_fail_callback(PTW_Invalid_PTE(), level, bits_of(pte_addr)); Err(PTW_Invalid_PTE(), ext_ptw) + } else { // Step 4 of VATP. let ppn = PPN_of_PTE(pte); // 22 or 44. @@ -122,9 +131,11 @@ function pt_walk( if level > 0 then // follow the pointer to walk next level (i.e., go to Step 2) pt_walk(sv_width, vpn, ac, priv, mxr, do_sum, ppn, level - 1, global, ext_ptw) - else - // level 0 PTE, but contains a pointer instead of a leaf - Err(PTW_Invalid_PTE(), ext_ptw) + else { + // level 0 PTE, but contains a pointer instead of a leaf + ptw_fail_callback(PTW_Invalid_PTE(), level, bits_of(pte_addr)); + Err(PTW_Invalid_PTE(), ext_ptw) + } } else { // Leaf PTE (Step 5 of VATP). let ppn_size_bits = if 'v == 32 then 10 else 9; @@ -132,12 +143,17 @@ function pt_walk( // Check for misaligned superpage. let low_bits = ppn_size_bits * level; if ppn[low_bits - 1 .. 0] != zeros() - then return Err(PTW_Misaligned(), ext_ptw); + then { + ptw_fail_callback(PTW_Misaligned(), level, bits_of(pte_addr)); + return Err(PTW_Misaligned(), ext_ptw); + }; }; // Steps 6, 7 (TODO: shadow stack protection), 8 of VATP. match check_PTE_permission(ac, priv, mxr, do_sum, pte_flags, pte_ext, ext_ptw) { - PTE_Check_Failure(ext_ptw, pte_failure) => - Err(ext_get_ptw_error(pte_failure), ext_ptw), + PTE_Check_Failure(ext_ptw, pte_failure) => { + ptw_fail_callback(ext_get_ptw_error(pte_failure), level, bits_of(pte_addr)); + Err(ext_get_ptw_error(pte_failure), ext_ptw) + }, PTE_Check_Success(ext_ptw) => { let ppn = if level > 0 then { // Compose final PA in superpage: @@ -147,6 +163,8 @@ function pt_walk( } else { ppn }; + ptw_success_callback(zero_extend(ppn), level); + Ok(struct {ppn=ppn, pte=pte, pteAddr=pte_addr, level=level, global=global}, ext_ptw) } }