From a54c0d6480185baece88b26a1bc3c3875ce20ddd Mon Sep 17 00:00:00 2001 From: xinyangli Date: Sat, 13 Apr 2024 21:15:56 +0800 Subject: [PATCH 1/5] build: build am for multiple platform in parallel --- abstract-machine/.gitignore | 2 + abstract-machine/CMakeLists.txt | 82 ++++++++++-------- abstract-machine/CMakePresets.json | 6 +- abstract-machine/CMakeUserPresets.json | 15 ++++ abstract-machine/am/CMakeLists.txt | 32 +++++-- abstract-machine/am/src/CMakeLists.txt | 54 +----------- abstract-machine/am/src/native/CMakeLists.txt | 5 +- abstract-machine/am/src/riscv/CMakeLists.txt | 12 +++ .../am/src/riscv/nemu/CMakeLists.txt | 38 +++++--- .../am/src/riscv/npc/CMakeLists.txt | 39 +++++++++ .../am/src/riscv/npc/libgcc/CMakeLists.txt | 14 +++ abstract-machine/am/src/riscv/npc/trm.c | 9 +- abstract-machine/cmake/am-config.cmake.in | 1 + abstract-machine/default.nix | 11 +-- abstract-machine/klib/CMakeLists.txt | 11 --- abstract-machine/klib/src/CMakeLists.txt | 3 +- abstract-machine/klib/tests/CMakeLists.txt | 9 +- abstract-machine/out/install/lib/libklib.a | Bin 87486 -> 0 bytes flake.nix | 9 +- 19 files changed, 206 insertions(+), 146 deletions(-) create mode 100644 abstract-machine/CMakeUserPresets.json create mode 100644 abstract-machine/am/src/riscv/CMakeLists.txt create mode 100644 abstract-machine/am/src/riscv/npc/CMakeLists.txt create mode 100644 abstract-machine/am/src/riscv/npc/libgcc/CMakeLists.txt delete mode 100644 abstract-machine/out/install/lib/libklib.a diff --git a/abstract-machine/.gitignore b/abstract-machine/.gitignore index bcba0ab..0990b71 100644 --- a/abstract-machine/.gitignore +++ b/abstract-machine/.gitignore @@ -2,5 +2,7 @@ **/build/ **/.envrc **/.cache +out/ .vscode compile_commands.json +cmakeUserh diff --git a/abstract-machine/CMakeLists.txt b/abstract-machine/CMakeLists.txt index 508bc68..6eb89d7 100644 --- a/abstract-machine/CMakeLists.txt +++ b/abstract-machine/CMakeLists.txt @@ -8,6 +8,7 @@ set(CMAKE_CXX_STANDARD 11) include(CMakeDependentOption) include(CMakePackageConfigHelpers) # Used to find libcheck include(CTest) +include(GNUInstallDirs) # -- General options set(ISA CACHE STRING "Target ISA") @@ -17,12 +18,15 @@ string(TOUPPER ${ISA} ISA_UPPER) cmake_dependent_option( __PLATFORM_NEMU__ "Run on NEMU" ON "ISA MATCHES \"(riscv | x86)\"" OFF) +cmake_dependent_option( + __PLATFORM_NPC__ "Run on NPC" + ON "ISA MATCHES riscv" OFF) cmake_dependent_option( __PLATFORM_NATIVE__ "Run on native" ON "ISA MATCHES native" OFF) # -- Set PLATFORM according to options -set(MATCH_PLATFORM_PATTERN "^__PLATFORM_([A-Z]*)__") +set(MATCH_PLATFORM_PATTERN "^__PLATFORM_([A-Z]*)__$") get_cmake_property(CACHE_VARS CACHE_VARIABLES) message(STATUS "ISA: ${ISA}") @@ -32,16 +36,27 @@ foreach(VAR IN LISTS CACHE_VARS) get_property(VAR_VALUE CACHE ${VAR} PROPERTY VALUE) set(PLATFORM_UPPER ${CMAKE_MATCH_1}) string(TOLOWER ${PLATFORM_UPPER} PLATFORM) + list(APPEND PLATFORMS ${PLATFORM}) message(STATUS "Variable: ${VAR}=${VAR_VALUE}, Platform: ${PLATFORM}") endif() endforeach() -if(${PLATFORM} MATCHES "native") -set(ARCH "native") -else() -set(ARCH ${ISA}-${PLATFORM}) +if((NOT PLATFORM) AND (NOT ISA MATCHES native)) + message(FATAL_ERROR "Platform not given!") endif() -string(TOUPPER ${ARCH} ARCH_UPPER) + +set(SUPPORTED_ARCH "riscv-nemu" "riscv-npc" "native") +foreach(PLATFORM IN LISTS PLATFORMS) + if(${ISA} MATCHES "native") + set(ARCH "native") + else() + set(ARCH ${ISA}-${PLATFORM}) + endif() + + if(NOT ARCH IN_LIST SUPPORTED_ARCH) + message(FATAL_ERROR "Given ISA-PLATFORM (${ISA}-${PLATFORM}) does not match one of the following: ${SUPPORTED_ARCH}") + endif() +endforeach() # -- Target specific options cmake_dependent_option( @@ -49,39 +64,34 @@ cmake_dependent_option( ON "NOT __ISA_NATIVE__" OFF) # -- Add compile definitions based on options -add_compile_definitions( - $ - __ISA_${ISA_UPPER}__ - __PLATFORM_${PLATFORM_UPPER}__ -) - -add_compile_definitions( - $<$:__NATIVE_USE_KLIB__> -) - -# -- Required compiler flags -add_compile_options( - # -Werror - -Wno-main - -fno-asynchronous-unwind-tables - -fno-builtin - -fno-stack-protector - -U_FORTIFY_SOURCE - $<$:-fno-exceptions> - $<$:-ffreestanding> - $<$:-fno-rtti>) - -add_link_options( - -znoexecstack -) - -# -- Include linker script here. Use this linker script at link time if INCLUDE_LINKER_SCRIPT is set to true -set(LINKER_SCRIPT linker.ld) list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") -add_compile_options(-march=rv32if -mabi=ilp32) -add_link_options(-march=rv32if -mabi=ilp32) +# NOTE: klib and am include header files in each other, +# so we need to create interface libraries for correct dependency +add_library(am_interface INTERFACE) +target_include_directories(am_interface INTERFACE + $ + $ + $) +target_compile_definitions(am_interface INTERFACE ARCH_H=) + +add_library(klib_interface INTERFACE) +target_include_directories(klib_interface + INTERFACE + $ + $) + +install(TARGETS am_interface klib_interface + EXPORT interfaceTargets + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/abstract-machine) + +install(EXPORT interfaceTargets + FILE interfaceTargets.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake) add_subdirectory(klib) add_subdirectory(am) + +# -- Test depends on klib and am should be added last. +add_subdirectory(klib/tests) diff --git a/abstract-machine/CMakePresets.json b/abstract-machine/CMakePresets.json index d14c0b6..73dab56 100644 --- a/abstract-machine/CMakePresets.json +++ b/abstract-machine/CMakePresets.json @@ -14,14 +14,14 @@ } }, { - "name": "riscv-nemu", - "displayName": "Riscv32 NEMU", + "name": "riscv", + "displayName": "RV32 all platform", "generator": "Unix Makefiles", "binaryDir": "${sourceDir}/out/build/${presetName}", - "installDir": "/home/xin/repo/ysyx-workbench/abstract-machine/out/install", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", "ISA": "riscv", + "__PLATFORM_NPC__": true, "__PLATFORM_NEMU__": true } } diff --git a/abstract-machine/CMakeUserPresets.json b/abstract-machine/CMakeUserPresets.json new file mode 100644 index 0000000..3f222ce --- /dev/null +++ b/abstract-machine/CMakeUserPresets.json @@ -0,0 +1,15 @@ +{ + "version": 6, + "configurePresets": [ + { + "name": "Native install", + "inherits": "native", + "installDir": "/home/xin/repo/ysyx-workbench/abstract-machine/out/install" + }, + { + "name": "RISCV install", + "inherits": "riscv", + "installDir": "/home/xin/repo/ysyx-workbench/abstract-machine/out/install" + } + ] +} \ No newline at end of file diff --git a/abstract-machine/am/CMakeLists.txt b/abstract-machine/am/CMakeLists.txt index b0462e4..25895b7 100644 --- a/abstract-machine/am/CMakeLists.txt +++ b/abstract-machine/am/CMakeLists.txt @@ -1,10 +1,26 @@ -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) - -add_library(am_interface INTERFACE) -target_include_directories(am_interface INTERFACE - $ - $) - add_subdirectory(src) -install(DIRECTORY include/ DESTINATION include/abstract-machine) +install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/abstract-machine) + +foreach(PLATFORM IN LISTS PLATFORMS) + if(ISA MATCHES "native") + set(ARCH "native") + else() + set(ARCH ${ISA}-${PLATFORM}) + endif() + install(TARGETS am-${ARCH} + EXPORT amTargets-${ARCH} + LIBRARY DESTINATION lib) + + install(EXPORT amTargets-${ARCH} + FILE amTargets.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/am-${ARCH}) + + configure_package_config_file( + ${CMAKE_SOURCE_DIR}/cmake/am-config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/am-${ARCH}-config.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/am-${ARCH}) + + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/am-${ARCH}-config.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/am-${ARCH}) +endforeach() diff --git a/abstract-machine/am/src/CMakeLists.txt b/abstract-machine/am/src/CMakeLists.txt index 533dd3b..4988276 100644 --- a/abstract-machine/am/src/CMakeLists.txt +++ b/abstract-machine/am/src/CMakeLists.txt @@ -1,53 +1 @@ -if(ISA MATCHES "native") -set(SOURCEDIR "./${PLATFORM}") -else() -set(SOURCEDIR "./${ISA}/${PLATFORM}") -endif() - -add_subdirectory(${SOURCEDIR}) - -target_include_directories(am-${ARCH} - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} - PUBLIC - $ - $) -target_link_libraries(am-${ARCH} - PUBLIC klib_interface - INTERFACE m) - -# TODO: Check -target_link_options(am-${ARCH} INTERFACE - $ - $) - -# Interface compile flags -target_link_options(am-${ARCH} INTERFACE - -znoexecstack) - -target_compile_options(am-${ARCH} INTERFACE - -fno-asynchronous-unwind-tables - -fno-builtin - -fno-stack-protector - -U_FORTIFY_SOURCE - $<$:-fno-exceptions> - $<$:-ffreestanding> - $<$:-fno-rtti>) - -install(TARGETS am-${ARCH} klib_interface am_interface - EXPORT amTargets - LIBRARY DESTINATION lib) - -install(EXPORT amTargets - FILE amTargets.cmake - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/am-${ARCH}) - -configure_package_config_file(${CMAKE_SOURCE_DIR}/cmake/am-config.cmake.in - ${CMAKE_CURRENT_BINARY_DIR}/am-${ARCH}-config.cmake - INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/am-${ARCH}) - -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/am-${ARCH}-config.cmake - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/am-${ARCH}) - -# TODO: check -install(FILES ${CMAKE_SOURCE_DIR}/scripts/${LINKER_SCRIPT} - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/am-${ARCH}) +add_subdirectory(${ISA}) diff --git a/abstract-machine/am/src/native/CMakeLists.txt b/abstract-machine/am/src/native/CMakeLists.txt index e3c9303..706aa80 100644 --- a/abstract-machine/am/src/native/CMakeLists.txt +++ b/abstract-machine/am/src/native/CMakeLists.txt @@ -1,6 +1,3 @@ -include(CheckPIESupported) -check_pie_supported() - set(SOURCES trap.S cte.c @@ -23,4 +20,4 @@ set_target_properties(am-native PROPERTIES INTERFACE_POSITION_INDEPENDENT_CODE TRUE) find_package(SDL2 REQUIRED) -target_link_libraries(am-${ARCH} PUBLIC SDL2::SDL2) +target_link_libraries(am-native PUBLIC SDL2::SDL2 PRIVATE klib_interface am_interface) diff --git a/abstract-machine/am/src/riscv/CMakeLists.txt b/abstract-machine/am/src/riscv/CMakeLists.txt new file mode 100644 index 0000000..ced867e --- /dev/null +++ b/abstract-machine/am/src/riscv/CMakeLists.txt @@ -0,0 +1,12 @@ +foreach(PLATFORM IN LISTS PLATFORMS) + string(TOUPPER ${ARCH} ARCH_UPPER) + set(AM_COMMON_COMPILE_DEF + # -- Arch related + $ + __ISA_${ISA_UPPER}__ + __PLATFORM_${PLATFORM_UPPER}__ + + $<$:__NATIVE_USE_KLIB__> + ) + add_subdirectory(${PLATFORM}) +endforeach() diff --git a/abstract-machine/am/src/riscv/nemu/CMakeLists.txt b/abstract-machine/am/src/riscv/nemu/CMakeLists.txt index a6992db..79db40f 100644 --- a/abstract-machine/am/src/riscv/nemu/CMakeLists.txt +++ b/abstract-machine/am/src/riscv/nemu/CMakeLists.txt @@ -1,7 +1,7 @@ include(nemu-settings) include(riscv-settings) -add_library(am-${ISA}-nemu +add_library(am-riscv-nemu cte.c start.S trap.S @@ -9,26 +9,44 @@ add_library(am-${ISA}-nemu ${NEMU_SOURCES} ) -target_compile_options(am-${ISA}-nemu PRIVATE +target_compile_options(am-riscv-nemu PRIVATE ${NEMU_COMPILE_OPTIONS} ${RISCV_COMPILE_OPTIONS}) -target_link_options(am-${ISA}-nemu PRIVATE + +target_link_options(am-riscv-nemu PRIVATE ${NEMU_LINK_OPITIONS} ${RISCV_LINK_OPTIONS}) -target_include_directories(am-${ISA}-nemu PRIVATE + +target_include_directories(am-riscv-nemu PRIVATE ${NEMU_INCLUDE_DIRECTORIES}) -target_link_options(am-${ISA}-nemu INTERFACE + +target_link_options(am-riscv-nemu INTERFACE LINKER:--defsym=_pmem_start=0x80000000 LINKER:--defsym=_entry_offset=0x0 LINKER:--gc-sections LINKER:-e _start -nostartfiles) -target_compile_definitions(am-${ISA}-nemu PUBLIC - ARCH_H="arch/riscv.h") -target_compile_definitions(am-${ISA}-nemu PRIVATE - ISA_H="riscv/riscv.h") +target_link_options(am-riscv-nemu INTERFACE + $ + $) -set_target_properties(am-${ISA}-nemu PROPERTIES +target_include_directories(am-riscv-nemu + PUBLIC + $ + $) + +target_link_libraries(am-riscv-nemu + PUBLIC am_interface klib_interface + INTERFACE m) + +target_compile_definitions(am-riscv-nemu PRIVATE + ISA_H=) + +set_target_properties(am-riscv-nemu PROPERTIES POSITION_INDEPENDENT_CODE OFF INTERFACE_POSITION_INDEPENDENT_CODE OFF) + +install(FILES ${CMAKE_SOURCE_DIR}/scripts/linker.ld + DESTINATION ${CMAKE_INSTALL_DATADIR}) + diff --git a/abstract-machine/am/src/riscv/npc/CMakeLists.txt b/abstract-machine/am/src/riscv/npc/CMakeLists.txt new file mode 100644 index 0000000..0859c11 --- /dev/null +++ b/abstract-machine/am/src/riscv/npc/CMakeLists.txt @@ -0,0 +1,39 @@ +include(riscv-settings) + +add_subdirectory(libgcc) + +add_library(am-riscv-npc + cte.c + input.c + ioe.c + mpe.c + start.S + timer.c + trap.S + trm.c + vme.c +) + +target_link_libraries(am-riscv-npc PRIVATE npcgcc PUBLIC am_interface klib_interface) + +target_link_options(am-riscv-npc INTERFACE + $ + $) + +target_link_options(am-riscv-npc INTERFACE + LINKER:--defsym=_pmem_start=0x80000000 + LINKER:--defsym=_entry_offset=0x0 + LINKER:--gc-sections + LINKER:-e _start + -nostartfiles) + +target_link_options(am-riscv-npc INTERFACE + $ + $) + +target_compile_definitions(am-riscv-npc + PUBLIC ${AM_COMMON_COMPILE_DEF} ARCH_H= +) + +install(FILES ${CMAKE_SOURCE_DIR}/scripts/linker.ld + DESTINATION ${CMAKE_INSTALL_DATADIR}) diff --git a/abstract-machine/am/src/riscv/npc/libgcc/CMakeLists.txt b/abstract-machine/am/src/riscv/npc/libgcc/CMakeLists.txt new file mode 100644 index 0000000..841adb6 --- /dev/null +++ b/abstract-machine/am/src/riscv/npc/libgcc/CMakeLists.txt @@ -0,0 +1,14 @@ +add_library(npcgcc + ashldi3.c + div.S + muldi3.S + multi3.c + unused.c +) + +target_link_libraries(npcgcc PRIVATE klib_interface am_interface) +target_link_options(npcgcc INTERFACE -nolibc -nostdlib) + +install(TARGETS npcgcc + EXPORT amTargets-riscv-npc + LIBRARY DESTINATION lib) diff --git a/abstract-machine/am/src/riscv/npc/trm.c b/abstract-machine/am/src/riscv/npc/trm.c index 0efe6e7..a5f4fac 100644 --- a/abstract-machine/am/src/riscv/npc/trm.c +++ b/abstract-machine/am/src/riscv/npc/trm.c @@ -6,7 +6,7 @@ int main(const char *args); extern char _pmem_start; #define PMEM_SIZE (128 * 1024 * 1024) -#define PMEM_END ((uintptr_t)&_pmem_start + PMEM_SIZE) +#define PMEM_END ((uintptr_t)&_pmem_start + PMEM_SIZE) Area heap = RANGE(&_heap_start, PMEM_END); #ifndef MAINARGS @@ -14,11 +14,12 @@ Area heap = RANGE(&_heap_start, PMEM_END); #endif static const char mainargs[] = MAINARGS; -void putch(char ch) { -} +void putch(char ch) {} void halt(int code) { - while (1); + asm volatile("mv a0, %0; ebreak" : : "r"(code)); + while (1) + ; } void _trm_init() { diff --git a/abstract-machine/cmake/am-config.cmake.in b/abstract-machine/cmake/am-config.cmake.in index f2fbb32..0445b66 100644 --- a/abstract-machine/cmake/am-config.cmake.in +++ b/abstract-machine/cmake/am-config.cmake.in @@ -6,4 +6,5 @@ find_dependency(SDL2 REQUIRED) endif() # Include the targets file +include("${CMAKE_CURRENT_LIST_DIR}/../interfaceTargets.cmake") include("${CMAKE_CURRENT_LIST_DIR}/amTargets.cmake") diff --git a/abstract-machine/default.nix b/abstract-machine/default.nix index 1f1f67d..2b614ac 100644 --- a/abstract-machine/default.nix +++ b/abstract-machine/default.nix @@ -1,9 +1,9 @@ -{ stdenv, +{ stdenv, lib, cmake, SDL2, isa ? "native", - platform ? "NEMU" + platform ? [ ] }: stdenv.mkDerivation { pname = "abstract-machine"; @@ -13,8 +13,7 @@ stdenv.mkDerivation { cmakeFlags = [ (lib.cmakeFeature "ISA" isa) - (lib.cmakeBool "__PLATFORM_${lib.strings.toUpper platform}__" true) - ]; + ] ++ map (p: (lib.cmakeBool "__PLATFORM_${lib.strings.toUpper p}__" true)) platform; nativeBuildInputs = [ cmake @@ -22,5 +21,7 @@ stdenv.mkDerivation { buildInputs = [ - ] ++ (if platform=="native" then [ SDL2 ] else [ ]); + ] ++ (if isa=="native" then [ SDL2 ] else [ ]); + + doCheck = true; } diff --git a/abstract-machine/klib/CMakeLists.txt b/abstract-machine/klib/CMakeLists.txt index 2cf4a78..febd4f0 100644 --- a/abstract-machine/klib/CMakeLists.txt +++ b/abstract-machine/klib/CMakeLists.txt @@ -1,12 +1 @@ -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) - -add_library(klib_interface INTERFACE) -target_include_directories(klib_interface - INTERFACE - $ - $) - add_subdirectory(src) -# add_subdirectory(tests) - -install(DIRECTORY include/ DESTINATION include/abstract-machine) diff --git a/abstract-machine/klib/src/CMakeLists.txt b/abstract-machine/klib/src/CMakeLists.txt index bf7e136..b3a59fd 100644 --- a/abstract-machine/klib/src/CMakeLists.txt +++ b/abstract-machine/klib/src/CMakeLists.txt @@ -13,8 +13,7 @@ set(SOURCES ) add_library(klib ${SOURCES}) -target_include_directories(klib PUBLIC $) -target_compile_definitions(klib PUBLIC $) +target_link_libraries(klib PRIVATE am_interface klib_interface) install(TARGETS klib EXPORT klibTargets diff --git a/abstract-machine/klib/tests/CMakeLists.txt b/abstract-machine/klib/tests/CMakeLists.txt index f72c555..6cb0e40 100644 --- a/abstract-machine/klib/tests/CMakeLists.txt +++ b/abstract-machine/klib/tests/CMakeLists.txt @@ -4,14 +4,11 @@ set(TEST_SOURCES ) foreach(TEST IN LISTS TEST_SOURCES) - add_executable(${TEST} ${TEST}.c) - target_link_libraries(${TEST} am-${ARCH} klib m) - target_include_directories(${TEST} - PRIVATE $ - PRIVATE $ - ) # TODO: Run tests in other configurations if(__PLATFORM_NATIVE__) + add_executable(${TEST} ${TEST}.c) + target_link_libraries(${TEST} PRIVATE am_interface klib_interface klib m) + target_link_libraries(${TEST} PRIVATE am-native) add_test(NAME ${TEST} COMMAND ${TEST}) endif() endforeach() diff --git a/abstract-machine/out/install/lib/libklib.a b/abstract-machine/out/install/lib/libklib.a deleted file mode 100644 index 5023a30456119001268273724f42950c73659d18..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 87486 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 Lv`GTe22Te7Y4qh2 diff --git a/flake.nix b/flake.nix index c824872..58652b3 100644 --- a/flake.nix +++ b/flake.nix @@ -42,6 +42,8 @@ src = ./.; hooks = { trim-trailing-whitespace.enable = true; + end-of-file-fixer.enable = true; + cmake-format.enable = true; clang-format = { enable = true; types_or = pkgs.lib.mkForce [ "c" "c++" ]; @@ -52,7 +54,8 @@ packages.nemu = pkgs.callPackage ./nemu { am-kernels = self.packages.${system}.am-kernels; }; packages.nemu-lib = pkgs.callPackage ./nemu { am-kernels = self.packages.${system}.am-kernels; defconfig = "riscv32-lib_defconfig"; }; - packages.abstract-machine = crossPkgs.callPackage ./abstract-machine { isa = "riscv"; platform = "nemu"; }; + packages.abstract-machine = crossPkgs.callPackage ./abstract-machine { isa = "riscv"; platform = [ "nemu" "npc" ]; }; + packages.abstract-machine-native = pkgs.callPackage ./abstract-machine { isa = "native"; }; packages.am-kernels = crossPkgs.stdenv.mkDerivation rec { pname = "am-kernels-cmake"; @@ -67,7 +70,6 @@ cmakeFlags = [ (pkgs.lib.cmakeFeature "ISA" "riscv") (pkgs.lib.cmakeFeature "PLATFORM" "nemu") - (pkgs.lib.cmakeFeature "CMAKE_INSTALL_DATADIR" "share") ]; buildInputs = [ @@ -87,13 +89,12 @@ IMAGES_PATH = "${self.packages.${system}.am-kernels}/share/binary"; }; - devShells.npc = with pkgs; mkShell { + devShells.npc = with pkgs; mkShell.override { stdenv = ccacheStdenv; } { inherit (self.checks.${system}.pre-commit-check) shellHook; CHISEL_FIRTOOL_PATH = "${nixpkgs-circt162.legacyPackages.${system}.circt}/bin"; packages = [ clang-tools cmake - ninja coursier espresso bloop From 7f0d1ba75ad71794a27f4a57541fdc7cb5b41c4e Mon Sep 17 00:00:00 2001 From: xinyangli Date: Thu, 25 Apr 2024 16:54:27 +0800 Subject: [PATCH 2/5] feat(nemu): gdb remote debug --- nemu/Kconfig | 4 +- nemu/default.nix | 2 + nemu/include/cpu/cpu.h | 10 + nemu/include/cpu/decode.h | 120 +++++--- nemu/include/isa.h | 4 + nemu/scripts/build.mk | 2 +- nemu/src/cpu/cpu-exec.c | 106 +++++--- nemu/src/engine/interpreter/init.c | 8 +- nemu/src/filelist.mk | 4 +- nemu/src/isa/riscv32/include/isa-def.h | 1 + nemu/src/isa/riscv32/inst.c | 256 +++++++++++++----- nemu/src/isa/riscv32/reg.c | 46 +++- nemu/src/memory/paddr.c | 45 +-- nemu/src/monitor/filelist.mk | 3 + nemu/src/monitor/gdbstub.cc | 130 +++++++++ nemu/src/monitor/monitor.c | 96 ++++--- nemu/src/monitor/sdb/addrexp.l | 24 -- nemu/src/monitor/sdb/addrexp.y | 60 ---- nemu/src/monitor/sdb/filelist.mk | 2 - nemu/src/monitor/sdb/sdb.c | 361 ------------------------- nemu/src/monitor/sdb/sdb.h | 25 -- nemu/src/monitor/sdb/watchpoint.c | 150 ---------- 22 files changed, 613 insertions(+), 846 deletions(-) create mode 100644 nemu/src/monitor/filelist.mk create mode 100644 nemu/src/monitor/gdbstub.cc delete mode 100644 nemu/src/monitor/sdb/addrexp.l delete mode 100644 nemu/src/monitor/sdb/addrexp.y delete mode 100644 nemu/src/monitor/sdb/filelist.mk delete mode 100644 nemu/src/monitor/sdb/sdb.c delete mode 100644 nemu/src/monitor/sdb/sdb.h delete mode 100644 nemu/src/monitor/sdb/watchpoint.c diff --git a/nemu/Kconfig b/nemu/Kconfig index 5816143..8c91522 100644 --- a/nemu/Kconfig +++ b/nemu/Kconfig @@ -180,7 +180,7 @@ config ITRACE_BUFFER default 10 config MTRACE - depends on TRACE + depends on TRACE && LOG_TRACE bool "Enable memory tracing" default n @@ -198,7 +198,7 @@ config MTRACE_RANGE_MAX default 10 config FTRACE - depends on TRACE && TARGET_NATIVE_ELF && ENGINE_INTERPRETER + depends on TRACE && TARGET_NATIVE_ELF && ENGINE_INTERPRETER && LOG_TRACE bool "Enable function tracing" default n diff --git a/nemu/default.nix b/nemu/default.nix index 052b4a7..e6ffb2b 100644 --- a/nemu/default.nix +++ b/nemu/default.nix @@ -3,6 +3,7 @@ stdenv, am-kernels, dtc, + mini-gdbstub, defconfig ? "alldefconfig", }: @@ -23,6 +24,7 @@ stdenv.mkDerivation rec { buildInputs = with pkgs; [ readline libllvm + mini-gdbstub ]; checkInputs = [ diff --git a/nemu/include/cpu/cpu.h b/nemu/include/cpu/cpu.h index 2be0d77..c9bb700 100644 --- a/nemu/include/cpu/cpu.h +++ b/nemu/include/cpu/cpu.h @@ -17,8 +17,18 @@ #define __CPU_CPU_H__ #include +#include + +#include +#include + +typedef struct { + size_t addr; + bp_type_t type; +} breakpoint_t; void cpu_exec(uint64_t n); +breakpoint_t *cpu_exec_with_bp(uint64_t n, breakpoint_t *bp, size_t len); void set_nemu_state(int state, vaddr_t pc, int halt_ret); void invalid_inst(vaddr_t thispc); diff --git a/nemu/include/cpu/decode.h b/nemu/include/cpu/decode.h index a17c888..81946bf 100644 --- a/nemu/include/cpu/decode.h +++ b/nemu/include/cpu/decode.h @@ -16,39 +16,62 @@ #ifndef __CPU_DECODE_H__ #define __CPU_DECODE_H__ +#include "types.h" #include +typedef enum { INST_NORMAL, INST_MEM_WRITE, INST_MEM_READ } inst_type_t; + +typedef union { + paddr_t rmem; + paddr_t wmem; +} inst_type_op; + typedef struct Decode { vaddr_t pc; vaddr_t snpc; // static next pc vaddr_t dnpc; // dynamic next pc + inst_type_t inst_type; + inst_type_op inst_value; ISADecodeInfo isa; } Decode; // --- pattern matching mechanism --- -__attribute__((always_inline)) -static inline void pattern_decode(const char *str, int len, - uint64_t *key, uint64_t *mask, uint64_t *shift) { +__attribute__((always_inline)) static inline void +pattern_decode(const char *str, int len, uint64_t *key, uint64_t *mask, + uint64_t *shift) { uint64_t __key = 0, __mask = 0, __shift = 0; -#define macro(i) \ - if ((i) >= len) goto finish; \ - else { \ - char c = str[i]; \ - if (c != ' ') { \ - Assert(c == '0' || c == '1' || c == '?', \ - "invalid character '%c' in pattern string", c); \ - __key = (__key << 1) | (c == '1' ? 1 : 0); \ - __mask = (__mask << 1) | (c == '?' ? 0 : 1); \ - __shift = (c == '?' ? __shift + 1 : 0); \ - } \ +#define macro(i) \ + if ((i) >= len) \ + goto finish; \ + else { \ + char c = str[i]; \ + if (c != ' ') { \ + Assert(c == '0' || c == '1' || c == '?', \ + "invalid character '%c' in pattern string", c); \ + __key = (__key << 1) | (c == '1' ? 1 : 0); \ + __mask = (__mask << 1) | (c == '?' ? 0 : 1); \ + __shift = (c == '?' ? __shift + 1 : 0); \ + } \ } -#define macro2(i) macro(i); macro((i) + 1) -#define macro4(i) macro2(i); macro2((i) + 2) -#define macro8(i) macro4(i); macro4((i) + 4) -#define macro16(i) macro8(i); macro8((i) + 8) -#define macro32(i) macro16(i); macro16((i) + 16) -#define macro64(i) macro32(i); macro32((i) + 32) +#define macro2(i) \ + macro(i); \ + macro((i) + 1) +#define macro4(i) \ + macro2(i); \ + macro2((i) + 2) +#define macro8(i) \ + macro4(i); \ + macro4((i) + 4) +#define macro16(i) \ + macro8(i); \ + macro8((i) + 8) +#define macro32(i) \ + macro16(i); \ + macro16((i) + 16) +#define macro64(i) \ + macro32(i); \ + macro32((i) + 32) macro64(0); panic("pattern too long"); #undef macro @@ -58,21 +81,24 @@ finish: *shift = __shift; } -__attribute__((always_inline)) -static inline void pattern_decode_hex(const char *str, int len, - uint64_t *key, uint64_t *mask, uint64_t *shift) { +__attribute__((always_inline)) static inline void +pattern_decode_hex(const char *str, int len, uint64_t *key, uint64_t *mask, + uint64_t *shift) { uint64_t __key = 0, __mask = 0, __shift = 0; -#define macro(i) \ - if ((i) >= len) goto finish; \ - else { \ - char c = str[i]; \ - if (c != ' ') { \ - Assert((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || c == '?', \ - "invalid character '%c' in pattern string", c); \ - __key = (__key << 4) | (c == '?' ? 0 : (c >= '0' && c <= '9') ? c - '0' : c - 'a' + 10); \ - __mask = (__mask << 4) | (c == '?' ? 0 : 0xf); \ - __shift = (c == '?' ? __shift + 4 : 0); \ - } \ +#define macro(i) \ + if ((i) >= len) \ + goto finish; \ + else { \ + char c = str[i]; \ + if (c != ' ') { \ + Assert((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || c == '?', \ + "invalid character '%c' in pattern string", c); \ + __key = (__key << 4) | (c == '?' ? 0 \ + : (c >= '0' && c <= '9') ? c - '0' \ + : c - 'a' + 10); \ + __mask = (__mask << 4) | (c == '?' ? 0 : 0xf); \ + __shift = (c == '?' ? __shift + 4 : 0); \ + } \ } macro16(0); @@ -84,18 +110,22 @@ finish: *shift = __shift; } - // --- pattern matching wrappers for decode --- -#define INSTPAT(pattern, ...) do { \ - uint64_t key, mask, shift; \ - pattern_decode(pattern, STRLEN(pattern), &key, &mask, &shift); \ - if ((((uint64_t)INSTPAT_INST(s) >> shift) & mask) == key) { \ - INSTPAT_MATCH(s, ##__VA_ARGS__); \ - goto *(__instpat_end); \ - } \ -} while (0) +#define INSTPAT(pattern, ...) \ + do { \ + uint64_t key, mask, shift; \ + pattern_decode(pattern, STRLEN(pattern), &key, &mask, &shift); \ + if ((((uint64_t)INSTPAT_INST(s) >> shift) & mask) == key) { \ + INSTPAT_MATCH(s, ##__VA_ARGS__); \ + goto *(__instpat_end); \ + } \ + } while (0) -#define INSTPAT_START(name) { const void ** __instpat_end = &&concat(__instpat_end_, name); -#define INSTPAT_END(name) concat(__instpat_end_, name): ; } +#define INSTPAT_START(name) \ + { \ + const void **__instpat_end = &&concat(__instpat_end_, name); +#define INSTPAT_END(name) \ + concat(__instpat_end_, name) :; \ + } #endif diff --git a/nemu/include/isa.h b/nemu/include/isa.h index 5040349..e860538 100644 --- a/nemu/include/isa.h +++ b/nemu/include/isa.h @@ -17,6 +17,7 @@ #define __ISA_H__ // Located at src/isa/$(GUEST_ISA)/include/isa-def.h +#include #include // The macro `__GUEST_ISA__` is defined in $(CFLAGS). @@ -30,8 +31,11 @@ void init_isa(); // reg extern CPU_state cpu; +extern arch_info_t isa_arch_info; void isa_reg_display(); word_t isa_reg_str2val(const char *name, bool *success); +int isa_read_reg(void *args, int regno, size_t *reg_value); +int isa_write_reg(void *args, int regno, size_t data); // exec struct Decode; diff --git a/nemu/scripts/build.mk b/nemu/scripts/build.mk index a3b71d2..f3bfe15 100644 --- a/nemu/scripts/build.mk +++ b/nemu/scripts/build.mk @@ -22,7 +22,7 @@ CXX := g++ endif LD := $(CXX) INCLUDES = $(addprefix -I, $(INC_PATH)) -CFLAGS := -O2 -MMD -Wall -Werror $(INCLUDES) $(CFLAGS) +CFLAGS := -O2 -MMD -Wall $(INCLUDES) $(CFLAGS) LDFLAGS := -O2 $(LDFLAGS) OBJS = $(SRCS:%.c=$(OBJ_DIR)/%.o) $(CXXSRC:%.cc=$(OBJ_DIR)/%.o) diff --git a/nemu/src/cpu/cpu-exec.c b/nemu/src/cpu/cpu-exec.c index 097c02f..f4be925 100644 --- a/nemu/src/cpu/cpu-exec.c +++ b/nemu/src/cpu/cpu-exec.c @@ -13,11 +13,12 @@ * See the Mulan PSL v2 for more details. ***************************************************************************************/ -#include +#include "gdbstub.h" #include #include #include #include +#include /* The assembly code of instructions executed is only output to the screen * when the number of instructions executed is less than this value. @@ -34,13 +35,16 @@ IFDEF(CONFIG_ITRACE, extern char logbuf[CONFIG_ITRACE_BUFFER][128]); IFDEF(CONFIG_ITRACE, extern int logbuf_rear); void device_update(); -bool wp_eval_all(); static void trace_and_difftest(Decode *_this, vaddr_t dnpc) { #ifdef CONFIG_ITRACE_COND - if (ITRACE_COND) { log_write("%s\n", logbuf[logbuf_rear]); } + if (ITRACE_COND) { + log_write("%s\n", logbuf[logbuf_rear]); + } #endif - if (g_print_step) { IFDEF(CONFIG_ITRACE, puts(logbuf[logbuf_rear])); } + if (g_print_step) { + IFDEF(CONFIG_ITRACE, puts(logbuf[logbuf_rear])); + } IFDEF(CONFIG_DIFFTEST, difftest_step(_this->pc, dnpc)); } @@ -56,12 +60,13 @@ static void exec_once(Decode *s, vaddr_t pc) { int ilen = s->snpc - s->pc; int i; uint8_t *inst = (uint8_t *)&s->isa.inst.val; - for (i = ilen - 1; i >= 0; i --) { + for (i = ilen - 1; i >= 0; i--) { p += snprintf(p, 4, " %02x", inst[i]); } int ilen_max = MUXDEF(CONFIG_ISA_x86, 8, 4); int space_len = ilen_max - ilen; - if (space_len < 0) space_len = 0; + if (space_len < 0) + space_len = 0; space_len = space_len * 3 + 1; memset(p, ' ', space_len); p += space_len; @@ -69,7 +74,8 @@ static void exec_once(Decode *s, vaddr_t pc) { #ifndef CONFIG_ISA_loongarch32r void disassemble(char *str, int size, uint64_t pc, uint8_t *code, int nbyte); disassemble(p, logbuf[logbuf_rear] + sizeof(logbuf[logbuf_rear]) - p, - MUXDEF(CONFIG_ISA_x86, s->snpc, s->pc), (uint8_t *)&s->isa.inst.val, ilen); + MUXDEF(CONFIG_ISA_x86, s->snpc, s->pc), + (uint8_t *)&s->isa.inst.val, ilen); #else p[0] = '\0'; // the upstream llvm does not support loongarch32r #endif @@ -78,15 +84,12 @@ static void exec_once(Decode *s, vaddr_t pc) { static void execute(uint64_t n) { Decode s; - for (;n > 0; n --) { + for (; n > 0; n--) { exec_once(&s, cpu.pc); - g_nr_guest_inst ++; + g_nr_guest_inst++; trace_and_difftest(&s, cpu.pc); - if (wp_eval_all()) { - IFDEF(CONFIG_ITRACE, puts(logbuf[logbuf_rear])); + if (nemu_state.state != NEMU_RUNNING) break; - } - if (nemu_state.state != NEMU_RUNNING) break; IFDEF(CONFIG_DEVICE, device_update()); } } @@ -96,8 +99,12 @@ static void statistic() { #define NUMBERIC_FMT MUXDEF(CONFIG_TARGET_AM, "%", "%'") PRIu64 Log("host time spent = " NUMBERIC_FMT " us", g_timer); Log("total guest instructions = " NUMBERIC_FMT, g_nr_guest_inst); - if (g_timer > 0) Log("simulation frequency = " NUMBERIC_FMT " inst/s", g_nr_guest_inst * 1000000 / g_timer); - else Log("Finish running in less than 1 us and can not calculate the simulation frequency"); + if (g_timer > 0) + Log("simulation frequency = " NUMBERIC_FMT " inst/s", + g_nr_guest_inst * 1000000 / g_timer); + else + Log("Finish running in less than 1 us and can not calculate the simulation " + "frequency"); } void assert_fail_msg() { @@ -109,10 +116,13 @@ void assert_fail_msg() { void cpu_exec(uint64_t n) { g_print_step = (n < MAX_INST_TO_PRINT); switch (nemu_state.state) { - case NEMU_END: case NEMU_ABORT: - printf("Program execution has ended. To restart the program, exit NEMU and run again.\n"); - return; - default: nemu_state.state = NEMU_RUNNING; + case NEMU_END: + case NEMU_ABORT: + printf("Program execution has ended. To restart the program, exit NEMU and " + "run again.\n"); + return; + default: + nemu_state.state = NEMU_RUNNING; } uint64_t timer_start = get_time(); @@ -123,18 +133,52 @@ void cpu_exec(uint64_t n) { g_timer += timer_end - timer_start; switch (nemu_state.state) { - case NEMU_RUNNING: nemu_state.state = NEMU_STOP; break; + case NEMU_RUNNING: + nemu_state.state = NEMU_STOP; + break; - case NEMU_END: case NEMU_ABORT: { - Log("nemu: %s at pc = " FMT_WORD, - (nemu_state.state == NEMU_ABORT ? ANSI_FMT("ABORT", ANSI_FG_RED) : - (nemu_state.halt_ret == 0 ? ANSI_FMT("HIT GOOD TRAP", ANSI_FG_GREEN) : - ANSI_FMT("HIT BAD TRAP", ANSI_FG_RED))), - nemu_state.halt_pc); - if(nemu_state.halt_ret != 0) { - IFDEF(CONFIG_ITRACE, log_itrace_print()); - } - } // fall through - case NEMU_QUIT: statistic(); + case NEMU_END: + case NEMU_ABORT: { + Log("nemu: %s at pc = " FMT_WORD, + (nemu_state.state == NEMU_ABORT + ? ANSI_FMT("ABORT", ANSI_FG_RED) + : (nemu_state.halt_ret == 0 + ? ANSI_FMT("HIT GOOD TRAP", ANSI_FG_GREEN) + : ANSI_FMT("HIT BAD TRAP", ANSI_FG_RED))), + nemu_state.halt_pc); + if (nemu_state.halt_ret != 0) { + IFDEF(CONFIG_ITRACE, log_itrace_print()); + } + } // fall through + case NEMU_QUIT: + statistic(); } } + +breakpoint_t *cpu_exec_with_bp(uint64_t n, breakpoint_t *bp, size_t len) { + static Decode s; + nemu_state.state = NEMU_RUNNING; + do { + exec_once(&s, cpu.pc); + g_nr_guest_inst++; + for (int i = 0; i < len; i++) { + size_t addr = bp[i].addr; + bp_type_t bptype = bp[i].type; + if (bptype == BP_SOFTWARE && cpu.pc == addr) { + return bp + i; + } + bool is_write = (bptype == BP_WRITE) || (bptype == BP_ACCESS); + bool is_read = (bptype == BP_READ) || (bptype == BP_ACCESS); + if (s.inst_type == INST_MEM_READ && s.inst_value.rmem == addr && + is_write) { + return bp + i; + } else if (s.inst_type == INST_MEM_WRITE && s.inst_value.wmem == addr && + is_read) { + return bp + i; + } + } + if (nemu_state.state != NEMU_RUNNING) + return NULL; + } while (--n); + return NULL; +} diff --git a/nemu/src/engine/interpreter/init.c b/nemu/src/engine/interpreter/init.c index a517c3e..fed4923 100644 --- a/nemu/src/engine/interpreter/init.c +++ b/nemu/src/engine/interpreter/init.c @@ -13,7 +13,9 @@ * See the Mulan PSL v2 for more details. ***************************************************************************************/ +#include "debug.h" #include +#include void sdb_mainloop(); @@ -22,6 +24,10 @@ void engine_start() { cpu_exec(-1); #else /* Receive commands from user. */ - sdb_mainloop(); + int nemu_gdbstub_run(); + if (nemu_gdbstub_run()) { + Error("gdbstub exited abnormally"); + exit(1); + } #endif } diff --git a/nemu/src/filelist.mk b/nemu/src/filelist.mk index 03e05df..4ae2376 100644 --- a/nemu/src/filelist.mk +++ b/nemu/src/filelist.mk @@ -14,12 +14,12 @@ #**************************************************************************************/ SRCS-y += src/nemu-main.c -DIRS-y += src/cpu src/monitor src/utils +DIRS-y += src/cpu src/utils DIRS-$(CONFIG_MODE_SYSTEM) += src/memory DIRS-BLACKLIST-$(CONFIG_TARGET_AM) += src/monitor/sdb SHARE = $(if $(CONFIG_TARGET_SHARE),1,0) -LIBS += $(if $(CONFIG_TARGET_NATIVE_ELF),-lreadline -ldl -pie,) +LIBS += $(if $(CONFIG_TARGET_NATIVE_ELF),-lgdbstub -lreadline -ldl -pie,) ifdef mainargs ASFLAGS += -DBIN_PATH=\"$(mainargs)\" diff --git a/nemu/src/isa/riscv32/include/isa-def.h b/nemu/src/isa/riscv32/include/isa-def.h index dd0105c..524cb4e 100644 --- a/nemu/src/isa/riscv32/include/isa-def.h +++ b/nemu/src/isa/riscv32/include/isa-def.h @@ -20,6 +20,7 @@ typedef struct { word_t gpr[MUXDEF(CONFIG_RVE, 16, 32)]; + // word_t csr[MUXDEF(CONFIG_RVE, )] vaddr_t pc; } MUXDEF(CONFIG_RV64, riscv64_CPU_state, riscv32_CPU_state); diff --git a/nemu/src/isa/riscv32/inst.c b/nemu/src/isa/riscv32/inst.c index 1c41c63..dc65f47 100644 --- a/nemu/src/isa/riscv32/inst.c +++ b/nemu/src/isa/riscv32/inst.c @@ -13,45 +13,91 @@ * See the Mulan PSL v2 for more details. ***************************************************************************************/ -#include #include "local-include/reg.h" #include "macro.h" +#include "types.h" +#include #include -#include #include +#include #include #include #define R(i) gpr(i) -#define Mr vaddr_read -#define Mw vaddr_write enum { - TYPE_R, TYPE_I, TYPE_I_SHIFT, TYPE_U, TYPE_S, TYPE_B, TYPE_J, + TYPE_R, + TYPE_I, + TYPE_I_SHIFT, + TYPE_U, + TYPE_S, + TYPE_B, + TYPE_J, TYPE_N, // none }; -#define src1R() do { *src1 = R(rs1); } while (0) -#define src2R() do { *src2 = R(rs2); } while (0) -#define immI() do { *imm = SEXT(BITS(i, 31, 20), 12); } while(0) -#define immU() do { *imm = SEXT(BITS(i, 31, 12), 20) << 12; } while(0) -#define immS() do { *imm = SEXT(BITS(i, 31, 25), 7) << 5 | BITS(i, 11, 7); } while(0) -#define immB() do { *imm = SEXT(BITS(i, 31, 31), 1) << 12 | BITS(i, 30, 25) << 5 | BITS(i, 11, 8) << 1 | BITS(i, 7, 7) << 11; } while(0) -#define immJ() do { *imm = SEXT(BITS(i, 31, 31), 1) << 20 | BITS(i, 30, 21) << 1 | BITS(i, 20, 20) << 11 | BITS(i, 19, 12) << 12; } while(0) +#define src1R() \ + do { \ + *src1 = R(rs1); \ + } while (0) +#define src2R() \ + do { \ + *src2 = R(rs2); \ + } while (0) +#define immI() \ + do { \ + *imm = SEXT(BITS(i, 31, 20), 12); \ + } while (0) +#define immU() \ + do { \ + *imm = SEXT(BITS(i, 31, 12), 20) << 12; \ + } while (0) +#define immS() \ + do { \ + *imm = SEXT(BITS(i, 31, 25), 7) << 5 | BITS(i, 11, 7); \ + } while (0) +#define immB() \ + do { \ + *imm = SEXT(BITS(i, 31, 31), 1) << 12 | BITS(i, 30, 25) << 5 | \ + BITS(i, 11, 8) << 1 | BITS(i, 7, 7) << 11; \ + } while (0) +#define immJ() \ + do { \ + *imm = SEXT(BITS(i, 31, 31), 1) << 20 | BITS(i, 30, 21) << 1 | \ + BITS(i, 20, 20) << 11 | BITS(i, 19, 12) << 12; \ + } while (0) static void decode_operand(Decode *s, int *rd, word_t *src1, word_t *src2, word_t *imm, int type) { uint32_t i = s->isa.inst.val; int rs1 = BITS(i, 19, 15); int rs2 = BITS(i, 24, 20); - *rd = BITS(i, 11, 7); + *rd = BITS(i, 11, 7); switch (type) { - case TYPE_R: src1R(); src2R(); break; - case TYPE_I: src1R(); immI(); break; - case TYPE_U: immU(); break; - case TYPE_J: immJ(); break; - case TYPE_S: src1R(); src2R(); immS(); break; - case TYPE_B: src1R(); src2R(); immB(); break; + case TYPE_R: + src1R(); + src2R(); + break; + case TYPE_I: + src1R(); + immI(); + break; + case TYPE_U: + immU(); + break; + case TYPE_J: + immJ(); + break; + case TYPE_S: + src1R(); + src2R(); + immS(); + break; + case TYPE_B: + src1R(); + src2R(); + immB(); + break; } } @@ -62,11 +108,23 @@ static void do_branch(Decode *s, bool condition, word_t offset) { } } +static inline word_t Mr(Decode *s, vaddr_t addr, int len) { + s->inst_type = INST_MEM_READ; + s->inst_value.rmem = addr & ~0x3; + return vaddr_read(addr, len); +} + +static inline void Mw(Decode *s, vaddr_t addr, int len, word_t data) { + vaddr_write(addr, len, data); + s->inst_type = INST_MEM_WRITE; + s->inst_value.wmem = addr & ~0x3; +} + #ifdef CONFIG_FTRACE static void ftrace_jalr(Decode *s, int rd, vaddr_t dst) { uint32_t i = s->isa.inst.val; int rs1 = BITS(i, 19, 15); - if(rs1 == 1 && rd == 0) { + if (rs1 == 1 && rd == 0) { ftrace_return(s->pc, dst); } else { ftrace_call(s->pc, dst); @@ -80,70 +138,118 @@ static int decode_exec(Decode *s) { s->dnpc = s->snpc; #define INSTPAT_INST(s) ((s)->isa.inst.val) -#define INSTPAT_MATCH(s, name, type, ... /* execute body */ ) { \ - decode_operand(s, &rd, &src1, &src2, &imm, concat(TYPE_, type)); \ - __VA_ARGS__ ; \ -} +#define INSTPAT_MATCH(s, name, type, ... /* execute body */) \ + { \ + decode_operand(s, &rd, &src1, &src2, &imm, concat(TYPE_, type)); \ + __VA_ARGS__; \ + } INSTPAT_START(); - INSTPAT("??????? ????? ????? ??? ????? 01101 11", lui , U, R(rd) = imm); - INSTPAT("??????? ????? ????? ??? ????? 00101 11", auipc , U, R(rd) = s->pc + imm); + INSTPAT("??????? ????? ????? ??? ????? 01101 11", lui, U, R(rd) = imm); + INSTPAT("??????? ????? ????? ??? ????? 00101 11", auipc, U, + R(rd) = s->pc + imm); - INSTPAT("??????? ????? ????? ??? ????? 11011 11", jal , J, do { - s->dnpc = s->pc + imm; R(rd) = s->pc + 4; - IFDEF(CONFIG_FTRACE, ftrace_call(s->pc, s->pc + imm)); } while(0)); - INSTPAT("??????? ????? ????? ??? ????? 11001 11", jalr , I, do { - s->dnpc = src1 + imm; R(rd) = s->pc + 4; - IFDEF(CONFIG_FTRACE, ftrace_jalr(s, rd, src1 + imm)); } while(0)); - INSTPAT("??????? ????? ????? 000 ????? 11000 11", beq , B, do_branch(s, src1 == src2, imm)); - INSTPAT("??????? ????? ????? 001 ????? 11000 11", bne , B, do_branch(s, src1 != src2, imm)); - INSTPAT("??????? ????? ????? 100 ????? 11000 11", blt , B, do_branch(s, (sword_t)src1 < (sword_t)src2, imm)); - INSTPAT("??????? ????? ????? 101 ????? 11000 11", bge , B, do_branch(s, (sword_t)src1 >= (sword_t)src2, imm)); - INSTPAT("??????? ????? ????? 110 ????? 11000 11", bltu , B, do_branch(s, src1 < src2, imm)); - INSTPAT("??????? ????? ????? 111 ????? 11000 11", bgeu , B, do_branch(s, src1 >= src2, imm)); + INSTPAT( + "??????? ????? ????? ??? ????? 11011 11", jal, J, do { + s->dnpc = s->pc + imm; + R(rd) = s->pc + 4; + IFDEF(CONFIG_FTRACE, ftrace_call(s->pc, s->pc + imm)); + } while (0)); + INSTPAT( + "??????? ????? ????? ??? ????? 11001 11", jalr, I, do { + s->dnpc = src1 + imm; + R(rd) = s->pc + 4; + IFDEF(CONFIG_FTRACE, ftrace_jalr(s, rd, src1 + imm)); + } while (0)); + INSTPAT("??????? ????? ????? 000 ????? 11000 11", beq, B, + do_branch(s, src1 == src2, imm)); + INSTPAT("??????? ????? ????? 001 ????? 11000 11", bne, B, + do_branch(s, src1 != src2, imm)); + INSTPAT("??????? ????? ????? 100 ????? 11000 11", blt, B, + do_branch(s, (sword_t)src1 < (sword_t)src2, imm)); + INSTPAT("??????? ????? ????? 101 ????? 11000 11", bge, B, + do_branch(s, (sword_t)src1 >= (sword_t)src2, imm)); + INSTPAT("??????? ????? ????? 110 ????? 11000 11", bltu, B, + do_branch(s, src1 < src2, imm)); + INSTPAT("??????? ????? ????? 111 ????? 11000 11", bgeu, B, + do_branch(s, src1 >= src2, imm)); - INSTPAT("??????? ????? ????? 000 ????? 00000 11", lb , I, R(rd) = SEXT(Mr(src1 + imm, 1), 8)); - INSTPAT("??????? ????? ????? 001 ????? 00000 11", lh , I, R(rd) = SEXT(Mr(src1 + imm, 2), 16)); - INSTPAT("??????? ????? ????? 010 ????? 00000 11", lw , I, R(rd) = SEXT(Mr(src1 + imm, 4), 32)); - INSTPAT("??????? ????? ????? 100 ????? 00000 11", lbu , I, R(rd) = Mr(src1 + imm, 1)); - INSTPAT("??????? ????? ????? 101 ????? 00000 11", lhu , I, R(rd) = Mr(src1 + imm, 2)); - INSTPAT("??????? ????? ????? 000 ????? 01000 11", sb , S, Mw(src1 + imm, 1, src2)); - INSTPAT("??????? ????? ????? 001 ????? 01000 11", sh , S, Mw(src1 + imm, 2, src2)); - INSTPAT("??????? ????? ????? 010 ????? 01000 11", sw , S, Mw(src1 + imm, 4, src2)); + INSTPAT("??????? ????? ????? 000 ????? 00000 11", lb, I, + R(rd) = SEXT(Mr(s, src1 + imm, 1), 8)); + INSTPAT("??????? ????? ????? 001 ????? 00000 11", lh, I, + R(rd) = SEXT(Mr(s, src1 + imm, 2), 16)); + INSTPAT("??????? ????? ????? 010 ????? 00000 11", lw, I, + R(rd) = SEXT(Mr(s, src1 + imm, 4), 32)); + INSTPAT("??????? ????? ????? 100 ????? 00000 11", lbu, I, + R(rd) = Mr(s, src1 + imm, 1)); + INSTPAT("??????? ????? ????? 101 ????? 00000 11", lhu, I, + R(rd) = Mr(s, src1 + imm, 2)); + INSTPAT("??????? ????? ????? 000 ????? 01000 11", sb, S, + Mw(s, src1 + imm, 1, src2)); + INSTPAT("??????? ????? ????? 001 ????? 01000 11", sh, S, + Mw(s, src1 + imm, 2, src2)); + INSTPAT("??????? ????? ????? 010 ????? 01000 11", sw, S, + Mw(s, src1 + imm, 4, src2)); - INSTPAT("??????? ????? ????? 000 ????? 00100 11", addi , I, R(rd) = src1 + imm); - INSTPAT("??????? ????? ????? 010 ????? 00100 11", slti , I, R(rd) = (sword_t)src1 < (sword_t)imm ? 1 : 0); - INSTPAT("??????? ????? ????? 011 ????? 00100 11", sltiu , I, R(rd) = src1 < imm ? 1 : 0); - INSTPAT("??????? ????? ????? 100 ????? 00100 11", xori , I, R(rd) = src1 ^ imm); - INSTPAT("??????? ????? ????? 110 ????? 00100 11", ori , I, R(rd) = src1 | imm); - INSTPAT("??????? ????? ????? 111 ????? 00100 11", andi , I, R(rd) = src1 & imm); - INSTPAT("0000000 ????? ????? 001 ????? 00100 11", slli , I, R(rd) = src1 << imm); - INSTPAT("0000000 ????? ????? 101 ????? 00100 11", srli , I, R(rd) = src1 >> imm); - INSTPAT("0100000 ????? ????? 101 ????? 00100 11", srai , I, R(rd) = (sword_t)src1 >> (imm & 0x01F)); - INSTPAT("0000000 ????? ????? 000 ????? 01100 11", add , R, R(rd) = src1 + src2); - INSTPAT("0100000 ????? ????? 000 ????? 01100 11", sub , R, R(rd) = src1 - src2); - INSTPAT("0000000 ????? ????? 001 ????? 01100 11", sll , R, R(rd) = src1 << src2); - INSTPAT("0000000 ????? ????? 010 ????? 01100 11", slt , R, R(rd) = (sword_t)src1 < (sword_t)src2 ? 1 : 0); - INSTPAT("0000000 ????? ????? 011 ????? 01100 11", sltu , R, R(rd) = src1 < src2 ? 1 : 0); - INSTPAT("0000000 ????? ????? 100 ????? 01100 11", xor , R, R(rd) = src1 ^ src2); - INSTPAT("0000000 ????? ????? 101 ????? 01100 11", srl , R, R(rd) = src1 >> src2); - INSTPAT("0100000 ????? ????? 101 ????? 01100 11", sra , R, R(rd) = (sword_t)src1 >> (src2 & 0x01F)); - INSTPAT("0000000 ????? ????? 110 ????? 01100 11", or , R, R(rd) = src1 | src2); - INSTPAT("0000000 ????? ????? 111 ????? 01100 11", and , R, R(rd) = src1 & src2); + INSTPAT("??????? ????? ????? 000 ????? 00100 11", addi, I, + R(rd) = src1 + imm); + INSTPAT("??????? ????? ????? 010 ????? 00100 11", slti, I, + R(rd) = (sword_t)src1 < (sword_t)imm ? 1 : 0); + INSTPAT("??????? ????? ????? 011 ????? 00100 11", sltiu, I, + R(rd) = src1 < imm ? 1 : 0); + INSTPAT("??????? ????? ????? 100 ????? 00100 11", xori, I, + R(rd) = src1 ^ imm); + INSTPAT("??????? ????? ????? 110 ????? 00100 11", ori, I, R(rd) = src1 | imm); + INSTPAT("??????? ????? ????? 111 ????? 00100 11", andi, I, + R(rd) = src1 & imm); + INSTPAT("0000000 ????? ????? 001 ????? 00100 11", slli, I, + R(rd) = src1 << imm); + INSTPAT("0000000 ????? ????? 101 ????? 00100 11", srli, I, + R(rd) = src1 >> imm); + INSTPAT("0100000 ????? ????? 101 ????? 00100 11", srai, I, + R(rd) = (sword_t)src1 >> (imm & 0x01F)); + INSTPAT("0000000 ????? ????? 000 ????? 01100 11", add, R, + R(rd) = src1 + src2); + INSTPAT("0100000 ????? ????? 000 ????? 01100 11", sub, R, + R(rd) = src1 - src2); + INSTPAT("0000000 ????? ????? 001 ????? 01100 11", sll, R, + R(rd) = src1 << src2); + INSTPAT("0000000 ????? ????? 010 ????? 01100 11", slt, R, + R(rd) = (sword_t)src1 < (sword_t)src2 ? 1 : 0); + INSTPAT("0000000 ????? ????? 011 ????? 01100 11", sltu, R, + R(rd) = src1 < src2 ? 1 : 0); + INSTPAT("0000000 ????? ????? 100 ????? 01100 11", xor, R, + R(rd) = src1 ^ src2); + INSTPAT("0000000 ????? ????? 101 ????? 01100 11", srl, R, + R(rd) = src1 >> src2); + INSTPAT("0100000 ????? ????? 101 ????? 01100 11", sra, R, + R(rd) = (sword_t)src1 >> (src2 & 0x01F)); + INSTPAT("0000000 ????? ????? 110 ????? 01100 11", or, R, R(rd) = src1 | src2); + INSTPAT("0000000 ????? ????? 111 ????? 01100 11", and, R, + R(rd) = src1 & src2); - INSTPAT("0000000 00001 00000 000 00000 11100 11", ebreak , N, NEMUTRAP(s->pc, R(10))); // R(10) is $a0 + INSTPAT("0000000 00001 00000 000 00000 11100 11", ebreak, N, + NEMUTRAP(s->pc, R(10))); // R(10) is $a0 // "M" - INSTPAT("0000001 ????? ????? 000 ????? 01100 11", mul , R, R(rd) = src1 * src2); - INSTPAT("0000001 ????? ????? 001 ????? 01100 11", mulh , R, R(rd) = (int64_t)(sword_t)src1 * (sword_t)src2 >> 32); - INSTPAT("0000001 ????? ????? 010 ????? 01100 11", mulhsu , R, R(rd) = (int64_t)(sword_t)src1 * (uint64_t)src2 >> 32); - INSTPAT("0000001 ????? ????? 011 ????? 01100 11", mulhu , R, R(rd) = (uint64_t)src1 * (uint64_t)src2 >> 32); - INSTPAT("0000001 ????? ????? 100 ????? 01100 11", div , R, R(rd) = (sword_t)src1 / (sword_t)src2); - INSTPAT("0000001 ????? ????? 101 ????? 01100 11", divu , R, R(rd) = src1 / src2); - INSTPAT("0000001 ????? ????? 110 ????? 01100 11", rem , R, R(rd) = (sword_t)src1 % (sword_t)src2); - INSTPAT("0000001 ????? ????? 111 ????? 01100 11", remu , R, R(rd) = src1 % src2); + INSTPAT("0000001 ????? ????? 000 ????? 01100 11", mul, R, + R(rd) = src1 * src2); + INSTPAT("0000001 ????? ????? 001 ????? 01100 11", mulh, R, + R(rd) = (int64_t)(sword_t)src1 * (sword_t)src2 >> 32); + INSTPAT("0000001 ????? ????? 010 ????? 01100 11", mulhsu, R, + R(rd) = (int64_t)(sword_t)src1 * (uint64_t)src2 >> 32); + INSTPAT("0000001 ????? ????? 011 ????? 01100 11", mulhu, R, + R(rd) = (uint64_t)src1 * (uint64_t)src2 >> 32); + INSTPAT("0000001 ????? ????? 100 ????? 01100 11", div, R, + R(rd) = (sword_t)src1 / (sword_t)src2); + INSTPAT("0000001 ????? ????? 101 ????? 01100 11", divu, R, + R(rd) = src1 / src2); + INSTPAT("0000001 ????? ????? 110 ????? 01100 11", rem, R, + R(rd) = (sword_t)src1 % (sword_t)src2); + INSTPAT("0000001 ????? ????? 111 ????? 01100 11", remu, R, + R(rd) = src1 % src2); - INSTPAT("??????? ????? ????? ??? ????? ????? ??", inv , N, INV(s->pc)); + INSTPAT("??????? ????? ????? ??? ????? ????? ??", inv, N, INV(s->pc)); INSTPAT_END(); R(0) = 0; // reset $zero to 0 diff --git a/nemu/src/isa/riscv32/reg.c b/nemu/src/isa/riscv32/reg.c index 4630df9..52e6ba0 100644 --- a/nemu/src/isa/riscv32/reg.c +++ b/nemu/src/isa/riscv32/reg.c @@ -13,20 +13,20 @@ * See the Mulan PSL v2 for more details. ***************************************************************************************/ -#include #include "local-include/reg.h" +#include "gdbstub.h" #include "macro.h" +#include +#include -const char *regs[] = { - "$0", "ra", "sp", "gp", "tp", "t0", "t1", "t2", - "s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5", - "a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7", - "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6" -}; +const char *regs[] = {"$0", "ra", "sp", "gp", "tp", "t0", "t1", "t2", + "s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5", + "a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7", + "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6"}; void isa_reg_display() { int colomn_per_row = 4; - for(int i = 0; i < ARRLEN(regs); i++) { + for (int i = 0; i < ARRLEN(regs); i++) { printf("\e[1;34m%3s\e[0m: " FMT_PADDR, reg_name(i), gpr(i)); if (i % colomn_per_row == 3) putchar('\n'); @@ -49,3 +49,33 @@ word_t isa_reg_str2val(const char *s, bool *success) { return gpr(i); } + +int isa_read_reg(void *args, int regno, size_t *reg_value) { + if (regno > 32) { + return EFAULT; + } + + if (regno == 32) { + *reg_value = cpu.pc; + } else { + *reg_value = cpu.gpr[regno]; + } + return 0; +} + +int isa_write_reg(void *args, int regno, size_t data) { + if (regno > 32) { + return EFAULT; + } + + if (regno == 32) { + cpu.pc = data; + } else { + cpu.gpr[regno] = data; + } + return 0; +} + +arch_info_t isa_arch_info = {.reg_num = 33, + .reg_byte = MUXDEF(CONFIG_RV64, 8, 4), + .target_desc = TARGET_RV32}; diff --git a/nemu/src/memory/paddr.c b/nemu/src/memory/paddr.c index 437debd..10ee66c 100644 --- a/nemu/src/memory/paddr.c +++ b/nemu/src/memory/paddr.c @@ -15,12 +15,12 @@ #include "common.h" #include "debug.h" -#include -#include #include #include +#include +#include -#if defined(CONFIG_PMEM_MALLOC) +#if defined(CONFIG_PMEM_MALLOC) static uint8_t *pmem = NULL; #else // CONFIG_PMEM_GARRAY static uint8_t pmem[CONFIG_MSIZE] PG_ALIGN = {}; @@ -31,7 +31,7 @@ static word_t mtrace_end[CONFIG_MTRACE_RANGE_MAX] = {0}; static int range_count = 0; #endif -uint8_t* guest_to_host(paddr_t paddr) { return pmem + paddr - CONFIG_MBASE; } +uint8_t *guest_to_host(paddr_t paddr) { return pmem + paddr - CONFIG_MBASE; } paddr_t host_to_guest(uint8_t *haddr) { return haddr - pmem + CONFIG_MBASE; } static word_t pmem_read(paddr_t addr, int len) { @@ -44,22 +44,24 @@ static void pmem_write(paddr_t addr, int len, word_t data) { } static void out_of_bound(paddr_t addr) { - panic("address = " FMT_PADDR " is out of bound of pmem [" FMT_PADDR ", " FMT_PADDR "] at pc = " FMT_WORD, - addr, PMEM_LEFT, PMEM_RIGHT, cpu.pc); + panic("address = " FMT_PADDR " is out of bound of pmem [" FMT_PADDR + ", " FMT_PADDR "] at pc = " FMT_WORD, + addr, PMEM_LEFT, PMEM_RIGHT, cpu.pc); } #ifdef CONFIG_MTRACE static void mtrace_print(char type, word_t addr, int len, word_t data) { for (int i = 0; i < range_count; i++) - if (addr <= mtrace_end[i] && addr >= mtrace_start[i] ) { - Trace("Mem %c " FMT_PADDR "%d D " FMT_PADDR, type, addr, len, data); + if (addr <= mtrace_end[i] && addr >= mtrace_start[i]) { + Trace("PC=" FMT_PADDR " Mem %c" FMT_PADDR " %d D " FMT_PADDR, cpu.pc, + type, addr, len, data); break; } } #endif void init_mem() { -#if defined(CONFIG_PMEM_MALLOC) +#if defined(CONFIG_PMEM_MALLOC) pmem = malloc(CONFIG_MSIZE); assert(pmem); #endif @@ -67,15 +69,17 @@ void init_mem() { char range[sizeof(CONFIG_MTRACE_RANGE)] = CONFIG_MTRACE_RANGE; char *saveptr, *ptr; ptr = strtok_r(range, ",", &saveptr); - for (range_count = 0; range_count < CONFIG_MTRACE_RANGE_MAX; ) { + for (range_count = 0; range_count < CONFIG_MTRACE_RANGE_MAX;) { word_t start, end; - Assert(sscanf(ptr, FMT_PADDR "-" FMT_PADDR, &start, &end) == 2, "Config option MTRACE_RANGE has wrong format"); + Assert(sscanf(ptr, FMT_PADDR "-" FMT_PADDR, &start, &end) == 2, + "Config option MTRACE_RANGE has wrong format"); mtrace_start[range_count] = start; mtrace_end[range_count] = end; range_count++; ptr = strtok_r(NULL, ",", &saveptr); - if (!ptr) break; + if (!ptr) + break; } Trace("MTRACE ranges: "); for (int i = 0; i < range_count; i++) { @@ -83,24 +87,31 @@ void init_mem() { } #endif IFDEF(CONFIG_MEM_RANDOM, memset(pmem, rand(), CONFIG_MSIZE)); - Log("physical memory area [" FMT_PADDR ", " FMT_PADDR "]", PMEM_LEFT, PMEM_RIGHT); + Log("physical memory area [" FMT_PADDR ", " FMT_PADDR "]", PMEM_LEFT, + PMEM_RIGHT); } word_t paddr_read(paddr_t addr, int len) { word_t result = 0; - if (likely(in_pmem(addr))) { result = pmem_read(addr, len); goto mtrace;} - IFDEF(CONFIG_DEVICE, result = mmio_read(addr, len); goto mtrace) + if (likely(in_pmem(addr))) { + result = pmem_read(addr, len); + goto mtrace; + } + IFDEF(CONFIG_DEVICE, result = mmio_read(addr, len); goto mtrace;) out_of_bound(addr); mtrace: IFDEF(CONFIG_MTRACE, mtrace_print('R', addr, len, result)); - + return result; } void paddr_write(paddr_t addr, int len, word_t data) { IFDEF(CONFIG_MTRACE, mtrace_print('W', addr, len, data)); - if (likely(in_pmem(addr))) { pmem_write(addr, len, data); return; } + if (likely(in_pmem(addr))) { + pmem_write(addr, len, data); + return; + } IFDEF(CONFIG_DEVICE, mmio_write(addr, len, data); return); out_of_bound(addr); } diff --git a/nemu/src/monitor/filelist.mk b/nemu/src/monitor/filelist.mk new file mode 100644 index 0000000..4094bee --- /dev/null +++ b/nemu/src/monitor/filelist.mk @@ -0,0 +1,3 @@ +DIRS-y += src/monitor/monitor.c + +CXXSRC += src/monitor/gdbstub.cc diff --git a/nemu/src/monitor/gdbstub.cc b/nemu/src/monitor/gdbstub.cc new file mode 100644 index 0000000..40aa594 --- /dev/null +++ b/nemu/src/monitor/gdbstub.cc @@ -0,0 +1,130 @@ +#include "utils.h" +#include + +extern "C" { +#include +#include +#include +#include +#include +#include +#include +#include +} + +typedef struct { + std::vector *bp; + bool halt; +} DbgState; + +int nemu_read_mem(void *args, size_t addr, size_t len, void *val) { + if (!in_pmem(addr)) + return EINVAL; + memcpy(val, guest_to_host(addr), len); + return 0; +} + +int nemu_write_mem(void *args, size_t addr, size_t len, void *val) { + if (!in_pmem(addr)) + return EINVAL; + memcpy(guest_to_host(addr), val, len); + return 0; +} + +static void nemu_is_stopped(gdb_action_t *act, breakpoint_t *stopped_at) { + switch (nemu_state.state) { + case NEMU_RUNNING: + nemu_state.state = NEMU_STOP; + switch (stopped_at->type) { + case BP_SOFTWARE: + act->reason = gdb_action_t::ACT_BREAKPOINT; + break; + case BP_ACCESS: + act->reason = gdb_action_t::ACT_WATCH; + break; + case BP_WRITE: + act->reason = gdb_action_t::ACT_WWATCH; + break; + case BP_READ: + act->reason = gdb_action_t::ACT_RWATCH; + break; + } + act->data = stopped_at->addr; + break; + + default: + act->reason = gdb_action_t::ACT_SHUTDOWN; + act->data = nemu_state.halt_ret; + } +} + +void nemu_cont(void *args, gdb_action_t *res) { + DbgState *dbg_state = (DbgState *)args; + breakpoint_t *stopped_at = + cpu_exec_with_bp(-1, dbg_state->bp->data(), dbg_state->bp->size()); + nemu_is_stopped(res, stopped_at); +} + +void nemu_stepi(void *args, gdb_action_t *res) { + DbgState *dbg_state = (DbgState *)args; + breakpoint_t *stopped_at = + cpu_exec_with_bp(1, dbg_state->bp->data(), dbg_state->bp->size()); + nemu_is_stopped(res, stopped_at); +} + +bool nemu_set_bp(void *args, size_t addr, bp_type_t type) { + DbgState *dbg_state = (DbgState *)args; + for (const auto &bp : *dbg_state->bp) { + if (bp.addr == addr && bp.type == type) { + return true; + } + } + dbg_state->bp->push_back({.addr = addr, .type = type}); + return true; +} + +bool nemu_del_bp(void *args, size_t addr, bp_type_t type) { + DbgState *dbg_state = (DbgState *)args; + for (auto it = dbg_state->bp->begin(); it != dbg_state->bp->end(); it++) { + if (it->addr == addr && it->type == type) { + std::swap(*it, *dbg_state->bp->rbegin()); + dbg_state->bp->pop_back(); + return true; + } + } + return false; +} + +void nemu_on_interrupt(void *args) { + // fputs("Not implemented", stderr); +} + +static struct target_ops nemu_gdbstub_ops = {.cont = nemu_cont, + .stepi = nemu_stepi, + .read_reg = isa_read_reg, + .write_reg = isa_write_reg, + .read_mem = nemu_read_mem, + .write_mem = nemu_write_mem, + .set_bp = nemu_set_bp, + .del_bp = nemu_del_bp, + .on_interrupt = NULL}; +static DbgState dbg; +extern "C" { +static gdbstub_t gdbstub_priv; +#define SOCKET_ADDR "127.0.0.1:1234" +int nemu_gdbstub_init() { + dbg.bp = new std::vector(); + assert(dbg.bp); + if (!gdbstub_init(&gdbstub_priv, &nemu_gdbstub_ops, + (arch_info_t)isa_arch_info, SOCKET_ADDR)) { + return EINVAL; + } + return 0; +} +int nemu_gdbstub_run() { + puts("Waiting for gdb connection at " SOCKET_ADDR); + bool success = gdbstub_run(&gdbstub_priv, &dbg); + gdbstub_close(&gdbstub_priv); + return !success; +} +} diff --git a/nemu/src/monitor/monitor.c b/nemu/src/monitor/monitor.c index 0154208..9f49786 100644 --- a/nemu/src/monitor/monitor.c +++ b/nemu/src/monitor/monitor.c @@ -22,24 +22,25 @@ void init_log(const char *log_file); void init_mem(); void init_difftest(char *ref_so_file, long img_size, int port); void init_device(); -void init_sdb(); void init_disasm(const char *triple); +int nemu_gdbstub_init(); static void welcome() { - Log("Trace: %s", MUXDEF(CONFIG_TRACE, ANSI_FMT("ON", ANSI_FG_GREEN), ANSI_FMT("OFF", ANSI_FG_RED))); - IFDEF(CONFIG_TRACE, Log("If trace is enabled, a log file will be generated " - "to record the trace. This may lead to a large log file. " - "If it is not necessary, you can disable it in menuconfig")); + Log("Trace: %s", MUXDEF(CONFIG_TRACE, ANSI_FMT("ON", ANSI_FG_GREEN), + ANSI_FMT("OFF", ANSI_FG_RED))); + IFDEF(CONFIG_TRACE, + Log("If trace is enabled, a log file will be generated " + "to record the trace. This may lead to a large log file. " + "If it is not necessary, you can disable it in menuconfig")); Log("Build time: %s, %s", __TIME__, __DATE__); - printf("Welcome to %s-NEMU!\n", ANSI_FMT(str(__GUEST_ISA__), ANSI_FG_YELLOW ANSI_BG_RED)); + printf("Welcome to %s-NEMU!\n", + ANSI_FMT(str(__GUEST_ISA__), ANSI_FG_YELLOW ANSI_BG_RED)); printf("For help, type \"help\"\n"); } #ifndef CONFIG_TARGET_AM #include -void sdb_set_batch_mode(); - static char *log_file = NULL; static char *elf_file = NULL; static char *diff_so_file = NULL; @@ -70,32 +71,41 @@ static long load_img() { static int parse_args(int argc, char *argv[]) { const struct option table[] = { - {"batch" , no_argument , NULL, 'b'}, - {"log" , required_argument, NULL, 'l'}, - {"diff" , required_argument, NULL, 'd'}, - {"port" , required_argument, NULL, 'p'}, - {"elf" , required_argument, NULL, 'f'}, - {"help" , no_argument , NULL, 'h'}, - {0 , 0 , NULL, 0 }, + {"batch", no_argument, NULL, 'b'}, + {"log", required_argument, NULL, 'l'}, + {"diff", required_argument, NULL, 'd'}, + {"port", required_argument, NULL, 'p'}, + {"elf", required_argument, NULL, 'f'}, + {"help", no_argument, NULL, 'h'}, + {0, 0, NULL, 0}, }; int o; - while ( (o = getopt_long(argc, argv, "-bhl:d:p:", table, NULL)) != -1) { + while ((o = getopt_long(argc, argv, "-bhl:d:p:", table, NULL)) != -1) { switch (o) { - case 'b': sdb_set_batch_mode(); break; - case 'p': sscanf(optarg, "%d", &difftest_port); break; - case 'l': log_file = optarg; break; - case 'd': diff_so_file = optarg; break; - case 'f': elf_file = optarg; break; - case 1: img_file = optarg; return 0; - default: - printf("Usage: %s [OPTION...] IMAGE [args]\n\n", argv[0]); - printf("\t-b,--batch run with batch mode\n"); - printf("\t-l,--log=FILE output log to FILE\n"); - printf("\t-d,--diff=REF_SO run DiffTest with reference REF_SO\n"); - printf("\t-p,--port=PORT run DiffTest with port PORT\n"); - printf("\t-f,--elf=FILE elf file with debug info\n"); - printf("\n"); - exit(0); + case 'p': + sscanf(optarg, "%d", &difftest_port); + break; + case 'l': + log_file = optarg; + break; + case 'd': + diff_so_file = optarg; + break; + case 'f': + elf_file = optarg; + break; + case 1: + img_file = optarg; + return 0; + default: + printf("Usage: %s [OPTION...] IMAGE [args]\n\n", argv[0]); + printf("\t-b,--batch run with batch mode\n"); + printf("\t-l,--log=FILE output log to FILE\n"); + printf("\t-d,--diff=REF_SO run DiffTest with reference REF_SO\n"); + printf("\t-p,--port=PORT run DiffTest with port PORT\n"); + printf("\t-f,--elf=FILE elf file with debug info\n"); + printf("\n"); + exit(0); } } return 0; @@ -128,11 +138,14 @@ void init_monitor(int argc, char *argv[]) { /* Initialize differential testing. */ init_difftest(diff_so_file, img_size, difftest_port); - /* Initialize the simple debugger. */ - init_sdb(); + /* Initialize debugger */ + if (nemu_gdbstub_init()) { + Error("Failed to init"); + exit(1); + } // printf("elf_file: %s\n", elf_file); - if(elf_file != NULL) { + if (elf_file != NULL) { #ifdef CONFIG_FTRACE void init_elf(const char *path); init_elf(elf_file); @@ -142,14 +155,13 @@ void init_monitor(int argc, char *argv[]) { } #ifndef CONFIG_ISA_loongarch32r - IFDEF(CONFIG_ITRACE, init_disasm( - MUXDEF(CONFIG_ISA_x86, "i686", - MUXDEF(CONFIG_ISA_mips32, "mipsel", - MUXDEF(CONFIG_ISA_riscv, - MUXDEF(CONFIG_RV64, "riscv64", - "riscv32"), - "bad"))) "-pc-linux-gnu" - )); + IFDEF(CONFIG_ITRACE, + init_disasm( + MUXDEF(CONFIG_ISA_x86, "i686", + MUXDEF(CONFIG_ISA_mips32, "mipsel", + MUXDEF(CONFIG_ISA_riscv, + MUXDEF(CONFIG_RV64, "riscv64", "riscv32"), + "bad"))) "-pc-linux-gnu")); #endif /* Display welcome message. */ diff --git a/nemu/src/monitor/sdb/addrexp.l b/nemu/src/monitor/sdb/addrexp.l deleted file mode 100644 index d814ee3..0000000 --- a/nemu/src/monitor/sdb/addrexp.l +++ /dev/null @@ -1,24 +0,0 @@ -%{ - #include - #include - static bool success = false; - void yyerror(word_t *result, const char *err); -%} -%option noyywrap - -%% - -0[xX][0-9a-fA-F]+ { yylval = strtoul(yytext, NULL, 16); return HEX_NUMBER; } -[0-9]+ { yylval = strtoul(yytext, NULL, 10); return NUMBER; } -$[asgprt$][0-9pa][0-9]? { - yylval = isa_reg_str2val(yytext + 1, &success); - if(!success) { - yyerror(NULL, "Failed to convert reg to value"); - return YYerror; - } - return REGISTER; -} -[+\-*/<=()] { return *yytext; } -[ \t] { } -. { printf("Unexpected character: %s\n", yytext); return YYerror; } -%% diff --git a/nemu/src/monitor/sdb/addrexp.y b/nemu/src/monitor/sdb/addrexp.y deleted file mode 100644 index 4094e0b..0000000 --- a/nemu/src/monitor/sdb/addrexp.y +++ /dev/null @@ -1,60 +0,0 @@ -%code requires { - #include - #include - #include - #include - extern int yylex(void); -} -%{ - #include - #include - #include - #include - #include - void yyerror(word_t *result, const char *err) { - Error("%s", err); - } -%} - -%token NUMBER HEX_NUMBER -%token REGISTER -%locations -%start input -%define api.value.type { word_t } -%parse-param { uint32_t *result } -%left '-' '+' -%left '*' '/' - -%% -input - : expression { *result = $1; } - ; - -expression - : number { $$ = $1; } - | expression '>' '=' expression { $$ = ($1 >= $4); } - | expression '<' '=' expression { $$ = ($1 <= $4); } - | expression '=' '=' expression { $$ = ($1 == $4); } - | expression '!' '=' expression { $$ = ($1 == $4); } - | expression '>' expression { $$ = ($1 > $3); } - | expression '<' expression { $$ = ($1 < $3); } - | expression '+' expression { $$ = $1 + $3; } - | expression '-' expression { $$ = $1 - $3; } - | expression '*' expression { $$ = $1 * $3; } - | expression '/' expression { - if($3 == 0) { - fprintf(stderr, "Error: divide by zero at %u / %u\n", $1, $3); - YYABORT; - }; - $$ = $1 / $3; - } - | '-' number { $$ = -$2; } - | '*' expression { $$ = vaddr_read($2, WORD_BYTES); } - | '(' expression ')' { $$ = $2; } - -number - : REGISTER - | NUMBER - | HEX_NUMBER - -%% diff --git a/nemu/src/monitor/sdb/filelist.mk b/nemu/src/monitor/sdb/filelist.mk deleted file mode 100644 index f477a4f..0000000 --- a/nemu/src/monitor/sdb/filelist.mk +++ /dev/null @@ -1,2 +0,0 @@ -SRCS-y += src/monitor/sdb/addrexp.tag.c src/monitor/sdb/addrexp.yy.c -LFLAGS += -DYY_NO_UNPUT -DYY_NO_INPUT diff --git a/nemu/src/monitor/sdb/sdb.c b/nemu/src/monitor/sdb/sdb.c deleted file mode 100644 index 3b56172..0000000 --- a/nemu/src/monitor/sdb/sdb.c +++ /dev/null @@ -1,361 +0,0 @@ -/*************************************************************************************** - * Copyright (c) 2014-2022 Zihao Yu, Nanjing University - * - * NEMU is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan - *PSL v2. You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY - *KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO - *NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * - * See the Mulan PSL v2 for more details. - ***************************************************************************************/ - -#include "sdb.h" -#include "common.h" -#include "sys/types.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int is_batch_mode = false; - -// command handlers -static int cmd_help(char *args); -static int cmd_c(char *args); -static int cmd_p(char *args); -static int cmd_q(char *args); -static int cmd_w(char *args); -static int cmd_x(char *args); -static int cmd_si(char *args); -static int cmd_info(char *args); -static int cmd_info_r(char *args); -static int cmd_info_w(char *args); - -static struct CommandTable { - const char *name; - const char *description; - int (*handler)(char *); - struct CommandTable *subcommand; - int nr_subcommand; -} cmd_info_table[] = - { - {"r", "List all registers and their contents", cmd_info_r, NULL, 0}, - {"w", "Status of specified watchpoints", cmd_info_w, NULL, 0}, -}, - cmd_table[] = { - {"help", "Display information about all supported commands", cmd_help, - NULL, 0}, - {"c", "Continue the execution of the program", cmd_c, NULL, 0}, - {"p", "Print expression result", cmd_p, NULL, 0}, - {"q", "Exit NEMU", cmd_q, NULL, 0}, - {"x", "Examine content of physical memory address", cmd_x, NULL, 0}, - {"w", "Break when expression is changed", cmd_w, NULL, 0}, - {"si", "Execute next [n] program line", cmd_si, NULL, 0}, - {"info", "Print information of registers or watchpoints", cmd_info, - cmd_info_table, ARRLEN(cmd_info_table)}, -}; - -#define NR_CMD ARRLEN(cmd_table) - -void init_regex(); -void init_wp_pool(); - -/* We use the `readline' library to provide more flexibility to read from stdin. - */ -static char *rl_gets() { - static char *line_read = NULL; - - if (line_read) { - free(line_read); - line_read = NULL; - } - - line_read = readline("\e[1;34m(nemu)\e[0m "); - - if (line_read && *line_read) { - add_history(line_read); - } - - return line_read; -} - -/* Extract Integer from a string. Can handle hex, binary and decimal numbers. - * Print error if meet any error. - * Return `UINTMAX_MAX` if the string is invalid or number exceed the limit of - * uint. - */ -static word_t parse_uint(const char *arg, bool *success) { - if (arg == NULL) { - puts("Invalid uint argument."); - *success = false; - return 0; - } - int base = 10; - int token_length = strnlen(arg, 34); - if (token_length > 2) { - if (arg[0] == '0' && (arg[1] == 'b' || arg[1] == 'B')) { - base = 2; - arg = arg + 2; - } else if (arg[0] == '0' && (arg[1] == 'x' || arg[1] == 'X')) { - base = 16; - arg = arg + 2; - } - } - char *endptr; - uintmax_t n = strtoumax(arg, &endptr, base); - if (errno == ERANGE || n > WORD_T_MAX) { - printf("%s exceed the limit of uint\n", arg); - *success = false; - return 0; - } else if (arg == endptr) { - puts("Invalid uint argument."); - *success = false; - return 0; - } else if (n > WORD_T_MAX) { - *success = false; - return WORD_T_MAX; - } else { - *success = true; - return n; - } -} - -word_t parse_expr(const char *arg, bool *success) { - if (arg == NULL) { - puts("Invalid expr argument."); - *success = false; - return 0; - } else { - word_t res; - yy_scan_string(arg); - *success = !yyparse(&res); - yylex_destroy(); - return res; - } -} - -static int cmd_c(char *args) { - cpu_exec(-1); - return 0; -} - -static int cmd_p(char *args) { - char *arg = strtok(NULL, ""); - bool res = false; - - word_t result = parse_expr(arg, &res); - if (!res) - goto wrong_usage; - printf("%s: %u\n", arg, result); - return 0; - -wrong_usage: - printf("Invalid argument for command p: %s\n", arg); - printf("Usage: p [EXPR: ]\n"); - return 0; -} - -static int cmd_q(char *args) { - nemu_state.state = NEMU_QUIT; - return -1; -} - -/* Single stepping - * : execute step - */ -static int cmd_si(char *args) { - char *arg = strtok(NULL, " "); - if (arg == NULL) { - cpu_exec(1); - } else { - bool res = false; - word_t n = parse_uint(arg, &res); - if (!res) - goto wrong_usage; - cpu_exec(n); - } - return 0; - -wrong_usage: - printf("Invalid argument for command si: %s\n", args); - printf("Usage: si [N: uint]\n"); - return 0; -} - -static int cmd_info_r(char *args) { - isa_reg_display(); - return 0; -} - -static int cmd_info_w(char *args) { - printf("Not implemented"); - return 0; -} - -static int cmd_w(char *args) { - char *expr = strtok(NULL, ""); - wp_add(expr); - return 0; -} - -static int cmd_x(char *args) { - char *arg = strtok(NULL, " "); - bool res = false; - word_t n = parse_uint(arg, &res); - if (!res) - goto wrong_usage; - // No deliminter here, just pass all the remain argument to `parse_expr()` - arg = strtok(NULL, ""); - word_t start_addr = parse_expr(arg, &res); - if (!res) - goto wrong_usage; - start_addr = start_addr & ~(WORD_BYTES - 1); - for (vaddr_t vaddr = start_addr; vaddr < start_addr + n; vaddr += WORD_BYTES) { - word_t value = vaddr_read(vaddr, WORD_BYTES); - printf("\e[1;34m" FMT_PADDR "\e[0m" - " " FMT_WORD "\n", - vaddr, value); - } - return 0; - -wrong_usage: - printf("Invalid argument for command x: %s\n", arg); - printf("Usage: x [N: uint] [EXPR: ]\n"); - return 0; -} - -static int cmd_info(char *args) { - char *arg = strtok(NULL, " "); - int i; - if (arg == NULL) { - goto wrong_usage; - return 0; - } - for (i = 0; i < ARRLEN(cmd_info_table); i++) { - if (strcmp(arg, cmd_info_table[i].name) == 0) { - cmd_info_table[i].handler(args); - return 0; - } - } - -wrong_usage: - printf("Invalid argument for command info: %s\n", args); - printf("Usage: info [r | w]\n"); - return 0; -} - -static int cmd_help_print(char *args, struct CommandTable *cur_cmd_table, - int cur_nr_cmd) { - int i; - char *arg = strtok(NULL, " "); - if (arg == NULL) { - return -1; - } else { - for (i = 0; i < cur_nr_cmd; i++) { - if (strcmp(arg, cur_cmd_table[i].name) == 0) { - printf("%s ", cur_cmd_table[i].name); - if (cmd_help_print(arg, cur_cmd_table[i].subcommand, - cur_cmd_table[i].nr_subcommand) == -1) { - printf("-- %s\n", cur_cmd_table[i].description); - } - return 0; - } - } - return -1; - } -} - -static int cmd_help(char *args) { - /* extract the first argument */ - char *arg = strtok(NULL, " "); - int i; - - if (arg == NULL) { - /* no argument given */ - for (i = 0; i < NR_CMD; i++) { - printf("%s -- %s\n", cmd_table[i].name, cmd_table[i].description); - } - } else { - for (i = 0; i < NR_CMD; i++) { - if (strcmp(arg, cmd_table[i].name) == 0) { - printf("%s ", cmd_table[i].name); - if (cmd_help_print(args, cmd_table[i].subcommand, - cmd_table[i].nr_subcommand) == -1) { - printf("-- %s\n", cmd_table[i].description); - // Print available subcommands - for (int j = 0; j < cmd_table[i].nr_subcommand; j++) { - struct CommandTable *sub_cmd_table = cmd_table[i].subcommand; - printf(" > %s -- %s\n", sub_cmd_table[j].name, - sub_cmd_table[j].description); - } - } - return 0; - } - } - printf("Unknown command '%s'\n", arg); - } - return 0; -} - -void sdb_set_batch_mode() { is_batch_mode = true; } - -void sdb_mainloop() { - if (is_batch_mode) { - cmd_c(NULL); - return; - } - - for (char *str; (str = rl_gets()) != NULL;) { - char *str_end = str + strlen(str); - - /* extract the first token as the command */ - char *cmd = strtok(str, " "); - if (cmd == NULL) { - continue; - } - - /* treat the remaining string as the arguments, - * which may need further parsing - */ - char *args = cmd + strlen(cmd) + 1; - if (args >= str_end) { - args = NULL; - } - -#ifdef CONFIG_DEVICE - extern void sdl_clear_event_queue(); - sdl_clear_event_queue(); -#endif - - int i; - for (i = 0; i < NR_CMD; i++) { - if (strcmp(cmd, cmd_table[i].name) == 0) { - if (cmd_table[i].handler(args) < 0) { - return; - } - break; - } - } - - if (i == NR_CMD) { - printf("Unknown command '%s'\n", cmd); - } - } -} - -void init_sdb() { - // /* Compile the regular expressions. */ - // init_regex(); - - /* Initialize the watchpoint pool. */ - init_wp_pool(); -} diff --git a/nemu/src/monitor/sdb/sdb.h b/nemu/src/monitor/sdb/sdb.h deleted file mode 100644 index 883f1b6..0000000 --- a/nemu/src/monitor/sdb/sdb.h +++ /dev/null @@ -1,25 +0,0 @@ -/*************************************************************************************** -* Copyright (c) 2014-2022 Zihao Yu, Nanjing University -* -* NEMU is licensed under Mulan PSL v2. -* You can use this software according to the terms and conditions of the Mulan PSL v2. -* You may obtain a copy of Mulan PSL v2 at: -* http://license.coscl.org.cn/MulanPSL2 -* -* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -* -* See the Mulan PSL v2 for more details. -***************************************************************************************/ - -#ifndef __SDB_H__ -#define __SDB_H__ - -#include - -word_t parse_expr(const char *arg, bool *success); -int wp_add(char * expr); -int wp_remove_by_number(int number); - -#endif diff --git a/nemu/src/monitor/sdb/watchpoint.c b/nemu/src/monitor/sdb/watchpoint.c deleted file mode 100644 index aa48d78..0000000 --- a/nemu/src/monitor/sdb/watchpoint.c +++ /dev/null @@ -1,150 +0,0 @@ -/*************************************************************************************** - * Copyright (c) 2014-2022 Zihao Yu, Nanjing University - * - * NEMU is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan - *PSL v2. You may obtain a copy of Mulan PSL v2 at: - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY - *KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO - *NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * - * See the Mulan PSL v2 for more details. - ***************************************************************************************/ - -#include "sdb.h" -#include -#include - -#define NR_WP 32 - -typedef struct watchpoint { - int NO; - struct watchpoint *next; - word_t val; - char *expr; -} WP; - -static WP wp_pool[NR_WP] = {}; -static WP *head = NULL, *tail = NULL, *free_ = NULL; -static int wp_count = 0; - -void init_wp_pool() { - int i; - for (i = 0; i < NR_WP; i++) { - wp_pool[i].NO = i; - wp_pool[i].next = (i == NR_WP - 1 ? NULL : &wp_pool[i + 1]); - } - - head = NULL; - free_ = wp_pool; -} - -static WP *wp_new() { - if (free_ == NULL) { - Error("wp_pool: Watchpoint pool not initialized or is full."); - return NULL; - } - - WP *ret = free_; - free_ = free_->next; - - ret->NO = 0; - ret->next = NULL; - return ret; -} - -static void wp_delete(WP *wp) { - Assert(wp, "Failed to delete watchpoint from pool."); - wp->next = free_; - free_ = wp; -} - -int wp_add(char * expr) { - WP *wp = wp_new(); - if (wp == NULL) { - Error("watchpoint: Failed to add watchpoint, pool is full."); - goto failed_create; - } - - wp->NO = wp_count++; - if (tail == NULL) { - head = wp; - tail = wp; - } else { - tail->next = wp; - tail = wp; - } - - bool success = false; - wp->val = parse_expr(expr, &success); - if (!success) { - Error("Failed to parse given expression `%s`", expr); - goto failed_create; - } - - int len = strlen(expr); - wp->expr = malloc((len + 1) * sizeof(char)); - if (wp->expr == NULL) { - Error("Failed to allocate memory for expression"); - goto failed_create; - } - strncpy(wp->expr, expr, len + 1); - wp->expr[len] = '\0'; - return 0; - -failed_create: - wp_delete(wp); - return 1; -} - -int wp_remove_by_number(int number) { - WP *target_prev; - // Find previous node of target number - for (target_prev = head; target_prev != NULL && target_prev->next->NO != number; target_prev = target_prev->next) ; - if (target_prev == NULL) { - Error("Watchpoint not found, you can check current watchpoints with `info w`"); - return 1; - } - WP *target = target_prev->next; - target_prev->next = target->next; - if (target == head) { - head = target->next; - } else if (target == tail) { - tail = target_prev; - } - wp_delete(target); - return 0; -} - -static bool wp_check_change(WP* wp) { - bool success = false; - word_t result; - - result = parse_expr(wp->expr, &success); - if (!success) { - panic("Failed to evaluate expression `%s`", wp->expr); - } - if (result != wp->val) { - wp->val = result; - return true; - } - return false; -} - -/* - Check if watchpoint value changed after execution -*/ -bool wp_eval_all() { - WP *wp; - bool value_change = false; - for (wp = head; wp != NULL; wp = wp->next) { - int prev_val = wp->val; - if (wp_check_change(wp)) { - printf("Watchpoint %d: %s\n %u -> %u\n", wp->NO, wp->expr, prev_val, wp->val); - value_change = true; - } - } - return value_change; -} From f38674ce79ab27781f1963e4f630419c5ea6e799 Mon Sep 17 00:00:00 2001 From: xinyangli Date: Tue, 9 Jul 2024 17:06:15 +0800 Subject: [PATCH 3/5] nemu: use NEMU_IMAGES_PATH as image search paths --- abstract-machine/am/src/riscv/npc/npc.h | 6 ++++ flake.nix | 3 +- nemu/default.nix | 2 +- nemu/src/isa/riscv64 | 1 - nemu/src/monitor/filelist.mk | 2 +- nemu/src/monitor/monitor.c | 43 +++++++++++++++++++++++-- nemu/src/utils/filelist.mk | 2 +- 7 files changed, 52 insertions(+), 7 deletions(-) create mode 100644 abstract-machine/am/src/riscv/npc/npc.h delete mode 120000 nemu/src/isa/riscv64 diff --git a/abstract-machine/am/src/riscv/npc/npc.h b/abstract-machine/am/src/riscv/npc/npc.h new file mode 100644 index 0000000..e6c0241 --- /dev/null +++ b/abstract-machine/am/src/riscv/npc/npc.h @@ -0,0 +1,6 @@ +#ifndef _AM_NPC_NPC_H_ +#define _AM_NPC_NPC_H_ +#define SERIAL_PORT 0x10000000 +#define RTC_ADDR 0x10001000 + +#endif \ No newline at end of file diff --git a/flake.nix b/flake.nix index 58652b3..0ea12c7 100644 --- a/flake.nix +++ b/flake.nix @@ -86,7 +86,8 @@ inputsFrom = [ self.packages.${system}.nemu ]; - IMAGES_PATH = "${self.packages.${system}.am-kernels}/share/binary"; + NEMU_HOME = "/home/xin/repo/ysyx-workbench/nemu"; + NEMU_IMAGES_PATH = self.packages.${system}.am-kernels + "/share/am-kernels"; }; devShells.npc = with pkgs; mkShell.override { stdenv = ccacheStdenv; } { diff --git a/nemu/default.nix b/nemu/default.nix index e6ffb2b..785d77e 100644 --- a/nemu/default.nix +++ b/nemu/default.nix @@ -43,7 +43,7 @@ stdenv.mkDerivation rec { doCheck = (defconfig == "alldefconfig"); checkPhase = if doCheck then '' - export IMAGES_PATH=${am-kernels}/share/binary + export NEMU_IMAGES_PATH=${am-kernels}/share/am-kernels make test '' else ""; diff --git a/nemu/src/isa/riscv64 b/nemu/src/isa/riscv64 deleted file mode 120000 index 4eb3f6d..0000000 --- a/nemu/src/isa/riscv64 +++ /dev/null @@ -1 +0,0 @@ -riscv32 \ No newline at end of file diff --git a/nemu/src/monitor/filelist.mk b/nemu/src/monitor/filelist.mk index 4094bee..07c4fe5 100644 --- a/nemu/src/monitor/filelist.mk +++ b/nemu/src/monitor/filelist.mk @@ -1,3 +1,3 @@ -DIRS-y += src/monitor/monitor.c +DIRS-y += src/monitor CXXSRC += src/monitor/gdbstub.cc diff --git a/nemu/src/monitor/monitor.c b/nemu/src/monitor/monitor.c index 9f49786..b9b343e 100644 --- a/nemu/src/monitor/monitor.c +++ b/nemu/src/monitor/monitor.c @@ -13,8 +13,10 @@ * See the Mulan PSL v2 for more details. ***************************************************************************************/ +#include #include #include +#include #include void init_rand(); @@ -48,13 +50,50 @@ static char *img_file = NULL; static int difftest_port = 1234; static long load_img() { + FILE *fp = NULL; + size_t img_filename_len = strlen(img_file); if (img_file == NULL) { Log("No image is given. Use the default build-in image."); return 4096; // built-in image size } - FILE *fp = fopen(img_file, "rb"); - Assert(fp, "Can not open '%s'", img_file); + // Image file is searched from paths in environment variable NEMU_IMAGES_PATH if it's a relative path + if (img_file[0] != '/') { + char *search_paths = getenv("NEMU_IMAGES_PATH"); + if(search_paths == NULL) search_paths = "./"; + search_paths = strdup(search_paths); + Trace("NEMU_IMAGES_PATH=%s", search_paths); + + char *paths_end = strchr(search_paths, '\0'); + char *p_start = search_paths; + do { + char *p = strchr(p_start, ':'); + if (p != NULL) *p = '\0'; + else p = paths_end; + + char *file_path = malloc(p - p_start + img_filename_len + 2); + strcpy(file_path, p_start); + strcat(file_path, "/"); + strcat(file_path, img_file); + + fp = fopen(file_path, "rb"); + free(file_path); + + if (fp) { + Log("Found '%s' in '%s'", img_file, p_start); + break; + } + + Assert(fp != NULL || errno == ENOENT, "Cannot open '%s'", img_file); + p_start = p + 1; + } while(p_start < paths_end); + free(search_paths); + + Assert(fp, "Cannot find '%s'", img_file); + } else { + fp = fopen(img_file, "rb"); + Assert(fp, "Cannot open '%s'", img_file); + } fseek(fp, 0, SEEK_END); long size = ftell(fp); diff --git a/nemu/src/utils/filelist.mk b/nemu/src/utils/filelist.mk index bcd69c5..2537b81 100644 --- a/nemu/src/utils/filelist.mk +++ b/nemu/src/utils/filelist.mk @@ -14,7 +14,7 @@ #**************************************************************************************/ ifneq ($(CONFIG_ITRACE)$(CONFIG_IQUEUE),) -CXXSRC = src/utils/disasm.cc +CXXSRC += src/utils/disasm.cc CXXFLAGS += $(shell llvm-config --cxxflags) -fPIE LIBS += $(shell llvm-config --libs) endif From 955f1f2d79427294fb6681a97bda073e445eb7fb Mon Sep 17 00:00:00 2001 From: xinyangli Date: Tue, 9 Jul 2024 17:26:11 +0800 Subject: [PATCH 4/5] klib: printf support --- abstract-machine/CMakeLists.txt | 120 +++++++----- abstract-machine/CMakePresets.json | 6 +- abstract-machine/am/CMakeLists.txt | 38 ++-- abstract-machine/am/src/native/CMakeLists.txt | 14 +- .../am/src/platform/nemu/ioe/timer.c | 13 +- abstract-machine/am/src/platform/nemu/trm.c | 9 +- abstract-machine/am/src/riscv/CMakeLists.txt | 17 +- .../am/src/riscv/nemu/CMakeLists.txt | 65 +++---- .../am/src/riscv/npc/CMakeLists.txt | 61 +++--- abstract-machine/am/src/riscv/npc/ioe.c | 24 ++- .../am/src/riscv/npc/libgcc/CMakeLists.txt | 15 +- abstract-machine/am/src/riscv/npc/timer.c | 15 +- abstract-machine/am/src/riscv/npc/trm.c | 6 +- abstract-machine/default.nix | 2 +- abstract-machine/klib/CMakeLists.txt | 3 + abstract-machine/klib/src/CMakeLists.txt | 34 ++-- abstract-machine/klib/src/stdio.c | 184 +++++++++++++++++- abstract-machine/klib/src/stdlib.c | 38 ++-- abstract-machine/klib/tests/CMakeLists.txt | 19 +- 19 files changed, 438 insertions(+), 245 deletions(-) diff --git a/abstract-machine/CMakeLists.txt b/abstract-machine/CMakeLists.txt index 6eb89d7..659bb84 100644 --- a/abstract-machine/CMakeLists.txt +++ b/abstract-machine/CMakeLists.txt @@ -6,7 +6,7 @@ set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 11) include(CMakeDependentOption) -include(CMakePackageConfigHelpers) # Used to find libcheck +include(CMakePackageConfigHelpers) # Used to find libcheck include(CTest) include(GNUInstallDirs) @@ -15,15 +15,11 @@ set(ISA CACHE STRING "Target ISA") set_property(CACHE ISA PROPERTY STRINGS "riscv" "x86" "x86_64" "native") string(TOUPPER ${ISA} ISA_UPPER) -cmake_dependent_option( - __PLATFORM_NEMU__ "Run on NEMU" - ON "ISA MATCHES \"(riscv | x86)\"" OFF) -cmake_dependent_option( - __PLATFORM_NPC__ "Run on NPC" - ON "ISA MATCHES riscv" OFF) -cmake_dependent_option( - __PLATFORM_NATIVE__ "Run on native" - ON "ISA MATCHES native" OFF) +cmake_dependent_option(__PLATFORM_NEMU__ "Run on NEMU" ON + "ISA MATCHES \"(riscv | x86)\"" OFF) +cmake_dependent_option(__PLATFORM_NPC__ "Run on NPC" ON "ISA MATCHES riscv" OFF) +cmake_dependent_option(__PLATFORM_NATIVE__ "Run on native" ON + "ISA MATCHES native" OFF) # -- Set PLATFORM according to options set(MATCH_PLATFORM_PATTERN "^__PLATFORM_([A-Z]*)__$") @@ -31,64 +27,96 @@ get_cmake_property(CACHE_VARS CACHE_VARIABLES) message(STATUS "ISA: ${ISA}") foreach(VAR IN LISTS CACHE_VARS) - if(VAR MATCHES ${MATCH_PLATFORM_PATTERN}) - # Retrieve the value of the cache variable - get_property(VAR_VALUE CACHE ${VAR} PROPERTY VALUE) - set(PLATFORM_UPPER ${CMAKE_MATCH_1}) - string(TOLOWER ${PLATFORM_UPPER} PLATFORM) - list(APPEND PLATFORMS ${PLATFORM}) - message(STATUS "Variable: ${VAR}=${VAR_VALUE}, Platform: ${PLATFORM}") - endif() + if(VAR MATCHES ${MATCH_PLATFORM_PATTERN}) + # Retrieve the value of the cache variable + get_property( + VAR_VALUE + CACHE ${VAR} + PROPERTY VALUE) + set(PLATFORM_UPPER ${CMAKE_MATCH_1}) + string(TOLOWER ${PLATFORM_UPPER} PLATFORM) + list(APPEND PLATFORMS ${PLATFORM}) + message(STATUS "Variable: ${VAR}=${VAR_VALUE}, Platform: ${PLATFORM}") + endif() endforeach() if((NOT PLATFORM) AND (NOT ISA MATCHES native)) - message(FATAL_ERROR "Platform not given!") + message(FATAL_ERROR "Platform not given!") endif() set(SUPPORTED_ARCH "riscv-nemu" "riscv-npc" "native") foreach(PLATFORM IN LISTS PLATFORMS) - if(${ISA} MATCHES "native") - set(ARCH "native") - else() - set(ARCH ${ISA}-${PLATFORM}) - endif() + if(${ISA} MATCHES "native") + set(ARCH "native") + else() + set(ARCH ${ISA}-${PLATFORM}) + endif() - if(NOT ARCH IN_LIST SUPPORTED_ARCH) - message(FATAL_ERROR "Given ISA-PLATFORM (${ISA}-${PLATFORM}) does not match one of the following: ${SUPPORTED_ARCH}") - endif() + if(NOT ARCH IN_LIST SUPPORTED_ARCH) + message( + FATAL_ERROR + "Given ISA-PLATFORM (${ISA}-${PLATFORM}) does not match one of the following: ${SUPPORTED_ARCH}" + ) + endif() endforeach() # -- Target specific options -cmake_dependent_option( - NATIVE_USE_KLIB "Use Klib even if on native" - ON "NOT __ISA_NATIVE__" OFF) +cmake_dependent_option(NATIVE_USE_KLIB "Use Klib even if on native" ON + "NOT __ISA_NATIVE__" OFF) # -- Add compile definitions based on options list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") -# NOTE: klib and am include header files in each other, -# so we need to create interface libraries for correct dependency +# NOTE: klib and am include header files in each other, so we need to create +# interface libraries for correct dependency add_library(am_interface INTERFACE) -target_include_directories(am_interface INTERFACE - $ - $ - $) +target_include_directories( + am_interface + INTERFACE $ + $ + $) target_compile_definitions(am_interface INTERFACE ARCH_H=) +file(GLOB_RECURSE AM_HEADERS "${CMAKE_SOURCE_DIR}/am/include/*.h") +target_sources( + am_interface + PUBLIC FILE_SET + am_headers + TYPE + HEADERS + BASE_DIRS + ${CMAKE_SOURCE_DIR}/am/include + FILES + ${AM_HEADERS}) add_library(klib_interface INTERFACE) -target_include_directories(klib_interface - INTERFACE - $ - $) +target_include_directories( + klib_interface + INTERFACE $ + $) +file(GLOB_RECURSE KLIB_HEADERS "${CMAKE_SOURCE_DIR}/klib/include/*.h") +target_sources( + klib_interface + PUBLIC FILE_SET + klib_headers + TYPE + HEADERS + BASE_DIRS + ${CMAKE_SOURCE_DIR}/klib/include + FILES + ${KLIB_HEADERS}) -install(TARGETS am_interface klib_interface - EXPORT interfaceTargets - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/abstract-machine) +install( + TARGETS am_interface klib_interface + EXPORT interfaceTargets + FILE_SET klib_headers FILE_SET am_headers + INCLUDES + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) -install(EXPORT interfaceTargets - FILE interfaceTargets.cmake - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake) +install( + EXPORT interfaceTargets + FILE interfaceTargets.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake) add_subdirectory(klib) add_subdirectory(am) diff --git a/abstract-machine/CMakePresets.json b/abstract-machine/CMakePresets.json index 73dab56..fb5eb21 100644 --- a/abstract-machine/CMakePresets.json +++ b/abstract-machine/CMakePresets.json @@ -7,7 +7,7 @@ "generator": "Unix Makefiles", "binaryDir": "${sourceDir}/out/build/${presetName}", "cacheVariables": { - "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_BUILD_TYPE": "RelWithDebInfo", "ISA": "native", "__PLATFORM_NATIVE__": true, "NATIVE_USE_KLIB": true @@ -19,11 +19,11 @@ "generator": "Unix Makefiles", "binaryDir": "${sourceDir}/out/build/${presetName}", "cacheVariables": { - "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_BUILD_TYPE": "RelWithDebInfo", "ISA": "riscv", "__PLATFORM_NPC__": true, "__PLATFORM_NEMU__": true } } ] -} \ No newline at end of file +} diff --git a/abstract-machine/am/CMakeLists.txt b/abstract-machine/am/CMakeLists.txt index 25895b7..f57d540 100644 --- a/abstract-machine/am/CMakeLists.txt +++ b/abstract-machine/am/CMakeLists.txt @@ -1,26 +1,26 @@ add_subdirectory(src) -install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/abstract-machine) - foreach(PLATFORM IN LISTS PLATFORMS) - if(ISA MATCHES "native") - set(ARCH "native") - else() - set(ARCH ${ISA}-${PLATFORM}) - endif() - install(TARGETS am-${ARCH} - EXPORT amTargets-${ARCH} - LIBRARY DESTINATION lib) + if(ISA MATCHES "native") + set(ARCH "native") + else() + set(ARCH ${ISA}-${PLATFORM}) + endif() + install( + TARGETS am-${ARCH} + EXPORT amTargets-${ARCH} + LIBRARY DESTINATION lib) - install(EXPORT amTargets-${ARCH} - FILE amTargets.cmake - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/am-${ARCH}) + install( + EXPORT amTargets-${ARCH} + FILE amTargets.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/am-${ARCH}) - configure_package_config_file( - ${CMAKE_SOURCE_DIR}/cmake/am-config.cmake.in - ${CMAKE_CURRENT_BINARY_DIR}/am-${ARCH}-config.cmake - INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/am-${ARCH}) + configure_package_config_file( + ${CMAKE_SOURCE_DIR}/cmake/am-config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/am-${ARCH}-config.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/am-${ARCH}) - install(FILES ${CMAKE_CURRENT_BINARY_DIR}/am-${ARCH}-config.cmake - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/am-${ARCH}) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/am-${ARCH}-config.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/am-${ARCH}) endforeach() diff --git a/abstract-machine/am/src/native/CMakeLists.txt b/abstract-machine/am/src/native/CMakeLists.txt index 706aa80..d991825 100644 --- a/abstract-machine/am/src/native/CMakeLists.txt +++ b/abstract-machine/am/src/native/CMakeLists.txt @@ -10,14 +10,16 @@ set(SOURCES ioe/disk.c ioe/gpu.c ioe/input.c - ioe/timer.c -) + ioe/timer.c) add_library(am-native ${SOURCES}) # FIXME: get free(): invalid address when user program compiled without pie -set_target_properties(am-native PROPERTIES - POSITION_INDEPENDENT_CODE TRUE - INTERFACE_POSITION_INDEPENDENT_CODE TRUE) +set_target_properties( + am-native PROPERTIES POSITION_INDEPENDENT_CODE TRUE + INTERFACE_POSITION_INDEPENDENT_CODE TRUE) find_package(SDL2 REQUIRED) -target_link_libraries(am-native PUBLIC SDL2::SDL2 PRIVATE klib_interface am_interface) +target_link_libraries( + am-native + PUBLIC SDL2::SDL2 + PRIVATE klib_interface am_interface) diff --git a/abstract-machine/am/src/platform/nemu/ioe/timer.c b/abstract-machine/am/src/platform/nemu/ioe/timer.c index f173ed4..ce545de 100644 --- a/abstract-machine/am/src/platform/nemu/ioe/timer.c +++ b/abstract-machine/am/src/platform/nemu/ioe/timer.c @@ -1,18 +1,17 @@ #include #include -void __am_timer_init() { -} +void __am_timer_init() {} void __am_timer_uptime(AM_TIMER_UPTIME_T *uptime) { - uptime->us = 0; + uptime->us = ((uint64_t)inl(RTC_ADDR + 4) << 32) + inl(RTC_ADDR); } void __am_timer_rtc(AM_TIMER_RTC_T *rtc) { rtc->second = 0; rtc->minute = 0; - rtc->hour = 0; - rtc->day = 0; - rtc->month = 0; - rtc->year = 1900; + rtc->hour = 0; + rtc->day = 0; + rtc->month = 0; + rtc->year = 1900; } diff --git a/abstract-machine/am/src/platform/nemu/trm.c b/abstract-machine/am/src/platform/nemu/trm.c index f1802aa..c4287c5 100644 --- a/abstract-machine/am/src/platform/nemu/trm.c +++ b/abstract-machine/am/src/platform/nemu/trm.c @@ -6,19 +6,18 @@ int main(const char *args); Area heap = RANGE(&_heap_start, PMEM_END); #ifndef MAINARGS -#define MAINARGS "" +#define MAINARGS "5" #endif static const char mainargs[] = MAINARGS; -void putch(char ch) { - outb(SERIAL_PORT, ch); -} +void putch(char ch) { outb(SERIAL_PORT, ch); } void halt(int code) { nemu_trap(code); // should not reach here - while (1); + while (1) + ; } void _trm_init() { diff --git a/abstract-machine/am/src/riscv/CMakeLists.txt b/abstract-machine/am/src/riscv/CMakeLists.txt index ced867e..0c876d0 100644 --- a/abstract-machine/am/src/riscv/CMakeLists.txt +++ b/abstract-machine/am/src/riscv/CMakeLists.txt @@ -1,12 +1,9 @@ foreach(PLATFORM IN LISTS PLATFORMS) - string(TOUPPER ${ARCH} ARCH_UPPER) - set(AM_COMMON_COMPILE_DEF - # -- Arch related - $ - __ISA_${ISA_UPPER}__ - __PLATFORM_${PLATFORM_UPPER}__ - - $<$:__NATIVE_USE_KLIB__> - ) - add_subdirectory(${PLATFORM}) + string(TOUPPER ${ARCH} ARCH_UPPER) + set(AM_COMMON_COMPILE_DEF + # -- Arch related + $ __ISA_${ISA_UPPER}__ + __PLATFORM_${PLATFORM_UPPER}__ + $<$:__NATIVE_USE_KLIB__>) + add_subdirectory(${PLATFORM}) endforeach() diff --git a/abstract-machine/am/src/riscv/nemu/CMakeLists.txt b/abstract-machine/am/src/riscv/nemu/CMakeLists.txt index 79db40f..0ec140f 100644 --- a/abstract-machine/am/src/riscv/nemu/CMakeLists.txt +++ b/abstract-machine/am/src/riscv/nemu/CMakeLists.txt @@ -1,52 +1,41 @@ include(nemu-settings) include(riscv-settings) -add_library(am-riscv-nemu - cte.c - start.S - trap.S - vme.c - ${NEMU_SOURCES} -) +add_library(am-riscv-nemu cte.c start.S trap.S vme.c ${NEMU_SOURCES}) -target_compile_options(am-riscv-nemu PRIVATE - ${NEMU_COMPILE_OPTIONS} - ${RISCV_COMPILE_OPTIONS}) +target_compile_options(am-riscv-nemu PRIVATE ${NEMU_COMPILE_OPTIONS} + ${RISCV_COMPILE_OPTIONS}) -target_link_options(am-riscv-nemu PRIVATE - ${NEMU_LINK_OPITIONS} - ${RISCV_LINK_OPTIONS}) +target_link_options(am-riscv-nemu PRIVATE ${NEMU_LINK_OPITIONS} + ${RISCV_LINK_OPTIONS}) -target_include_directories(am-riscv-nemu PRIVATE - ${NEMU_INCLUDE_DIRECTORIES}) +target_include_directories(am-riscv-nemu PRIVATE ${NEMU_INCLUDE_DIRECTORIES}) -target_link_options(am-riscv-nemu INTERFACE - LINKER:--defsym=_pmem_start=0x80000000 - LINKER:--defsym=_entry_offset=0x0 - LINKER:--gc-sections - LINKER:-e _start - -nostartfiles) +target_link_options( + am-riscv-nemu + INTERFACE + LINKER:--defsym=_pmem_start=0x80000000 + LINKER:--defsym=_entry_offset=0x0 + LINKER:--gc-sections + LINKER:-e + _start + -nostartfiles) -target_link_options(am-riscv-nemu INTERFACE - $ - $) +target_link_options( + am-riscv-nemu INTERFACE + $ + $) -target_include_directories(am-riscv-nemu - PUBLIC - $ - $) +target_link_libraries( + am-riscv-nemu + PUBLIC am_interface klib_interface + INTERFACE m) -target_link_libraries(am-riscv-nemu - PUBLIC am_interface klib_interface - INTERFACE m) +target_compile_definitions(am-riscv-nemu PRIVATE ISA_H=) -target_compile_definitions(am-riscv-nemu PRIVATE - ISA_H=) - -set_target_properties(am-riscv-nemu PROPERTIES - POSITION_INDEPENDENT_CODE OFF - INTERFACE_POSITION_INDEPENDENT_CODE OFF) +set_target_properties( + am-riscv-nemu PROPERTIES POSITION_INDEPENDENT_CODE OFF + INTERFACE_POSITION_INDEPENDENT_CODE OFF) install(FILES ${CMAKE_SOURCE_DIR}/scripts/linker.ld DESTINATION ${CMAKE_INSTALL_DATADIR}) - diff --git a/abstract-machine/am/src/riscv/npc/CMakeLists.txt b/abstract-machine/am/src/riscv/npc/CMakeLists.txt index 0859c11..fbcdd90 100644 --- a/abstract-machine/am/src/riscv/npc/CMakeLists.txt +++ b/abstract-machine/am/src/riscv/npc/CMakeLists.txt @@ -2,38 +2,45 @@ include(riscv-settings) add_subdirectory(libgcc) -add_library(am-riscv-npc - cte.c - input.c - ioe.c - mpe.c - start.S - timer.c - trap.S - trm.c - vme.c -) +add_library( + am-riscv-npc + cte.c + input.c + ioe.c + mpe.c + start.S + timer.c + trap.S + trm.c + vme.c) -target_link_libraries(am-riscv-npc PRIVATE npcgcc PUBLIC am_interface klib_interface) +target_link_libraries( + am-riscv-npc + PRIVATE npcgcc + PUBLIC am_interface klib_interface) -target_link_options(am-riscv-npc INTERFACE - $ - $) +target_link_options( + am-riscv-npc INTERFACE + $ + $) -target_link_options(am-riscv-npc INTERFACE - LINKER:--defsym=_pmem_start=0x80000000 - LINKER:--defsym=_entry_offset=0x0 - LINKER:--gc-sections - LINKER:-e _start - -nostartfiles) +target_link_options( + am-riscv-npc + INTERFACE + LINKER:--defsym=_pmem_start=0x80000000 + LINKER:--defsym=_entry_offset=0x0 + LINKER:--gc-sections + LINKER:-e + _start + -nostartfiles) -target_link_options(am-riscv-npc INTERFACE - $ - $) +target_link_options( + am-riscv-npc INTERFACE + $ + $) -target_compile_definitions(am-riscv-npc - PUBLIC ${AM_COMMON_COMPILE_DEF} ARCH_H= -) +target_compile_definitions(am-riscv-npc PUBLIC ${AM_COMMON_COMPILE_DEF} + ARCH_H=) install(FILES ${CMAKE_SOURCE_DIR}/scripts/linker.ld DESTINATION ${CMAKE_INSTALL_DATADIR}) diff --git a/abstract-machine/am/src/riscv/npc/ioe.c b/abstract-machine/am/src/riscv/npc/ioe.c index 26bad0a..f794ce2 100644 --- a/abstract-machine/am/src/riscv/npc/ioe.c +++ b/abstract-machine/am/src/riscv/npc/ioe.c @@ -7,26 +7,32 @@ void __am_timer_rtc(AM_TIMER_RTC_T *); void __am_timer_uptime(AM_TIMER_UPTIME_T *); void __am_input_keybrd(AM_INPUT_KEYBRD_T *); -static void __am_timer_config(AM_TIMER_CONFIG_T *cfg) { cfg->present = true; cfg->has_rtc = true; } -static void __am_input_config(AM_INPUT_CONFIG_T *cfg) { cfg->present = true; } +static void __am_timer_config(AM_TIMER_CONFIG_T *cfg) { + cfg->present = true; + cfg->has_rtc = true; +} +static void __am_input_config(AM_INPUT_CONFIG_T *cfg) { cfg->present = true; } +static void __am_uart_config(AM_UART_CONFIG_T *cfg) { cfg->present = false; } typedef void (*handler_t)(void *buf); static void *lut[128] = { - [AM_TIMER_CONFIG] = __am_timer_config, - [AM_TIMER_RTC ] = __am_timer_rtc, - [AM_TIMER_UPTIME] = __am_timer_uptime, - [AM_INPUT_CONFIG] = __am_input_config, - [AM_INPUT_KEYBRD] = __am_input_keybrd, + [AM_UART_CONFIG] = __am_uart_config, + [AM_TIMER_CONFIG] = __am_timer_config, + [AM_TIMER_RTC] = __am_timer_rtc, + [AM_TIMER_UPTIME] = __am_timer_uptime, + [AM_INPUT_CONFIG] = __am_input_config, + [AM_INPUT_KEYBRD] = __am_input_keybrd, }; static void fail(void *buf) { panic("access nonexist register"); } bool ioe_init() { for (int i = 0; i < LENGTH(lut); i++) - if (!lut[i]) lut[i] = fail; + if (!lut[i]) + lut[i] = fail; __am_timer_init(); return true; } -void ioe_read (int reg, void *buf) { ((handler_t)lut[reg])(buf); } +void ioe_read(int reg, void *buf) { ((handler_t)lut[reg])(buf); } void ioe_write(int reg, void *buf) { ((handler_t)lut[reg])(buf); } diff --git a/abstract-machine/am/src/riscv/npc/libgcc/CMakeLists.txt b/abstract-machine/am/src/riscv/npc/libgcc/CMakeLists.txt index 841adb6..feaec7e 100644 --- a/abstract-machine/am/src/riscv/npc/libgcc/CMakeLists.txt +++ b/abstract-machine/am/src/riscv/npc/libgcc/CMakeLists.txt @@ -1,14 +1,9 @@ -add_library(npcgcc - ashldi3.c - div.S - muldi3.S - multi3.c - unused.c -) +add_library(npcgcc ashldi3.c div.S muldi3.S multi3.c unused.c) target_link_libraries(npcgcc PRIVATE klib_interface am_interface) target_link_options(npcgcc INTERFACE -nolibc -nostdlib) -install(TARGETS npcgcc - EXPORT amTargets-riscv-npc - LIBRARY DESTINATION lib) +install( + TARGETS npcgcc + EXPORT amTargets-riscv-npc + LIBRARY DESTINATION lib) diff --git a/abstract-machine/am/src/riscv/npc/timer.c b/abstract-machine/am/src/riscv/npc/timer.c index 6ea0ffa..844463d 100644 --- a/abstract-machine/am/src/riscv/npc/timer.c +++ b/abstract-machine/am/src/riscv/npc/timer.c @@ -1,17 +1,18 @@ +#include "npc.h" #include +#include -void __am_timer_init() { -} +void __am_timer_init() {} void __am_timer_uptime(AM_TIMER_UPTIME_T *uptime) { - uptime->us = 0; + uptime->us = ((uint64_t)inl(RTC_ADDR + 4) << 32) + inl(RTC_ADDR); } void __am_timer_rtc(AM_TIMER_RTC_T *rtc) { rtc->second = 0; rtc->minute = 0; - rtc->hour = 0; - rtc->day = 0; - rtc->month = 0; - rtc->year = 1900; + rtc->hour = 0; + rtc->day = 0; + rtc->month = 0; + rtc->year = 1900; } diff --git a/abstract-machine/am/src/riscv/npc/trm.c b/abstract-machine/am/src/riscv/npc/trm.c index a5f4fac..ec9c965 100644 --- a/abstract-machine/am/src/riscv/npc/trm.c +++ b/abstract-machine/am/src/riscv/npc/trm.c @@ -1,5 +1,7 @@ +#include "npc.h" #include #include +#include extern char _heap_start; int main(const char *args); @@ -10,11 +12,11 @@ extern char _pmem_start; Area heap = RANGE(&_heap_start, PMEM_END); #ifndef MAINARGS -#define MAINARGS "" +#define MAINARGS "3" #endif static const char mainargs[] = MAINARGS; -void putch(char ch) {} +void putch(char ch) { outb(SERIAL_PORT, ch); } void halt(int code) { asm volatile("mv a0, %0; ebreak" : : "r"(code)); diff --git a/abstract-machine/default.nix b/abstract-machine/default.nix index 2b614ac..ef21cf2 100644 --- a/abstract-machine/default.nix +++ b/abstract-machine/default.nix @@ -7,7 +7,7 @@ }: stdenv.mkDerivation { pname = "abstract-machine"; - version = "2024.02.18"; + version = "2024.06.01"; src = ./.; diff --git a/abstract-machine/klib/CMakeLists.txt b/abstract-machine/klib/CMakeLists.txt index febd4f0..4a7a3aa 100644 --- a/abstract-machine/klib/CMakeLists.txt +++ b/abstract-machine/klib/CMakeLists.txt @@ -1 +1,4 @@ +install(DIRECTORY include/ + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/abstract-machine) + add_subdirectory(src) diff --git a/abstract-machine/klib/src/CMakeLists.txt b/abstract-machine/klib/src/CMakeLists.txt index b3a59fd..5d173b7 100644 --- a/abstract-machine/klib/src/CMakeLists.txt +++ b/abstract-machine/klib/src/CMakeLists.txt @@ -1,32 +1,30 @@ -# find_package(FLEX) -# find_package(BISON) +# find_package(FLEX) find_package(BISON) # FLEX_TARGET(fmt_scanner fmt_scanner.l fmt_scanner.c) -set(SOURCES - cpp.c - int64.c - stdio.c - stdlib.c - string.c - # ${FLEX_fmt_scanner_OUTPUTS} +set(SOURCES cpp.c int64.c stdio.c stdlib.c string.c + # ${FLEX_fmt_scanner_OUTPUTS} ) add_library(klib ${SOURCES}) -target_link_libraries(klib PRIVATE am_interface klib_interface) +target_link_libraries(klib PUBLIC am_interface klib_interface) +target_compile_options(klib PUBLIC -fno-builtin) +target_link_options(klib PUBLIC -nostartfiles -nolibc) -install(TARGETS klib - EXPORT klibTargets - LIBRARY DESTINATION lib) +install( + TARGETS klib + EXPORT klibTargets + LIBRARY DESTINATION lib) -install(EXPORT klibTargets - FILE klibTargets.cmake - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/klib) +install( + EXPORT klibTargets + FILE klibTargets.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/klib) -configure_package_config_file(${CMAKE_SOURCE_DIR}/cmake/klib-config.cmake.in +configure_package_config_file( + ${CMAKE_SOURCE_DIR}/cmake/klib-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/klib-config.cmake INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/klib) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/klib-config.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/klib) - diff --git a/abstract-machine/klib/src/stdio.c b/abstract-machine/klib/src/stdio.c index fec63bc..35f9f59 100644 --- a/abstract-machine/klib/src/stdio.c +++ b/abstract-machine/klib/src/stdio.c @@ -1,16 +1,91 @@ #include -#include #include +#include #include #if !defined(__ISA_NATIVE__) || defined(__NATIVE_USE_KLIB__) -int vprintf(const char *fmt, va_list ap) { - const char *p = fmt; - while(*p != '\0') { - putch(*p); +void print_int(int num, int width, char pad) { + int reverse = 0; + int count = 0; + + if (num == 0) { + reverse = 0; + count = 1; + } else { + if (num < 0) { + putch('-'); + num = -num; + } + while (num != 0) { + reverse = reverse * 10 + (num % 10); + num /= 10; + count++; + } + } + + while (width > count) { + putch(pad); + width--; + } + + if (reverse == 0) { + putch('0'); + } else { + while (reverse != 0) { + putch('0' + (reverse % 10)); + reverse /= 10; + } + } +} + +int vprintf(const char *format, va_list args) { + const char *p = format; + + while (*p) { + if (*p == '%') { + p++; // Skip the '%' + char pad = ' '; + int width = 0; + + if (*p == '0') { + pad = '0'; + p++; + } + + while (*p >= '0' && *p <= '9') { + width = width * 10 + (*p - '0'); + p++; + } + + switch (*p) { + case 'd': { // Integer + int ival = va_arg(args, int); + print_int(ival, width, pad); + break; + } + case 'c': { // Character + char c = (char)va_arg(args, int); + putch(c); + break; + } + case 's': { // String + char *s = va_arg(args, char *); + putstr(s); + break; + } + case '%': { + putch('%'); + break; + } + default: + panic("Wrong formatter provided to printf"); + } + } else { + putch(*p); + } + p++; } - return 0; } int printf(const char *fmt, ...) { @@ -21,12 +96,103 @@ int printf(const char *fmt, ...) { return 0; } -int vsprintf(char *out, const char *fmt, va_list ap) { - panic("Not implemented"); +void append_to_buffer(char **buf, int *pos, char c) { (*buf)[(*pos)++] = c; } + +void print_int_to_buf(char **buf, int *pos, int num, int width, char pad) { + int reverse = 0, count = 0, neg = 0; + + if (num == 0) { + reverse = 0; + count = 1; + } else { + if (num < 0) { + append_to_buffer(buf, pos, '-'); + num = -num; + neg = 1; + } + while (num != 0) { + reverse = reverse * 10 + (num % 10); + num /= 10; + count++; + } + } + + width -= neg; + while (width > count) { + append_to_buffer(buf, pos, pad); + width--; + } + + if (reverse == 0) { + append_to_buffer(buf, pos, '0'); + } else { + while (reverse != 0) { + append_to_buffer(buf, pos, '0' + (reverse % 10)); + reverse /= 10; + } + } +} + +int vsprintf(char *buf, const char *format, va_list args) { + const char *p = format; + int pos = 0; // Position in buf + + while (*p) { + if (*p == '%') { + p++; // Skip the '%' + char pad = ' '; + int width = 0; + + if (*p == '0') { + pad = '0'; + p++; + } + + while (*p >= '0' && *p <= '9') { + width = width * 10 + (*p - '0'); + p++; + } + + switch (*p) { + case 'd': { // Integer + int ival = va_arg(args, int); + print_int_to_buf(&buf, &pos, ival, width, pad); + break; + } + case 'c': { // Character + char c = (char)va_arg(args, int); + append_to_buffer(&buf, &pos, c); + break; + } + case 's': { // String + char *s = va_arg(args, char *); + while (*s) { + append_to_buffer(&buf, &pos, *s++); + } + break; + } + case '%': { + append_to_buffer(&buf, &pos, '%'); + break; + } + default: + panic("Unsupported format specifier"); + } + } else { + append_to_buffer(&buf, &pos, *p); + } + p++; + } + buf[pos] = '\0'; // Null-terminate the string + return pos; } int sprintf(char *out, const char *fmt, ...) { - panic("Not implemented"); + va_list args; + va_start(args, fmt); + vsprintf(out, fmt, args); + va_end(args); + return 0; } int snprintf(char *out, size_t n, const char *fmt, ...) { diff --git a/abstract-machine/klib/src/stdlib.c b/abstract-machine/klib/src/stdlib.c index 382635d..90432b6 100644 --- a/abstract-machine/klib/src/stdlib.c +++ b/abstract-machine/klib/src/stdlib.c @@ -1,6 +1,6 @@ #include -#include #include +#include #if !defined(__ISA_NATIVE__) || defined(__NATIVE_USE_KLIB__) static unsigned long int next = 1; @@ -8,23 +8,21 @@ static unsigned long int next = 1; int rand(void) { // RAND_MAX assumed to be 32767 next = next * 1103515245 + 12345; - return (unsigned int)(next/65536) % 32768; + return (unsigned int)(next / 65536) % 32768; } -void srand(unsigned int seed) { - next = seed; -} +void srand(unsigned int seed) { next = seed; } -int abs(int x) { - return (x < 0 ? -x : x); -} +int abs(int x) { return (x < 0 ? -x : x); } -int atoi(const char* nptr) { +int atoi(const char *nptr) { int x = 0; - while (*nptr == ' ') { nptr ++; } + while (*nptr == ' ') { + nptr++; + } while (*nptr >= '0' && *nptr <= '9') { x = x * 10 + *nptr - '0'; - nptr ++; + nptr++; } return x; } @@ -33,13 +31,19 @@ void *malloc(size_t size) { // On native, malloc() will be called during initializaion of C runtime. // Therefore do not call panic() here, else it will yield a dead recursion: // panic() -> putchar() -> (glibc) -> malloc() -> panic() -#if !(defined(__ISA_NATIVE__) && defined(__NATIVE_USE_KLIB__)) - panic("Not implemented"); -#endif - return NULL; + static void *addr = NULL; + void *ret = NULL; + if (addr == 0) { + addr = heap.start; + ret = addr; + } else { + panic_on(addr + size > heap.end, "Memory space not enough"); + ret = addr; + addr += size; + } + return ret; } -void free(void *ptr) { -} +void free(void *ptr) {} #endif diff --git a/abstract-machine/klib/tests/CMakeLists.txt b/abstract-machine/klib/tests/CMakeLists.txt index 6cb0e40..6d1c225 100644 --- a/abstract-machine/klib/tests/CMakeLists.txt +++ b/abstract-machine/klib/tests/CMakeLists.txt @@ -1,14 +1,11 @@ -set(TEST_SOURCES - stdio - string -) +set(TEST_SOURCES stdio string) foreach(TEST IN LISTS TEST_SOURCES) - # TODO: Run tests in other configurations - if(__PLATFORM_NATIVE__) - add_executable(${TEST} ${TEST}.c) - target_link_libraries(${TEST} PRIVATE am_interface klib_interface klib m) - target_link_libraries(${TEST} PRIVATE am-native) - add_test(NAME ${TEST} COMMAND ${TEST}) - endif() + # TODO: Run tests in other configurations + if(__PLATFORM_NATIVE__) + add_executable(${TEST} ${TEST}.c) + target_link_libraries(${TEST} PRIVATE am_interface klib_interface klib m) + target_link_libraries(${TEST} PRIVATE am-native) + add_test(NAME ${TEST} COMMAND ${TEST}) + endif() endforeach() From 29a9850210d5818fc356d0cd02e6cc45c87639d6 Mon Sep 17 00:00:00 2001 From: xinyangli Date: Tue, 9 Jul 2024 18:44:26 +0800 Subject: [PATCH 5/5] chore: update gitignore and submodules --- .gitignore | 2 ++ .gitmodules | 1 + 2 files changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 7a06b53..76cec07 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ !*/ +difftest/ !/nemu/* !/nexus-am/* !/nanos-lite/* @@ -13,3 +14,4 @@ **/.cache **/result /.pre-commit-config.yaml +**/.vscode/ diff --git a/.gitmodules b/.gitmodules index 3d834b3..7553118 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,4 @@ [submodule "am-kernels"] path = am-kernels url = https://git.xinyang.life/xin/am-kernels.git + branch = dev