URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
[/] [or1k/] [trunk/] [insight/] [gdb/] [or1k-tdep.c] - Rev 119
Go to most recent revision | Compare with Previous | Blame | View Log
/* Target-dependent code for the or1k architecture, for GDB, the GNU Debugger. Copyright 1988-1999, Free Software Foundation, Inc. Contributed by Alessandro Forin(af@cs.cmu.edu at CMU and by Per Bothner(bothner@cs.wisc.edu) at U.Wisconsin. This file is part of GDB. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "demangle.h" #include "defs.h" #include "gdb_string.h" #include "frame.h" #include "inferior.h" #include "symtab.h" #include "value.h" #include "gdbcmd.h" #include "language.h" #include "gdbcore.h" #include "symfile.h" #include "objfiles.h" #include "gdbtypes.h" #include "target.h" #include "opcode/or32.h" /* *INDENT-OFF* */ /* Group reg name size. See or1k_reg_names. */ int or1k_group_name_sizes[OR1K_NUM_SPR_GROUPS] = { 80, 0, 0, 6, 4, 2, 23, 16, 1, 3, 2, 8}; int or1k_group_name_start[OR1K_NUM_SPR_GROUPS] = { 0, 0, 0, 253, 254, 256, 32, 248, 16, 16, 255, 0}; /* Generated reg names (max valid alias index). See or1k_spr_reg_name. */ int or1k_spr_valid_aliases[OR1K_NUM_SPR_GROUPS] = { 2047+1, 2047+1, 2047+1, 258+1, 257+1, 257+1, 79+1, 263+1, 16+1, 18+1, 256+1, 7+1}; /* Register names. */ char *or1k_reg_names[] = { /* group 0 - general*/ "VR", "UPR", "CPUCFGR", "DMMUCFGR", "IMMUCFGR", "DCCFGR", "ICCFGR", "DCFGR", "PCCFGR", "SPR0_9", "SPR0_10", "SPR0_11", "SPR0_12", "SPR0_13", "SPR0_14", "SPR0_15", "PC", "SR", "SPR0_18", "SPR0_19", "SPR0_20", "SPR0_21", "SPR0_22", "SPR0_23", "EPCR0", "EPCR1", "EPCR2", "EPCR3", "EPCR4", "EPCR5", "EPCR6", "EPCR7", "EPCR8", "EPCR9", "EPCR10", "EPCR11", "EPCR12", "EPCR13", "EPCR14", "EPCR15", "EEAR0","EEAR1", "EEAR2", "EEAR3", "EEAR4", "EEAR5", "EEAR6", "EEAR7", "EEAR8", "EEAR9", "EEAR10", "EEAR11", "EEAR12", "EEAR13", "EEAR14", "EEAR15", "ESR0", "ESR1", "ESR2", "ESR3", "ESR4", "ESR5", "ESR6", "ESR7", "ESR8", "ESR9", "ESR10", "ESR11", "ESR12", "ESR13", "ESR14", "ESR15", /* gpr+vf registers generated */ /* group 1 - Data MMU - not listed, generated */ /* group 2 - Instruction MMU - not listed, generated */ /* group 3 - Data cache */ "DCCR", "DCBIR", "DCBPR", "DCBFR", "DCBWR", "DCBLR", /* group 4 - Instruction cache */ "ICCR", "ICBLR", "ICBIR", "ICBPR", /* group 5 - MAC */ "MACLO", "MACHI", /* group 6 - debug */ "DVR0", "DVR1", "DVR2", "DVR3", "DVR4", "DVR5", "DVR6", "DVR7", "DCR0", "DCR1", "DCR2", "DCR3", "DCR4", "DCR5", "DCR6", "DCR7", "DMR1", "DMR2", "DCWR0","DCWR1","DSR", "DRR", "DIR", /* group 7 - performance counters unit */ "PCCM0", "PCMR1", "PCMR2", "PCMR3", "PCMR4", "PCMR5", "PCMR6", "PCMR7", "PCCR0", "PCCR1", "PCCR2", "PCCR3", "PCCR4", "PCCR5", "PCCR6", "PCCR7", /* group 8 - power management */ "PMR", /* group 9 - PIC */ "PICMR", "PICPR", /* group 10 - tick timer */ "TTMR", "TTCR", /* group 11 - configureation */ "CPUCFGR", "DMMUCFGR", "IMMUCFGR", "DCCFGR", "ICCFGR", "SPR11_5", "DCFGR", "PCCFGR" }; static char *or1k_gdb_reg_names[] = { /* general purpose registers */ "ZERO", "SP", "FP", "A0", "A1", "A2", "A3", "A4", "A5", "LR", "R10", "RV", "R12", "R13", "R14", "R15", "R16", "R17", "R18", "R19", "R20", "R21", "R22", "R23", "R24", "R25", "R26", "R27", "R28", "R29", "R30", "R31", /* vector/floating point registers */ "VFA0", "VFA1", "VFA2", "VFA3", "VFA4", "VFA5", "VFRV ", "VFR7", "VFR8", "VFR9", "VFR10", "VFR11", "VFR12", "VFR13", "VFR14", "VFR15", "VFR16", "VFR17", "VFR18", "VFR19", "VFR20", "VFR21", "VFR22", "VFR23", "VFR24", "VFR25", "VFR26", "VFR27", "VFR28", "VFR29", "VFR30", "VFR31", "PC", "SR", "EPCR" }; static char *or1k_group_names[] = { "SYS", "DMMU", "IMMU", "DCACHE", "ICACHE", "MAC", "DEBUG", "PERF", "POWER", "PIC", "TIMER" }; /* Table of or1k signals. */ static struct { char *name; char *string; } or1k_signals [NUM_OR1K_SIGNALS + 1] = { {"RSTE", "Reset Exception"}, {"DFPE", "Data Page Fault Exception"}, {"IFPE", "Instruction Page Fault Exception"}, {"LPINTE", "Low Priority Interrupt Exception"}, {"AE", "Alignment Exception"}, {"HPINTE", "High Priority Interrupt Exception"}, {"DME", "DTLB Miss Exception"}, {"IME", "ITLB Miss Exception"}, {"RE", "Range Exception"}, {"SCE", "SCE Exception"}, {NULL, NULL} }; const char *compare_to_names[NUM_CT_NAMES] = { "DISABLED", "FETCH", "LEA", "SEA", "LDATA", "SDATA", "AEA", "ADATA" }; const char *or1k_record_names[MAX_RECORD_NAMES] = { "PC", "LSEA", "LDATA", "SDATA", "READSPR", "WRITESPR", "INSTR" }; const char *or1k_is_names[MAX_IS_NAMES] = { "IF_NONE", "IF_NORMAL", "IF_BRANCH", "IF_DELAY" }; const char *or1k_ls_names[MAX_LS_NAMES] = { "LS_NONE", " ", "LBZ", "LBS", "LHZ", "LHS", "LWZ", "LWS", " ", " ", "SB", " ", "SH", " ", "SW", " " }; /* *INDENT-ON* */ /* The list of available "set or1k " and "show or1k " commands */ static struct cmd_list_element *htrace_cmdlist = NULL; static struct cmd_list_element *htrace_mode_cmdlist = NULL; /* List of all saved register addresses, produced by skip_prologue. Relative address to sp, not used if 0. */ static int or1k_saved_reg_addr[NUM_REGS]; struct htrace_struct or1k_htrace; struct hwatch_struct or1k_hwatch[MAX_HW_WATCHES]; int num_hw_watches = 0; /* Trace data. */ char trace_filename[TRACE_FILENAME_SIZE] = "trace.dat"; /* Size of trace file in records. */ int trace_size = 0; /* Previous values for buffer display. */ static int prev_length = 10; static int prev_from = 0; /* Converts regno to sprno. or1k debug unit has GPRs mapped to SPRs, which are not compact, so we are mapping them for GDB. */ int or1k_regnum_to_sprnum (int regno) { if (regno < MAX_GPR_REGS) return SPR_REG(SPR_SYSTEM_GROUP, regno + CURRENT_CID * MAX_GPR_REGS + SPR_GPR_START); if (regno < MAX_GPR_REGS + MAX_VF_REGS) return SPR_REG(SPR_SYSTEM_GROUP, regno - MAX_GPR_REGS + CURRENT_CID * MAX_GPR_REGS + SPR_VFPR_START); if (regno == PS_REGNUM) return SR_SPRNUM; if (regno == PC_REGNUM) return PC_SPRNUM; if (regno == CCR_REGNUM) return CCR_SPRNUM(CURRENT_CID); error ("Invalid register number!"); } /* Builds and returns register name. */ static char tmp_name[16]; static char * or1k_spr_register_name (i) int i; { int group = i >> SPR_GROUP_SIZE_BITS; int index = i & (SPR_GROUP_SIZE - 1); switch (group) { /* Names covered in or1k_reg_names. */ case 0: /* Generate upper names. */ if (index >= SPR_GPR_START) { if (index < SPR_VFPR_START) sprintf (tmp_name, "GPR%i", index - SPR_GPR_START); else sprintf (tmp_name, "VFR%i", index - SPR_VFPR_START); return (char *)&tmp_name; } case 3: case 4: case 5: case 6: case 7: case 8: case 9: case 10: { int group_start = 0; for (i = 0; i < group; i++) group_start += or1k_group_name_sizes[i]; if (index >= or1k_group_name_sizes[group]) { sprintf (tmp_name, "SPR%i_%i", group, index); return (char *)&tmp_name; } else return or1k_reg_names[group_start + index - or1k_group_name_start[group]]; } /* Build names for DMMU group. */ case 1: case 2: strcpy (tmp_name, (group == 1)?"D":"I"); switch (index) { case 16: return strcat (tmp_name, "MMUCR"); case 17: return strcat (tmp_name, "MMUPR"); case 18: return strcat (tmp_name, "TLBEIR"); case 24: return strcat (tmp_name, "ATBMR0"); case 25: return strcat (tmp_name, "ATBMR1"); case 26: return strcat (tmp_name, "ATBMR2"); case 27: return strcat (tmp_name, "ATBMR3"); case 28: return strcat (tmp_name, "ATBTR0"); case 29: return strcat (tmp_name, "ATBTR0"); case 30: return strcat (tmp_name, "ATBTR0"); case 31: return strcat (tmp_name, "ATBTR0"); default: if (index >= 1024) { index -= 1024; sprintf (tmp_name, "%sTLBW%iMR%i", (group == 1)?"D":"I", index / 128, index % 128); return (char *)&tmp_name; } sprintf (tmp_name, "SPR%i_%i", group, index); return (char *)&tmp_name; } default: sprintf (tmp_name, "SPR%i_%i", group, index); return (char *)&tmp_name; } } /* Get register index in group from name. Negative if no match. */ static int or1k_regno_from_name (group, name) int group; char *name; { int i; if (toupper(name[0]) == 'S' && toupper(name[1]) == 'P' && toupper(name[2]) == 'R') { char *ptr_c; name += 3; i = (int) strtoul (name, &ptr_c, 10); if (*ptr_c != '_' || i != group) return -1; ptr_c++; i = (int) strtoul (name, &ptr_c, 10); if (*ptr_c) return -1; else return i; } for (i = 0; i < or1k_spr_valid_aliases[group]; i++) { char *s; s = or1k_spr_register_name (SPR_REG(group, i)); if (strcasecmp (name, s) == 0) return i; } return -1; } /* Returns gdb register name. */ char * or1k_register_name (regno) int regno; { return or1k_gdb_reg_names[regno]; } /* Utility function to display vf regs. */ static int do_vf_register (regnum) int regnum; { /* do values for FP (float) regs */ char *raw_buffer; /* doubles extracted from raw hex data */ double doub, flt; int inv1, inv3, byte; raw_buffer = (char *) alloca (OR1K_VF_REGSIZE); /* Get the data in raw format. */ if (read_relative_register_raw_bytes (regnum, raw_buffer)) error ("can't read register %d (%s)", regnum, REGISTER_NAME (regnum)); flt = unpack_double (builtin_type_float, raw_buffer, &inv1); doub = unpack_double (builtin_type_double, raw_buffer, &inv3); if (inv1) printf_filtered (" %-5s flt: <invalid float>", REGISTER_NAME (regnum)); else printf_filtered (" %-5s flt:%-17.9g", REGISTER_NAME (regnum), flt); printf_filtered (inv3 ? " dbl: <invalid double> " : " dbl: %-24.17g ", doub); /* pad small registers */ for (byte = 0; byte < (OR1K_GPR_REGSIZE - REGISTER_VIRTUAL_SIZE (regnum)); byte++) printf_filtered (" "); /* Now print the register value in hex, endian order. */ if (TARGET_BYTE_ORDER == BIG_ENDIAN) for (byte = REGISTER_RAW_SIZE (regnum) - REGISTER_VIRTUAL_SIZE (regnum); byte < REGISTER_RAW_SIZE (regnum); byte++) printf_filtered ("%02x", (unsigned char) raw_buffer[byte]); else for (byte = REGISTER_VIRTUAL_SIZE (regnum) - 1; byte >= 0; byte--) printf_filtered ("%02x", (unsigned char) raw_buffer[byte]); printf_filtered ("\n"); regnum++; return regnum; } /* Print a row's worth of GP (int) registers, with name labels above */ static int do_gp_register_row (regnum) int regnum; { /* do values for GP (int) regs */ char raw_buffer[MAX_REGISTER_RAW_SIZE]; /* display cols per row */ int ncols = (OR1K_64BIT_IMPLEMENTATION ? 4 : 8); int col, byte; int start_regnum = regnum; int numregs = NUM_REGS; /* For GP registers, we print a separate row of names above the vals */ printf_filtered (" "); for (col = 0; col < ncols && regnum < numregs; regnum++) { if (*REGISTER_NAME (regnum) == '\0') continue; /* unused register */ if (OR1K_IS_VF(regnum)) break; /* end the row: reached VF register */ printf_filtered (OR1K_64BIT_IMPLEMENTATION ? "%17s" : "%9s", REGISTER_NAME (regnum)); col++; } printf_filtered ("\n "); regnum = start_regnum; /* go back to start of row */ /* now print the values in hex, 4 or 8 to the row */ for (col = 0; col < ncols && regnum < numregs; regnum++) { /* unused register? */ if (*REGISTER_NAME (regnum) == '\0') continue; /* end row: reached VF register? */ if (OR1K_IS_VF(regnum)) break; /* OK: get the data in raw format. */ if (read_relative_register_raw_bytes (regnum, raw_buffer)) error ("can't read register %d (%s)", regnum, REGISTER_NAME (regnum)); /* pad small registers */ for (byte = 0; byte < (OR1K_GPR_REGSIZE - REGISTER_VIRTUAL_SIZE (regnum)); byte++) printf_filtered (" "); /* Now print the register value in hex, endian order. */ if (TARGET_BYTE_ORDER == BIG_ENDIAN) for (byte = REGISTER_RAW_SIZE (regnum) - REGISTER_VIRTUAL_SIZE (regnum); byte < REGISTER_RAW_SIZE (regnum); byte++) printf_filtered ("%02x", (unsigned char) raw_buffer[byte]); else for (byte = REGISTER_VIRTUAL_SIZE (regnum) - 1; byte >= 0; byte--) printf_filtered ("%02x", (unsigned char) raw_buffer[byte]); printf_filtered (" "); col++; } /* ie. if we actually printed anything... */ if (col > 0) printf_filtered ("\n"); return regnum; } /* Replacement for generic do_registers_info. Print regs in pretty columns. */ static void print_register (regnum, all) int regnum, all; { int offset; char raw_buffer[MAX_REGISTER_RAW_SIZE]; /* Get the data in raw format. */ if (read_relative_register_raw_bytes (regnum, raw_buffer)) { printf_filtered ("%s: [Invalid]", REGISTER_NAME (regnum)); return; } /* If virtual format is floating, print it that way. */ if (OR1K_IS_VF (regnum)) do_vf_register (regnum); else { int byte; printf_filtered ("%-16s\t", REGISTER_NAME (regnum)); /* pad small registers */ for (byte = 0; byte < (OR1K_GPR_REGSIZE - REGISTER_VIRTUAL_SIZE (regnum)); byte++) printf_filtered (" "); /* Now print the register value in hex, endian order. */ if (TARGET_BYTE_ORDER == BIG_ENDIAN) for (byte = REGISTER_RAW_SIZE (regnum) - REGISTER_VIRTUAL_SIZE (regnum); byte < REGISTER_RAW_SIZE (regnum); byte++) printf_filtered ("%02x", (unsigned char) raw_buffer[byte]); else for (byte = REGISTER_VIRTUAL_SIZE (regnum) - 1; byte >= 0; byte--) printf_filtered ("%02x", (unsigned char) raw_buffer[byte]); printf_filtered (" "); } } /* DO_REGISTERS_INFO: called by "info register" command */ void or1k_do_registers_info (regnum, fpregs) int regnum; int fpregs; { if (fpregs && !or1k_implementation.vf_present) { warning ("VF register set not present in this implementation."); fpregs = 0; } if (regnum != -1) { /* do one specified register */ if (*(REGISTER_NAME (regnum)) == '\0') error ("Not a valid register for the current processor type"); print_register (regnum, 0); printf_filtered ("\n"); } else /* do all (or most) registers */ { regnum = 0; while (regnum < NUM_REGS) { if (OR1K_IS_VF (regnum)) /* true for "INFO ALL-REGISTERS" command */ if (fpregs) /* FP regs */ regnum = do_vf_register (regnum); else /* skip floating point regs */ regnum++; else /* GP (int) regs */ regnum = do_gp_register_row (regnum); } } } /* Given the address at which to insert a breakpoint (BP_ADDR), what will that breakpoint be? For or1k, we have a breakpoint instruction. Since all or1k instructions are 32 bits, this is all we need, regardless of address. K is not used. */ unsigned char * or1k_breakpoint_from_pc (bp_addr, bp_size) CORE_ADDR * bp_addr; int *bp_size; { static char breakpoint[] = BRK_INSTR_STRUCT; *bp_size = OR1K_INSTLEN; return breakpoint; } /* Return the string for a signal. Replacement for target_signal_to_string (sig). NOT USED. */ char *or1k_signal_to_string (sig) enum target_signal sig; { if ((sig >= TARGET_SIGNAL_FIRST) && (sig <= TARGET_SIGNAL_LAST)) return or1k_signals[sig].string; else if (sig <= TARGET_SIGNAL_LAST + NUM_OR1K_SIGNALS) return or1k_signals[sig - TARGET_SIGNAL_LAST].string; else return 0; } /* Return the name for a signal. */ char * or1k_signal_to_name (sig) enum target_signal sig; { if (sig >= TARGET_SIGNAL_LAST) if (sig <= TARGET_SIGNAL_LAST + NUM_OR1K_SIGNALS) return or1k_signals[sig - TARGET_SIGNAL_LAST].name; else /* I think the code which prints this will always print it along with the string, so no need to be verbose. */ return "?"; if (sig == TARGET_SIGNAL_UNKNOWN) return "?"; return 0; } /* Given a name, return its signal. NOT USED. */ enum target_signal or1k_signal_from_name (name) char *name; { enum target_signal sig; /* It's possible we also should allow "SIGCLD" as well as "SIGCHLD" for TARGET_SIGNAL_SIGCHLD. SIGIOT, on the other hand, is more questionable; seems like by now people should call it SIGABRT instead. */ /* This ugly cast brought to you by the native VAX compiler. */ for (sig = TARGET_SIGNAL_FIRST; or1k_signal_to_name (sig) != NULL; sig = (enum target_signal) ((int) sig + 1)) if (STREQ (name, or1k_signal_to_name (sig))) return sig; return TARGET_SIGNAL_UNKNOWN; } /* Given a return value in `regbuf' with a type `valtype', extract and copy its value into `valbuf'. */ void or1k_extract_return_value (valtype, regbuf, valbuf) struct type *valtype; char regbuf[REGISTER_BYTES]; char *valbuf; { if (TYPE_CODE_FLT == TYPE_CODE (valtype)) memcpy (valbuf, ®buf[REGISTER_BYTE (VFRV_REGNUM)], TYPE_LENGTH (valtype)); else memcpy (valbuf, ®buf[REGISTER_BYTE (RV_REGNUM)], TYPE_LENGTH (valtype)); } /* The or1k cc defines the following prologue: 00000000 <_proc1>: 0: d7 e1 17 e4 l.sw 0xffffffe4(r1),r2 4: 9c 41 00 00 l.addi r2,r1,0x0 8: 9c 21 ff e8 l.addi r1,r1,0xffffffe8 c: d7 e2 1f f8 l.sw 0xfffffff8(r2),r3 10: d7 e2 27 f4 l.sw 0xfffffff4(r2),r4 14: 84 82 ff f8 l.lwz r4,0xfffffff8(r2) 18: 9d 24 00 00 l.addi r9,r4,0x0 1c: 00 00 00 02 l.j 0x2 20: 15 00 00 00 l.nop 00000024 <_L2>: 24: 84 41 ff fc l.lwz r2,0xfffffffc(r1) 28: 48 00 58 00 l.jalr r11 2c: 9c 21 00 18 l.addi r1,r1,0x18 */ CORE_ADDR or1k_skip_prologue (CORE_ADDR pc) { unsigned long inst; CORE_ADDR skip_pc; CORE_ADDR func_addr, func_end; struct symtab_and_line sal; int i; int offset = 0; for (i = 0; i < MAX_GPR_REGS; i++) or1k_saved_reg_addr[i] = -1; /* Is there a prologue? */ inst = or1k_fetch_instruction (pc); if (inst & 0xfc1ff800 != 0xd4011000) return pc; /* l.sw I(r1),r2 */ or1k_saved_reg_addr[2] = offset++; inst = or1k_fetch_instruction (pc + OR1K_INSTLEN); if (inst & 0xFFFF0000 != 0x9c410000) return pc; /* l.addi r2,r1,I */ pc += 2 * OR1K_INSTLEN; inst = or1k_fetch_instruction (pc); if (inst & 0xFFFF0000 != 0x9c210000) return pc; /* l.addi r1,r1,I */ pc += OR1K_INSTLEN; /* Skip stored registers. */ inst = or1k_fetch_instruction (pc); while (inst & 0xfc1ff800 != 0xd4020000) /* l.sw 0x0(r2),rx */ { /* get saved reg. */ or1k_saved_reg_addr[(inst >> 11) & 0x1f] = offset++; pc += OR1K_INSTLEN; inst = or1k_fetch_instruction (pc); } return pc; } /* Determines whether this function has frame. */ int or1k_frameless_function_invocation (struct frame_info *fi) { CORE_ADDR func_start, after_prologue; int frameless; func_start = (get_pc_function_start ((fi)->pc) + FUNCTION_START_OFFSET); after_prologue = SKIP_PROLOGUE (func_start); /* If we don't skip pc, we don't have even shortest possible prologue. */ frameless = (after_prologue <= func_start); return frameless; } /* Given a GDB frame, determine the address of the calling function's frame. This will be used to create a new GDB frame struct, and then INIT_EXTRA_FRAME_INFO and INIT_FRAME_PC will be called for the new frame. */ CORE_ADDR or1k_frame_chain (frame) struct frame_info *frame; { CORE_ADDR fp; if (USE_GENERIC_DUMMY_FRAMES) { if (PC_IN_CALL_DUMMY (frame->pc, frame->frame, frame->frame)) /* dummy frame same as caller's frame */ return frame->frame; } if (inside_entry_file (frame->pc) || frame->pc == entry_point_address ()) return 0; if (frame->signal_handler_caller) fp = read_memory_integer (frame->frame, 4); else if (frame->next != NULL && frame->next->signal_handler_caller && FRAMELESS_FUNCTION_INVOCATION (frame)) /* A frameless function interrupted by a signal did not change the frame pointer. */ fp = FRAME_FP (frame); else fp = read_memory_integer (frame->frame, 4); if (USE_GENERIC_DUMMY_FRAMES) { CORE_ADDR fpp, lr; lr = read_register (LR_REGNUM); if (lr == entry_point_address ()) if (fp != 0 && (fpp = read_memory_integer (fp, 4)) != 0) if (PC_IN_CALL_DUMMY (lr, fpp, fpp)) return fpp; } return fp; } /* The code to store, into a struct frame_saved_regs, the addresses of the saved registers of frame described by FRAME_INFO. This includes special registers such as pc and fp saved in special ways in the stack frame. sp is even more special: the address we return for it IS the sp for the next frame. */ void or1k_init_saved_regs (struct frame_info *fi) { int i; CORE_ADDR frame_addr; frame_saved_regs_zalloc (fi); /* Skip prologue sets or1k_saved_reg_addr[], we will use it later. */ or1k_skip_prologue (get_pc_function_start ((fi)->pc) + FUNCTION_START_OFFSET); for (i = 0; i < NUM_GPR_REGS + NUM_VF_REGS; i++) if (or1k_saved_reg_addr[i] >= 0) fi->saved_regs[i] = fi->frame + or1k_saved_reg_addr[i]; } static CORE_ADDR read_next_frame_reg (fi, regno) struct frame_info *fi; int regno; { for (; fi; fi = fi->next) { /* We have to get the saved sp from the sigcontext if it is a signal handler frame. */ if (regno == SP_REGNUM && !fi->signal_handler_caller) return fi->frame; else { if (fi->saved_regs == NULL) or1k_init_saved_regs (fi); if (fi->saved_regs[regno]) return read_memory_integer (ADDR_BITS_REMOVE (fi->saved_regs[regno]), OR1K_GPR_REGSIZE); } } return read_register (regno); } /* Find the caller of this frame. We do this by seeing if LR_REGNUM is saved in the stack anywhere, otherwise we get it from the registers. */ CORE_ADDR or1k_frame_saved_pc (fi) struct frame_info *fi; { CORE_ADDR saved_pc; /* We have to get the saved pc from the sigcontext if it is a signal handler frame. */ if (PC_IN_CALL_DUMMY (fi->pc, fi->frame, fi->frame)) saved_pc = read_memory_integer (fi->frame, OR1K_GPR_REGSIZE); else saved_pc = read_next_frame_reg (fi, PC_REGNUM); return ADDR_BITS_REMOVE (saved_pc); } /* Discard from the stack the innermost frame, restoring all registers. */ void or1k_pop_frame () { register int regnum; struct frame_info *frame = get_current_frame (); CORE_ADDR new_sp = FRAME_FP (frame); write_register (PC_REGNUM, FRAME_SAVED_PC (frame)); if (frame->saved_regs == NULL) or1k_init_saved_regs (frame); for (regnum = 0; regnum < NUM_REGS; regnum++) { if (regnum != SP_REGNUM && regnum != PC_REGNUM && frame->saved_regs[regnum] >= 0) write_register (regnum, read_memory_integer (frame->saved_regs[regnum], OR1K_GPR_REGSIZE)); } write_register (SP_REGNUM, new_sp); flush_cached_frames (); } CORE_ADDR or1k_push_arguments (nargs, args, sp, struct_return, struct_addr) int nargs; value_ptr *args; CORE_ADDR sp; int struct_return; CORE_ADDR struct_addr; { int argreg; int float_argreg; int argnum; int len = 0; int stack_offset = 0; /* Initialize the integer and float register pointers. */ argreg = A0_REGNUM; float_argreg = VFA0_REGNUM; /* The struct_return pointer occupies the RV value register. */ if (struct_return) write_register (RV_REGNUM, struct_addr); /* Now load as many as possible of the first arguments into registers, and push the rest onto the stack. Loop thru args from first to last. */ for (argnum = 0; argnum < nargs; argnum++) { char *val; char valbuf[MAX_REGISTER_RAW_SIZE]; value_ptr arg = args[argnum]; struct type *arg_type = check_typedef (VALUE_TYPE (arg)); int len = TYPE_LENGTH (arg_type); enum type_code typecode = TYPE_CODE (arg_type); /* The EABI passes structures that do not fit in a register by reference. In all other cases, pass the structure by value. */ if (len > OR1K_GPR_REGSIZE && (typecode == TYPE_CODE_STRUCT || typecode == TYPE_CODE_UNION)) { store_address (valbuf, OR1K_GPR_REGSIZE, VALUE_ADDRESS (arg)); typecode = TYPE_CODE_PTR; len = OR1K_GPR_REGSIZE; val = valbuf; } else { val = (char *) VALUE_CONTENTS (arg); if (typecode == TYPE_CODE_FLT /* Doubles are not stored in regs on 32b target. */ && len <= OR1K_VF_REGSIZE && OR1K_VF_PRESENT) { if (float_argreg <= OR1K_LAST_FP_ARG_REGNUM) { CORE_ADDR regval = extract_address (val, len); write_register (float_argreg++, regval); } else { write_memory ((CORE_ADDR) sp, val, OR1K_VF_REGSIZE); sp -= OR1K_STACK_ALIGN; } } else { if (argreg <= OR1K_LAST_ARG_REGNUM) { CORE_ADDR regval = extract_address (val, len); write_register (argreg++, regval); } else { write_memory ((CORE_ADDR) sp, val, OR1K_GPR_REGSIZE); sp -= OR1K_STACK_ALIGN; } } } } /* Return adjusted stack pointer. */ return sp; } /* Return nonzero when instruction has delay slot. */ static int is_delayed (insn) unsigned long insn; { int i; for (i = 0; i < num_opcodes; ++i) if ((or32_opcodes[i].flags & OR32_IF_DELAY) && (or32_opcode_match (insn, or32_opcodes[i].encoding))) break; return (i < num_opcodes); } int or1k_step_skips_delay (pc) CORE_ADDR pc; { char buf[OR1K_INSTLEN]; if (target_read_memory (pc, buf, OR1K_INSTLEN) != 0) /* If error reading memory, guess that it is not a delayed branch. */ return 0; return is_delayed ((unsigned long) extract_unsigned_integer (buf, OR1K_INSTLEN)); } CORE_ADDR or1k_push_return_address (pc, sp) CORE_ADDR pc; CORE_ADDR sp; { /* Set the return address register to point to the entry point of the program, where a breakpoint lies in wait. */ write_register (LR_REGNUM, CALL_DUMMY_ADDRESS ()); return sp; } /* Parses args for spr name and puts result into group and index. */ static char * parse_spr_params (args, group, index) char *args; int *group, *index; { *index = -1; if (args) { int i; char *ptr_c; /* Check if group number was supplied. */ ptr_c = args; while (*ptr_c != ' ' && *ptr_c != 0) ptr_c++; *ptr_c = 0; *group = (int) strtoul (args, &ptr_c, 0); if (*ptr_c != 0) { *group = OR1K_NUM_SPR_GROUPS; /* check for group name */ for (i = 0; i < OR1K_NUM_SPR_GROUPS; i++) if (strcasecmp (or1k_group_names[i], args) == 0) { *group = i; break; } /* Invalid group => check all register names in all groups. */ if (*group >= OR1K_NUM_SPR_GROUPS) { for (i = 0; i < OR1K_NUM_SPR_GROUPS; i++) { int regno; regno = or1k_regno_from_name (i, args); if (regno >= 0) { *group = i; *index = regno; } } } } if (*group < 0 || *group >= OR1K_NUM_SPR_GROUPS) error ("Invalid group or register.\n"); if (*index < 0) { args += strlen(args) + 1; ptr_c = args; while (*ptr_c != ' ' && *ptr_c != 0) ptr_c++; *ptr_c = 0; *index = (int) strtoul (args, &ptr_c, 0); if (*ptr_c != 0) *index = or1k_regno_from_name (*group, args); else *index = -1; if (*index < 0) { printf_filtered ("No register supplied. Valid registers are:\n"); for (i = 0; i < or1k_spr_valid_aliases[*group]; i++) { char reg_name[16]; char *gen_name = or1k_spr_register_name (SPR_REG(*group, i)); sprintf (reg_name, "SPR%i_%i", *group, i); if (strcmp (reg_name, gen_name) != 0) printf_filtered ("%s\t", gen_name); } printf_filtered ("\n"); return args + strlen(args) + 1; } } } else { /* No parameters - print groups */ int i; printf_filtered ("No parameter supplied. Valid groups are:\n"); for (i = 0; i < OR1K_NUM_SPR_GROUPS; i++) printf_filtered ("%s\t", or1k_group_names[i]); printf_filtered ("\nSingle register name or register name or number after the group can be also supplied.\n"); return args; } return args + strlen(args) + 1; } /* SPR register info. */ void info_spr_command (args, from_tty) char *args; int from_tty; { int group, index; parse_spr_params (args, &group, &index); if (index >= 0) { printf_unfiltered ("%s.%s (SPR%i_%i) set to %i(%X), was:%i(%X)\n", or1k_group_names[group], or1k_spr_register_name (SPR_REG(group, index)), group, index, or1k_read_spr_reg (SPR_REG(group, index))); } } /* Set SPR register. */ void spr_command (args, from_tty) char *args; int from_tty; { int group, index; char *nargs = parse_spr_params (args, &group, &index); if (index >= 0) { unsigned long prev; unsigned long value; char *ptr_c; /* Any arguments left? */ if (args + strlen(args) >= nargs) error ("Invalid register value."); prev = or1k_read_spr_reg (SPR_REG(group, index)); ptr_c = nargs; while (*ptr_c != ' ' && *ptr_c != 0) ptr_c++; *ptr_c = 0; value = strtoul (nargs, &ptr_c, 0); if (*ptr_c != 0) error ("Invalid register value."); printf_unfiltered ("%s.%s (SPR%i_%i) set to %i(%X), was:%i(%X)\n", or1k_group_names[group], or1k_spr_register_name (SPR_REG(group, index)), group, index, value, value, prev, prev); } } /* Calls extended command on target. */ void sim_command (args, from_tty) char *args; int from_tty; { or1k_sim_cmd (args, from_tty); } static union exp_element exp_error; /* Parses compare variable and returns it into ct. */ union exp_element * or1k_parse_ct (exp, ct) union exp_element *exp; int *ct; { int i; if (exp->opcode != OP_INTERNALVAR) error ("Valid lvalue expected."); exp++; for (i = 1; i < NUM_CT_NAMES; i++) if (strcasecmp (compare_to_names[i], exp->internalvar->name) == 0) break; if (i >= NUM_CT_NAMES) error ("Invalid compare to operand."); *ct = i; exp++; if (exp->opcode != OP_INTERNALVAR) return &exp_error; exp++; return exp; } /* Parses compare value and returns it into cv. */ union exp_element * or1k_parse_cv (exp, cv) union exp_element *exp; unsigned int *cv; { switch (exp->opcode) { case UNOP_IND: exp++; exp = or1k_parse_cv (exp, cv); *cv = or1k_fetch_word (*cv); break; case OP_LONG: exp += 2; *cv = exp->longconst; exp += 2; break; case OP_REGISTER: exp++; *cv = read_register (exp->longconst); exp += 2; break; default: error ("Value expected."); } return exp; } /* Parse conditional. Puts freshly allocated array of matchpoints into match. */ union exp_element * or1k_parse_cond (exp, match, nmatch) union exp_element *exp; struct matchpoint **match; int *nmatch; { unsigned int ct; *match = (struct matchpoint *) malloc (sizeof (struct matchpoint)); *nmatch = 1; switch (exp->opcode) { case BINOP_EQUAL: (*match)->dcr.cc = CC_EQUAL; break; case BINOP_NOTEQUAL: (*match)->dcr.cc = CC_NEQUAL; break; case BINOP_LESS: (*match)->dcr.cc = CC_LESS; break; case BINOP_GTR: (*match)->dcr.cc = CC_GREAT; break; case BINOP_LEQ: (*match)->dcr.cc = CC_LESSE; break; case BINOP_GEQ: (*match)->dcr.cc = CC_GREATE; break; case BINOP_BITWISE_AND: (*match)->dcr.cc = CC_MASKED; break; default: return &exp_error; } exp++; (*match)->dcr.dp = 1; (*match)->dcr.sc = 0; if (exp->opcode == OP_INTERNALVAR) { exp = or1k_parse_ct (exp, &ct); exp = or1k_parse_cv (exp, &(*match)->dvr); } else { exp = or1k_parse_cv (exp, &(*match)->dvr); exp = or1k_parse_ct (exp, &ct); } (*match)->dcr.ct = ct; (*match)->chain_type = CHAINING_NONE; (*match)->cause_breakpoint = 0; return exp; } /* Parses expression with && or || operators. Puts freshly allocated array of matchpoints into match. valid & 1: && is allowed, valid & 2: || is allowed. */ union exp_element * or1k_parse_any (exp, match, nmatch, valid) union exp_element *exp; struct matchpoint **match; int *nmatch; int valid; { union exp_element *tmp; int first_and_only = 0, first_or_only = 0; struct matchpoint *tmp_match1, *tmp_match2, *tmpm; int tmp_nmatch1, tmp_nmatch2, tmpn; switch (exp->opcode) { case BINOP_LOGICAL_AND: if (!(valid & 1)) return &exp_error; exp++; /* Parse first argument. */ tmp = or1k_parse_any (exp, &tmp_match1, &tmp_nmatch1, 1); if (tmp == &exp_error) exp = or1k_parse_any (exp, &tmp_match1, &tmp_nmatch1, valid); else { /* and_only successful */ exp = tmp; first_and_only = 1; } if (exp == &exp_error) return &exp_error; /* Parse second argument. */ if (first_and_only) exp = or1k_parse_any (exp, &tmp_match2, &tmp_nmatch2, valid); else exp = or1k_parse_any (exp, &tmp_match2, &tmp_nmatch2, 1); if (exp == &exp_error) return &exp_error; if (first_and_only) { /* Exchange structures, so that and_only is listed last. */ struct matchpoint *tmpm = tmp_match1; int tmpn = tmp_nmatch1; tmp_match1 = tmp_match2; tmp_nmatch1 = tmp_nmatch2; tmp_match2 = tmpm; tmp_nmatch2 = tmpn; } *nmatch = tmp_nmatch1 + tmp_nmatch2; *match = (struct matchpoint *)malloc (*nmatch * sizeof (struct matchpoint)); memcpy (*match, tmp_match1, tmp_nmatch1 * sizeof (struct matchpoint)); free (tmp_match1); tmp_match2[0].chain_type = CHAINING_AND; memcpy (*match + tmp_nmatch1, tmp_match2, tmp_nmatch2 * sizeof (struct matchpoint)); free (tmp_match2); return exp; case BINOP_LOGICAL_OR: if (!(valid & 2)) return &exp_error; exp++; /* Parse first argument. */ tmp = or1k_parse_any (exp, &tmp_match1, &tmp_nmatch1, 2); if (tmp == &exp_error) exp = or1k_parse_any (exp, &tmp_match1, &tmp_nmatch1, valid); else { /* and_only successful */ exp = tmp; first_or_only = 1; } if (exp == &exp_error) return &exp_error; /* Parse second argument. */ if (first_or_only) exp = or1k_parse_any (exp, &tmp_match2, &tmp_nmatch2, valid); else exp = or1k_parse_any (exp, &tmp_match2, &tmp_nmatch2, 2); if (exp == &exp_error) return &exp_error; if (first_or_only) { /* Exchange structures, so that and_only is listed first. */ struct matchpoint *tmpm = tmp_match1; int tmpn = tmp_nmatch1; tmp_match1 = tmp_match2; tmp_nmatch1 = tmp_nmatch2; tmp_match2 = tmpm; tmp_nmatch2 = tmpn; } *nmatch = tmp_nmatch1 + tmp_nmatch2; *match = (struct matchpoint *)malloc (*nmatch * sizeof (struct matchpoint)); memcpy (*match, tmp_match1, tmp_nmatch1 * sizeof (struct matchpoint)); free (tmp_match1); tmp_match2[0].chain_type = CHAINING_OR; memcpy (*match + tmp_nmatch1, tmp_match2, tmp_nmatch2 * sizeof (struct matchpoint)); free (tmp_match2); return exp; default: return or1k_parse_cond (exp, match, nmatch); } } /* Parses sequence of ||s. Puts freshly allocated array of matchpoints into match. */ union exp_element * or1k_parse_or (exp, match, nmatch, set_break) union exp_element *exp; struct matchpoint **match; int *nmatch; int set_break; { struct matchpoint *tmp_match1, *tmp_match2; int tmp_nmatch1, tmp_nmatch2; switch (exp->opcode) { case BINOP_LOGICAL_OR: exp++; exp = or1k_parse_or (exp, &tmp_match1, &tmp_nmatch1); if (exp == &exp_error) return &exp_error; exp = or1k_parse_any (exp, &tmp_match2, &tmp_nmatch2, 3); if (set_break) { tmp_match1[tmp_nmatch1 - 1].cause_breakpoint = 1; tmp_match2[tmp_nmatch2 - 1].cause_breakpoint = 1; } *nmatch = tmp_nmatch1 + tmp_nmatch2; *match = (struct matchpoint *)malloc (*nmatch * sizeof (struct matchpoint)); memcpy (*match, tmp_match1, tmp_nmatch1 * sizeof (struct matchpoint)); free (tmp_match1); memcpy (*match + tmp_nmatch1, tmp_match2, tmp_nmatch2 * sizeof (struct matchpoint)); free (tmp_match2); return exp; default: return or1k_parse_any (exp, match, nmatch, 3); if (set_break) (*match)[*nmatch - 1].cause_breakpoint = 1; } } /* Prints single matchpoint from specified struct. */ static void print_matchpoint_struct (mp) struct matchpoint *mp; { printf_filtered ("%-6s (%i) %u, ON=%i, chain_type=%i, cause break=%i\n", compare_to_names[mp->dcr.ct], mp->dcr.cc, mp->dvr, mp->dcr.dp, mp->chain_type, mp->cause_breakpoint); } /* Build watchpoint(s) based on given structure. */ static void set_matchpoints (match, nmatch) struct matchpoint *match; int nmatch; { int i; debug_regs_changed = 1; sift_matchpoints (); for (i = 0; i < nmatch; i++) { int num = or1k_implementation.num_used_matchpoints; dcr[num] = match[i].dcr; dvr[num] = match[i].dvr; /* Set chaining bits. */ dmr1 &= ~(3 << (2 * num)); dmr1 |= match[i].chain_type << (2 * num); /* Set watchpoint bits */ dmr2 &= 1 << num; dmr2 |= match[i].cause_breakpoint << num; matchpoint_user_count[i]++; or1k_implementation.num_used_matchpoints++; } } /* Returns nonzero, if matchpoints [start .. start+nmatch-1] are equal to match record. */ static int matchpoint_matches (start, match, nmatch) int start; struct matchpoint *match; int nmatch; { int i; if (nmatch + start >= or1k_implementation.num_matchpoints) return 0; for (i = 0; i < nmatch; i++) { int j = i + start; /* Everything exept cause breakpoint must match. */ if (dcr[j].dp != match[i].dcr.dp || dcr[j].ct != match[i].dcr.ct || dcr[j].cc != match[i].dcr.cc || dcr[j].sc != match[i].dcr.sc || dvr[j] != match[i].dvr || match[i].chain_type != (dmr1 >> (2 * j)) & 3) return 0; } return 1; } static void hwatch_command (arg, from_tty) char *arg; int from_tty; { struct expression *exp; int i, nfree, nmatch, remove = 0; struct matchpoint *match; if (arg == NULL) arg = ""; if (strncasecmp ("remove ", arg, 7) == 0) { arg += 7; remove = 1; } /* Parse arguments. */ exp = parse_exp_1 (&arg, 0, 0); #ifdef DEBUG dump_prefix_expression (exp, gdb_stdout, "expr1"); #endif if (or1k_parse_or (&exp->elts[0], &match, &nmatch, 1) == &exp_error) error ("Watchpoint too complex."); for (i = 0; i < nmatch; i++) print_matchpoint_struct (&match[i]); if (remove) { int start = -1; int cleared = 0; if (num_hw_watches <= 0) error ("No extended hardware supported watchpoints present."); for (i = 0; i < num_hw_watches; i++) if (matchpoint_matches (or1k_hwatch[i].matchpoint_start, match, nmatch)) { start = or1k_hwatch[i].matchpoint_start; break; } if (start < 0) error ("Watchpoint not found."); for (i = 0; i < nmatch; i++) { int j = start + i; if (--matchpoint_user_count[j] <= 0) { debug_regs_changed = 1; memset (&dcr[j], 0, sizeof (dcr[j])); or1k_implementation.num_used_matchpoints--; cleared = 1; } } if (!cleared) warning ("No matchpoint(s) freed. Resources are busy."); } else { if (num_hw_watches >= MAX_HW_WATCHES) error ("Number of watchpoints too large."); /* Now we have to find out if given prefix expression matches our HW based support. It may take up to or1k_implementation.num_matchpoints - or1k_implementation.num_used_matchpoints. */ nfree = or1k_implementation.num_matchpoints - or1k_implementation.num_used_matchpoints; if (nmatch > nfree) error ("Not enough free matchpoint resources."); /* Build watchpoint(s) based on just built structure. */ or1k_hwatch[num_hw_watches].matchpoint_start = or1k_implementation.num_used_matchpoints; set_matchpoints (match, nmatch); num_hw_watches++; printf_unfiltered ("Watchpoint successfully allocated.\n"); } free (match); free (exp); } static void htrace_command (args, from_tty) char *args; int from_tty; { help_list (htrace_cmdlist, "htrace ", all_commands, gdb_stdout); } static void htrace_mode_command (args, from_tty) char *args; int from_tty; { help_list (htrace_mode_cmdlist, "htrace mode ", all_commands, gdb_stdout); } static void htrace_mode_contin_command (args, from_tty) char *args; int from_tty; { or1k_htrace.moder.contin = 1; printf_unfiltered ("Continuous trace mode set.\n"); } static void htrace_mode_suspend_command (args, from_tty) char *args; int from_tty; { or1k_htrace.moder.contin = 0; printf_unfiltered ("Suspend trace mode set.\n"); } static void print_event_struct (event, stop) struct htrace_event_struct *event; int stop; { int i; if (event->operation == TRIGOP_ANY) if (stop) printf_filtered ("not active"); else printf_filtered ("always active"); else { char *comma; if (event->operation == TRIGOP_AND) comma = "&("; else comma = "|("; if (event->is_valid) { printf_filtered ("%s%s", comma, or1k_is_names[event->is_trig]); comma = ", "; } if (event->ls_valid) { printf_filtered ("%s%s", comma, or1k_ls_names[event->ls_trig]); comma = ", "; } if (event->bp_valid) { printf_filtered ("%sbreak", comma); comma = ", "; } if (event->wp_valid) for (i = 0; i < 11; i++) if ((event->wp_trig >> i) & 1) { printf_filtered ("%sWP%i", comma, i); comma = ", "; } if (comma[0] == ',') printf_filtered (")"); else printf_filtered ("not active"); } } static void print_record_struct (record) struct htrace_record_struct *record; { int i; char *comma = ""; for (i = 0; i < MAX_RECORD_NAMES; i++) { if ((record->rec >> i)&1) { printf_filtered ("%s%s", comma, or1k_record_names[i]); comma = ", "; } } if (!*comma) printf_unfiltered ("none"); } static void htrace_info_command (args, from_tty) char *args; int from_tty; { int i; printf_filtered ("Trace trigger: "); print_event_struct (&or1k_htrace.trig, 0); printf_filtered ("\nTrace qualifier: "); print_event_struct (&or1k_htrace.qual, 0); for (i = 0; i < MAX_MATCHPOINTS; i++) { printf_filtered ("\n WP%i records: ", i); print_record_struct (&or1k_htrace.recwp[i]); } printf_filtered ("\n BP records: "); print_record_struct (&or1k_htrace.recbp); printf_filtered ("\nTrace stop: "); print_event_struct (&or1k_htrace.stop, 1); printf_filtered ("\n"); } /* Parses event from given string. Result is placed into event structure, and previously allocated resources are freed. Parameter stop is nonzero, when we are parsing for stop criteria. */ static void parse_event (args, event, stop) char *args; struct htrace_event_struct *event; int stop; { int i, op_type = 0, was_last_op = 1, any = 0; /* Release previous resources. */ for (i = 0; i < MAX_MATCHPOINTS; i++) { if ((event->wp_trig << i) & 1) if (--matchpoint_user_count[i] <= 0) { memset (&dcr[i], 0, sizeof (dcr[i])); debug_regs_changed = 1; or1k_implementation.num_used_matchpoints--; } } event->is_valid = event->is_trig = 0; event->ls_valid = event->ls_trig = 0; event->bp_valid = event->bp_trig = 0; event->wp_valid = event->wp_trig = 0; if (args == NULL) args = ""; while (*args == ' ') args++; while (*args != '\0') { if (strncasecmp ("breakpoint", args, 10) == 0) { if (!was_last_op) error ("Syntax error."); was_last_op = 0; event->bp_valid = event->bp_trig = 1; } else if (!stop && strncasecmp ("any", args, 3) == 0 || stop && strncasecmp ("none", args, 4) == 0) { if (!was_last_op) error ("Syntax error."); was_last_op = 0; any = 1; } else if (strncasecmp ("||", args, 2) == 0) { if (op_type == TRIGOP_AND) error ("Only one type of logical operator allowed at a time."); op_type = TRIGOP_OR; if (was_last_op) error ("Syntax error."); was_last_op = 1; args += 2; } else if (strncasecmp ("&&", args, 2) == 0) { if (op_type == TRIGOP_OR) error ("Only one type of logical operator allowed at a time."); op_type = TRIGOP_AND; if (was_last_op) error ("Syntax error."); was_last_op = 1; args += 2; } else { int found = 0; if (!was_last_op) error ("Syntax error."); was_last_op = 0; /* Search through is and ls tables for a match. */ for (i = 0; i < MAX_IS_NAMES; i++) if (strncasecmp (args, or1k_is_names[i], strlen (or1k_is_names[i])) == 0) { event->is_valid = 1; event->is_trig = i; args += strlen (or1k_is_names[i]); found = 1; break; } if (!found) { for (i = 0; i < MAX_LS_NAMES; i++) if (strncasecmp (args, or1k_ls_names[i], strlen (or1k_ls_names[i])) == 0) { event->ls_valid = 1; event->ls_trig = i; args += strlen (or1k_ls_names[i]); found = 1; break; } } if (!found) { /* No special name was found => parse expression. */ struct expression *exp; struct matchpoint *match; int nmatch, nfree; exp = parse_exp_1 (&args, 0, 0); if (or1k_parse_any (&exp->elts[0], &match, &nmatch, 3) == &exp_error) error ("Expression too complex."); for (i = 0; i < nmatch; i++) print_matchpoint_struct (&match[i]); /* Now we have to find out if given prefix expression matches our HW based support. It may take up to or1k_implementation.num_matchpoints - or1k_implementation.num_used_matchpoints. */ nfree = or1k_implementation.num_matchpoints - or1k_implementation.num_used_matchpoints; if (nmatch > nfree) error ("Not enough free matchpoint resources."); /* Build matchpoint(s) based on just built structure. */ set_matchpoints (match, nmatch); event->wp_valid = 1; event->wp_trig |= 1 << (or1k_implementation.num_used_matchpoints - 1); printf_unfiltered ("Watchpoint successfully allocated.\n"); free (match); free (exp); found = 1; } if (!found) warning ("Invalid event at '%s'", args); } while (*args == ' ') args++; } if (any) event->operation = TRIGOP_ANY; else { if (op_type == 0) op_type = TRIGOP_AND; event->operation = op_type; } } static void htrace_trigger_command (args, from_tty) char *args; int from_tty; { parse_event (args, &or1k_htrace.trig, 0); printf_filtered ("Trace starts, when:\n"); print_event_struct (&or1k_htrace.trig, 0); printf_filtered ("\n"); } static void htrace_qualifier_command (args, from_tty) char *args; int from_tty; { parse_event (args, &or1k_htrace.qual, 0); printf_filtered ("Trace records, when:\n"); print_event_struct (&or1k_htrace.qual, 0); printf_filtered ("\n"); } static void htrace_stop_command (args, from_tty) char *args; int from_tty; { parse_event (args, &or1k_htrace.stop, 1); printf_filtered ("Trace stops, when:\n"); print_event_struct (&or1k_htrace.stop, 1); printf_filtered ("\n"); } static void htrace_clear_records_command (args, from_tty) char *args; int from_tty; { int i, j, cleared = 0; /* Clear all. */ for (i = 0; i < MAX_MATCHPOINTS; i++) { for (j = 0; j < MAX_MATCHPOINTS; j++) { if ((or1k_htrace.wp_record_uses[i] << j) & 1) if (--matchpoint_user_count[j] <= 0) { memset (&dcr[j], 0, sizeof (dcr[j])); debug_regs_changed = 1; cleared = 1; or1k_implementation.num_used_matchpoints--; } } or1k_htrace.wp_record_uses[i] = 0; } if (!cleared) warning ("No matchpoints freed. Resources are busy."); } /* Syntax: htrace record {data}* when {expr} */ static void htrace_record_command (args, from_tty) char *args; int from_tty; { struct expression *exp; int i, nfree, nmatch, wp; struct matchpoint *match; unsigned int recdata = 0; char *c; if (args == '\0') error ( "Please specify data to record, e.g.:\n" "htrace record PC WRITESPR SDATA when $SEA == 100\n" "htrace record when $SEA == 100 to remove record"); for (i = 0; *args != '\0' && strncasecmp ("when ", args, 5); i++) { int j, found = 0; for (j = 0; j < MAX_RECORD_NAMES; j++) if (strncasecmp (args, or1k_record_names[j], strlen (or1k_record_names[j])) == 0) { recdata |= 1 << j; found = 1; break; } if (!found) warning ("Invalid record data name at '%s'.", args); while (*args != ' ' && *args != '\0') args++; while (*args == ' ') args++; } if (strncasecmp ("when ", args, 5) != 0) if (*args == '\0') { warning ("Condition not set. Assuming breakpoint."); wp = -1; } else error ("Syntax error."); else { args += 5; if (strcasecmp ("breakpoint", args) == 0) wp = -1; else { /* Parse arguments. */ exp = parse_exp_1 (&args, 0, 0); #ifdef DEBUG dump_prefix_expression (exp, gdb_stdout, "expr1"); #endif if (or1k_parse_any (&exp->elts[0], &match, &nmatch, 3) == &exp_error) error ("Expression too complex."); for (i = 0; i < nmatch; i++) print_matchpoint_struct (&match[i]); if (recdata) { /* Now we have to find out if given prefix expression matches our HW based support. It may take up to or1k_implementation.num_matchpoints - or1k_implementation.num_used_matchpoints. */ nfree = or1k_implementation.num_matchpoints - or1k_implementation.num_used_matchpoints; if (nmatch > nfree) error ("Not enough free matchpoint resources."); wp = or1k_implementation.num_used_matchpoints - 1; or1k_htrace.wp_record_uses[wp] = 0; for (i = or1k_implementation.num_used_matchpoints; i <= wp; i++) or1k_htrace.wp_record_uses[wp] |= 1 << i; set_matchpoints (match, nmatch); } else { /* Remove record. */ int start = -1, cleared = 0; for (i = 0; i < MAX_MATCHPOINTS; i++) { int mp_start = 0, j; j = or1k_htrace.wp_record_uses[i]; while (j > 0 && j & 1 == 0) mp_start++; if (matchpoint_matches (mp_start, match, nmatch)) { start = mp_start; or1k_htrace.wp_record_uses[i] = 0; break; } } if (start < 0) error ("Record with such expression not found."); for (i = 0; i < nmatch; i++) { int j = i + start; if (--matchpoint_user_count[j] <= 0) { memset (&dcr[j], 0, sizeof (dcr[j])); debug_regs_changed = 1; cleared = 1; } } if (!cleared) warning ("No matchpoint(s) freed."); } } } /* If we reached this point we have matchpoints set, and wp holds the value of that watchpoint. wp == -1, if breakpoint was specified. */ if (wp < 0) or1k_htrace.recbp.rec = recdata; else or1k_htrace.recwp[wp].rec = recdata; if (recdata) { printf_unfiltered ("Data"); for (i = 0; i < MAX_RECORD_NAMES; i++) if ((recdata >> i) & 1) printf_unfiltered (" %s,", or1k_record_names[i]); } else printf_unfiltered ("No data"); if (wp < 0) printf_unfiltered (" will be recorded when breakpoint occurs\n"); else printf_unfiltered (" will be recorded when watchpoint #%i occurs\n", wp); } static void htrace_enable_command (args, from_tty) char *args; int from_tty; { or1k_htrace.moder.trace_enable = 1; printf_unfiltered ("HW Trace enabled.\n"); } static void htrace_disable_command (args, from_tty) char *args; int from_tty; { or1k_htrace.moder.trace_enable = 0; printf_unfiltered ("HW Trace disabled.\n"); } static void htrace_rewind_command (args, from_tty) char *args; int from_tty; { FILE *f; if (args != NULL && *args != '\0') strncpy (TRACE_FILENAME, args, TRACE_FILENAME_SIZE); /* Just empty it. */ if ((f = fopen (TRACE_FILENAME, "wb+")) == NULL) error ("Cannot open trace file."); fclose (f); printf_unfiltered ("Trace data cleared.\n"); } static void print_data_struct (pos, data) unsigned int pos; struct htrace_data_struct *data; { struct symbol *func; char *funname = NULL; if (data->type < 4) { /* Determine function name - copied from stack.c */ func = find_pc_function (data->data); if (func) { struct minimal_symbol *msymbol = lookup_minimal_symbol_by_pc (data->data); if (msymbol != NULL && (SYMBOL_VALUE_ADDRESS (msymbol) > BLOCK_START (SYMBOL_BLOCK_VALUE (func)))) funname = SYMBOL_NAME (msymbol); else { char *demangled; funname = SYMBOL_NAME (func); if (SYMBOL_LANGUAGE (func) == language_cplus) { demangled = cplus_demangle (funname, DMGL_ANSI); if (demangled == NULL) funname = SYMBOL_SOURCE_NAME (func); } } } else { struct minimal_symbol *msymbol = lookup_minimal_symbol_by_pc (data->data); if (msymbol != NULL) funname = SYMBOL_NAME (msymbol); } } printf_filtered ("%06X%c %-8s %08X", pos, data->valid ? '>' : ':', or1k_record_names[data->type], data->data); if (funname) printf_filtered (" (%s)\n", funname); else printf_filtered ("\n"); } /* Prints out trace buffer. */ static void htrace_print_command (args, from_tty) char *args; int from_tty; { int i, from = 0, length = prev_length; FILE *f; struct htrace_data_struct *td; if (args == NULL) args = ""; while (*args == ' ') args++; if (*args == '\0') { /* We will display buffer further. */ from = prev_from + prev_length; } else { /* Display buffer range. */ int numbers = 0; char *cnum = args; while (*args != ' ' && *args != '\0') args++; /* Any arguments? */ if (*args == '\0') numbers = 1; else { *args = 0; args++; numbers = 2; } from = strtoul (cnum, &cnum, 0); if (*cnum != 0) error ("Invalid from value."); if (from < 0) from += trace_size; if (numbers == 2) { while (*args == ' ') args++; length = strtoul (cnum, &cnum, 0); if (*args != 0) error ("Invalid length value."); if (length < 0) { from += length; length = -length; } } } if (from >= trace_size) from = trace_size - 1; if (from < 0) from = 0; if (from + length >= trace_size) length = trace_size - from; prev_length = length; prev_from = from; if (length == 0) error ("Nothing to print."); printf_filtered ("Trace buffer %06x:%06x (size = %i)\n", from, from + length - 1, length); if ((f = fopen (TRACE_FILENAME, "rb")) == NULL) error ("Cannot open trace file."); if (fseek (f, TRACE_DATA_SIZE * from, SEEK_SET)) error ("Error reading trace file."); td = (struct htrace_data_struct *) malloc (TRACE_DATA_SIZE * length); length = fread (td, TRACE_DATA_SIZE, length, f); for (i = 0; i < length; i++) print_data_struct (from + i, td[i]); fclose (f); } void _initialize_or1k_tdep () { /* Commands to show and set sprs. */ add_info ("spr", info_spr_command, "Show information about the spr registers."); add_com ("spr", class_support, spr_command, "Set specified SPR register."); /* hwatch command. */ add_com ("hwatch", class_breakpoint, hwatch_command, "Set hardware watchpoint.\n\ Example: ($LEA == my_var)&&($LDATA < 50)||($SEA == my_var)&&($SDATA >= 50).\n\ See OR1k Architecture document for more info."); /* htrace commands. */ add_prefix_cmd ("htrace", class_breakpoint, htrace_command, "Set hardware trace.\nExamples:\n" " htrace info\n" " htrace trigger <expr>\n" " htrace qualifier <expr>\n" " htrace stop <expr>\n" " htrace record <data> when [<expr>|breakpoint]\n" " htrace clear recors\n" " htrace enable\n" " htrace disable\n" " htrace control\n\n" "See OR1k Architecture and gdb for or1k documents for more info.", &htrace_cmdlist, "htrace ", 0, &cmdlist); add_cmd ("info", class_breakpoint, htrace_info_command, "Display information about HW trace.", &htrace_cmdlist); add_alias_cmd ("i", "info", class_breakpoint, 1, &htrace_cmdlist); add_cmd ("trigger", class_breakpoint, htrace_trigger_command, "Set starting criteria for trace.", &htrace_cmdlist); add_alias_cmd ("t", "trigger", class_breakpoint, 1, &htrace_cmdlist); add_cmd ("qualifier", class_breakpoint, htrace_qualifier_command, "Set acquisition qualifier for HW trace.", &htrace_cmdlist); add_alias_cmd ("q", "qualifier", class_breakpoint, 1, &htrace_cmdlist); add_cmd ("stop", class_breakpoint, htrace_stop_command, "Set HW trace stopping criteria.", &htrace_cmdlist); add_alias_cmd ("s", "stop", class_breakpoint, 1, &htrace_cmdlist); add_cmd ("record", class_breakpoint, htrace_record_command, "Sets data to be recorded when expression occurs.", &htrace_cmdlist); add_alias_cmd ("r", "record", class_breakpoint, 1, &htrace_cmdlist); add_cmd ("clear records", class_breakpoint, htrace_clear_records_command, "Disposes all matchpoints used by records.", &htrace_cmdlist); add_cmd ("enable", class_breakpoint, htrace_enable_command, "Enables the HW trace.", &htrace_cmdlist); add_alias_cmd ("e", "enable", class_breakpoint, 1, &htrace_cmdlist); add_cmd ("disable", class_breakpoint, htrace_disable_command, "Disables the HW trace.", &htrace_cmdlist); add_alias_cmd ("d", "disable", class_breakpoint, 1, &htrace_cmdlist); add_cmd ("rewind", class_breakpoint, htrace_rewind_command, "Clears currently recorded trace data.\n" "If filename is specified, new trace file is made and any newly collected data\n" "will be written there.", &htrace_cmdlist); add_cmd ("print", class_breakpoint, htrace_print_command, "Prints trace buffer, using current record configuration.\n" "htrace print [<start> [<len>]]\n" "htrace print" , &htrace_cmdlist); add_alias_cmd ("p", "print", class_breakpoint, 1, &htrace_cmdlist); add_prefix_cmd ("mode", class_breakpoint, htrace_mode_command, "Configures the HW trace.\n" "htrace mode [continuous|suspend]" , &htrace_mode_cmdlist, "htrace mode ", 0, &htrace_cmdlist); add_alias_cmd ("m", "mode", class_breakpoint, 1, &htrace_cmdlist); add_cmd ("continuous", class_breakpoint, htrace_mode_contin_command, "Set continuous trace mode.\n", &htrace_mode_cmdlist); add_cmd ("suspend", class_breakpoint, htrace_mode_suspend_command, "Set suspend trace mode.\n", &htrace_mode_cmdlist); /* Extra functions supported by simulator. */ add_com ("sim", class_obscure, sim_command, "Send a extended command to the simulator."); }
Go to most recent revision | Compare with Previous | Blame | View Log