Compare commits

...

3 commits

Author SHA1 Message Date
504d270947 pa1.3: watchpoint support 2024-02-08 20:07:03 +08:00
2675a647b0
fix: change x paddr to vaddr 2024-02-05 22:53:39 +08:00
4aa536e1ef
pa1.2: fix include and word type 2024-01-13 11:42:35 +08:00
17 changed files with 389 additions and 57 deletions

2
nemu/.gitignore vendored
View file

@ -7,6 +7,8 @@ build/
.config
.config.old
.envrc
.metals/
.vscode/
compile_commands.json
### C ###

View file

@ -48,6 +48,7 @@ CFLAGS_BUILD += $(if $(CONFIG_CC_ASAN),-fsanitize=address,)
CFLAGS_TRACE += -DITRACE_COND=$(if $(CONFIG_ITRACE_COND),$(call remove_quote,$(CONFIG_ITRACE_COND)),true)
CFLAGS += $(CFLAGS_BUILD) $(CFLAGS_TRACE) -D__GUEST_ISA__=$(GUEST_ISA)
LDFLAGS += $(CFLAGS_BUILD)
INC_PATH += $(NEMU_HOME)/src/isa/$(GUEST_ISA)/local-include
# Include rules for menuconfig
include $(NEMU_HOME)/scripts/config.mk
@ -60,8 +61,6 @@ else
include $(NEMU_HOME)/scripts/native.mk
endif
.PHONY: test
include $(NEMU_HOME)/tests/Makefile
all-tests: TEST_OBJS = $(filter-out $(OBJ_DIR)/src/nemu-main.o, $(OBJS))
all-tests: CFLAGS += $(shell pkg-config --cflags check)
@ -70,3 +69,5 @@ all-tests: $(TEST_SRCS:%.c=$(OBJ_DIR)/%)
test: all-tests
@$(OBJ_DIR)/tests/expr_test
.PHONY: test

View file

@ -0,0 +1,74 @@
#
# Automatically generated file; DO NOT EDIT.
# NEMU Configuration Menu
#
# CONFIG_ISA_x86 is not set
# CONFIG_ISA_mips32 is not set
CONFIG_ISA_riscv=y
# CONFIG_ISA_loongarch32r is not set
CONFIG_ISA="riscv32"
#
# ISA-dependent Options for riscv
#
# CONFIG_RV64 is not set
# CONFIG_RVE is not set
# end of ISA-dependent Options for riscv
CONFIG_ENGINE_INTERPRETER=y
CONFIG_ENGINE="interpreter"
CONFIG_MODE_SYSTEM=y
CONFIG_TARGET_NATIVE_ELF=y
# CONFIG_TARGET_SHARE is not set
# CONFIG_TARGET_AM is not set
#
# Build Options
#
CONFIG_CC_GCC=y
# CONFIG_CC_GPP is not set
# CONFIG_CC_CLANG is not set
CONFIG_CC="gcc"
# CONFIG_CC_O0 is not set
# CONFIG_CC_O1 is not set
CONFIG_CC_O2=y
# CONFIG_CC_O3 is not set
CONFIG_CC_OPT="-O2"
# CONFIG_CC_LTO is not set
# CONFIG_CC_DEBUG is not set
CONFIG_CC_ASAN=y
# end of Build Options
#
# Testing and Debugging
#
CONFIG_TRACE=y
CONFIG_TRACE_START=0
CONFIG_TRACE_END=10000
CONFIG_ITRACE=y
CONFIG_ITRACE_COND="true"
# CONFIG_DIFFTEST is not set
CONFIG_DIFFTEST_REF_PATH="none"
CONFIG_DIFFTEST_REF_NAME="none"
# end of Testing and Debugging
#
# Memory Configuration
#
CONFIG_MBASE=0x80000000
CONFIG_MSIZE=0x8000000
CONFIG_PC_RESET_OFFSET=0
# CONFIG_PMEM_MALLOC is not set
CONFIG_PMEM_GARRAY=y
CONFIG_MEM_RANDOM=y
# end of Memory Configuration
# CONFIG_DEVICE is not set
#
# Miscellaneous
#
CONFIG_TIMER_GETTIMEOFDAY=y
# CONFIG_TIMER_CLOCK_GETTIME is not set
CONFIG_RT_CHECK=y
# end of Miscellaneous

View file

@ -50,6 +50,21 @@
libllvm
];
configurePhase = ''
echo NEMU_HOME=$NEMU_HOME
echo pwd=$(pwd)
mkdir -p $(pwd)/kconfig
WORK_DIR=$(pwd) obj=$(pwd)/kconfig make --trace -e -f scripts/config.mk WORK_DIR=$(pwd) obj=$(pwd)/kconfig rv32_defconfig
'';
installPhase = ''
BUILD_DIR=$out make install
'';
checkPhase = ''
BUILD_DIR=$out make test
'';
NEMU_HOME = src;
meta = with lib; {

View file

@ -21,7 +21,15 @@
#include <utils.h>
#define Log(format, ...) \
_Log(ANSI_FMT("[%s:%d %s] " format, ANSI_FG_BLUE) "\n", \
_Log(ANSI_FMT("[INFO] %s:%d %s() ", ANSI_FG_BLUE) format "\n", \
__FILE__, __LINE__, __func__, ## __VA_ARGS__)
#define Warning(format, ...) \
_Log(ANSI_FMT("[WARNING] %s:%d %s() ", ANSI_FG_YELLOW) format "\n", \
__FILE__, __LINE__, __func__, ## __VA_ARGS__)
#define Error(format, ...) \
_Log(ANSI_FMT("[ERROR] %s:%d %s() ", ANSI_FG_RED) format "\n", \
__FILE__, __LINE__, __func__, ## __VA_ARGS__)
#define Assert(cond, format, ...) \

View file

@ -10,7 +10,7 @@ endif
WORK_DIR = $(shell pwd)
BUILD_DIR = $(WORK_DIR)/build
INC_PATH := $(WORK_DIR)/include $(INC_PATH)
INC_PATH := $(WORK_DIR)/include $(BUILD_DIR)/include $(INC_PATH)
OBJ_DIR = $(BUILD_DIR)/obj-$(NAME)$(SO)
BINARY = $(BUILD_DIR)/$(NAME)$(SO)
@ -42,13 +42,13 @@ $(OBJ_DIR)/%.o: %.cc
$(OBJ_DIR)/%.tag.c: %.y
@echo + YACC $<
@mkdir -p $(dir $@)
@$(YACC) $(YFLAGS) --header=$(<:.y=.h) -o $@ $<
@mkdir -p $(dir $@) $(BUILD_DIR)/include
@$(YACC) $(YFLAGS) --header=$(BUILD_DIR)/include/$(notdir $(<:.y=.h)) -o $@ $<
$(OBJ_DIR)/%.yy.c: %.l $(OBJ_DIR)/%.tag.c
@echo + LEX $<
@mkdir -p $(dir $@)
@$(LEX) $(LFLAGS) --header=$(<:.l=_lex.h) -o $@ $<
@mkdir -p $(dir $@) $(BUILD_DIR)/include
@$(LEX) $(LFLAGS) --header=$(BUILD_DIR)/include/$(notdir $(<:.l=_lex.h)) -o $@ $<
$(OBJ_DIR)/%.tag.o: $(OBJ_DIR)/%.tag.c
@echo + CC $<
@ -67,7 +67,7 @@ $(OBJ_DIR)/%.yy.o: $(OBJ_DIR)/%.yy.c
# Some convenient rules
.PHONY: app clean
.PHONY: app install clean
app: $(BINARY)
@ -75,5 +75,9 @@ $(BINARY):: $(OBJS) $(ARCHIVES)
@echo + LD $@
@$(LD) -o $@ $(OBJS) $(LDFLAGS) $(ARCHIVES) $(LIBS)
install: $(BINARY)
@mkdir -p $(PREFIX)/bin
@cp $(BINARY) $(PREFIX)/bin/
clean:
-rm -rf $(BUILD_DIR)

View file

@ -31,6 +31,7 @@ static uint64_t g_timer = 0; // unit: us
static bool g_print_step = false;
void device_update();
bool wp_eval_all();
static void trace_and_difftest(Decode *_this, vaddr_t dnpc) {
#ifdef CONFIG_ITRACE_COND
@ -77,6 +78,10 @@ static void execute(uint64_t n) {
exec_once(&s, cpu.pc);
g_nr_guest_inst ++;
trace_and_difftest(&s, cpu.pc);
if (wp_eval_all()) {
puts(s.logbuf);
break;
}
if (nemu_state.state != NEMU_RUNNING) break;
IFDEF(CONFIG_DEVICE, device_update());
}

View file

@ -36,5 +36,16 @@ void isa_reg_display() {
}
word_t isa_reg_str2val(const char *s, bool *success) {
return 0;
assert(s);
int i;
for (i = 0; i < 32 && strcmp(s, regs[i]) != 0; i++)
;
if (i == 32) {
*success = false;
return 0;
}
*success = true;
return gpr(i);
}

View file

@ -1,2 +0,0 @@
addrexp_lex.h
addrexp.h

View file

@ -1,5 +1,8 @@
%{
#include <isa.h>
#include <addrexp.h>
static bool success = false;
void yyerror(word_t *result, const char *err);
%}
%option noyywrap
@ -7,7 +10,15 @@
0[xX][0-9a-fA-F]+ { yylval = strtoul(yytext, NULL, 16); return HEX_NUMBER; }
[0-9]+ { yylval = strtoul(yytext, NULL, 10); return NUMBER; }
[+\-*/()] { return *yytext; }
$[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); }
. { printf("Unexpected character: %s\n", yytext); return YYerror; }
%%

View file

@ -1,16 +1,25 @@
%{
%code requires {
#include <common.h>
#include <memory/vaddr.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
extern int yylex(void);
void yyerror(uint32_t *result, const char *err) {
fprintf(stderr, "Error: %s\n", err);
}
%{
#include <common.h>
#include <isa.h>
#include <stdio.h>
#include <stdlib.h>
void yyerror(word_t *result, const char *err) {
Error("%s", err);
}
%}
%token NUMBER HEX_NUMBER
%token REGISTER
%locations
%start input
%define api.value.type { uint32_t }
%define api.value.type { word_t }
%parse-param { uint32_t *result }
%left '-' '+'
%left '*' '/'
@ -22,6 +31,12 @@ input
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; }
@ -33,10 +48,12 @@ expression
$$ = $1 / $3;
}
| '-' number { $$ = -$2; }
| '*' expression { $$ = vaddr_read($2, WORD_BYTES); }
| '(' expression ')' { $$ = $2; }
number
: NUMBER
: REGISTER
| NUMBER
| HEX_NUMBER
%%

View file

@ -1,3 +1,2 @@
SRCS-y += src/monitor/sdb/addrexp.tag.c src/monitor/sdb/addrexp.yy.c
INC_PATH += src/monitor/sdb
LFLAGS += -DYY_NO_UNPUT -DYY_NO_INPUT

View file

@ -16,22 +16,24 @@
#include "sdb.h"
#include "common.h"
#include "sys/types.h"
#include <addrexp.h>
#include <addrexp_lex.h>
#include <cpu/cpu.h>
#include <errno.h>
#include <isa.h>
#include <memory/paddr.h>
#include <memory/vaddr.h>
#include <readline/history.h>
#include <readline/readline.h>
#include <stdint.h>
#include <addrexp.h>
#include <addrexp_lex.h>
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);
@ -53,8 +55,10 @@ static struct CommandTable {
{"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)},
@ -125,21 +129,17 @@ static word_t parse_uint(const char *arg, bool *success) {
}
}
static paddr_t parse_expr(const char *arg, bool *success) {
word_t parse_expr(const char *arg, bool *success) {
if (arg == NULL) {
puts("Invalid expr argument.");
*success = false;
return 0;
} else {
// bool res = false;
// FIXME: We cannot use `parse_uint` here, it accept `-1234` as input
// paddr_t addr = parse_uint(arg, &res);
// *success = res;
paddr_t addr;
word_t res;
yy_scan_string(arg);
*success = !yyparse(&addr);
*success = !yyparse(&res);
yylex_destroy();
return addr;
return res;
}
}
@ -148,6 +148,22 @@ static int cmd_c(char *args) {
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: <expr>]\n");
return 0;
}
static int cmd_q(char *args) {
nemu_state.state = NEMU_QUIT;
return -1;
@ -185,6 +201,12 @@ static int cmd_info_w(char *args) {
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;
@ -193,15 +215,15 @@ static int cmd_x(char *args) {
goto wrong_usage;
// No deliminter here, just pass all the remain argument to `parse_expr()`
arg = strtok(NULL, "");
word_t addr = parse_expr(arg, &res);
word_t start_addr = parse_expr(arg, &res);
if (!res)
goto wrong_usage;
addr = addr & ~(WORD_BYTES - 1);
for (paddr_t paddr = addr; paddr < addr + n; paddr += WORD_BYTES) {
word_t value = paddr_read(addr, WORD_BYTES);
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",
paddr, value);
vaddr, value);
}
return 0;

View file

@ -18,4 +18,8 @@
#include <common.h>
word_t parse_expr(const char *arg, bool *success);
int wp_add(char * expr);
int wp_remove_by_number(int number);
#endif

View file

@ -1,36 +1,38 @@
/***************************************************************************************
* 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.
***************************************************************************************/
* 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 <stdio.h>
#define NR_WP 32
typedef struct watchpoint {
int NO;
struct watchpoint *next;
/* TODO: Add more members if necessary */
word_t val;
char *expr;
} WP;
static WP wp_pool[NR_WP] = {};
static WP *head = NULL, *free_ = NULL;
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 ++) {
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]);
}
@ -39,5 +41,110 @@ void init_wp_pool() {
free_ = wp_pool;
}
/* TODO: Implement the functionality of watchpoint */
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;
}

View file

@ -1,4 +1,5 @@
TEST_SRCS += tests/expr_test.c
YACC = bison
$(OBJ_DIR)/%: %.c $(TEST_OBJS) app
@mkdir -p $(dir $@)

View file

@ -1,5 +1,6 @@
#include "macro.h"
#include "sys/types.h"
#include "unistd.h"
#include <unistd.h>
#include <assert.h>
#include <check.h>
#include <math.h>
@ -9,6 +10,8 @@
#include <time.h>
#include <addrexp.h>
#include <addrexp_lex.h>
#include <isa.h>
#include <reg.h>
char buf[65536] = {}, ref_buf[65536] = {};
static char code_buf[65536 + 128] = {}; // a little larger than `buf`
@ -147,6 +150,12 @@ struct {
{"-0x1", 0xFFFFFFFFU},
{"0--1", 0x1},
{"0--0x1", 0x1},
}, reg_exprs[] = {
{"$ra", 0x1},
{"0x2 + 4*-$a7", 0xFFFFFFBEU},
{"0x1831/$gp + 13", 2077U},
{"$$0 == 123", 0},
{"$$0 == 0", 1},
};
START_TEST(test_expr_negative_operand) {
yy_scan_string(exprs[_i].expr);
@ -160,6 +169,47 @@ START_TEST(test_expr_negative_operand) {
}
END_TEST
extern const char *regs[];
START_TEST(test_expr_plain_register) {
int i, j, result;
char buf[30] = {};
uint32_t value;
// NOTE: need to fix this if want to support more arch
buf[0] = '$';
for (i = 0; i < 32; i++) {
ck_assert(strncpy(buf + 1, regs[i], 10) != NULL);
gpr(i) = i;
yy_scan_string(buf);
result = yyparse(&value);
yylex_destroy();
ck_assert_msg(result == 0, "expr = %s\n", buf);
ck_assert(value == i);
for (j = 1; j < 10; j++) {
buf[j] = '\0';
}
}
}
END_TEST
START_TEST(test_expr_register) {
int i;
uint32_t value;
for (i = 0; i < 32; i++) {
gpr(i) = i;
}
yy_scan_string(reg_exprs[_i].expr);
ck_assert(!yyparse(&value));
yylex_destroy();
ck_assert_msg(value == reg_exprs[_i].reference,
"\n\texpr = %s\n\t(addr = %u) != (reference = %u)\n", reg_exprs[_i].expr,
value, reg_exprs[_i].reference);
}
END_TEST
Suite *expr_suite(void) {
Suite *s;
TCase *tc_core;
@ -170,6 +220,9 @@ Suite *expr_suite(void) {
tcase_add_loop_test(tc_core, test_expr_random_100, 0, 20);
tcase_add_loop_test(tc_core, test_expr_negative_operand, 0,
sizeof(exprs) / sizeof(exprs[0]));
tcase_add_loop_test(tc_core, test_expr_register, 0,
sizeof(reg_exprs) / sizeof(reg_exprs[0]));
tcase_add_test(tc_core, test_expr_plain_register);
suite_add_tcase(s, tc_core);
return s;