Merge branch 'master' into npc
Some checks failed
Build abstract machine with nix / build-abstract-machine (push) Has been cancelled

This commit is contained in:
xin 2024-03-26 03:12:30 +00:00
commit 642444b384
44 changed files with 924 additions and 110 deletions

View file

@ -0,0 +1,21 @@
name: Build abstract machine with nix
on: [push]
jobs:
build-abstract-machine:
runs-on: nix
steps:
- uses: https://github.com/cachix/cachix-action@v14
with:
name: ysyx
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
- uses: actions/checkout@v4
with:
submodules: true
- name: Build abstract-machine
run: |
nix build .#abstract-machine
- name: Build nemu
run: |
nix build .#nemu

3
.gitignore vendored
View file

@ -10,4 +10,5 @@
!init.sh !init.sh
/fceux-am /fceux-am
/nvboard /nvboard
/am-kernels **/.cache
**/result

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "am-kernels"]
path = am-kernels
url = https://git.xinyang.life/xin/am-kernels.git

View file

@ -1,19 +1,6 @@
* **/.direnv/
!*/ **/build/
!*.h **/.envrc
!*.c **/.cache
!*.cc .vscode
!*.S compile_commands.json
!*.ld
!*.sh
!*.py
!*.mk
!Makefile
!README
!LICENSE
.*
_*
*~
build/
!.gitignore
.vscode

View file

@ -0,0 +1,87 @@
cmake_minimum_required(VERSION 3.22)
project(abstract-machine)
enable_language(CXX C ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 11)
include(CMakeDependentOption)
include(CMakePackageConfigHelpers) # Used to find libcheck
include(CTest)
# -- General options
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_NATIVE__ "Run on native"
ON "ISA MATCHES native" OFF)
# -- Set PLATFORM according to options
set(MATCH_PLATFORM_PATTERN "^__PLATFORM_([A-Z]*)__")
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)
message(STATUS "Variable: ${VAR}=${VAR_VALUE}, Platform: ${PLATFORM}")
endif()
endforeach()
if(${PLATFORM} MATCHES "native")
set(ARCH "native")
else()
set(ARCH ${ISA}-${PLATFORM})
endif()
string(TOUPPER ${ARCH} ARCH_UPPER)
# -- Target specific options
cmake_dependent_option(
NATIVE_USE_KLIB "Use Klib even if on native"
ON "NOT __ISA_NATIVE__" OFF)
# -- Add compile definitions based on options
add_compile_definitions(
$<MAKE_C_IDENTIFIER:__ARCH_${ARCH_UPPER}__>
__ISA_${ISA_UPPER}__
__PLATFORM_${PLATFORM_UPPER}__
)
add_compile_definitions(
$<$<BOOL:${NATIVE_USE_KLIB}>:__NATIVE_USE_KLIB__>
)
# -- Required compiler flags
add_compile_options(
# -Werror
-Wno-main
-fno-asynchronous-unwind-tables
-fno-builtin
-fno-stack-protector
-U_FORTIFY_SOURCE
$<$<COMPILE_LANGUAGE:CXX>:-fno-exceptions>
$<$<COMPILE_LANGUAGE:CXX>:-ffreestanding>
$<$<COMPILE_LANGUAGE:CXX>:-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)
add_subdirectory(klib)
add_subdirectory(am)

View file

@ -0,0 +1,29 @@
{
"version": 6,
"configurePresets": [
{
"name": "native",
"displayName": "Native",
"generator": "Unix Makefiles",
"binaryDir": "${sourceDir}/out/build/${presetName}",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"ISA": "native",
"__PLATFORM_NATIVE__": true,
"NATIVE_USE_KLIB": true
}
},
{
"name": "riscv-nemu",
"displayName": "Riscv32 NEMU",
"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_NEMU__": true
}
}
]
}

View file

@ -0,0 +1,10 @@
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
add_library(am_interface INTERFACE)
target_include_directories(am_interface INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include/abstract-machine>)
add_subdirectory(src)
install(DIRECTORY include/ DESTINATION include/abstract-machine)

View file

@ -0,0 +1,53 @@
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
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include>
$<INSTALL_INTERFACE:include/abstract-machine>)
target_link_libraries(am-${ARCH}
PUBLIC klib_interface
INTERFACE m)
# TODO: Check
target_link_options(am-${ARCH} INTERFACE
$<BUILD_INTERFACE:-T${CMAKE_SOURCE_DIR}/scripts/${LINKER_SCRIPT}>
$<INSTALL_INTERFACE:-T${CMAKE_INSTALL_LIBDIR}/cmake/am-${ARCH}/${LINKER_SCRIPT}>)
# 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
$<$<COMPILE_LANGUAGE:CXX>:-fno-exceptions>
$<$<COMPILE_LANGUAGE:CXX>:-ffreestanding>
$<$<COMPILE_LANGUAGE:CXX>:-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})

View file

@ -0,0 +1,26 @@
include(CheckPIESupported)
check_pie_supported()
set(SOURCES
trap.S
cte.c
ioe.c
mpe.c
platform.c
trm.c
vme.c
ioe/audio.c
ioe/disk.c
ioe/gpu.c
ioe/input.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)
find_package(SDL2 REQUIRED)
target_link_libraries(am-${ARCH} PUBLIC SDL2::SDL2)

View file

@ -0,0 +1,34 @@
include(nemu-settings)
include(riscv-settings)
add_library(am-${ISA}-nemu
cte.c
start.S
trap.S
vme.c
${NEMU_SOURCES}
)
target_compile_options(am-${ISA}-nemu PRIVATE
${NEMU_COMPILE_OPTIONS}
${RISCV_COMPILE_OPTIONS})
target_link_options(am-${ISA}-nemu PRIVATE
${NEMU_LINK_OPITIONS}
${RISCV_LINK_OPTIONS})
target_include_directories(am-${ISA}-nemu PRIVATE
${NEMU_INCLUDE_DIRECTORIES})
target_link_options(am-${ISA}-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")
set_target_properties(am-${ISA}-nemu PROPERTIES
POSITION_INDEPENDENT_CODE OFF
INTERFACE_POSITION_INDEPENDENT_CODE OFF)

View file

@ -0,0 +1,9 @@
@PACKAGE_INIT@
include(CMakeFindDependencyMacro)
if(${ARCH} MATCHES "native")
find_dependency(SDL2 REQUIRED)
endif()
# Include the targets file
include("${CMAKE_CURRENT_LIST_DIR}/amTargets.cmake")

View file

@ -0,0 +1,6 @@
@PACKAGE_INIT@
include(CMakeFindDependencyMacro)
# Include the targets file
include("${CMAKE_CURRENT_LIST_DIR}/klibTargets.cmake")

View file

@ -0,0 +1,11 @@
set(NEMU_COMPILE_OPTIONS -fdata-sections -ffunction-sections)
set(NEMU_LINK_OPTIONS
--defsym=_pmem_start=0x80000000
--defsym=_entry_offset=0x0
--gc-sections
-e _start)
set(NEMU_INCLUDE_DIRECTORIES
${CMAKE_SOURCE_DIR}/am/src/platform/nemu/include)
file(GLOB_RECURSE NEMU_SOURCES
${CMAKE_SOURCE_DIR}/am/src/platform/nemu/*.[cS])
set(INCLUDE_LINKER_SCRIPT ON)

View file

@ -0,0 +1,2 @@
set(RISCV_COMPILE_OPTIONS)
set(RISCV_LINK_OPTIONS)

View file

@ -0,0 +1,26 @@
{ stdenv,
lib,
cmake,
SDL2,
isa ? "native",
platform ? "NEMU"
}:
stdenv.mkDerivation {
pname = "abstract-machine";
version = "2024.02.18";
src = ./.;
cmakeFlags = [
(lib.cmakeFeature "ISA" isa)
(lib.cmakeBool "__PLATFORM_${lib.strings.toUpper platform}__" true)
];
nativeBuildInputs = [
cmake
];
buildInputs = [
] ++ (if platform=="native" then [ SDL2 ] else [ ]);
}

View file

@ -0,0 +1,12 @@
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
add_library(klib_interface INTERFACE)
target_include_directories(klib_interface
INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include/abstract-machine>)
add_subdirectory(src)
# add_subdirectory(tests)
install(DIRECTORY include/ DESTINATION include/abstract-machine)

View file

@ -35,6 +35,7 @@ int atoi (const char *nptr);
int printf (const char *format, ...); int printf (const char *format, ...);
int sprintf (char *str, const char *format, ...); int sprintf (char *str, const char *format, ...);
int snprintf (char *str, size_t size, const char *format, ...); int snprintf (char *str, size_t size, const char *format, ...);
int vprintf (const char *format, va_list ap);
int vsprintf (char *str, const char *format, va_list ap); int vsprintf (char *str, const char *format, va_list ap);
int vsnprintf (char *str, size_t size, const char *format, va_list ap); int vsnprintf (char *str, size_t size, const char *format, va_list ap);

View file

@ -0,0 +1,33 @@
# 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}
)
add_library(klib ${SOURCES})
target_include_directories(klib PUBLIC $<TARGET_PROPERTY:am_interface,INTERFACE_INCLUDE_DIRECTORIES>)
target_compile_definitions(klib PUBLIC $<TARGET_PROPERTY:am-${ARCH},INTERFACE_COMPILE_DEFINITIONS>)
install(TARGETS klib
EXPORT klibTargets
LIBRARY DESTINATION lib)
install(EXPORT klibTargets
FILE klibTargets.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/klib)
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)

View file

@ -5,8 +5,20 @@
#if !defined(__ISA_NATIVE__) || defined(__NATIVE_USE_KLIB__) #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);
}
return 0;
}
int printf(const char *fmt, ...) { int printf(const char *fmt, ...) {
panic("Not implemented"); va_list args;
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
return 0;
} }
int vsprintf(char *out, const char *fmt, va_list ap) { int vsprintf(char *out, const char *fmt, va_list ap) {

View file

@ -5,43 +5,115 @@
#if !defined(__ISA_NATIVE__) || defined(__NATIVE_USE_KLIB__) #if !defined(__ISA_NATIVE__) || defined(__NATIVE_USE_KLIB__)
size_t strlen(const char *s) { size_t strlen(const char *s) {
panic("Not implemented"); const char *p = s;
size_t len = 0;
while(*(p++) != '\0') len++;
return len;
} }
char *strcpy(char *dst, const char *src) { char *strcpy(char *dst, const char *src) {
panic("Not implemented"); char *p_dst = dst;
const char *p_src = src;
for(; *p_src != '\0'; p_src++, p_dst++) {
*p_dst = *p_src;
}
*p_dst = '\0';
return dst;
} }
char *strncpy(char *dst, const char *src, size_t n) { char *strncpy(char *dst, const char *src, size_t n) {
panic("Not implemented"); int i = 0;
for(; i < n && src[i] != '\0'; i++) {
dst[i] = src[i];
}
for(; i < n; i++) {
dst[i] = '\0';
}
return dst;
} }
char *strcat(char *dst, const char *src) { char *strcat(char *dst, const char *src) {
panic("Not implemented"); char *p_dst = dst;
const char *p_src = src;
while(*p_dst != '\0') p_dst++;
for(; *p_src != '\0'; p_src++, p_dst++) {
*p_dst = *p_src;
}
*p_dst = '\0';
return dst;
} }
int strcmp(const char *s1, const char *s2) { int strcmp(const char *s1, const char *s2) {
panic("Not implemented"); const char *p_s1 = s1, *p_s2 = s2;
for(; *p_s1 == *p_s2; p_s1++, p_s2++) {
if(*p_s1 == '\0' || *p_s2 == '\0') {
break;
}
}
return *p_s1 - *p_s2;
} }
int strncmp(const char *s1, const char *s2, size_t n) { int strncmp(const char *s1, const char *s2, size_t n) {
panic("Not implemented"); const char *p_s1 = s1, *p_s2 = s2;
int i = 0;
for(i = 0; i < n - 1; i++) {
if(s1[i] == '\0' || s2[i] == '\0')
break;
}
return s1[i] - s2[i];
} }
void *memset(void *s, int c, size_t n) { void *memset(void *s, int c, size_t n) {
panic("Not implemented"); uint8_t *p = s;
for(int i = 0; i < n; i++) {
p[i] = c;
}
return s;
} }
void *memmove(void *dst, const void *src, size_t n) { void *memmove(void *dst, const void *src, size_t n) {
panic("Not implemented"); if (src + n > dst && src < dst) {
size_t len = dst - src;
void *p_dst = (void *)src + n;
const void *p_src = src + n - len;
while(p_dst >= dst) {
memcpy(p_dst, p_src, len);
p_src -= len;
p_dst -= len;
}
if(n % len) memcpy(dst, src, n % len);
} else if (dst < src && dst + n > src) {
size_t len = src - dst;
void *p_dst = dst;
const void *p_src = src;
while(p_src < src + n) {
memcpy(p_dst, p_src, len);
p_src += len;
p_dst += len;
}
if(n % len) memcpy(p_dst, p_src, n % len);
} else {
memcpy(dst, src, n);
}
return dst;
} }
void *memcpy(void *out, const void *in, size_t n) { void *memcpy(void *out, const void *in, size_t n) {
panic("Not implemented"); for (size_t i = 0 ; i < n ; i++) {
*(uint8_t *)(out + i) = *(uint8_t *)(in + i);
}
return out;
} }
int memcmp(const void *s1, const void *s2, size_t n) { int memcmp(const void *s1, const void *s2, size_t n) {
panic("Not implemented"); const uint8_t *p1 = s1, *p2 = s2;
for (int i = 0; i < n; i++) {
if(*p1 != *p2)
return p1 - p2;
p1++; p2++;
}
return 0;
} }
#endif #endif

View file

@ -0,0 +1,17 @@
set(TEST_SOURCES
stdio
string
)
foreach(TEST IN LISTS TEST_SOURCES)
add_executable(${TEST} ${TEST}.c)
target_link_libraries(${TEST} am-${ARCH} klib m)
target_include_directories(${TEST}
PRIVATE $<TARGET_PROPERTY:am_interface,INTERFACE_INCLUDE_DIRECTORIES>
PRIVATE $<TARGET_PROPERTY:klib_interface,INTERFACE_INCLUDE_DIRECTORIES>
)
# TODO: Run tests in other configurations
if(__PLATFORM_NATIVE__)
add_test(NAME ${TEST} COMMAND ${TEST})
endif()
endforeach()

View file

@ -0,0 +1,5 @@
#include <klib.h>
int main(void) {
return 0;
}

View file

@ -0,0 +1,75 @@
#include <klib.h>
#include <klib-macros.h>
#include <stdint.h>
void test_strcpy() {
char b[32];
char *s;
b[16]='a'; b[17]='b'; b[18]='c'; b[19]=0;
panic_on((s = strcpy(b, b+16)) != b, "strcpy wrong return value");
panic_on(strcmp(s, "abc") != 0, "strcpy gave incorrect string");
panic_on((s = strcpy(b+1, b+16)) != b+1, "strcpy wrong return value");
panic_on(strcmp(s, "abc") != 0, "strcpy gave incorrect string");
panic_on((s = strcpy(b+1, b+17)) != b+1, "strcpy wrong return value");
panic_on(strcmp(s, "bc") != 0, "strcpy gave incorrect string");
}
void test_strncpy() {
char b[32];
char *s;
int i;
b[3] = 'x'; b[4] = 0;
panic_on((s = strncpy(b, "abc", 3)) != b, "strncpy wrong return value");
panic_on(b[2] != 'c', "strncpy fails to copy last byte");
panic_on(b[3] != 'x', "strncpy overruns buffer to null-terminate");
}
void test_strncmp() {
panic_on(strncmp("abcd", "abce", 3) != 0, "strncmp compares past n");
panic_on(strncmp("abc", "abd", 3) == 0, "strncmp fails to compare n-1st byte");
}
void test_memset() {
uint8_t arr[128];
arr[120] = 0xd;
panic_on(memset(arr, 0xf, 120) != arr, "memset wrong return value");
panic_on(arr[7] != 0xf, "memset fails to set value in range");
panic_on(arr[120] != 0xd, "memset set value past n");
}
void test_memcpy() {
const uint8_t src[] = { 0x0, 0x0, 0x1, 0x2, 0x3, 0x4, 0x0, 0x0 };
uint8_t dst[8] = {0};
memcpy(dst, src, 8);
panic_on(memcmp(dst, src, 8) != 0, "memcpy fails to copy memory");
}
void test_memmove() {
const uint8_t ref[] = { 0x0, 0x0, 0x1, 0x2, 0x3, 0x4, 0x0, 0x0 };
uint8_t dst[8] = {0};
const uint8_t ans1[] = { 0x1, 0x2, 0x3, 0x4, 0x3, 0x4, 0x0, 0x0 };
const uint8_t ans2[] = { 0x1, 0x2, 0x2, 0x3, 0x4, 0x3, 0x0, 0x0 };
const uint8_t ans3[] = { 0x1, 0x2, 0x2, 0x1, 0x2, 0x2, 0x3, 0x4 };
memmove(dst, ref, 8);
panic_on(memcmp(dst, ref, 8) != 0, "memmove fails to copy non-overlapping memory");
memmove(dst, dst + 2, 4);
panic_on(memcmp(dst, ans1, 8) != 0, "memmove fails to copy overlapping memory (dst < src)");
memmove(dst + 2, dst + 1, 4);
panic_on(memcmp(dst, ans2, 8) != 0, "memmove fails to copy overlapping memory (src < dst)");
memmove(dst + 3, dst, 5);
panic_on(memcmp(dst, ans3, 8) != 0, "memmove fails to copy overlapping memory (src < dst)");
}
int main(void) {
test_strcpy();
test_strncpy();
test_strncmp();
test_memset();
test_memcpy();
test_memmove();
return 0;
}

Binary file not shown.

1
am-kernels Submodule

@ -0,0 +1 @@
Subproject commit 2f559823a63cf6909d5a9e32dee47d6891caf553

View file

@ -12,59 +12,43 @@
localSystem = system; localSystem = system;
crossSystem = { crossSystem = {
config = "riscv32-none-elf"; config = "riscv32-none-elf";
abi = "ilp32"; gcc = {
abi = "ilp32";
arch = "rv32if";
};
}; };
}; };
in in
{ {
packages.nemu = pkgs.callPackage ./nemu { am-kernels = self.packages.${system}.am-kernels; }; packages.nemu = pkgs.callPackage ./nemu { am-kernels = self.packages.${system}.am-kernels; };
packages.abstract-machine = crossPkgs.callPackage ./abstract-machine { isa = "riscv"; platform = "nemu"; };
packages.am-kernels = crossPkgs.stdenv.mkDerivation rec { packages.am-kernels = crossPkgs.stdenv.mkDerivation rec {
pname = "am-kernels"; pname = "am-kernels-cmake";
version = "2024.02.18"; version = "2024.02.18";
src = pkgs.fetchFromGitHub { src = ./am-kernels;
owner = "NJU-ProjectN";
repo = "am-kernels";
rev = "bb725d6f8223dd7de831c3b692e8c4531e9d01af";
hash = "sha256-ZHdrw28TN8cMvhhzM469OV7cp0Yp+8yao855HP4+P4A=";
};
AM_HOME = pkgs.fetchFromGitHub { nativeBuildInputs = [
owner = "xinyangli"; pkgs.cmake
repo = "abstract-machine"; ];
rev = "788595aac61c6b2f3b78ca8aa7d08dc33911bca4";
hash = "sha256-YvWHIBP9tz3HL2TyibftvvQrpkWUDPnviCF4oyLmdjg=";
};
ARCH = "riscv32-nemu"; cmakeFlags = [
(pkgs.lib.cmakeFeature "ISA" "riscv")
(pkgs.lib.cmakeFeature "PLATFORM" "nemu")
(pkgs.lib.cmakeFeature "CMAKE_INSTALL_DATADIR" "share")
];
patchPhase = '' buildInputs = [
sed -i 's/\/bin\/echo/echo/' tests/cpu-tests/Makefile # SDL2
''; self.packages.${system}.abstract-machine
];
buildPhase = ''
AS=$CC make -C tests/cpu-tests BUILD_DIR=$(pwd)/build ARCH=$ARCH
'';
installPhase = ''
mkdir -p $out/share/images $out/share/dump
cp build/riscv32-nemu/*.bin $out/share/images
cp build/riscv32-nemu/*.txt $out/share/dump
'';
dontFixup = true;
};
devShells.default = pkgs.mkShell {
packages = with pkgs; [
gdb
] ++ builtins.attrValues self.packages.${system};
}; };
devShells.nemu = pkgs.mkShell { devShells.nemu = pkgs.mkShell {
packages = with pkgs; [ packages = with pkgs; [
clang-tools clang-tools
gdb
]; ];
inputsFrom = [ inputsFrom = [
self.packages.${system}.nemu self.packages.${system}.nemu
@ -73,4 +57,3 @@
} }
); );
} }

View file

@ -143,14 +143,54 @@ config TRACE_END
config ITRACE config ITRACE
depends on TRACE && TARGET_NATIVE_ELF && ENGINE_INTERPRETER depends on TRACE && TARGET_NATIVE_ELF && ENGINE_INTERPRETER
bool "Enable instruction tracer" bool "Enable instruction tracing"
default y default y
help
Instraction tracing will log past instructions into a ring buffer
and print them when NEMU exit unexpectedly.
config ITRACE_COND config ITRACE_COND
depends on ITRACE depends on ITRACE
string "Only trace instructions when the condition is true" string "Only trace instructions when the condition is true"
default "true" default "true"
config ITRACE_BUFFER
depends on ITRACE
int "Buffer size for intruction trace (unit: number of instructions)"
default 10
config MTRACE
depends on TRACE && TARGET_NATIVE_ELF && ENGINE_INTERPRETER
bool "Enable memory tracing"
default n
config MTRACE_RANGE
depends on MTRACE
string "Memory trace active range"
default "0x0-0xfffffff"
help
Memory tracer will only print memory access in these ranges.
Use comma to seperate between ranges.
config MTRACE_RANGE_MAX
depends on MTRACE
int "Max range count in MTRACE_RANGE"
default 10
config FTRACE
depends on TRACE && TARGET_NATIVE_ELF && ENGINE_INTERPRETER
bool "Enable function tracing"
default y
config FTRACE_STACK_SIZE
depends on FTRACE
int "Max function track stack size"
default 100
config FTRACE_LOG
depends on FTRACE
bool "Print log when entering a funciton"
default n
config DIFFTEST config DIFFTEST
depends on TARGET_NATIVE_ELF depends on TARGET_NATIVE_ELF

View file

@ -94,4 +94,4 @@ integration-tests: $(IMAGES)
test: unit-tests integration-tests test: unit-tests integration-tests
.PHONY: test unit-tests integration-tests .PHONY: test unit-tests integration-tests

View file

@ -1,7 +1,8 @@
{ pkgs, { pkgs,
lib, lib,
stdenv, stdenv,
am-kernels am-kernels,
dtc
}: }:
stdenv.mkDerivation rec { stdenv.mkDerivation rec {
@ -15,6 +16,7 @@ stdenv.mkDerivation rec {
pkg-config pkg-config
flex flex
bison bison
dtc
]; ];
buildInputs = with pkgs; [ buildInputs = with pkgs; [
@ -38,7 +40,7 @@ stdenv.mkDerivation rec {
doCheck = true; doCheck = true;
checkPhase = '' checkPhase = ''
export IMAGES_PATH=${am-kernels}/share/images export IMAGES_PATH=${am-kernels}/share/binary
make test make test
''; '';
@ -49,7 +51,7 @@ stdenv.mkDerivation rec {
shellHook = '' shellHook = ''
export NEMU_HOME=$(pwd) export NEMU_HOME=$(pwd)
export IMAGES_PATH=${am-kernels}/share/images export IMAGES_PATH=${am-kernels}/share/binary
''; '';
meta = with lib; { meta = with lib; {

View file

@ -17,12 +17,12 @@
#define __COMMON_H__ #define __COMMON_H__
#include <stdint.h> #include <stdint.h>
#include <inttypes.h>
#include <stdbool.h> #include <stdbool.h>
#include <string.h> #include <string.h>
#include <generated/autoconf.h> #include <generated/autoconf.h>
#include <macro.h> #include <macro.h>
#include <types.h>
#ifdef CONFIG_TARGET_AM #ifdef CONFIG_TARGET_AM
#include <klib.h> #include <klib.h>
@ -31,23 +31,6 @@
#include <stdlib.h> #include <stdlib.h>
#endif #endif
#if CONFIG_MBASE + CONFIG_MSIZE > 0x100000000ul
#define PMEM64 1
#endif
typedef MUXDEF(CONFIG_ISA64, uint64_t, uint32_t) word_t;
typedef MUXDEF(CONFIG_ISA64, int64_t, int32_t) sword_t;
static const word_t WORD_T_MAX = MUXDEF(CONFIG_ISA64, UINT64_MAX, UINT32_MAX);
static const sword_t SWORD_T_MAX = MUXDEF(CONFIG_ISA64, INT64_MAX, INT32_MAX);
static const sword_t SWORD_T_MIN = MUXDEF(CONFIG_ISA64, INT64_MIN, INT32_MIN);
#define WORD_BYTES MUXDEF(CONFIG_ISA64, 8, 4)
#define FMT_WORD MUXDEF(CONFIG_ISA64, "0x%016" PRIx64, "0x%08" PRIx32)
typedef word_t vaddr_t;
typedef MUXDEF(PMEM64, uint64_t, uint32_t) paddr_t;
#define FMT_PADDR MUXDEF(PMEM64, "0x%016" PRIx64, "0x%08" PRIx32)
typedef uint16_t ioaddr_t;
#include <debug.h> #include <debug.h>
#endif #endif

View file

@ -23,7 +23,6 @@ typedef struct Decode {
vaddr_t snpc; // static next pc vaddr_t snpc; // static next pc
vaddr_t dnpc; // dynamic next pc vaddr_t dnpc; // dynamic next pc
ISADecodeInfo isa; ISADecodeInfo isa;
IFDEF(CONFIG_ITRACE, char logbuf[128]);
} Decode; } Decode;
// --- pattern matching mechanism --- // --- pattern matching mechanism ---

View file

@ -16,9 +16,14 @@
#ifndef __DEBUG_H__ #ifndef __DEBUG_H__
#define __DEBUG_H__ #define __DEBUG_H__
#include <common.h>
#include <stdio.h> #include <stdio.h>
#include <utils.h> #include <utils.h>
#include <macro.h>
IFDEF(CONFIG_ITRACE, void log_itrace_print());
#define Trace(format, ...) \
_Log("[TRACE] " format "\n", ## __VA_ARGS__)
#define Log(format, ...) \ #define Log(format, ...) \
_Log(ANSI_FMT("[INFO] %s:%d %s() ", ANSI_FG_BLUE) format "\n", \ _Log(ANSI_FMT("[INFO] %s:%d %s() ", ANSI_FG_BLUE) format "\n", \
@ -38,6 +43,7 @@
MUXDEF(CONFIG_TARGET_AM, printf(ANSI_FMT(format, ANSI_FG_RED) "\n", ## __VA_ARGS__), \ MUXDEF(CONFIG_TARGET_AM, printf(ANSI_FMT(format, ANSI_FG_RED) "\n", ## __VA_ARGS__), \
(fflush(stdout), fprintf(stderr, ANSI_FMT(format, ANSI_FG_RED) "\n", ## __VA_ARGS__))); \ (fflush(stdout), fprintf(stderr, ANSI_FMT(format, ANSI_FG_RED) "\n", ## __VA_ARGS__))); \
IFNDEF(CONFIG_TARGET_AM, extern FILE* log_fp; fflush(log_fp)); \ IFNDEF(CONFIG_TARGET_AM, extern FILE* log_fp; fflush(log_fp)); \
IFDEF(CONFIG_ITRACE, log_itrace_print()); \
extern void assert_fail_msg(); \ extern void assert_fail_msg(); \
assert_fail_msg(); \ assert_fail_msg(); \
assert(cond); \ assert(cond); \

18
nemu/include/ftrace.h Normal file
View file

@ -0,0 +1,18 @@
#ifndef __FUNC_DEF_H__
#define __FUNC_DEF_H__
#include <common.h>
#ifdef CONFIG_FTRACE
typedef struct {
vaddr_t start;
vaddr_t len;
char * name;
} func_t;
extern func_t *func_table;
void ftrace_call(vaddr_t, vaddr_t);
void ftrace_return(vaddr_t, vaddr_t);
// const char *get_func_name(vaddr_t addr);
#endif
#endif

View file

@ -92,6 +92,8 @@
#define PG_ALIGN __attribute((aligned(4096))) #define PG_ALIGN __attribute((aligned(4096)))
#define FAILED_GOTO(tag, exp) do {if((exp)) goto tag;} while(0)
#if !defined(likely) #if !defined(likely)
#define likely(cond) __builtin_expect(cond, 1) #define likely(cond) __builtin_expect(cond, 1)
#define unlikely(cond) __builtin_expect(cond, 0) #define unlikely(cond) __builtin_expect(cond, 0)

21
nemu/include/types.h Normal file
View file

@ -0,0 +1,21 @@
#ifndef __TYPES_H__
#define __TYPES_H__
#include <inttypes.h>
#include <macro.h>
#if CONFIG_MBASE + CONFIG_MSIZE > 0x100000000ul
#define PMEM64 1
#endif
typedef MUXDEF(CONFIG_ISA64, uint64_t, uint32_t) word_t;
typedef MUXDEF(CONFIG_ISA64, int64_t, int32_t) sword_t;
static const word_t WORD_T_MAX = MUXDEF(CONFIG_ISA64, UINT64_MAX, UINT32_MAX);
static const sword_t SWORD_T_MAX = MUXDEF(CONFIG_ISA64, INT64_MAX, INT32_MAX);
static const sword_t SWORD_T_MIN = MUXDEF(CONFIG_ISA64, INT64_MIN, INT32_MIN);
#define WORD_BYTES MUXDEF(CONFIG_ISA64, 8, 4)
#define FMT_WORD MUXDEF(CONFIG_ISA64, "0x%016" PRIx64, "0x%08" PRIx32)
typedef word_t vaddr_t;
typedef MUXDEF(PMEM64, uint64_t, uint32_t) paddr_t;
#define FMT_PADDR MUXDEF(PMEM64, "0x%016" PRIx64, "0x%08" PRIx32)
typedef uint16_t ioaddr_t;
#endif

View file

@ -16,7 +16,7 @@
#ifndef __UTILS_H__ #ifndef __UTILS_H__
#define __UTILS_H__ #define __UTILS_H__
#include <common.h> #include <types.h>
// ----------- state ----------- // ----------- state -----------

View file

@ -13,6 +13,7 @@
* See the Mulan PSL v2 for more details. * See the Mulan PSL v2 for more details.
***************************************************************************************/ ***************************************************************************************/
#include <utils.h>
#include <cpu/cpu.h> #include <cpu/cpu.h>
#include <cpu/decode.h> #include <cpu/decode.h>
#include <cpu/difftest.h> #include <cpu/difftest.h>
@ -29,15 +30,17 @@ CPU_state cpu = {};
uint64_t g_nr_guest_inst = 0; uint64_t g_nr_guest_inst = 0;
static uint64_t g_timer = 0; // unit: us static uint64_t g_timer = 0; // unit: us
static bool g_print_step = false; static bool g_print_step = false;
IFDEF(CONFIG_ITRACE, extern char logbuf[CONFIG_ITRACE_BUFFER][128]);
IFDEF(CONFIG_ITRACE, extern int logbuf_rear);
void device_update(); void device_update();
bool wp_eval_all(); bool wp_eval_all();
static void trace_and_difftest(Decode *_this, vaddr_t dnpc) { static void trace_and_difftest(Decode *_this, vaddr_t dnpc) {
#ifdef CONFIG_ITRACE_COND #ifdef CONFIG_ITRACE_COND
if (ITRACE_COND) { log_write("%s\n", _this->logbuf); } if (ITRACE_COND) { log_write("%s\n", logbuf[logbuf_rear]); }
#endif #endif
if (g_print_step) { IFDEF(CONFIG_ITRACE, puts(_this->logbuf)); } if (g_print_step) { IFDEF(CONFIG_ITRACE, puts(logbuf[logbuf_rear])); }
IFDEF(CONFIG_DIFFTEST, difftest_step(_this->pc, dnpc)); IFDEF(CONFIG_DIFFTEST, difftest_step(_this->pc, dnpc));
} }
@ -47,8 +50,9 @@ static void exec_once(Decode *s, vaddr_t pc) {
isa_exec_once(s); isa_exec_once(s);
cpu.pc = s->dnpc; cpu.pc = s->dnpc;
#ifdef CONFIG_ITRACE #ifdef CONFIG_ITRACE
char *p = s->logbuf; logbuf_rear = (logbuf_rear + 1) % CONFIG_ITRACE_BUFFER;
p += snprintf(p, sizeof(s->logbuf), FMT_WORD ":", s->pc); char *p = logbuf[logbuf_rear];
p += snprintf(p, sizeof(logbuf), FMT_WORD ":", s->pc);
int ilen = s->snpc - s->pc; int ilen = s->snpc - s->pc;
int i; int i;
uint8_t *inst = (uint8_t *)&s->isa.inst.val; uint8_t *inst = (uint8_t *)&s->isa.inst.val;
@ -64,7 +68,7 @@ static void exec_once(Decode *s, vaddr_t pc) {
#ifndef CONFIG_ISA_loongarch32r #ifndef CONFIG_ISA_loongarch32r
void disassemble(char *str, int size, uint64_t pc, uint8_t *code, int nbyte); void disassemble(char *str, int size, uint64_t pc, uint8_t *code, int nbyte);
disassemble(p, s->logbuf + sizeof(s->logbuf) - p, 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 #else
p[0] = '\0'; // the upstream llvm does not support loongarch32r p[0] = '\0'; // the upstream llvm does not support loongarch32r
@ -79,7 +83,7 @@ static void execute(uint64_t n) {
g_nr_guest_inst ++; g_nr_guest_inst ++;
trace_and_difftest(&s, cpu.pc); trace_and_difftest(&s, cpu.pc);
if (wp_eval_all()) { if (wp_eval_all()) {
puts(s.logbuf); puts(logbuf[logbuf_rear]);
break; break;
} }
if (nemu_state.state != NEMU_RUNNING) break; if (nemu_state.state != NEMU_RUNNING) break;
@ -121,13 +125,16 @@ void cpu_exec(uint64_t n) {
switch (nemu_state.state) { 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: case NEMU_END: case NEMU_ABORT: {
Log("nemu: %s at pc = " FMT_WORD, Log("nemu: %s at pc = " FMT_WORD,
(nemu_state.state == NEMU_ABORT ? ANSI_FMT("ABORT", ANSI_FG_RED) : (nemu_state.state == NEMU_ABORT ? ANSI_FMT("ABORT", ANSI_FG_RED) :
(nemu_state.halt_ret == 0 ? ANSI_FMT("HIT GOOD TRAP", ANSI_FG_GREEN) : (nemu_state.halt_ret == 0 ? ANSI_FMT("HIT GOOD TRAP", ANSI_FG_GREEN) :
ANSI_FMT("HIT BAD TRAP", ANSI_FG_RED))), ANSI_FMT("HIT BAD TRAP", ANSI_FG_RED))),
nemu_state.halt_pc); nemu_state.halt_pc);
// fall through if(nemu_state.halt_ret != 0) {
log_itrace_print();
}
} // fall through
case NEMU_QUIT: statistic(); case NEMU_QUIT: statistic();
} }
} }

View file

@ -18,7 +18,10 @@
#include "../local-include/reg.h" #include "../local-include/reg.h"
bool isa_difftest_checkregs(CPU_state *ref_r, vaddr_t pc) { bool isa_difftest_checkregs(CPU_state *ref_r, vaddr_t pc) {
return false; for(int i = 0; i < MUXDEF(CONFIG_RVE, 16, 32); i++) {
if(!difftest_check_reg(reg_name(i), pc, ref_r->gpr[i], gpr(i))) return false;
}
return true;
} }
void isa_difftest_attach() { void isa_difftest_attach() {

View file

@ -13,11 +13,14 @@
* See the Mulan PSL v2 for more details. * See the Mulan PSL v2 for more details.
***************************************************************************************/ ***************************************************************************************/
#include "common.h" #include <common.h>
#include "local-include/reg.h" #include "local-include/reg.h"
#include "macro.h"
#include <cpu/cpu.h> #include <cpu/cpu.h>
#include <cpu/ifetch.h> #include <cpu/ifetch.h>
#include <cpu/decode.h> #include <cpu/decode.h>
#include <ftrace.h>
#include <utils.h>
#define R(i) gpr(i) #define R(i) gpr(i)
#define Mr vaddr_read #define Mr vaddr_read
@ -54,11 +57,23 @@ static void decode_operand(Decode *s, int *rd, word_t *src1, word_t *src2,
static void do_branch(Decode *s, bool condition, word_t offset) { static void do_branch(Decode *s, bool condition, word_t offset) {
if (condition) { if (condition) {
puts(s->logbuf); // puts(s->logbuf[s->logbuf_rear]);
s->dnpc = s->pc + offset; s->dnpc = s->pc + offset;
} }
} }
#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) {
ftrace_return(s->pc, dst);
} else {
ftrace_call(s->pc, dst);
}
}
#endif
static int decode_exec(Decode *s) { static int decode_exec(Decode *s) {
int rd = 0; int rd = 0;
word_t src1 = 0, src2 = 0, imm = 0; word_t src1 = 0, src2 = 0, imm = 0;
@ -74,8 +89,12 @@ static int decode_exec(Decode *s) {
INSTPAT("??????? ????? ????? ??? ????? 01101 11", lui , U, R(rd) = imm); INSTPAT("??????? ????? ????? ??? ????? 01101 11", lui , U, R(rd) = imm);
INSTPAT("??????? ????? ????? ??? ????? 00101 11", auipc , U, R(rd) = s->pc + 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; } while(0)); INSTPAT("??????? ????? ????? ??? ????? 11011 11", jal , J, do {
INSTPAT("??????? ????? ????? ??? ????? 11001 11", jalr , I, do {s->dnpc = src1 + imm; R(rd) = s->pc + 4; } while(0)); 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("??????? ????? ????? 000 ????? 11000 11", beq , B, do_branch(s, src1 == src2, imm));
INSTPAT("??????? ????? ????? 001 ????? 11000 11", bne , 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("??????? ????? ????? 100 ????? 11000 11", blt , B, do_branch(s, (sword_t)src1 < (sword_t)src2, imm));

View file

@ -13,6 +13,8 @@
* See the Mulan PSL v2 for more details. * See the Mulan PSL v2 for more details.
***************************************************************************************/ ***************************************************************************************/
#include "common.h"
#include "debug.h"
#include <memory/host.h> #include <memory/host.h>
#include <memory/paddr.h> #include <memory/paddr.h>
#include <device/mmio.h> #include <device/mmio.h>
@ -23,6 +25,11 @@ static uint8_t *pmem = NULL;
#else // CONFIG_PMEM_GARRAY #else // CONFIG_PMEM_GARRAY
static uint8_t pmem[CONFIG_MSIZE] PG_ALIGN = {}; static uint8_t pmem[CONFIG_MSIZE] PG_ALIGN = {};
#endif #endif
#ifdef CONFIG_MTRACE
static word_t mtrace_start[CONFIG_MTRACE_RANGE_MAX] = {0};
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; } paddr_t host_to_guest(uint8_t *haddr) { return haddr - pmem + CONFIG_MBASE; }
@ -41,23 +48,58 @@ static void out_of_bound(paddr_t addr) {
addr, PMEM_LEFT, PMEM_RIGHT, cpu.pc); 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);
break;
}
}
#endif
void init_mem() { void init_mem() {
#if defined(CONFIG_PMEM_MALLOC) #if defined(CONFIG_PMEM_MALLOC)
pmem = malloc(CONFIG_MSIZE); pmem = malloc(CONFIG_MSIZE);
assert(pmem); assert(pmem);
#endif
#ifdef CONFIG_MTRACE
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; ) {
word_t start, end;
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;
}
Trace("MTRACE ranges: ");
for (int i = 0; i < range_count; i++) {
Trace("[0x%x, 0x%x]", mtrace_start[i], mtrace_end[i]);
}
#endif #endif
IFDEF(CONFIG_MEM_RANDOM, memset(pmem, rand(), CONFIG_MSIZE)); 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 paddr_read(paddr_t addr, int len) {
if (likely(in_pmem(addr))) return pmem_read(addr, len); word_t result = 0;
IFDEF(CONFIG_DEVICE, return mmio_read(addr, len)); 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); out_of_bound(addr);
return 0;
mtrace:
IFDEF(CONFIG_MTRACE, mtrace_print('R', addr, len, result));
return result;
} }
void paddr_write(paddr_t addr, int len, word_t data) { 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); IFDEF(CONFIG_DEVICE, mmio_write(addr, len, data); return);
out_of_bound(addr); out_of_bound(addr);

View file

@ -15,6 +15,7 @@
#include <isa.h> #include <isa.h>
#include <memory/paddr.h> #include <memory/paddr.h>
#include <utils.h>
void init_rand(); void init_rand();
void init_log(const char *log_file); void init_log(const char *log_file);
@ -40,6 +41,7 @@ static void welcome() {
void sdb_set_batch_mode(); void sdb_set_batch_mode();
static char *log_file = NULL; static char *log_file = NULL;
static char *elf_file = NULL;
static char *diff_so_file = NULL; static char *diff_so_file = NULL;
static char *img_file = NULL; static char *img_file = NULL;
static int difftest_port = 1234; static int difftest_port = 1234;
@ -72,6 +74,7 @@ static int parse_args(int argc, char *argv[]) {
{"log" , required_argument, NULL, 'l'}, {"log" , required_argument, NULL, 'l'},
{"diff" , required_argument, NULL, 'd'}, {"diff" , required_argument, NULL, 'd'},
{"port" , required_argument, NULL, 'p'}, {"port" , required_argument, NULL, 'p'},
{"elf" , required_argument, NULL, 'f'},
{"help" , no_argument , NULL, 'h'}, {"help" , no_argument , NULL, 'h'},
{0 , 0 , NULL, 0 }, {0 , 0 , NULL, 0 },
}; };
@ -82,6 +85,7 @@ static int parse_args(int argc, char *argv[]) {
case 'p': sscanf(optarg, "%d", &difftest_port); break; case 'p': sscanf(optarg, "%d", &difftest_port); break;
case 'l': log_file = optarg; break; case 'l': log_file = optarg; break;
case 'd': diff_so_file = optarg; break; case 'd': diff_so_file = optarg; break;
case 'f': elf_file = optarg; break;
case 1: img_file = optarg; return 0; case 1: img_file = optarg; return 0;
default: default:
printf("Usage: %s [OPTION...] IMAGE [args]\n\n", argv[0]); printf("Usage: %s [OPTION...] IMAGE [args]\n\n", argv[0]);
@ -89,6 +93,7 @@ static int parse_args(int argc, char *argv[]) {
printf("\t-l,--log=FILE output log to FILE\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-d,--diff=REF_SO run DiffTest with reference REF_SO\n");
printf("\t-p,--port=PORT run DiffTest with port PORT\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"); printf("\n");
exit(0); exit(0);
} }
@ -126,6 +131,16 @@ void init_monitor(int argc, char *argv[]) {
/* Initialize the simple debugger. */ /* Initialize the simple debugger. */
init_sdb(); init_sdb();
// printf("elf_file: %s\n", elf_file);
if(elf_file != NULL) {
#ifdef CONFIG_FTRACE
void init_elf(const char *path);
init_elf(elf_file);
#else
Warning("Elf file provided, but ftrace not turned on. Ignoring elf file.");
#endif
}
#ifndef CONFIG_ISA_loongarch32r #ifndef CONFIG_ISA_loongarch32r
IFDEF(CONFIG_ITRACE, init_disasm( IFDEF(CONFIG_ITRACE, init_disasm(
MUXDEF(CONFIG_ISA_x86, "i686", MUXDEF(CONFIG_ISA_x86, "i686",

View file

@ -7,6 +7,7 @@
} }
%{ %{
#include <common.h> #include <common.h>
#include <utils.h>
#include <isa.h> #include <isa.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>

124
nemu/src/utils/ftrace.c Normal file
View file

@ -0,0 +1,124 @@
#include <assert.h>
#include <common.h>
#include <elf.h>
#include <ftrace.h>
#include <utils.h>
// Put this into another file
#ifdef CONFIG_FTRACE
static vaddr_t ftrace_stack[CONFIG_FTRACE_STACK_SIZE] = {0};
static vaddr_t ftrace_stack_len = 0;
func_t *func_table = NULL;
int func_table_len = 0, func_table_size = 8;
static int cmp_func_t(const void *a, const void *b) {
return ((func_t *)a)->start > ((func_t *)b)->start;
}
static func_t *get_func(vaddr_t addr) {
int l = 0, r = func_table_len - 1;
while(l <= r) {
int mid = (l + r) / 2;
if(func_table[mid].start <= addr) l = mid + 1;
else r = mid - 1;
}
return l == 0 ? NULL : &func_table[l - 1];
}
void init_elf(const char *path) {
FILE *elf_file = fopen(path, "rb");
Elf32_Ehdr header;
Elf32_Shdr section_header[200], *psh;
func_table = (func_t *)calloc(func_table_size, sizeof(func_t));
assert(func_table);
FAILED_GOTO(failed_header, fread(&header, sizeof(Elf32_Ehdr), 1, elf_file) <= 0);
FAILED_GOTO(failed_header, fseek(elf_file, header.e_shoff, SEEK_SET) != 0);
FAILED_GOTO(failed_header, fread(section_header, header.e_shentsize, header.e_shnum, elf_file) <= 0);
char *shstrtab = calloc(1, section_header[header.e_shstrndx].sh_size);
FAILED_GOTO(failed_shstrtab, fseek(elf_file, section_header[header.e_shstrndx].sh_offset, SEEK_SET) != 0);
FAILED_GOTO(failed_shstrtab, fread(shstrtab, section_header[header.e_shstrndx].sh_size, 1, elf_file) <= 0);
Elf32_Shdr *symtab = NULL, *strtab = NULL;
for(int i = 0; i < header.e_shnum; i++) {
psh = section_header + i;
if (psh->sh_type == SHT_SYMTAB) {
symtab = psh;
} else if (psh->sh_type == SHT_STRTAB && strncmp(shstrtab + psh->sh_name, ".strtab", 8) == 0) {
strtab = psh;
}
}
int sym_length = symtab->sh_size / sizeof(Elf32_Sym);
Elf32_Sym *sym = calloc(sym_length, sizeof(Elf32_Sym));
assert(sym);
FAILED_GOTO(failed_funcname, fseek(elf_file, symtab->sh_offset, SEEK_SET) != 0);
FAILED_GOTO(failed_funcname, fread(sym, sizeof(Elf32_Sym), sym_length, elf_file) <= 0);
for(int j = 0; j < sym_length; j++) {
if(ELF32_ST_TYPE(sym[j].st_info) != STT_FUNC) continue;
// Only read function type symbol
func_t *f = &func_table[func_table_len];
char *func = (char *)malloc(30);
FAILED_GOTO(failed_funcname, fseek(elf_file, strtab->sh_offset + sym[j].st_name, SEEK_SET) != 0);
FAILED_GOTO(failed_funcname, fgets(func, 30, elf_file) <= 0);
f->start = sym[j].st_value;
f->len = sym[j].st_size;
f->name = func;
++func_table_len;
if(func_table_len >= func_table_size) {
Assert(func_table_size * 2 > func_table_size, "Function table exceed memory limit");
func_table_size *= 2;
func_table = realloc(func_table, func_table_size * sizeof(func_t));
Assert(func_table, "Function table exceed memory limit");
}
}
qsort(func_table, func_table_len, sizeof(func_t), cmp_func_t);
goto success;
success:
free(sym);
free(shstrtab);
return;
failed_funcname:
free(sym);
failed_shstrtab:
free(shstrtab);
failed_header:
for(int i = 0; i < func_table_len; i++) {
func_t *f = &func_table[i];
if(f->name) { free(f->name); }
}
free(func_table);
Error("Failed reading elf file");
return;
}
void ftrace_call(vaddr_t pc, vaddr_t addr) {
func_t *f = get_func(addr);
Assert(ftrace_stack_len < CONFIG_FTRACE_STACK_SIZE,
"Ftrace stack exceed size limit, consider turn off ftrace or increase "
"FTRACE_STACK_SIZE.");
ftrace_stack[ftrace_stack_len] = pc + 4;
Trace("%*s0x%x call 0x%x <%s+0x%x>", ftrace_stack_len, "", pc, addr,
f == NULL ? "???" : f->name, addr - f->start);
ftrace_stack_len++;
}
void ftrace_return(vaddr_t pc, vaddr_t addr) {
--ftrace_stack_len;
for (; addr != ftrace_stack[ftrace_stack_len] && ftrace_stack_len >= 0;
ftrace_stack_len--) {
vaddr_t tco_addr = ftrace_stack[ftrace_stack_len];
func_t *f = get_func(tco_addr);
Trace("%*s0x%x ret 0x%x <%s+0x%x> (TCO)", ftrace_stack_len, "", pc, tco_addr,
f == NULL ? "???" : f->name, tco_addr - f->start);
}
func_t *f = get_func(addr);
Trace("%*s0x%x ret 0x%x <%s+0x%x>", ftrace_stack_len, "", pc, addr,
f == NULL ? "???" : f->name, addr - f->start);
}
#endif

View file

@ -14,6 +14,7 @@
***************************************************************************************/ ***************************************************************************************/
#include <common.h> #include <common.h>
#include <utils.h>
extern uint64_t g_nr_guest_inst; extern uint64_t g_nr_guest_inst;
@ -35,3 +36,18 @@ bool log_enable() {
(g_nr_guest_inst <= CONFIG_TRACE_END), false); (g_nr_guest_inst <= CONFIG_TRACE_END), false);
} }
#endif #endif
IFDEF(CONFIG_ITRACE, char logbuf[CONFIG_ITRACE_BUFFER][128]);
IFDEF(CONFIG_ITRACE, int logbuf_rear);
#ifdef CONFIG_ITRACE
void log_itrace_print() {
puts("ITRACE buffer:");
for (int i = (logbuf_rear + 1) % CONFIG_ITRACE_BUFFER; i != logbuf_rear; i = (i + 1) % CONFIG_ITRACE_BUFFER) {
if (logbuf[i][0] == '\0') continue;
puts(logbuf[i]);
}
puts("Current command:");
puts(logbuf[logbuf_rear]);
}
#endif