feat(nemu): gdb remote debug

This commit is contained in:
xinyangli 2024-04-25 16:54:27 +08:00
parent a54c0d6480
commit 7f0d1ba75a
Signed by: xin
SSH key fingerprint: SHA256:qZ/tzd8lYRtUFSrfBDBMcUqV4GHKxqeqRA3huItgvbk
22 changed files with 613 additions and 846 deletions

View file

@ -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

View file

@ -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 = [

View file

@ -17,8 +17,18 @@
#define __CPU_CPU_H__
#include <common.h>
#include <stddef.h>
#include <gdbstub.h>
#include <stddef.h>
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);

View file

@ -16,22 +16,33 @@
#ifndef __CPU_DECODE_H__
#define __CPU_DECODE_H__
#include "types.h"
#include <isa.h>
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; \
if ((i) >= len) \
goto finish; \
else { \
char c = str[i]; \
if (c != ' ') { \
@ -43,12 +54,24 @@ static inline void pattern_decode(const char *str, int len,
} \
}
#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,18 +81,21 @@ 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; \
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); \
__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); \
} \
@ -84,9 +110,9 @@ finish:
*shift = __shift;
}
// --- pattern matching wrappers for decode ---
#define INSTPAT(pattern, ...) do { \
#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) { \
@ -95,7 +121,11 @@ finish:
} \
} 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

View file

@ -17,6 +17,7 @@
#define __ISA_H__
// Located at src/isa/$(GUEST_ISA)/include/isa-def.h
#include <gdbstub.h>
#include <isa-def.h>
// 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;

View file

@ -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)

View file

@ -13,11 +13,12 @@
* See the Mulan PSL v2 for more details.
***************************************************************************************/
#include <utils.h>
#include "gdbstub.h"
#include <cpu/cpu.h>
#include <cpu/decode.h>
#include <cpu/difftest.h>
#include <locale.h>
#include <utils.h>
/* 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));
}
@ -61,7 +65,8 @@ static void exec_once(Decode *s, vaddr_t pc) {
}
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
@ -82,11 +88,8 @@ 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()) {
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");
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;
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: {
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.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_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;
}

View file

@ -13,7 +13,9 @@
* See the Mulan PSL v2 for more details.
***************************************************************************************/
#include "debug.h"
#include <cpu/cpu.h>
#include <gdbstub.h>
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
}

View file

@ -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)\"

View file

@ -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);

View file

@ -13,31 +13,59 @@
* See the Mulan PSL v2 for more details.
***************************************************************************************/
#include <common.h>
#include "local-include/reg.h"
#include "macro.h"
#include "types.h"
#include <common.h>
#include <cpu/cpu.h>
#include <cpu/ifetch.h>
#include <cpu/decode.h>
#include <cpu/ifetch.h>
#include <ftrace.h>
#include <utils.h>
#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) {
@ -46,12 +74,30 @@ static void decode_operand(Decode *s, int *rd, word_t *src1, word_t *src2,
int rs2 = BITS(i, 24, 20);
*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,6 +108,18 @@ 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;
@ -80,68 +138,116 @@ 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 */ ) { \
#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("??????? ????? ????? ??? ????? 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("??????? ????? ????? 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("??????? ????? ????? 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 ????? ????? 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_END();

View file

@ -13,16 +13,16 @@
* See the Mulan PSL v2 for more details.
***************************************************************************************/
#include <isa.h>
#include "local-include/reg.h"
#include "gdbstub.h"
#include "macro.h"
#include <errno.h>
#include <isa.h>
const char *regs[] = {
"$0", "ra", "sp", "gp", "tp", "t0", "t1", "t2",
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"
};
"s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6"};
void isa_reg_display() {
int colomn_per_row = 4;
@ -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};

View file

@ -15,10 +15,10 @@
#include "common.h"
#include "debug.h"
#include <memory/host.h>
#include <memory/paddr.h>
#include <device/mmio.h>
#include <isa.h>
#include <memory/host.h>
#include <memory/paddr.h>
#if defined(CONFIG_PMEM_MALLOC)
static uint8_t *pmem = NULL;
@ -44,7 +44,8 @@ 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,
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);
}
@ -52,7 +53,8 @@ static void out_of_bound(paddr_t addr) {
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);
Trace("PC=" FMT_PADDR " Mem %c" FMT_PADDR " %d D " FMT_PADDR, cpu.pc,
type, addr, len, data);
break;
}
}
@ -69,13 +71,15 @@ void init_mem() {
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");
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,13 +87,17 @@ 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:
@ -100,7 +108,10 @@ mtrace:
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);
}

View file

@ -0,0 +1,3 @@
DIRS-y += src/monitor/monitor.c
CXXSRC += src/monitor/gdbstub.cc

130
nemu/src/monitor/gdbstub.cc Normal file
View file

@ -0,0 +1,130 @@
#include "utils.h"
#include <vector>
extern "C" {
#include <cpu/cpu.h>
#include <debug.h>
#include <errno.h>
#include <gdbstub.h>
#include <isa.h>
#include <memory/paddr.h>
#include <stddef.h>
#include <stdlib.h>
}
typedef struct {
std::vector<breakpoint_t> *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<breakpoint_t>();
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;
}
}

View file

@ -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 "
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 <getopt.h>
void sdb_set_batch_mode();
static char *log_file = NULL;
static char *elf_file = NULL;
static char *diff_so_file = NULL;
@ -81,12 +82,21 @@ static int parse_args(int argc, char *argv[]) {
int o;
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;
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");
@ -128,8 +138,11 @@ 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) {
@ -142,14 +155,13 @@ void init_monitor(int argc, char *argv[]) {
}
#ifndef CONFIG_ISA_loongarch32r
IFDEF(CONFIG_ITRACE, init_disasm(
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"
));
MUXDEF(CONFIG_RV64, "riscv64", "riscv32"),
"bad"))) "-pc-linux-gnu"));
#endif
/* Display welcome message. */

View file

@ -1,24 +0,0 @@
%{
#include <isa.h>
#include <addrexp.h>
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; }
%%

View file

@ -1,60 +0,0 @@
%code requires {
#include <common.h>
#include <memory/vaddr.h>
#include <stdio.h>
#include <stdlib.h>
extern int yylex(void);
}
%{
#include <common.h>
#include <utils.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 { 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
%%

View file

@ -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

View file

@ -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 <addrexp.h>
#include <addrexp_lex.h>
#include <cpu/cpu.h>
#include <errno.h>
#include <isa.h>
#include <memory/vaddr.h>
#include <readline/history.h>
#include <readline/readline.h>
#include <stdint.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);
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: <expr>]\n");
return 0;
}
static int cmd_q(char *args) {
nemu_state.state = NEMU_QUIT;
return -1;
}
/* Single stepping
* <step>: execute <step> 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: <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();
}

View file

@ -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 <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,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 <common.h>
#include <stdio.h>
#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;
}