From: Sandra Loosemore Date: Mon, 3 Aug 2015 18:39:52 +0000 (-0700) Subject: Nios II R2 support for GDB. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=af60a1ef46d2c4aca22868b0f2819234b949018e;p=binutils-gdb.git Nios II R2 support for GDB. 2015-08-03 Sandra Loosemore gdb/ * nios2-tdep.h: Include opcode/nios2.h here. (NIOS2_CDX_OPCODE_SIZE): New. (struct gdbarch_tdep): Add OP parameter to syscall_next_pc. * nios2-tdep.c: Don't include opcode/nios2.h here. (nios2_fetch_insn): For R2, try reading 2-byte instruction if 4-byte read fails. (nios2_match_add, nios2_match_sub): Add cases for R2 encodings. (nios2_match_addi, nios2_match_orhi): Likewise. (nios2_match_stw, nios2_match_ldw): Likewise. (nios2_match_rdctl): Likewise. (nios2_match_stwm, nios2_match_ldwm): New. (nios2_match_branch): Add cases for R2 encodings. (nios2_match_jmpi, nios2_match_calli): Likewise. (nios2_match_jmpr, nios2_match_callr): Likewise. (nios2_match_break, nios2_match_trap): Likewise. (nios2_in_epilogue_p): Add R2 support. (nios2_analyze_prologue): Update comments. Recognize R2 CDX prologues. (nios2_breakpoint_from_pc): Handle R2 instructions. (nios2_get_next_pc): Likewise. Adjust call to tdep->syscall_next_pc. * nios2-linux-tdep.c (nios2_r1_linux_rt_sigreturn_tramp_frame): Renamed from nios2_linux_rt_sigreturn_tramp_frame. Use instruction field macros instead of literal hex values. (nios2_r2_linux_rt_sigreturn_tramp_frame): New. (nios2_linux_syscall_next_pc): Adjust signature to pass OP. Use size field from OP instead of assuming all instructions are the same size. (nios2_linux_init_abi): Register appropriate unwinder for mach. gdb/gdbserver/ * linux-nios2-low.c (NIOS2_BREAKPOINT): Conditionalize for arch variant. (CDX_BREAKPOINT): Define for R2. (nios2_breakpoint_at): Check for CDX_BREAKPOINT when R2. (the_low_target): Add comments. --- diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 9d9761a3139..83406340589 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,35 @@ +2015-08-03 Sandra Loosemore + + * nios2-tdep.h: Include opcode/nios2.h here. + (NIOS2_CDX_OPCODE_SIZE): New. + (struct gdbarch_tdep): Add OP parameter to syscall_next_pc. + * nios2-tdep.c: Don't include opcode/nios2.h here. + (nios2_fetch_insn): For R2, try reading 2-byte instruction if + 4-byte read fails. + (nios2_match_add, nios2_match_sub): Add cases for R2 encodings. + (nios2_match_addi, nios2_match_orhi): Likewise. + (nios2_match_stw, nios2_match_ldw): Likewise. + (nios2_match_rdctl): Likewise. + (nios2_match_stwm, nios2_match_ldwm): New. + (nios2_match_branch): Add cases for R2 encodings. + (nios2_match_jmpi, nios2_match_calli): Likewise. + (nios2_match_jmpr, nios2_match_callr): Likewise. + (nios2_match_break, nios2_match_trap): Likewise. + (nios2_in_epilogue_p): Add R2 support. + (nios2_analyze_prologue): Update comments. Recognize R2 CDX + prologues. + (nios2_breakpoint_from_pc): Handle R2 instructions. + (nios2_get_next_pc): Likewise. Adjust call to + tdep->syscall_next_pc. + * nios2-linux-tdep.c (nios2_r1_linux_rt_sigreturn_tramp_frame): + Renamed from nios2_linux_rt_sigreturn_tramp_frame. Use + instruction field macros instead of literal hex values. + (nios2_r2_linux_rt_sigreturn_tramp_frame): New. + (nios2_linux_syscall_next_pc): Adjust signature to pass OP. + Use size field from OP instead of assuming all instructions + are the same size. + (nios2_linux_init_abi): Register appropriate unwinder for mach. + 2015-08-03 Ulrich Weigand * cp-namespace.c (cp_lookup_symbol_via_imports): Fix uninitialized diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index e01fa46b201..8862a4846bc 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,11 @@ +2015-08-03 Sandra Loosemore + + * linux-nios2-low.c (NIOS2_BREAKPOINT): Conditionalize for + arch variant. + (CDX_BREAKPOINT): Define for R2. + (nios2_breakpoint_at): Check for CDX_BREAKPOINT when R2. + (the_low_target): Add comments. + 2015-07-30 Yao Qi * linux-arm-low.c (arm_hwcap): Remove it. diff --git a/gdb/gdbserver/linux-nios2-low.c b/gdb/gdbserver/linux-nios2-low.c index 8a7ac28b2b3..71542b495a6 100644 --- a/gdb/gdbserver/linux-nios2-low.c +++ b/gdb/gdbserver/linux-nios2-low.c @@ -117,9 +117,17 @@ nios2_set_pc (struct regcache *regcache, CORE_ADDR pc) supply_register_by_name (regcache, "pc", newpc.buf); } -/* Breakpoint support. */ +/* Breakpoint support. Also see comments on nios2_breakpoint_from_pc + in nios2-tdep.c. */ + +#if defined(__nios2_arch__) && __nios2_arch__ == 2 +#define NIOS2_BREAKPOINT 0xb7fd0020 +#define CDX_BREAKPOINT 0xd7c9 +#else +#define NIOS2_BREAKPOINT 0x003b6ffa +#endif -static const unsigned int nios2_breakpoint = 0x003b6ffa; +static const unsigned int nios2_breakpoint = NIOS2_BREAKPOINT; #define nios2_breakpoint_len 4 /* Implement the breakpoint_reinsert_addr linux_target_ops method. */ @@ -141,6 +149,13 @@ nios2_breakpoint_at (CORE_ADDR where) { unsigned int insn; + /* For R2, first check for the 2-byte CDX trap.n breakpoint encoding. */ +#if defined(__nios2_arch__) && __nios2_arch__ == 2 + (*the_target->read_memory) (where, (unsigned char *) &insn, 2); + if (insn == CDX_BREAKPOINT) + return 1; +#endif + (*the_target->read_memory) (where, (unsigned char *) &insn, 4); if (insn == nios2_breakpoint) return 1; @@ -248,6 +263,12 @@ struct linux_target_ops the_low_target = NULL, nios2_get_pc, nios2_set_pc, + + /* We only register the 4-byte breakpoint, even on R2 targets which also + support 2-byte breakpoints. Since there is no supports_z_point_type + function provided, gdbserver never inserts software breakpoints itself + and instead relies on GDB to insert the breakpoint of the correct length + via a memory write. */ (const unsigned char *) &nios2_breakpoint, nios2_breakpoint_len, nios2_reinsert_addr, diff --git a/gdb/nios2-linux-tdep.c b/gdb/nios2-linux-tdep.c index 68c949a09fb..6b483e82f42 100644 --- a/gdb/nios2-linux-tdep.c +++ b/gdb/nios2-linux-tdep.c @@ -156,13 +156,30 @@ nios2_linux_rt_sigreturn_init (const struct tramp_frame *self, trad_frame_set_id (this_cache, frame_id_build (base, func)); } -static struct tramp_frame nios2_linux_rt_sigreturn_tramp_frame = +/* Trampoline for sigreturn. This has the form + movi r2, __NR_rt_sigreturn + trap 0 + appropriately encoded for R1 or R2. */ + +static struct tramp_frame nios2_r1_linux_rt_sigreturn_tramp_frame = +{ + SIGTRAMP_FRAME, + 4, + { + { MATCH_R1_MOVI | SET_IW_I_B (2) | SET_IW_I_IMM16 (139), -1 }, + { MATCH_R1_TRAP | SET_IW_R_IMM5 (0), -1}, + { TRAMP_SENTINEL_INSN } + }, + nios2_linux_rt_sigreturn_init +}; + +static struct tramp_frame nios2_r2_linux_rt_sigreturn_tramp_frame = { SIGTRAMP_FRAME, 4, { - { 0x00800004 | (139 << 6), -1 }, /* movi r2,__NR_rt_sigreturn */ - { 0x003b683a, -1 }, /* trap */ + { MATCH_R2_MOVI | SET_IW_F2I16_B (2) | SET_IW_F2I16_IMM16 (139), -1 }, + { MATCH_R2_TRAP | SET_IW_X2L5_IMM5 (0), -1}, { TRAMP_SENTINEL_INSN } }, nios2_linux_rt_sigreturn_init @@ -172,7 +189,8 @@ static struct tramp_frame nios2_linux_rt_sigreturn_tramp_frame = instruction to be executed. */ static CORE_ADDR -nios2_linux_syscall_next_pc (struct frame_info *frame) +nios2_linux_syscall_next_pc (struct frame_info *frame, + const struct nios2_opcode *op) { CORE_ADDR pc = get_frame_pc (frame); ULONGEST syscall_nr = get_frame_register_unsigned (frame, NIOS2_R2_REGNUM); @@ -182,7 +200,7 @@ nios2_linux_syscall_next_pc (struct frame_info *frame) if (syscall_nr == 139 /* rt_sigreturn */) return frame_unwind_caller_pc (frame); - return pc + NIOS2_OPCODE_SIZE; + return pc + op->size; } /* Hook function for gdbarch_register_osabi. */ @@ -207,8 +225,12 @@ nios2_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) set_gdbarch_iterate_over_regset_sections (gdbarch, nios2_iterate_over_regset_sections); /* Linux signal frame unwinders. */ - tramp_frame_prepend_unwinder (gdbarch, - &nios2_linux_rt_sigreturn_tramp_frame); + if (gdbarch_bfd_arch_info (gdbarch)->mach == bfd_mach_nios2r2) + tramp_frame_prepend_unwinder (gdbarch, + &nios2_r2_linux_rt_sigreturn_tramp_frame); + else + tramp_frame_prepend_unwinder (gdbarch, + &nios2_r1_linux_rt_sigreturn_tramp_frame); tdep->syscall_next_pc = nios2_linux_syscall_next_pc; diff --git a/gdb/nios2-tdep.c b/gdb/nios2-tdep.c index 12056b51a54..11073eeebd4 100644 --- a/gdb/nios2-tdep.c +++ b/gdb/nios2-tdep.c @@ -45,9 +45,6 @@ /* To get entry_point_address. */ #include "objfiles.h" -/* Nios II ISA specific encodings and macros. */ -#include "opcode/nios2.h" - /* Nios II specific header. */ #include "nios2-tdep.h" @@ -287,8 +284,16 @@ nios2_fetch_insn (struct gdbarch *gdbarch, CORE_ADDR pc, unsigned long mach = gdbarch_bfd_arch_info (gdbarch)->mach; unsigned int insn; - if (!safe_read_memory_integer (pc, NIOS2_OPCODE_SIZE, - gdbarch_byte_order (gdbarch), &memword)) + if (mach == bfd_mach_nios2r2) + { + if (!safe_read_memory_integer (pc, NIOS2_OPCODE_SIZE, + BFD_ENDIAN_LITTLE, &memword) + && !safe_read_memory_integer (pc, NIOS2_CDX_OPCODE_SIZE, + BFD_ENDIAN_LITTLE, &memword)) + return NULL; + } + else if (!safe_read_memory_integer (pc, NIOS2_OPCODE_SIZE, + gdbarch_byte_order (gdbarch), &memword)) return NULL; insn = (unsigned int) memword; @@ -305,13 +310,38 @@ static int nios2_match_add (uint32_t insn, const struct nios2_opcode *op, unsigned long mach, int *ra, int *rb, int *rc) { - if (op->match == MATCH_R1_ADD || op->match == MATCH_R1_MOV) + int is_r2 = (mach == bfd_mach_nios2r2); + + if (!is_r2 && (op->match == MATCH_R1_ADD || op->match == MATCH_R1_MOV)) { *ra = GET_IW_R_A (insn); *rb = GET_IW_R_B (insn); *rc = GET_IW_R_C (insn); return 1; } + else if (!is_r2) + return 0; + else if (op->match == MATCH_R2_ADD || op->match == MATCH_R2_MOV) + { + *ra = GET_IW_F3X6L5_A (insn); + *rb = GET_IW_F3X6L5_B (insn); + *rc = GET_IW_F3X6L5_C (insn); + return 1; + } + else if (op->match == MATCH_R2_ADD_N) + { + *ra = nios2_r2_reg3_mappings[GET_IW_T3X1_A3 (insn)]; + *rb = nios2_r2_reg3_mappings[GET_IW_T3X1_B3 (insn)]; + *rc = nios2_r2_reg3_mappings[GET_IW_T3X1_C3 (insn)]; + return 1; + } + else if (op->match == MATCH_R2_MOV_N) + { + *ra = GET_IW_F2_A (insn); + *rb = 0; + *rc = GET_IW_F2_B (insn); + return 1; + } return 0; } @@ -322,13 +352,31 @@ static int nios2_match_sub (uint32_t insn, const struct nios2_opcode *op, unsigned long mach, int *ra, int *rb, int *rc) { - if (op->match == MATCH_R1_SUB) + int is_r2 = (mach == bfd_mach_nios2r2); + + if (!is_r2 && op->match == MATCH_R1_SUB) { *ra = GET_IW_R_A (insn); *rb = GET_IW_R_B (insn); *rc = GET_IW_R_C (insn); return 1; } + else if (!is_r2) + return 0; + else if (op->match == MATCH_R2_SUB) + { + *ra = GET_IW_F3X6L5_A (insn); + *rb = GET_IW_F3X6L5_B (insn); + *rc = GET_IW_F3X6L5_C (insn); + return 1; + } + else if (op->match == MATCH_R2_SUB_N) + { + *ra = nios2_r2_reg3_mappings[GET_IW_T3X1_A3 (insn)]; + *rb = nios2_r2_reg3_mappings[GET_IW_T3X1_B3 (insn)]; + *rc = nios2_r2_reg3_mappings[GET_IW_T3X1_C3 (insn)]; + return 1; + } return 0; } @@ -340,13 +388,49 @@ static int nios2_match_addi (uint32_t insn, const struct nios2_opcode *op, unsigned long mach, int *ra, int *rb, int *imm) { - if (op->match == MATCH_R1_ADDI) + int is_r2 = (mach == bfd_mach_nios2r2); + + if (!is_r2 && op->match == MATCH_R1_ADDI) { *ra = GET_IW_I_A (insn); *rb = GET_IW_I_B (insn); *imm = (signed) (GET_IW_I_IMM16 (insn) << 16) >> 16; return 1; } + else if (!is_r2) + return 0; + else if (op->match == MATCH_R2_ADDI) + { + *ra = GET_IW_F2I16_A (insn); + *rb = GET_IW_F2I16_B (insn); + *imm = (signed) (GET_IW_F2I16_IMM16 (insn) << 16) >> 16; + return 1; + } + else if (op->match == MATCH_R2_ADDI_N || op->match == MATCH_R2_SUBI_N) + { + *ra = nios2_r2_reg3_mappings[GET_IW_T2X1I3_A3 (insn)]; + *rb = nios2_r2_reg3_mappings[GET_IW_T2X1I3_B3 (insn)]; + *imm = nios2_r2_asi_n_mappings[GET_IW_T2X1I3_IMM3 (insn)]; + if (op->match == MATCH_R2_SUBI_N) + *imm = - (*imm); + return 1; + } + else if (op->match == MATCH_R2_SPADDI_N) + { + *ra = nios2_r2_reg3_mappings[GET_IW_T1I7_A3 (insn)]; + *rb = NIOS2_SP_REGNUM; + *imm = GET_IW_T1I7_IMM7 (insn) << 2; + return 1; + } + else if (op->match == MATCH_R2_SPINCI_N || op->match == MATCH_R2_SPDECI_N) + { + *ra = NIOS2_SP_REGNUM; + *rb = NIOS2_SP_REGNUM; + *imm = GET_IW_X1I7_IMM7 (insn) << 2; + if (op->match == MATCH_R2_SPDECI_N) + *imm = - (*imm); + return 1; + } return 0; } @@ -358,13 +442,24 @@ static int nios2_match_orhi (uint32_t insn, const struct nios2_opcode *op, unsigned long mach, int *ra, int *rb, unsigned int *uimm) { - if (op->match == MATCH_R1_ORHI) + int is_r2 = (mach == bfd_mach_nios2r2); + + if (!is_r2 && op->match == MATCH_R1_ORHI) { *ra = GET_IW_I_A (insn); *rb = GET_IW_I_B (insn); *uimm = GET_IW_I_IMM16 (insn); return 1; } + else if (!is_r2) + return 0; + else if (op->match == MATCH_R2_ORHI) + { + *ra = GET_IW_F2I16_A (insn); + *rb = GET_IW_F2I16_B (insn); + *uimm = GET_IW_F2I16_IMM16 (insn); + return 1; + } return 0; } @@ -376,13 +471,52 @@ static int nios2_match_stw (uint32_t insn, const struct nios2_opcode *op, unsigned long mach, int *ra, int *rb, int *imm) { - if (op->match == MATCH_R1_STW || op->match == MATCH_R1_STWIO) + int is_r2 = (mach == bfd_mach_nios2r2); + + if (!is_r2 && (op->match == MATCH_R1_STW || op->match == MATCH_R1_STWIO)) { *ra = GET_IW_I_A (insn); *rb = GET_IW_I_B (insn); *imm = (signed) (GET_IW_I_IMM16 (insn) << 16) >> 16; return 1; } + else if (!is_r2) + return 0; + else if (op->match == MATCH_R2_STW) + { + *ra = GET_IW_F2I16_A (insn); + *rb = GET_IW_F2I16_B (insn); + *imm = (signed) (GET_IW_F2I16_IMM16 (insn) << 16) >> 16; + return 1; + } + else if (op->match == MATCH_R2_STWIO) + { + *ra = GET_IW_F2X4I12_A (insn); + *rb = GET_IW_F2X4I12_B (insn); + *imm = (signed) (GET_IW_F2X4I12_IMM12 (insn) << 20) >> 20; + return 1; + } + else if (op->match == MATCH_R2_STW_N) + { + *ra = nios2_r2_reg3_mappings[GET_IW_T2I4_A3 (insn)]; + *rb = nios2_r2_reg3_mappings[GET_IW_T2I4_B3 (insn)]; + *imm = GET_IW_T2I4_IMM4 (insn) << 2; + return 1; + } + else if (op->match == MATCH_R2_STWSP_N) + { + *ra = NIOS2_SP_REGNUM; + *rb = GET_IW_F1I5_B (insn); + *imm = GET_IW_F1I5_IMM5 (insn) << 2; + return 1; + } + else if (op->match == MATCH_R2_STWZ_N) + { + *ra = nios2_r2_reg3_mappings[GET_IW_T1X1I6_A3 (insn)]; + *rb = 0; + *imm = GET_IW_T1X1I6_IMM6 (insn) << 2; + return 1; + } return 0; } @@ -394,13 +528,45 @@ static int nios2_match_ldw (uint32_t insn, const struct nios2_opcode *op, unsigned long mach, int *ra, int *rb, int *imm) { - if (op->match == MATCH_R1_LDW || op->match == MATCH_R1_LDWIO) + int is_r2 = (mach == bfd_mach_nios2r2); + + if (!is_r2 && (op->match == MATCH_R1_LDW || op->match == MATCH_R1_LDWIO)) { *ra = GET_IW_I_A (insn); *rb = GET_IW_I_B (insn); *imm = (signed) (GET_IW_I_IMM16 (insn) << 16) >> 16; return 1; } + else if (!is_r2) + return 0; + else if (op->match == MATCH_R2_LDW) + { + *ra = GET_IW_F2I16_A (insn); + *rb = GET_IW_F2I16_B (insn); + *imm = (signed) (GET_IW_F2I16_IMM16 (insn) << 16) >> 16; + return 1; + } + else if (op->match == MATCH_R2_LDWIO) + { + *ra = GET_IW_F2X4I12_A (insn); + *rb = GET_IW_F2X4I12_B (insn); + *imm = (signed) (GET_IW_F2X4I12_IMM12 (insn) << 20) >> 20; + return 1; + } + else if (op->match == MATCH_R2_LDW_N) + { + *ra = nios2_r2_reg3_mappings[GET_IW_T2I4_A3 (insn)]; + *rb = nios2_r2_reg3_mappings[GET_IW_T2I4_B3 (insn)]; + *imm = GET_IW_T2I4_IMM4 (insn) << 2; + return 1; + } + else if (op->match == MATCH_R2_LDWSP_N) + { + *ra = NIOS2_SP_REGNUM; + *rb = GET_IW_F1I5_B (insn); + *imm = GET_IW_F1I5_IMM5 (insn) << 2; + return 1; + } return 0; } @@ -411,15 +577,126 @@ static int nios2_match_rdctl (uint32_t insn, const struct nios2_opcode *op, unsigned long mach, int *ra, int *rc) { - if (op->match == MATCH_R1_RDCTL) + int is_r2 = (mach == bfd_mach_nios2r2); + + if (!is_r2 && (op->match == MATCH_R1_RDCTL)) { *ra = GET_IW_R_IMM5 (insn); *rc = GET_IW_R_C (insn); return 1; } + else if (!is_r2) + return 0; + else if (op->match == MATCH_R2_RDCTL) + { + *ra = GET_IW_F3X6L5_IMM5 (insn); + *rc = GET_IW_F3X6L5_C (insn); + return 1; + } return 0; } +/* Match and disassemble a PUSH.N or STWM instruction. + Returns true on success, and fills in the operand pointers. */ + +static int +nios2_match_stwm (uint32_t insn, const struct nios2_opcode *op, + unsigned long mach, unsigned int *reglist, + int *ra, int *imm, int *wb, int *id) +{ + int is_r2 = (mach == bfd_mach_nios2r2); + + if (!is_r2) + return 0; + else if (op->match == MATCH_R2_PUSH_N) + { + *reglist = 1 << 31; + if (GET_IW_L5I4X1_FP (insn)) + *reglist |= (1 << 28); + if (GET_IW_L5I4X1_CS (insn)) + { + int val = GET_IW_L5I4X1_REGRANGE (insn); + *reglist |= nios2_r2_reg_range_mappings[val]; + } + *ra = NIOS2_SP_REGNUM; + *imm = GET_IW_L5I4X1_IMM4 (insn) << 2; + *wb = 1; + *id = 0; + return 1; + } + else if (op->match == MATCH_R2_STWM) + { + unsigned int rawmask = GET_IW_F1X4L17_REGMASK (insn); + if (GET_IW_F1X4L17_RS (insn)) + { + *reglist = ((rawmask << 14) & 0x00ffc000); + if (rawmask & (1 << 10)) + *reglist |= (1 << 28); + if (rawmask & (1 << 11)) + *reglist |= (1 << 31); + } + else + *reglist = rawmask << 2; + *ra = GET_IW_F1X4L17_A (insn); + *imm = 0; + *wb = GET_IW_F1X4L17_WB (insn); + *id = GET_IW_F1X4L17_ID (insn); + return 1; + } + return 0; +} + +/* Match and disassemble a POP.N or LDWM instruction. + Returns true on success, and fills in the operand pointers. */ + +static int +nios2_match_ldwm (uint32_t insn, const struct nios2_opcode *op, + unsigned long mach, unsigned int *reglist, + int *ra, int *imm, int *wb, int *id, int *ret) +{ + int is_r2 = (mach == bfd_mach_nios2r2); + + if (!is_r2) + return 0; + else if (op->match == MATCH_R2_POP_N) + { + *reglist = 1 << 31; + if (GET_IW_L5I4X1_FP (insn)) + *reglist |= (1 << 28); + if (GET_IW_L5I4X1_CS (insn)) + { + int val = GET_IW_L5I4X1_REGRANGE (insn); + *reglist |= nios2_r2_reg_range_mappings[val]; + } + *ra = NIOS2_SP_REGNUM; + *imm = GET_IW_L5I4X1_IMM4 (insn) << 2; + *wb = 1; + *id = 1; + *ret = 1; + return 1; + } + else if (op->match == MATCH_R2_LDWM) + { + unsigned int rawmask = GET_IW_F1X4L17_REGMASK (insn); + if (GET_IW_F1X4L17_RS (insn)) + { + *reglist = ((rawmask << 14) & 0x00ffc000); + if (rawmask & (1 << 10)) + *reglist |= (1 << 28); + if (rawmask & (1 << 11)) + *reglist |= (1 << 31); + } + else + *reglist = rawmask << 2; + *ra = GET_IW_F1X4L17_A (insn); + *imm = 0; + *wb = GET_IW_F1X4L17_WB (insn); + *id = GET_IW_F1X4L17_ID (insn); + *ret = GET_IW_F1X4L17_PC (insn); + return 1; + } + return 0; +} /* Match and disassemble a branch instruction, with (potentially) 2 register operands and one immediate operand. @@ -440,36 +717,93 @@ nios2_match_branch (uint32_t insn, const struct nios2_opcode *op, unsigned long mach, int *ra, int *rb, int *imm, enum branch_condition *cond) { - switch (op->match) + int is_r2 = (mach == bfd_mach_nios2r2); + + if (!is_r2) { - case MATCH_R1_BR: - *cond = branch_none; - break; - case MATCH_R1_BEQ: - *cond = branch_eq; - break; - case MATCH_R1_BNE: - *cond = branch_ne; - break; - case MATCH_R1_BGE: - *cond = branch_ge; - break; - case MATCH_R1_BGEU: - *cond = branch_geu; - break; - case MATCH_R1_BLT: - *cond = branch_lt; - break; - case MATCH_R1_BLTU: - *cond = branch_ltu; - break; - default: - return 0; + switch (op->match) + { + case MATCH_R1_BR: + *cond = branch_none; + break; + case MATCH_R1_BEQ: + *cond = branch_eq; + break; + case MATCH_R1_BNE: + *cond = branch_ne; + break; + case MATCH_R1_BGE: + *cond = branch_ge; + break; + case MATCH_R1_BGEU: + *cond = branch_geu; + break; + case MATCH_R1_BLT: + *cond = branch_lt; + break; + case MATCH_R1_BLTU: + *cond = branch_ltu; + break; + default: + return 0; + } + *imm = (signed) (GET_IW_I_IMM16 (insn) << 16) >> 16; + *ra = GET_IW_I_A (insn); + *rb = GET_IW_I_B (insn); + return 1; } - *imm = (signed) (GET_IW_I_IMM16 (insn) << 16) >> 16; - *ra = GET_IW_I_A (insn); - *rb = GET_IW_I_B (insn); - return 1; + else + { + switch (op->match) + { + case MATCH_R2_BR_N: + *cond = branch_none; + *ra = NIOS2_Z_REGNUM; + *rb = NIOS2_Z_REGNUM; + *imm = (signed) ((GET_IW_I10_IMM10 (insn) << 1) << 21) >> 21; + return 1; + case MATCH_R2_BEQZ_N: + *cond = branch_eq; + *ra = nios2_r2_reg3_mappings[GET_IW_T1I7_A3 (insn)]; + *rb = NIOS2_Z_REGNUM; + *imm = (signed) ((GET_IW_T1I7_IMM7 (insn) << 1) << 24) >> 24; + return 1; + case MATCH_R2_BNEZ_N: + *cond = branch_ne; + *ra = nios2_r2_reg3_mappings[GET_IW_T1I7_A3 (insn)]; + *rb = NIOS2_Z_REGNUM; + *imm = (signed) ((GET_IW_T1I7_IMM7 (insn) << 1) << 24) >> 24; + return 1; + case MATCH_R2_BR: + *cond = branch_none; + break; + case MATCH_R2_BEQ: + *cond = branch_eq; + break; + case MATCH_R2_BNE: + *cond = branch_ne; + break; + case MATCH_R2_BGE: + *cond = branch_ge; + break; + case MATCH_R2_BGEU: + *cond = branch_geu; + break; + case MATCH_R2_BLT: + *cond = branch_lt; + break; + case MATCH_R2_BLTU: + *cond = branch_ltu; + break; + default: + return 0; + } + *ra = GET_IW_F2I16_A (insn); + *rb = GET_IW_F2I16_B (insn); + *imm = (signed) (GET_IW_F2I16_IMM16 (insn) << 16) >> 16; + return 1; + } + return 0; } /* Match and disassemble a direct jump instruction, with an @@ -480,11 +814,20 @@ static int nios2_match_jmpi (uint32_t insn, const struct nios2_opcode *op, unsigned long mach, unsigned int *uimm) { - if (op->match == MATCH_R1_JMPI) + int is_r2 = (mach == bfd_mach_nios2r2); + + if (!is_r2 && op->match == MATCH_R1_JMPI) { *uimm = GET_IW_J_IMM26 (insn) << 2; return 1; } + else if (!is_r2) + return 0; + else if (op->match == MATCH_R2_JMPI) + { + *uimm = GET_IW_L26_IMM26 (insn) << 2; + return 1; + } return 0; } @@ -496,11 +839,20 @@ static int nios2_match_calli (uint32_t insn, const struct nios2_opcode *op, unsigned long mach, unsigned int *uimm) { - if (op->match == MATCH_R1_CALL) + int is_r2 = (mach == bfd_mach_nios2r2); + + if (!is_r2 && op->match == MATCH_R1_CALL) { *uimm = GET_IW_J_IMM26 (insn) << 2; return 1; } + else if (!is_r2) + return 0; + else if (op->match == MATCH_R2_CALL) + { + *uimm = GET_IW_L26_IMM26 (insn) << 2; + return 1; + } return 0; } @@ -512,23 +864,49 @@ static int nios2_match_jmpr (uint32_t insn, const struct nios2_opcode *op, unsigned long mach, int *ra) { - switch (op->match) - { - case MATCH_R1_JMP: - *ra = GET_IW_I_A (insn); - return 1; - case MATCH_R1_RET: - *ra = NIOS2_RA_REGNUM; - return 1; - case MATCH_R1_ERET: - *ra = NIOS2_EA_REGNUM; - return 1; - case MATCH_R1_BRET: - *ra = NIOS2_BA_REGNUM; - return 1; - default: - return 0; - } + int is_r2 = (mach == bfd_mach_nios2r2); + + if (!is_r2) + switch (op->match) + { + case MATCH_R1_JMP: + *ra = GET_IW_I_A (insn); + return 1; + case MATCH_R1_RET: + *ra = NIOS2_RA_REGNUM; + return 1; + case MATCH_R1_ERET: + *ra = NIOS2_EA_REGNUM; + return 1; + case MATCH_R1_BRET: + *ra = NIOS2_BA_REGNUM; + return 1; + default: + return 0; + } + else + switch (op->match) + { + case MATCH_R2_JMP: + *ra = GET_IW_F2I16_A (insn); + return 1; + case MATCH_R2_JMPR_N: + *ra = GET_IW_F1X1_A (insn); + return 1; + case MATCH_R2_RET: + case MATCH_R2_RET_N: + *ra = NIOS2_RA_REGNUM; + return 1; + case MATCH_R2_ERET: + *ra = NIOS2_EA_REGNUM; + return 1; + case MATCH_R2_BRET: + *ra = NIOS2_BA_REGNUM; + return 1; + default: + return 0; + } + return 0; } /* Match and disassemble an indirect call instruction, with a register @@ -538,11 +916,25 @@ static int nios2_match_callr (uint32_t insn, const struct nios2_opcode *op, unsigned long mach, int *ra) { - if (op->match == MATCH_R1_CALLR) + int is_r2 = (mach == bfd_mach_nios2r2); + + if (!is_r2 && op->match == MATCH_R1_CALLR) { *ra = GET_IW_I_A (insn); return 1; } + else if (!is_r2) + return 0; + else if (op->match == MATCH_R2_CALLR) + { + *ra = GET_IW_F2I16_A (insn); + return 1; + } + else if (op->match == MATCH_R2_CALLR_N) + { + *ra = GET_IW_F1X1_A (insn); + return 1; + } return 0; } @@ -553,11 +945,25 @@ static int nios2_match_break (uint32_t insn, const struct nios2_opcode *op, unsigned long mach, unsigned int *uimm) { - if (op->match == MATCH_R1_BREAK) + int is_r2 = (mach == bfd_mach_nios2r2); + + if (!is_r2 && op->match == MATCH_R1_BREAK) { *uimm = GET_IW_R_IMM5 (insn); return 1; } + else if (!is_r2) + return 0; + else if (op->match == MATCH_R2_BREAK) + { + *uimm = GET_IW_F3X6L5_IMM5 (insn); + return 1; + } + else if (op->match == MATCH_R2_BREAK_N) + { + *uimm = GET_IW_X2L5_IMM5 (insn); + return 1; + } return 0; } @@ -568,11 +974,25 @@ static int nios2_match_trap (uint32_t insn, const struct nios2_opcode *op, unsigned long mach, unsigned int *uimm) { - if (op->match == MATCH_R1_TRAP) + int is_r2 = (mach == bfd_mach_nios2r2); + + if (!is_r2 && op->match == MATCH_R1_TRAP) { *uimm = GET_IW_R_IMM5 (insn); return 1; } + else if (!is_r2) + return 0; + else if (op->match == MATCH_R2_TRAP) + { + *uimm = GET_IW_F3X6L5_IMM5 (insn); + return 1; + } + else if (op->match == MATCH_R2_TRAP_N) + { + *uimm = GET_IW_X2L5_IMM5 (insn); + return 1; + } return 0; } @@ -589,6 +1009,7 @@ nios2_in_epilogue_p (struct gdbarch *gdbarch, CORE_ADDR start_pc) { unsigned long mach = gdbarch_bfd_arch_info (gdbarch)->mach; + int is_r2 = (mach == bfd_mach_nios2r2); /* Maximum number of possibly-epilogue instructions to check. Note that this number should not be too large, else we can potentially end up iterating through unmapped memory. */ @@ -597,6 +1018,7 @@ nios2_in_epilogue_p (struct gdbarch *gdbarch, const struct nios2_opcode *op = NULL; unsigned int uimm; int imm; + int wb, id, ret; int ra, rb, rc; enum branch_condition cond; CORE_ADDR pc; @@ -605,17 +1027,41 @@ nios2_in_epilogue_p (struct gdbarch *gdbarch, if (current_pc <= start_pc) return 0; - /* Find the previous instruction before current_pc. - For the moment we will assume that all instructions are the - same size here. */ - pc = current_pc - NIOS2_OPCODE_SIZE; + /* Find the previous instruction before current_pc. For R2, it might + be either a 16-bit or 32-bit instruction; the only way to know for + sure is to scan through from the beginning of the function, + disassembling as we go. */ + if (is_r2) + for (pc = start_pc; ; ) + { + op = nios2_fetch_insn (gdbarch, pc, &insn); + if (op == NULL) + return 0; + if (pc + op->size < current_pc) + pc += op->size; + else + break; + /* We can skip over insns to a forward branch target. Since + the branch offset is relative to the next instruction, + it's correct to do this after incrementing the pc above. */ + if (nios2_match_branch (insn, op, mach, &ra, &rb, &imm, &cond) + && imm > 0 + && pc + imm < current_pc) + pc += imm; + } + /* Otherwise just go back to the previous 32-bit insn. */ + else + pc = current_pc - NIOS2_OPCODE_SIZE; /* Beginning with the previous instruction we just located, check whether we are in a sequence of at least one stack adjustment instruction. Possible instructions here include: ADDI sp, sp, n ADD sp, sp, rn - LDW sp, n(sp) */ + LDW sp, n(sp) + SPINCI.N n + LDWSP.N sp, n(sp) + LDWM {reglist}, (sp)++, wb */ for (ninsns = 0; ninsns < max_insns; ninsns++) { int ok = 0; @@ -633,6 +1079,9 @@ nios2_in_epilogue_p (struct gdbarch *gdbarch, ok = (rc == NIOS2_SP_REGNUM); else if (nios2_match_ldw (insn, op, mach, &ra, &rb, &imm)) ok = (rb == NIOS2_SP_REGNUM); + else if (nios2_match_ldwm (insn, op, mach, &uimm, &ra, + &imm, &wb, &ret, &id)) + ok = (ra == NIOS2_SP_REGNUM && wb && id); if (!ok) break; } @@ -648,9 +1097,12 @@ nios2_in_epilogue_p (struct gdbarch *gdbarch, return 1; /* The next instruction following the stack adjustments must be a - return, jump, or unconditional branch. */ + return, jump, or unconditional branch, or a CDX pop.n or ldwm + that does an implicit return. */ if (nios2_match_jmpr (insn, op, mach, &ra) || nios2_match_jmpi (insn, op, mach, &uimm) + || (nios2_match_ldwm (insn, op, mach, &uimm, &ra, &imm, &wb, &id, &ret) + && ret) || (nios2_match_branch (insn, op, mach, &ra, &rb, &imm, &cond) && cond == branch_none)) return 1; @@ -684,10 +1136,12 @@ nios2_stack_frame_destroyed_p (struct gdbarch *gdbarch, CORE_ADDR pc) mov ra, r8 2) A stack adjustment and save of R4-R7 for varargs functions. - This is typically merged with item 3. + For R2 CDX this is typically handled with a STWM, otherwise + this is typically merged with item 3. - 3) A stack adjustment and save of the callee-saved registers; - typically an explicit SP decrement and individual register + 3) A stack adjustment and save of the callee-saved registers. + For R2 CDX these are typically handled with a PUSH.N or STWM, + otherwise as an explicit SP decrement and individual register saves. There may also be a stack switch here in an exception handler @@ -744,6 +1198,7 @@ nios2_analyze_prologue (struct gdbarch *gdbarch, const CORE_ADDR start_pc, int regno; enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); unsigned long mach = gdbarch_bfd_arch_info (gdbarch)->mach; + int is_r2 = (mach == bfd_mach_nios2r2); /* Does the frame set up the FP register? */ int base_reg = 0; @@ -789,7 +1244,7 @@ nios2_analyze_prologue (struct gdbarch *gdbarch, const CORE_ADDR start_pc, int ra, rb, rc, imm; unsigned int uimm; unsigned int reglist; - int wb, ret; + int wb, id, ret; enum branch_condition cond; if (pc == current_pc) @@ -812,7 +1267,12 @@ nios2_analyze_prologue (struct gdbarch *gdbarch, const CORE_ADDR start_pc, pc += op->size; if (nios2_debug) - fprintf_unfiltered (gdb_stdlog, "[%08X]", insn); + { + if (op->size == 2) + fprintf_unfiltered (gdb_stdlog, "[%04X]", insn & 0xffff); + else + fprintf_unfiltered (gdb_stdlog, "[%08X]", insn); + } /* The following instructions can appear in the prologue. */ @@ -954,6 +1414,42 @@ nios2_analyze_prologue (struct gdbarch *gdbarch, const CORE_ADDR start_pc, break; } + else if (nios2_match_stwm (insn, op, mach, + ®list, &ra, &imm, &wb, &id)) + { + /* PUSH.N {reglist}, adjust + or + STWM {reglist}, --(SP)[, writeback] */ + int i; + int off = 0; + + if (ra != NIOS2_SP_REGNUM || id != 0) + /* This is a non-stack-push memory write and cannot be + part of the prologue. */ + break; + + for (i = 31; i >= 0; i--) + if (reglist & (1 << i)) + { + int orig = value[i].reg; + + off += 4; + if (orig > 0 && value[i].offset == 0 && pc < current_pc) + { + cache->reg_saved[orig].basereg + = value[NIOS2_SP_REGNUM].reg; + cache->reg_saved[orig].addr + = value[NIOS2_SP_REGNUM].offset - off; + } + } + + if (wb) + value[NIOS2_SP_REGNUM].offset -= off; + value[NIOS2_SP_REGNUM].offset -= imm; + + prologue_end = pc; + } + else if (nios2_match_rdctl (insn, op, mach, &ra, &rc)) { /* RDCTL rC, ctlN @@ -1037,6 +1533,9 @@ nios2_analyze_prologue (struct gdbarch *gdbarch, const CORE_ADDR start_pc, else if (nios2_match_callr (insn, op, mach, &ra) || nios2_match_jmpr (insn, op, mach, &ra) || nios2_match_jmpi (insn, op, mach, &uimm) + || (nios2_match_ldwm (insn, op, mach, ®list, &ra, + &imm, &wb, &id, &ret) + && ret) || nios2_match_trap (insn, op, mach, &uimm) || nios2_match_break (insn, op, mach, &uimm)) break; @@ -1215,16 +1714,45 @@ nios2_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *bp_addr, enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch); unsigned long mach = gdbarch_bfd_arch_info (gdbarch)->mach; - /* R1 trap encoding: - ((0x1d << 17) | (0x2d << 11) | (0x1f << 6) | (0x3a << 0)) - 0x003b6ffa */ - static const gdb_byte r1_breakpoint_le[] = {0xfa, 0x6f, 0x3b, 0x0}; - static const gdb_byte r1_breakpoint_be[] = {0x0, 0x3b, 0x6f, 0xfa}; - *bp_size = NIOS2_OPCODE_SIZE; - if (byte_order_for_code == BFD_ENDIAN_BIG) - return r1_breakpoint_be; + if (mach == bfd_mach_nios2r2) + { + /* R2 trap encoding: + ((0x2d << 26) | (0x1f << 21) | (0x1d << 16) | (0x20 << 0)) + 0xb7fd0020 + CDX trap.n encoding: + ((0xd << 12) | (0x1f << 6) | (0x9 << 0)) + 0xd7c9 + Note that code is always little-endian on R2. */ + static const gdb_byte r2_breakpoint_le[] = {0x20, 0x00, 0xfd, 0xb7}; + static const gdb_byte cdx_breakpoint_le[] = {0xc9, 0xd7}; + unsigned int insn; + const struct nios2_opcode *op + = nios2_fetch_insn (gdbarch, *bp_addr, &insn); + + if (op && op->size == NIOS2_CDX_OPCODE_SIZE) + { + *bp_size = NIOS2_CDX_OPCODE_SIZE; + return cdx_breakpoint_le; + } + else + { + *bp_size = NIOS2_OPCODE_SIZE; + return r2_breakpoint_le; + } + } else - return r1_breakpoint_le; + { + /* R1 trap encoding: + ((0x1d << 17) | (0x2d << 11) | (0x1f << 6) | (0x3a << 0)) + 0x003b6ffa */ + static const gdb_byte r1_breakpoint_le[] = {0xfa, 0x6f, 0x3b, 0x0}; + static const gdb_byte r1_breakpoint_be[] = {0x0, 0x3b, 0x6f, 0xfa}; + *bp_size = NIOS2_OPCODE_SIZE; + if (byte_order_for_code == BFD_ENDIAN_BIG) + return r1_breakpoint_be; + else + return r1_breakpoint_le; + } } /* Implement the print_insn gdbarch method. */ @@ -1597,7 +2125,7 @@ nios2_get_next_pc (struct frame_info *frame, CORE_ADDR pc) int rb; int imm; unsigned int uimm; - int wb, ret; + int wb, id, ret; enum branch_condition cond; /* Do something stupid if we can't disassemble the insn at pc. */ @@ -1654,10 +2182,21 @@ nios2_get_next_pc (struct frame_info *frame, CORE_ADDR pc) || nios2_match_callr (insn, op, mach, &ra)) pc = get_frame_register_unsigned (frame, ra); - else if (nios2_match_trap (insn, op, mach, &uimm)) + else if (nios2_match_ldwm (insn, op, mach, &uimm, &ra, &imm, &wb, &id, &ret) + && ret) + { + /* If ra is in the reglist, we have to use the value saved in the + stack frame rather than the current value. */ + if (uimm & (1 << NIOS2_RA_REGNUM)) + pc = nios2_unwind_pc (gdbarch, frame); + else + pc = get_frame_register_unsigned (frame, NIOS2_RA_REGNUM); + } + + else if (nios2_match_trap (insn, op, mach, &uimm) && uimm == 0) { if (tdep->syscall_next_pc != NULL) - return tdep->syscall_next_pc (frame); + return tdep->syscall_next_pc (frame, op); } else diff --git a/gdb/nios2-tdep.h b/gdb/nios2-tdep.h index af36c4187e9..1a4e5ab3ff2 100644 --- a/gdb/nios2-tdep.h +++ b/gdb/nios2-tdep.h @@ -20,6 +20,9 @@ #ifndef NIOS2_TDEP_H #define NIOS2_TDEP_H +/* Nios II ISA specific encodings and macros. */ +#include "opcode/nios2.h" + /* Registers. */ #define NIOS2_Z_REGNUM 0 /* Zero */ #define NIOS2_R2_REGNUM 2 /* used for return value */ @@ -61,13 +64,15 @@ /* Size of an instruction, in bytes. */ #define NIOS2_OPCODE_SIZE 4 +#define NIOS2_CDX_OPCODE_SIZE 2 /* Target-dependent structure in gdbarch. */ struct gdbarch_tdep { /* Assumes FRAME is stopped at a syscall (trap) instruction; returns the expected next PC. */ - CORE_ADDR (*syscall_next_pc) (struct frame_info *frame); + CORE_ADDR (*syscall_next_pc) (struct frame_info *frame, + const struct nios2_opcode *op); /* Offset to PC value in jump buffer. If this is negative, longjmp support will be disabled. */