diff options
Diffstat (limited to 'gdb')
96 files changed, 24980 insertions, 7958 deletions
diff --git a/gdb/JTAG_aps_driver.c b/gdb/JTAG_aps_driver.c new file mode 100644 index 00000000000..1118948c62b --- /dev/null +++ b/gdb/JTAG_aps_driver.c @@ -0,0 +1,560 @@ +/* Target dependent code for ARC700, for GDB, the GNU debugger. + + Copyright 2008, 2009 Free Software Foundation, Inc. + + Contributed by ARC International (www.arc.com) + + Authors: + Richard Stuckey <richard.stuckey@arc.com> + + 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 3 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, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This module contains a test driver for the JTAG actionpoints module of */ +/* the ARC port of gdb. */ +/* */ +/* Usage: */ +/* <driver> [ -c ] [ -d ] [ -s ] [ -r <count> ] [ -a N ] */ +/* */ +/* where -c specifies target connection & disconnection only */ +/* -d switches on JTAG operation debuggging */ +/* -s simulates the JTAG target */ +/* -r specifies the JTAG operation retry count */ +/* -a 2 | 4 | 8 specifies the number of simulated actionpoints */ +/* */ +/******************************************************************************/ + +/* system header files */ +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdarg.h> +#include <stdlib.h> +#include <unistd.h> + +/* gdb header files */ +#include "defs.h" +#include "breakpoint.h" + +/* ARC header files */ +#include "arc-jtag.h" +#include "arc-jtag-ops.h" +#include "arc-jtag-actionpoints.h" +#include "arc-registers.h" + + +/* -------------------------------------------------------------------------- */ +/* local types */ +/* -------------------------------------------------------------------------- */ + +// complete the type here +struct aux_register_definition +{ + const char* name; + ARC_RegisterNumber number; +}; + + +typedef enum +{ + CLEAR_USER_BIT, + RESTORE_USER_BIT +} Status32Action; + + +/* -------------------------------------------------------------------------- */ +/* externally visible data */ +/* -------------------------------------------------------------------------- */ + +/* global debug flag */ +Boolean arc_debug_target; + +ARC_RegisterNumber arc_debug_regnum = ARC_HW_DEBUG_REGNUM; + + +/* -------------------------------------------------------------------------- */ +/* local data */ +/* -------------------------------------------------------------------------- */ + +#define SUCCESS 0 +#define FAILURE (-1) + +// what should this be? +#define DATA_AREA 0x00001000 +#define DATA_AREA_LENGTH 1024 + +#define BP_ADDRESS (DATA_AREA + 0x0400) + +#define MAX_ACTION_POINTS 8 + +#define WRITE_WATCHPOINT 0 +#define READ_WATCHPOINT 1 +#define ACCESS_WATCHPOINT 2 + +#define AP_BUILD 0x76 + + +static Boolean test = TRUE; +static Boolean simulate = FALSE; +static unsigned int num_actionpoints = 2; +static struct target_ops operations; + +static const ARC_AuxRegisterDefinition registers[] = +{ + { "DEBUG", ARC_HW_DEBUG_REGNUM }, + { "IDENTITY", ARC_HW_IDENTITY_REGNUM }, + { "PC", ARC_HW_PC_REGNUM }, + { "STATUS32", ARC_HW_STATUS32_REGNUM }, + { "AP_BUILD", ARC_HW_AP_BUILD_REGNUM }, + { "AMV0", ARC_HW_AMV0_REGNUM }, + { "AMM0", ARC_HW_AMM0_REGNUM }, + { "AC0", ARC_HW_AC0_REGNUM } +}; + + +/* -------------------------------------------------------------------------- */ +/* local macros */ +/* -------------------------------------------------------------------------- */ + +#define CHECK(status, expected) \ +{ \ + if ((status) != (expected)) \ + { \ + failed("%d line: status %d != %d", __LINE__, status, expected); \ + } \ +} + + +/* -------------------------------------------------------------------------- */ +/* local functions */ +/* -------------------------------------------------------------------------- */ + +static void failed(const char* fmt, ...) +{ + va_list ap; + + fprintf(stderr, "*** FAILED: "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + +// exit(EXIT_FAILURE); +} + + +static void run_tests(Boolean before_reset) +{ +static struct bp_target_info bpt[MAX_ACTION_POINTS + 1]; + + int status; + int result; + CORE_ADDR addr; + unsigned int i; + unsigned int max_actionpoints; + + if (before_reset) + { + memset(&bpt, sizeof(bpt), 0); + + for (i = 0; i <= MAX_ACTION_POINTS; i++) + bpt[i].placed_address = BP_ADDRESS + 4 * i; + } + else + { + arc_dump_actionpoints("after reset"); + + /* remove the actionpoints that were set before the reset */ + status = operations.to_remove_hw_breakpoint(&bpt[0]); CHECK(status, SUCCESS); + status = operations.to_remove_hw_breakpoint(&bpt[1]); CHECK(status, SUCCESS); + } + + + if (operations.to_can_use_hw_breakpoint(bp_hardware_breakpoint, 8, 0) == 1) + max_actionpoints = 8; + else if (operations.to_can_use_hw_breakpoint(bp_hardware_breakpoint, 4, 0) == 1) + max_actionpoints = 4; + else if (operations.to_can_use_hw_breakpoint(bp_hardware_breakpoint, 2, 0) == 1) + max_actionpoints = 2; + else + { + printf("can not use h/w breakpoints\n"); + return; + } + + printf("target supports %d actionpoints\n", max_actionpoints); + + + /* N breakpoints that each require 1 actionpoint can be set */ + for (i = 0; i < max_actionpoints + 1; i++) + { + status = operations.to_insert_hw_breakpoint(&bpt[i]); + CHECK(status, (i < max_actionpoints) ? SUCCESS : FAILURE); + } + for (i = 0; i < max_actionpoints + 1; i++) + { + status = operations.to_remove_hw_breakpoint(&bpt[i]); + CHECK(status, (i < max_actionpoints) ? SUCCESS : FAILURE); + } + + + if (operations.to_region_ok_for_hw_watchpoint(DATA_AREA, DATA_AREA_LENGTH)) + { + /* N watchpoints that each require 1 actionpoint can be set */ + for (i = 0; i < max_actionpoints + 1; i++) + { + status = operations.to_insert_watchpoint(DATA_AREA, 4, WRITE_WATCHPOINT); + CHECK(status, (i < max_actionpoints) ? SUCCESS : FAILURE); + } + for (i = 0; i < max_actionpoints + 1; i++) + { + status = operations.to_remove_watchpoint(DATA_AREA, 4, WRITE_WATCHPOINT); + CHECK(status, (i < max_actionpoints) ? SUCCESS : FAILURE); + } + + if (max_actionpoints == 8) + { + /* this requires 2 actionpoints (a pair) */ + status = operations.to_insert_watchpoint(DATA_AREA, 6, ACCESS_WATCHPOINT); CHECK(status, SUCCESS); + arc_display_actionpoints(); + status = operations.to_remove_watchpoint(DATA_AREA, 6, ACCESS_WATCHPOINT); CHECK(status, SUCCESS); + + /* this requires 3 actionpoints (a quad) */ + status = operations.to_insert_watchpoint(DATA_AREA, 5, ACCESS_WATCHPOINT); CHECK(status, SUCCESS); + arc_display_actionpoints(); + status = operations.to_remove_watchpoint(DATA_AREA, 5, ACCESS_WATCHPOINT); CHECK(status, SUCCESS); + + /* this requires 4 actionpoints (a quad) */ + status = operations.to_insert_watchpoint(DATA_AREA, 9, ACCESS_WATCHPOINT); CHECK(status, SUCCESS); + + /* this requires another 4 actionpoints (another quad) */ + status = operations.to_insert_watchpoint(DATA_AREA, 25, ACCESS_WATCHPOINT); CHECK(status, SUCCESS); + + status = operations.to_remove_watchpoint(DATA_AREA, 9, ACCESS_WATCHPOINT); CHECK(status, SUCCESS); + status = operations.to_remove_watchpoint(DATA_AREA, 25, ACCESS_WATCHPOINT); CHECK(status, SUCCESS); + + /* this would require 5 actionpoints! */ + status = operations.to_insert_watchpoint(DATA_AREA, 17, ACCESS_WATCHPOINT); CHECK(status, FAILURE); + + /* this will tie up actionpoints 0, 2 and 6 */ + status = operations.to_insert_hw_breakpoint(&bpt[0]); CHECK(status, SUCCESS); + status = operations.to_insert_hw_breakpoint(&bpt[1]); CHECK(status, SUCCESS); + status = operations.to_insert_hw_breakpoint(&bpt[2]); CHECK(status, SUCCESS); + status = operations.to_insert_hw_breakpoint(&bpt[3]); CHECK(status, SUCCESS); + status = operations.to_insert_hw_breakpoint(&bpt[4]); CHECK(status, SUCCESS); + status = operations.to_insert_hw_breakpoint(&bpt[5]); CHECK(status, SUCCESS); + status = operations.to_insert_hw_breakpoint(&bpt[6]); CHECK(status, SUCCESS); + status = operations.to_remove_hw_breakpoint(&bpt[1]); CHECK(status, SUCCESS); + status = operations.to_remove_hw_breakpoint(&bpt[3]); CHECK(status, SUCCESS); + status = operations.to_remove_hw_breakpoint(&bpt[4]); CHECK(status, SUCCESS); + status = operations.to_remove_hw_breakpoint(&bpt[5]); CHECK(status, SUCCESS); + + arc_display_actionpoints(); + + arc_dump_actionpoints("before quad shuffle"); + + /* this requires 4 actionpoints (a quad) - this should cause the + * actionpoints to be shuffled so that a quad is available + */ + status = operations.to_insert_watchpoint(DATA_AREA, 9, ACCESS_WATCHPOINT); CHECK(status, SUCCESS); + + arc_dump_actionpoints("after quad shuffle"); + + arc_display_actionpoints(); + + /* release the tied-up actionpoints */ + status = operations.to_remove_hw_breakpoint(&bpt[0]); CHECK(status, SUCCESS); + status = operations.to_remove_hw_breakpoint(&bpt[2]); CHECK(status, SUCCESS); + status = operations.to_remove_hw_breakpoint(&bpt[6]); CHECK(status, SUCCESS); + + arc_dump_actionpoints("before quad shuffle"); + + /* this requires another 4 actionpoints (a quad) - this should + * cause the actionpoints to be shuffled so that the other quad is + * available + */ + status = operations.to_insert_watchpoint(DATA_AREA, 36, ACCESS_WATCHPOINT); CHECK(status, SUCCESS); + + arc_dump_actionpoints("after quad shuffle"); + + /* release the two quads */ + status = operations.to_remove_watchpoint(DATA_AREA, 9, ACCESS_WATCHPOINT); CHECK(status, SUCCESS); + status = operations.to_remove_watchpoint(DATA_AREA, 36, ACCESS_WATCHPOINT); CHECK(status, SUCCESS); + } + else if (max_actionpoints == 4) + { + /* this requires 2 actionpoints (a pair) */ + status = operations.to_insert_watchpoint(DATA_AREA, 6, ACCESS_WATCHPOINT); CHECK(status, SUCCESS); + status = operations.to_remove_watchpoint(DATA_AREA, 6, ACCESS_WATCHPOINT); CHECK(status, SUCCESS); + + /* this requires 3 actionpoints (a quad) */ + status = operations.to_insert_watchpoint(DATA_AREA, 5, ACCESS_WATCHPOINT); CHECK(status, SUCCESS); + status = operations.to_remove_watchpoint(DATA_AREA, 5, ACCESS_WATCHPOINT); CHECK(status, SUCCESS); + + /* this requires 4 actionpoints (a quad) */ + status = operations.to_insert_watchpoint(DATA_AREA, 9, ACCESS_WATCHPOINT); CHECK(status, SUCCESS); + status = operations.to_remove_watchpoint(DATA_AREA, 9, ACCESS_WATCHPOINT); CHECK(status, SUCCESS); + + /* this would require 5 actionpoints! */ + status = operations.to_insert_watchpoint(DATA_AREA, 17, ACCESS_WATCHPOINT); CHECK(status, FAILURE); + + /* this will tie up actionpoints 0 and 2 */ + status = operations.to_insert_hw_breakpoint(&bpt[0]); CHECK(status, SUCCESS); + status = operations.to_insert_hw_breakpoint(&bpt[1]); CHECK(status, SUCCESS); + status = operations.to_insert_hw_breakpoint(&bpt[2]); CHECK(status, SUCCESS); + status = operations.to_remove_hw_breakpoint(&bpt[1]); CHECK(status, SUCCESS); + + arc_dump_actionpoints("before quad shuffle"); + + /* this requires 2 actionpoints (a pair) - this should cause the + * actionpoints to be shuffled so that a pair is available + */ + status = operations.to_insert_watchpoint(DATA_AREA, 6, ACCESS_WATCHPOINT); CHECK(status, SUCCESS); + status = operations.to_remove_watchpoint(DATA_AREA, 6, ACCESS_WATCHPOINT); CHECK(status, SUCCESS); + + arc_dump_actionpoints("after quad shuffle"); + + status = operations.to_remove_hw_breakpoint(&bpt[0]); CHECK(status, SUCCESS); + status = operations.to_remove_hw_breakpoint(&bpt[2]); CHECK(status, SUCCESS); + } + else if (max_actionpoints == 2) + { + /* this requires 2 actionpoints (a pair) */ + status = operations.to_insert_watchpoint(DATA_AREA, 6, ACCESS_WATCHPOINT); CHECK(status, SUCCESS); + status = operations.to_remove_watchpoint(DATA_AREA, 6, ACCESS_WATCHPOINT); CHECK(status, SUCCESS); + + /* this would require 3 actionpoints! */ + status = operations.to_insert_watchpoint(DATA_AREA, 5, ACCESS_WATCHPOINT); CHECK(status, FAILURE); + } + + result = operations.to_stopped_by_watchpoint(); + printf("%sstopped by watchpoint\n", (result) ? "" : "not "); + + result = operations.to_stopped_data_address(&operations, &addr); + if (result) + printf("stopped by data access at address 0x%08X\n", (unsigned int) addr); + } + else + printf("area %0x08X .. %0x08X does not allow watchpoints\n", DATA_AREA, DATA_AREA + DATA_AREA_LENGTH - 1); + + arc_dump_actionpoints("before reset"); + + /* leave some actionpoints set to ensure that there are some to re-establish + * after target board reset + */ + if (before_reset) + { + status = operations.to_insert_hw_breakpoint(&bpt[0]); CHECK(status, SUCCESS); + status = operations.to_insert_hw_breakpoint(&bpt[1]); CHECK(status, SUCCESS); + } +} + + +static void process_options(int argc, char** argv) +{ + int c; + + while ((c = getopt (argc, argv, "sdcr:a:")) != -1) + { + switch (c) + { + case 'd': + arc_jtag_ops.state_machine_debug = TRUE; + break; + case 'r': + arc_jtag_ops.retry_count = atoi(optarg); + break; + case 'a': + num_actionpoints = atoi(optarg); + if (!(num_actionpoints == 2 || num_actionpoints == 4 || num_actionpoints == 8)) + { + fprintf(stderr, "Usage: %s [ -d ]\n", argv[0]); + exit(EXIT_FAILURE); + } + break; + case 'c': + test = FALSE; + break; + case 's': + simulate = TRUE; + break; + default: + fprintf(stderr, "Usage: %s [ -dcs ] [ -r <count> ] [ -a <actionpoints> ]\n", argv[0]); + exit(EXIT_FAILURE); + } + } +} + + +/* -------------------------------------------------------------------------- */ +/* externally visible functions */ +/* -------------------------------------------------------------------------- */ + +extern void _initialize_arc_jtag_ops(void); + + +int main(int argc, char** argv) +{ + Boolean opened; + + printf("Starting test of ARC JTAG actionpoints...\n"); + + _initialize_arc_jtag_ops(); + + process_options(argc, argv); + + opened = ((simulate) ? TRUE : arc_jtag_ops.open()); + + if (opened) + { + printf("ARC processor is connected\n"); + + /* this can be done only after connection */ + if (arc_initialize_actionpoint_ops(&operations)) + { + if (test) + { + run_tests(TRUE); + printf("resetting board...\n"); + if (!simulate) + arc_jtag_ops.reset_board(); + printf("board reset\n"); + if (!arc_restore_actionpoints_after_reset()) + failed("could not restore actionpoints after reset"); + run_tests(FALSE); + } + } + else + printf("processor does not support actionpoints\n"); + + if (!simulate) + arc_jtag_ops.close(); + } + + printf("Finished test of ARC JTAG actionpoints\n"); + + return 0; +} + + +/* N.B. these functions are found in arc-jtag.c and arc-registers.c, but are + * included here in order to avoid including that module in the built test + * driver, as that would also pull in a lot of the gdb modules! + * + * Also, this allows the AUX register read/write operations to be simulated, + * thus making it possible to test much of the logic of the arc-jtag-actionpoints + * module without a real JTAG target. + */ + +void arc_change_status32(Status32Action action) +{ + static ARC_RegisterContents status32; + + if (action == CLEAR_USER_BIT) + { + /* Get processor out of user mode. */ + + if (arc_read_jtag_aux_register(ARC_HW_STATUS32_REGNUM, &status32, FALSE)) + { + /* if the User bit is actually set */ + if (status32 & STATUS32_USER) + if (!arc_write_jtag_aux_register(ARC_HW_STATUS32_REGNUM, + status32 & ~STATUS32_USER, FALSE)) + warning(_("Can not clear User bit in STATUS32 register")); + } + else + warning(_("Can not read STATUS32 register")); + } + else + { + /* if the User bit was actually cleared */ + if (status32 & STATUS32_USER) + if (!arc_write_jtag_aux_register(ARC_HW_STATUS32_REGNUM, status32, FALSE)) + warning(_("Can not restore User bit in STATUS32 register")); + } +} + + +Boolean arc_read_jtag_aux_register (ARC_RegisterNumber hwregno, + ARC_RegisterContents* contents, + Boolean warn_on_failure) +{ + if (simulate) + { + if (hwregno == AP_BUILD) + { + if (num_actionpoints == 2) + *contents = 0x00000004; + else if (num_actionpoints == 4) + *contents = 0x00000104; + else + *contents = 0x00000204; + } + else + *contents = 0; + return TRUE; + } + + return (arc_jtag_ops.read_aux_reg(hwregno, contents) == JTAG_SUCCESS); +} + + +Boolean arc_write_jtag_aux_register (ARC_RegisterNumber hwregno, + ARC_RegisterContents contents, + Boolean warn_on_failure) +{ + if (simulate) + { +// printf("AUX: hwregno = %d, contents = 0x%08X\n", hwregno, contents); + return TRUE; + } + + return (arc_jtag_ops.write_aux_reg(hwregno, contents) == JTAG_SUCCESS); +} + + +ARC_AuxRegisterDefinition* +arc_find_aux_register_by_name(const char* name) +{ + unsigned int i; + + for (i = 0; i < ELEMENTS_IN_ARRAY(registers); i++) + { + const ARC_AuxRegisterDefinition* def = ®isters[i]; + + if (strcmp(name, def->name) == 0) + return (ARC_AuxRegisterDefinition*) def; + } + + return NULL; +} + + +ARC_RegisterNumber arc_aux_hw_register_number(ARC_AuxRegisterDefinition* def) +{ + return def->number; +} + + +ARC_RegisterNumber arc_aux_find_register_number(const char* name, + ARC_RegisterNumber defaultNumber) +{ + ARC_AuxRegisterDefinition* def = arc_find_aux_register_by_name(name); + + return (def) ? def->number : defaultNumber; +} + +/******************************************************************************/ diff --git a/gdb/JTAG_download.c b/gdb/JTAG_download.c new file mode 100755 index 00000000000..d3f0626906a --- /dev/null +++ b/gdb/JTAG_download.c @@ -0,0 +1,208 @@ +/* Target dependent code for ARC700, for GDB, the GNU debugger. + + Copyright 2008, 2009 Free Software Foundation, Inc. + + Contributed by ARC International (www.arc.com) + + Authors: + Richard Stuckey <richard.stuckey@arc.com> + + 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 3 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, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This module contains a test driver for the JTAG operations module of */ +/* the ARC port of gdb. It measures the speed at which data can be */ +/* downloaded to the target. */ +/* */ +/* Usage: */ +/* <driver> [ -c ] [ -d ] [ -r <count> ] */ +/* */ +/* where -c specifies target connection & disconnection only */ +/* -d switches on JTAG operation debuggging */ +/* -r specifies the JTAG operation retry count */ +/* */ +/******************************************************************************/ + +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdarg.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/time.h> + +#include "arc-jtag-ops.h" + + +/* -------------------------------------------------------------------------- */ +/* local data */ +/* -------------------------------------------------------------------------- */ + +#define MB 1024 * 1024 +#define DATA_AREA 0x00001000 +#define DATA_AREA_SIZE 1 * MB + + +static ARC_Byte data[DATA_AREA_SIZE]; + + +static Boolean test = TRUE; + + +/* -------------------------------------------------------------------------- */ +/* externally visible data */ +/* -------------------------------------------------------------------------- */ + +/* global debug flag */ +Boolean arc_debug_target; + + +/* -------------------------------------------------------------------------- */ +/* local functions */ +/* -------------------------------------------------------------------------- */ + +static void failed(const char* fmt, ...) +{ + va_list ap; + + printf("*** FAILED: "); + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + printf("\n"); + +// exit(EXIT_FAILURE); +} + + +static void run_test(void) +{ + unsigned int i; + unsigned int bytes; + struct timeval start_time, end_time; + long long int time_count; + + for (i = 0; i < DATA_AREA_SIZE; i++) + data[i] = (ARC_Byte) i; + + gettimeofday (&start_time, NULL); + + bytes = arc_jtag_ops.memory_write_chunk(DATA_AREA, data, DATA_AREA_SIZE); + + gettimeofday (&end_time, NULL); + + if (bytes != DATA_AREA_SIZE) + failed("memory write: %d", bytes); + + /* Compute the elapsed time in milliseconds, as a tradeoff between + accuracy and overflow. */ + time_count = (end_time.tv_sec - start_time.tv_sec) * 1000; + time_count += (end_time.tv_usec - start_time.tv_usec) / 1000; + + if (time_count > 0) + { + unsigned long rate = (bytes * 1000) / time_count; + + printf("0x%x bytes downloaded in %lld milliseconds\n", bytes, time_count); + printf("transfer rate: %lu bytes/sec\n", rate); + } + + + gettimeofday (&start_time, NULL); + + bytes = arc_jtag_ops.memory_write_pattern(DATA_AREA, 0, DATA_AREA_SIZE); + + gettimeofday (&end_time, NULL); + + if (bytes != DATA_AREA_SIZE) + failed("memory fill zero: %d", bytes); + + /* Compute the elapsed time in milliseconds, as a tradeoff between + accuracy and overflow. */ + time_count = (end_time.tv_sec - start_time.tv_sec) * 1000; + time_count += (end_time.tv_usec - start_time.tv_usec) / 1000; + + if (time_count > 0) + { + unsigned long rate = (bytes * 1000) / time_count; + + printf("0x%x bytes zeroed in %lld milliseconds\n", bytes, time_count); + printf("zero rate: %lu bytes/sec\n", rate); + } + +} + + +static void process_options(int argc, char** argv) +{ + int c; + + while ((c = getopt (argc, argv, "dcr:")) != -1) + { + switch (c) + { + case 'd': + arc_jtag_ops.state_machine_debug = TRUE; + break; + case 'r': + arc_jtag_ops.retry_count = atoi(optarg); + break; + case 'c': + test = FALSE; + break; + default: + fprintf(stderr, "Usage: %s [ -d ]\n", argv[0]); + exit(EXIT_FAILURE); + } + } +} + + +/* -------------------------------------------------------------------------- */ +/* externally visible functions */ +/* -------------------------------------------------------------------------- */ + +extern void _initialize_arc_jtag_ops(void); + + +int main(int argc, char** argv) +{ + Boolean opened; + + printf("Starting test of ARC JTAG download...\n"); + + _initialize_arc_jtag_ops(); + + process_options(argc, argv); + + opened = arc_jtag_ops.open(); + + if (opened) + { + printf("ARC processor is connected\n"); + + if (test) + run_test(); + + arc_jtag_ops.close(); + } + + printf("Finished test of ARC JTAG download\n"); + + return 0; +} + +/******************************************************************************/ diff --git a/gdb/JTAG_ops_driver.c b/gdb/JTAG_ops_driver.c new file mode 100644 index 00000000000..8ae877627c6 --- /dev/null +++ b/gdb/JTAG_ops_driver.c @@ -0,0 +1,904 @@ +/* Target dependent code for ARC700, for GDB, the GNU debugger. + + Copyright 2008, 2009 Free Software Foundation, Inc. + + Contributed by ARC International (www.arc.com) + + Authors: + Richard Stuckey <richard.stuckey@arc.com> + + 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, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This module contains a test driver for the JTAG operations module of */ +/* the ARC port of gdb. */ +/* */ +/* Usage: */ +/* <driver> [ -c ] [ -d ] [ -r <count> ] [ -m ] */ +/* */ +/* where -c specifies target connection & disconnection only */ +/* -d switches on JTAG operation debuggging */ +/* -r specifies the JTAG operation retry count */ +/* -m specifies testing memory operations only */ +/* */ +/******************************************************************************/ + +#include <string.h> +#include <stdio.h> +#include <assert.h> +#include <errno.h> +#include <stdarg.h> +#include <stdlib.h> +#include <unistd.h> + +#include "arc-jtag-ops.h" +#include "arc-memory.h" + + +/* -------------------------------------------------------------------------- */ +/* local types */ +/* -------------------------------------------------------------------------- */ + +typedef enum +{ + RO, // read-only + RW, // read/write + WO, // write-only + UU +} RegisterMode; + + +typedef struct +{ + const char* name; + ARC_RegisterNumber regno; + ARC_RegisterContents mask; + RegisterMode mode; +} RegisterInfo; + + +/* -------------------------------------------------------------------------- */ +/* local data */ +/* -------------------------------------------------------------------------- */ + +// what should this be? +#define DATA_AREA 0x00001000 +#define BUFFER_LENGTH 128 + +#define ARC_NR_CORE_REGS 32 + + +#undef RBCR +#define RAUX(name, hwregno, desc, gdbregno, mask, mode, version) { #name, hwregno, mask, mode }, +static const RegisterInfo aux[] = +{ + #include "arc-regnums-defs.h" +}; + +#undef RAUX +#define RBCR(name, hwregno, desc, gdbregno, mask, mode, version) { #name, hwregno, mask, mode }, +static const RegisterInfo bcr[] = +{ + #include "arc-regnums-defs.h" +}; + + +static Boolean test = TRUE; +static Boolean memory_only = FALSE; +static TargetOperations operations; + + +/* -------------------------------------------------------------------------- */ +/* externally visible data */ +/* -------------------------------------------------------------------------- */ + +/* global debug flag */ +Boolean arc_debug_target; + + +/* -------------------------------------------------------------------------- */ +/* local functions */ +/* -------------------------------------------------------------------------- */ + +static void failed(const char* fmt, ...) +{ + va_list ap; + + fprintf(stderr, "*** FAILED: "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + +// exit(EXIT_FAILURE); +} + + +static void read_core_registers(void) +{ + ARC_RegisterNumber r; + + for (r = 0; r < ARC_NR_CORE_REGS; r++) + { + ARC_RegisterContents contents; + JTAG_OperationStatus status = arc_jtag_ops.read_core_reg(r, &contents); + + if (status == JTAG_SUCCESS) + printf("core register %02d: 0x%08X\n", r, contents); + else + failed("could not read core register %02d: %d", r, (int) status); + } +} + + +static void write_core_registers(void) +{ + ARC_RegisterNumber r; + + for (r = 0; r < ARC_NR_CORE_REGS; r++) + { + ARC_RegisterContents contents = 0xDEADBEEF + r; + JTAG_OperationStatus status = arc_jtag_ops.write_core_reg(r, contents); + + if (status == JTAG_SUCCESS) + { + ARC_RegisterContents new_contents; + + status = arc_jtag_ops.read_core_reg(r, &new_contents); + + if (status == JTAG_SUCCESS) + { + if (new_contents != contents) + failed("discrepancy in core register %02d contents: 0x%08X != 0x%08X", r, contents, new_contents); + } + else + failed("could not read back core register %02d: %d", r, (int) status); + } + else + failed("could not write core register %02d: %d", r, (int) status); + } +} + + +static void read_auxiliary_registers(void) +{ + unsigned int i; + + for (i = 0; i < ELEMENTS_IN_ARRAY(aux); i++) + { + const RegisterInfo* info = &aux[i]; + + if (info->mode != WO) + { + ARC_RegisterContents contents; + JTAG_OperationStatus status = arc_jtag_ops.read_aux_reg(info->regno, &contents); + + if (status == JTAG_SUCCESS) + printf("aux register 0x%03x (%-15s): 0x%08X\n", info->regno, info->name, contents); + else + failed("could not read aux register 0x%03x (%s): %d", info->regno, info->name, (int) status); + } + } +} + + +static void write_auxiliary_registers(void) +{ + unsigned int i; + + for (i = 0; i < ELEMENTS_IN_ARRAY(aux); i++) + { + const RegisterInfo* info = &aux[i]; + + if (info->mode != RO) + { + ARC_RegisterContents contents = 0xFFFFFFFF; + JTAG_OperationStatus status = arc_jtag_ops.write_aux_reg(info->regno, contents); + + if (status == JTAG_SUCCESS) + { + if (info->mode != WO) + { + ARC_RegisterContents new_contents; + + status = arc_jtag_ops.read_aux_reg(info->regno, &new_contents); + + if (status == JTAG_SUCCESS) + { + ARC_RegisterContents masked_contents = contents & info->mask; + ARC_RegisterContents masked_new_contents = new_contents & info->mask; + + if (masked_new_contents != masked_contents) + failed("discrepancy in aux register %03x (%s) contents: 0x%08X != 0x%08X", + info->regno, info->name, masked_contents, masked_new_contents); + } + else + failed("could not read back aux register 0x%03x (%s): %d", info->regno, info->name, (int) status); + } + } + else + failed("could not write aux register 0x%03x (%s): %d", info->regno, info->name, (int) status); + } + } +} + + +static void read_build_configuration_registers(void) +{ + unsigned int i; + + for (i = 0; i < ELEMENTS_IN_ARRAY(bcr); i++) + { + const RegisterInfo* info = &bcr[i]; + + /* if the register is not unused */ + if (info->mode != UU) + { + ARC_RegisterContents contents; + JTAG_OperationStatus status = arc_jtag_ops.read_aux_reg(info->regno, &contents); + + if (status == JTAG_SUCCESS) + printf("BCR 0x%02x (%-16s): 0x%08X\n", info->regno, info->name, contents); + else + failed("could not read BCR 0x%02x (%s): %d", info->regno, info->name, (int) status); + } + } +} + + +/* these functions should NOT be used within this module: they are intended + * purely for use by the arc-memory module for reading/writing multiple words + * of data at word-aligned addresses + */ + +static unsigned int read_jtag_words(ARC_Address address, + ARC_Byte* data, + unsigned int words) +{ + DEBUG("reading %u words from 0x%08X in xISS\n", words, address); + + assert(IS_WORD_ALIGNED(address)); + + return arc_jtag_ops.memory_read_chunk(address, data, words); +} + + +static unsigned int write_jtag_words(ARC_Address address, + ARC_Byte* data, + unsigned int words) +{ + assert(IS_WORD_ALIGNED(address)); + + DEBUG("writing %u words to 0x%08X in xISS\n", words, address); + + return arc_jtag_ops.memory_write_chunk(address, data, words); +} + + +static unsigned int write_jtag_pattern(ARC_Address address, + ARC_Word pattern, + unsigned int words) +{ + assert(IS_WORD_ALIGNED(address)); + + DEBUG("writing pattern 0x%08X repeated %u times to 0x%08X in xISS\n", pattern, words, address); + + return arc_jtag_ops.memory_write_pattern(address, pattern, words); +} + + +/* these functions should be used within this module for all memory accesses */ +static unsigned int read_chunk(ARC_Address addr, ARC_Byte* data, unsigned int bytes) +{ + unsigned int total_read; + + ENTERARGS("addr 0x%08X, bytes %u", addr, bytes); + + total_read = arc_read_memory(&operations, addr, data, bytes); + + DEBUG("read %u bytes\n", total_read); + + return total_read; +} + + +static unsigned int write_chunk(ARC_Address addr, ARC_Byte* data, unsigned int bytes) +{ + unsigned int total_written; + + ENTERARGS("addr 0x%08X, bytes %u", addr, bytes); + + total_written = arc_write_memory(&operations, addr, data, bytes); + + DEBUG("written %u bytes\n", total_written); + + return total_written; +} + + +/* write a repeated pattern of data to memory; + * the start of each pattern is always word-aligned, so if the given address is + * not word-aligned, the first partial word written will contain trailing bytes + * of the pattern + */ +static unsigned int write_pattern(ARC_Address addr, ARC_Word pattern, unsigned int bytes) +{ + unsigned int total_written; + + ENTERARGS("addr 0x%08X, pattern 0x%08X, bytes %u", addr, pattern, bytes); + + total_written = arc_write_pattern(&operations, addr, pattern, bytes); + + DEBUG("written %u bytes\n", total_written); + + return total_written; +} + + +/* test word read/write operations - write several words to different + * addresses to ensure that JTAG Data Register does not still hold first + * word; use both contiguous and non-contiguous words to check that + * JTAG Address Register is loaded correctly + */ +static void read_write_memory_words(void) +{ + const ARC_Word write1 = 0xCAFEBABE; + const ARC_Word write2 = 0xDEADBEEF; + const ARC_Word write3 = 0xBABEFACE; + const ARC_Word write4 = 0x12345678; + ARC_Word read1 = 0; + ARC_Word read2 = 0; + ARC_Word read3 = 0; + ARC_Word read4 = 0; + unsigned int bytes; + + bytes = arc_jtag_ops.memory_write_word(DATA_AREA, write1); + if (bytes != 4) + failed("memory word 1 write: %d", bytes); + + bytes = arc_jtag_ops.memory_write_word(DATA_AREA + 4, write2); + if (bytes != 4) + failed("memory word 2 write: %d", bytes); + + bytes = arc_jtag_ops.memory_write_word(DATA_AREA + 8, write3); + if (bytes != 4) + failed("memory word 3 write: %d", bytes); + + bytes = arc_jtag_ops.memory_write_word(DATA_AREA + 20, write4); + if (bytes != 4) + failed("memory word 4 write: %d", bytes); + + bytes = arc_jtag_ops.memory_read_word(DATA_AREA, &read1); + if (bytes != 4) + failed("memory word 1 read: %d", bytes); + + bytes = arc_jtag_ops.memory_read_word(DATA_AREA + 4, &read2); + if (bytes != 4) + failed("memory word 2 read: %d", bytes); + + bytes = arc_jtag_ops.memory_read_word(DATA_AREA + 8, &read3); + if (bytes != 4) + failed("memory word 3 read: %d", bytes); + + bytes = arc_jtag_ops.memory_read_word(DATA_AREA + 20, &read4); + if (bytes != 4) + failed("memory word 4 read: %d", bytes); + + if (read1 != write1) + failed("word 1: %08X != %08X", read1, write1); + + if (read2 != write2) + failed("word 2: %08X != %08X", read2, write2); + + if (read3 != write3) + failed("word 3: %08X != %08X", read3, write3); + + if (read3 != write3) + failed("word 3: %08X != %08X", read3, write3); + + if (read4 != write4) + failed("word 4: %08X != %08X", read4, write4); +} + + +/* test chunk read/write operations */ +static void read_write_memory_chunks(void) +{ + ARC_Byte in [BUFFER_LENGTH]; + ARC_Byte out[BUFFER_LENGTH]; + unsigned int bytes; + unsigned int i; + unsigned int offset; + + /* initialize output buffer to 7 bit values */ + for (i = 0; i < BUFFER_LENGTH; i++) + out[i] = (ARC_Byte) i; + + /* ----------------------------------------------------------- */ + /* initialize data area */ + /* ----------------------------------------------------------- */ + + /* write series of complete words */ + + bytes = write_chunk(DATA_AREA, out, BUFFER_LENGTH); + if (bytes != BUFFER_LENGTH) + failed("memory chunk write: %d", bytes); + + /* and read them back */ + + bytes = read_chunk(DATA_AREA, in, BUFFER_LENGTH); + if (bytes != BUFFER_LENGTH) + failed("memory chunk read: %d", bytes); + + if (memcmp(in, out, BUFFER_LENGTH) != 0) + failed("memory chunk read/write:"); + + + /* ----------------------------------------------------------- */ + /* read operations */ + /* ----------------------------------------------------------- */ + + /* 0) read single word */ + + bytes = read_chunk(DATA_AREA, in, 4); + if (bytes != 4) + failed("memory chunk read word: %d", bytes); + + if (memcmp(in, out, 4) != 0) + failed("memory chunk read word"); + + /* 1) read leading bytes */ + + for (i = 1; i <= 3; i++) + { + bytes = read_chunk(DATA_AREA + 4 - i, in, i); + if (bytes != i) + failed("memory chunk read leading bytes: %d", bytes); + + if (memcmp(in, out + 4 - i, i) != 0) + failed("memory chunk read leading bytes"); + } + + /* 2) read trailing bytes */ + + for (i = 1; i <= 3; i++) + { + bytes = read_chunk(DATA_AREA, in, i); + if (bytes != i) + failed("memory chunk read trailing bytes: %d", bytes); + + if (memcmp(in, out, i) != 0) + failed("memory chunk read trailing bytes"); + } + + /* 3) read leading bytes and series of complete words */ + + bytes = read_chunk(DATA_AREA + 1, in, 11); + if (bytes != 11) + failed("memory chunk read leading bytes and words: %d", bytes); + + if (memcmp(in, out + 1, 11) != 0) + failed("memory chunk read leading bytes and words"); + + /* 4) read series of complete words and trailing bytes */ + + bytes = read_chunk(DATA_AREA, in, 11); + if (bytes != 11) + failed("memory chunk read words and trailing bytes: %d", bytes); + + if (memcmp(in, out, 11) != 0) + failed("memory chunk read words and trailing bytes"); + + /* 5) read leading bytes and trailing bytes */ + + bytes = read_chunk(DATA_AREA + 1, in, 5); + if (bytes != 5) + failed("memory chunk read leading and trailing bytes: %d", bytes); + + if (memcmp(in, out + 1, 5) != 0) + failed("memory chunk read leading and trailing bytes"); + + /* 6) read leading bytes, series of complete words and trailing bytes */ + + bytes = read_chunk(DATA_AREA + 2, in, 23); + if (bytes != 23) + failed("memory chunk read leading bytes, words and trailing bytes: %d", bytes); + + if (memcmp(in, out + 2, 23) != 0) + failed("memory chunk read leading bytes, words and trailing bytes"); + + /* 7) pathological cases: bytes in middle of word */ + + bytes = read_chunk(DATA_AREA + 1, in, 1); + if (bytes != 1) + failed("memory chunk read bytes in middle (1) : %d", bytes); + + if (memcmp(in, out + 1, 1) != 0) + failed("memory chunk read middle bytes (1)"); + + bytes = read_chunk(DATA_AREA + 2, in, 2); + if (bytes != 2) + failed("memory chunk read bytes in middle (2) : %d", bytes); + + if (memcmp(in, out + 2, 2) != 0) + failed("memory chunk read middle bytes (2)"); + + bytes = read_chunk(DATA_AREA + 3, in, 1); + if (bytes != 1) + failed("memory chunk read bytes in middle (3) : %d", bytes); + + if (memcmp(in, out + 3, 1) != 0) + failed("memory chunk read middle bytes (3)"); + + + /* ----------------------------------------------------------- */ + /* write operations */ + /* ----------------------------------------------------------- */ + + /* on each test, read back the whole area: we must check that no other data + * in the area has been changed, hence the use of 8-bit values on the write + * to distinguish them from the 7-bit values used to initialise the area; + * also, a different section of the area is used for each test + */ + + /* 0) write single word */ + + out[0] = 45; + out[1] = 89; + out[2] = 66; + out[3] = 53; + + bytes = write_chunk(DATA_AREA, out, 4); + if (bytes != 4) + failed("memory chunk write word: %d", bytes); + + bytes = read_chunk(DATA_AREA, in, 4); + if (memcmp(in, out, 4) != 0) + failed("memory chunk write word"); + + /* 1) write leading bytes */ + + for (i = 1; i <= 3; i++) + { + offset = 8 - i; + + /* change data in output buffer to 8-bit values */ + *(out + offset) = 128 + i; + + bytes = write_chunk(DATA_AREA + offset, out + offset, i); + if (bytes != i) + failed("memory chunk write leading bytes: %d", bytes); + + bytes = read_chunk(DATA_AREA, in, BUFFER_LENGTH); + if (memcmp(in, out, BUFFER_LENGTH) != 0) + failed("memory chunk write leading bytes"); + } + + /* 2) write trailing bytes */ + + offset = 8; + + for (i = 1; i <= 3; i++) + { + /* change data in output buffer to 8-bit values */ + *(out + offset + i - 1) = 128 + i; + + bytes = write_chunk(DATA_AREA + offset, out + offset, i); + if (bytes != i) + failed("memory chunk write trailing bytes: %d", bytes); + + bytes = read_chunk(DATA_AREA, in, BUFFER_LENGTH); + if (memcmp(in, out, BUFFER_LENGTH) != 0) + failed("memory chunk write trailing bytes"); + } + + /* 3) write leading bytes and series of complete words */ + + offset = 13; + + for (i = 0; i < 11; i++) + *(out + offset + i) = 128 + i; + + bytes = write_chunk(DATA_AREA + offset, out + offset, 11); + if (bytes != 11) + failed("memory chunk write leading bytes and words: %d", bytes); + + bytes = read_chunk(DATA_AREA, in, BUFFER_LENGTH); + if (memcmp(in, out, BUFFER_LENGTH) != 0) + failed("memory chunk write leading bytes and words"); + + /* 4) write series of complete words and trailing bytes */ + + offset = 28; + + for (i = 0; i < 11; i++) + *(out + offset + i) = 128 + i; + + bytes = write_chunk(DATA_AREA + offset, out + offset, 11); + if (bytes != 11) + failed("memory chunk write words and trailing bytes: %d", bytes); + + bytes = read_chunk(DATA_AREA, in, BUFFER_LENGTH); + if (memcmp(in, out, BUFFER_LENGTH) != 0) + failed("memory chunk write words and trailing bytes"); + + /* 5) write leading bytes and trailing bytes */ + + offset = 40; + + for (i = 0; i < 5; i++) + *(out + offset + i) = 128 + i; + + bytes = write_chunk(DATA_AREA + offset, out + offset, 5); + if (bytes != 5) + failed("memory chunk write leading and trailing bytes: %d", bytes); + + bytes = read_chunk(DATA_AREA, in, BUFFER_LENGTH); + if (memcmp(in, out, BUFFER_LENGTH) != 0) + failed("memory chunk write leading and trailing bytes"); + + /* 6) write leading bytes, series of complete words and trailing bytes */ + + offset = 48; + + for (i = 0; i < 23; i++) + *(out + offset + i) = 128 + i; + + bytes = write_chunk(DATA_AREA + offset, out + offset, 23); + if (bytes != 23) + failed("memory chunk write leading bytes, words and trailing bytes: %d", bytes); + + bytes = read_chunk(DATA_AREA, in, BUFFER_LENGTH); + if (memcmp(in, out, BUFFER_LENGTH) != 0) + failed("memory chunk write leading bytes, words and trailing bytes"); + + /* 7) pathological cases: bytes in middle of word */ + + offset = 85; + *(out + offset) = 128; + + bytes = write_chunk(DATA_AREA + offset, out + offset, 1); + if (bytes != 1) + failed("memory chunk write bytes in middle (1) : %d", bytes); + + bytes = read_chunk(DATA_AREA, in, BUFFER_LENGTH); + if (memcmp(in, out, BUFFER_LENGTH) != 0) + failed("memory chunk write middle bytes (1)"); + + offset = 95; + *(out + offset) = 129; + *(out + offset + 1) = 130; + + bytes = write_chunk(DATA_AREA + offset, out + offset, 2); + if (bytes != 2) + failed("memory chunk write bytes in middle (2) : %d", bytes); + + bytes = read_chunk(DATA_AREA, in, BUFFER_LENGTH); + if (memcmp(in, out, BUFFER_LENGTH) != 0) + failed("memory chunk write middle bytes (2)"); + + offset = 106; + *(out + offset) = 131; + + bytes = write_chunk(DATA_AREA + offset, out + offset, 1); + if (bytes != 1) + failed("memory chunk write bytes in middle (3) : %d", bytes); + + bytes = read_chunk(DATA_AREA, in, BUFFER_LENGTH); + if (memcmp(in, out, BUFFER_LENGTH) != 0) + failed("memory chunk write middle bytes (3)"); + + + /* ----------------------------------------------------------- */ + /* write zeroes operation */ + /* ----------------------------------------------------------- */ + + offset = 110; + + for (i = 0; i < 15; i++) + *(out + offset + i) = 0; + + bytes = write_pattern(DATA_AREA + offset, 0, 15); + if (bytes != 15) + failed("memory zero: %d", bytes); + + bytes = read_chunk(DATA_AREA, in, BUFFER_LENGTH); + if (memcmp(in, out, BUFFER_LENGTH) != 0) + failed("memory zero"); + + + /* ----------------------------------------------------------- */ + /* write pattern operation */ + /* ----------------------------------------------------------- */ + + offset = 38; + + /* the pattern will be word-aligned */ + +#ifdef TARGET_IS_BIG_ENDIAN + *(out + offset + 0) = 0xBE; // leading bytes + *(out + offset + 1) = 0xEF; + + *(out + offset + 2) = 0xDE; // word 0 + *(out + offset + 3) = 0xAD; + *(out + offset + 4) = 0xBE; + *(out + offset + 5) = 0xEF; + + *(out + offset + 6) = 0xDE; // word 1 + *(out + offset + 7) = 0xAD; + *(out + offset + 8) = 0xBE; + *(out + offset + 9) = 0xEF; + + *(out + offset + 10) = 0xDE; // word 2 + *(out + offset + 11) = 0xAD; + *(out + offset + 12) = 0xBE; + *(out + offset + 13) = 0xEF; + + *(out + offset + 14) = 0xDE; // word 3 + *(out + offset + 15) = 0xAD; + *(out + offset + 16) = 0xBE; + *(out + offset + 17) = 0xEF; + + *(out + offset + 18) = 0xDE; // trailing bytes + *(out + offset + 19) = 0xAD; +#else + *(out + offset + 0) = 0xAD; // leading bytes + *(out + offset + 1) = 0xDE; + + *(out + offset + 5) = 0xDE; // word 0 + *(out + offset + 4) = 0xAD; + *(out + offset + 3) = 0xBE; + *(out + offset + 2) = 0xEF; + + *(out + offset + 9) = 0xDE; // word 1 + *(out + offset + 8) = 0xAD; + *(out + offset + 7) = 0xBE; + *(out + offset + 6) = 0xEF; + + *(out + offset + 13) = 0xDE; // word 2 + *(out + offset + 12) = 0xAD; + *(out + offset + 11) = 0xBE; + *(out + offset + 10) = 0xEF; + + *(out + offset + 17) = 0xDE; // word 3 + *(out + offset + 16) = 0xAD; + *(out + offset + 15) = 0xBE; + *(out + offset + 14) = 0xEF; + + *(out + offset + 18) = 0xEF; // trailing bytes + *(out + offset + 19) = 0xBE; +#endif + + bytes = write_pattern(DATA_AREA + offset, 0xDEADBEEF, 20); + if (bytes != 20) + failed("memory pattern: %d", bytes); + + bytes = read_chunk(DATA_AREA, in, BUFFER_LENGTH); + if (memcmp(in, out, BUFFER_LENGTH) != 0) + failed("memory pattern"); + + /* ----------------------------------------------------------- */ + /* write repeated values operation */ + /* ----------------------------------------------------------- */ + + /* initialize output buffer to the same value */ + for (i = 0; i < BUFFER_LENGTH; i++) + out[i] = 0xA; + + bytes = write_chunk(DATA_AREA, out, BUFFER_LENGTH); + if (bytes != BUFFER_LENGTH) + failed("memory chunk write repeated values : %d", bytes); + + bytes = read_chunk(DATA_AREA, in, BUFFER_LENGTH); + if (memcmp(in, out, BUFFER_LENGTH) != 0) + failed("memory chunk write repeated values"); +} + + +static void run_tests(void) +{ + if (!memory_only) + { + read_core_registers(); + read_auxiliary_registers(); + read_build_configuration_registers(); + + write_core_registers(); + write_auxiliary_registers(); + } + + read_write_memory_words(); + read_write_memory_chunks(); +} + + +static void process_options(int argc, char** argv) +{ + int c; + + while ((c = getopt (argc, argv, "mdcr:")) != -1) + { + switch (c) + { + case 'd': + arc_jtag_ops.state_machine_debug = TRUE; + break; + case 'r': + arc_jtag_ops.retry_count = atoi(optarg); + break; + case 'c': + test = FALSE; + break; + case 'm': + memory_only = TRUE; + break; + default: + fprintf(stderr, "Usage: %s [ -d ]\n", argv[0]); + exit(EXIT_FAILURE); + } + } +} + + +/* -------------------------------------------------------------------------- */ +/* externally visible functions */ +/* -------------------------------------------------------------------------- */ + +extern void _initialize_arc_jtag_ops(void); + + +int main(int argc, char** argv) +{ + Boolean opened; + + printf("Starting test of ARC JTAG interface...\n"); + + _initialize_arc_jtag_ops(); + + process_options(argc, argv); + + opened = arc_jtag_ops.open(); + + if (opened) + { + printf("ARC processor is connected\n"); + + if (test) + { + operations.read_core_register = NULL; + operations.write_core_register = NULL; + operations.read_auxiliary_register = NULL; + operations.write_auxiliary_register = NULL; + operations.read_memory = read_jtag_words; + operations.write_memory = write_jtag_words; + operations.fill_memory = write_jtag_pattern; + + run_tests(); + printf("resetting board...\n"); + arc_jtag_ops.reset_board(); + printf("board reset\n"); + run_tests(); + } + + arc_jtag_ops.close(); + } + + printf("Finished test of ARC JTAG interface\n"); + + return 0; +} + +/******************************************************************************/ diff --git a/gdb/Makefile.in b/gdb/Makefile.in index f52607342f6..20cce290f7b 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -1,5 +1,5 @@ # Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, -# 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 +# 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 # Free Software Foundation, Inc. # This file is part of GDB. @@ -141,6 +141,9 @@ READLINE_CFLAGS = @READLINE_CFLAGS@ # Where is expat? This will be empty if expat was not available. LIBEXPAT = @LIBEXPAT@ +# Where are the ARC xISS header files? +XISS_INCLUDES = @XISS_INCLUDES@ + WARN_CFLAGS = @WARN_CFLAGS@ WERROR_CFLAGS = @WERROR_CFLAGS@ GDB_WARN_CFLAGS = $(WARN_CFLAGS) @@ -364,7 +367,8 @@ INTERNAL_CFLAGS_BASE = \ $(CFLAGS) $(GLOBAL_CFLAGS) $(PROFILE_CFLAGS) \ $(GDB_CFLAGS) $(OPCODES_CFLAGS) $(READLINE_CFLAGS) \ $(BFD_CFLAGS) $(INCLUDE_CFLAGS) $(LIBDECNUMBER_CFLAGS) \ - $(INTL_CFLAGS) $(ENABLE_CFLAGS) $(INTERNAL_CPPFLAGS) + $(INTL_CFLAGS) $(ENABLE_CFLAGS) $(INTERNAL_CPPFLAGS) \ + $(XISS_INCLUDES) INTERNAL_WARN_CFLAGS = $(INTERNAL_CFLAGS_BASE) $(GDB_WARN_CFLAGS) INTERNAL_CFLAGS = $(INTERNAL_WARN_CFLAGS) $(GDB_WERROR_CFLAGS) @@ -392,7 +396,7 @@ INSTALLED_LIBS=-lbfd -lreadline -lopcodes -liberty -ldecnumber \ CLIBS = $(SIM) $(READLINE) $(OPCODES) $(BFD) $(INTL) $(LIBIBERTY) $(LIBDECNUMBER) \ $(XM_CLIBS) $(TM_CLIBS) $(NAT_CLIBS) $(GDBTKLIBS) @LIBS@ \ $(LIBICONV) $(LIBEXPAT) \ - $(LIBIBERTY) $(WIN32LIBS) + $(LIBIBERTY) $(WIN32LIBS) -lz CDEPS = $(XM_CDEPS) $(TM_CDEPS) $(NAT_CDEPS) $(SIM) $(BFD) $(READLINE_DEPS) \ $(OPCODES) $(INTL_DEPS) $(LIBIBERTY) $(CONFIG_DEPS) @@ -411,7 +415,8 @@ RUNTESTFLAGS= # XML files to build in to GDB. XMLFILES = $(srcdir)/features/gdb-target.dtd $(srcdir)/features/xinclude.dtd \ - $(srcdir)/features/library-list.dtd + $(srcdir)/features/library-list.dtd \ + $(srcdir)/features/arc-registers.dtd # This is ser-unix.o for any system which supports a v7/BSD/SYSV/POSIX # interface to the serial port. Hopefully if get ported to OS/2, VMS, @@ -423,7 +428,7 @@ SER_HARDWIRE = @SER_HARDWIRE@ # The `remote' debugging target is supported for most architectures, # but not all (e.g. 960) -REMOTE_OBS = remote.o dcache.o tracepoint.o ax-general.o ax-gdb.o remote-fileio.o +REMOTE_OBS = remote.o dcache.o tracepoint.o ax-general.o ax-gdb.o remote-fileio.o target-fileio.o # This is remote-sim.o if a simulator is to be linked in. SIM_OBS = @SIM_OBS@ @@ -624,7 +629,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c \ solib.c solib-null.c source.c \ stabsread.c stack.c std-regs.c symfile.c symfile-mem.c symmisc.c \ symtab.c \ - target.c target-descriptions.c target-memory.c \ + target.c target-descriptions.c target-fileio.c target-memory.c \ thread.c top.c tracepoint.c \ trad-frame.c \ tramp-frame.c \ @@ -647,6 +652,7 @@ aout_stabs_gnu_h = $(INCLUDE_DIR)/aout/stabs_gnu.h getopt_h = $(INCLUDE_DIR)/getopt.h floatformat_h = $(INCLUDE_DIR)/floatformat.h bfd_h = $(BFD_DIR)/bfd.h +bfd_in2_h = $(BFD_DIR)/bfd-in2.h coff_sym_h = $(INCLUDE_DIR)/coff/sym.h coff_symconst_h = $(INCLUDE_DIR)/coff/symconst.h coff_ecoff_h = $(INCLUDE_DIR)/coff/ecoff.h @@ -731,10 +737,25 @@ annotate_h = annotate.h $(symtab_h) $(gdbtypes_h) arch_utils_h = arch-utils.h arm_linux_tdep_h = arm-linux-tdep.h arm_tdep_h = arm-tdep.h +arc_board_h = arc-board.h +arc_gpio_h = arc-gpio.h arc_tdep_h = arc-tdep.h arc_jtag_h = arc-jtag.h +arc_xiss_h = arc-xiss.h +arc_inst_tracing_h = arc-inst-tracing.h +arc_memory_h = arc-memory.h +arc_arguments_h = arc-arguments.h +arc_elf32_tdep_h = arc-elf32-tdep.h +arc_linux_tdep_h = arc-linux-tdep.h arc_jtag_ops_h = arc-jtag-ops.h -arc_regnums_defs_h = arc-regnums-defs.h +arc_tm_linux_h = config/arc/tm-linux.h +arc_tm_embed_h = config/arc/tm-embed.h +arc_sim_registers_h = ../sim/arc/arc-sim-registers.h +arc_jtag_actionpoints_h = arc-jtag-actionpoints.h +arc_registers_h = arc-registers.h +arc_remote_fileio_h = arc-remote-fileio.h +arc_architecture_h = arc-architecture.h +arc_support_h = arc-support.h auxv_h = auxv.h ax_gdb_h = ax-gdb.h ax_h = ax.h $(doublest_h) @@ -849,6 +870,8 @@ nto_tdep_h = nto-tdep.h $(solist_h) $(osabi_h) $(regset_h) objc_lang_h = objc-lang.h objfiles_h = objfiles.h $(gdb_obstack_h) $(symfile_h) obsd_tdep_h = obsd-tdep.h +opcodes_arc_dis_h = $(OPCODES_DIR)/arc-dis.h +opcodes_arcompact_dis_h = $(OPCODES_DIR)/arcompact-dis.h osabi_h = osabi.h parser_defs_h = parser-defs.h $(doublest_h) p_lang_h = p-lang.h @@ -895,6 +918,7 @@ symfile_h = symfile.h $(symtab_h) symtab_h = symtab.h target_h = target.h $(bfd_h) $(symtab_h) $(dcache_h) $(memattr_h) $(vec_h) target_descriptions_h = target-descriptions.h +target_fileio_h = target-fileio.h terminal_h = terminal.h top_h = top.h tracepoint_h = tracepoint.h @@ -1535,8 +1559,11 @@ ALLDEPFILES = \ amd64obsd-nat.c amd64obsd-tdep.c \ amd64-linux-nat.c amd64-linux-tdep.c \ amd64-sol2-tdep.c \ - arc-tdep.c arc-linux-tdep.c arc-jtag.c \ - arc-jtag-ops.c arc-jtag.c \ + arc-jtag-actionpoints.c arc-remote-fileio.c \ + arc-tdep.c arc-linux-tdep.c arc-jtag.c arc-architecture.c \ + arc-jtag-ops.c arc-jtag.c arc-registers.c arc-board.c \ + arc-xiss.c arc-gpio.c arc-memory.c arc-arguments.c \ + arc-inst-tracing.c \ arm-linux-nat.c arm-linux-tdep.c arm-tdep.c \ armnbsd-nat.c armbsd-tdep.c armnbsd-tdep.c armobsd-tdep.c \ avr-tdep.c \ @@ -1901,20 +1928,78 @@ arch-utils.o: arch-utils.c $(defs_h) $(arch_utils_h) $(buildsym_h) \ $(gdbcmd_h) $(inferior_h) $(gdb_string_h) $(regcache_h) \ $(gdb_assert_h) $(sim_regno_h) $(gdbcore_h) $(osabi_h) $(version_h) \ $(floatformat_h) $(target_descriptions_h) -arc-jtag.o:arc-jtag.c $(arc_jtag_h) $(defs_h) $(gdbcore_h) \ - $(arc_regnums_defs_h) $(gdbcmd_h) -arc-jtag-ops.o: arc-jtag-ops.c $(arc_jtag_ops_h) $(arc_tdep_h) -arc-jtag-tdep.o:arc-jtag-tdep.c $(arc_tdep_h) $(arc_jtag_h) $(defs_h) \ - $(osabi_h) $(frame_h) $(regcache_h) $(gdb_assert_h) $(inferior_h) \ - $(arc_tdep_h) $(arc_jtag_h) -arc-linux-tdep.o: arc-linux-tdep.c $(defs_h) $(osabi_h) $(frame_h) \ - $(regcache_h) $(gdb_assert_h) $(inferior_h) $(reggroups_h) \ - $(solib_svr4_h) $(symtab_h) $(objfiles_h) $(block_h) $(arc_tdep_h) -arc-tdep.o: arc-tdep.c $(defs_h) $(arch_utils_h) $(frame_h) $(inferior_h) \ - $(gdbcmd_h) $(gdbcore_h) $(gdb_string_h) $(dis_asm_h) $(regcache_h) \ - $(doublest_h) $(value_h) $(frame_unwind_h) $(frame_base_h) \ - $(trad_frame_h) $(arc_tdep_h) $(elf_bfd_h) $(elf_arc_h) \ - $(opcode_arc_h) $(gdb_assert_h) $(bfd_in2_h) $(observer_h) +arc-architecture.o: arc-architecture.c \ + $(arc_architecture_h) $(arc_registers_h) $(arch_utils_h) \ + $(arc_elf32_tdep_h) $(arc_support_h) $(arc_tdep_h) $(bfd_in2_h) \ + $(defs_h) $(gdbarch_h) $(gdb_events_h) $(objfiles_h) $(breakpoint_h) \ + $(target_h) $(gdb_assert_h) + $(CC) -c $(INTERNAL_CFLAGS) -Wno-format-nonliteral $< +arc-registers.o: arc-registers.c \ + $(arc_architecture_h) $(arc_registers_h) $(arc_elf32_tdep_h) \ + $(arc_sim_registers_h) $(arc_support_h) $(arc_tdep_h) \ + $(arc_tm_embed_h) $(bfd_in2_h) $(defs_h) $(gdbarch_h) $(gdbcmd_h) \ + $(inferior_h) $(objfiles_h) $(regcache_h) \ + $(target_h) $(xml_support_h) $(breakpoint_h) $(gdb_assert_h) + $(CC) -c $(INTERNAL_CFLAGS) -Wno-format-nonliteral $< +arc-board.o: arc-board.c $(arc_architecture_h) $(arc_registers_h) \ + $(arc_board_h) $(arc_gpio_h) $(arc_jtag_h) $(arc_jtag_ops_h) \ + $(arc_support_h) $(arc_tdep_h) $(bfd_in2_h) \ + $(completer_h) $(defs_h) $(gdbarch_h) $(gdbcmd_h) $(objfiles_h) + $(CC) -c $(INTERNAL_CFLAGS) -Wno-format-nonliteral $< +arc-gpio.o: arc-gpio.c \ + $(arc_gpio_h) $(arc_support_h) $(defs_h) +arc-jtag.o: arc-jtag.c \ + $(arc_architecture_h) $(arc_registers_h) $(arc_board_h) \ + $(arc_gpio_h) $(arc_jtag_actionpoints_h) $(arc_jtag_h) \ + $(arc_jtag_ops_h) $(arc_elf32_tdep_h) $(arc_support_h) $(arc_tdep_h) \ + $(arc_tm_embed_h) $(bfd_in2_h) $(breakpoint_h) $(defs_h) $(gdbarch_h) \ + $(gdbcmd_h) $(inferior_h) $(libiberty_h) $(objfiles_h) $(target_h) \ + $(gdb_assert_h) +arc-arguments.o: arc-arguments.c $(arc_arguments_h) $(arc_support_h) \ + $(arc_tdep_h) $(defs_h) $(objfiles_h) \ + $(regcache_h) $(symtab_h) $(target_h) +arc-memory.o: arc-memory.c \ + $(arc_memory_h) $(arc_tdep_h) $(arc_support_h) $(defs_h) +arc-inst-tracing.o: arc-inst-tracing.c \ + $(arc_inst_tracing_h) $(arc_tdep_h) $(arc_support_h) +arc-xiss.o: arc-xiss.c \ + $(arc_architecture_h) $(arc_elf32_tdep_h) $(arc_registers_h) \ + $(arc_support_h) $(arc_tdep_h) $(arc_tm_embed_h) $(arc_xiss_h) \ + $(arc_inst_tracing_h) $(bfd_in2_h) $(breakpoint_h) $(defs_h) \ + $(disasm_h) $(gdbarch_h) $(gdbcmd_h) $(inferior_h) $(libiberty_h) \ + $(objfiles_h) $(completer_h) $(target_h) $(gdb_assert_h) +arc-jtag-ops.o: arc-jtag-ops.c $(arc_gpio_h) $(arc_jtag_h) $(arc_jtag_ops_h) \ + $(arc_support_h) $(defs_h) $(gdb_assert_h) +arc-elf32-tdep.o:arc-elf32-tdep.c $(arc_architecture_h) $(arc_registers_h) \ + $(arch_utils_h) $(arc_remote_fileio_h) $(arc_memory_h) \ + $(arc_elf32_tdep_h) $(arc_support_h) $(arc_tdep_h) $(arc_tm_embed_h) \ + $(arc_arguments_h) $(bfd_in2_h) $(defs_h) $(gdbarch_h) $(gdbcmd_h) \ + $(gdb_events_h) $(inferior_h) $(objfiles_h) \ + $(observer_h) $(exceptions_h) $(regcache_h) $(reggroups_h) \ + $(gdbcore_h) $(breakpoint_h) $(target_h) $(gdb_assert_h) + $(CC) -c $(INTERNAL_CFLAGS) -Wno-format-nonliteral $< +arc-linux-tdep.o: arc-linux-tdep.c $(arc_linux_tdep_h) $(arc_support_h) \ + $(arc_tdep_h) $(arc_tm_linux_h) $(block_h) $(defs_h) $(dis_asm_h) \ + $(gdbarch_h) $(inferior_h) $(opcode_arc_h) $(osabi_h) $(regcache_h) \ + $(reggroups_h) $(regset_h) $(solib_svr4_h) $(gdb_assert_h) +arc-tdep.o: arc-tdep.c $(arch_utils_h) $(arc_support_h) $(arc_tdep_h) \ + $(arc_tm_linux_h) $(arc_tm_embed_h) \ + $(block_h) $(defs_h) $(demangle_h) $(dictionary_h) $(dis_asm_h) \ + $(dwarf2_frame_h) $(frame_h) $(frame_unwind_h) $(gdbcmd_h) \ + $(gdbcore_h) $(inferior_h) $(language_h) \ + $(objfiles_h) $(observer_h) $(opcode_arc_h) $(opcodes_arc_dis_h) \ + $(opcodes_arcompact_dis_h) $(osabi_h) $(regcache_h) $(reggroups_h) \ + $(trad_frame_h) $(gdb_assert_h) +arc-jtag-actionpoints.o: arc-jtag-actionpoints.c $(arc_architecture_h) \ + $(arc_registers_h) $(arc_jtag_actionpoints_h) $(arc_jtag_h) \ + $(arc_jtag_ops_h) $(arc_support_h) $(arc_tdep_h) $(arc_elf32_tdep_h) \ + $(bfd_in2_h) $(defs_h) $(gdbarch_h) $(gdb_assert_h) $(inferior_h) \ + $(target_h) $(breakpoint_h) +arc-remote-fileio.o: arc-remote-fileio.c \ + $(arc_remote_fileio_h) $(arc_support_h) $(arc_tm_embed_h) \ + $(block_h) $(defs_h) $(frame_h) $(target_h) \ + $(gdb_fileio_h) $(symtab_h) $(target_fileio_h) $(exceptions_h) + $(CC) -c $(INTERNAL_CFLAGS) -Wno-format-nonliteral $< arm-linux-nat.o: arm-linux-nat.c $(defs_h) $(inferior_h) $(gdbcore_h) \ $(gdb_string_h) $(regcache_h) $(arm_tdep_h) $(gregset_h) \ $(target_h) $(linux_nat_h) $(gdb_proc_service_h) $(arm_linux_tdep_h) \ @@ -2645,8 +2730,8 @@ remote.o: remote.c $(defs_h) $(gdb_string_h) $(inferior_h) $(bfd_h) \ $(cli_decode_h) $(cli_setshow_h) $(memory_map_h) \ $(target_descriptions_h) $(gdb_fileio_h) remote-fileio.o: remote-fileio.c $(defs_h) $(gdb_string_h) $(gdbcmd_h) \ - $(remote_h) $(gdb_fileio_h) $(gdb_wait_h) $(gdb_stat_h) \ - $(exceptions_h) $(remote_fileio_h) + $(remote_h) $(gdb_fileio_h) \ + $(exceptions_h) $(remote_fileio_h) $(target_fileio_h) remote-m32r-sdi.o: remote-m32r-sdi.c $(defs_h) $(gdbcmd_h) $(gdbcore_h) \ $(inferior_h) $(target_h) $(regcache_h) $(gdb_string_h) $(serial_h) remote-mips.o: remote-mips.c $(defs_h) $(inferior_h) $(bfd_h) $(symfile_h) \ @@ -2898,6 +2983,9 @@ target-descriptions.o: target-descriptions.c $(defs_h) $(arch_utils_h) \ $(target_h) $(target_descriptions_h) $(vec_h) $(xml_tdesc_h) \ $(gdbcmd_h) $(gdb_assert_h) $(gdbtypes_h) $(reggroups_h) \ $(xml_support_h) $(gdb_obstack_h) $(hashtab_h) +target-fileio.o: target-fileio.c $(defs_h) $(exceptions_h) $(gdbcmd_h) \ + $(gdb_fileio_h) $(gdb_stat_h) $(gdb_string_h) $(gdb_wait_h) \ + $(target_fileio_h) target-memory.o: target-memory.c $(defs_h) $(vec_h) $(target_h) \ $(memory_map_h) $(gdb_assert_h) thread.o: thread.c $(defs_h) $(symtab_h) $(frame_h) $(inferior_h) \ diff --git a/gdb/arc-architecture.c b/gdb/arc-architecture.c new file mode 100755 index 00000000000..f520d6141f1 --- /dev/null +++ b/gdb/arc-architecture.c @@ -0,0 +1,502 @@ +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. + + Copyright 2008, 2009 Free Software Foundation, Inc. + + Contributed by ARC International (www.arc.com) + + Author: + Richard Stuckey <richard.stuckey@arc.com> + + 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 3 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, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This module provides support for handling the architectural version of */ +/* of the ARC target processor. */ +/* */ +/* Architectural Checks: */ +/* Checks upon architectural consistency are (currently) performed at */ +/* these points: */ +/* */ +/* 1) after connection to target */ +/* 2) after reading (non-default) XML file */ +/* 3) after blasting JTAG target FPGA */ +/* 4) after attaching to JTAG target */ +/* 5) after selecting executable file */ +/* 6) before downloading program to target */ +/* */ +/******************************************************************************/ + +/* gdb header files. */ +#include "defs.h" +#include "objfiles.h" +#include "arch-utils.h" +#include "gdb-events.h" +#include "gdb_assert.h" + +/* ARC header files. */ +#include "arc-architecture.h" +#include "arc-elf32-tdep.h" + + +/* -------------------------------------------------------------------------- */ +/* local types */ +/* -------------------------------------------------------------------------- */ + +typedef enum +{ + DEFAULT_SIMULATOR_WITH_NO_AUXILIARY_REGISTERS, + DEFAULT_SIMULATOR_WITH_AUXILIARY_REGISTERS, + SELECTED_SIMULATOR_WITH_NO_AUXILIARY_REGISTERS, + SELECTED_SIMULATOR_WITH_AUXILIARY_REGISTERS, + TARGET_WITH_NO_AUXILIARY_REGISTERS, + TARGET_WITH_AUXILIARY_REGISTERS, + EXECUTABLE_WITH_NO_AUXILIARY_REGISTERS, + EXECUTABLE_WITH_AUXILIARY_REGISTERS, + DEFAULT_SIMULATOR_AND_EXECUTABLE_WITH_NO_AUXILIARY_REGISTERS, + DEFAULT_SIMULATOR_AND_EXECUTABLE_WITH_AUXILIARY_REGISTERS, + SELECTED_SIMULATOR_AND_EXECUTABLE_WITH_NO_AUXILIARY_REGISTERS, + SELECTED_SIMULATOR_AND_EXECUTABLE_WITH_AUXILIARY_REGISTERS, + TARGET_AND_EXECUTABLE_WITH_NO_AUXILIARY_REGISTERS, + TARGET_AND_EXECUTABLE_WITH_AUXILIARY_REGISTERS, + NO_ERROR +} Diagnostic; + + +/* -------------------------------------------------------------------------- */ +/* local data */ +/* -------------------------------------------------------------------------- */ + +/* N.B. the messages MUST correspond to the values in the Diagnostic enumeration + type above, as a value of that type is used to index the array. */ + +static const char *messages[] = +{ + "default simulator architecture is %s but auxiliary registers are not defined", + "default simulator architecture is %s but auxiliary registers are defined for %s", + "selected simulator architecture is %s but auxiliary registers are not defined", + "selected simulator architecture is %s but auxiliary registers are defined for %s", + "target architecture is %s but auxiliary registers are not defined", + "target architecture is %s but auxiliary registers are defined for %s", + "executable file %s is built for %s but auxiliary registers are not defined", + "executable file %s is built for %s but auxiliary registers are defined for %s", + "default simulator architecture is %s, executable file %s is built for %s but auxiliary registers are not defined", + "default simulator architecture is %s, executable file %s is built for %s and auxiliary registers are defined for %s", + "selected simulator is %s, executable file %s is built for %s but auxiliary registers are not defined", + "selected simulator is %s, executable file %s is built for %s and auxiliary registers are defined for %s", + "target architecture is %s, executable file %s is built for %s but auxiliary registers are not defined", + "target architecture is %s, executable file %s is built for %s and auxiliary registers are defined for %s" +}; + + +/* This is the architecture of the current target. */ +static ARC_ProcessorVersion ARC_processor = NO_ARCHITECTURE; + + +/* -------------------------------------------------------------------------- */ +/* local functions */ +/* -------------------------------------------------------------------------- */ + +/* Map the BFD architerctural type onto the ARC processor type. */ + +static ARC_ProcessorVersion +architecture (const bfd_arch_info_type *arch) +{ + switch (arch->mach) + { + case bfd_mach_arc_a5 : return A5; + case bfd_mach_arc_arc600: return ARC600; + case bfd_mach_arc_arc700: return ARC700; + default : return UNSUPPORTED_ARCHITECTURE; + } +} + + +/* This function performs the architectural consistency check. + + Parameters: + target : the architectural version of the target + auxregs : the architectural version of the auxiliary registers + program : the architectural version of the executable program + is_builtin_simulator: TRUE if the target is the built-in simulator + is_default : TRUE if built-in simulator's version is the default + objfile_bfd : a pointer to the BFD for the object file + + If the check fails, an appropriate warning message is output. */ + +static void +perform_check (ARC_ProcessorVersion target, + ARC_ProcessorVersion auxregs, + ARC_ProcessorVersion program, + Boolean is_builtin_simulator, + bfd_boolean is_default, + bfd *objfile_bfd) +{ + Diagnostic diagnostic = NO_ERROR; + + /* If we do not yet have an object file (and hence we do not know the + program's architecture). */ + if (program == NO_ARCHITECTURE) + { + if (target != auxregs) + { + if (is_builtin_simulator) + { + if (is_default) + { + if (auxregs == NO_ARCHITECTURE) + diagnostic = DEFAULT_SIMULATOR_WITH_NO_AUXILIARY_REGISTERS; + else + diagnostic = DEFAULT_SIMULATOR_WITH_AUXILIARY_REGISTERS; + } + else + { + if (auxregs == NO_ARCHITECTURE) + diagnostic = SELECTED_SIMULATOR_WITH_NO_AUXILIARY_REGISTERS; + else + diagnostic = SELECTED_SIMULATOR_WITH_AUXILIARY_REGISTERS; + } + } + else + if (auxregs == NO_ARCHITECTURE) + diagnostic = TARGET_WITH_NO_AUXILIARY_REGISTERS; + else + diagnostic = TARGET_WITH_AUXILIARY_REGISTERS; + } + } + else + { + if (program != target || program != auxregs) + { + if (is_builtin_simulator) + { + if (target == program) + { + if (auxregs == NO_ARCHITECTURE) + diagnostic = EXECUTABLE_WITH_NO_AUXILIARY_REGISTERS; + else + diagnostic = EXECUTABLE_WITH_AUXILIARY_REGISTERS; + } + else + { + if (is_default) + { + if (auxregs == NO_ARCHITECTURE) + diagnostic = DEFAULT_SIMULATOR_AND_EXECUTABLE_WITH_NO_AUXILIARY_REGISTERS; + else + diagnostic = DEFAULT_SIMULATOR_AND_EXECUTABLE_WITH_AUXILIARY_REGISTERS; + } + else + { + if (auxregs == NO_ARCHITECTURE) + diagnostic = SELECTED_SIMULATOR_AND_EXECUTABLE_WITH_NO_AUXILIARY_REGISTERS; + else + diagnostic = SELECTED_SIMULATOR_AND_EXECUTABLE_WITH_AUXILIARY_REGISTERS; + } + } + } + else + if (auxregs == NO_ARCHITECTURE) + diagnostic = TARGET_AND_EXECUTABLE_WITH_NO_AUXILIARY_REGISTERS; + else + diagnostic = TARGET_AND_EXECUTABLE_WITH_AUXILIARY_REGISTERS; + } + } + + if (diagnostic != NO_ERROR) + { + const char *message = messages[diagnostic]; + + switch (diagnostic) + { + case DEFAULT_SIMULATOR_WITH_NO_AUXILIARY_REGISTERS: + case SELECTED_SIMULATOR_WITH_NO_AUXILIARY_REGISTERS: + case TARGET_WITH_NO_AUXILIARY_REGISTERS: + warning(message, arc_version_image(target)); + break; + + case DEFAULT_SIMULATOR_WITH_AUXILIARY_REGISTERS: + case SELECTED_SIMULATOR_WITH_AUXILIARY_REGISTERS: + case TARGET_WITH_AUXILIARY_REGISTERS: + warning(message, arc_version_image(target), arc_version_image(auxregs)); + break; + + case EXECUTABLE_WITH_NO_AUXILIARY_REGISTERS: + warning(message, objfile_bfd->filename, arc_version_image(program)); + break; + + case EXECUTABLE_WITH_AUXILIARY_REGISTERS: + warning(message, objfile_bfd->filename, arc_version_image(program), arc_version_image(auxregs)); + break; + + case DEFAULT_SIMULATOR_AND_EXECUTABLE_WITH_NO_AUXILIARY_REGISTERS: + case SELECTED_SIMULATOR_AND_EXECUTABLE_WITH_NO_AUXILIARY_REGISTERS: + case TARGET_AND_EXECUTABLE_WITH_NO_AUXILIARY_REGISTERS: + warning(message, arc_version_image(target), objfile_bfd->filename, arc_version_image(program)); + break; + + case DEFAULT_SIMULATOR_AND_EXECUTABLE_WITH_AUXILIARY_REGISTERS: + case SELECTED_SIMULATOR_AND_EXECUTABLE_WITH_AUXILIARY_REGISTERS: + case TARGET_AND_EXECUTABLE_WITH_AUXILIARY_REGISTERS: + warning(message, arc_version_image(target), objfile_bfd->filename, arc_version_image(program), arc_version_image(auxregs)); + break; + + default: + break; + } + } +} + + +/* -------------------------------------------------------------------------- */ +/* externally visible functions */ +/* -------------------------------------------------------------------------- */ + +/* Set the architectural version of the target processor as being unknown. */ + +void +arc_architecture_is_unknown (void) +{ + ARC_processor = NO_ARCHITECTURE; +} + + +/* Return the processor variant that is connected. */ + +ARC_ProcessorVersion +arc_get_architecture (ReadRegisterFunction read_aux_register) +{ + ENTERMSG; + + if (ARC_processor == NO_ARCHITECTURE) + { + ARC_RegisterNumber identity_regnum = arc_aux_find_register_number("IDENTITY", ARC_HW_IDENTITY_REGNUM); + ARC_RegisterContents value; + + if (read_aux_register(identity_regnum, &value, TRUE)) + { + DEBUG("IDENTITY = 0x%X\n", value); + + /* Get the processor version number. */ + value &= IDENTITY_ARCVER; + + if ((value >= 0x30) && (value <= 0x3f)) + ARC_processor = ARC700; + else if ((value >= 0x20) && (value <= 0x2f)) + ARC_processor = ARC600; + else if ((value >= 0x10) && (value <= 0x1f)) + ARC_processor = A5; + else if (value <= 0x0f) + ARC_processor = A4; + else + warning(_("unsupported processor version 0x%x"), value); + + DEBUG("target (from IDENTITY) is %s\n", arc_version_image(ARC_processor)); + } + } + + return ARC_processor; +} + + +/* Get the target processor architecture, check that is it supported, and + * update any architecture-related information that gdb requires. */ + +void +arc_update_architecture (ReadRegisterFunction read_register) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); + ARC_ProcessorVersion version = arc_get_architecture(read_register); + + /* Record the target processor architecture in the targt-dependent + * variant information. */ + tdep->processor_variant_info->processor_version = version; + + switch (version) + { + case NO_ARCHITECTURE: + break; + case ARC700: + set_gdbarch_decr_pc_after_break (current_gdbarch, 0); + break; + case ARC600: + set_gdbarch_decr_pc_after_break (current_gdbarch, 2); + break; + case A5: + warning(_("A5 debugging is unsupported and may be unreliable.")); + break; + case A4: + /* N.B. this will not return. */ + error(_("A4 debugging is unsupported.")); + break; + case UNSUPPORTED_ARCHITECTURE: + break; + } +} + + +/* This function checks for architectural consistency; there are three possible + architectures to be considered: + + 1) the architecture for which the program to be debugged has been built + 2) the architecture of the target upon which the program is to be debugged + 3) the architecture for which we have a description of the auxiliary registers + + The 'gdbarch' parameter to this function corresponds to 2). + + A hardware target (such as an ARCangel) has a fixed architecture (e.g. that + defined by the XBF file used to configure it; and the xISS simulator's + architecture is defined by the simulator definition (.xis) file; however, + if the target is the built-in simulator, the architecture depends upon how + the simulator instance is created: if the instance is created by use of the + commands + + file <program_file> + target sim + + then the simulator architecture is taken from the program file; if it is + created by the commands + + set endian big | little + set architecture <name> + target sim + + then the architecture is the named one; if it is created by the commands + + set endian big | little + target sim + + the architecture is the default ARC architecture as defined in bfd/cpu-arc.c, + and this is indicated by the 'the_default' flag in the 'bfd_arch_info' struct + being TRUE. */ + +void +arc_check_architecture (struct gdbarch *gdbarch, bfd *objfile_bfd, + const char* file, const char* function) +{ + ARC_ProcessorVersion target = gdbarch_tdep (gdbarch)->processor_variant_info->processor_version; + ARC_ProcessorVersion auxregs = arc_aux_architecture(gdbarch); + ARC_ProcessorVersion program = NO_ARCHITECTURE; + bfd_boolean is_default = FALSE; + Boolean is_builtin_simulator = FALSE; + + ENTERARGS("current target = %s (%sconnected), objfile = %s", + current_target.to_shortname, + (arc_target_is_connected) ? "" : "not ", + (objfile_bfd) ? bfd_get_filename(objfile_bfd) : "<none>"); + + DEBUG("architectural check in function '%s' in file '%s'\n", function, file); + + /* If the target is the built-in simulator. */ + if (strcmp(current_target.to_shortname, "sim") == 0) + { + const char *name = selected_architecture_name(); + const bfd_arch_info_type *arch; + + if (name) + arch = bfd_scan_arch(name); + else + arch = gdbarch_bfd_arch_info(gdbarch); + + is_default = arch->the_default; + is_builtin_simulator = TRUE; + } + + if (objfile_bfd) + program = architecture(objfile_bfd->arch_info); + else + program = NO_ARCHITECTURE; + + DEBUG("target = %s\n", arc_version_image(target)); + DEBUG("auxregs = %s\n", arc_version_image(auxregs)); + DEBUG("program = %s\n", arc_version_image(program)); + + /* If we are connected to a target, we should know its architecture; if we + are not connected, if we don't know both the program and aux registers + architectures then there is no point in checking anything (this situation + could occur if a 'file <program>' or 'arc-reg-read-file <xmlfile>' + command has been issued before the connection is made). */ + if (arc_target_is_connected) + gdb_assert(target != NO_ARCHITECTURE); + else + if (program == NO_ARCHITECTURE || auxregs == NO_ARCHITECTURE) + return; + + /* Check that the architectures are the same. */ + perform_check(target, + auxregs, + program, + is_builtin_simulator, + is_default, + objfile_bfd); + + /* Unfortunately, this event can not be sent at the point that it is known + that the register architecture has changed, as at that point the global + variable current_gdbarch may have the value NULL, and that could cause + an error elsewhere where gdbarch_num_regs or gdbarch_num_pseudo_regs is + used (e.g. in setup_architecture_data in gdbtk/generic/gdbtk-register.c). */ + if (arc_pending_register_architecture_change_event) + { + DEBUG("sending register architecture changed event\n"); + arc_pending_register_architecture_change_event = FALSE; + reg_architecture_changed_event(); + } +} + + +/* Get the ARC architectural version from a string. */ + +ARC_ProcessorVersion +arc_version (const char *arch) +{ + ARC_ProcessorVersion version; + +#define ARCH_IS(ident) (strcmp(arch, ident) == 0) + + if (ARCH_IS("ARC700")) + version = ARC700; + else if (ARCH_IS("ARC600")) + version = ARC600; + else if (ARCH_IS("A5")) + version = A5; + else if (ARCH_IS("A4")) + version = A4; + else + version = UNSUPPORTED_ARCHITECTURE; + + return version; +} + + +/* Return a string representation of the ARC architectural version. */ + +const char* +arc_version_image (ARC_ProcessorVersion version) +{ + switch (version) + { + case NO_ARCHITECTURE : return _("NONE"); + case ARC700 : return _("ARC700"); + case ARC600 : return _("ARC600"); + case A5 : return _("A5"); + case A4 : return _("A4"); + case UNSUPPORTED_ARCHITECTURE: return _("UNSUPPORTED"); + default : return _("???"); + } +} + +/******************************************************************************/ diff --git a/gdb/arc-architecture.h b/gdb/arc-architecture.h new file mode 100644 index 00000000000..f7a2751ecec --- /dev/null +++ b/gdb/arc-architecture.h @@ -0,0 +1,74 @@ +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. + + Copyright 2008, 2009 Free Software Foundation, Inc. + + Contributed by ARC International (www.arc.com) + + Authors: + Richard Stuckey <richard.stuckey@arc.com> + + 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 3 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, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This header file defines an enumeration type for representing the */ +/* architectural version of an ARC processor, and associated functions. */ +/* */ +/******************************************************************************/ + +#ifndef ARC_ARCHITECTURE_H +#define ARC_ARCHITECTURE_H + +/* gdb header files */ +#include "defs.h" +#include "gdbarch.h" +#include "bfd/bfd-in2.h" + +/* ARC header files */ +#include "arc-support.h" + + +typedef enum arc_processor_version +{ + NO_ARCHITECTURE, + ARC700, + ARC600, + A5, + A4, + UNSUPPORTED_ARCHITECTURE +} ARC_ProcessorVersion; + + +#define ARCHITECTURE_CHECK(arch, bfd) arc_check_architecture (arch, bfd, __FILE__, __FUNCTION__) + + +void arc_architecture_is_unknown (void); + +ARC_ProcessorVersion arc_get_architecture (ReadRegisterFunction read_aux_register); + +void arc_update_architecture (ReadRegisterFunction read_register); + +void arc_check_architecture (struct gdbarch *gdbarch, bfd *objfile_bfd, + const char* file, const char* function); + +ARC_ProcessorVersion arc_version (const char *arch); + +const char* arc_version_image (ARC_ProcessorVersion version); + + +#endif /* ARC_ARCHITECTURE_H */ +/******************************************************************************/ diff --git a/gdb/arc-arguments.c b/gdb/arc-arguments.c new file mode 100644 index 00000000000..bd3404a6986 --- /dev/null +++ b/gdb/arc-arguments.c @@ -0,0 +1,427 @@ +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. + + Copyright 2008, 2009 Free Software Foundation, Inc. + + Contributed by ARC International (www.arc.com) + + Authors: + Richard Stuckey <richard.stuckey@arc.com> + + 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 3 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, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This module implements operations for setting up the command line */ +/* arguments to the program which is being debugged. */ +/* */ +/* E.g. if we are passing 4 arguments to the program's 'main' function, */ +/* we must place them on the stack in the layout: */ +/* */ +/* . */ +/* . */ +/* stack[top + A3] <== <arg_3> */ +/* . */ +/* . */ +/* stack[top + A2] <== <arg_2> */ +/* . */ +/* . */ +/* stack[top + A1] <== <arg_1> */ +/* . */ +/* . */ +/* stack[top + A0] <== <arg_0> */ +/* stack[top + 24] <== 0x0 # ? NULL terminator */ +/* stack[top + 20] <== 0x0 # envp NULL terminator */ +/* stack[top + 16] <== 0x0 # argv NULL terminator */ +/* stack[top + 12] <== TOP + A3 # argv[3] */ +/* stack[top + 8] <== TOP + A2 # argv[2] */ +/* stack[top + 4] <== TOP + A1 # argv[1] */ +/* stack[top + 0] <== TOP + A0 # argv[0] */ +/* */ +/* where TOP = &stack[top] */ +/* and A0 .. A3 are the offsets of the stored arguments from the stack */ +/* top. */ +/* */ +/******************************************************************************/ + +/* system header files */ +#include <byteswap.h> + +/* gdb header files */ +#include "defs.h" +#include "target.h" +#include "symtab.h" +#include "regcache.h" +#include "objfiles.h" + +/* ARC header files */ +#include "arc-arguments.h" +#include "arc-support.h" +#include "arc-tdep.h" + + +/* -------------------------------------------------------------------------- */ +/* local data */ +/* -------------------------------------------------------------------------- */ + +#define MINIMUM_INSTRUCTION_SIZE 2 +#define MOV_SP_INSTRUCTION_LE 0x3F80240A +#define MOV_SP_INSTRUCTION_BE 0x803F0A24 + + +/* The address of the top of the target program's stack before the program + arguments were pushed onto the stack. */ +static CORE_ADDR old_stack_top; + +/* The address in the target program code of the instructions which set up the + stack pointer (SP) at program start-up. */ +static CORE_ADDR stack_pointer_setup_code_operand_address; + + +/* -------------------------------------------------------------------------- */ +/* local macros */ +/* -------------------------------------------------------------------------- */ + +#define TARGET_IS_BIG_ENDIAN (gdbarch_byte_order (current_gdbarch) == BFD_ENDIAN_BIG) + +#define TARGET_ENDIAN(word) ((TARGET_IS_BIG_ENDIAN) ? __bswap_32(word) : (word)) + +#define MK_OPERAND_LE(x) (ARC_Word) ((((x) & 0xffff0000) >> 16) | \ + (((x) & 0x0000ffff) << 16)) + +#define MK_OPERAND_BE(x) (ARC_Word) (__bswap_32(x)) + + +/* -------------------------------------------------------------------------- */ +/* local functions */ +/* -------------------------------------------------------------------------- */ + +/* Find the address in the target program code of the given label. + Return 0 if the label is not found. */ + +static CORE_ADDR +find_label (const char *label) +{ + struct minimal_symbol *msymbol = lookup_minimal_symbol(label, NULL, symfile_objfile); + CORE_ADDR address = 0; + + if (msymbol != NULL) + address = SYMBOL_VALUE_ADDRESS (msymbol); + + DEBUG("%s = %x\n", label, (unsigned int) address); + + return address; +} + + +/* Write a word of data to target memory. + + Parameters: + address : the address in memory to write the data + word : the data to be written + + Returns TRUE if the data is written. FALSE otherwise. + + If the data is written, the given address is incremented to reference the + next word in target memory. */ + +static Boolean +write_word (CORE_ADDR *address, ARC_Word word) +{ + word = TARGET_ENDIAN(word); + + if (target_write_memory(*address, (gdb_byte*) &word, BYTES_IN_WORD) == 0) + { + *address += BYTES_IN_WORD; + return TRUE; + } + + return FALSE; +} + + +/* Try to find the address in the target program code of the instructions which + set up the program stack pointer (SP). + + Parameter: + stack_top: the address which is loaded into SP by the setup code + + Return TRUE if the code is found. + + If the setup code is found, the variable stack_pointer_setup_code_operand_address + is set to the code's address. */ + +static Boolean +find_stack_top_setup_code (CORE_ADDR stack_top) +{ + /* Try to find the start address in the target program. */ + CORE_ADDR code_start = find_label("__start"); + + if (code_start != 0) + { + CORE_ADDR code = (ARC_Address) code_start; + ARC_Word set_sp_insn[2]; + ARC_Byte buffer[16 * BYTES_IN_WORD]; + + DEBUG("setting up arguments: stack_top = %x, code_start = %x\n", + (unsigned int) stack_top, (unsigned int) code_start); + + if (TARGET_IS_BIG_ENDIAN) + { + set_sp_insn[0] = MOV_SP_INSTRUCTION_BE; + set_sp_insn[1] = MK_OPERAND_BE(stack_top); + } + else + { + set_sp_insn[0] = MOV_SP_INSTRUCTION_LE; + set_sp_insn[1] = MK_OPERAND_LE(stack_top); + } + + /* Scan through the start code of the program, looking for the code that + sets up the program's stack pointer; we recognize this as a 32-bit + 'mov sp' instruction followed by a 32-bit operand which is the + address of the stack top (which we obtained from the executable file). */ + while (TRUE) + { + int result = target_read_memory(code, (gdb_byte*) buffer, (int) sizeof(buffer)); + + if (result == 0) + { + size_t offset = 0; + + while (offset <= sizeof(buffer) - sizeof(set_sp_insn)) + { + if (memcmp(buffer + offset, set_sp_insn, sizeof(set_sp_insn)) == 0) + { + stack_pointer_setup_code_operand_address = code + (CORE_ADDR) offset + BYTES_IN_WORD; + + DEBUG("found 'mov sp, <stacktop>' instruction operand at address 0x%x\n", + (unsigned int) stack_pointer_setup_code_operand_address); + return TRUE; + } + + offset += MINIMUM_INSTRUCTION_SIZE; + } + } + else + { + warning(_("can not find read target program start code")); + break; + } + + code += (CORE_ADDR) (sizeof(buffer) - sizeof(set_sp_insn)); + + /* If we haven't found it in the first 100 bytes. */ + if (code - code_start > 100) + { + warning(_("can not find 'mov sp, <stacktop>' instruction in start code")); + break; + } + } + } + + return FALSE; +} + + +/* Try to change the setup code in the program so that SP is loaded with a + given address. + + Parameters: + old_stack_top: the address which is currently loaded into SP by the code + new_stack_top: the address which we wish to be loaded into SP by the code + + Return TRUE if the setup code is changed. */ + +static Boolean +set_stack_top (CORE_ADDR old_stack_top, CORE_ADDR new_stack_top) +{ + ARC_Word operand = (TARGET_IS_BIG_ENDIAN) ? MK_OPERAND_BE(new_stack_top) + : MK_OPERAND_LE(new_stack_top); + + /* If we do not yet know the address in the program code at which the + program's stack pointer is set up. */ + if (stack_pointer_setup_code_operand_address == 0) + { + /* Try to find it. */ + if (!find_stack_top_setup_code(old_stack_top)) + return FALSE; + } + + DEBUG("set stack top @ 0x%08X to 0x%08X (0x%08X)\n", + (unsigned int) stack_pointer_setup_code_operand_address, + (unsigned int) new_stack_top, + operand); + + return (target_write_memory(stack_pointer_setup_code_operand_address, + (gdb_byte*) &operand, + BYTES_IN_WORD) == 0); +} + + +/* -------------------------------------------------------------------------- */ +/* externally visible functions */ +/* -------------------------------------------------------------------------- */ + +/* This is called when a program is downloaded to the debug target. */ + +void +arc_program_loaded (void) +{ + /* The program has just been loaded, so we do not yet know the address in + the program code at which the program's stack pointer is set up. */ + stack_pointer_setup_code_operand_address = 0; +} + + +/* Store the program's arguments on the stack. + Return TRUE if they are stored successfully. */ + +Boolean +arc_setup_arguments (char *args) +{ + Boolean done = FALSE; + + /* Try to find the top of stack in the target program. */ + old_stack_top = find_label("__stack_top"); + + if (old_stack_top != 0) + { + char **argv = buildargv (args); + char **argp; + size_t string_length = 0; + unsigned int argc = 0; + unsigned int num_pointers; + unsigned int total_size; + CORE_ADDR new_stack_top; + + if (argv == NULL) + nomem (0); + + /* Calculate the space required to hold the args. */ + + for (argp = argv; *argp != NULL; argp++) + { + string_length += strlen (*argp) + 1; + argc++; + } + + DEBUG("%d arguments\n", argc); + + num_pointers = argc + 3; + + total_size = (unsigned int) string_length + num_pointers * BYTES_IN_WORD; + + /* Round up to a multiple of 32: strlen expects memory to come in chunks + * that are at least cache-line (32 bytes) sized. */ + total_size += 31; + total_size &= -32; + + DEBUG("total size: %d\n", total_size); + + new_stack_top = old_stack_top - total_size; + + DEBUG("new stack top: 0x%08x\n", (unsigned int) new_stack_top); + + /* Adjust the setting of the top of the stack in the object code. */ + if (set_stack_top(old_stack_top, new_stack_top)) + { + struct regcache *regcache = get_current_regcache(); + CORE_ADDR data_space = new_stack_top + num_pointers * BYTES_IN_WORD; + CORE_ADDR stack_top = new_stack_top; + unsigned int i; + + DEBUG("data space: 0x%08x\n", (unsigned int) data_space); + + done = TRUE; + + /* Write the args onto the top of the stack. */ + + for (i = 0; i < argc; i++) + { + char *parameter = argv[i]; + size_t length = strlen(parameter) + 1; + int result = target_write_memory(data_space, (gdb_byte*) parameter, (int) length); + + if (result == 0) + { + DEBUG("written argv[%d] to 0x%08x: \"%s\"\n", + i, (unsigned int) data_space, parameter); + } + else + done = FALSE; + + /* Write a pointer to the argument onto the stack. */ + if (!write_word(&stack_top, (ARC_Word) data_space)) + done = FALSE; + + data_space += length; + } + + /* Try to write the NULLs. */ + if (!write_word(&stack_top, 0) || + !write_word(&stack_top, 0) || + !write_word(&stack_top, 0)) + done = FALSE; + + /* Set up the R0 and R1 parameter registers. */ + + /* Convert to target byte order if necessary. */ + if (HOST_AND_TARGET_ENDIANNESS_DIFFER(current_gdbarch)) + { + argc = __bswap_32(argc); + new_stack_top = __bswap_32(new_stack_top); + } + + regcache_raw_supply (regcache, 0, &argc); + regcache_raw_supply (regcache, 1, &new_stack_top); + target_store_registers(regcache, 0); + target_store_registers(regcache, 1); + } + + freeargv(argv); + } + + return done; +} + + +/* This function is called just before disconnection from the debug target. */ + +void +arc_restore_stack_top_address (void) +{ + /* If we know the address in the program start-up code at which the stack + pointer is set up, it must be because we changed the stack top address + in the code - so change it back to the original address as read from the + excutable file. + + This is done so that if the user disconnects from the target, then + reconnects to it in a subsequent debugging session but does NOT download + the program to the target again (as it is still in target memory), the + mechanism for altering the stack top will still work. + + Note that this has no effect if the target is allowed to resume execution + (i.e. a 'detach' is being performed) as we are changing code that has + already been executed. + + 0 is passed as the "old" stack top as it is not used in this situation. */ + + if (stack_pointer_setup_code_operand_address != 0) + (void) set_stack_top(0, old_stack_top); +} + +/******************************************************************************/ diff --git a/gdb/arc-arguments.h b/gdb/arc-arguments.h new file mode 100644 index 00000000000..d0d79ed5214 --- /dev/null +++ b/gdb/arc-arguments.h @@ -0,0 +1,48 @@ +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. + + Copyright 2008, 2009 Free Software Foundation, Inc. + + Contributed by ARC International (www.arc.com) + + Authors: + Richard Stuckey <richard.stuckey@arc.com> + + 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 3 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, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This header file defines operations for setting up the command line */ +/* arguments to the program which is being debugged. */ +/* */ +/******************************************************************************/ + +#ifndef ARC_ARGUMENTS +#define ARC_ARGUMENTS + +/* ARC header files */ +#include "arc-support.h" + + +void arc_program_loaded (void); + +Boolean arc_setup_arguments (char *args); + +void arc_restore_stack_top_address (void); + + +#endif /* ARC_ARGUMENTS */ +/******************************************************************************/ diff --git a/gdb/arc-board.c b/gdb/arc-board.c new file mode 100644 index 00000000000..664222014b1 --- /dev/null +++ b/gdb/arc-board.c @@ -0,0 +1,1996 @@ +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. + + Copyright 2008, 2009 Free Software Foundation, Inc. + + Contributed by ARC International (www.arc.com) + + Authors: + Tim Gore + Tom Pennello <tom.pennello@arc.com> + Justin Wilde <justin.wilde@arc.com> + Phil Barnard <phil.barnard@arc.com> + Richard Stuckey <richard.stuckey@arc.com> + + 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 3 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, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This module implements operations for controlling an ARC target board. */ +/* */ +/* These operations are: */ +/* 1) configuring ("blasting") an FPGA target with the contents of an */ +/* XBF file; */ +/* 2) checking whether a target has been so configured; */ +/* 3) setting the clock frequency of the target; */ +/* 4) setting the clock sources of the target. */ +/* */ +/* Notes: */ +/* The blast_board function implements an ARC-specific command; hence its */ +/* 'args' parameter contains data entered by the debugger user, which */ +/* must be checked for validity. */ +/* */ +/* Target Board: */ +/* It is assumed that the target board is actually an ARCangel 4 (AA4). */ +/* */ +/* See */ +/* ARCangel 4 Development System */ +/* User's Guide */ +/* 5801-001 */ +/* */ +/* for a full description of the target. */ +/* */ +/* The AA4 contains a Configurable Programmable Logic Device (CPLD) which */ +/* is used to control the system services on the board; this includes the */ +/* configuration of the board's PLL (Phase Lock Loop) clock chip, and */ +/* clock routing. */ +/* */ +/* The AA4 also has a 48 MHz crystal oscillator module, and has a number */ +/* of DIP switches which may be set manually: these may be used to select */ +/* a divisor (1, 2, 4 or 8) which may be applied to the crystal frequency */ +/* to obtain a lower frequency. */ +/* */ +/* The target FPGA has 4 global clock pins (GCLK0-3); a different clock */ +/* source may be routed to each of these by the CPLD. The available */ +/* sources are: */ +/* */ +/* crystal : the physical crystal */ +/* dips : the physical crystal divided by the DIP switch divisors */ +/* highimp : high impedance */ +/* host : use the STR (strobe) input of the host interface */ +/* mclk : use a clock provided by the PLL */ +/* vclk : use a clock provided by the PLL */ +/* */ +/* Note that "high impedance" (also referred to as "Tri-state") means, in */ +/* effect, that the clock is switched off. */ +/* */ +/* It is also possible to specify that the PLL clock should be a Harvard */ +/* clock generator. */ +/* */ +/* The main clock for the target's ARC processor is provided by GLCK3. */ +/* */ +/* The PLL is assumed to be a Cypress Semiconductor Corporation ICD2061A */ +/* Dual Programmable Graphics Clock Generator; the Data Sheet describing */ +/* this device may be readily found on the Web, and should be consulted */ +/* for an understanding of how the clock is programmed. */ +/* */ +/* The ICD2061A actually provides two independent clocks: MCLK (Memory or */ +/* I/O Timing Clock) and VCLK (Video Clock). It is recommended that the */ +/* frequency of one clock should not be an integer multiple of that of */ +/* other, in order to avoid clock signal degradation through jitter. */ +/* */ +/******************************************************************************/ + +/* system header files */ +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <math.h> + +/* gdb header files */ +#include "defs.h" +#include "completer.h" +#include "objfiles.h" +#include "gdbcmd.h" + +/* ARC header files */ +#include "arc-board.h" +#include "arc-architecture.h" +#include "arc-registers.h" +#include "arc-gpio.h" +#include "arc-jtag.h" +#include "arc-jtag-ops.h" + + +/* -------------------------------------------------------------------------- */ +/* local types */ +/* -------------------------------------------------------------------------- */ + +typedef enum +{ + CLOCK_SOURCE_HIGH_IMPEDANCE, + CLOCK_SOURCE_PLL_MCLK, + CLOCK_SOURCE_PLL_VCLK, + CLOCK_SOURCE_CRYSTAL, + CLOCK_SOURCE_PLL_MCLK_HARVARD, + CLOCK_SOURCE_PLL_VCLK_HARVARD, + CLOCK_SOURCE_HOST_STROBE, + CLOCK_SOURCE_CRYSTAL_DIVIDED +} ClockSource; + +typedef enum +{ + PLL_MCLK = 0, + PLL_VCLK = 1, + NO_PLL_CLK = 2 +} PLL_ClockId; + +typedef unsigned int GlobalClockId; // 0 .. 3 + +typedef double MegaHertz; + +typedef struct global_clock +{ + ClockSource source; + Boolean set; + PLL_ClockId PLL_clock; +} GlobalClock; + +typedef struct pll_clock +{ + MegaHertz requested_frequency; + MegaHertz actual_frequency; + Boolean in_use; +} PLL_Clock; + +typedef struct pll_clock_info +{ + const char *name; + unsigned int PLL_register; + MegaHertz MIN_VCO_FREQ; + MegaHertz MAX_VCO_FREQ; +} PLL_ClockInfo; + + +/* -------------------------------------------------------------------------- */ +/* local data */ +/* -------------------------------------------------------------------------- */ + +#define ARC_SET_CLOCK_FREQUENCY_COMMAND "arc-set-clock-frequency" +#define ARC_SET_CLOCK_SOURCE_COMMAND "arc-set-clock-source" +#define ARC_CLOCK_SETTINGS_COMMAND "arc-clock-settings" +#define ARC_BLAST_BOARD_COMMAND "arc-blast-board" +#define ARC_FPGA_COMMAND "arc-fpga" + +#define ARC_SET_CLOCK_FREQUENCY_COMMAND_USAGE "Usage: " ARC_SET_CLOCK_FREQUENCY_COMMAND " [ <CLOCK> = ] <FREQUENCY> |\n" \ + " " \ + " <FREQUENCY> , <FREQUENCY>\n" + +#define ARC_SET_CLOCK_SOURCE_COMMAND_USAGE "Usage: " ARC_SET_CLOCK_SOURCE_COMMAND " gclk[N] = <SOURCE> |\n" \ + " " \ + " gclks = <SOURCE> { , <SOURCE> } |\n" \ + " " \ + " harvard\n" +#define ARC_CLOCK_SETTINGS_COMMAND_USAGE "Usage: info " ARC_CLOCK_SETTINGS_COMMAND "\n" +#define ARC_BLAST_BOARD_COMMAND_USAGE "Usage: " ARC_BLAST_BOARD_COMMAND " <FILE>\n" +#define ARC_FPGA_COMMAND_USAGE "Usage: info " ARC_FPGA_COMMAND "\n" + + +#define MAX_MAX_BURST 256 + +#define S_XOR (Byte) 0x80 /* XOR value with this to get all bits positive. */ +#define C_XOR (Byte) 0x0b /* with respect to the signal values. */ + +/* Control bits in the masks for the Control port. */ +#define STR (Byte) 0x01 // strobe +#define CNT (Byte) 0x02 +#define SS0 (Byte) 0x04 +#define SS1 (Byte) 0x08 +#define BI (Byte) 0x20 // bi-directional? + +/* Control bits in the masks for the Status port. */ +#define OP (Byte) 0x20 +#define ACK (Byte) 0x40 +#define BUSY (Byte) 0x80 + +/* Special meanings of some of those bits. */ +#define FPA_CFG_DONE OP +#define CFG_FROM_ROM BUSY +#define PAR_CFG_MODE ACK + + +/* Constants for the PLL. */ +#define MREG_ADDRESS 3 +#define VCLK_SETUP_REG_NO 0 /* which of Reg0 .. Reg2 is used to set the VClock freq. */ +#define MCLK_RESET_FREQUENCY (MegaHertz) 25.0 +#define VCLK_RESET_FREQUENCY (MegaHertz) 25.0 + +#define NUM_GLOBAL_CLOCKS 4 +#define NUM_PLL_CLOCKS 2 + +#define UNDEFINED_FREQUENCY (MegaHertz) (-1.0) + + +static const char *CLOCK_SOURCE_STRINGS[] = +{ + "High Impedance", + "PLL MCLK", + "PLL VCLK", + "Crystal", + "High Impedance", + "High Impedance", + "Host Strobe", + "Crystal With Division" +}; + +static const char *GCLOCK3_SOURCE_STRINGS[] = +{ + "High Impedance", + "PLL MCLK", + "PLL VCLK", + "Crystal", + "PLL MCLK (+Harvard)", + "PLL VCLK (+Harvard)", + "Host Strobe", + "Crystal With Division (+Harvard)" +}; + + +static const ClockSource default_GCLK_sources[] = +{ + CLOCK_SOURCE_HIGH_IMPEDANCE, // GCLK0 + CLOCK_SOURCE_CRYSTAL, // GCLK1 + CLOCK_SOURCE_HOST_STROBE, // GCLK2 + CLOCK_SOURCE_CRYSTAL_DIVIDED // GCLK3 +}; + + +static const MegaHertz VCO_PRESET_BOUNDARIES[] = {50.0, 51.0, 53.2, 58.5, 60.7, 64.4, 66.8, + 73.5, 75.6, 80.9, 83.2, 91.5, 100.0, 120.0}; + + +/* Unchanging information for the two PLL clocks. */ +static const PLL_ClockInfo PLL_clock_fixed_info[NUM_PLL_CLOCKS] = +{ + { "MCLK", MREG_ADDRESS, 52.0, 120.0 }, + { "VCLK", VCLK_SETUP_REG_NO, 65.0, 165.0 } +}; + + +/* Data describing the 2 PLL clocks and the 4 global clock sources. */ +static PLL_Clock PLL_clocks [NUM_PLL_CLOCKS]; +static GlobalClock global_clocks[NUM_GLOBAL_CLOCKS]; +static Boolean harvard; + + +/* -------------------------------------------------------------------------- */ +/* local macros */ +/* -------------------------------------------------------------------------- */ + +#define IS_SET(bit, byte) (((bit) & (byte)) == (bit)) +#define __MIN(X, Y) ((X) < (Y) ? (X) : (Y)) +#define __MAX(X, Y) ((X) < (Y) ? (Y) : (X)) + +#define FREQUENCY(clock) ((PLL_clocks[clock].in_use) ? PLL_clocks[clock].requested_frequency \ + : UNDEFINED_FREQUENCY) + +#define PLL_CLOCK_NAME(clock) PLL_clock_fixed_info[clock].name + + +/* -------------------------------------------------------------------------- */ +/* local functions */ +/* -------------------------------------------------------------------------- */ + +/* Sleep for the given number of milliseconds. */ + +static void +Sleep (unsigned int milliseconds) +{ + usleep((unsigned long) (1000 * milliseconds)); +} + + +/* Read a byte of data from the Status port. */ + +static Byte +read_status_port (void) +{ + return gpio_read(STATUS_PORT) ^ S_XOR; +} + + +/* Write a byte of data to the Control port, then sleep for the given delay. */ + +static void +write_control_port (Byte data, unsigned int delay) +{ + Byte value = data ^ C_XOR; + + gpio_write(CONTROL_PORT, value); + Sleep(delay); +} + + +/* Write a byte of data to the Data port. */ + +static void +write_data_port (Byte value) +{ + gpio_write(DATA_PORT, value); +} + + +/* Extract the value from a string containing a name/value pair of the form + + [ <name> = ] <value> + + Return 0 if the string is not of the given form + 1 if the string is of the form <value> + 2 if the string is of the form <name> = <value> +*/ + +static int +name_value_pair (char *args, char **value) +{ + char *equals = strchr(args, '='); + + if (equals) + { + char *val = equals + 1; + + /* If the key is missing from the argument string. */ + if (equals == args) + return 0; + + equals--; + while (*equals == ' ') equals--; + equals[1] = '\0'; + + while (*val == ' ') val++; + if (*val == '\0') + return 0; + + *value = val; + return 2; + } + + return 1; +} + + +/* -------------------------------------------------------------------------- */ +/* local functions for FPGA blasting */ +/* -------------------------------------------------------------------------- */ + +/* Initialize the FPGA ready for blasting. + Return TRUE if the initialization is successful. */ + +static Boolean +initialize_FPGA (void) +{ + Byte status; + Byte iOriginalState; + Byte iControlState; + unsigned int cpld_rev; + + ENTERMSG; + + /* snapshot the control port. */ + iOriginalState = gpio_read(CONTROL_PORT); + + /* Initialize FPGA by taking SS0 and SS1 low (all other ctrl's low as well). */ + iControlState = iOriginalState & (0xFFFFFFFF ^ (SS0 | SS1 | CNT | STR | BI)); + write_control_port(iControlState, 51); + + /* Tri-state port outputs so we can read CPLD revision number. */ + iControlState = iControlState | BI; + write_control_port(iControlState, 1); + + // Read the CPLD revision number LSB. */ + cpld_rev = (unsigned int) gpio_read(DATA_PORT); + + /* Set CNT high and read CPLD revision number MSB. */ + iControlState = iControlState | CNT; + write_control_port(iControlState, 1); + + cpld_rev += (unsigned int) gpio_read(DATA_PORT) << 8; + + /* Test the CPLD rev no; if it is 0xffff then this CPLD may not support + parallel blasting. */ + if ((cpld_rev & 0xffff) == 0xffff) + { + warning(_("old board type (AA2), not supported")); + gpio_close(); + return FALSE; + } + else + { + char rev_string[32]; + unsigned int temp = cpld_rev; + unsigned int char_pos = 0; + unsigned int i; + + for (i = 0; i < 16; i++) + { + if (temp & 0x8000) + rev_string[char_pos++] = '1'; + else + rev_string[char_pos++] = '0'; + + temp <<= 1; + if ((i % 4) == 3) + rev_string[char_pos++] = ' '; + } + + rev_string[char_pos] = '\0'; + + printf_filtered(_("\nCPLD Revision = %20s\n"), rev_string); + } + + /* Take CNT low. */ + iControlState = iControlState & (0xFFFFFFFF ^ (SS0 | SS1 | CNT | STR | BI)); + + /* Now take STR high, CNT low and SS0 high to enter FPGA download mode. */ + iControlState = iControlState | STR; + write_control_port(iControlState, 1); + + iControlState = iControlState | SS0; + write_control_port(iControlState, 3); + + /* Check that FPA_CFG_DONE=0. */ + status = read_status_port(); + + if (IS_SET(FPA_CFG_DONE, status)) + { + warning(_("FPGA is not responding - status = 0x%08x"), status); + gpio_close(); + return FALSE; + } + + LEAVEMSG; + + return TRUE; +} + + +/* Try to send data to the target in a parallel stream. + Return TRUE if it is sent. */ + +static Boolean +parallel_send_data (Byte *buffer, unsigned int count) +{ + GPIO_Pair arr[MAX_MAX_BURST * 3 + 1]; + + /* Work out how many bytes we can send in one PIO program. */ + unsigned const int bytes_per_burst = 127; + + /* Initialize offsets into config data buffer. */ + unsigned int burst_start = 0; + unsigned int burst_end = bytes_per_burst - 1; + + /* Snapshot the control port. */ + Byte iOriginalState = gpio_read(CONTROL_PORT); + Byte iControlState = (iOriginalState | SS0) & (0xFFFFFFFF ^ (SS1 | CNT | STR | BI)); + + while (TRUE) + { + GPIO_Pair *gpio = arr; + unsigned int i; + + /* Do not try to write more data than is in the buffer. */ + if (burst_end > (count - 1)) + burst_end = count - 1; + + /* Initialize the gpio driver instruction stream. */ + for (i = burst_start; i <= burst_end; i++) + { + gpio->port = DATA_PORT; + gpio->data = buffer[i]; + gpio++; + gpio->port = CONTROL_PORT; + gpio->data = iControlState ^ C_XOR; + gpio++; + gpio->port = CONTROL_PORT; + gpio->data = (iControlState | STR) ^ C_XOR; + gpio++; + } + + gpio_write_array(arr, gpio - arr); + + /* Last block of data written. */ + if (burst_end == count - 1) + break; + + burst_start = burst_end + 1; + burst_end = burst_start + bytes_per_burst - 1; + } + + return TRUE; +} + + +/* Try to send data to the target in a serial stream. + Return TRUE if it is sent. */ + +static Boolean +serial_send_data (Byte *buff, unsigned int count) +{ + /* There is code which implements serial blasting in the SeeCode debugger + file os/arc/connect/par/arc/aa3blast.cpp, which is intended to work with + either Win95 or WinNT. If serial blasting is required for Linux, this + code would have to be re-written to use Linux O/S operations. However, + there is currently no requirement for that. */ + warning(_("sorry, serial download is not supported")); + return FALSE; +} + + + +/* Try to blast the target board FPGA with the contents of an XBF file. + Return TRUE if the blast is succcessful. */ + +static Boolean +blast_FPGA (FILE *xbf) +{ + Boolean parallel_cfg; + unsigned long file_size; + unsigned long five_percent; + unsigned long bytes_sent = 0; + unsigned long twentieths_complete = 0; + Byte status; + + ENTERMSG; + + /* Get parallel port status, and see whether the board is expecting parallel + or serial blast. */ + status = read_status_port(); + + if ((status & CFG_FROM_ROM) == CFG_FROM_ROM) + { + /* Oops - FPGA is configured from ROM! */ + if (IS_SET(FPA_CFG_DONE, status)) + printf_filtered(_("FPGA is configured from ROM")); + else + warning(_("FPGA should be configured from ROM - BUT IT IS NOT!")); + return FALSE; + } + + parallel_cfg = ((status & PAR_CFG_MODE) == PAR_CFG_MODE); + + /* Find the length of the file (could use fstat instead here). */ + (void) fseek(xbf, 0, SEEK_END); + file_size = (unsigned long) ftell(xbf); + (void) fseek(xbf, 0, SEEK_SET); + + five_percent = file_size / 20; + + /* Read file and blast. */ + + while (TRUE) + { + Byte data_buffer[1024]; + size_t n_bytes = fread(data_buffer, 1, sizeof(data_buffer), xbf); + + if (gpio_port_error) + error(_("Error in accessing JTAG port (device " GPIO_DEVICE ")")); + + /* End of file reached? (fread returns 0 for both EOF and error!). */ + if (n_bytes == 0) + { + if (!feof(xbf)) + { + warning(_("error in reading XBF file")); + return FALSE; + } + break; + } + + if (!(((parallel_cfg) ? parallel_send_data + : serial_send_data) (data_buffer, (unsigned int) n_bytes))) + break; + + bytes_sent += n_bytes; + if (bytes_sent == file_size) + break; + + if ((bytes_sent / five_percent) > twentieths_complete) + { + twentieths_complete++; + printf_filtered(_("*")); + gdb_flush (gdb_stdout); + } + } + + printf_filtered(_("\n")); + + /* Check for the ConfigDone signal. */ + status = read_status_port(); + + if (!IS_SET(FPA_CFG_DONE, status)) + { + warning(_("FPGA configuration failed")); + return FALSE; + } + + printf_filtered(_("FPGA configured\n")); + + /* Set SS0 and SS1 high to take board out of reset. */ + { + Byte iControlState = (gpio_read(CONTROL_PORT) | SS0 | SS1 | STR) & (0xFFFFFFFF ^ (CNT | BI)); + + write_control_port(iControlState, 1); + } + + LEAVEMSG; + + return TRUE; +} + + +/* -------------------------------------------------------------------------- */ +/* local functions for setting clocks */ +/* -------------------------------------------------------------------------- */ + +/* Reset the clock configuration information to its default values (i.e. the + values that the h/w has after a hard reset of the target. */ + +static void +reset_clock_configuration (void) +{ + unsigned int i; + + for (i = 0; i < ELEMENTS_IN_ARRAY(PLL_clocks); i++) + { + PLL_clocks[i].requested_frequency = MCLK_RESET_FREQUENCY; + PLL_clocks[i].actual_frequency = VCLK_RESET_FREQUENCY; + PLL_clocks[i].in_use = FALSE; + } + + for (i = 0; i < ELEMENTS_IN_ARRAY(global_clocks); i++) + { + global_clocks[i].source = default_GCLK_sources[i]; + global_clocks[i].set = FALSE; + global_clocks[i].PLL_clock = NO_PLL_CLK; + } + + harvard = FALSE; +} + + +/* Calculate the control word required to set a PLL clock to a particular frequency. + + Parameters: + requested_frequency : the frequency we want + min_vco_frequency : the minimum VCO frequency for this clock + max_vco_frequency : the maximum VCO frequency for this clock + actual_frequency : the frequency we actually get + + Result: the control word; 0 if no frequency can be set + + The PLL consists of a VCO and 3 counters that divide by p, q and 2^d. The + VCO runs at 2*RefClk*p/q. This is divided by 2^d to give the PLL output. + + There are several contraints on the various values: + 4 <= p <= 130 + 3 <= q <= 129 + 0 <= d <= 7 + 0.2 <= ref_clk / q <= 1.0 (200kHz .. 1MHz) + + This method is a bit of a palaver - very procedural. Basically it uses + trial and error to find the best values for d, p and q, within the given + contraints. */ + +static unsigned int +calculate_ctrl_word (const MegaHertz requested_frequency, + const MegaHertz min_vco_frequency, + const MegaHertz max_vco_frequency, + MegaHertz *actual_frequency) +{ + const unsigned int MIN_P = 4; + const unsigned int MAX_P = 130; + const unsigned int MIN_Q = 3; + const unsigned int MAX_Q = 129; + const unsigned int MIN_D = 0; + const unsigned int MAX_D = 7; + const MegaHertz MIN_REF_CLK_OVER_Q = 0.2; + const MegaHertz MAX_REF_CLK_OVER_Q = 1.0; + const MegaHertz REF_CLK = 14.31818; // input to PLL + +#define NUM_PRESETS ELEMENTS_IN_ARRAY(VCO_PRESET_BOUNDARIES) + + unsigned int index, p = 0, q = 0, d = MIN_D; /* PLL parameters (see ICD2061A Data Sheet). */ + unsigned int trial_p, trial_q; /* Temp vars for p & q values that we are trying out. */ + unsigned int first_q, last_q; + unsigned int ctrl_word; + double min_delta = 1.0; /* Smallest error so far. */ + MegaHertz vco_frequency = requested_frequency; /* Freq at which the VCO will run. */ + double p_over_q; + + /* Find a value of d which gives a VCO frequency that is within limits (VCO + output is divided by 2^d). */ + while ((vco_frequency < min_vco_frequency) && (d < MAX_D)) + { + vco_frequency *= 2; + d++; + } + + DEBUG("request = %g, vco = %g, min = %g, max = %g, d = %d\n", + requested_frequency, vco_frequency, min_vco_frequency, max_vco_frequency, d); + + /* Check that we have found a suitable value for d. */ + if ((vco_frequency < min_vco_frequency) || (vco_frequency > max_vco_frequency)) + { + DEBUG("frequency is out of range\n"); + return 0; + } + + /* Calculate the ratio needed for p/q, to get vco_frequency from ref_clk. */ + p_over_q = vco_frequency / (2.0 * REF_CLK); + + /* Now use some brute force and ignorance to find the best values for p & q: + we look for p & q such that p / q is the best approximation to p_over_q. */ + + /* Calculate range of values allowed for q. */ + first_q = __MAX((unsigned int) (REF_CLK / MAX_REF_CLK_OVER_Q + 0.999999), MIN_Q); + last_q = __MIN((unsigned int) (REF_CLK / MIN_REF_CLK_OVER_Q), MAX_Q); + + /* Look at each possible value of q. */ + for (trial_q = first_q; trial_q <= last_q; trial_q++) + { + /* Calculate the value of p needed with this q value. */ + double raw_p = p_over_q * (double) trial_q; + double delta; + + /* Round the raw value for p to the nearest integer. */ + trial_p = (unsigned int) (raw_p + 0.5); + + /* Range check the required p value: note that because trial_q is + increasing, trial_p is also increasing, so if it is less than MIN_P + we may find a suitable value in a later iteration, whereas if it is + greater than MAX_P we will never find a suitable value in a later + iteration. */ + if (trial_p < MIN_P) + continue; + if (trial_p > MAX_P) + break; + + /* See how much error is caused by p being an integer. */ + delta = fabs (1.0 - ((double) trial_p / raw_p)); + + /* If this is the most accurate so far, then keep track of it. */ + if (delta < min_delta) + { + p = trial_p; + q = trial_q; + + /* If it is exact then quit (we won't be able to find a better approximation!). */ + if (min_delta == 0.0) + break; + + min_delta = delta; + } + } + + /* Just in case. */ + if (p == 0) + { + DEBUG("loop failed to find p & q!"); + return 0; + } + + /* Have sorted out values for p, q & d - now form them into a control word. */ + + /* First, look up the value for Index (VCO preset). */ + for (index = 0; index < NUM_PRESETS; index++) + { + if (VCO_PRESET_BOUNDARIES[index] > vco_frequency) + break; + } + + // make sure we have found a suitable value for I + if ((index == 0) || (index == NUM_PRESETS)) + { + DEBUG("can not find preset for %g\n", vco_frequency); + return 0; + } + + /* The index must now be in the range 1 .. 13; so subtract 1, to change the + range to 0 .. 12 as required by the encoding. */ + index--; + + /* Return the frequency calculated as best approximation to the one requested. */ + *actual_frequency = (2.0 * REF_CLK * p / q) / (1 << d); + + DEBUG("p = %d, q = %d, d = %d, I = %d\n", p, q, d, index); + + /* The ranges for p, q & d are: + + I : 0 .. 12 + p : 4 .. 130 + d : 0 .. 7 + q : 3 .. 129 + + Subtracting a bias of 3 from p and 2 from q converts these to: + + I : 0 .. 12 which can be held in 4 bits + p : 1 .. 127 which can be held in 7 bits + d : 0 .. 7 which can be held in 3 bits + q : 1 .. 126 which can be held in 7 bits + + which gives a control word with bitfields: + + 00000000000IIIIPPPPPPPDDDQQQQQQQ + + Note that 0 is not a valid value for the control word, which is why + it is safe to return 0 from this function in the error cases. */ + + ctrl_word = (index & 0xf); + ctrl_word = (ctrl_word << 7) | ((p - 3) & 0x7f); + ctrl_word = (ctrl_word << 3) | (d & 0x7); + ctrl_word = (ctrl_word << 7) | ((q - 2) & 0x7f); + + return ctrl_word; +} + + +/* Write a control word to the PLL clock control register whose address is given. + Return TRUE if the write is successful. */ + +static Boolean +write_PLL_register (unsigned int address, unsigned int ctrl_word) +{ + const Byte S0S1_FINAL_STATE[] = {(Byte) 0x0, (Byte) 0x1, (Byte) 0x2}; + const Byte PLL_CLK_BIT = (Byte) 0x08; + const Byte PLL_DATA_BIT = (Byte) 0x10; + unsigned int i; + Byte data; + Byte iControlState; + Byte iOriginalState; + int manchester_bitstream[64]; + + DEBUG("writing 0x%08X to PLL register %d\n", ctrl_word, address); + + /* Add the address in the MSBs of the ctrl_word: this gives us a 24-bit value + with the fields AAAIIIIPPPPPPPDDDQQQQQQQ */ + + ctrl_word = ((address & 0x7) << 21) | (ctrl_word & 0x1fffff); + + /* Create a bit stream at twice the data rate that incorporates the pseudo + Manchester encoding for the data and also the unlock sequence. */ + for (i = 0; i < 11; i++) + manchester_bitstream[i] = 1; + + /* The start bit. */ + manchester_bitstream[11] = 0; + manchester_bitstream[12] = 0; + manchester_bitstream[13] = 0; + + i = 14; + while (i < 62) + { + if ((ctrl_word & 0x1) == 0) + { + manchester_bitstream[i++] = 1; + manchester_bitstream[i++] = 0; + } + else + { + manchester_bitstream[i++] = 0; + manchester_bitstream[i++] = 1; + } + + ctrl_word >>= 1; + } + + /* The stop bit. */ + manchester_bitstream[62] = 1; + manchester_bitstream[63] = 1; + + /* Snapshot the control port state. */ + iOriginalState = gpio_read(CONTROL_PORT); + + /* Set the parallel port data to 0, in preparation for sending config data. */ + write_data_port((Byte) 0); + Sleep(2); + + /* Set CPLD into config mode. */ + + /* Set SS0=1, SS1=1, CNT=1, BIDir=0. */ + iControlState = (iOriginalState | SS0 | SS1 | CNT | BI) ^ BI; + write_control_port(iControlState, 2); + + /* Ensure STROBE is high. */ + iControlState = iControlState | STR; + write_control_port(iControlState, 2); + + /* Set CPLD into config mode by setting SS0=1, SS1=0, CNT=1. */ + iControlState = iControlState ^ SS1; + write_control_port(iControlState, 2); + + /* Now send the double rate data stream. */ + + // Set the clock high and data low. */ + data = PLL_CLK_BIT & ~PLL_DATA_BIT; + write_data_port(data); + //Sleep(1); + + for (i = 0; i < 64; i++) + { + /* Put the next Manchester code bit out. */ + if (manchester_bitstream[i] == 1) + data = data | PLL_DATA_BIT; + else + data = data & ~PLL_DATA_BIT; + + write_data_port(data); + //Sleep(1); + + /* Toggle the clock bit. */ + data = data ^ PLL_CLK_BIT; + write_data_port(data); + //Sleep(1); + } + + /* Set data/clock (alias s1/s0) to select the programmed divisor register + for the video clock. */ + write_data_port(S0S1_FINAL_STATE[VCLK_SETUP_REG_NO]); + //Sleep(1); + + /* Set CPLD into ARC-Run Host-Read mode. */ + iControlState = iControlState | SS1 | BI; + write_control_port(iControlState, 2); + + return TRUE; +} + + +/* Configure the target board's CPLD. */ + +static void +configure_CPLD (void) +{ + const Byte CPLD_CLK_BIT = (Byte) 0x01; + const Byte CPLD_DATA_BIT = (Byte) 0x02; + const Byte CPLD_SET_BIT = (Byte) 0x04; + const unsigned int NUM_OF_CPLD_CFG_BITS = 16; + unsigned int cpldConfigData = 0; + Byte iControlState; + unsigned int i; + + /* Snapshot the control port. */ + Byte iOriginalState = gpio_read(CONTROL_PORT); + + /* Set the parallel port data to 0, in preparation for sending config data. */ + write_data_port((Byte) 0); + Sleep(1); + + /* Set CPLD into configuration mode. */ + + /* Set SS0=1, SS1=1, CNT=1, BIDir=0. */ + iControlState = (iOriginalState | SS0 | SS1 | CNT | BI) ^ BI; + write_control_port(iControlState, 2); + + /* Ensure STROBE is high. */ + iControlState = iControlState | STR; + write_control_port(iControlState, 2); + + /* Set CPLD into config mode by setting SS0=1, SS1=0, CNT=1. */ + iControlState = iControlState ^ SS1; + write_control_port(iControlState, 2); + + /* Now send the config data stream with set low. */ + + /* Set clock high and data low. */ + + for (i = 0; i < ELEMENTS_IN_ARRAY(global_clocks); i++) + cpldConfigData += (unsigned int) global_clocks[i].source << (3 * i); + + for (i = 0; i < NUM_OF_CPLD_CFG_BITS; i++) + { + Byte value; + + /* See if the next cfg bit is 0 or 1. */ + if ((cpldConfigData & 0x1) == 0x1) + value = CPLD_DATA_BIT; + else + value = (Byte) 0; + + /* Put data bit out to parallel port. */ + write_data_port(value); + + /* And toggle the clock line. */ + value |= CPLD_CLK_BIT; + write_data_port(value); + + value &= ~CPLD_CLK_BIT; + write_data_port(value); + + cpldConfigData >>= 1; + } + + /* Now take the clock and set bits high. */ + write_data_port(CPLD_CLK_BIT | CPLD_SET_BIT); + + /* Finally, take the clock low. */ + write_data_port(CPLD_SET_BIT); + + /* And put the CPLD into ARC run mode, SS0=1, SS1= 1, CNT=x. */ + iControlState = iControlState | SS1 | BI; + write_control_port(iControlState, 2); +} + + +/* Try to set the frequency of a PLL clock. + + Parameters: + clock : the identity of the clock (MCLK or VCLK) + requested_frequency: the desired frequency fro the clock + inform : TRUE if a message should be output if the clock is set + emit_warning : TRUE if a warning should be output if the clock is not set + + Returns TRUE if the clock is set. +*/ + +static Boolean +set_PLL_clock_frequency (PLL_ClockId clock, + MegaHertz requested_frequency, + Boolean inform, + Boolean emit_warning) +{ + /* First need to work out the control words for the frequencies set. */ + MegaHertz actual_frequency = UNDEFINED_FREQUENCY; + unsigned int ctrl_word = calculate_ctrl_word (requested_frequency, + PLL_clock_fixed_info[clock].MIN_VCO_FREQ, + PLL_clock_fixed_info[clock].MAX_VCO_FREQ, + &actual_frequency); + Boolean set; + + DEBUG("set_PLL_clock_frequency: %s ctrl_word = %08X, freq = %.2lf MHz\n", + PLL_CLOCK_NAME(clock), ctrl_word, requested_frequency); + + if (ctrl_word == 0) + { + if (emit_warning) + warning(_("it is not possible to set %s to %.2lf"), + PLL_CLOCK_NAME(clock), requested_frequency); + return FALSE; + } + + DEBUG("set_PLL_clock_frequency: %s %.2lf, %.2lf, %.2lf\n", + PLL_CLOCK_NAME(clock), + requested_frequency, + actual_frequency, + PLL_clocks[clock].actual_frequency); + + if (actual_frequency != PLL_clocks[clock].actual_frequency) + { + /* Set up the PLL chip. We program the MREG, the REG0/1/2 - whichever + is selected to control VCLK. */ + set = write_PLL_register (PLL_clock_fixed_info[clock].PLL_register, ctrl_word); + + if (set) + { + PLL_clocks[clock].requested_frequency = requested_frequency; + PLL_clocks[clock].actual_frequency = actual_frequency; + } + else + if (emit_warning) + warning(_("PLL programming failed")); + } + else + set = TRUE; + + if (set && inform) + printf_filtered(_("PLL clock %s set to %.2lf MHz.\n"), PLL_CLOCK_NAME(clock), actual_frequency); + + return set; +} + + +/* Check the frequencies of the two PLL clocks, and emit a warning if necessary. + + The ICD2061A Data Sheet recommends that the two clocks should not be set to + frequencies such that one is an integer multiple of the other, in order to + avoid jitter. */ + +static void +check_PLL_clock_frequencies (void) +{ + DEBUG("check_PLL_clock_frequencies\n"); + + /* If both clocks are in use. */ + if (PLL_clocks[PLL_MCLK].in_use && PLL_clocks[PLL_VCLK].in_use) + { + /* Check whether the two chosen clocks are divisible by one another, in + which case print a warning. */ + double multiplier = PLL_clocks[PLL_VCLK].actual_frequency / + PLL_clocks[PLL_MCLK].actual_frequency; + double modulus; + + if (multiplier < 1.00) + multiplier = 1 / multiplier; + + modulus = multiplier - ((int) multiplier); + + /* Check also for near multiples. */ + if ((modulus < 0.02) || (modulus > 0.98)) + warning(_("PLL MCLK and PLL VCLK frequencies are (near) multiples of each other.\n" + "This may lead to clock degradation.")); + } + else if (PLL_clocks[PLL_MCLK].in_use || PLL_clocks[PLL_VCLK].in_use) + { + MegaHertz requested_frequency; + MegaHertz actual_frequency; + PLL_ClockId clock; + + /* If we now are only using one PLL clock then ensure that the second + clock's frequency is not a multiple of the first's (M == V is OK). */ + if (PLL_clocks[PLL_MCLK].in_use) + { + clock = PLL_VCLK; + actual_frequency = PLL_clocks[PLL_MCLK].actual_frequency; + } + else + { + clock = PLL_MCLK; + actual_frequency = PLL_clocks[PLL_VCLK].actual_frequency; + } + + if (actual_frequency < VCO_PRESET_BOUNDARIES[0]) + requested_frequency = actual_frequency * 1.43; + else + requested_frequency = actual_frequency; + + if (!set_PLL_clock_frequency(clock, requested_frequency, TRUE, FALSE)) + (void) set_PLL_clock_frequency(clock, actual_frequency, TRUE, TRUE); + } +} + + +/* Try to set the source of the given global clock to be a PLL clock set to the + given frequency. + + If one of the PLL clocks is already set to the given frequency, we use that + as the source; otherwise, if the global clock's source is a PLL clock, and + no other global clock is using that PLL clock as its source, we change its + frequency to the required frequency; otherwise, if the other PLL clock is not + already in use, we set that other clock to the required frequency and use it + as the source; otherwise (both PLL clocks are in use), we find the clock + whose frequency is closest to the required frequency and use that clock as + the source. */ + +static void +use_PLL_clock (GlobalClockId clockId, MegaHertz clockValue) +{ + PLL_ClockId PLL_clock_id = NO_PLL_CLK; + PLL_ClockId free_PLL_clock_id = NO_PLL_CLK; + unsigned int i; + + /* Is this global clock not already using a PLL clock? */ + if (global_clocks[clockId].PLL_clock == NO_PLL_CLK) + { + /* Has this frequency already been assigned to a PLL clock? */ + for (i = 0; i < ELEMENTS_IN_ARRAY(PLL_clocks); i++) + { + PLL_Clock* clock = &PLL_clocks[i]; + + if (clock->in_use) + { + /* The actual frequency to which the clock is set may differ + slightly from the frequency that was requested - so check + both. */ + if (clockValue == clock->requested_frequency || + clockValue == clock->actual_frequency) + { + PLL_clock_id = (PLL_ClockId) i; + break; + } + } + else + { + /* Use MCLK (first in array) in preference to VCLK. */ + if (free_PLL_clock_id == NO_PLL_CLK) + free_PLL_clock_id = (PLL_ClockId) i; + } + } + } + else + { + PLL_ClockId this_clock = global_clocks[clockId].PLL_clock; + int users = 0; + + /* How many global clocks are using this PLL clock? */ + for (i = 0; i < ELEMENTS_IN_ARRAY(global_clocks); i++) + { + if (global_clocks[i].PLL_clock == this_clock) + users++; + } + + if (users == 1) + { + /* Just this one - so we can change its frequency without affecting + any other global clocks. */ + free_PLL_clock_id = this_clock; + } + else + { + /* Look at the other clock - if it is not already in use, we can use it. */ + PLL_ClockId other_clock = (this_clock == PLL_MCLK) ? PLL_VCLK : PLL_MCLK; + + if (!PLL_clocks[other_clock].in_use) + free_PLL_clock_id = other_clock; + } + } + + /* Do we need another PLL clock? */ + if (PLL_clock_id == NO_PLL_CLK) + { + /* If so, and there aren't any which are not in use. */ + if (free_PLL_clock_id == NO_PLL_CLK) + { + MegaHertz M_delta = fabs(PLL_clocks[PLL_MCLK].actual_frequency - clockValue); + MegaHertz V_delta = fabs(PLL_clocks[PLL_VCLK].actual_frequency - clockValue); + + /* Which clock has the closet frequency to what we want? */ + PLL_clock_id = (M_delta <= V_delta) ? PLL_MCLK : PLL_VCLK; + + warning(_("can not set GCLK%d to %.2lf MHz - " + "there are no more PLL clocks available.\n" + "Using closest match instead (%s @ %.2lf MHz)."), + clockId, clockValue, + PLL_CLOCK_NAME(PLL_clock_id), + PLL_clocks[PLL_clock_id].actual_frequency); + } + else + { + /* Otherwise, use a free PLL clock. */ + PLL_clock_id = free_PLL_clock_id; + + if (set_PLL_clock_frequency(PLL_clock_id, clockValue, TRUE, TRUE)) + { + PLL_clocks[PLL_clock_id].in_use = TRUE; + check_PLL_clock_frequencies(); + } + } + } + + global_clocks[clockId].PLL_clock = PLL_clock_id; + global_clocks[clockId].source = (PLL_clock_id == PLL_MCLK) ? CLOCK_SOURCE_PLL_MCLK : CLOCK_SOURCE_PLL_VCLK; + global_clocks[clockId].set = TRUE; +} + + +/* Set the source of the given global clock as specified. + This may be one of: crystal, dips, highimp, host, mclk, vclk or <frequency>. + Specifying an explicit frequency means that the source should be a PLL clock + set to that frequency. */ + +static void +set_global_clock (GlobalClockId clockId, const char *clockData) +{ + static const struct table_entry + { + ClockSource source; + PLL_ClockId clock; + const char *name; + Boolean harvard; + } table[] = + { { CLOCK_SOURCE_CRYSTAL, NO_PLL_CLK, "crystal", FALSE }, + { CLOCK_SOURCE_CRYSTAL_DIVIDED, NO_PLL_CLK, "dips", FALSE }, + { CLOCK_SOURCE_HIGH_IMPEDANCE, NO_PLL_CLK, "highimp", FALSE }, + { CLOCK_SOURCE_HOST_STROBE, NO_PLL_CLK, "host", FALSE }, + { CLOCK_SOURCE_PLL_MCLK_HARVARD, PLL_MCLK, "mclk", TRUE }, + { CLOCK_SOURCE_PLL_VCLK_HARVARD, PLL_MCLK, "vclk", TRUE }, + { CLOCK_SOURCE_PLL_MCLK, PLL_MCLK, "mclk", FALSE }, + { CLOCK_SOURCE_PLL_VCLK, PLL_VCLK, "vclk", FALSE } }; + + MegaHertz clockValue; + unsigned int i; + + /* Look at each possible clock source in the table. */ + for (i = 0; i < ELEMENTS_IN_ARRAY(table); i++) + { + const struct table_entry *entry = &table[i]; + + if (strcasecmp(clockData, entry->name) == 0) + { + /* N.B. the order of the entries in the table is important! */ + if (entry->harvard && !harvard) + continue; + + global_clocks[clockId].source = entry->source; + global_clocks[clockId].PLL_clock = entry->clock; + global_clocks[clockId].set = TRUE; + + /* N.B. "high impedance" effectively means "off". */ + if ((clockId == 3) && (entry->source == CLOCK_SOURCE_HIGH_IMPEDANCE)) + { + warning(_("GCLK3 must be valid in order for the ARC processor's debug interface to interact with the processor.")); + } + else if ((clockId == 2) && (entry->source != CLOCK_SOURCE_HOST_STROBE)) + { + warning(_("GCLK2 must be %s for the JTAG clock to be connected to the ARC processor's debug interface."), + CLOCK_SOURCE_STRINGS[CLOCK_SOURCE_HOST_STROBE]); + } + + return; + } + } + + /* We did not find a match in the table - so the given source may be a frequency. */ + if (sscanf(clockData, "%lf", &clockValue) == 1) + { + if (clockId == 2) + { + warning(_("GCLK2 must be %s for the JTAG clock to be connected to the ARC processor's debug interface."), + CLOCK_SOURCE_STRINGS[CLOCK_SOURCE_HOST_STROBE]); + } + + use_PLL_clock(clockId, clockValue); + } + else + warning(_("'%s' is not a valid source for clock %d\n"), clockData, clockId); +} + + +/* Enable Harvard clock to drive global clock GLK3. */ + +static void +enable_Harvard_clock (void) +{ + /* Does GCLK3 come from the PLL? */ + if (global_clocks[3].PLL_clock != NO_PLL_CLK) + { + /* Save existing settings. */ + const PLL_ClockId saved_clock[] = {global_clocks[0].PLL_clock, + global_clocks[1].PLL_clock, + global_clocks[2].PLL_clock, + global_clocks[3].PLL_clock}; + const MegaHertz saved_value[] = {PLL_clocks[PLL_MCLK].actual_frequency, + PLL_clocks[PLL_VCLK].actual_frequency}; + GlobalClockId clockId; + + printf_filtered(_("Configuring clocks to drive Harvard Ctl_Clk.\n")); + + reset_clock_configuration(); + harvard = TRUE; + + /* Now re-assign the Harvard inputs and double the requested frequency. */ + use_PLL_clock(3, 2 * saved_value[saved_clock[3]]); + + /* Now ensure GCLK3 is configured as a Harvard generator. */ + if (saved_clock[3] == PLL_MCLK) + global_clocks[3].source = CLOCK_SOURCE_PLL_MCLK_HARVARD; + else + global_clocks[3].source = CLOCK_SOURCE_PLL_VCLK_HARVARD; + + /* Re-assign any existing PLL clocks. */ + for (clockId = 0; clockId < 3; clockId++) + { + if (saved_clock[clockId] != NO_PLL_CLK) + use_PLL_clock(saved_clock[clockId], saved_value[saved_clock[clockId]]); + } + } +} + + +/* Print out the settings of the PLL clocks and the global clock sources. + + Parameters: + with_PLL_clocks : if TRUE, print the settings of the PLL clocks + with_global_only_if_using_PLL: if TRUE, print the sources of the global + clocks only if at least one of those sources + is a PLL clock + */ + +static void +print_clock_settings (Boolean with_PLL_clocks, Boolean with_global_only_if_using_PLL) +{ + Boolean with_global_clocks = TRUE; + unsigned int i; + + if (with_global_only_if_using_PLL) + { + with_global_clocks = FALSE; + + for (i = 0; i < ELEMENTS_IN_ARRAY(global_clocks); i++) + { + if (global_clocks[i].PLL_clock != NO_PLL_CLK) + { + with_global_clocks = TRUE; + break; + } + } + } + + if (with_PLL_clocks) + { + printf_filtered(_("PLL clock %s : %.2lf MHz.\n"), PLL_CLOCK_NAME(PLL_MCLK), PLL_clocks[PLL_MCLK].actual_frequency); + printf_filtered(_("PLL clock %s : %.2lf MHz.\n"), PLL_CLOCK_NAME(PLL_VCLK), PLL_clocks[PLL_VCLK].actual_frequency); + } + + if (with_global_clocks) + { + for (i = 0; i < ELEMENTS_IN_ARRAY(global_clocks); i++) + { + GlobalClock clock = global_clocks[i]; + const char *format = "GCLK%d << %s @ %.2lf MHz\n"; + const char *source; + MegaHertz value; + + switch (clock.source) + { + case CLOCK_SOURCE_PLL_MCLK: + source = CLOCK_SOURCE_STRINGS[clock.source]; + value = PLL_clocks[PLL_MCLK].actual_frequency; + break; + + case CLOCK_SOURCE_PLL_VCLK: + source = CLOCK_SOURCE_STRINGS[clock.source]; + value = PLL_clocks[PLL_VCLK].actual_frequency; + break; + + case CLOCK_SOURCE_PLL_MCLK_HARVARD: + source = GCLOCK3_SOURCE_STRINGS[clock.source]; + value = PLL_clocks[PLL_MCLK].actual_frequency / 2.0; + break; + + case CLOCK_SOURCE_PLL_VCLK_HARVARD: + source = GCLOCK3_SOURCE_STRINGS[clock.source]; + value = PLL_clocks[PLL_VCLK].actual_frequency / 2.0; + break; + + default: + format = "GCLK%d << %s\n"; + source = ((i == 3) ? GCLOCK3_SOURCE_STRINGS : CLOCK_SOURCE_STRINGS)[clock.source]; + value = 0.0; + break; + } + + printf_filtered(format, i, source, value); + } + } +} + + +/* Set the two PLL clocks to the given frequencies. */ + +static void +set_PLL_clocks (MegaHertz requested_MCLK_frequency, + MegaHertz requested_VCLK_frequency) +{ + DEBUG("set_PLL_clocks: MCLK = %.2lf MHz, VCLK = %.2lf MHz\n", + requested_MCLK_frequency, requested_VCLK_frequency); + + /* Configure PLL clocks. */ + + if (requested_MCLK_frequency != UNDEFINED_FREQUENCY) + { + if (set_PLL_clock_frequency(PLL_MCLK, requested_MCLK_frequency, TRUE, TRUE)) + PLL_clocks[PLL_MCLK].in_use = TRUE; + } + + if (requested_VCLK_frequency != UNDEFINED_FREQUENCY) + { + if (set_PLL_clock_frequency(PLL_VCLK, requested_VCLK_frequency, TRUE, TRUE)) + PLL_clocks[PLL_VCLK].in_use = TRUE; + } +} + + +/* Set the clock settings. + + The PLL clocks are set only if this is being done after the target board + FPGA has been blasted. + If any of the global clock sources needs to be set, the target CPLD is + configured, and the given message is printed out. */ + +static void +program_clock_settings (const char *message, Boolean after_blast) +{ + unsigned int i; + + /* If the FPGA has been blasted, configure the PLL clocks. */ + if (after_blast) + set_PLL_clocks(FREQUENCY(PLL_MCLK), FREQUENCY(PLL_VCLK)); + + /* Do any of the global clocks need to be set? */ + for (i = 0; i < ELEMENTS_IN_ARRAY(global_clocks); i++) + { + if (global_clocks[i].set) + { + /* Print status message only if there is something to be done. */ + printf_filtered("%s\n", message); + + if (harvard) + enable_Harvard_clock(); + + configure_CPLD(); + + print_clock_settings(after_blast, FALSE); + break; + } + } + + /* Reset the JTAG Test Access Port Controller. */ + arc_jtag_ops.reset(); +} + + +/* -------------------------------------------------------------------------- */ +/* local functions for blasting the FPGA */ +/* -------------------------------------------------------------------------- */ + +/* Try to blast the target board FPGA. + Return TRUE if blasting is done. */ + +static Boolean +blast_board (char *args, int from_tty) +{ + /* Check that a file name has been given. */ + if (args == NULL) + printf_filtered (_(ARC_BLAST_BOARD_COMMAND_USAGE)); + else + { + char *suffix = strrchr(args, '.'); + + /* Check the file is an .xbf file. */ + if ((suffix != NULL) && (strcasecmp(suffix, ".xbf") == 0)) + { + FILE *fp; + + /* Check that the JTAG interface (which opens the GPIO driver) is open + (do this before opening the file, as this function does not return + here if the interface is not open). */ + arc_jtag_ops.check_open(); + + fp = fopen(args, "rb"); + + if (fp) + { + char *message = NULL; + + if (initialize_FPGA()) + { + if (blast_FPGA(fp)) + { + /* Reset the JTAG Test Access Port Controller. */ + arc_jtag_ops.reset(); + + program_clock_settings(_("Reconfiguring clock settings after FPGA blast."), TRUE); + + return TRUE; + } + else + message = _("Can not blast FPGA"); + } + else + message = _("Can not initialize FPGA for blasting"); + + (void) fclose(fp); + + if (message) + error("%s", message); + } + else + error(_("Can not open file '%s': %s"), args, strerror(errno)); + } + else + error(_("Filename does not have suffix .xbf, so is presumably not an XBF file")); + } + + return FALSE; +} + + +/* -------------------------------------------------------------------------- */ +/* local functions implementing commands */ +/* -------------------------------------------------------------------------- */ + +/* Command: <command> <XBF_file> + + Blast the target board's FPGA with an XBF file. */ + +static void +arc_blast_board_FPGA (char *args, int from_tty) +{ + if (blast_board(args, from_tty)) + { + /* We no longer know what the target processor is. */ + arc_architecture_is_unknown(); + + /* So find it out again. */ + arc_update_architecture(arc_read_jtag_aux_register); + + /* And check that it matches the aux registers and the executable file. */ + ARCHITECTURE_CHECK(current_gdbarch, + (current_objfile) ? current_objfile->obfd : NULL); + } +} + + +/* Command: <command> [ <clock> = ] <frequency> [ , <frequency> ] + + Set the frequency of one or both PLL clocks. */ + +static void +arc_set_clock_frequency (char *args, int from_tty) +{ + MegaHertz MCLK_frequency = UNDEFINED_FREQUENCY; + MegaHertz VCLK_frequency = UNDEFINED_FREQUENCY; + int result; + char *value; + + if (args == NULL) + { + printf_filtered (_(ARC_SET_CLOCK_FREQUENCY_COMMAND_USAGE)); + return; + } + + result = name_value_pair(args, &value); + + if (result == 0) + { + printf_filtered (_(ARC_SET_CLOCK_FREQUENCY_COMMAND_USAGE)); + return; + } + + if (result == 1) + { + char *comma = strchr(args, ','); + + if (comma) + { + *comma = '\0'; + MCLK_frequency = strtod(args, NULL); + VCLK_frequency = strtod(comma + 1, NULL); + } + else + MCLK_frequency = strtod(args, NULL); + + } + else if (result == 2) + { + char *comma = strchr(value, ','); + + if (comma) + { + printf_filtered (_(ARC_SET_CLOCK_FREQUENCY_COMMAND_USAGE)); + return; + } + + if (strcasecmp(args, "mclk") == 0) + MCLK_frequency = strtod(value, NULL); + else if (strcasecmp(args, "vclk") == 0) + VCLK_frequency = strtod(value, NULL); + else + { + warning(_("invalid PLL clock '%s'"), args); + return; + } + } + + /* strtod returns 0 for an invalid argument - and 0 is not a valid clock + frequency anyway! */ + if (MCLK_frequency == 0.0 || VCLK_frequency == 0.0) + { + warning(_("invalid clock frequency")); + } + else + { + DEBUG(_("MCLK : %.2lf MHz.\n"), MCLK_frequency); + DEBUG(_("VCLK : %.2lf MHz.\n"), VCLK_frequency); + + /* Check that the JTAG interface (which opens the GPIO driver) is open. */ + arc_jtag_ops.check_open(); + + set_PLL_clocks(MCLK_frequency, VCLK_frequency); + check_PLL_clock_frequencies(); + print_clock_settings(FALSE, TRUE); + + /* Reset the JTAG Test Access Port Controller. */ + arc_jtag_ops.reset(); + } +} + + +/* Command: <command> gclk<N> = <source> + gclk = <source> + gclks = <source> , { <source> } + harvard + + Set the source of one or more global clocks. */ + +static void +arc_set_clock_source (char *args, int from_tty) +{ + Boolean invalid = FALSE; + + if (args) + { + int result; + char *value; + + /* Check that the JTAG interface (which opens the GPIO driver) is open. */ + arc_jtag_ops.check_open(); + + result = name_value_pair(args, &value); + + if (result == 1) + { + if (strcasecmp(args, "harvard") == 0) + harvard = TRUE; + else + invalid = TRUE; + } + else if (result == 2) + { + char *key = args; + + DEBUG("key = %s, value = %s\n", key, value); + + if (strncasecmp(key, "gclk", 4) == 0) + { + size_t keylength = strlen(key); + + if (keylength == 4) + set_global_clock(3, value); + else if (keylength == 5) + { + if (key[4] == 's' || key[4] == 'S') + { + GlobalClockId clockId = 0; + char *clockData = strtok(value, " ,"); + + do + { + if (clockId == NUM_GLOBAL_CLOCKS) + { + warning(_("too many clock sources specified")); + return; + } + + set_global_clock(clockId++, clockData); + clockData = strtok(NULL, " ,"); + } while (clockData != NULL); + } + else if ('0' <= key[4] && key[4] < '0' + (char) NUM_GLOBAL_CLOCKS) + { + DEBUG("gclkN found\n"); + set_global_clock((GlobalClockId) (key[4] - (char) '0'), value); + } + else + { + warning(_("'%c' is not a valid clock number"), key[4]); + return; + } + } + else + invalid = TRUE; + } + else + invalid = TRUE; + } + else + invalid = TRUE; + } + else + invalid = TRUE; + + if (invalid) + printf_filtered (_(ARC_SET_CLOCK_SOURCE_COMMAND_USAGE)); + else + program_clock_settings(_("Attempting to set clocks."), FALSE); +} + + +/* Command: <command> + + Show the current clock settings. */ + +static void +arc_print_clock_settings (char *args, int from_tty) +{ + if (args) + { + printf_filtered (_(ARC_CLOCK_SETTINGS_COMMAND_USAGE)); + return; + } + + /* Check that the JTAG interface (which opens the GPIO driver) is open. */ + arc_jtag_ops.check_open(); + + print_clock_settings(TRUE, FALSE); +} + + +/* Command: <command> + + Show the current target board FPGA status. */ + +static void +arc_check_FPGA_configuration (char *args, int from_tty) +{ + if (args) + { + printf_filtered (_(ARC_FPGA_COMMAND_USAGE)); + return; + } + + switch (arc_is_FPGA_configured()) + { + case INACCESSIBLE: + break; + case CONFIGURED: + printf_filtered(_("FPGA is configured.\n")); + break; + case UNCONFIGURED: + printf_filtered(_("FPGA is not configured.\n")); + break; + } +} + + +/* -------------------------------------------------------------------------- */ +/* externally visible functions */ +/* -------------------------------------------------------------------------- */ + +/* Blast the target board FPGA. */ + +void +arc_blast_board (char *args, int from_tty) +{ + (void) blast_board(args, from_tty); +} + + +/* Reset the target board. */ + +void +arc_reset_board (void) +{ + /* Toggle the SS1 line - this should do a soft reset. */ + + write_control_port(SS1 | SS0 | CNT, 0); + write_control_port( SS0 | CNT, 200); /* TBH 18 JUN 2003 delay needed by slower simulations. */ + write_control_port(SS1 | SS0 | CNT, 0); + + /* Reset the PLL clocks and the global clock sources - this should be done + by the soft reset, but that does not appear to happen! */ + reset_clock_configuration(); + + (void) set_PLL_clock_frequency(PLL_MCLK, MCLK_RESET_FREQUENCY, FALSE, FALSE); + (void) set_PLL_clock_frequency(PLL_VCLK, VCLK_RESET_FREQUENCY, FALSE, FALSE); + + configure_CPLD(); +} + + +/* Check whether the FPGA has been configured (i.e. blasted with an XBF). */ + +FPGA_Status +arc_is_FPGA_configured (void) +{ + FPGA_Status result; + + ENTERMSG; + + /* Try to open the JTAG interface (which opens the GPIO driver). */ + if (arc_jtag_ops.open(arc_aux_find_register_number("MEMSUBSYS", ARC_HW_MEMSUBSYS_REGNUM))) + { + /* Get the current state of the control register. */ + Byte origCTRL = gpio_read(CONTROL_PORT) ^ C_XOR; + Byte newCTRL; + Byte status; + + /* If SS0 is low, bring this high first (to protect against reset). */ + if (SS0 != (origCTRL & SS0)) + { + /* Output new control state. */ + newCTRL = (origCTRL | SS0); + write_control_port(newCTRL, 1); + } + + /* Ensure that SS0 is high, and SS1 and CNT are low. */ + newCTRL = (origCTRL | SS0) & 0xF5; // 11110101 + newCTRL = newCTRL | BI; + write_control_port(newCTRL, 1); + + /* Read the OP input. */ + status = read_status_port(); + + /* If SS1 was originally high then bring high now (to protect against reset). */ + if (SS1 == (origCTRL & SS1)) + { + /* Output new control state (Gray code transition). */ + newCTRL = (origCTRL | SS1); + write_control_port(newCTRL, 1); + } + + /* Restore the control register. */ + write_control_port(origCTRL, 1); + + /* Reset the JTAG Test Access Port Controller. */ + arc_jtag_ops.reset(); + + result = IS_SET(FPA_CFG_DONE, status) ? CONFIGURED : UNCONFIGURED; + } + else + result = INACCESSIBLE; + + LEAVEMSG; + return result; +} + + +/* Initialize the module. This function is called from the gdb core on start-up. */ + +void +_initialize_arc_board (void) +{ + struct cmd_list_element* c; + + /* Reset the configuration info to its default state. */ + reset_clock_configuration(); + + /* Add support for blasting an FPGA board (ARCangel). */ + c = add_cmd (ARC_BLAST_BOARD_COMMAND, + class_obscure, + arc_blast_board_FPGA, + _("Blast the ARC board FPGA.\n" + ARC_BLAST_BOARD_COMMAND_USAGE + "<FILE> is the filepath of an XBF (eXtended Binary Format) file.\n"), + &cmdlist); + set_cmd_completer (c, filename_completer); + + /* Add support for setting the CPU clock frequency. */ + (void) add_cmd (ARC_SET_CLOCK_FREQUENCY_COMMAND, + class_obscure, + arc_set_clock_frequency, + _("Set the PLL frequency on the ARC board.\n" + ARC_SET_CLOCK_FREQUENCY_COMMAND_USAGE + "<CLOCK> is 'mclk' or 'vclk'; if omitted, and only one frequency is given, it defaults to 'mclk'.\n" + "<FREQUENCY> is a number (interpreted as MegaHertz).\n"), + &cmdlist); + + /* Add support for setting the CPU clock sources. */ + (void) add_cmd (ARC_SET_CLOCK_SOURCE_COMMAND, + class_obscure, + arc_set_clock_source, + _("Set the clock sources on the ARC board.\n" + ARC_SET_CLOCK_SOURCE_COMMAND_USAGE + "N is in the range 0 .. 3; if omitted, it defaults to 3.\n" + "<SOURCE> is 'crystal', 'dips', 'highimp', 'host', 'mclk', 'vclk' or a number (interpreted as MegaHertz). \n"), + &cmdlist); + + /* Add support for showing the clock settings. */ + (void) add_cmd (ARC_CLOCK_SETTINGS_COMMAND, + class_info, + arc_print_clock_settings, + _("Show the clock settings on the ARC board.\n" + ARC_CLOCK_SETTINGS_COMMAND_USAGE), + &infolist); + + /* Add support for checking whether the FPGA board has been configured. */ + (void) add_cmd (ARC_FPGA_COMMAND, + class_info, + arc_check_FPGA_configuration, + _("Check ARC board FPGA configuration.\n" + ARC_FPGA_COMMAND_USAGE), + &infolist); +} + +/******************************************************************************/ diff --git a/gdb/arc-board.h b/gdb/arc-board.h new file mode 100644 index 00000000000..3683b5baee4 --- /dev/null +++ b/gdb/arc-board.h @@ -0,0 +1,65 @@ +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. + + Copyright 2008, 2009 Free Software Foundation, Inc. + + Contributed by ARC International (www.arc.com) + + Authors: + Phil Barnard <phil.barnard@arc.com> + Richard Stuckey <richard.stuckey@arc.com> + + 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 3 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, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This header file defines operations for controlling an ARC target */ +/* board. */ +/* */ +/* These operations are: */ +/* 1) configuring ("blasting") an FPGA target with the contents of an */ +/* XBF file; */ +/* 2) checking whether a target has been so configured; and */ +/* 3) setting the clock frequency of the target. */ +/* */ +/* Notes: */ +/* The arc_blast_board function implements an ARC-specific command; hence */ +/* its 'args' parameter contains data entered by the debugger user, which */ +/* must be checked for validity. */ +/* */ +/******************************************************************************/ + +#ifndef ARC_BOARD_H +#define ARC_BOARD_H + + +typedef enum +{ + INACCESSIBLE, + CONFIGURED, + UNCONFIGURED +} FPGA_Status; + + +void arc_blast_board (char *args, int from_tty); + +void arc_reset_board (void); + +FPGA_Status arc_is_FPGA_configured (void); + + +#endif /* ARC_BOARD_H */ +/******************************************************************************/ diff --git a/gdb/arc-dummy-gpio.c b/gdb/arc-dummy-gpio.c new file mode 100644 index 00000000000..8297408215d --- /dev/null +++ b/gdb/arc-dummy-gpio.c @@ -0,0 +1,94 @@ +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. + + Copyright 2008, 2009 Free Software Foundation, Inc. + + Contributed by ARC International (www.arc.com) + + Authors: + Richard Stuckey <richard.stuckey@arc.com> + + 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 3 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, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This module implements a dummy set of operations for reading data from */ +/* / writing data to a parallel port using a GPIO (General Purpose Input/ */ +/* Output) driver. */ +/* */ +/* It is useful for testing when no hardware target is available. */ +/* */ +/******************************************************************************/ + +#include "arc-gpio.h" + + +/* -------------------------------------------------------------------------- */ +/* externally visible data */ +/* -------------------------------------------------------------------------- */ + +/* This is set to TRUE if an I/O error occurs in accessing the port. + (This never happens in this dummy module). */ +Boolean gpio_port_error; + + +/* -------------------------------------------------------------------------- */ +/* externally visible functions */ +/* -------------------------------------------------------------------------- */ + +/* Initialization of the GPIO interface. */ + +Boolean +gpio_open (void) +{ + gpio_port_error = FALSE; + return TRUE; +} + + +/* Close the GPIO interface. */ + +void +gpio_close (void) +{ +} + + +/* Write a byte of data to the given port. */ + +void +gpio_write (ParallelPort port, Byte data) +{ +} + + +/* Read a byte of data from the given port. */ + +Byte +gpio_read (ParallelPort port) +{ + return 0; +} + + +/* Write a series of bytes of data to the ports. */ + +void +gpio_write_array (GPIO_Pair array[], unsigned int num_elements) +{ +} + +/******************************************************************************/ diff --git a/gdb/arc-dummy-jtag-ops.c b/gdb/arc-dummy-jtag-ops.c new file mode 100644 index 00000000000..257330b79f4 --- /dev/null +++ b/gdb/arc-dummy-jtag-ops.c @@ -0,0 +1,274 @@ +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. + + Copyright 2008, 2009 Free Software Foundation, Inc. + + Contributed by ARC International (www.arc.com) + + Author: + Richard Stuckey <richard.stuckey@arc.com> + + 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 3 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, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This module implements a dummy set of operations for debug access to */ +/* an ARC processor via its JTAG interface. */ +/* */ +/* It is useful for testing when no hardware target is available. */ +/* */ +/******************************************************************************/ + +/* system header files */ +#include <stdio.h> + +/* ARC header files */ +#include "arc-jtag-ops.h" +#include "arc-registers.h" + + +/* -------------------------------------------------------------------------- */ +/* local data */ +/* -------------------------------------------------------------------------- */ + +#define AMV1_REG (ARC_HW_AMV0_REGNUM + 3) + + +/* The number of actionpoints supported by the target. Set this to different + values (2, 4 or 8) when testing. */ +static unsigned int num_actionpoints = 8; + + +/* -------------------------------------------------------------------------- */ +/* externally visible data */ +/* -------------------------------------------------------------------------- */ + +JTAG_Operations arc_jtag_ops; + + +/* -------------------------------------------------------------------------- */ +/* main operations */ +/* -------------------------------------------------------------------------- */ + +/* These are the functions that are called from outside this module via the + pointers in the arc_jtag_ops global object. + + N.B. none of these functions are called from within this module. */ + + +/* Read a processor core register. */ + +static JTAG_OperationStatus +jtag_read_core_reg (ARC_RegisterNumber regnum, ARC_RegisterContents *contents) +{ + return JTAG_SUCCESS; +} + + +/* Write a processor core register. */ + +static JTAG_OperationStatus +jtag_write_core_reg (ARC_RegisterNumber regnum, ARC_RegisterContents contents) +{ + return JTAG_SUCCESS; +} + + +/* Read a processor auxiliary register. */ + +static JTAG_OperationStatus +jtag_read_aux_reg (ARC_RegisterNumber regnum, ARC_RegisterContents *contents) +{ + /* Return a "useful" value for some specific registers. */ + + if (regnum == ARC_HW_PC_REGNUM) + *contents = 0x00001008; + else if (regnum == ARC_HW_AP_BUILD_REGNUM) + { + if (num_actionpoints == 2) + *contents = 0x00000004; + else if (num_actionpoints == 4) + *contents = 0x00000104; + else + *contents = 0x00000204; + } + else if (regnum == ARC_HW_DEBUG_REGNUM) + { + /* Fake trigger of AP 1. */ + *contents = DEBUG_ACTIONPOINT_HALT | + (1 << (DEBUG_ACTIONPOINT_STATUS_SHIFT + 1)); + } + else if (regnum == AMV1_REG) + { + *contents = 0x4008; + } + else if (regnum == ARC_HW_IDENTITY_REGNUM) + { + *contents = 0x31; + } + else if (regnum == ARC_HW_STATUS32_REGNUM) + { + *contents = STATUS32_HALT; + } + else + *contents = 0; + +// DEBUG("regnum = %x, contents = 0x%08X", regnum, *contents); + + return JTAG_SUCCESS; +} + + +/* Write a processor auxiliary register. */ + +static JTAG_OperationStatus +jtag_write_aux_reg (ARC_RegisterNumber regnum, ARC_RegisterContents contents) +{ +// printf(_("AUX: regnum = %d, contents = 0x%08X\n"), regnum, contents); + return JTAG_SUCCESS; +} + + +/* Read a word of data from memory; the given address must be word-aligned. + Returns number of bytes read. */ + +static unsigned int +jtag_read_word (ARC_Address addr, ARC_Word *data) +{ + *data = 0; + return BYTES_IN_WORD; +} + + +/* Write a word of data to memory; the given address must be word-aligned. + Returns number of bytes written. */ + +static unsigned int +jtag_write_word (ARC_Address addr, ARC_Word data) +{ + return BYTES_IN_WORD; +} + + +/* Read a number of words of data from target memory starting at the given address. + Returns number of bytes read. */ + +static unsigned int +jtag_read_chunk (ARC_Address addr, ARC_Byte *data, unsigned int words) +{ + return words * BYTES_IN_WORD; +} + + +/* Write a number of words of data to target memory starting at the given address. + Returns number of bytes written. */ + +static unsigned int +jtag_write_chunk (ARC_Address addr, ARC_Byte *data, unsigned int words) +{ + return words * BYTES_IN_WORD; +} + + +/* Write a number of copies of a word-sized pattern of data to memory starting + at the given address. + Returns number of bytes written. */ + +static unsigned int +jtag_write_pattern (ARC_Address addr, ARC_Word pattern, unsigned int words) +{ + return words * BYTES_IN_WORD; +} + + +/* Open the JTAG interface. + Returns TRUE for success. */ + +static Boolean +jtag_open (void) +{ + arc_jtag_ops.status = JTAG_OPENED; + return TRUE; +} + + +/* Close the JTAG interface. */ + +static void +jtag_close (void) +{ + arc_jtag_ops.status = JTAG_CLOSED; +} + + +/* Reset the target JTAG controller. */ + +static void +jtag_reset (void) +{ +} + + +/* Reset the target board. */ + +static void +jtag_reset_board (void) +{ +} + + +/* Check that the JTAG interface is open. + If it is closed, 'error' is called. */ + +static void +jtag_check_open (void) +{ + if (arc_jtag_ops.status == JTAG_CLOSED) + error(_("JTAG connection is closed. " + "Use command 'target " ARC_TARGET_NAME "' first.")); +} + + +/* -------------------------------------------------------------------------- */ +/* externally visible functions */ +/* -------------------------------------------------------------------------- */ + +/* Initialize the module. This function is called from the gdb core on start-up. */ + +void +_initialize_arc_jtag_ops (void) +{ + arc_jtag_ops.status = JTAG_CLOSED; + arc_jtag_ops.state_machine_debug = FALSE; + arc_jtag_ops.retry_count = 50; + + arc_jtag_ops.open = jtag_open; + arc_jtag_ops.close = jtag_close; + arc_jtag_ops.check_open = jtag_check_open; + arc_jtag_ops.reset_board = jtag_reset_board; + arc_jtag_ops.reset = jtag_reset; + arc_jtag_ops.memory_read_word = jtag_read_word; + arc_jtag_ops.memory_write_word = jtag_write_word; + arc_jtag_ops.memory_read_chunk = jtag_read_chunk; + arc_jtag_ops.memory_write_chunk = jtag_write_chunk; + arc_jtag_ops.memory_write_pattern = jtag_write_pattern; + arc_jtag_ops.read_aux_reg = jtag_read_aux_reg; + arc_jtag_ops.write_aux_reg = jtag_write_aux_reg; + arc_jtag_ops.read_core_reg = jtag_read_core_reg; + arc_jtag_ops.write_core_reg = jtag_write_core_reg; +} + +/******************************************************************************/ diff --git a/gdb/arc-elf32-tdep.c b/gdb/arc-elf32-tdep.c new file mode 100755 index 00000000000..2c7de60639f --- /dev/null +++ b/gdb/arc-elf32-tdep.c @@ -0,0 +1,2064 @@ +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. + + Copyright 2005, 2008, 2009 Free Software Foundation, Inc. + + Contributed by Codito Technologies Pvt. Ltd. (www.codito.com) + + Authors: + Soam Vasani <soam.vasani@codito.com> + Ramana Radhakrishnan <ramana.radhakrishnan@codito.com> + Richard Stuckey <richard.stuckey@arc.com> + + 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 3 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, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This module provides support for the ARC processor family's target */ +/* dependencies which are specific to the arc-elf32 configuration of the */ +/* ARC gdb. */ +/* */ +/* */ +/* Functionality: */ +/* This module provides a number of operations: */ +/* */ +/* 1) a function which returns the name of a register, given its number */ +/* */ +/* 2) a function which determines whether a given register belongs to a */ +/* particular group (e.g. the group of registers which should be saved */ +/* and restored across a function call) */ +/* */ +/* 3) a function which prints out registers */ +/* */ +/* 4) functions which implement the gdb extended commands */ +/* */ +/* arc-watch-range <start> [<kind>] for setting a watchpoint range */ +/* arc-break-range <start> <length> for setting a breakpoint range */ +/* arc-fill-memory <start> <length> [<pattern>] for filling memory */ +/* */ +/* 5) functions for various operations (such as program loading) which */ +/* are common to the different arc-elf32 targets supported */ +/* */ +/* */ +/* Usage: */ +/* The module exports a function _initialize_arc_elf32_tdep: the call to */ +/* this function is generated by the gdb build mechanism, so this function*/ +/* should not be explicitly called. */ +/* */ +/* This module exports a function arc_elf32_initialize which creates the */ +/* user commands which use those command-implementing functions; it also */ +/* stores pointers to the other functions in a data structure so that */ +/* they may be called from outside this module. */ +/* */ +/* Some of the operations provided by this module are registered with gdb */ +/* during initialization; gdb then calls them via function pointers, */ +/* rather than by name (this allows gdb to handle multiple target */ +/* architectures): */ +/* */ +/* set_gdbarch_XXX (gdbarch, <function>); */ +/* */ +/* */ +/* Register Numbering Scheme: */ +/* The N target processor registers are assigned gdb numbers which form a */ +/* contiguous range starting at 0. The scheme used is: */ +/* */ +/* 0 .. n1 : core registers R0 .. R31 */ +/* n1+1 .. n2 : extension core registers R32 .. R59 (if any) */ +/* n2+1 : r60 (LP_COUNT) */ +/* n2+2 : r63 (PCL) */ +/* n2+3 : IDENTITY auxiliary register */ +/* n2+4 .. n3 : non-BCR auxiliary registers in address order */ +/* n3+1 .. N-1 : Build Configuration Registers in address order */ +/* */ +/* N.B. 1) core registers R61 and R62 are not included in the scheme, as */ +/* R61 is reserved, and R62 is not a real register; */ +/* */ +/* 2) the set of non-BCR auxiliary registers, and the set of BCRs, */ +/* are each ordered by increasing register address in the ARC */ +/* auxiliary register space; */ +/* */ +/* 3) the IDENTITY auxiliary register comes before all the other */ +/* auxiliary registers, even though it does not come first in the */ +/* address space: this is so that the debugger can always get the */ +/* contents of the register, and so determine the architectural */ +/* version of the target processor, regardless of what that */ +/* version may be (and hence what the processor's auxiliary */ +/* register set is) - this is vital when the debug target is a */ +/* remote target, as the position of the register contents in the */ +/* 'g' RSP response packet (or the number to be specified in the */ +/* 'p' query packet) depends upon the target processor version; */ +/* otherwise, we would have the problem that the debugger could */ +/* not determine the target processor version without already */ +/* knowing it! */ +/* */ +/* The numbers are assigned to the registers by the arc-registers module, */ +/* after the XML definitions of the auxiliary registers and any extension */ +/* core registers defined for the target processor have been read and */ +/* processed. */ +/* */ +/* */ +/* Auxiliary Registers Definition: */ +/* The ARC processor is configurable, and the set of auxiliary registers */ +/* that a target processor may possess depends upon the configuration. It */ +/* is therefore not possible to hard-code descriptions of this register */ +/* set into the debugger. Instead, the descriptions of the registers are */ +/* read from an XML file (or files). */ +/* */ +/* The arc-elf32-gdb debugger provides commands which allow the user to */ +/* instruct it to read an XML file and use the register definitions in */ +/* that file; these definitions either completely replace, or are added */ +/* to (depending upon which command is used), any existing set of */ +/* definitions the debugger has; this allows processor architecture */ +/* variants to be described by groups of files (e.g. a common main file */ +/* describing the base configuration, plus additional files describing */ +/* cache-related registers, or MMU-related registers, which are specific */ +/* to different variants). */ +/* */ +/* Note that this scheme may also be used to define the set of extension */ +/* core registers (if any) possessed by the target processor. */ +/* */ +/* If no register set is defined by use of the commands, the debugger */ +/* attempts to read the register definitions from a default file; it will */ +/* look for this file first in the user's current working directory, then */ +/* in the user's home directory. In order to provide maximum flexibility, */ +/* the debugger delays the attempt to read the default file until it is */ +/* necessary, e.g. when connection to a target is being attempted, or a */ +/* check is made that the executable file to be debugged has been built */ +/* for the same architectural version (e.g. ARC600, ARC700, etc.) as the */ +/* version of the target processor. */ +/* */ +/* The arc-elf32 specific code makes as few assumptions as possible about */ +/* the auxiliary register set: it will always try to get the number (i.e. */ +/* the hardware number, the offset in the auxiliary register space) of an */ +/* auxiliary register from the definition of that register, even in the */ +/* case that the register is defined in the base ARC architecture, and */ +/* hence should be present in all processor variants. */ +/* */ +/* In particular, no assumption is made about the PC; so, to guarantee */ +/* that the debugger has read a definition of the PC by the time that it */ +/* is needed (as the complete set of auxiliary register definitions may */ +/* be read from a number of files, there is no requirement for the PC's */ +/* definition to be in any given file), a "guard" is set upon the PC such */ +/* that any attempt to read/write the PC by gdb will result in a check */ +/* that the PC is defined: if the PC is defined, the guard is removed, */ +/* and the read/write operation performed - otherwise, an error message */ +/* is reported, and the operation aborted. */ +/* */ +/* Register Byte Order: */ +/* The target register contents are held in gdb's register cache (i.e. in */ +/* a regcache struct) in target byte order; however, when values are */ +/* read/written to/from the xISS target (i.e. the dynamically loaded xISS */ +/* simulator) or the ARCangel4 target those values must be in host byte */ +/* order. */ +/* */ +/* The ARC debugger is currently built only to run on an X86 Linux host, */ +/* so the assumption is made that the host is little-endian. */ +/* */ +/******************************************************************************/ + +/* system header files */ +#include <string.h> +#include <signal.h> +#include <byteswap.h> + +/* gdb header files */ +#include "defs.h" +#include "inferior.h" +#include "gdbcore.h" +#include "gdbcmd.h" +#include "regcache.h" +#include "exceptions.h" +#include "reggroups.h" +#include "observer.h" +#include "objfiles.h" +#include "arch-utils.h" +#include "gdb-events.h" +#include "gdb_assert.h" + +/* ARC header files */ +#include "config/arc/tm-embed.h" +#include "arc-tdep.h" +#include "arc-memory.h" +#include "arc-arguments.h" +#include "arc-elf32-tdep.h" +#include "arc-registers.h" +#include "arc-remote-fileio.h" + + +/* -------------------------------------------------------------------------- */ +/* local types */ +/* -------------------------------------------------------------------------- */ + +typedef struct +{ + struct gdbarch *gdbarch; + struct ui_file *file; + struct frame_info *frame; +} PrintData; + + +/* -------------------------------------------------------------------------- */ +/* local data */ +/* -------------------------------------------------------------------------- */ + +#define INVALID_REGISTER_NUMBER (ARC_RegisterNumber) 0xFFFFFFFFU + +#define WATCH_MEMORY_COMMAND "arc-watch-range" +#define BREAK_MEMORY_COMMAND "arc-break-range" +#define FILL_MEMORY_COMMAND "arc-fill-memory" + +#define WATCH_MEMORY_COMMAND_USAGE "Usage: " WATCH_MEMORY_COMMAND " <START> <LENGTH> [ read | write | access ]\n" +#define BREAK_MEMORY_COMMAND_USAGE "Usage: " BREAK_MEMORY_COMMAND " <START> <LENGTH>\n" +#define FILL_MEMORY_COMMAND_USAGE "Usage: " FILL_MEMORY_COMMAND " <START> <LENGTH> [ <PATTERN> ]\n" + + + +/* ARC 700 brk_s instruction. */ +static const unsigned char le_breakpoint_instruction[] = { 0xff, 0x7f }; +static const unsigned char be_breakpoint_instruction[] = { 0x7f, 0xff }; + + +/* N.B. the array size is specified in the declaration so that the compiler + will warn of "excess elements in array initializer" if there is a + mismatch (but not of too few elements, unfortunately!). */ +static const char *register_names[ARC_MAX_CORE_REGS] = +{ + "r0", "r1", "r2", "r3", "r4", "r5", "r6", + "r7", "r8", "r9", "r10", "r11", "r12", "r13", + "r14", "r15", "r16", "r17", "r18", "r19", "r20", + "r21", "r22", "r23", "r24", "r25", "r26", + + "fp", // r27 + "sp", // r28 + "ilink1", // r29 + "ilink2", // r30 + "blink", // r31 + + /* Extension core registers are 32 .. 59 inclusive. */ + "r32", "r33", "r34", "r35", "r36", "r37", "r38", "r39", + "r40", "r41", "r42", "r43", "r44", "r45", "r46", "r47", "r48", "r49", + "r50", "r51", "r52", "r53", "r54", "r55", "r56", "r57", "r58", "r59", + + "lp_count", + + /* 61 is reserved, 62 is not a real register. */ + "r61", + "r62", + + "pcl" +}; + + +/* For the Ctrl-C signal handler. */ +static void (*old_signal_handler) (int); + +/* This flag is used by the Ctrl-C interrupt mechanism: it is set by an + interrupt handler and tested by non-interrupt code, so must be declared + as volatile to avoid possible optimisation problems. */ +static volatile Boolean interrupt_processor; + +/* A pointer to the remote target's register store function. */ +static void (*remote_register_store)(struct regcache *regcache, int regno); + + +/* -------------------------------------------------------------------------- */ +/* externally visible data */ +/* -------------------------------------------------------------------------- */ + +/* These are the h/w register numbers of the DEBUG, PC and STATUS32 registers. */ +ARC_RegisterNumber arc_debug_regnum; +ARC_RegisterNumber arc_pc_regnum; +ARC_RegisterNumber arc_status32_regnum; + +/* Whether a program has been loaded to the target. */ +Boolean arc_program_is_loaded; + +/* Whether a target is connected. */ +Boolean arc_target_is_connected; + + +/* -------------------------------------------------------------------------- */ +/* local macros */ +/* -------------------------------------------------------------------------- */ + +#define PRINT(regnum) \ + default_print_registers_info (gdbarch, file, frame, regnum, all) + +#define PRINT_HW(hw_regnum) PRINT(arc_core_register_gdb_number(hw_regnum)) + +#define PRINT_BY_NAME(regname) \ +{ \ + ARC_AuxRegisterDefinition *def = arc_find_aux_register_by_name(regname); \ + \ + if (def) \ + PRINT(arc_aux_gdb_register_number(def)); \ +} while (0) + + +#define EXTRACT(argument, type, result) \ +{ \ + struct expression *expr = parse_expression(argument); \ + struct value *val = evaluate_expression(expr); \ + struct cleanup *chain = make_cleanup(free_current_contents, &expr); \ + \ + result = *(type*) (value_contents (val)); \ + do_cleanups (chain); \ +} + + +/* -------------------------------------------------------------------------- */ +/* local functions */ +/* -------------------------------------------------------------------------- */ + +/* This function creates the processor-specific information for the arc-elf32-gdb + variant of the the ARC gdb deubbger. */ + +static void +create_variant_info (struct gdbarch_tdep *tdep) +{ + tdep->processor_variant_info = xmalloc(sizeof(ARC_VariantsInfo)); + tdep->processor_variant_info->processor_version = NO_ARCHITECTURE; + + arc_initialize_aux_reg_info(&tdep->processor_variant_info->registers); +} + + +/* This function is used to read an auxiliary register on the target. */ + +static Boolean +read_target_aux_register (ARC_RegisterNumber hw_regno, + ARC_RegisterContents *contents, + Boolean warn_on_failure) +{ + struct regcache *regcache = get_current_regcache(); + ARC_AuxRegisterDefinition *def = arc_find_aux_register_by_hw_number(hw_regno); + int gdb_regno = arc_aux_gdb_register_number(def); + + /* Read the register contents from the target to the register cache, + then collect the register value from the cache. */ + target_fetch_registers(regcache, gdb_regno); + regcache_raw_collect (regcache, gdb_regno, contents); + + /* The register cache holds the contents in target byte order, so convert to + host byte order if the target and host orders are different. */ + if (HOST_AND_TARGET_ENDIANNESS_DIFFER(current_gdbarch)) + *contents = __bswap_32(*contents); + + /* Unfortunately, we can not tell whether the read succeeded or failed. */ + return TRUE; +} + + +/* This function returns a pointer to a function which may be used to read an + auxiliary register on the target. The register contents returned by that + function are in host byte order. */ + +static ReadRegisterFunction +read_aux_register (struct target_ops *target) +{ + /* If we have a function which can read an aux register on the target, + and return a success/failure result, use it instead. */ + if ((strcmp(target->to_shortname, "arcxiss") == 0) || + (strcmp(target->to_shortname, "arcjtag") == 0)) + { + TargetOperations *operations = (TargetOperations*) target->to_data; + return operations->read_auxiliary_register; + } + + return read_target_aux_register; +} + + +/* Convert the contents of the given register in the given cache so that it can + be written to the target (i.e. if there are any fields in the register which + are required by the ARC processor architectural definition to have particular + values on write, set those fields to those values). */ + +static void convert_register(int gdb_regno, + struct regcache *regcache) +{ + ARC_RegisterContents contents; + + regcache_raw_collect (regcache, gdb_regno, &contents); + arc_convert_aux_contents_for_write (gdb_regno, &contents); + regcache_raw_supply (regcache, gdb_regno, &contents); +} + + +/* This function is called when a remote target calls its 'to_store_registers' + operation: we intercept that call, and convert the register contents as + required, before calling the real operation. */ + +static void +intercept_remote_register_store (struct regcache *regcache, int gdb_regno) +{ + struct gdbarch *gdbarch = get_regcache_arch(regcache); + struct regcache *savedcache = regcache_xmalloc(gdbarch); + + ENTERARGS("gdb_regno: %d", gdb_regno); + + /* Save the register cache. */ + regcache_cpy(savedcache, regcache); + + /* Convert the value of the register(s) for writing. */ + if (gdb_regno >= 0) + convert_register(gdb_regno, regcache); + else + { + int num_regs = gdbarch_num_regs (gdbarch); + + for (gdb_regno = 0; gdb_regno < num_regs; gdb_regno++) + convert_register(gdb_regno, regcache); + } + + /* Now use the real remote target 'store registers' operation to store the + converted cache. */ + remote_register_store(regcache, gdb_regno); + + /* Restore the register cache. */ + regcache_cpy(regcache, savedcache); + regcache_xfree(savedcache); +} + + +/* This is a callback function which gets called by gdb whenever the current + object file changes. */ + +static void +new_object_file (struct objfile *objfile) +{ + if (objfile) + ARCHITECTURE_CHECK(current_gdbarch, objfile->obfd); +} + + +/* This is a callback function which gets called by gdb just before connection + * to a target is attempted. */ + +static void +pre_target_connection (struct target_ops *target) +{ + DEBUG("pre_target_connect : %s\n", target->to_shortname); + + /* We do not yet know the version of the target processor. */ + arc_architecture_is_unknown(); + + /* If we have not read yet any aux register definitions for this architecture, + * try to read the default file now. */ + if (!arc_aux_regs_defined(current_gdbarch)) + arc_read_default_aux_registers(current_gdbarch); +} + + +/* This is a callback function which gets called by gdb just after connection + to a target is completed. */ + +static void +post_target_connection (struct target_ops *target) +{ + DEBUG("post_target_connect : %s\n", target->to_shortname); + + arc_target_is_connected = TRUE; + + arc_update_architecture(read_aux_register(target)); + + ARCHITECTURE_CHECK(current_gdbarch, + (current_objfile) ? current_objfile->obfd : NULL); +} + + +/* This is a callback function which gets called by gdb just after disconnection + from a target has been completed. */ + +static void +post_target_disconnection (struct target_ops *target) +{ + DEBUG("post_target_disconnect : %s\n", target->to_shortname); + + arc_target_is_connected = FALSE; +} + + +/* This is a callback function which gets called by gdb after a target has been updated. */ + +static void +target_updated (struct target_ops *target) +{ + DEBUG("target_updated : %s\n", target->to_shortname); + + if (strcmp(target->to_shortname, "remote") == 0) + { + DEBUG("remote target register store interception is in force\n"); + + /* We must intercept the remote target's register store operation: we + need to convert register contents before they are written to the + target (we can not do that in remote.c as that is generic gdb code). */ + if (target->to_store_registers != intercept_remote_register_store) + { + remote_register_store = target->to_store_registers; + target->to_store_registers = intercept_remote_register_store; + } + } +} + + +/* -------------------------------------------------------------------------- */ +/* 1) local functions for handling Ctrl-C */ +/* -------------------------------------------------------------------------- */ + +/* The command line interface's stop routines. The interrupted_by_user function + is installed as a signal handler for SIGINT (it gets called when the user + types Ctrl-C). + + The first time a user requests a stop, we set the interrupt_processor flag. + If this does not work, and the user tries a second time, we ask the user if + he'd like to detach from the target. */ + + +static void +interrupted_twice (int signo); + + +/* This function is called when the user types Ctrl-C. */ + +static void +interrupted_by_user (int signo) +{ + /* Change the signal handler for Ctrl-C to the second level handler so that + if we get the signal again whilst waiting for the program to halt, we do + something more drastic. */ + (void) signal (SIGINT, interrupted_twice); + + /* This flag is checked in each iteration of the loop that polls the target + processor to see whether it has halted (e.g. at a breakpoint); if the + flag is set, an attempt will be made to force the processor to halt. + + N.B. once the polling loop is running, this flag is set only by this + handler, and is read only by the polling loop - so there is no + mutual exclusion problem to be worried about here; this is a MUCH + cleaner and more reliable method than trying to have this handler + force the halt itself, e.g. by calling target_stop. */ + interrupt_processor = TRUE; + + DEBUG("Attempting to interrupt...\n"); +} + + +/* This function is called when the user types Ctrl-C twice. */ + +static void +interrupted_twice (int signo) +{ + if (query(_("Interrupted while waiting for the program to halt.\n" + "Give up (and stop debugging it)?"))) + { + struct gdb_exception exception = {RETURN_QUIT, + GDB_NO_ERROR, + _("Interrupted by user")}; + + /* Put the old signal handler back. */ + (void) signal (signo, old_signal_handler); + + target_mourn_inferior(); + DEBUG("interrupted_twice: throwing exception\n"); + throw_exception (exception); + + /* Control does not return here! */ + } + + /* Change the signal handler for Ctrl-C back to the first level handler. */ + (void) signal (SIGINT, interrupted_by_user); +} + + +/* -------------------------------------------------------------------------- */ +/* 2) functions for reading/writing registers */ +/* -------------------------------------------------------------------------- */ + +/* This function maps a gdb internal register number to the hardware number + (i.e. core register number or number in the auxiliary register space). */ + +static ARC_RegisterNumber +get_hw_regnum_mapping (int gdb_regno) +{ + ARC_AuxRegisterDefinition *def; + + if (arc_is_core_register(gdb_regno)) + return arc_core_register_number(gdb_regno); + + def = arc_find_aux_register_by_gdb_number(gdb_regno); + + if (def) + return arc_aux_hw_register_number(def); + + /* Not found. */ + return INVALID_REGISTER_NUMBER; +} + + +/* This function fetches one register from the target and saves its contents in + the given register cache. The register is identified both by its gdb number + and its ARC hardware number. */ + +static void +debug_fetch_one_register (struct regcache * regcache, + ARC_RegisterNumber hw_regno, + int gdb_regno) +{ + TargetOperations *operations = (TargetOperations*) current_target.to_data; + ARC_RegisterContents contents; + Boolean register_read = FALSE; + + ENTERARGS("gdb = %d, h/w = %d", gdb_regno, hw_regno); + + gdb_assert(gdb_regno >= 0); + + /* N.B. do not give a warning message if the register is write-only, as gdb + may be reading all registers, and it is best to quietly ignore the + ones that can not be read! */ + if (arc_is_core_register(gdb_regno)) + { + if (arc_core_register_access(hw_regno) != WRITE_ONLY) + register_read = operations->read_core_register(hw_regno, &contents, TRUE); + } + else + { + ARC_AuxRegisterDefinition *def = arc_find_aux_register_by_hw_number(hw_regno); + + if (arc_aux_register_access(def) != WRITE_ONLY) + register_read = operations->read_auxiliary_register (hw_regno, &contents, TRUE); + } + + if (register_read) + { + DEBUG("read 0x%08X from target\n", contents); + + /* The read_<type>_register functions return the register contents in + host order, but the register cache holds them in target byte order, + so swap the bytes if necessary before supplying the contents to the + cache. */ + if (HOST_AND_TARGET_ENDIANNESS_DIFFER(get_regcache_arch(regcache))) + { + contents = __bswap_32(contents); + DEBUG("byte-swapped to 0x%08X\n", contents); + } + + regcache_raw_supply (regcache, (int) gdb_regno, &contents); + } + + LEAVEMSG; +} + + +/* This function is passed to the arc_all_aux_registers iterator. + It fetches one auxiliary register from the target. */ + +static void +debug_fetch_reg (ARC_AuxRegisterDefinition *def, void *data) +{ + debug_fetch_one_register((struct regcache*) data, + arc_aux_hw_register_number (def), + arc_aux_gdb_register_number(def)); +} + + +/* This function gets a register from the given register cache and stores it + to the target. The register is identified both by its gdb number and its + ARC hardware number. */ + +static void +debug_store_one_register (struct regcache *regcache, + ARC_RegisterNumber hw_regno, + int gdb_regno) +{ + TargetOperations *operations = (TargetOperations*) current_target.to_data; + ARC_RegisterContents contents; + + ENTERARGS("gdb = %d, h/w = %d", gdb_regno, hw_regno); + + gdb_assert(gdb_regno >= 0); + + regcache_raw_collect(regcache, gdb_regno, &contents); + + DEBUG("collected 0x%08X from cache\n", contents); + + /* The write_<type>_register functions take the register contents in + host order, but the register cache holds them in target byte order, + so swap the bytes if necessary after collecting the contents from the + functions. */ + if (HOST_AND_TARGET_ENDIANNESS_DIFFER(get_regcache_arch(regcache))) + { + contents = __bswap_32(contents); + DEBUG("byte-swapped to 0x%08X\n", contents); + } + + if (arc_is_core_register(gdb_regno)) + { + if (arc_core_register_access(hw_regno) == READ_ONLY) + arc_elf32_core_warning(REGISTER_IS_READ_ONLY, hw_regno); + else + (void) operations->write_core_register(hw_regno, contents, TRUE); + } + else + { + ARC_AuxRegisterDefinition *def = arc_find_aux_register_by_hw_number(hw_regno); + + if (arc_aux_register_access(def) == READ_ONLY) + arc_elf32_aux_warning(REGISTER_IS_READ_ONLY, hw_regno); + else + (void) operations->write_auxiliary_register (hw_regno, contents, TRUE); + } + + LEAVEMSG; +} + + +/* This function is passed to the arc_all_aux_registers iterator. + It stores one auxiliary register to the target. */ + +static void +debug_store_reg (ARC_AuxRegisterDefinition *def, void *data) +{ + debug_store_one_register ((struct regcache*) data, + arc_aux_hw_register_number (def), + arc_aux_gdb_register_number(def)); +} + + +/* -------------------------------------------------------------------------- */ +/* 3) functions for reading/writing mmeory */ +/* -------------------------------------------------------------------------- */ + +static unsigned int +read_bytes (ARC_Address address, + ARC_Byte *data, /* May be not word-aligned. */ + unsigned int bytes) +{ + TargetOperations *operations = (TargetOperations*) current_target.to_data; + + return arc_read_memory(operations, address, data, bytes); +} + + +static unsigned int +write_bytes (ARC_Address address, + ARC_Byte *data, /* May be not word-aligned. */ + unsigned int bytes) +{ + TargetOperations *operations = (TargetOperations*) current_target.to_data; + + return arc_write_memory(operations, address, data, bytes); +} + + +static unsigned int +write_pattern (ARC_Address address, + ARC_Word pattern, + unsigned int bytes) +{ + TargetOperations *operations = (TargetOperations*) current_target.to_data; + + return arc_write_pattern(operations, address, 0, bytes); +} + + +static unsigned int +write_zeros (ARC_Address address, + unsigned int bytes) +{ + TargetOperations *operations = (TargetOperations*) current_target.to_data; + + return arc_write_pattern(operations, address, 0, bytes); +} + + +/* -------------------------------------------------------------------------- */ +/* 4) local functions for processor control */ +/* -------------------------------------------------------------------------- */ + +/* This function is called when execution on the target has halted for some + reason (such as a breakpoint trigger). It determines whether the halt should + be reported to gdb, or execution resumed. + + Parameters: + status : a pointer to the target status information + debug : the contents of the target processor DEBUG register + read_core_register : a function which can read a target core register + + Result: TRUE if the halt is to be reported, FALSE if execution is to resume. */ + +static Boolean +report_processor_halt (struct target_waitstatus *status, + ARC_RegisterContents debug, + ReadRegisterFunction read_core_register) +{ + /* Test BH bit of DEBUG register. */ + if (debug & DEBUG_BH) + { + DEBUG("s/w breakpoint instruction was executed\n"); + + /* If the breakpoint is on an intercepted function entrypoint. */ + switch (arc_check_interception_breakpoint(&status->value.integer)) + { + case INTERCEPTION_RESUME: + /* If the user has typed a Ctrl-C since target execution was + last started. */ + if (interrupt_processor) + { + /* The interception is complete, so honour the interrupt + request by making it appear that the target was stopped + by a SIGINT signal; the PC has been set to the return + address of the intercepted function, so it will look to + the user as though the program was interrupted at that + point. */ + status->kind = TARGET_WAITKIND_STOPPED; + status->value.sig = TARGET_SIGNAL_INT; + } + else + { + /* This is the only case in which we return FALSE. */ + return FALSE; + } + break; + + case INTERCEPTION_HALT: + /* Some other breakpoint has triggered. */ + status->kind = TARGET_WAITKIND_STOPPED; + status->value.sig = TARGET_SIGNAL_TRAP; + break; + + case INTERCEPTION_EXIT: + /* The program called the 'exit' routine (its exit status has + been read by the interception mechanism and returned to us in + status->value.integer). */ + status->kind = TARGET_WAITKIND_EXITED; + break; + + case INTERCEPTION_INTERRUPT: + DEBUG("*** interception was interrupted!\n"); + status->kind = TARGET_WAITKIND_STOPPED; + status->value.sig = TARGET_SIGNAL_INT; + break; + } + } + /* Test SH bit of DEBUG register. */ + else if (debug & DEBUG_SH) + { + ARC_RegisterContents exit_code = 0; + + /* If the DEBUG.SH ("self halt") bit is set, we stopped because of the + flag instruction, which is used by programs to exit. */ + status->kind = TARGET_WAITKIND_EXITED; + + /* Get the exit code of the program (held in R0). */ + if (read_core_register(0, &exit_code, TRUE)) + { + DEBUG("exit code = %d\n", exit_code); + } + else + warning(_("assuming exit code = 0")); + + status->value.integer = (int) exit_code; + } + else + { + /* We stopped for some other reason: if the user had tried to interrupt + with a Ctrl-C, return the event as a SIGINT, otherwise as a SIGTRAP + (and let gdb work out what happened). */ + status->kind = TARGET_WAITKIND_STOPPED; + status->value.sig = (interrupt_processor) ? TARGET_SIGNAL_INT + : TARGET_SIGNAL_TRAP; + } + + return TRUE; +} + + +/* Determine whether the given register is a member of the given group. + + Returns 0, 1, or -1: + 0 means the register is not in the group. + 1 means the register is in the group. + -1 means the tdep has nothing to say about this register and group. */ + +static int +register_reggroup_p (int regnum, struct reggroup *group) +{ + gdb_assert(regnum >= 0); + + /* Save/restore: + 1. all standard core regs, except PCL (PCL is not writable) + 2. those extension core regs which are read/write + 3. aux regs LP_START .. LP_END (IDENTITY is not writable) + 4. aux regs PC_REGNUM .. STATUS32_L2 + 5. aux regs ERET .. EFA */ + + if (arc_is_core_register(regnum)) + { + ARC_RegisterNumber hw_num = arc_core_register_number(regnum); + + /* R61 and R62 are reserved; they are not in any reggroup. */ + if (hw_num == 61 || hw_num == 62) + return 0; + + if ((group == save_reggroup || group == restore_reggroup)) + { + if (IS_EXTENSION_CORE_REGISTER(hw_num)) + return (arc_core_register_access(hw_num) == READ_WRITE) ? 1 : 0; + + return (hw_num == ARC_PCL_REGNUM) ? 0 : 1; + } + + if (group == general_reggroup) + return 1; + } + else + { +#define REGISTER_NAME_IS(ident) (strcasecmp(name, ident) == 0) + + ARC_AuxRegisterDefinition *def = arc_find_aux_register_by_gdb_number(regnum); + + if (def) + { + const char *name = arc_aux_register_name(def); + + if (arc_aux_is_unused(def)) + return 0; + + if ((group == save_reggroup || group == restore_reggroup)) + { + if (arc_aux_register_access(def) != READ_WRITE) + return 0; + } + + /* Which regs to save/restore? */ + if ((group == save_reggroup || group == restore_reggroup)) + { + return (REGISTER_NAME_IS("LP_START") || + REGISTER_NAME_IS("LP_END") || + REGISTER_NAME_IS("PC") || + REGISTER_NAME_IS("STATUS32") || + REGISTER_NAME_IS("STATUS32_L1") || + REGISTER_NAME_IS("STATUS32_L2") || + REGISTER_NAME_IS("ERET") || + REGISTER_NAME_IS("ERBTA") || + REGISTER_NAME_IS("ERSTATUS") || + REGISTER_NAME_IS("ECR") || + REGISTER_NAME_IS("EFA")) ? 1 : 0; + } + + if (group == general_reggroup) + return (REGISTER_NAME_IS("STATUS32")) ? 0 : 1; + + if (group == system_reggroup) + { + return (REGISTER_NAME_IS("SEMAPHORE") || + REGISTER_NAME_IS("STATUS32_L1") || + REGISTER_NAME_IS("STATUS32_L2") || + REGISTER_NAME_IS("AUX_IRQ_LV12") || + REGISTER_NAME_IS("AUX_IRQ_LEV") || + REGISTER_NAME_IS("AUX_IRQ_HINT") || + REGISTER_NAME_IS("ERET") || + REGISTER_NAME_IS("ERBTA") || + REGISTER_NAME_IS("ERSTATUS") || + REGISTER_NAME_IS("ECR") || + REGISTER_NAME_IS("EFA") || + REGISTER_NAME_IS("ICAUSE1") || + REGISTER_NAME_IS("ICAUSE2") || + REGISTER_NAME_IS("AUX_IENABLE") || + REGISTER_NAME_IS("AUX_ITRIGGER") || + REGISTER_NAME_IS("BTA_L1") || + REGISTER_NAME_IS("BTA_L2") || + REGISTER_NAME_IS("AUX_IRQ_PULSE_CANCEL") || + REGISTER_NAME_IS("AUX_IRQ_PENDING")) ? 1 : 0; + } + } + } + + /* Let the caller sort it out! */ + return -1; +} + + +/* This function is passed to the arc_all_aux_registers iterator. + It prints the value of one auxiliary register. */ + +static void +print_one_aux_register (ARC_AuxRegisterDefinition *def, void *data) +{ + if (!arc_aux_is_unused(def)) + { + PrintData *p = (PrintData*) data; + int regnum = arc_aux_gdb_register_number(def); + + default_print_registers_info (p->gdbarch, p->file, p->frame, regnum, TRUE); + } +} + + +/* -------------------------------------------------------------------------- */ +/* 5) local functions called from gdb */ +/* -------------------------------------------------------------------------- */ + +/* Mapping from binutils/gcc register number to gdb register number ("regnum"). + + N.B. registers such as ARC_FP_REGNUM, ARC_SP_REGNUM, etc., actually have + different gdb register numbers in the arc-elf32 and arc-linux-uclibc + configurations of the ARC gdb. */ + +static int +arc_elf32_binutils_reg_to_regnum (struct gdbarch *gdbarch, int reg) +{ + return arc_core_register_gdb_number((ARC_RegisterNumber) reg); +} + + +/* Print the contents of one, some or all registers. */ + +static void +arc_elf32_print_registers_info (struct gdbarch *gdbarch, + struct ui_file *file, + struct frame_info *frame, + int regnum, + int all) +{ + if (regnum >= 0) + PRINT(regnum); + else + /* If regnum < 0, print registers. */ + { + /* R32 .. R59 are the extension core registers, R61 and R62 are reserved. */ + + /* R0 .. R26 */ + for (regnum = 0; regnum <= 26; regnum++) + PRINT_HW ((ARC_RegisterNumber) regnum); + + PRINT_HW (ARC_FP_REGNUM ); // r27 + PRINT_HW (ARC_SP_REGNUM ); // r28 + PRINT_HW (ARC_ILINK1_REGNUM ); // r29 + PRINT_HW (ARC_ILINK2_REGNUM ); // r30 + PRINT_HW (ARC_BLINK_REGNUM ); // r31 + PRINT_HW (ARC_LP_COUNT_REGNUM); // r60 + PRINT_HW (ARC_PCL_REGNUM ); // r63 + + if (all) + { + PrintData data = {gdbarch, file, frame}; + + /* Print all of the aux registers. */ + arc_all_aux_registers(print_one_aux_register, &data); + } + else + { + /* Print just a selection of the aux registers. */ + PRINT_BY_NAME ("LP_START" ); + PRINT_BY_NAME ("LP_END" ); + PRINT_BY_NAME ("STATUS32" ); + PRINT_BY_NAME ("BTA" ); + PRINT_BY_NAME ("EFA" ); + PRINT_BY_NAME ("ERET" ); + PRINT_BY_NAME ("STATUS32_L1"); + PRINT_BY_NAME ("STATUS32_L2"); + PRINT_BY_NAME ("ERSTATUS" ); + PRINT_BY_NAME ("PC" ); + } + } +} + + +/* Return the name of the given register. */ + +static const char* +arc_elf32_register_name (struct gdbarch *gdbarch, int gdb_regno) +{ + if (gdb_regno >= 0) + { + if (arc_is_core_register(gdb_regno)) + { + ARC_RegisterNumber hw_num = arc_core_register_number(gdb_regno); + + if (hw_num < ELEMENTS_IN_ARRAY(register_names)) + return register_names[hw_num]; + } + else + { + ARC_AuxRegisterDefinition *def = arc_find_aux_register_by_gdb_number(gdb_regno); + + /* If it is an auxiliary register. */ + if (def) + return arc_aux_register_name(def); + } + } + + internal_error(__FILE__, __LINE__, _("invalid register number: %d"), gdb_regno); +} + + +/* Determine whether the given register is read-only. */ + +static int +arc_elf32_cannot_store_register (struct gdbarch *gdbarch, int gdb_regno) +{ + ARC_AuxRegisterDefinition *def = arc_find_aux_register_by_gdb_number(gdb_regno); + + /* No warning should be printed. arc_cannot_store_register being + called does not imply that someone is actually writing to regnum. */ + + /* If it is an auxiliary register. */ + if (def) + return (arc_aux_register_access(def) == READ_ONLY) ? 1 : 0; + + /* It is writable. */ + return 0; +} + + +/* -------------------------------------------------------------------------- */ +/* 6) local functions implementing commands */ +/* -------------------------------------------------------------------------- */ + +/* This function handles seeting a hardware breakpoint or watchpoint across a + range of memory address (rather than at a single location). + + Parameters: + args : the user arguments to the command + from_tty : non-zero if the command was issued at the command-line + is_watchpoint : TRUE if a watchpoint to to be set, FALSE if a breakpoint + command : the name of the command + usage : the usage message for the command +*/ + +static void +memory_range_command (char *args, + int from_tty, + Boolean is_watchpoint, + const char *command, + const char *usage) +{ + char *length_arg; + unsigned int start; + int length; + enum target_hw_bp_type type; + + if (!args) + { + printf_filtered (_("%s"), usage); + return; + } + + length_arg = strchr(args, ' '); + + if (!length_arg) + { + printf_filtered (_("%s : no second argument\n%s"), command, usage); + return; + } + + /* Split up the input string. */ + length_arg[0] = (char) 0; + length_arg++; + while (*length_arg == ' ') length_arg++; + + if (is_watchpoint) + { + char *access_arg = strchr(length_arg, ' '); + + if (access_arg) + { + /* Split up the input string. */ + access_arg[0] = (char) 0; + access_arg++; + while (*access_arg == ' ') access_arg++; + + if (strcmp(access_arg, "read") == 0) + type = hw_read; + else if (strcmp(access_arg, "write") == 0) + type = hw_write; + else if (strcmp(access_arg, "access") == 0) + type = hw_access; + else + { + printf_filtered (_("%s: invalid type '%s'\n%s"), command, access_arg, usage); + return; + } + } + else + /* Access is write by default. */ + type = hw_write; + } + else + type = hw_execute; + + /* The address expression. */ + EXTRACT(args, unsigned int, start) + + /* The length expression. */ + EXTRACT(length_arg, int, length) + + if (length <= 0) + { + warning(_("%s: %s <= 0"), command, length_arg); + return; + } + + DEBUG("try to set %u breakpoint at 0x%08X length %d bytes\n", + type, start, length); + + watch_range_command(start, (unsigned int) length, type, from_tty); + + /* Although the call to insert_breakpoints would result in an error message + if the range breakpoint could not be set, the breakpoint would still be + entered into gdb's breakpoint table, and displayed by the 'info break' + command - that would be even more confusing to the user! */ +#if 0 + /* gdb manages breakpoints by deleting them from the target as soon as it + has halted, then re-inserting them again immediately before execution is + resumed (no, I don't know why either, unless it is to make generating a + disassembly display easier by removing all the s/w b/ps from the code) - + so in order to display what actionpoints are currently in use, we must + temporarily re-insert the breakpoints! */ + insert_breakpoints(); + arc_display_actionpoints(); + (void) remove_breakpoints(); +#endif +} + + +/* arc-break-range <start> <length> + Set hardware breakpoint at address START covering LENGTH bytes. */ + +static void +arc_elf32_break_memory_command (char *arg, int from_tty) +{ + memory_range_command(arg, from_tty, FALSE, BREAK_MEMORY_COMMAND, BREAK_MEMORY_COMMAND_USAGE); +} + + +/* arc-watch-range <start> <length> [read|write|access] + Set hardware watchpoint at address START covering LENGTH bytes. */ + +static void +arc_elf32_watch_memory_command (char *arg, int from_tty) +{ + memory_range_command(arg, from_tty, TRUE, WATCH_MEMORY_COMMAND, WATCH_MEMORY_COMMAND_USAGE); +} + + +/* arc-fill-memory <start> <length> [<pattern>] + Write repeated copies of PATTERN at address START covering LENGTH bytes. */ + +static void +arc_elf32_fill_memory_command (char *arg, int from_tty) +{ + TargetOperations *operations = (TargetOperations*) current_target.to_data; + char *length_arg; + char *pattern_arg; + ARC_Address start; + ARC_Word pattern; + int length; + + gdb_assert(operations != NULL); + + if (!arg) + { + printf_filtered ("%s", _(FILL_MEMORY_COMMAND_USAGE)); + return; + } + + length_arg = strchr(arg, ' '); + + if (!length_arg) + { + printf_filtered (_(FILL_MEMORY_COMMAND " : no second argument\n" FILL_MEMORY_COMMAND_USAGE)); + return; + } + + /* Split up the input string. */ + length_arg[0] = (char) 0; + length_arg++; + while (*length_arg == ' ') length_arg++; + + pattern_arg = strchr(length_arg, ' '); + if (pattern_arg) + { + /* Split up the input string. */ + pattern_arg[0] = (char) 0; + pattern_arg++; + } + + /* The address expression. */ + EXTRACT(arg, ARC_Address, start) + + /* The length expression. */ + EXTRACT(length_arg, int, length) + + if (length <= 0) + { + warning(_(FILL_MEMORY_COMMAND ": %s <= 0"), length_arg); + return; + } + + if (pattern_arg) + { + /* The pattern expression. */ + EXTRACT(pattern_arg, ARC_Word, pattern) + } + else + pattern = 0; + + if (operations) + { + unsigned int written = write_pattern(start, pattern, (unsigned int) length); + + if (written != (unsigned int) length) + warning (_(FILL_MEMORY_COMMAND ": only %u bytes written to target memory"), written); + } + else + error(_(FILL_MEMORY_COMMAND " is not available for this target")); +} + + +/* -------------------------------------------------------------------------- */ +/* externally visible functions */ +/* -------------------------------------------------------------------------- */ + +/* Perform arc-elf32-gdb specific initialization. */ + +struct gdbarch* +arc_elf32_initialize (struct gdbarch *gdbarch, + struct gdbarch_list *arches) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + + /* Set up a guard on the PC: if any attempt is made by gdb to read or write + the PC before an XML definition of the PC aux register has been read this + will cause an error message to be output. */ + arc_aux_pc_guard(gdbarch, TRUE); + + /* N.B. this function may be called twice: once when gdb is started, then again + when the 'target' command is issued; do not try to read the aux + registers definitions the first time (when 'arches' is NULL) as that + results in an error message (if the XML file is not found) which is + output too early in the start-up process (before gdb has identified + itself). */ + if (arches == NULL) + create_variant_info(tdep); + else + { + /* This is the arch that was created earlier. */ + struct gdbarch *gdbarch0 = arches[0].gdbarch; + struct gdbarch_tdep *tdep0 = gdbarch_tdep(gdbarch0); + + /* Have auxiliary registers been defined for that arch? */ + if (arc_aux_regs_defined(gdbarch0)) + { + /* Share the variant info. */ + tdep->processor_variant_info = tdep0->processor_variant_info; + } + else + create_variant_info(tdep); + } + + /* Fill in target-dependent info in ARC-private structure. */ + + tdep->is_sigtramp = NULL; + tdep->sigcontext_addr = NULL; + tdep->sc_reg_offset = NULL; + tdep->sc_num_regs = 0; + tdep->pc_regnum_in_sigcontext = 0; + + tdep->le_breakpoint_instruction = le_breakpoint_instruction; + tdep->be_breakpoint_instruction = be_breakpoint_instruction; + tdep->breakpoint_size = (unsigned int) sizeof(le_breakpoint_instruction); + + tdep->register_reggroup_p = register_reggroup_p; + tdep->lowest_pc = 0; + + /* Pass target-dependent info to gdb. */ + + DEBUG("setting PC %d\n", arc_aux_pc_number(gdbarch)); + DEBUG("setting #regs %d\n", ARC_NR_REGS); + DEBUG("setting #pseudo %d\n", ARC_NR_PSEUDO_REGS); + + /* ARC_NR_REGS and ARC_NR_PSEUDO_REGS are defined in the tm-embed.h configuration file. */ + set_gdbarch_pc_regnum (gdbarch, arc_aux_pc_number(gdbarch)); + set_gdbarch_num_regs (gdbarch, ARC_NR_REGS); + set_gdbarch_num_pseudo_regs (gdbarch, ARC_NR_PSEUDO_REGS); + set_gdbarch_print_registers_info (gdbarch, arc_elf32_print_registers_info); + set_gdbarch_register_name (gdbarch, arc_elf32_register_name); + set_gdbarch_cannot_store_register (gdbarch, arc_elf32_cannot_store_register); + set_gdbarch_dwarf2_reg_to_regnum (gdbarch, arc_elf32_binutils_reg_to_regnum); + + /* See ARC Bug #96650: disable the use of the 'P' and 'p' RSP packets, so + forcing gdb to use the 'G' and 'g' packets instead, in case the arc-elf32 + debug target is the xISS being used as a remote target. */ + { + static Boolean packets_disabled = FALSE; + + /* N.B. this is something of a kluge: if execution of the target program + is restarted (e.g. a 'start' command is followed by a 'run' + command) this function is called again - but if execute_command + is called again, an assertion fails way down in the regcache code + because the target appears to have a stack, but current_gdbarch + is set to NULL! So do this only once. */ + if (!packets_disabled) + { + execute_command("set remote set-register-packet off", 0); + execute_command("set remote fetch-register-packet off", 0); + + packets_disabled = TRUE; + } + } + + return gdbarch; +} + + +/* This is the module initialization function that is called from gdb. */ + +void +_initialize_arc_elf32_tdep (void) +{ + (void) add_cmd (BREAK_MEMORY_COMMAND, + class_breakpoint, + arc_elf32_break_memory_command, + _("Set a breakpoint on a memory address range.\n" + BREAK_MEMORY_COMMAND_USAGE + "<START> and <LENGTH> can be any expressions that evaluate to integers.\n"), + &cmdlist); + + (void) add_cmd (WATCH_MEMORY_COMMAND, + class_breakpoint, + arc_elf32_watch_memory_command, + _("Set a watchpoint on a memory address range.\n" + WATCH_MEMORY_COMMAND_USAGE + "<START> and <LENGTH> can be any expressions that evaluate to integers.\n" + "If the watchpoint mode is omitted, it defaults to 'access'.\n"), + &cmdlist); + + (void) add_cmd (FILL_MEMORY_COMMAND, + class_obscure, + arc_elf32_fill_memory_command, + _("Fill a memory address range with a repeated pattern.\n" + FILL_MEMORY_COMMAND_USAGE + "<START>, <LENGTH> and <PATTERN> can be any expressions that evaluate to integers.\n" + "If <PATTERN> is omitted, it defaults to 0.\n"), + &cmdlist); + + (void) observer_attach_new_objfile (new_object_file); + (void) observer_attach_target_pre_connect (pre_target_connection); + (void) observer_attach_target_post_connect (post_target_connection); + (void) observer_attach_target_post_disconnect(post_target_disconnection); + (void) observer_attach_target_updated (target_updated); +} + + +/* Find the ARC hardware numbers of the DEBUG, POC and STATUS32 aux registers: this + is the minimal set of registers required for controlling program execution on a + h/w target. */ + +void arc_elf32_find_register_numbers (void) +{ + arc_debug_regnum = arc_aux_find_register_number("DEBUG", ARC_HW_DEBUG_REGNUM); + arc_pc_regnum = arc_aux_find_register_number("PC", ARC_HW_PC_REGNUM); + arc_status32_regnum = arc_aux_find_register_number("STATUS32", ARC_HW_STATUS32_REGNUM); +} + + +/* Check that an XML definition of the PC aux register has been read: 'error' + is called if that is not the case. + + This function is simply a wrapper for a call to arc_aux_check_pc_defined; + there is a function of the same name in the arc-linux-tdep module (which does + nothing) so that it may be called from the arc-tdep module in either of the + builds of the ARC debugger (as the arc-registers module is present only in + the arc-elf32-gdb build). */ + +void +arc_check_pc_defined (struct gdbarch *gdbarch) +{ + arc_aux_check_pc_defined(gdbarch); +} + + +/* Load the program to be debugged to the tarrget. + + Parameters: + filename : the executable file + from_tty : non-zero if the command was issued at the command-line +*/ + +void +arc_elf32_load_program (char *filename, int from_tty) +{ + TargetOperations *operations = (TargetOperations*) current_target.to_data; + asection *bss_section; + + ENTERARGS("%s", filename); + + if (exec_bfd == NULL) + error(_("Must use 'file' command before 'load' command")); + + arc_aux_check_pc_defined(NULL); + + /* Now that we know the program file as well as the target (since we can be + loading only if the target is connected), check that the program has been + built for the processor version that is in the target. */ + ARCHITECTURE_CHECK(current_gdbarch, exec_bfd); + + if (filename == NULL || *filename == '\0') + filename = bfd_get_filename(exec_bfd); + + /* Check that the file has been compiled for the endianness of the target. */ + { + ARC_RegisterNumber memsubsys = arc_aux_find_register_number("MEMSUBSYS", ARC_HW_MEMSUBSYS_REGNUM); + ARC_RegisterContents contents; + + if (read_aux_register(¤t_target)(memsubsys, &contents, TRUE)) + { + Boolean big_endian_target = ((contents & 4) != 0); + + DEBUG("MEMSUBSYS BCR: 0x%08X\n", contents); + + if (big_endian_target && bfd_little_endian(exec_bfd)) + warning(_("target is big-endian but file %s is little-endian"), filename); + + if (!big_endian_target && bfd_big_endian(exec_bfd)) + warning(_("target is little-endian but file %s is big-endian"), filename); + } + } + + /* In case anything was previously loaded. */ + arc_set_IO_interception(operations, INTERCEPTION_RESET); + + /* Let gdb do all the real work of loading. */ + generic_load(filename, from_tty); + + /* Zero the BSS section in memory, if it exists. */ + bss_section = bfd_get_section_by_name (exec_bfd, ".bss"); + + if (bss_section) + { + CORE_ADDR bss_addr = bfd_section_lma (exec_bfd, bss_section); + bfd_size_type bss_size = bfd_get_section_size (bss_section); + unsigned int bytes; + + printf_filtered(_("Zeroing section .bss, size 0x%0x lma 0x%0x\n"), + (unsigned int) bss_size, (unsigned int) bss_addr); + + bytes = write_zeros((ARC_Address) bss_addr, (unsigned int) bss_size); + + if (bytes != (unsigned int) bss_size) + warning(_("load: error zeroing BSS section - only %u bytes zeroed"), bytes); + } + else + { + DEBUG("%s: no BSS section\n", __FUNCTION__); + } + + + /* We now have a program ready for execution on the target; inform the + program arguments module that we have a newly-loaded program (so any + information that it had about any program loaded before is now invalid). */ + arc_program_is_loaded = TRUE; + arc_program_loaded(); + + /* But the program has not yet been executed. */ + current_target.to_has_execution = 0; +} + + +/* Create the inferior, ready for execution on the target to start. + + Parameters: + exec_file : the executable file from which the loaded program was read + args : the arguments to be passed to the program in argc/argv + env : the environment (name/value pairs) for program execution + target_ops : the target operations structure for the target +*/ + +void +arc_elf32_create_inferior (char *exec_file, + char *args, + char **env, + struct target_ops *target_ops) +{ + TargetOperations *operations = (TargetOperations*) target_ops->to_data; + Boolean set_no_args = TRUE; + char *all_args = NULL; + CORE_ADDR start_address; + + ENTERARGS("exec_file = \"%s\", args = \"%s\"", exec_file, args); + + /* If no exec file handed to us, get it from the exec-file command - + with a good, common error message if none is specified. */ + if (exec_file == NULL) + exec_file = get_exec_file (1); + + /* Include the exec file name as arg[0]. */ + if (exec_file != NULL || args != NULL) + { + size_t length = 10; /* Safety margin. */ + + if (exec_file != NULL) + length += strlen(exec_file) + 1; + + if (args != NULL) + length += strlen(args) + 1; + + all_args = xmalloc(length); + + all_args[0] = '\0'; + + if (exec_file != NULL) + (void) strcat(all_args, exec_file); + + if (args != NULL) + { + (void) strcat(all_args, " "); + (void) strcat(all_args, args); + } + } + + /* Check that we do know which register is the PC. */ + arc_aux_check_pc_defined(NULL); + + if (!arc_program_is_loaded) + error(_("No program loaded")); + + /* We don't really have a PID or anything, but GDB uses this value to check + if the program is running. */ + inferior_ptid.pid = 42; + + /* Must set the PC to the program start address. */ + start_address = bfd_get_start_address (exec_bfd); + + DEBUG("setting PC to 0x%x\n", (unsigned int) start_address); + + write_pc (start_address); + + /* This sets the target's to_has_execution flag to 1. */ + target_mark_running(target_ops); + + /* Do we have arguments to pass to the program? */ + if (all_args != NULL) + { + if (arc_setup_arguments(all_args)) + set_no_args = FALSE; + else + warning(_("can not set up arguments to program")); + + xfree(all_args); + } + + /* If there are no arguments to be passed to the program, or we failed to + set them up, at least try to set R0 and R1 to indicate that are no + arguments! */ + if (set_no_args) + { + /* N.B. we do not use the target_store_registers operation here, as that + does not give us an indication of success or failure. */ + if (!operations->write_core_register(0, 0, TRUE)) + warning(_("can not set parameter register R0 to 0 - main parameter 'argc' will be undefined")); + + if (!operations->write_core_register(1, 0, TRUE)) + warning(_("can not set parameter register R1 to 0 - main parameter 'argv' will be undefined")); + } + + /* Set I/O interception on, so that any I/O operations performed by the program + when it is executed will be trapped and handled by the debugger. */ + arc_set_IO_interception(operations, INTERCEPTION_ON); +} + + +/* There are two ways in which the target processor may be executed: + + 1) once started, the processor will run asynchronously until an explicit + attempt to stop it is made (e.g. real h/w); + + 2) the processor may be run synchronously until it has executed a certain + 'chunk' of instructions (e.g. the xISS). + + So, this function gets either 1) a 'run' operation, or 2) 'start' and 'stop' + operations - but not all three of them! + + If an attempt is made (by the user typing Ctrl-C) to interrupt the processor + whilst it is running, in case 1) we must wait until the 'run' operation has + completed before checking the 'interrupt_processor' flag; whereas in case 2), + we can attempt to force the processor to stop. */ + +void +arc_elf32_execute (struct target_waitstatus *status, + ProcessorControlFunction run_processor, + ProcessorControlFunction start_processor, + ProcessorControlFunction stop_processor) +{ + TargetOperations *operations = (TargetOperations*) current_target.to_data; + + /* This flag will be set if the user types Ctrl-C. */ + interrupt_processor = FALSE; + + /* Set up a signal handler for Ctrl-C. */ + old_signal_handler = signal (SIGINT, interrupted_by_user); + + /* Wait until the processor has *really* halted. */ + while (TRUE) + { + /* Set to 0 in case we leave inner loop without reading DEBUG register. */ + ARC_RegisterContents debug = 0; + + /* Wait until the processor has *apparently* halted. */ + while (TRUE) + { + ARC_RegisterContents status32; + + /* Are we running the processor synchronously? */ + if (run_processor) + { + /* If the user has typed a Ctrl-C since the last chunk of + instructions were executed, exit from this inner loop. */ + if (interrupt_processor) + break; + + DEBUG("running processor...\n"); + + /* Otherwise, run the processor to execute another chunk. */ + run_processor(); + } + else + { + /* If the user has typed a Ctrl-C since target execution was + last started, try to force the processor to halt; it does not + matter if we do not succeed, as we will simply try again on + the next iteration of the loop. */ + if (interrupt_processor) + stop_processor(); + } + + /* Now try to read the STATUS32 register, and check whether its H + bit is set, indicating that the processor has really halted (as + opposed to having simply finished executing a chunk); again, it + does not matter if we do not succeed, as we will simply try again + on the next iteration of the loop. */ + if (operations->read_auxiliary_register(arc_status32_regnum, &status32, TRUE)) + { +#if 0 + ARC_RegisterContents PC; + + printf_filtered(_("STATUS32: %08X\n"), status32); + + if (operations->read_auxiliary_register(arc_pc_regnum, &PC, TRUE)) + printf_filtered(_("PC: %08X\n"), PC); +#endif + + if (status32 & STATUS32_HALT) + { + DEBUG("halted: STATUS32 = %08X\n", status32); + + /* Perform a polling wait for any delayed load to complete. + + N.B. this is necessary for real hardware, though the xISS + currently does not simulate pending loads, although + it will do so at some future date - however, it is + not an error to check the flag! */ + while (TRUE) + { + if (operations->read_auxiliary_register(arc_debug_regnum, &debug, TRUE)) + { + if (!(debug & DEBUG_LOAD_PENDING)) + break; + } + } + + break; + } + } + } + + /* The processor is now halted in a reliable state, but it might need to + be re-started... */ + if (report_processor_halt(status, debug, operations->read_core_register)) + break; + + DEBUG("*** resuming execution\n"); + + /* If we are running the processor asynchronously, we must explicitly + start it again - otherwise, we simply execute another chunk in the + next iteration of this loop. */ + if (start_processor) + start_processor(); + } + + /* Put the old signal handler back. */ + (void) signal (SIGINT, old_signal_handler); + + DEBUG("processor has halted\n"); +} + + +/* Close the connection to the target. */ + +void +arc_elf32_close (Boolean resume) +{ + TargetOperations *operations = (TargetOperations*) current_target.to_data; + + /* Do this while the target is halted. */ + arc_restore_stack_top_address(); + + /* We will no longer intercept any I/O operations. */ + arc_set_IO_interception(operations, INTERCEPTION_OFF); + + /* Let the target continue. */ + if (resume) + target_resume (inferior_ptid, 0, 0); + + current_target.to_has_execution = 0; + + arc_architecture_is_unknown(); +} + + +/* Fetch one or all registers from the target. + + Parameters: + regcache : cache to write register to + gdb_regno: register number (-1 means 'all registers') +*/ + +void +arc_elf32_fetch_registers (struct regcache *regcache, int gdb_regno) +{ + ENTERARGS("%d", gdb_regno); + + /* All registers. */ + if (gdb_regno == -1) + { + int num_core_registers = (int) arc_core_register_count(get_regcache_arch(regcache)); + + /* Core registers. */ + for (gdb_regno = 0; gdb_regno < num_core_registers; gdb_regno++) + debug_fetch_one_register(regcache, (ARC_RegisterNumber) gdb_regno, gdb_regno); + + /* Auxiliary registers (incl. build configuration registers). */ + arc_all_aux_registers(debug_fetch_reg, regcache); + } + else + { + ARC_RegisterNumber hw_regno = get_hw_regnum_mapping (gdb_regno); + + if (hw_regno == INVALID_REGISTER_NUMBER) + error(_("Invalid register number: %d"), gdb_regno); + else + debug_fetch_one_register(regcache, hw_regno, gdb_regno); + } + + LEAVEMSG; +} + + +/* Store one or all registers to the target. + + Parameters : + regcache : cache to read register from + gdb_regno: register number (-1 means 'all registers') +*/ + +void +arc_elf32_store_registers (struct regcache *regcache, int gdb_regno) +{ + ENTERARGS("%d", gdb_regno); + + /* All registers. */ + if (gdb_regno == -1) + { + int num_core_registers = (int) arc_core_register_count(get_regcache_arch(regcache)); + + /* Core registers. */ + for (gdb_regno = 0; gdb_regno < num_core_registers; gdb_regno++) + debug_store_one_register(regcache, (ARC_RegisterNumber) gdb_regno, gdb_regno); + + /* Auxiliary registers (excl. build configuration registers, which are not writable). */ + arc_all_aux_registers(debug_store_reg, regcache); + } + else + { + ARC_RegisterNumber hw_regno = get_hw_regnum_mapping (gdb_regno); + + if (hw_regno == INVALID_REGISTER_NUMBER) + error(_("Invalid register number: %d"), gdb_regno); + else + debug_store_one_register(regcache, hw_regno, gdb_regno); + } + + LEAVEMSG; +} + + +/* Read or write data to/from target memory. + + if 'object' is TARGET_OBJECT_MEMORY then + if 'writebuf' is NULL + read 'len' bytes of data from target memory starting at address 'offset' to 'readbuf' + else + write 'len' bytes of data from 'writebuf' to target memory starting at address 'offset' + + Returns number of bytes of memory read/written. + + Returns -1 for all other operations (i.e. object != TARGET_OBJECT_MEMORY). */ + +LONGEST +arc_elf32_xfer_partial (struct target_ops *ops, // unused + enum target_object object, + const char *annex, // unused + gdb_byte *readbuf, + const gdb_byte *writebuf, + ULONGEST offset, + LONGEST len) +{ + ENTERARGS("object %d offset 0x%x len %lld", (unsigned int) object, (unsigned int) offset, len); + + /* Handle memory access. */ + if (object == TARGET_OBJECT_MEMORY) + { + TargetOperations *operations = (TargetOperations*) current_target.to_data; + unsigned int xfered; + + /* No need to worry about the alignment of the address 'offset', or the number + of bytes to be transffered - the memory read/write operations handle that. */ + if (writebuf != NULL) + xfered = write_bytes((ARC_Address) offset, + (ARC_Byte*) writebuf, + (unsigned int) len); + else + xfered = read_bytes((ARC_Address) offset, + (ARC_Byte*) readbuf, + (unsigned int) len); + + DEBUG("...leaving %s(memory %s) with return value %d\n", + __FUNCTION__, (writebuf == NULL) ? "read" : "write", xfered); + + return (LONGEST) xfered; + } + + if (object == TARGET_OBJECT_AVAILABLE_FEATURES) + { + /* We should create and return an XML string here. */ + return -1; + } + + printf_filtered(_("\nRequested target_object %d not yet supported with target %s\n"), + (int) object, current_target.to_shortname); + return -1; +} + + +/* Insert a software breakpoint in the program code on the target. + + Parameters: + bpt: information defining the breakpoint. + + Returns: 0 for success, 1 for failure. */ + +int +arc_elf32_insert_breakpoint (struct bp_target_info *bpt) +{ + TargetOperations *operations = (TargetOperations*) current_target.to_data; + const unsigned char *breakpt_instruction; + unsigned int bytes; + + ENTERARGS("0x%08X", (unsigned int) bpt->placed_address); + + /* Get the breakpoint instruction code, and its size in bytes. */ + breakpt_instruction = gdbarch_breakpoint_from_pc (current_gdbarch, + &bpt->placed_address, + &bpt->placed_size); + + /* FIXME: alignment of breakpt_instruction data! */ + DEBUG("breakpoint size = %d and breakpoint instruction = 0x%x\n", + bpt->placed_size, *(unsigned int *) breakpt_instruction); + + /* Save the existing instruction at the given address as the shadow contents. */ + bytes = read_bytes((ARC_Address) bpt->placed_address, + (ARC_Byte*) bpt->shadow_contents, + (unsigned int) bpt->placed_size); + + if (bytes == (unsigned int) bpt->placed_size) + /* Overwrite the instruction with the breakpoint instruction. */ + bytes = write_bytes((ARC_Address) bpt->placed_address, + (ARC_Byte*) breakpt_instruction, + (unsigned int) bpt->placed_size); + + return (bytes == (unsigned int) bpt->placed_size) ? 0 : 1; +} + + +/* Remove a software breakpoint from the program code on the target. + + Parameters: + bpt: information defining breakpoint. + + Returns: 0 for success, 1 for failure. */ + +int +arc_elf32_remove_breakpoint (struct bp_target_info *bpt) +{ + unsigned int bytes; + + /* FIXME: alignment of shadow_contents data! */ + ENTERARGS("0x%08X, 0x%lx", (unsigned int) bpt->placed_address, + *(unsigned long *) bpt->shadow_contents); + + /* Write the old code back. */ + bytes = write_bytes((ARC_Address) bpt->placed_address, + (ARC_Byte*) bpt->shadow_contents, + (unsigned int) bpt->placed_size); + + return (bytes == (unsigned int) bpt->placed_size) ? 0 : 1; +} + + +/* Output a warning message related to a core register. */ + +void +arc_elf32_core_warning (RegisterError error, + ARC_RegisterNumber hw_regno) +{ + int gdb_regno = arc_core_register_gdb_number(hw_regno); + + /* N.B. the supplied format string contains a %s specifier which + allows the string "extension " to be inserted into the message. */ + warning((error == REGISTER_IS_READ_ONLY) ? _("%score register %s is read-only") : + (error == ERROR_ON_READING_REGISTER) ? _("failure reading %score register %s") : + _("failure writing %score register %s"), + IS_EXTENSION_CORE_REGISTER(hw_regno) ? _("extension ") : _(""), + gdbarch_register_name(current_gdbarch, gdb_regno)); +} + + +/* Output a warning message related to an auxiliary register. */ + +void +arc_elf32_aux_warning (RegisterError error, + ARC_RegisterNumber hw_regno) +{ + warning((error == REGISTER_IS_READ_ONLY) ? _("auxiliary register %s (0x%x) is read-only") : + (error == ERROR_ON_READING_REGISTER) ? _("failure reading auxiliary register %s (0x%x)") : + _("failure writing auxiliary register %s (0x%x)"), + arc_aux_register_name_of(hw_regno), hw_regno); +} + +/******************************************************************************/ diff --git a/gdb/arc-elf32-tdep.h b/gdb/arc-elf32-tdep.h new file mode 100644 index 00000000000..eb7d3e9497b --- /dev/null +++ b/gdb/arc-elf32-tdep.h @@ -0,0 +1,118 @@ +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. + + Copyright 2008, 2009 Free Software Foundation, Inc. + + Contributed by ARC International (www.arc.com) + + Authors: + Richard Stuckey <richard.stuckey@arc.com> + + 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 3 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, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This header file defines an initialization function for the arc_elf32 */ +/* architecture, and various operations which are useful for the various */ +/* arc-elf32 targets supported. */ +/* */ +/******************************************************************************/ + +#ifndef ARC_ELF32_TDEP_H +#define ARC_ELF32_TDEP_H + +/* gdb header files */ +#include "defs.h" +#include "gdbarch.h" +#include "target.h" +#include "breakpoint.h" + +/* ARC header files */ +#include "arc-registers.h" +#include "arc-architecture.h" + + +/* Complete the structure definition here. */ +struct arc_variant_info +{ + ARC_ProcessorVersion processor_version; + ARC_RegisterInfo registers; +}; + + +typedef enum +{ + REGISTER_IS_READ_ONLY, + ERROR_ON_READING_REGISTER, + ERROR_ON_WRITING_REGISTER +} RegisterError; + + +typedef void (*ProcessorControlFunction)(void); + + +struct gdbarch* arc_elf32_initialize (struct gdbarch *gdbarch, + struct gdbarch_list *arches); + +void arc_check_pc_defined (struct gdbarch *gdbarch); + +void arc_elf32_find_register_numbers (void); + +void arc_elf32_load_program (char *filename, int from_tty); + +void arc_elf32_create_inferior (char *exec_file, + char *args, + char **env, + struct target_ops *target_ops); + +void arc_elf32_execute (struct target_waitstatus *status, + ProcessorControlFunction run_processor, + ProcessorControlFunction start_processor, + ProcessorControlFunction stop_processor); + +void arc_elf32_close (Boolean resume); + +void arc_elf32_fetch_registers (struct regcache *regcache, int gdb_regno); +void arc_elf32_store_registers (struct regcache *regcache, int gdb_regno); + +int arc_elf32_insert_breakpoint (struct bp_target_info *bpt); +int arc_elf32_remove_breakpoint (struct bp_target_info *bpt); + +LONGEST arc_elf32_xfer_partial (struct target_ops *ops, + enum target_object object, + const char *annex, + gdb_byte *readbuf, + const gdb_byte *writebuf, + ULONGEST offset, + LONGEST len); + +void arc_elf32_core_warning (RegisterError error, + ARC_RegisterNumber hw_regno); + +void arc_elf32_aux_warning (RegisterError error, + ARC_RegisterNumber hw_regno); + + +extern ARC_RegisterNumber arc_debug_regnum; +extern ARC_RegisterNumber arc_pc_regnum; +extern ARC_RegisterNumber arc_status32_regnum; + +extern Boolean arc_program_is_loaded; +extern Boolean arc_target_is_connected; + + +#endif /* ARC_ELF32_TDEP_H */ +/******************************************************************************/ diff --git a/gdb/arc-gpio.c b/gdb/arc-gpio.c new file mode 100644 index 00000000000..cc9f7753d31 --- /dev/null +++ b/gdb/arc-gpio.c @@ -0,0 +1,251 @@ +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. + + Copyright 2005, 2008, 2009 Free Software Foundation, Inc. + + Contributed by ARC International (www.arc.com) + + Authors: + Richard Stuckey <richard.stuckey@arc.com> + A.N. Other <unknown> + + 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 3 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, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This module implements operations for reading data from / writing data */ +/* to a parallel port using a GPIO (General Purpose Input/Output) driver. */ +/* */ +/******************************************************************************/ + +/* system header files */ +#include <string.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <errno.h> + +/* gdb header files */ +#include "defs.h" + +/* ARC header files */ +#include "arc-gpio.h" + + +/* -------------------------------------------------------------------------- */ +/* local data */ +/* -------------------------------------------------------------------------- */ + +// I/O control numbers. + +// Linux kernel uses 0x54XX for special purposes. Avoid such. +// We'll pick large numbers. +// We don't use the linux convention of dividing the IOC into a bunch +// of bit fields. We can always switch to this later, as the +// IOC 0 returns the driver version (which IOC value will never change). + +#define GPIO_IOC_VERSION 0 // returns version +#define GPIO_IOC_BASE 0xaa3a0000UL // Intended for use on ARCangel 3! + + +// Switch base address of parallel port. 0x3f8 is assumed. +// WARNING! You can write on any port whatsoever with this driver. +// BE CAREFUL! +#define GPIO_IOC_SET_PORT_BASE (GPIO_IOC_BASE + 1) // cmd, arg=port base + +// General input/output ioctl. See GPIO_ioctl struct. +#define GPIO_IOC_DO_IO (GPIO_IOC_BASE + 2) // cmd, arg=GPIO_ioctl * + +// For emergency purposes in case the driver is goofed up. +#define GPIO_IOC_HARD_RESET (GPIO_IOC_BASE + 3) // cmd, no arg + +// Do you have an antiquated parallel port? You might need to ask +// the driver to use outb_p and inb_p (_p = pause). Default is not to. +#define GPIO_IOC_SET_PAUSE (GPIO_IOC_BASE + 4) // arg = 1 => use pause; otherwise not. + + +/* parallel port I/O addresses (LPT1) */ +#define PORT_BASE_ADDRESS 0x378 +#define DATA_PORT_ADDRESS (PORT_BASE_ADDRESS + DATA_PORT) +#define STATUS_PORT_ADDRESS (PORT_BASE_ADDRESS + STATUS_PORT) +#define CONTROL_PORT_ADDRESS (PORT_BASE_ADDRESS + CONTROL_PORT) + +#define NULL_FILE_DESCRIPTOR (-1) + + +typedef struct +{ + // This is used for general input and output in the same ioctl. + // N.B. "input" is input TO the port, and "output" is output FROM the port. + // + // inlen is always even and represents a number of pairs: + // + // [0|1|2, value] : write value to port_base+0|1|2. + // [0x80|0x81|0x82, <ignored>] : read value from port_base+0|1|2 + // and append result to outbuf. + // + // Thus one can intermix read and write in the same ioctl. + + // inlen is replaced by the number of input bytes consumed. + unsigned inlen; + char *inbuf; + + // outlen is replaced by the number of output bytes written. + unsigned outlen; + char *outbuf; +} GPIO_ioctl; + + +/* Buffers to hold data read from / written to ports; we generally read/write + only 1 byte of data at a time, so the buffers need hold only 1 byte pair each. */ +static char input_buffer [2]; +static char output_buffer[2]; +static GPIO_ioctl ioctl_data = { 0, input_buffer, 0, output_buffer }; + +/* A file descriptor for the GPIO driver. */ +static int gpio_driver = NULL_FILE_DESCRIPTOR; + +/* Debug data. */ +// static const char name[] = {'D', 'S', 'C'}; + + +/* -------------------------------------------------------------------------- */ +/* externally visible data */ +/* -------------------------------------------------------------------------- */ + +/* This is set to TRUE if an I/O error occurs in accessing the port. */ +Boolean gpio_port_error; + + +/* -------------------------------------------------------------------------- */ +/* externally visible functions */ +/* -------------------------------------------------------------------------- */ + +/* Initialization of the GPIO interface. */ + +Boolean +gpio_open (void) +{ + /* Open the driver, if not already open. */ + if (gpio_driver == NULL_FILE_DESCRIPTOR) + { + gpio_driver = open(GPIO_DEVICE, O_RDWR); + + if ((gpio_driver == NULL_FILE_DESCRIPTOR) || + ioctl(gpio_driver, GPIO_IOC_HARD_RESET) || + ioctl(gpio_driver, GPIO_IOC_SET_PORT_BASE, PORT_BASE_ADDRESS)) + { + warning(_("unable to open JTAG port (device " GPIO_DEVICE "): %s"), + strerror(errno)); + return FALSE; + } + + gpio_port_error = FALSE; + } + + return TRUE; +} + + +/* Close the GPIO interface. */ + +void +gpio_close (void) +{ + /* Close the driver, if not already closed. */ + if (gpio_driver != NULL_FILE_DESCRIPTOR) + { + /* Close file descriptor opened for communication with gpio driver. */ + if (close(gpio_driver) == -1) + warning(_("unable to close JTAG port (device " GPIO_DEVICE "): %s"), + strerror(errno)); + + gpio_driver = NULL_FILE_DESCRIPTOR; + } +} + + +/* Write a byte of data to the given port. */ + +void +gpio_write (ParallelPort port, Byte data) +{ + ioctl_data.inlen = 2; + ioctl_data.inbuf[0] = (char) port; + ioctl_data.inbuf[1] = (char) data; + + if (ioctl(gpio_driver, GPIO_IOC_DO_IO, &ioctl_data)) + error(_("Failure writing to port %d: %s"), port, strerror(errno)); + + /* If no data has been consumed by the port. */ + if (ioctl_data.inlen == 0) + gpio_port_error = TRUE; + +// DEBUG("ioctl_data.inlen: %d\n", ioctl_data.inlen); +// DEBUG("GPIO write: %02x -> %c\n", data, name[port]); +} + + +/* Read a byte of data from the given port. */ + +Byte +gpio_read (ParallelPort port) +{ + ioctl_data.inlen = 2; + ioctl_data.inbuf[0] = (char) (port + 0x80); +// ioctl_data.inbuf[1] is ignored + + /* N.B. outlen must be set! */ + ioctl_data.outlen = 1; + ioctl_data.outbuf[0] = (char) 0; // in case the read fails + + if (ioctl(gpio_driver, GPIO_IOC_DO_IO, &ioctl_data)) + error(_("Failure reading from port %d: %s"), port, strerror(errno)); + + /* If no data has been provided by the port */ + if (ioctl_data.outlen == 0) + gpio_port_error = TRUE; + +// DEBUG("ioctl_data.outlen: %d\n", ioctl_data.outlen); +// DEBUG("GPIO read : %02x <- %c\n", (Byte) ioctl_data.outbuf[0], name[port]); + + return (Byte) ioctl_data.outbuf[0]; +} + + +/* Write a series of bytes of data to the ports. */ + +void +gpio_write_array (GPIO_Pair array[], unsigned int num_elements) +{ + char buffer[num_elements * 2]; + GPIO_ioctl data = { 0, buffer, 0, NULL }; + unsigned int i; + + for (i = 0; i < num_elements; i++) + { + buffer[data.inlen++] = (char) array[i].port; + buffer[data.inlen++] = (char) array[i].data; + } + + if (ioctl(gpio_driver, GPIO_IOC_DO_IO, &data)) + error(_("Failure writing to port: %s"), strerror(errno)); + + /* If no data has been consumed by the port. */ + if (ioctl_data.inlen == 0) + gpio_port_error = TRUE; +} + +/******************************************************************************/ diff --git a/gdb/arc-gpio.h b/gdb/arc-gpio.h new file mode 100755 index 00000000000..914138c75f2 --- /dev/null +++ b/gdb/arc-gpio.h @@ -0,0 +1,72 @@ +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. + + Copyright 2008, 2009 Free Software Foundation, Inc. + + Contributed by ARC International (www.arc.com) + + Authors: + Richard Stuckey <richard.stuckey@arc.com> + + 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 3 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, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This header file defines operations for reading data from / writing */ +/* data to a parallel port using a GPIO (General Purpose Input/Output) */ +/* driver. */ +/* */ +/******************************************************************************/ + +#ifndef ARC_GPIO_H +#define ARC_GPIO_H + +/* ARC header files */ +#include "arc-support.h" + + +#define GPIO_DEVICE "/dev/gpio" + + +typedef enum +{ + DATA_PORT = 0, + STATUS_PORT = 1, + CONTROL_PORT = 2 +} ParallelPort; + +typedef unsigned char Byte; + +typedef struct +{ + ParallelPort port; + Byte data; +} GPIO_Pair; + + +Boolean gpio_open (void); +void gpio_close (void); + +void gpio_write (ParallelPort port, Byte data); +Byte gpio_read (ParallelPort port); + +void gpio_write_array (GPIO_Pair array[], unsigned int num_elements); + + +extern Boolean gpio_port_error; + +#endif /* ARC_GPIO_H */ +/******************************************************************************/ diff --git a/gdb/arc-inst-tracing.c b/gdb/arc-inst-tracing.c new file mode 100644 index 00000000000..c22ddb732ec --- /dev/null +++ b/gdb/arc-inst-tracing.c @@ -0,0 +1,356 @@ +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. + + Copyright 2009 Free Software Foundation, Inc. + + Contributed by ARC International (www.arc.com) + + Authors: + Richard Stuckey <richard.stuckey@arc.com> + + 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 3 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, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This module implements operations for recording an instruction trace */ +/* of successive PC values in a compressed binary format in a file. */ +/* */ +/* The file is regarded as a linear sequence of bits, of the form: */ +/* */ +/* <64-bit first instruction count> */ +/* <64-bit last instruction count> */ +/* { <3-bit-code> [ <16-bit-data> | <31-bit-data> ] } */ +/* [ <zero-pad-bits> ] */ +/* */ +/******************************************************************************/ + +/* system header files */ +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <errno.h> + +/* gdb header files */ +#include "defs.h" + +/* ARC header files */ +#include "arc-inst-tracing.h" +#include "arc-tdep.h" + + +/* -------------------------------------------------------------------------- */ +/* local data */ +/* -------------------------------------------------------------------------- */ + +#define MAX_ORDINAL 0xFFFFFFFFFFFFFFFFULL + +/* The file that is currently open for read or write. */ +static FILE *file; + +/* The byte of data that has been read from, or is about to be written to, the + currently open file; and the number of bits in that byte that have been used + (i.e. been consumed on input, or produced on output). */ +static unsigned char byte; +static unsigned int bits_used; + +/* Set to TRUE if EOF occurs whilst trying to read from the file. */ +static Boolean EOF_detected; + + +/* -------------------------------------------------------------------------- */ +/* local functions */ +/* -------------------------------------------------------------------------- */ + +/* Write the N least-significant bits of the given value to the file. */ + +static void +output_bits (unsigned int N, unsigned int value) +{ + unsigned int i; + + for (i = 0; i < N; i++) + { + if (bits_used == BITS_IN_BYTE) + { + (void) putc((int) byte, file); + byte = 0; + bits_used = 0; +// printf("byte = %02X\n", byte); + } + + byte <<= 1; + byte |= (value >> (N - i - 1)) & 0x1; + bits_used++; + } +} + + +/* Read N bits from the file and return them as the least-significant bits of a value. */ + +static unsigned int +input_bits (unsigned int N) +{ + unsigned int value = 0; + unsigned int i; + + for (i = 0; i < N; i++) + { + unsigned int bit; + + /* If all the bits in the current input byte have been used. */ + if (bits_used == BITS_IN_BYTE) + { + /* Read the next byte from the file. */ + int input = getc(file); + + if (input == EOF) + { + EOF_detected = TRUE; + return 0; + } + + byte = (unsigned char) input; + bits_used = 0; +// printf("byte = %02X\n", byte); + } + + bit = (unsigned int) ((byte & 0x80) >> 7); + value <<= 1; + value |= bit; + byte <<= 1; + bits_used++; + } + + return value; +} + + +/* Write a 64-bit value to the file. */ + +static void +output_ordinal (Ordinal value) +{ + output_bits(32, (unsigned int) (value >> 32)); + output_bits(32, (unsigned int) (value & 0xFFFFFFFF)); +} + + +/* Read a 64-bit value from the file. */ + +static Ordinal +input_ordinal (void) +{ + unsigned int high = input_bits(32); + unsigned int low = input_bits(32); + + return ((Ordinal) high) << 32 | (Ordinal) low; +} + + +/* Open the named file with the given access mode. */ + +static Boolean +open_file (const char *filename, const char *mode) +{ + file = fopen(filename, mode); + + if (file == NULL) + { + fprintf(stderr, "Can not open file %s: %s\n", filename, strerror(errno)); + return FALSE; + } + + return TRUE; +} + + +/* -------------------------------------------------------------------------- */ +/* externally visible functions */ +/* -------------------------------------------------------------------------- */ + +/* Start encoding trace data into the named file. */ + +Boolean +arc_start_encoding (const char *filename, + Ordinal first_instr_count) +{ + ENTERARGS("filename = %s, first = %llu", filename, first_instr_count); + + byte = 0; + bits_used = 0; + + if (open_file(filename, "wb")) + { + /* The second value will be fixed up later. */ + output_ordinal(first_instr_count); + output_ordinal(MAX_ORDINAL); + return TRUE; + } + + return FALSE; +} + + +/* Stop encoding trace data into the file. */ + +void +arc_stop_encoding (Ordinal last_instr_count) +{ + ENTERARGS("last = %llu", last_instr_count); + + if (file) + { + /* Make sure the last partial byte output is flushed to the file, + padded with 0 bits as necessary. */ + if (bits_used > 0) + output_bits(BITS_IN_BYTE, 0); + + /* N.B. this is necessary! */ + (void) fflush(file); + + /* Now fix up the second 8-byte value in tjhe file. */ + + if (lseek(fileno(file), (off_t) sizeof(Ordinal), SEEK_SET) == -1) + warning(_("can not seek in file: %s"), strerror(errno)); + else + { + bits_used = 0; + byte = 0; + output_ordinal(last_instr_count); + + /* Make sure the last byte is flushed. */ + output_bits(1, 0); + } + + (void) fclose(file); + file = NULL; + } +} + + +/* Start decoding trace data from the named file. + Retrieve the first and last instruction ordinal positions. */ + +Boolean +arc_start_decoding (const char *filename, + Ordinal *first_instr_count, + Ordinal *last_instr_count) +{ + ENTERARGS("filename = %s", filename); + + /* The first attempt to input a bit will result in a read from the file. */ + bits_used = BITS_IN_BYTE; + EOF_detected = FALSE; + + if (open_file(filename, "r")) + { + *first_instr_count = input_ordinal(); + *last_instr_count = input_ordinal(); + + DEBUG("first = %llu, last = %llu\n", *first_instr_count, *last_instr_count); + + return !EOF_detected; + } + + return FALSE; +} + + +/* Stop decoding trace data from the file. */ + +void +arc_stop_decoding (void) +{ + if (file) + { + if (bits_used < BITS_IN_BYTE) + fprintf(stderr, "all data not processed!\n"); + + (void) fclose(file); + file = NULL; + } +} + + +/* Write a PC value into the file with the specified encoding. */ + +void +arc_encode_PC (ARC_ProgramCounterEncoding encoding, unsigned int value) +{ +// printf("%d:%x\n", encoding, value); + + output_bits(3, (unsigned int) encoding); + + switch (encoding) + { + case NO_CHANGE: + case PLUS_16_BITS: + case PLUS_32_BITS: + case PLUS_48_BITS: + case PLUS_64_BITS: + break; + case DELTA_16_BIT_POSITIVE: + case DELTA_16_BIT_NEGATIVE: + output_bits(16, value); + break; + case ABSOLUTE_31_BITS: + output_bits(31, value); + break; + } +} + + +/* Read a PC value and its encoding from the file. + Return TRUE if a value could be read, FALSE otherwise. */ + +Boolean +arc_decode_PC (ARC_ProgramCounterEncoding *encoding, unsigned int *value) +{ + unsigned int code = input_bits(3); + + if (EOF_detected) + return FALSE; + + *encoding = (ARC_ProgramCounterEncoding) code; + + switch (*encoding) + { + case NO_CHANGE: + case PLUS_16_BITS: + case PLUS_32_BITS: + case PLUS_48_BITS: + case PLUS_64_BITS: + *value = 0; + break; + case DELTA_16_BIT_POSITIVE: + case DELTA_16_BIT_NEGATIVE: + *value = input_bits(16); + break; + case ABSOLUTE_31_BITS: + *value = input_bits(31); + break; + } + + if (EOF_detected) + return FALSE; + +// printf("%d:%x\n", *encoding, *value); + + return TRUE; +} + +/******************************************************************************/ diff --git a/gdb/arc-inst-tracing.h b/gdb/arc-inst-tracing.h new file mode 100644 index 00000000000..4dacad9f985 --- /dev/null +++ b/gdb/arc-inst-tracing.h @@ -0,0 +1,89 @@ +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. + + Copyright 2009 Free Software Foundation, Inc. + + Contributed by ARC International (www.arc.com) + + Authors: + Richard Stuckey <richard.stuckey@arc.com> + + 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 3 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, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This header file defines operations for recording an instruction trace */ +/* of successive PC values in a compressed binary format in a file. */ +/* */ +/* We use the fact that instructions are always 16-bit aligned to encode */ +/* 17-bit deltas as 16-bit quantities. We apply a 2-byte bias to the */ +/* delta (as the minimum delta could be -2, for a shortest distance */ +/* backward branch), so giving us */ +/* */ +/* delta range: 2 .. 2^17 */ +/* encoding range: 0 .. 2^16 - 1 */ +/* */ +/* We could encode positive and negative deltas differently (since the */ +/* minimum positive delta is 10, given that 2, 4, 6 and 8 byte positive */ +/* deltas are handled diferently), but this hardly seems worthwhile. */ +/* */ +/******************************************************************************/ + +#ifndef ARC_INSTRUCTION_TRACING_H +#define ARC_INSTRUCTION_TRACING_H + +/* ARC header files */ +#include "arc-support.h" + + +typedef enum +{ + NO_CHANGE, + PLUS_16_BITS, + PLUS_32_BITS, + PLUS_48_BITS, + PLUS_64_BITS, + DELTA_16_BIT_POSITIVE, + DELTA_16_BIT_NEGATIVE, + ABSOLUTE_31_BITS +} ARC_ProgramCounterEncoding; + + +typedef unsigned long long Ordinal; + + +#define MAX_DELTA (1 << 17) + +#define ENCODE_DELTA(delta) (unsigned int) (((delta) - 2) / 2) +#define DECODE_DELTA(value) (((value) * 2) + 2) + + +Boolean arc_start_encoding (const char *filename, + Ordinal first_instr_count); + +void arc_stop_encoding (Ordinal last_instr_count); + +Boolean arc_start_decoding (const char *filename, + Ordinal *first_instr_count, + Ordinal *last_instr_count); + +void arc_stop_decoding (void); + +void arc_encode_PC (ARC_ProgramCounterEncoding encoding, unsigned int value); +Boolean arc_decode_PC (ARC_ProgramCounterEncoding *encoding, unsigned int *value); + +#endif /* ARC_INSTRUCTION_TRACING_H */ +/******************************************************************************/ diff --git a/gdb/arc-jtag-actionpoints.c b/gdb/arc-jtag-actionpoints.c new file mode 100644 index 00000000000..4c5d81826db --- /dev/null +++ b/gdb/arc-jtag-actionpoints.c @@ -0,0 +1,1337 @@ +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. + + Copyright 2008, 2009 Free Software Foundation, Inc. + + Contributed by ARC International (www.arc.com) + + Author: + Richard Stuckey <richard.stuckey@arc.com> + + 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 3 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, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This module implements hardware breakpoints and watchpoints on the ARC */ +/* processor using actionpoints. */ +/* */ +/* See */ +/* ARC 700 System Components Reference */ +/* 5126-016 */ +/* */ +/* for a full description of the processor actionpoints mechanism. */ +/* */ +/******************************************************************************/ + +/* gdb header files */ +#include "defs.h" +#include "inferior.h" +#include "gdb_assert.h" + +/* ARC header files */ +#include "arc-jtag-actionpoints.h" +#include "arc-registers.h" +#include "arc-elf32-tdep.h" +#include "arc-jtag.h" +#include "arc-jtag-ops.h" +#include "arc-support.h" + + +/* -------------------------------------------------------------------------- */ +/* local types */ +/* -------------------------------------------------------------------------- */ + +typedef enum +{ + NOT_IN_USE, + SINGLE, + PAIR_0, + PAIR_1, + QUAD_0, + QUAD_1, + QUAD_2, + QUAD_3 +} Actionpoint_Usage; + + +/* This information describes an individual actionpoint. */ +typedef struct arc_actionpoint +{ + Actionpoint_Usage usage; + Boolean triggered; + Boolean is_exclude; + unsigned int length; + ARC_RegisterContents match_value; + ARC_RegisterContents match_mask; + ARC_RegisterContents control; + ARC_RegisterContents point; +} ARC_ActionPoint; + + +/* -------------------------------------------------------------------------- */ +/* local data */ +/* -------------------------------------------------------------------------- */ + +#define SUCCESS 0 +#define FAILURE (-1) + +#define MAX_ACTION_POINTS 8 +#define MAX_ACTION_POINTS_IN_GROUP 4 +#define HW_BP_SIZE 1 + + +/* Bit masks for the Actionpoint Control Registers AP_ACx (10 low-order bits only). */ +#define AP_TARGET_INSTRUCTION_ADDRESS 0x000 +#define AP_TARGET_INSTRUCTION_DATA 0x001 +#define AP_TARGET_LOAD_STORE_ADDRESS 0x002 +#define AP_TARGET_LOAD_STORE_DATA 0x003 +#define AP_TARGET_AUX_REGISTER_ADDRESS 0x004 +#define AP_TARGET_AUX_REGISTER_DATA 0x005 +#define AP_TARGET_EXTERNAL_PARAMETER_0 0x006 +#define AP_TARGET_EXTERNAL_PARAMETER_1 0x007 +#define AP_TARGET_MASK 0x007 +#define AP_TARGET_SHIFT 0 + +#define AP_TRANSACTION_TYPE_DISABLED 0x000 +#define AP_TRANSACTION_TYPE_WRITE 0x010 +#define AP_TRANSACTION_TYPE_READ 0x020 +#define AP_TRANSACTION_TYPE_ACCESS 0x030 +#define AP_TRANSACTION_TYPE_MASK 0x030 +#define AP_TRANSACTION_TYPE_SHIFT 4 + +#define AP_MODE_TRIGGER_IN_RANGE 0x000 +#define AP_MODE_TRIGGER_OUTSIDE_RANGE 0x040 +#define AP_MODE_MASK 0x040 + +#define AP_ACTION_BREAK 0x000 +#define AP_ACTION_EXCEPTION 0x100 +#define AP_ACTION_MASK 0x100 + +#define AP_PAIR 0x080 +#define AP_QUAD 0x200 + + +/* Data defining the actionpoints. */ +static unsigned int num_actionpoints; +static Boolean full_target_actionpoints; +static ARC_ActionPoint actionpoints[MAX_ACTION_POINTS]; + +/* The h/w numbers of the AMV0, AMM0 and AC0 auxiliary registers. */ +static ARC_RegisterNumber amv0_regnum; +static ARC_RegisterNumber amm0_regnum; +static ARC_RegisterNumber ac0_regnum; + + +/* -------------------------------------------------------------------------- */ +/* local macros */ +/* -------------------------------------------------------------------------- */ + +/* The N actionpoint auxiliary registers (where N is 0 .. MAX_ACTION_POINTS - 1) + (fortunately, the numbers of the registers are one contiguous block, so + a simple addition is sufficient here). */ +#define ARC_HW_AMV_REGNUM(n) (ARC_RegisterNumber) (amv0_regnum + 3 * (n)) +#define ARC_HW_AMM_REGNUM(n) (ARC_RegisterNumber) (amm0_regnum + 3 * (n)) +#define ARC_HW_AC_REGNUM(n) (ARC_RegisterNumber) (ac0_regnum + 3 * (n)) + +/* This will give a value in range 0 .. MAX_ACTION_POINTS - 1. */ +#define AP_INSTANCE(ap) ((ap) - actionpoints) +#define IN_USE(ap) ((ap)->usage != NOT_IN_USE) + + +/* -------------------------------------------------------------------------- */ +/* local functions */ +/* -------------------------------------------------------------------------- */ + +#if 0 +/* This function checks that a given number is a power of two, and, if so, + returns a bitmask corresponding to (2 ** number - 1). */ + +static Boolean +is_power_of_two (int number, ARC_Word *mask) +{ + unsigned int power = 1; + unsigned int i; + + *mask = 0; + + for (i = 0; (i < BITS_IN_WORD) && (power <= (unsigned int) number); i++) + { + if (power == (unsigned int) number) + return TRUE; + + power <<= 1; + *mask = (*mask << 1) + 1; + } + + /* not a power */ + return FALSE; +} +#endif + + +/* This function determines whether the ARC processor in the connected target + provides support for actionpoints (that is a configuratiopn option). */ + +static Boolean +target_has_actionpoints (void) +{ + ARC_RegisterNumber ap_build_regnum; + ARC_RegisterContents ap_build; + + ap_build_regnum = arc_aux_find_register_number("AP_BUILD", ARC_HW_AP_BUILD_REGNUM); + amv0_regnum = arc_aux_find_register_number("AMV0", ARC_HW_AMV0_REGNUM); + amm0_regnum = arc_aux_find_register_number("AMM0", ARC_HW_AMM0_REGNUM); + ac0_regnum = arc_aux_find_register_number("AC0", ARC_HW_AC0_REGNUM); + + num_actionpoints = 0; + + if (arc_read_jtag_aux_register(ap_build_regnum, &ap_build, TRUE)) + { + DEBUG("AP_BUILD BCR: 0x%08X\n", ap_build); + + /* AP_BUILD returns 0 if actionpoints are not selected in the + processor configuration. */ + if (ap_build != 0) + { + /* If the processor's implementation of the actionpoint mechanism is + the one we know about. */ + if ((ap_build & AP_BUILD_VERSION) == 0x4) + { + unsigned int type = (ap_build & AP_BUILD_TYPE) >> AP_BUILD_TYPE_SHIFT; + unsigned int i; + + /* The low-order two bits of the type field encode the number + of actionpoints supported by the processor. */ + switch (type % 4) + { + case 0 : num_actionpoints = 2; break; + case 1 : num_actionpoints = 4; break; + case 2 : num_actionpoints = 8; break; + default: internal_error (__FILE__, __LINE__, _("invalid AP_BUILD.TYPE: 0x%X"), type); + } + + /* The next bit specifies whether the processor supports full + or minimum targets for the actionpoints. */ + full_target_actionpoints = ((type & 4) == 0); + + for (i = 0; i < MAX_ACTION_POINTS; i++) + { + actionpoints[i].usage = NOT_IN_USE; + actionpoints[i].triggered = FALSE; + actionpoints[i].is_exclude = FALSE; + } + + DEBUG("ARC processor supports %u %s actionpoints\n", + num_actionpoints, (full_target_actionpoints) ? "full" : "minimum"); + + return TRUE; + } + else + warning(_("ARC processor actionpoint mechanism version (0x%x) is not supported."), + ap_build & AP_BUILD_VERSION); + } + } + + DEBUG("ARC processor supports no actionpoints\n"); + + return FALSE; +} + + +/* This function determines the set of actionpoints that would be required to + cover exactly the memory region specified by (addr,length), by using one + actionpoint with an inclusive range, and zero or more actionpoints with + exclusive ranges. + + The set of values and masks for the actionpoint AMV and AMM registers are + returned in the actionpoint_value and actionpoint_mask arrays - these must + be able to hold 4 entries. The value and mask for the inclusive actionpoint + are returned as the first elements in the arrays. */ + +static unsigned int +map_actionpoints (ARC_Address addr, + unsigned int length, + ARC_Address actionpoint_value[], + ARC_Word actionpoint_mask[]) +{ + ARC_Address first_data = addr; + ARC_Address last_data = first_data + length - 1; + ARC_Address include_start; + ARC_Address include_end; + ARC_Word mask; + unsigned int power_of_two; + unsigned int points; + + ENTERARGS("addr 0x%08X, length %d", addr, length); +// DEBUG("range: %08X .. %08X\n", first_data, last_data); + + /* If the range extends across the midpoint of the address space. */ + if (((first_data & 0x80000000) == 0) && ((last_data & 0x80000000) != 0)) + { +// DEBUG("pathological case!\n"); + + /* Must cover entire address space. */ + include_start = 0x00000000; + include_end = 0xFFFFFFFF; + mask = 0xFFFFFFFF; // Ignore all bits! + } + else + { + unsigned int i; + + mask = 0; + power_of_two = 1; + + /* Determine what actionpoint would be required to include all of the given + memory region (this include range may have leading and trailing parts + that extend beyond the given region). */ + for (i = 0; i < 32; i++) + { + unsigned int include_size = power_of_two; + + include_start = (first_data / include_size) * include_size; + include_end = include_start + include_size - 1; + + if (include_start <= first_data && include_end >= last_data) + { +// DEBUG("include: 0x%08X .. 0x%08X (mask 0x%08x)\n", +// include_start, include_end, mask); + break; + } + + mask = (mask << 1) + 1; + power_of_two <<= 1; + } + } + + /* This is the first actionpoint in the list. */ + actionpoint_value[0] = include_start; + actionpoint_mask [0] = mask; + points = 1; + + + /* Determine what actionpoints would be required to mask out the leading part + of the include range. */ + { + unsigned int to_be_excluded = first_data - include_start; + ARC_Address boundary = include_start; + + while (to_be_excluded > 0) + { + unsigned int j; + + mask = 0; + power_of_two = 1; + + for (j = 0; j < 32; j++) + { + unsigned int exclude_size = power_of_two; + ARC_Address exclude_start = (first_data / exclude_size - 1) * exclude_size; + ARC_Address exclude_end = exclude_start + exclude_size - 1; + + if (exclude_end < first_data && exclude_start <= boundary) + { +// DEBUG("leading exclude: 0x%08X .. 0x%08X (mask 0x%08x)\n", +// exclude_start, exclude_end, mask); + + to_be_excluded = first_data - exclude_end - 1; + boundary = exclude_end + 1; + + /* There is no point in returning the details of + more than the maximum number of actionpoints that + can be linked together (in a quad). */ + if (points < MAX_ACTION_POINTS_IN_GROUP) + { + actionpoint_value[points] = exclude_start; + actionpoint_mask [points] = mask; + } + points++; + + break; + } + + mask = (mask << 1) + 1; + power_of_two <<= 1; + } + } + } + + /* Determine what actionpoints would be required to mask out the trailing + part of the include range. */ + { + unsigned int to_be_excluded = include_end - last_data; + ARC_Address boundary = include_end; + + while (to_be_excluded > 0) + { + unsigned int j; + + mask = 0; + power_of_two = 1; + + for (j = 0; j < 32; j++) + { + unsigned int exclude_size = power_of_two; + ARC_Address exclude_start = (last_data / exclude_size + 1) * exclude_size; + ARC_Address exclude_end = exclude_start + exclude_size - 1; + + if (exclude_start > last_data && exclude_end >= boundary) + { +// DEBUG("trailing exclude: 0x%08X .. 0x%08X (mask 0x%08x)\n", +// exclude_start, exclude_end, mask); + + to_be_excluded = exclude_start - last_data - 1; + boundary = exclude_start - 1; + + /* There is no point in returning the details of + more than the maximum number of actionpoints that + can be linked together (in a quad). */ + if (points < MAX_ACTION_POINTS_IN_GROUP) + { + actionpoint_value[points] = exclude_start; + actionpoint_mask [points] = mask; + } + points++; + + break; + } + + mask = (mask << 1) + 1; + power_of_two <<= 1; + } + } + } + + return points; +} + + +/* Set the given actionpoint on the target by writing to the corresponding set + of AMV, AMM and AC auxiliary registers. + Return TRUE if it is set successfully. */ + +static Boolean +set_actionpoint_on_target (ARC_ActionPoint *actionpoint) +{ + unsigned int instance = AP_INSTANCE(actionpoint); + + return arc_write_jtag_aux_register(ARC_HW_AMV_REGNUM(instance), actionpoint->match_value, TRUE) && + arc_write_jtag_aux_register(ARC_HW_AMM_REGNUM(instance), actionpoint->match_mask, TRUE) && + arc_write_jtag_aux_register(ARC_HW_AC_REGNUM (instance), actionpoint->control, TRUE); +} + + +/* Clear the given actionpoint on the target by writing 'DISABLED' to the + corresponding AC auxiliary register. + Return TRUE if it is cleared successfully. */ + +static Boolean +clear_actionpoint_from_target (ARC_ActionPoint *actionpoint) +{ + return arc_write_jtag_aux_register(ARC_HW_AC_REGNUM (AP_INSTANCE(actionpoint)), + AP_TRANSACTION_TYPE_DISABLED, + TRUE); +} + + +/* Set the given actionpoint on the target, and update its data structure. + Return TRUE if it is set successfully. */ + +static Boolean +set_actionpoint (ARC_ActionPoint *actionpoint) +{ + Boolean set = set_actionpoint_on_target(actionpoint); + + if (set) + { + actionpoint->triggered = FALSE; + actionpoint->point = 0; + } + else + actionpoint->usage = NOT_IN_USE; + + return set; +} + + +/* Insert an actionpoint to cover a range of target memory. + + Parameters: + bpt : the information describing a breakpoint (NULL for a watchpoint) + length : the length in bytes of the range + match_value: the value for the actionpoint value (AMV) aux register + match_mask : the value for the actionpoint mask (AMM) aux register + control : the value for the actionpoint control (AC) aux register + + Returns 0 for success, -1 for failure. */ + +static int +insert_actionpoint (struct bp_target_info *bpt, + unsigned int length, + ARC_RegisterContents match_value, + ARC_RegisterContents match_mask, + ARC_RegisterContents control) +{ + unsigned int i; + + /* Look for an unused actionpoint. */ + for (i = 0; i < num_actionpoints; i++) + { + ARC_ActionPoint *actionpoint = &actionpoints[i]; + + /* Got one! */ + if (!IN_USE(actionpoint)) + { + /* Record its data. */ + actionpoint->match_value = match_value; + actionpoint->match_mask = match_mask; + actionpoint->control = control; + actionpoint->is_exclude = FALSE; + actionpoint->length = length; + + /* Try to set it on the target. */ + if (set_actionpoint(actionpoint)) + { + /* Now it is in use. */ + actionpoint->usage = SINGLE; + + /* Is it a breakpoint? */ + if (bpt) + { + /* We have not actually saved code from the target program. */ + bpt->shadow_len = 0; + bpt->placed_size = (int) actionpoint->length; + } + + return SUCCESS; + } + } + } + + /* Failed: no free actionpoints. */ +// warning(_("no actionpoints available")); + return FAILURE; +} + + +/* Restore the actionpoints on the target according to their current settings. + If 'clear_unused' is TRUE, any actionpoints which are unused are explicitly + cleared on the target. + + Returns 0 for success, -1 for failure. */ + +static int +restore_actionpoints (Boolean clear_unused) +{ + unsigned int i; + + /* Look at each of the actionpoints. */ + for (i = 0; i < num_actionpoints; i++) + { + ARC_ActionPoint *actionpoint = &actionpoints[i]; + + if (IN_USE(actionpoint)) + { + if (!set_actionpoint_on_target(actionpoint)) + { + actionpoint->usage = NOT_IN_USE; + return FAILURE; + } + } + else if (clear_unused) + { + if (!clear_actionpoint_from_target (actionpoint)) + return FAILURE; + } + } + + return SUCCESS; +} + + +/* Find a number of unused actionpoints whose numbers (0..7) lie in a contiguous + range (allowing for wraparound of the numbers, i.e. % 8). + + Parameters: + required : the number of unused actionpoints required + from : set to the number (0..7) of the first actionpoint + compacted: set to TRUE if the currently used set of actionpoints + had to be compacted to give a contiguous range of unused + actionpoints + + Returns TRUE if the required number was found, FALSE otherwise. */ + +static Boolean +find_unused_actionpoints (unsigned int required, + unsigned int *from, + Boolean *compacted) +{ + unsigned int unused = 0; + unsigned int first_unused = 0; + unsigned int i; + + /* How many slots are not currently used? */ + for (i = 0; i < num_actionpoints; i++) + { + ARC_ActionPoint *actionpoint = &actionpoints[i]; + + if (!IN_USE(actionpoint)) + unused++; + } + + DEBUG("%u actionpoints unused, %u required\n", unused, required); + + if (required > unused) + return FALSE; + + /* When used in pairs or quads, the action points wrap around, e.g. a pair + might be actionpoints (3, 0), if the target has 4 actionpoints; and a + quad might be (6, 7, 0, 1), if the target has 8 actionpoints. */ + + /* First try to find 'required' contiguous unused slots. */ + for (i = 0; i < num_actionpoints + required - 2; i++) + { + ARC_ActionPoint *actionpoint = &actionpoints[i % num_actionpoints]; + + if (IN_USE(actionpoint)) + { + /* The first unused one MAY be the next one after this one. */ + first_unused = i + 1; + } + else + { + DEBUG("%u: AP%u is unused\n", i, i % num_actionpoints); + + if (i - first_unused + 1 >= required) + { + /* A sufficiently large sequence of unused actionpoints has been + found. */ + *from = first_unused % num_actionpoints; + *compacted = FALSE; + return TRUE; + } + } + } + + DEBUG("compacting array\n"); + + /* There are sufficient unused slots, but they are not contiguous - so move + all the used ones towards the start of the array so that all the unused + ones are contiguous at the end of the array. */ + first_unused = MAX_ACTION_POINTS; + + for (i = 0; i < num_actionpoints; i++) + { + ARC_ActionPoint *actionpoint = &actionpoints[i]; + + if (IN_USE(actionpoint)) + { + if (first_unused != MAX_ACTION_POINTS) + { + DEBUG("moving %u to %u\n", i, first_unused); + + /* Move the used one into the unused slot. */ + actionpoints[first_unused] = *actionpoint; + + actionpoint->usage = NOT_IN_USE; + + /* The first unused entry in the array is now the next one after + it - this is true whether that next one was the used one that + has just been moved, or was the next in a sequence of unused + entries. */ + first_unused++; + } + } + else if (first_unused == MAX_ACTION_POINTS) + { + /* This one really is the first unused one we have found. */ + first_unused = i; + } + } + + *from = num_actionpoints - unused; + + DEBUG("from = %u\n", *from); + + *compacted = TRUE; + return TRUE; +} + + +/* Insert an actionpoint group to cover a range of target memory. + + Parameters: + length : the length in bytes of the range + number : the number of actionpoints required + match_value: the values for the actionpoint value (AMV) aux registers + match_mask : the values for the actionpoint mask (AMM) aux registers + control : the value for the actionpoint control (AC) aux registers + + Returns 0 for success, -1 for failure. */ + +static int +insert_actionpoint_group (unsigned int length, + unsigned int number, + ARC_RegisterContents match_value[], + ARC_RegisterContents match_mask[], + ARC_RegisterContents control) +{ + /* For 2 actionpoints, we can use a pair; for 3 or 4, we must use a quad. */ + unsigned int required = (number == 2) ? 2 : 4; + unsigned int first_free; + Boolean is_pair = (required == 2); + Boolean compacted; + + gdb_assert(2 <= number && number <= 4); + + /* Try to find the required number of unused actionpoints. */ + if (find_unused_actionpoints(required, &first_free, &compacted)) + { + ARC_ActionPoint *actionpoint[MAX_ACTION_POINTS_IN_GROUP]; + unsigned int i; + + /* Get an array of pointers to the data for those actionpoints. */ + for (i = 0; i < required; i++) + actionpoint[i] = &actionpoints[(first_free + i) % num_actionpoints]; + + actionpoint[0]->length = length; + actionpoint[0]->is_exclude = FALSE; + + /* The Control register for the first actionpoint in the group must be + set to indicate whether the group is a pair or a quad. */ + actionpoint[0]->usage = (is_pair) ? PAIR_0 : QUAD_0; + actionpoint[0]->match_value = match_value[0]; + actionpoint[0]->match_mask = match_mask[0]; + actionpoint[0]->control = control | ((is_pair) ? AP_PAIR : AP_QUAD); + + /* All subsequent actionpoints in the group have exclusive rather than + inclusive address ranges. */ + control &= ~AP_MODE_TRIGGER_IN_RANGE; + control |= AP_MODE_TRIGGER_OUTSIDE_RANGE; + + for (i = 1; i < number; i++) + { + actionpoint[i]->usage = actionpoint[0]->usage + i; + actionpoint[i]->match_value = match_value[i]; + actionpoint[i]->match_mask = match_mask[i]; + actionpoint[i]->control = control; + actionpoint[i]->length = 0; + actionpoint[i]->is_exclude = TRUE; + } + + /* If we are using only 3 of the 4 actionpoints in a quad, the 4th one + must be disabled (or we could just make it the same as one of the + other exclusive ones). */ + if (number == 3) + { + ARC_ActionPoint *disabled = actionpoint[3]; + + disabled->usage = QUAD_3; + disabled->match_value = 0; + disabled->match_mask = 0; + disabled->control = AP_TRANSACTION_TYPE_DISABLED; + disabled->length = 0; + } + + /* If we had to compact the array of actionpoints in order to get a + long enough contiguous sequence of unused entries, then set ALL of + the actionpoints that are now in use, and explicitly clear all that + are not in use (this is simplest!). */ + if (compacted) + return restore_actionpoints(TRUE); + + /* Otherwise, just set the ones for the group, which were previously + unused. */ + for (i = 0; i < required; i++) + if (!set_actionpoint(actionpoint[i])) + return FAILURE; + + return SUCCESS; + } + +// warning(_("insufficient actionpoints available")); + return FAILURE; +} + + +/* Insert a h/w breakpoint or watchpoint to cover a range of target memory. + + Parameters: + address : the start address of the range + control : the value for the actionpoint control (AC) aux register + length : the length in bytes of the range + bpt : the information describing the breakpoint (NULL for a watchpoint) + + Returns 0 for success, -1 for failure. */ + +static int +insert_range (ARC_RegisterContents address, + ARC_RegisterContents control, + unsigned int length, + struct bp_target_info *bpt) +{ + /* At most 4 actionpoints can be connected (as a quad). */ + ARC_Address actionpoint_value[MAX_ACTION_POINTS_IN_GROUP]; + ARC_Word actionpoint_mask [MAX_ACTION_POINTS_IN_GROUP]; + unsigned int actionpoints_needed; + + /* Work out how many actionpoints would be required to exactly cover the + given memory range. */ + actionpoints_needed = map_actionpoints(address, + length, + actionpoint_value, + actionpoint_mask); + + if (actionpoints_needed == 1) + return insert_actionpoint(bpt, + length, + actionpoint_value[0], + actionpoint_mask[0], + control); + + if (actionpoints_needed <= MAX_ACTION_POINTS_IN_GROUP) + return insert_actionpoint_group(length, + actionpoints_needed, + actionpoint_value, + actionpoint_mask, + control); + + warning (_("break/watchpoint would require %u linked actionpoints, " + "but at most %u actionpoints may be linked together"), + actionpoints_needed, MAX_ACTION_POINTS_IN_GROUP); + + return FAILURE; +} + + +/* Remove an actionpoint from a range of target memory. + + Parameters: + address : the start address of the range + length : the length in bytes of the range + + Returns -1 for failure, 0 for success. */ + +static int +remove_actionpoint (CORE_ADDR address, unsigned int length) +{ + unsigned int i; + + /* Look at all the actionpoints. */ + for (i = 0; i < num_actionpoints; i++) + { + ARC_ActionPoint *actionpoint = &actionpoints[i]; + + if (IN_USE(actionpoint) && !actionpoint->is_exclude) + { + if (actionpoint->match_value == (ARC_RegisterContents) address && + actionpoint->length == length) + { + unsigned int points; + unsigned int p; + + /* Is this the first of a pair or quad? */ + if ((actionpoint->control & AP_PAIR) != 0) + points = 2; + else if ((actionpoint->control & AP_QUAD) != 0) + points = 4; + else + points = 1; + +// DEBUG("points = %u\n", points); + + for (p = 1; p < points; p++) + { + ARC_ActionPoint *next = &actionpoints[(i + p) % num_actionpoints]; + + if (clear_actionpoint_from_target (next)) + next->usage = NOT_IN_USE; + else + return FAILURE; + } + + if (clear_actionpoint_from_target (actionpoint)) + { + actionpoint->usage = NOT_IN_USE; + return SUCCESS; + } + + break; + } + } + } + + /* Failed: could not find actionpoint, or could not clear it. */ + return FAILURE; +} + + +/* -------------------------------------------------------------------------- */ +/* local functions called from outside this module */ +/* -------------------------------------------------------------------------- */ + +/* Check if we can set a hardware watchpoint of type TYPE. TYPE is + one of bp_hardware_watchpoint, bp_read_watchpoint, bp_write_watchpoint, or + bp_hardware_breakpoint. COUNT is the number of such watchpoints used so far + (including this one). OTHERTYPE is the total number of hardware breakpoints + and watchpoints of other types that are "already" set (0 if type == bp_hardware_breakpoint). + + Result: 0 if hardware watchpoints are not supported + -1 if there are not enough hardware watchpoints + 1 if there are enough hardware watchpoints + + N.B. this is not what is stated in target.h, but it does conform to the use + made of this function's result in breakpoint.c! */ + +static int +arc_debug_can_use_hw_breakpoint (int type, int count, int othertype) +{ + ENTERARGS("type %d, count %d", type, count); + + if (num_actionpoints == 0) + return 0; + + /* N.B. this will sometimes give a "false positive" result, i.e. that there + sufficient actionpoints available when in fact there are not: the + ARC processor actionpoints can be used for all of the types, but gdb + assumes that there are separate sets of resources for breakpoints + and watchpoints, and when asking for a breakpoint does not give the + number of watchpoints "already" set. + + It is not possible simply to check how many actionpoints are currently + set, as gdb does not actually set the breakpoints and watchpoints + until program execution is started or resumed - so when this function + is called, none are actually set. + + Also, the breakpoints and watchpoints may require pairs or quads of + actionpoints, rather than single actionpoints, and this will not be + known until they are set, and their addresses and ranges are known! */ + return ((int) num_actionpoints >= count + othertype) ? 1 : -1; +} + + +/* Insert a hardware breakpoint on the target. + Returns 0 for success, -1 for failure. */ + +static int +arc_debug_insert_hw_breakpoint (struct bp_target_info *bpt) +{ + ARC_RegisterContents control = AP_TARGET_INSTRUCTION_ADDRESS | + AP_TRANSACTION_TYPE_READ | + AP_MODE_TRIGGER_IN_RANGE | + AP_ACTION_BREAK; + + ENTERARGS("0x%x : %u", (unsigned int) bpt->placed_address, bpt->range); + + /* Is it a range breakpoint? */ + if (bpt->range) + return insert_range((ARC_RegisterContents) bpt->placed_address, + control, + bpt->range, + bpt); + + /* No, just a single-instruction breakpoint? */ + return insert_actionpoint(bpt, + HW_BP_SIZE, + (ARC_RegisterContents) bpt->placed_address, + 0, /* All bits of address. */ + control); +} + + +/* Remove a hardware breakpoint from the target. + Returns 0 for success, non-zero for failure. */ + +static int +arc_debug_remove_hw_breakpoint (struct bp_target_info *bpt) +{ + unsigned int range = (bpt->range) ? bpt->range : HW_BP_SIZE; + + ENTERARGS("0x%x : %u", (unsigned int) bpt->placed_address, range); + + return remove_actionpoint(bpt->placed_address, range); +} + + +/* Insert a hardware watchpoint on the target. + + Parameters: + addr : the start address of the region of memory to be watched + length: the length in bytes of the region of memory + type : 0 => write, 1 => read, 2 => read/write + + Returns 0 for success, -1 for failure. */ + +static int +arc_debug_insert_watchpoint (CORE_ADDR addr, int length, int type) +{ + ARC_RegisterContents control = AP_TARGET_LOAD_STORE_ADDRESS | + AP_MODE_TRIGGER_IN_RANGE | + AP_ACTION_BREAK; + + ENTERARGS("0x%08X:%d %d", (unsigned int) addr, length, type); + + gdb_assert(length > 0); + + switch (type) + { + case 0: + control |= AP_TRANSACTION_TYPE_WRITE; break; + case 1: + control |= AP_TRANSACTION_TYPE_READ; break; + case 2: + control |= AP_TRANSACTION_TYPE_ACCESS; break; + default: + internal_error (__FILE__, __LINE__, _("invalid watchpoint type: %d"), type); + } + + return insert_range((ARC_RegisterContents) addr, + control, + (unsigned int) length, + NULL); +} + + +/* Remove a hardware watchpoint from the target. + + Parameters: + addr : the start address of the region of memory being watched + length: the length in bytes of the region of memory + type : 0 => write, 1 => read, 2 => read/write + + Returns 0 for success, non-zero for failure. */ + +static int +arc_debug_remove_watchpoint (CORE_ADDR addr, int length, int type) +{ + ENTERARGS("0x%x:%d %d", (unsigned int) addr, length, type); + + return remove_actionpoint(addr, (unsigned int) length); +} + + +/* Returns non-zero if the execution of the target program has been stopped by + the trigger of a hardware watchpoint (i.e. on memory read or write), zero + otherwise. */ + +static int +arc_debug_stopped_by_watchpoint (void) +{ + unsigned int i; + + ENTERMSG; + + /* Look at all of the actionpoints. */ + for (i = 0; i < num_actionpoints; i++) + { + ARC_ActionPoint *actionpoint = &actionpoints[i]; + + if (IN_USE(actionpoint) && actionpoint->triggered) + { + /* Is it a memory read or write actionpoint? */ + if ((actionpoint->control & AP_TARGET_LOAD_STORE_ADDRESS) != 0) + { + DEBUG("actionpoint %d (load/store) triggered\n", i); + return 1; + } + } + } + + return 0; +} + + +/* Get the address of the data that was read/written causing a h/w watchpoint to + trigger; the address is returned in the '*addr' parameter. + Returns 0 for failure, non-zero for success. */ + +static int +arc_debug_stopped_data_address (struct target_ops *ops, CORE_ADDR *addr) +{ + unsigned int i; + + ENTERMSG; + + /* Look at each of the actionpoints. */ + for (i = 0; i < num_actionpoints; i++) + { + ARC_ActionPoint *actionpoint = &actionpoints[i]; + + DEBUG("AP%u: in use = %d, triggered = %d\n", i, IN_USE(actionpoint), actionpoint->triggered); + + /* If this actionpoint has been triggered. */ + if (IN_USE(actionpoint) && actionpoint->triggered) + { + /* Is it a memory read or write actionpoint? */ + if ((actionpoint->control & AP_TARGET_LOAD_STORE_ADDRESS) != 0) + { + DEBUG("actionpoint %d (load/store) triggered by access at 0x%08X\n", i, actionpoint->point); + + /* OK, got the data address! */ + *addr = (CORE_ADDR) actionpoint->point; + return 1; + } + } + } + + DEBUG("no watchpoint triggered\n"); + + return 0; +} + + +/* Can a h/w watchpoint 'length' bytes long be set at address 'addr' in target memory? */ + +static int +arc_debug_region_ok_for_hw_watchpoint (CORE_ADDR addr, int length) +{ + /* As far as we know, we can set a h/w watchpoint anywhere! */ + return 1; +} + + +/* -------------------------------------------------------------------------- */ +/* externally visible functions */ +/* -------------------------------------------------------------------------- */ + +/* This function is called after a reset of the target has been performed (which + clears all the aux registers associated with actionpoints). It attempts to + restore all actionpoints to their pre-reset settings. + + Returns TRUE if the actionpoints are restored, FALSE otherwise. */ + +Boolean +arc_restore_actionpoints_after_reset (void) +{ + return (restore_actionpoints(FALSE) == SUCCESS); +} + + +/* If the debug target supports actionpoints, set up the function pointers in + the given target operations structure to point to the functions which + implement the associated operations. + + Returns TRUE if actionpoints are supported, FALSE otherwise. */ + +Boolean +arc_initialize_actionpoint_ops (struct target_ops *debug_ops) +{ + if (target_has_actionpoints()) + { + debug_ops->to_can_use_hw_breakpoint = arc_debug_can_use_hw_breakpoint; + debug_ops->to_insert_hw_breakpoint = arc_debug_insert_hw_breakpoint; + debug_ops->to_remove_hw_breakpoint = arc_debug_remove_hw_breakpoint; + debug_ops->to_insert_watchpoint = arc_debug_insert_watchpoint; + debug_ops->to_remove_watchpoint = arc_debug_remove_watchpoint; + debug_ops->to_stopped_by_watchpoint = arc_debug_stopped_by_watchpoint; + debug_ops->to_stopped_data_address = arc_debug_stopped_data_address; + debug_ops->to_region_ok_for_hw_watchpoint = arc_debug_region_ok_for_hw_watchpoint; + + /* This is the default, but just to make it clear that watchpoints must + be cleared before execution can resume. */ + debug_ops->to_have_continuable_watchpoint = 0; + + return TRUE; + } + + return FALSE; +} + + +/* Display all the target actionpoints. */ + +void +arc_display_actionpoints (void) +{ + unsigned int i; + + char *targets[8] = + { + _("Instruction Address"), + _("Instruction Data"), + _("Load/Store Address"), + _("Load/Store Data"), + _("Aux Register Address"), + _("Aux Register Data"), + _("Ext Parameter 0"), + _("Ext Parameter 1") + }; + + char *transactions[4] = + { + _("disabled"), + _("write"), + _("read"), + _("read/write") + }; + + char *explanations[8] = + { + _("execution of instruction at address"), + _("execution of instruction"), + _("load or store of data at address"), + _("load or store of data"), + _("read or write of auxiliary register"), + _("read or write of auxiliary register contents"), + _("value"), + _("value") + }; + + + /* Look at each of the actionpoints in turn. */ + for (i = 0; i < num_actionpoints; i++) + { + ARC_ActionPoint *actionpoint = &actionpoints[i]; + + if (IN_USE(actionpoint)) + { + ARC_RegisterContents control = actionpoint->control; + const unsigned int targ = (control & AP_TARGET_MASK ) >> AP_TARGET_SHIFT; + const unsigned int trans = (control & AP_TRANSACTION_TYPE_MASK) >> AP_TRANSACTION_TYPE_SHIFT; + const char *target = targets [targ]; + const char *type = transactions[trans]; + const char *mode = ((control & AP_MODE_MASK) == + AP_MODE_TRIGGER_OUTSIDE_RANGE) ? _("outside range") : _("in range"); + const char *action = ((control & AP_ACTION_MASK) == + AP_ACTION_BREAK) ? _("break") : _("raise exception"); + const char *usage; + + switch (actionpoint->usage) + { + case SINGLE: usage = _(" "); break; + case PAIR_0: usage = _(" (Pair 0)"); break; + case PAIR_1: usage = _(" (Pair 1)"); break; + case QUAD_0: usage = _(" (Quad 0)"); break; + case QUAD_1: usage = _(" (Quad 1)"); break; + case QUAD_2: usage = _(" (Quad 2)"); break; + case QUAD_3: usage = _(" (Quad 3)"); break; + default: + internal_error (__FILE__, __LINE__, _("invalid AP usage: %u"), actionpoint->usage); + return; + } + + printf_filtered(_("AP %u%s :: "), i, usage); + + printf_filtered( _("value : %08X\n"), actionpoint->match_value); + printf_filtered( _(" mask : %08X\n"), actionpoint->match_mask); + if ((control & AP_TRANSACTION_TYPE_MASK) == AP_TRANSACTION_TYPE_DISABLED) + printf_filtered(_(" control : %08X disabled\n"), actionpoint->control); + else + printf_filtered(_(" control : %08X %s, %s on %s %s\n"), actionpoint->control, target, action, type, mode); + if (actionpoint->triggered) + { + const char *explain = explanations[targ]; + + printf_filtered(_(" triggered by %s %08x\n"), explain, actionpoint->point); + } + } + else + { + printf_filtered(_("AP %u :: not in use\n"), i); + } + } +} + + +/* This function is called as soon as execution of the target program has halted. + It checks whether the halt is due to an actionpoint trigger, and, if so, + identifies the actionpoint that has been triggered and finds the address (code + or data) at which memory access (read, write or execute) has caused the trigger. */ + +void +arc_target_halted (void) +{ + ARC_RegisterContents debug; + + ENTERMSG; + + if (arc_read_jtag_aux_register(arc_debug_regnum, &debug, TRUE)) + { + /* If the bit indicating that an actionpoint has halted the processor is + set. */ + if ((debug & DEBUG_ACTIONPOINT_HALT) != 0) + { + /* Get the Actionpoints Status Register from the DEBUG register: + this contains one bit for each actionpoint in the processor + configuration. */ + unsigned int ASR = (debug & DEBUG_ACTIONPOINT_STATUS) >> + DEBUG_ACTIONPOINT_STATUS_SHIFT; + unsigned int i; + + /* Now look at each of the actionpoints. */ + for (i = 0; i < num_actionpoints; i++) + { + ARC_ActionPoint *actionpoint = &actionpoints[i]; + + actionpoint->triggered = FALSE; + + /* Is the ASR bit for this actionpoint set? */ + if ((ASR & 1) != 0) + { + if (IN_USE(actionpoint)) + { + actionpoint->triggered = TRUE; + + /* The AMV register for this action point has been + updated with the address to which access has caused + the actionpoint to trigger. */ + (void) arc_read_jtag_aux_register(ARC_HW_AMV_REGNUM(AP_INSTANCE(actionpoint)), + &actionpoint->point, + TRUE); + } + else + internal_error (__FILE__, __LINE__, _("actionpoint %u triggered but not set"), i); + } + + ASR >>= 1; + } + } + } +} + + +/* For debugging - just give the values. */ + +void +arc_dump_actionpoints (const char *message) +{ + unsigned int i; + + DEBUG("%s\n", message); + + /* Look at each of the actionpoints in turn. */ + for (i = 0; i < num_actionpoints; i++) + { + ARC_ActionPoint *actionpoint = &actionpoints[i]; + + DEBUG("slot %u:: ", i); + + if (IN_USE(actionpoint)) + { + DEBUG( "value : %08X\n", actionpoint->match_value); + DEBUG(" mask : %08X\n", actionpoint->match_mask); + DEBUG(" control : %08X\n", actionpoint->control); + DEBUG(" triggered: %u\n", actionpoint->triggered); + DEBUG(" point : %08x\n", actionpoint->point); + } + else + { + DEBUG("not in use\n"); + } + } +} + +/******************************************************************************/ diff --git a/gdb/arc-jtag-actionpoints.h b/gdb/arc-jtag-actionpoints.h new file mode 100644 index 00000000000..741d8696120 --- /dev/null +++ b/gdb/arc-jtag-actionpoints.h @@ -0,0 +1,68 @@ +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. + + Copyright 2008, 2009 Free Software Foundation, Inc. + + Contributed by ARC International (www.arc.com) + + Authors: + Richard Stuckey <richard.stuckey@arc.com> + + 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 3 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, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This header file defines the interface to hardware actionpoints in the */ +/* ARC processor. */ +/* */ +/* Usage: */ +/* Once the connection to the target has been established, the function */ +/* arc_initialize_actionpoint_ops should be called: this will update the */ +/* debug_ops structure according to the support provided by the ARC */ +/* processor in the target for hardware breakpoints and watchpoints. */ +/* */ +/* The function arc_restore_actionpoints_after_reset should be called */ +/* after the target board has been reset, in order to re-establish any */ +/* actionpoints that have been set, as they will have been cleared by */ +/* the reset; it returns TRUE if successful. */ +/* */ +/******************************************************************************/ + +#ifndef ARC_JTAG_ACTIONPOINTS_H +#define ARC_JTAG_ACTIONPOINTS_H + +/* gdb header files */ +#include "defs.h" +#include "target.h" + +/* ARC header files */ +#include "arc-support.h" + + +Boolean arc_initialize_actionpoint_ops (struct target_ops *debug_ops); + +Boolean arc_restore_actionpoints_after_reset (void); + +void arc_target_halted (void); + +void arc_display_actionpoints (void); + + +/* For debugging. */ +void arc_dump_actionpoints (const char *message); + +#endif /* ARC_JTAG_ACTIONPOINTS_H */ +/******************************************************************************/ diff --git a/gdb/arc-jtag-ops.c b/gdb/arc-jtag-ops.c index 0a237f69d30..54631b92efd 100644 --- a/gdb/arc-jtag-ops.c +++ b/gdb/arc-jtag-ops.c @@ -1,1229 +1,1496 @@ -/* Target dependent code for ARC700, for GDB, the GNU debugger. +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. - Copyright 2005 Free Software Foundation, Inc. + Copyright 2005, 2008, 2009 Free Software Foundation, Inc. - Contributed by Codito Technologies Pvt. Ltd. (www.codito.com) + Contributed by ARC International (www.arc.com) - Authors: - Sameer Dhavale <sameer.dhavale@codito.com> - Soam Vasani <soam.vasani@codito.com> + Author: + Sameer Dhavale <sameer.dhavale@codito.com> + Soam Vasani <soam.vasani@codito.com> + Richard Stuckey <richard.stuckey@arc.com> 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 + the Free Software Foundation; either version 3 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. -*/ + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This module implements debug access to an ARC processor via its JTAG */ +/* interface. */ +/* */ +/* See */ +/* ARCompact Instruction Set Architecture */ +/* Programmer's Reference (5115-018) */ +/* */ +/* for a description of ARC processor architecture (in particular */ +/* the auxiliary registers and the halting procedure); */ +/* */ +/* ARC 700 External Interfaces */ +/* Reference (5117-013) */ +/* */ +/* for a description of the JTAG interface (in particular the Test */ +/* Access Port Controller (TAPC) state machine). */ +/* */ +/* The JTAG interface is accessed by three parallel ports: control, data */ +/* and status. Data is read from or written to these ports one byte at a */ +/* at a time, using a GPIO (General Purpose Input/Output) driver. */ +/* */ +/* The TDI and TMS signals are written to the data port. */ +/* The TCK signal is written to the control port. */ +/* The TDO signal is read from the status port. */ +/* */ +/* Host/Target Byte Order: */ +/* The core and auxiliary register contents read from or written to the */ +/* JTAG interface are ALWAYS in little-endian format, regardless of the */ +/* endianness of the target processor. */ +/* */ +/******************************************************************************/ + +/* system header files */ #include <stdio.h> -#include <unistd.h> -#include <stdlib.h> +#include <string.h> #include <errno.h> -#include <sys/io.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <assert.h> +#include <unistd.h> +#include <byteswap.h> + +/* gdb header files */ +#include "defs.h" +#include "gdb_assert.h" + +/* ARC header files */ +#include "arc-gpio.h" +#include "arc-jtag.h" #include "arc-jtag-ops.h" -#include "gpio.h" -#include <signal.h> -#include <defs.h> -#include <sys/ioctl.h> -#include "arc-tdep.h" -#define printf printf_filtered +#include "arc-support.h" -unsigned int arcjtag_retry_count = 50; +/* -------------------------------------------------------------------------- */ +/* conditional compilation flags */ +/* -------------------------------------------------------------------------- */ +/* We would really like to optimise the use of the JTAG Data Register by + loading a word into it only if that word differs from the last word loaded; + but there seems to be undocumented behaviour of the JTAG mechanism in that + the Data Register is altered after a write operation! See ARC Bug #93814. */ +#define JTAG_DATA_REGISTER_IS_CORRUPTED_BY_WRITE -/* ------------------------------------ */ -/* For ARC jtag Cable */ -/* */ -/* Pin no. Signal Word , Bit */ -/* */ -/* - TRST */ -/* 8 TMS Data 6 */ -/* 1 TCK Control 0 */ -/* 9 TDI Data 7 */ -/* 13 TDO Status 4 */ +/* Define this if you wish to check the whether the contents of the JTAG Data + Register are corrupted by write operations. */ +//#define CHECK_JTAG_DATA_REGISTER +/* Define this if you wish to perform low-level debugging of the JTAG state + machine emulated in this module (this should not be necessary). */ +//#define STATE_MACHINE_DEBUG -#define JTAG_TRST 0x00 /* not there */ -#define JTAG_TMS 0x40 /* data port */ -#define JTAG_TCK 0x01 /* control port. Driven Low. */ -#define JTAG_TDI 0x80 /* data port */ -#define JTAG_TDO 0x10 /* status port */ +/* -------------------------------------------------------------------------- */ +/* local types */ +/* -------------------------------------------------------------------------- */ -/* ------------------------------------ */ +/* Sizes of quantities. */ +#define BITS_IN_COMMAND_CODE 4 +#define BITS_IN_REGISTER_CODE 4 -/* */ -#define A4_HALT_VALUE 0x02000000 -#define ARC700_HALT_VALUE 0x1 +typedef unsigned int Bit; /* Only LSB of word is used. */ +typedef unsigned int JTAG_RegisterContents; -/* Parallel port i/o addr. (LPT1) */ -#define DATA_PORT 0x378 -#define STATUS_PORT 0x379 -#define CONTROL_PORT 0x37A +typedef enum +{ + MSB_FIRST, + MSB_LAST +} Order; -unsigned char tapc_dataport=0, tapc_statusport=0, tapc_controlport=0x1; -int fd; -struct GPIO_ioctl jtag_ioctl; -sigset_t block_mask; -enum AA3SSPinState - { - READ_FROM_AA3 = 0, - WRITE_TO_AA3 = 1 - }; -enum AA3SSPinState rw_flag; +typedef enum +{ + Memory, + Register +} JTAG_TransactionType; +typedef enum +{ + STALLED, + FAILURE, + READY, + NOT_READY +} JTAG_TransactionStatus; -struct jtag_ops arc_jtag_ops; -static enum ARCProcessorVersion ARCProcessor = UNSUPPORTED; +/* Only these JTAG registers are currently used. */ +typedef enum +{ + JTAG_STATUS_REGISTER = 0x8, + JTAG_TRANSACTION_COMMAND_REGISTER = 0x9, + JTAG_ADDRESS_REGISTER = 0xA, + JTAG_DATA_REGISTER = 0xB, +} JTAG_Register; -/* Sanity check to give error if jtag is not opened at all. */ -static void -check_and_raise_error_if_jtag_closed (void) +#ifdef STATE_MACHINE_DEBUG +typedef enum +{ + UNDEFINED, + TEST_LOGIC_RESET, + RUN_TEST_IDLE, + SELECT_DR_SCAN, + CAPTURE_DR, + SHIFT_DR, + EXIT1_DR, + PAUSE_DR, + EXIT2_DR, + UPDATE_DR, + SELECT_IR_SCAN, + CAPTURE_IR, + SHIFT_IR, + EXIT1_IR, + PAUSE_IR, + EXIT2_IR, + UPDATE_IR, + NUMBER_OF_STATES /* An end-marker, not a state. */ +} JTAG_ControllerState; +#endif + + +/* -------------------------------------------------------------------------- */ +/* local data */ +/* -------------------------------------------------------------------------- */ + +/* -------------------------------------- */ +/* For ARC JTAG Cable */ +/* */ +/* Pin no. Signal Port Bit */ +/* */ +/* - TRST */ +/* 8 TMS Data 6 */ +/* 1 TCK Control 0 */ +/* 9 TDI Data 7 */ +/* 13 TDO Status 4 */ +/* -------------------------------------- */ + +/* Bit masks for signals written to parallel ports. */ + +#define JTAG_TRST 0 /* not there */ +#define JTAG_TMS (1 << 6) /* on Data port */ +#define JTAG_TCK (1 << 0) /* on Control port (driven low) */ +#define JTAG_TDI (1 << 7) /* on Data port */ +#define JTAG_TDO (1 << 4) /* on Status port */ + + +/* Commands which can be written to the JTAG Transaction Command Register. */ +#define WRITE_MEMORY_LOCATION 0x0 +#define WRITE_CORE_REGISTER 0x1 +#define WRITE_AUX_REGISTER 0x2 +#define NOP 0x3 +#define READ_MEMORY_LOCATION 0x4 +#define READ_CORE_REGISTER 0x5 +#define READ_AUX_REGISTER 0x6 + + +/* Gives the endianness of the target processor. */ +static Boolean target_is_big_endian; + +/* These accumulate the bit masks to be written to the data and control ports. */ +static Byte data_port_value, control_port_value; + +/* Variables for tracking the contents of the JTAG Address and Transaction + Command registers. */ +static JTAG_RegisterContents address_register_contents; +static JTAG_RegisterContents command_register_contents; +static Boolean address_register_contents_known; +static Boolean command_register_contents_known; + + +#ifdef STATE_MACHINE_DEBUG +/* This table encodes all possible transitions of the JTAG Test Access Port + (TAP) Controller State Machine: for each state, the transition to one of two + possible next states is determined by whether a 0 bit or a 1 bit is written + as the JTAG TMS interface signal. */ +static const JTAG_ControllerState transitions[NUMBER_OF_STATES][2] = { - if( arc_jtag_ops.jtag_status == JTAG_CLOSED ) - error ("JTAG connection is closed. Use target arcjtag first\n"); +/* 0 1 */ +/* UNDEFINED */ { UNDEFINED, UNDEFINED }, +/* TEST_LOGIC_RESET */ { RUN_TEST_IDLE, TEST_LOGIC_RESET }, +/* RUN_TEST_IDLE */ { RUN_TEST_IDLE, SELECT_DR_SCAN }, +/* SELECT_DR_SCAN */ { CAPTURE_DR, SELECT_IR_SCAN }, +/* CAPTURE_DR */ { SHIFT_DR, EXIT1_DR }, +/* SHIFT_DR */ { SHIFT_DR, EXIT1_DR }, +/* EXIT1_DR */ { PAUSE_DR, UPDATE_DR }, +/* PAUSE_DR */ { PAUSE_DR, EXIT2_DR }, +/* EXIT2_DR */ { SHIFT_DR, UPDATE_DR }, +/* UPDATE_DR */ { RUN_TEST_IDLE, SELECT_DR_SCAN }, +/* SELECT_IR_SCAN */ { CAPTURE_IR, TEST_LOGIC_RESET }, +/* CAPTURE_IR */ { SHIFT_IR, EXIT1_IR }, +/* SHIFT_IR */ { SHIFT_IR, EXIT1_IR }, +/* EXIT1_IR */ { PAUSE_IR, UPDATE_IR }, +/* PAUSE_IR */ { PAUSE_IR, EXIT2_IR }, +/* EXIT2_IR */ { SHIFT_IR, UPDATE_IR }, +/* UPDATE_IR */ { RUN_TEST_IDLE, SELECT_DR_SCAN }, +}; + +/* The current state of the TAP Controller State Machine. */ +static JTAG_ControllerState current_state = UNDEFINED; +#endif + + +/* -------------------------------------------------------------------------- */ +/* externally visible data */ +/* -------------------------------------------------------------------------- */ + +/* This structure holds the operations and data exported by this module. */ +JTAG_Operations arc_jtag_ops; + + +/* -------------------------------------------------------------------------- */ +/* local macros */ +/* -------------------------------------------------------------------------- */ + +#ifdef DEBUG +#undef DEBUG +#endif + +#define DEBUG(...) \ + if (arc_jtag_ops.state_machine_debug) fprintf_unfiltered(gdb_stdlog, __VA_ARGS__) + + +#ifdef STATE_MACHINE_DEBUG +#define SET_STATE(s) set_state(s) +#define NEXT_STATE(b) next_state(b) +#define CHANGE_STATE(x, s) change_state(x, s) +#define STATE_IS(s) gdb_assert(current_state == s) +#define STATE_IS_EITHER(s1, s2) gdb_assert(current_state == s1 || \ + current_state == s2) +#define STATE_IS_ONE_OF(s1, s2, s3) gdb_assert(current_state == s1 || \ + current_state == s2 || \ + current_state == s3) +#else +#define SET_STATE(s) +#define NEXT_STATE(b) +#define CHANGE_STATE(x, s) tapc_TMS(x) +#define STATE_IS(s) +#define STATE_IS_EITHER(s1, s2) +#define STATE_IS_ONE_OF(s1, s2, s3) +#endif + + +#define IS_WORD_ALIGNED(addr) ((addr) % BYTES_IN_WORD == 0) +#define BYTE(val) (Byte) ((val) & 0xFF) + +/* This is more efficient than memcpy(to, from, BYTES_IN_WORD). */ +#define COPY_WORD(to, from) { ((Byte*) to)[0] = ((Byte*) from)[0]; \ + ((Byte*) to)[1] = ((Byte*) from)[1]; \ + ((Byte*) to)[2] = ((Byte*) from)[2]; \ + ((Byte*) to)[3] = ((Byte*) from)[3]; } + + +/* -------------------------------------------------------------------------- */ +/* forward declarations */ +/* -------------------------------------------------------------------------- */ + +static void tapc_TMS (Bit x); +static void set_interface (JTAG_Status status); +static void interface_is_closed (void); +static JTAG_RegisterContents read_jtag_reg (JTAG_Register regnum, + unsigned int num_data_bits); + + +/* -------------------------------------------------------------------------- */ +/* local functions */ +/* -------------------------------------------------------------------------- */ + +/* -------------------------------------------------------------------------- */ +/* 1) debug functions */ +/* -------------------------------------------------------------------------- */ + +/* Debug function. */ + +static const char* +JTAG_register_name (JTAG_Register r) +{ + switch (r) + { + case JTAG_STATUS_REGISTER : return "Status"; + case JTAG_TRANSACTION_COMMAND_REGISTER : return "Transaction Command"; + case JTAG_ADDRESS_REGISTER : return "Address"; + case JTAG_DATA_REGISTER : return "Data"; + default: internal_error(__FILE__, __LINE__, _("invalid JTAG register %d"), r); + } } -/* Initializations for GPIO interface */ -static int -gpio_setup (void) +/* Debug function. */ + +static const char* +JTAG_TransactionStatus_Image (JTAG_TransactionStatus value) { - fd=open("//dev//gpio",O_RDWR); - ioctl(fd,GPIO_IOC_HARDRESET); - ioctl(fd,GPIO_IOC_SET_PORT_BASE,0x378); - jtag_ioctl.inlen=0; - jtag_ioctl.outlen=0; - jtag_ioctl.inbuf=(unsigned char *)xmalloc(2*sizeof(unsigned char)); - jtag_ioctl.outbuf=(unsigned char *)xmalloc(2*sizeof(unsigned char)); - return 0; + switch (value) + { + case STALLED : return "STALLED"; + case FAILURE : return "FAILURE"; + case READY : return "READY"; + case NOT_READY: return "NOT READY"; + default: internal_error(__FILE__, __LINE__, _("invalid JTAG transaction status %d"), value); + } } -void gpio_write(unsigned int port, unsigned char *data) + +#ifdef STATE_MACHINE_DEBUG +/* Debug function. */ + +static const char* +JTAG_ControllerState_Image (JTAG_ControllerState state) { - jtag_ioctl.inlen=2; - jtag_ioctl.inbuf[1]=*data; - switch(port) + switch (state) { - case DATA_PORT: - jtag_ioctl.inbuf[0]=0; - break; - - case STATUS_PORT: - jtag_ioctl.inbuf[0]=1; - break; - - case CONTROL_PORT: - jtag_ioctl.inbuf[0]=2; - break; - - default: - error("Invalid port\n"); + case UNDEFINED : return "<undefined> "; + case TEST_LOGIC_RESET: return "Test-Logic-Reset"; + case RUN_TEST_IDLE : return "Run-Test/Idle "; + case SELECT_DR_SCAN : return "Select-DR-Scan "; + case CAPTURE_DR : return "Capture-DR "; + case SHIFT_DR : return "Shift-DR "; + case EXIT1_DR : return "Exit1-DR "; + case PAUSE_DR : return "Pause-DR "; + case EXIT2_DR : return "Exit2-DR "; + case UPDATE_DR : return "Update-DR "; + case SELECT_IR_SCAN : return "Select-IR-Scan "; + case CAPTURE_IR : return "Capture-IR "; + case SHIFT_IR : return "Shift-IR "; + case EXIT1_IR : return "Exit1-IR "; + case PAUSE_IR : return "Pause-IR "; + case EXIT2_IR : return "Exit2-IR "; + case UPDATE_IR : return "Update-IR "; + default : return "<invalid> "; } +} +#endif - - if(ioctl(fd,GPIO_IOC_DO_IO,&jtag_ioctl)) - error("Failure writing to port 0x%x\n",port); +#ifdef CHECK_JTAG_DATA_REGISTER +/* Read back the contents of the JTAG Data Register, and check that the value + is what is expected, i.e. the last value that was written to that register. */ + +static void +check_Data_Register (ARC_Word expected) +{ + /* Read the data from the JTAG Data Register. */ + ARC_Word actual = (ARC_Word) read_jtag_reg(JTAG_DATA_REGISTER, BITS_IN_WORD); + + /* Is the data still in the register? */ + if (actual != expected) + warning(_("JTAG Data Register: expected = %08X, actual = %08X\n"), expected, actual); + else + printf_unfiltered(_("word %08X is still in JTAG Data Register\n"), expected); } +#endif -unsigned char gpio_read(unsigned int port) -{ - //jtag_ioctl.inbuf[1]=tapc_statusport; - jtag_ioctl.inlen=2; - jtag_ioctl.outlen=1; +/* -------------------------------------------------------------------------- */ +/* 2) helper functions for setting TMS / TCK / TDI */ +/* -------------------------------------------------------------------------- */ - switch(port) - { - case DATA_PORT: - jtag_ioctl.inbuf[0]=0x80; - break; - - case STATUS_PORT: - jtag_ioctl.inbuf[0]=0x81; - break; - - case CONTROL_PORT: - jtag_ioctl.inbuf[0]=0x82; - break; - - default: - error("Invalid port\n"); - } - - if(ioctl(fd,GPIO_IOC_DO_IO,&jtag_ioctl)) - error("Failure reading from port 0x%x\n",port); - - return jtag_ioctl.outbuf[0]; +/* These functions set the accumulated values to be written out to the ports. + The final values are written only by doing the pulse, e.g. if TDI and TMS + are set/unset by subsequent calls, the last GPIO write operation performed + by those calls before the pulse writes the accumulated bit mask value to + the port (overwriting the values written by the preceding calls), and it + is this bit mask that is significant when the JTAG is clocked. */ +static void +tapc_set_TMS (Bit x) +{ + Byte current_value = data_port_value; + + if (x) + data_port_value |= JTAG_TMS; + else + data_port_value &= ~JTAG_TMS; + if (data_port_value != current_value) + gpio_write(DATA_PORT, data_port_value); } -/* Helper functions for setting - TMS / TCK / TDI. Valid inputs - are 1 and 0. The tapc_<tms/tck/tdi> - functions set the internal values - to be written out to the port. The - final values are written only by doing - the pulse. - */ static void -tapc_set_tms (char x) +tapc_set_TDI (Bit x) { - if(x) - tapc_dataport |= JTAG_TMS; + Byte current_value = data_port_value; + + if (x) + data_port_value |= JTAG_TDI; else - tapc_dataport &= ~JTAG_TMS; + data_port_value &= ~JTAG_TDI; - /* outb(tapc_dataport, DATA_PORT); */ - gpio_write(DATA_PORT,&tapc_dataport); + if (data_port_value != current_value) + gpio_write(DATA_PORT, data_port_value); } -/* Set TCK. */ static void -tapc_set_tck (char x) +tapc_set_TCK (Bit x) { - /* active low. The clock is active low. */ - if(!x) - tapc_controlport |= JTAG_TCK; + /* The clock is active low. */ + if (x) + control_port_value &= ~JTAG_TCK; else - tapc_controlport &= ~JTAG_TCK; - - if(rw_flag == READ_FROM_AA3) - tapc_controlport |= 0x4; - else - tapc_controlport &= ~(0x4); - - /* outb(tapc_controlport, CONTROL_PORT); */ - gpio_write(CONTROL_PORT,&tapc_controlport); + control_port_value |= JTAG_TCK; + + gpio_write(CONTROL_PORT, control_port_value); } +/* -------------------------------------------------------------------------- */ +/* 3) JTAG state machine handlers */ +/* -------------------------------------------------------------------------- */ + +#ifdef STATE_MACHINE_DEBUG +/* Debug function. Perform a state change to the given state. */ + static void -tapc_set_tdi (char x) +set_state (JTAG_ControllerState new_state) { - if(x) - tapc_dataport |= JTAG_TDI; - else - tapc_dataport &= ~JTAG_TDI; - - /* outb(tapc_dataport, DATA_PORT); */ - gpio_write(DATA_PORT,&tapc_dataport); + DEBUG("TAPC state: %s ====> %s\n", + JTAG_ControllerState_Image(current_state), + JTAG_ControllerState_Image(new_state)); + + current_state = new_state; + + gdb_assert(current_state != UNDEFINED); +} + + +/* Debug function. Perform a state change from the current state according to + the transition specified by the given bit. */ + +static void +next_state (Bit x) +{ + current_state = (transitions[current_state][x]); } -/* Unused function clockdelay. Why not add a command - that allows the user to set the clock delay ? */ + +/* Debug function. Perform a state change from the current state according to + the transition specified by the given bit, and check that the new state is + as expected. */ static void -clockdelay (void) +change_state (Bit x, JTAG_ControllerState new_state) { - int i; - //for (i=0; i<10; ++i) - - // usleep(0); + tapc_TMS(x); + + gdb_assert(current_state == new_state); } +#endif /* STATE_MACHINE_DEBUG */ + + +/* Clock the JTAG on the ARC platform. */ -/* Clock the JTAG on the ARC platform. */ static void tapc_pulse (void) { - /* Control 0 bit is active low */ - unsigned char temp; - assert( (tapc_controlport & JTAG_TCK) ); // clock should be zero on entry - - /* outb(tapc_controlport & ~JTAG_TCK, CONTROL_PORT); */ - temp = tapc_controlport & ~JTAG_TCK; - gpio_write(CONTROL_PORT,&temp); - /* outb(tapc_controlport, CONTROL_PORT); */ - gpio_write(CONTROL_PORT,&tapc_controlport); - + /* TCK control bit is active low. */ + gdb_assert((control_port_value & JTAG_TCK) != (Byte) 0); /* Clock should be zero on entry. */ + + gpio_write(CONTROL_PORT, control_port_value & ~JTAG_TCK); + gpio_write(CONTROL_PORT, control_port_value); } -/* All the JTAG state machine handlers. */ -/* Reset the TAPC controller on the JTAG. - */ +/* Reset the TAP Controller on the JTAG. */ + static void tapc_reset (void) { - /* from any state, these many ones should get us into "test-logic reset" - */ - tapc_set_tms(1); - tapc_set_tck(0); /* want rising edge */ + ENTERMSG; + + /* The Test Clock signal is active low (i.e. the signal is active when the + corresponding bit written to the control bit is 0; so initialize the bit + in the control port value to 1 so that the signal is initially not active. */ + control_port_value = (Byte) JTAG_TCK; + data_port_value = (Byte) 0; + + /* From any state, this many TCK pulses should get the controller into state + Test-Logic-Reset. */ + tapc_set_TMS(1); + tapc_set_TCK(0); /* We want the rising edge. */ tapc_pulse(); tapc_pulse(); tapc_pulse(); tapc_pulse(); tapc_pulse(); tapc_pulse(); + + SET_STATE(TEST_LOGIC_RESET); + + DEBUG("TAPC has been reset\n"); + + /* The reset has re-initialized all the JTAG registers. */ + address_register_contents_known = FALSE; + command_register_contents_known = FALSE; + + CHANGE_STATE(0, RUN_TEST_IDLE); + + LEAVEMSG; } -/* Set the tms to the value of the bit and - clock the jtag. */ + +/* Set the TMS to the value of the bit and clock the JTAG. + This will cause the TAP Controller State Machine to move to another state. */ static void -tapc_tms (char x) +tapc_TMS (Bit x) { - tapc_set_tms(x); + tapc_set_TMS(x); tapc_pulse(); + NEXT_STATE(x); } -/* Read bit from the TDO of the JTAG. */ -static char -tapc_readbit (void) + +/* Read a bit from the TDO of the JTAG. */ + +static Bit +tapc_readTDO (void) { - if(gpio_read(STATUS_PORT) & JTAG_TDO) - return 1; - else - return 0; + Byte byte = gpio_read(STATUS_PORT); + + /* Read from the status port. */ + return ((byte & JTAG_TDO) != (Byte) 0) ? (Bit) 1 : (Bit) 0; } -/* Interface functions that use the below mentioned - JTAG state machine handler functions. -*/ +/* -------------------------------------------------------------------------- */ +/* 4) interface functions that use the JTAG state machine handler functions */ +/* -------------------------------------------------------------------------- */ -/* Shift one bit out on the JTAG TDI. */ -static char -tapc_shiftbit (char x) -{ - char read; +/* Shift one bit out on the JTAG TDO and one bit in on the JTAG TDI. */ - //printf("tapc_shiftbit: Shifted %d\n", x); +static Bit +tapc_shift_bit (Bit out) +{ + Bit in = tapc_readTDO(); - read = tapc_readbit(); - tapc_set_tdi(x); + tapc_set_TDI(out); tapc_pulse(); - return read; +// DEBUG("%u (out) >>> %u (in)\n", out, in); + + return in; } -/* - * Shift N bits from to_write into TDI, and out from TDO into read. - * - * If msb_first=0, shift LSB first, starting from to_write[0], to_write[1], - * etc. - - * If msb_first=1, shift to_write[-1] MSB first, then to_write[-2] etc. - * - * Must be called in Shift DR/IR state. - * Leaves in Exit1 DR/IR state. - */ +/* Shift N bits from to_write into TDI, and out from TDO into to_read. + + If order == MSB_LAST, shift LSB first, starting from to_write[0], to_write[1], + etc. + + If order == MSB_FIRST, shift to_write[N-1] MSB first, then to_write[N-2] etc. + + Must be called in Shift DR/IR state. + Leaves in Exit1-DR/IR state. */ + static void -tapc_shiftnbits (int n, - unsigned char *to_write, - unsigned char *read, - char msb_first) +tapc_shift_N_bits (unsigned int n, + Byte *to_write, + Byte *to_read, + Order order) { - unsigned char outbyte, inbyte; - int nbytes = (n-1)/8 + 1,limit=8; - int i, j; + unsigned int nbytes = (n - 1) / BITS_IN_BYTE + 1; + unsigned int nbits = BITS_IN_BYTE; + unsigned int i, j; + + ENTERARGS("shift %u bits", n); - for(i=0; i < nbytes; ++i) + STATE_IS_EITHER(SHIFT_DR, SHIFT_IR); + + for (i = 0; i < nbytes; i++) { - if(msb_first) - outbyte = to_write[-1-i]; - else - outbyte = to_write[i]; - - inbyte = 0; - /* should write a maximum of 8 bits */ - if(i == nbytes-1) - limit = ((n-1) % 8) + 1; - - for(j = 0; j < limit; ++j) - { - unsigned char outbit, inbit; - - if(msb_first) - outbit = !!(outbyte & 0x80); - else - outbit = outbyte & 1; - /* the last bit of the last byte */ - /* transition to EXIT-1 state before last bit */ - if((i == nbytes-1)&&(j == limit-1)) - tapc_set_tms(1); - - inbit = tapc_shiftbit(outbit); - - if(msb_first) - { - inbyte |= (inbit << (7-j)); - outbyte <<= 1; - } - else - { - inbyte |= inbit << j; - outbyte >>= 1; - } - } - - //tapc_tms(1); - if(msb_first) - read[-1-i] = inbyte; - else - read[i] = inbyte; + Boolean is_last_byte = (i == nbytes - 1); + Byte inbyte = (Byte) 0; + Byte outbyte; + + if (order == MSB_FIRST) + outbyte = to_write[nbytes - 1 - i]; + else + outbyte = to_write[i]; + + if (is_last_byte) + { + /* How many significant bits are in this byte? */ + nbits = ((n - 1) % BITS_IN_BYTE) + 1; + } + + /* gdb_assert (nbits <= BITS_IN_BYTE); */ + + for (j = 0; j < nbits; j++) + { + Bit outbit, inbit; + +// DEBUG("byte %u, bit %u\n", i, j); + + /* Get the next bit to be output from the current byte. */ + if (order == MSB_FIRST) + { + /* Get MSB from byte. */ + outbit = (Bit) ((outbyte >> BITS_IN_BYTE) & 1); + outbyte <<= 1; + } + else + { + /* Get LSB from byte. */ + outbit = (Bit) (outbyte & 1); + outbyte >>= 1; + } + + /* The last bit of the last byte. */ + if (is_last_byte && (j == nbits - 1)) + { + /* Change to Exit1-DR/IR state before the last bit is shifted: + this is necessary because the TAP Controller performs the + last sample of TDI when exiting the Shift-DR/IR state. */ + tapc_set_TMS(1); + NEXT_STATE(1); + } + + /* Shift one bit in from the JTAG TDO and one bit out to the JTAG TDI. */ + inbit = tapc_shift_bit(outbit); + + /* Add the bit read into the input byte. + N.B. the shift amount will always be positive, as 0 <= j < BITS_IN_BYTE */ + if (order == MSB_FIRST) + inbyte |= (Byte) (inbit << (BITS_IN_BYTE - 1 - j)); + else + inbyte |= (Byte) (inbit << j); + } + + if (order == MSB_FIRST) + to_read[nbytes - 1 - i] = inbyte; + else + to_read[i] = inbyte; } -} + STATE_IS_EITHER(EXIT1_DR, EXIT1_IR); -/* Read the JTAG status register. This indicates - the status of the JTAG for the user. -*/ -static unsigned int -read_jtag_status_reg (void) -{ - unsigned int wr, rd; - int x; - //rw_flag=0; - //tapc_tms(0); // runtest/idle - tapc_tms(1); // select dr - tapc_tms(1); // select ir - tapc_tms(0); // capture ir - tapc_tms(0); // shift ir - - wr = 0x8; // IR = status register - - tapc_shiftnbits(4, (unsigned char *)&wr, (unsigned char*)&rd, 0); - - - - // goto shift DR - tapc_tms(1); // update ir - //tapc_tms(0); // runtest/idle - tapc_tms(1); // select dr - tapc_tms(0); // capture dr - tapc_tms(0); // shift dr - - rd = 0; - - // read 1 bit, if it is zero then keep reading - rd = tapc_shiftbit(0); - if (rd) - return rd; - - rd |= tapc_shiftbit(0) << 1; - if (rd) - return rd; - - rd |= tapc_shiftbit(0) << 2; - - /* the last bit is optional */ - /*rd |= tapc_shiftbit(0) << 3;*/ - - return rd; + LEAVEMSG; } -/* Interpret the status message. */ -static void -print_jtag_status_reg_val (unsigned int status) +/* Read the JTAG Status Register. + This indicates the status of the JTAG transaction that has been attempted. */ + +static JTAG_TransactionStatus +read_jtag_status_register( void) { - int i ; - char * messages [] = { "Stalled" , "Failure", "Ready", "PC Selected" }; - for(i=0;i<=3;i++) - { - printf_filtered("%s %s \t",(status & 1)?"":"Not",messages[i]); - status = status >> 1; - } - printf_filtered("\n"); + JTAG_RegisterContents rd, wr; + Bit bit; + + ENTERMSG; + + STATE_IS_EITHER(RUN_TEST_IDLE, UPDATE_DR); + + CHANGE_STATE(1, SELECT_DR_SCAN); + CHANGE_STATE(1, SELECT_IR_SCAN); + CHANGE_STATE(0, CAPTURE_IR); + CHANGE_STATE(0, SHIFT_IR); + + wr = JTAG_STATUS_REGISTER; + + tapc_shift_N_bits(BITS_IN_REGISTER_CODE, (Byte*) &wr, (Byte*) &rd, MSB_LAST); + + CHANGE_STATE(1, UPDATE_IR); +// CHANGE_STATE(0, RUN_TEST_IDLE); + CHANGE_STATE(1, SELECT_DR_SCAN); + CHANGE_STATE(0, CAPTURE_DR); + CHANGE_STATE(0, SHIFT_DR); + + /* The JTAG Status Register is read-only, and any bits shifted in are + ignored - hence the parameter to tapc_shift_bit is irrelevant here. */ + + /* Read Stalled bit; if it is zero then keep reading. */ + bit = tapc_shift_bit(0); + if (bit) + return STALLED; + + /* Read Failed bit; if it is zero then keep reading. */ + bit = tapc_shift_bit(0); + if (bit) + return FAILURE; + + /* Read Ready bit. */ + bit = tapc_shift_bit(0); + if (bit) + return READY; + + /* The last bit (PC_SEL) is optional. */ + + return NOT_READY; } +/* Write a value to a JTAG register. + enter in Update-DR/IR state or Run-Test/Idle. + exit in Update-DR */ -/* Write a JTAG Command to a JTAG register. - enter in update dr/ir state - or Test-Logic-Reset. - exit in update dr -*/ static void -write_jtag_reg (char regnum, unsigned int data, int ndatabits) +write_jtag_reg (JTAG_Register regnum, + JTAG_RegisterContents data, + unsigned int num_data_bits) { - unsigned int wr=0,rd=0; - rw_flag = WRITE_TO_AA3 ; - // tapc_tms(0); // runtest/idle - tapc_tms(1); // select dr - tapc_tms(1); // select ir - tapc_tms(0); // capture ir - tapc_tms(0); // shift ir - - - tapc_shiftnbits(4, (unsigned char *)®num, (unsigned char *)&rd, 0); - - - tapc_tms(1); // update ir - - tapc_tms(1); // select dr - tapc_tms(0); // capture dr - tapc_tms(0); // shift dr - - tapc_shiftnbits(ndatabits, (unsigned char *)&data, - (unsigned char *)&rd, 0); - tapc_tms(1); // update dr + Byte num = (Byte) regnum; + JTAG_RegisterContents rd = 0; + + ENTERARGS("regnum %d <== 0x%08X (:%d)", regnum, data, num_data_bits); + + STATE_IS_ONE_OF(UPDATE_DR, UPDATE_IR, RUN_TEST_IDLE); + +// CHANGE_STATE(0, RUN_TEST_IDLE); + CHANGE_STATE(1, SELECT_DR_SCAN); + CHANGE_STATE(1, SELECT_IR_SCAN); + CHANGE_STATE(0, CAPTURE_IR); + CHANGE_STATE(0, SHIFT_IR); + + tapc_shift_N_bits(BITS_IN_REGISTER_CODE, &num, (Byte*) &rd, MSB_LAST); + + CHANGE_STATE(1, UPDATE_IR); + CHANGE_STATE(1, SELECT_DR_SCAN); + CHANGE_STATE(0, CAPTURE_DR); + CHANGE_STATE(0, SHIFT_DR); + + tapc_shift_N_bits(num_data_bits, (Byte*) &data, (Byte*) &rd, MSB_LAST); + + CHANGE_STATE(1, UPDATE_DR); + + DEBUG("written 0x%08X to JTAG %s register\n", data, JTAG_register_name(regnum)); + + LEAVEMSG; } - -// enter in update dr/ir state -// exit in update dr -static unsigned int -read_jtag_reg (char regnum, int ndatabits) + +/* Read a value from a JTAG register. + enter in Update-DR/IR state + exit in Update-DR */ + +static JTAG_RegisterContents +read_jtag_reg (JTAG_Register regnum, unsigned int num_data_bits) { - unsigned int wr=0x0,rd=0; - rw_flag = READ_FROM_AA3; - // tapc_tms(0); // runtest/idle - tapc_tms(1); // select dr - tapc_tms(1); // select ir - tapc_tms(0); // capture ir - tapc_tms(0); // shift ir - - tapc_shiftnbits(4, (unsigned char *)®num, (unsigned char *)&rd, 0); - tapc_tms(1); // update ir - - /* JTAG registers can be read without going to run-test/idle state. - - Doing tapc_tms(0) will take us to run-test/idle state. - This will make JTAG perform the transaction as per TCR. - We dont want this. - */ - // tapc_tms(0); // runtest/idle - tapc_tms(1); // select dr - tapc_tms(0); // capture dr - tapc_tms(0); // shift dr - - tapc_shiftnbits(ndatabits, (unsigned char *)&wr, (unsigned char *)&rd, 0); - tapc_tms(1); // update dr + Byte num = (Byte) regnum; + JTAG_RegisterContents wr = 0, rd = 0; + + ENTERARGS("regnum %u, %u bits", regnum, num_data_bits); + + STATE_IS_EITHER(UPDATE_DR, UPDATE_IR); + +// CHANGE_STATE(0, RUN_TEST_IDLE); + CHANGE_STATE(1, SELECT_DR_SCAN); + CHANGE_STATE(1, SELECT_IR_SCAN); + CHANGE_STATE(0, CAPTURE_IR); + CHANGE_STATE(0, SHIFT_IR); + + tapc_shift_N_bits(BITS_IN_REGISTER_CODE, &num, (Byte*) &rd, MSB_LAST); + + CHANGE_STATE(1, UPDATE_IR); + + /* JTAG registers can be read without going to Run-Test/Idle state. + + Doing CHANGE_STATE(0) would take us to Run-Test/Idle state. This would + make JTAG perform the transaction in the Transaction Command Register. + We don't want that! */ +// CHANGE_STATE(0, RUN_TEST_IDLE); + CHANGE_STATE(1, SELECT_DR_SCAN); + CHANGE_STATE(0, CAPTURE_DR); + CHANGE_STATE(0, SHIFT_DR); + + tapc_shift_N_bits(num_data_bits, (Byte*) &wr, (Byte*) &rd, MSB_LAST); + CHANGE_STATE(1, UPDATE_DR); + + DEBUG("read 0x%08X from JTAG %s register\n", rd, JTAG_register_name(regnum)); return rd; } +/* -------------------------------------------------------------------------- */ +/* 5) JTAG transaction functions */ +/* -------------------------------------------------------------------------- */ - - - +/* Start a JTAG transaction. + Parameters: + command: the JTAG command to be performed + address: the address (memory address or register number) for the command +*/ -static int -arc_jtag_read_core_reg (unsigned int regnum, unsigned int *readbuf) +static void +start_jtag_transaction (JTAG_RegisterContents command, + JTAG_RegisterContents address) { - unsigned int rd, wr, data, i; - check_and_raise_error_if_jtag_closed(); - tapc_reset(); - tapc_tms(0);//run-test idle - write_jtag_reg(0xA, regnum, 32);//update dr + ENTERARGS("command = %d, address = 0x%x", command, address); - + STATE_IS_ONE_OF(UPDATE_DR, UPDATE_IR, RUN_TEST_IDLE); - // Setup instruction register to 0x9 indicating - // a JTAG instruction is being downloaded. - // jtag transaction command reg = 0x5 (read core reg) - write_jtag_reg(0x9, 0x5, 4);//update dr + /* N.B. do NOT reset the TAP Controller at the start of each transaction, as + that would re-initialize the JTAG registers to their default values + (whatever those might be), so invalidating the optimisation of the + use of the Address and Transaction Command registers performed by + this module. */ - /* Perform the transaction. - */ - tapc_tms(0); // run-test idle - - // poll the status + if (command == READ_MEMORY_LOCATION || command == WRITE_MEMORY_LOCATION) + { + gdb_assert(IS_WORD_ALIGNED(address)); + } - for (i=0;i<arcjtag_retry_count;i++) + /* Load the command that is required into the JTAG Transaction Command + Register, and the address into the JTAG Address Register; by keeping + track of the values that these registers contain, we can avoid re-loading + them unnecessarily, which can save time when transferring a stream of data. */ + + if (!command_register_contents_known || + (command_register_contents != command)) { - unsigned int status = read_jtag_status_reg(); - //if( !(status & 1) && (status & 4) ) - if(status == 4) - break; - if(status == 2) - return JTAG_READ_FAILURE; - tapc_tms(1);//exit1-dr - tapc_tms(1);//update-dr - /* Do not redo the transaction. Pause and re-try. - */ - //tapc_tms(0);//run-test-idle + write_jtag_reg(JTAG_TRANSACTION_COMMAND_REGISTER, command, BITS_IN_COMMAND_CODE); + + command_register_contents_known = TRUE; + command_register_contents = command; } - if (i==arcjtag_retry_count) - return JTAG_READ_FAILURE; - - /* JTAG status register leaves us in Shift DR */ - tapc_tms(1); /* Move to Exit-1 DR */ - tapc_tms(1); /* Move to Update DR */ - - // data = jtag data reg - data = read_jtag_reg(0xB, 32); - - *readbuf = data; - - return sizeof(data); + + if (!address_register_contents_known || + (address_register_contents != address)) + { + write_jtag_reg(JTAG_ADDRESS_REGISTER, address, BITS_IN_WORD); + + address_register_contents_known = TRUE; + address_register_contents = address; + } + + LEAVEMSG; } -static int -arc_jtag_write_core_reg (unsigned int regnum, unsigned int data) -{ - unsigned int rd, wr, i; - check_and_raise_error_if_jtag_closed(); - tapc_reset(); - tapc_tms(0);//run-test idle +/* Perform the given JTAG transaction (a mmeory or register operation). + If the transaction fails, this function returns the given error. */ - // data = jtag data reg - write_jtag_reg(0xB, data, 32);//update dr +static JTAG_OperationStatus +perform_jtag_transaction (JTAG_TransactionType transaction, + JTAG_OperationStatus error) +{ + JTAG_OperationStatus result = error; + unsigned int tries = 0; - // jtag addr register = regnum: - write_jtag_reg(0xA, regnum, 32);//update dr - - + ENTERARGS("transaction: %u", transaction); - // jtag transaction command reg = 0x1(write core reg) - write_jtag_reg(0x9, 0x1, 4);//update dr + /* This causes the TAP Controller to perform the transaction, according to + the contents of the JTAG Transaction Command, Address and Data registers. */ + CHANGE_STATE(0, RUN_TEST_IDLE); - /* Perform the transaction. - */ - tapc_tms(0); // run-test idle - - for (i=0;i<arcjtag_retry_count;i++) + /* Poll the JTAG Status Register. */ + do { - unsigned int status = read_jtag_status_reg(); - if(status == 4) - break; - if(status == 2) - return JTAG_WRITE_FAILURE; - tapc_tms(1);//exit1-dr - tapc_tms(1);//update-dr - /* Do not redo the transaction. Pause and re-try. - */ - //tapc_tms(0);//run-test-idle + JTAG_TransactionStatus status = read_jtag_status_register(); + + DEBUG("status: %s\n", JTAG_TransactionStatus_Image(status)); + + /* The read has left the TAP Controller FSM in state Shift-DR. */ + STATE_IS(SHIFT_DR); + + CHANGE_STATE(1, EXIT1_DR); + CHANGE_STATE(1, UPDATE_DR); + + /* If the transaction is complete. */ + if (status == READY) + { + /* The value in the JTAG Address Register is incremented by four + (a memory access) or one (a register access) when a read/write + transaction has completed. */ + address_register_contents += (transaction == Memory) ? BYTES_IN_WORD : 1; + + result = JTAG_SUCCESS; + break; + } + + if (status == FAILURE) + break; + + /* Pause and re-try. */ + usleep(1); } - if (i==arcjtag_retry_count) - return JTAG_READ_FAILURE; - - tapc_tms(1);//exit1-dr - tapc_tms(1);//update-dr - /* This should have been done earlier i.e. before - reading the JTAG status register. - */ - //tapc_tms(0);//rn-test-idle - return sizeof(data); + while (++tries <= arc_jtag_ops.retry_count); + + LEAVEMSG; + return result; } -static int -arc_jtag_read_aux_reg (unsigned int regnum, unsigned int *readbuf) -{ - unsigned int rd, wr, data, i; - if(arc_jtag_ops.arc_jtag_state_machine_debug) - printf_filtered("\nEntered arc_jtag_read_aux_reg()\ - \n Regnum:%d \n",regnum); - check_and_raise_error_if_jtag_closed(); - check_and_raise_error_if_jtag_closed(); - tapc_reset(); - tapc_tms(0);//run-test idle - write_jtag_reg(0xA, regnum, 32);//update dr +/* -------------------------------------------------------------------------- */ +/* 6) read/write helper functions */ +/* -------------------------------------------------------------------------- */ +/* These functions aid in reading/writing registers/memory. */ - // Setup instruction register to 0x9 indicating - // a JTAG instruction is being downloaded. - // jtag transaction command reg = 0x6 (read aux reg) - write_jtag_reg(0x9, 0x6, 4);//update dr +/* Read a processor register (core or auxiliary). */ - /* Perform the transaction. - */ - tapc_tms(0); // run-test idle +static JTAG_OperationStatus +read_processor_register (ARC_RegisterNumber regnum, + JTAG_RegisterContents command, + ARC_RegisterContents *contents) +{ + JTAG_OperationStatus status; - // poll the status + /* Load the number of the register that is to be read into the JTAG Address + Register. */ + start_jtag_transaction(command, regnum); - for (i=0;i<arcjtag_retry_count;i++) - { - unsigned int status = read_jtag_status_reg(); - - if(status == 4) - break; - if(status == 2) - return JTAG_READ_FAILURE; - tapc_tms(1);//exit1-dr - tapc_tms(1);//update-dr - /* Do not redo the transaction. Pause and re-try. - */ - //tapc_tms(0);//run-test-idle + status = perform_jtag_transaction(Register, JTAG_READ_FAILURE); + if (status == JTAG_SUCCESS) + { + /* Read the register contents from the JTAG Data Register. */ + *contents = (ARC_RegisterContents) read_jtag_reg(JTAG_DATA_REGISTER, BITS_IN_REGISTER); } - if (i==arcjtag_retry_count) - return JTAG_READ_FAILURE; - - -/* JTAG status register leaves us in Shift DR */ - tapc_tms(1); /* Move to Exit-1 IR */ - tapc_tms(1); /* Move to Update IR */ - - // data = jtag data reg - data = read_jtag_reg(0xB, 32); - *readbuf = data; - if(arc_jtag_ops.arc_jtag_state_machine_debug) - printf(" Data: %d\n",data); - return sizeof(data); + + return status; } -static int -arc_jtag_write_aux_reg (unsigned int regnum, unsigned int data) + +/* Write a processor register (core or auxiliary). */ + +static JTAG_OperationStatus +write_processor_register (ARC_RegisterNumber regnum, + JTAG_RegisterContents command, + ARC_RegisterContents contents) { - unsigned int rd, wr, i; - if(arc_jtag_ops.arc_jtag_state_machine_debug) - printf_filtered("\nEntered arc_jtag_write_aux_reg()\n Regnum:%d\nData:%d\n",regnum,data); - check_and_raise_error_if_jtag_closed(); - tapc_reset(); - tapc_tms(0);//run-test idle - - // data = jtag data reg - write_jtag_reg(0xB, data, 32);//update dr + /* Load the number of the register that is to be written into the JTAG + Address Register. */ + start_jtag_transaction(command, regnum); + + /* Load the new register contents into the JTAG Data Register. */ + write_jtag_reg(JTAG_DATA_REGISTER, contents, BITS_IN_REGISTER); - // jtag addr register = regnum: - write_jtag_reg(0xA, regnum, 32);//update dr - - + return perform_jtag_transaction(Register, JTAG_WRITE_FAILURE); +} - // jtag transaction command reg = 0x2 (write aux reg) - write_jtag_reg(0x9, 0x2, 4);//update dr - /* Perform the transaction. - */ - tapc_tms(0); // run-test idle +/* Write a processor core register. */ - for (i=0;i<arcjtag_retry_count;i++) - { - unsigned int status = read_jtag_status_reg(); - if(status == 4) - break; - if(status == 2) - return JTAG_WRITE_FAILURE; - tapc_tms(1);//exit1-dr - tapc_tms(1);//update-dr - /* Do not redo the transaction. Pause and re-try. - */ - //tapc_tms(0);//run-test-idle - } - if (i==arcjtag_retry_count) - return JTAG_READ_FAILURE; - - tapc_tms(1);//exit1-dr - tapc_tms(1);//update-dr - /* This should have been done earlier i.e. before - reading the JTAG status register. - */ - //tapc_tms(0);//run-test-idle - return sizeof(data); +static JTAG_OperationStatus +jtag_write_core_reg (ARC_RegisterNumber regnum, ARC_RegisterContents contents) +{ + ENTERARGS("regnum %d", regnum); + + return write_processor_register(regnum, WRITE_CORE_REGISTER, contents); } -static int -read_mem (unsigned int addr, unsigned int *readbuf) +/* Read a processor auxiliary register. */ + +static JTAG_OperationStatus +jtag_read_aux_reg (ARC_RegisterNumber regnum, ARC_RegisterContents *contents) { - unsigned int rd, wr, data, i; + ENTERARGS("regnum %d", regnum); + + return read_processor_register(regnum, READ_AUX_REGISTER, contents); +} - rw_flag = READ_FROM_AA3; - if(arc_jtag_ops.arc_jtag_state_machine_debug) - printf_filtered("\nEntered read_mem() 0x%x\n",addr); - // jtag addr register = regnum: - tapc_reset(); - tapc_tms(0);//run-test idle - write_jtag_reg(0xA, addr, 32);//update dr - +/* Write a processor auxiliary register. */ - // Setup instruction register to 0x9 indicating - // a JTAG instruction is being downloaded. - // jtag transaction command reg = 0x5 (read core reg) - write_jtag_reg(0x9, 0x4, 4);//update dr +static JTAG_OperationStatus +jtag_write_aux_reg (ARC_RegisterNumber regnum, ARC_RegisterContents contents) +{ + ENTERARGS("regnum %d", regnum); + + return write_processor_register(regnum, WRITE_AUX_REGISTER, contents); +} + + +/* Read a word of data from memory; the given address must be word-aligned. + Returns number of bytes read. */ + +static unsigned int +jtag_read_word (ARC_Address addr, ARC_Word *data) +{ + JTAG_OperationStatus status; + + ENTERARGS("addr 0x%08X", addr); - /* Perform the transaction. - */ - tapc_tms(0); // run-test idle + gdb_assert(IS_WORD_ALIGNED(addr)); - // poll the status + /* Load the address of the memory word that is to be read into the JTAG + Address Register. */ + start_jtag_transaction(READ_MEMORY_LOCATION, addr); - for (i=0;i<arcjtag_retry_count;i++) + status = perform_jtag_transaction(Memory, JTAG_READ_FAILURE); + + if (status == JTAG_SUCCESS) { - unsigned int status = read_jtag_status_reg(); - if(status == 4) - break; - if(status == 2) - return JTAG_READ_FAILURE; - tapc_tms(1);//exit1-dr - tapc_tms(1);//update-dr - /* Do not redo the transaction. Pause and re-try. - */ - //tapc_tms(0); // run-test-idle + /* Read the data from the JTAG Data Register. */ + ARC_Word word = (ARC_Word) read_jtag_reg(JTAG_DATA_REGISTER, BITS_IN_WORD); + + /* N.B. this assumes that the host is little-endian! */ + if (target_is_big_endian) + word = __bswap_32(word); + + DEBUG("read 0x%08X\n", word); + + *data = word; + + return BYTES_IN_WORD; } - if (i==arcjtag_retry_count) - return JTAG_READ_FAILURE; - - /* JTAG status register leaves us in Shift DR */ - tapc_tms(1); /* Move to Exit-1 DR */ - tapc_tms(1); /* Move to Update DR */ - - // data = jtag data reg - data = read_jtag_reg(0xB, 32); - *readbuf = data; - if(arc_jtag_ops.arc_jtag_state_machine_debug) - printf_filtered("\n Read rd =0x%x in read_mem",data); - - return sizeof(data); + + /* Failed: no data read. */ + return 0; } -static int -write_mem (unsigned int addr, unsigned int data) +/* Write a word of data to memory; the given address must be word-aligned. + Returns number of bytes written. */ + +static unsigned int +jtag_write_word (ARC_Address addr, ARC_Word data) { - unsigned int rd, wr, i; - if(arc_jtag_ops.arc_jtag_state_machine_debug) - printf_filtered("\nEntered write_mem() to write 0x%x at 0x%x\n",data,addr); - tapc_reset(); - tapc_tms(0);//run-test idle - - // data = jtag data reg - write_jtag_reg(0xB, data, 32);//update dr + ENTERARGS("addr 0x%08X, data 0x%08X", addr, data); - // jtag addr register = regnum: - write_jtag_reg(0xA, addr, 32);//update dr - - + gdb_assert(IS_WORD_ALIGNED(addr)); - // jtag transaction command reg = 0x0(write mem) - write_jtag_reg(0x9, 0x0, 4);//update dr + /* Load the address of the memory word that is to be written into the JTAG + Address Register. */ + start_jtag_transaction(WRITE_MEMORY_LOCATION, addr); - /* Perform the transaction. - */ - tapc_tms(0); // run-test idle + /* N.B. this assumes that the host is little-endian! */ + if (target_is_big_endian) + data = __bswap_32(data); - for (i=0;i<arcjtag_retry_count;i++) - { - unsigned int status = read_jtag_status_reg(); - if(status == 4) - break; - if(status == 2) - return JTAG_WRITE_FAILURE; - tapc_tms(1);//exit1-dr - tapc_tms(1);//update-dr - /* Do not redo the transaction. Pause and re-try. - */ - //tapc_tms(0);//run-tes-idle - } - if (i==arcjtag_retry_count) - return JTAG_READ_FAILURE; - - tapc_tms(1);//exit1-dr - tapc_tms(1);//update-dr - /* This should have been done earlier i.e. before - reading the JTAG status register. - */ - //tapc_tms(0);//run-test-idle - return sizeof(data); + /* Load the data to be written into the JTAG Data Register. */ + write_jtag_reg(JTAG_DATA_REGISTER, data, BITS_IN_WORD); + + if (perform_jtag_transaction(Memory, JTAG_WRITE_FAILURE) == JTAG_SUCCESS) + return BYTES_IN_WORD; + + /* Failed: no data written. */ + return 0; } -static int -arc_jtag_write_chunk (unsigned int addr, unsigned int *write_buf, int len) +/* Read a number of words of data from target memory starting at the given address. + Returns number of bytes read. */ + +static unsigned int +jtag_read_chunk (ARC_Address address, ARC_Byte *data, unsigned int words) { - unsigned int rd, wr; - unsigned int status; - check_and_raise_error_if_jtag_closed(); - - rw_flag = WRITE_TO_AA3 ; - int i = 0; - int len_mod = len % 4; - len = len - len_mod; - - tapc_reset(); - if(arc_jtag_ops.arc_jtag_state_machine_debug) - printf_filtered("Entered arc_jtag_write_chunk()......0x%x\n",*write_buf); - tapc_tms(0);//run-test idle - - // jtag addr register = regnum: - write_jtag_reg(0xA, addr, 32);//update dr - - - - // jtag transaction command reg = 0x0(write mem) - write_jtag_reg(0x9, 0x0, 4);//update dr - - while(len) - { - - // data = jtag data reg - write_jtag_reg(0xB, write_buf[i++], 32);//update dr - - /* Perform the transaction. - */ - tapc_tms(0); // run-test idle - - - while(1) - { - status = read_jtag_status_reg(); - if(status == 4) - break; - if(status == 2) - return (i-1) * 4; - - tapc_tms(1);//exit1-dr - tapc_tms(1);//update-dr - /* Do not redo the transaction. Pause and re-try. - */ - //tapc_tms(0);//run-tes-idle - } - - tapc_tms(1);//exit1-dr - tapc_tms(1);//update-dr - /* This should have been done earlier i.e. before - reading the JTAG status register. - */ - //tapc_tms(0);//run-test idle - len = len - 4; - } + unsigned int total_read = 0; + + ENTERARGS("address 0x%08X, words %u", address, words); - if(arc_jtag_ops.arc_jtag_state_machine_debug) - printf_filtered("leaving arc_jtag_write_chunk() with return value %d",i*4); - - // added support for writing no of bytes those are not exact mutiple of four - - if(!len_mod) - return i*4; - else + /* Load the start address of the memory chunk that is to be read + into the JTAG Address Register. */ + start_jtag_transaction(READ_MEMORY_LOCATION, address); + + /* Read all the words of data. */ + while (words--) { - - addr=addr+(i*4); - if(read_mem(addr,&rd)==JTAG_READ_FAILURE) - return i*4; - if(arc_jtag_ops.arc_jtag_state_machine_debug) - printf_filtered("\nrd=0x%x and wr=0x%x\n",rd,write_buf[i]); - - switch(len_mod) - { - - case 1: - wr = (rd & 0xffffff00)|(write_buf[i] & 0xff); - break; - - case 2: - wr = (rd & 0xffff0000)|(write_buf[i] & 0xffff); - break; - - case 3: - wr = (rd & 0xff000000)|(write_buf[i] & 0xffffff); - break; - - } - if(arc_jtag_ops.arc_jtag_state_machine_debug) - printf_filtered("\nwrite_mem writing 0x%x at 0x%x",wr,addr); - arc_jtag_write_chunk(addr,&wr,4); - return (i*4 + len_mod); - } - - + ARC_Word word; + + /* Read the next word of data - this increments the address in + the JTAG Address Register by the word size, so the register + does not have to be re-loaded with the next address. */ + if (perform_jtag_transaction(Memory, JTAG_READ_FAILURE) != JTAG_SUCCESS) + { + DEBUG("FAIL: read %u bytes\n", total_read); + + /* Failed - just return amount of data read so far. */ + return total_read; + } + + /* Read the word of data from the JTAG Data Register. */ + word = (ARC_Word) read_jtag_reg(JTAG_DATA_REGISTER, BITS_IN_WORD); + + /* N.B. this assumes that the host is little-endian! */ + if (target_is_big_endian) + word = __bswap_32(word); + + /* Copy it into the buffer (byte-by-byte copy means that alignment does not matter). */ + COPY_WORD(data, &word); + + total_read += BYTES_IN_WORD; + data += BYTES_IN_WORD; + } + + return total_read; } -static int -arc_jtag_read_chunk (unsigned int addr, unsigned int *read_buf, int len) + +/* Write a number of words of data to target memory starting at the given address. + Returns number of bytes written. */ + +static unsigned int +jtag_write_chunk (ARC_Address address, ARC_Byte *data, unsigned int words) { - unsigned int rd, wr, data; - int i=0; - rw_flag = READ_FROM_AA3 ; - int len_mod=len%4; - len=len-len_mod; - if(arc_jtag_ops.arc_jtag_state_machine_debug) - printf_filtered("\nEntered arc_jtag_read_chunk() 0x%x\n",addr); - check_and_raise_error_if_jtag_closed(); - - // jtag addr register = regnum: - tapc_reset(); - tapc_tms(0);//run-test idle - - write_jtag_reg(0xA, addr, 32);//update dr + unsigned int total_written = 0; - - while(len) - { - // Setup instruction register to 0x9 indicating - // a JTAG instruction is being downloaded. - // jtag transaction command reg = 0x5 (read core reg) - write_jtag_reg(0x9, 0x4, 4);//update dr +#ifndef JTAG_DATA_REGISTER_IS_CORRUPTED_BY_WRITE + ARC_Word last_word; - /* Perform the transaction. - */ - tapc_tms(0); // run-test idle + /* Initialise last_word with a value that is different from the + first word to be written. */ + COPY_WORD(&last_word, data); + last_word++; +#endif - // poll the status + ENTERARGS("address 0x%08X, words %u", address, words); - while(1) + /* Load the start address of the memory chunk that is to be written + into the JTAG Address Register. */ + start_jtag_transaction(WRITE_MEMORY_LOCATION, address); + + /* write all the words of data */ + while (words--) { - unsigned int status = read_jtag_status_reg(); - if(status == 4) - break; - if(status == 2) - return (i-1)*4; - tapc_tms(1);//exit1-dr - tapc_tms(1);//update-dr - /* Do not redo the transaction. Pause and re-try. - */ - //tapc_tms(0);//run-test-idle + ARC_Word word; - } + /* Copy the next word of data from the buffer + (byte-by-byte copy means that alignment does not matter). */ + COPY_WORD(&word, data); + + /* N.B. this assumes that the host is little-endian! */ + if (target_is_big_endian) + word = __bswap_32(word); + +#ifndef JTAG_DATA_REGISTER_IS_CORRUPTED_BY_WRITE + if (word != last_word) + { +#endif + write_jtag_reg(JTAG_DATA_REGISTER, word, BITS_IN_WORD); - /* JTAG status register leaves us in Shift DR */ - tapc_tms(1); /* Move to Exit-1 DR */ - tapc_tms(1); /* Move to Update DR */ - - // data = jtag data reg - read_buf[i++] = read_jtag_reg(0xB, 32);// exits in Update DR - len= len-4; - //tapc_tms(0);/* Move to run-test-idle */ +#ifdef CHECK_JTAG_DATA_REGISTER + check_Data_Register(word); +#endif +#ifndef JTAG_DATA_REGISTER_IS_CORRUPTED_BY_WRITE + last_word = word; + } +#endif + + data += BYTES_IN_WORD; + + /* Write the word - this increments the address in the JTAG + Address Register by the word size, so the register does not + have to be re-loaded with the next address. */ + if (perform_jtag_transaction(Memory, JTAG_WRITE_FAILURE) != JTAG_SUCCESS) + { + DEBUG("FAIL: written %u bytes\n", total_written); + + /* Failed - just return amount of data written so far. */ + return total_written; + } + + total_written += BYTES_IN_WORD; } - // added support for reading no of bytes those are not exact mutiple of four + return total_written; +} - if(!len_mod) - return i*4; - else + +/* Write a number of copies of a word-sized pattern of data to memory starting + at the given address. + Returns number of bytes written. */ + +static unsigned int +jtag_write_pattern (ARC_Address address, ARC_Word pattern, unsigned int words) +{ + unsigned int total_written = 0; + + ENTERARGS("address 0x%08X, pattern 0x%08X, words %u", address, pattern, words); + + /* Load the start address of the memory chunk that is to be written + into the JTAG Address Register. */ + start_jtag_transaction(WRITE_MEMORY_LOCATION, address); + + /* N.B. this assumes that the host is little-endian! */ + if (target_is_big_endian) + pattern = __bswap_32(pattern); + +#ifndef JTAG_DATA_REGISTER_IS_CORRUPTED_BY_WRITE + /* Load the pattern into the JTAG Data Register. */ + write_jtag_reg(JTAG_DATA_REGISTER, pattern, BITS_IN_WORD); +#endif + + /* Write all the complete words of data. */ + while (words--) { - char *ptr = (char *)read_buf + i*4; - addr=addr+(i*4); - if(read_mem(addr,&rd)==JTAG_READ_FAILURE) - return i*4; - - switch(len_mod) - { - case 1: - ptr[0] = rd & 0xff; - break; - - case 2: - ptr[0] = rd & 0xff; - ptr[1] = (rd>>8) & 0xff; - break; - - case 3: - ptr[0] = rd & 0xff; - ptr[1] = (rd>>8) & 0xff; - ptr[2] = (rd>>16) & 0xff; - break; - } - - return ((i*4)+len_mod); - +#ifdef JTAG_DATA_REGISTER_IS_CORRUPTED_BY_WRITE + /* Load the pattern into the JTAG Data Register. */ + write_jtag_reg(JTAG_DATA_REGISTER, pattern, BITS_IN_WORD); +#endif + +#ifdef CHECK_JTAG_DATA_REGISTER + check_Data_Register(pattern); +#endif + + /* Write the word - this increments the address in the JTAG + Address Register by the word size, so the register does not + have to be re-loaded with the next address. */ + if (perform_jtag_transaction(Memory, JTAG_WRITE_FAILURE) != JTAG_SUCCESS) + { + DEBUG("FAIL: written %u bytes\n", total_written); + + /* Failed - just return amount of data written so far. */ + return total_written; + } + + total_written += BYTES_IN_WORD; } - + return total_written; } +/* -------------------------------------------------------------------------- */ +/* 7) main operations */ +/* -------------------------------------------------------------------------- */ + +/* These are the functions that are called from outside this module via the + pointers in the arc_jtag_ops global object. + N.B. none of these functions are called from within this module. */ -/* - * Return the Processor Variant that is connected. - */ -int -arc_get_architecture() + +/* Read a processor core register. */ + +static JTAG_OperationStatus +jtag_read_core_reg (ARC_RegisterNumber regnum, ARC_RegisterContents *contents) { - if (ARCProcessor == UNSUPPORTED) { - unsigned int value; - - /* Read the Identity Register. */ - if (arc_jtag_read_aux_reg(4, &value) == JTAG_READ_FAILURE) - error("Failure reading from auxillary IDENTITY register"); - - /* Get Identity Mask. */ - value &= 0xff ; - - if((value >= 0x30) && (value <= 0x3f)) - ARCProcessor = ARC700; - else if((value >= 0x20) && (value <= 0x2f)) - ARCProcessor = ARC600; - else if((value >= 0x10) && (value <= 0x1f)) - ARCProcessor = A5; - else if ((value >= 0x00) && (value <= 0x0f)) - ARCProcessor = A4; - else - error("Unsupported Processor Version 0x%x\n", value); - } - - return ARCProcessor; + ENTERARGS("regnum %d", regnum); + + return read_processor_register(regnum, READ_CORE_REGISTER, contents); } +/* Try to open the JTAG interface. + Returns TRUE for success. */ -static void -arc_jtag_open (void) +static Boolean +jtag_open (ARC_RegisterNumber mem_subsys) { - int retval; - unsigned int read_status = 0; - sigaddset(&block_mask,SIGINT); - retval = gpio_setup(); - if(retval != 0) + ENTERMSG; + + if (arc_jtag_ops.status == JTAG_CLOSED) { - error("Unable to open JTAG Port .%s \n",(retval == EINVAL)? - "Invalid Params":"Permission Denied" ); - arc_jtag_ops.jtag_status = JTAG_CLOSED; - return ; + JTAG_OperationStatus status; + + /* Make sure that the GPIO driver is open. */ + if (!gpio_open()) + return FALSE; + + set_interface(JTAG_OPENED); + + tapc_reset(); + + /* Load the number of the MEMSUBSYS BCR that is to be read into the JTAG + Address Register. */ + start_jtag_transaction(READ_AUX_REGISTER, mem_subsys); + + status = perform_jtag_transaction(Register, JTAG_READ_FAILURE); + + if (status == JTAG_SUCCESS) + { + /* Read the register contents from the JTAG Data Register. */ + ARC_RegisterContents contents = + (ARC_RegisterContents) read_jtag_reg(JTAG_DATA_REGISTER, BITS_IN_REGISTER); + + DEBUG("MEMSUBSYS BCR: 0x%08X\n", contents); + + target_is_big_endian = ((contents & 4) != 0); + } + else + { + warning(_("can not discover endianness of target\n")); + return FALSE; + } + + DEBUG("arcjtag opened\n"); } - arc_jtag_ops.jtag_status = JTAG_OPENED; - tapc_reset(); - //Writing debug bit of debug register - - do - { - /* Note: Reading the status/status32 register here to - check if halt bit is set*/ - if (IS_A4) { - if(arc_jtag_read_aux_reg( 0x0, &read_status) == JTAG_READ_FAILURE) - error("Failure reading auxillary register 0xA\n"); - if(read_status & A4_HALT_VALUE) - break; - } - else { - if(arc_jtag_read_aux_reg( 0xA, &read_status) == JTAG_READ_FAILURE) - error("Failure reading auxillary register 0xA\n"); - if(read_status & ARC700_HALT_VALUE) - break; - } - printf_filtered("Processor running. Trying to halt.....\n"); - if(arc_jtag_write_aux_reg(0x5,0x2)==JTAG_WRITE_FAILURE) - error("Failure writing 0x2 to auxillary register 0x5:debug register\n"); - }while(1); - if (arc_jtag_ops.arc_jtag_state_machine_debug) - printf_filtered("Processor halted.....\n"); + LEAVEMSG; + return TRUE; } -static void -arc_jtag_close (void) + +/* Close the JTAG interface. */ + +static void +jtag_close (void) { - ARCProcessor = UNSUPPORTED; + ENTERMSG; - if(arc_jtag_ops.jtag_status != JTAG_CLOSED) + if (arc_jtag_ops.status == JTAG_OPENED) { - tapc_reset(); - /* closing file descriptor opened for communication with gpio driver */ - close(fd); - if(arc_jtag_ops.arc_jtag_state_machine_debug) - printf_filtered("arc-jtag closed\n"); - arc_jtag_ops.jtag_status = JTAG_CLOSED; + tapc_reset(); + + /* Close the file descriptor opened for communication with gpio driver. */ + gpio_close(); + + set_interface(JTAG_CLOSED); + +#ifdef STATE_MACHINE_DEBUG + current_state = UNDEFINED; +#endif + + DEBUG("arcjtag closed\n"); } + LEAVEMSG; } -static void -arc_jtag_wait(void) + +/* Reset the target JTAG controller. */ + +static void +jtag_reset (void) { - unsigned int read_status; - check_and_raise_error_if_jtag_closed(); - do - { - sigprocmask(SIG_BLOCK,&block_mask, NULL); - - if (IS_A4) { - if(arc_jtag_read_aux_reg( 0x0, &read_status) == JTAG_READ_FAILURE) - error("Failure reading auxillary register 0x0\n"); - //FIXMEA: if(debug_arc_jtag_target_message) - // printf_filtered ("\n Read Status: 0x%x,%d\n", read_status, read_status); - - if(read_status & A4_HALT_VALUE) - { - sigprocmask(SIG_BLOCK,&block_mask, NULL); - break; - } - } - else { - if(arc_jtag_read_aux_reg( 0xA, &read_status) == JTAG_READ_FAILURE) - error("Failure reading auxillary register 0xA\n"); - if(read_status & ARC700_HALT_VALUE) - { - sigprocmask(SIG_BLOCK,&block_mask, NULL); - break; - - } - } - sigprocmask(SIG_UNBLOCK,&block_mask, NULL); - }while(1); - - while (1) + ENTERMSG; + tapc_reset(); + LEAVEMSG; +} + + +/* Reset the target board. */ + +static void +jtag_reset_board (void) +{ + ENTERMSG; + + /* Make sure that the GPIO driver is open. */ + if (gpio_open()) { - if (arc_jtag_read_aux_reg (0x5,&read_status) == JTAG_READ_FAILURE) - error ("Failure reading Debug register \n"); - if (!(read_status & 0x80000000)) - break; + /* Writing 9 did not work. But that's what the manual says. Hmmm. */ +// gpio_write (CONTROL_PORT, 9); + + /* What is this for? */ + gpio_write(CONTROL_PORT, (Byte) JTAG_TCK); + gpio_write(CONTROL_PORT, (Byte) 0xD); + gpio_write(CONTROL_PORT, (Byte) JTAG_TCK); + gpio_write(DATA_PORT, (Byte) 0); + gpio_write(DATA_PORT, (Byte) JTAG_TMS); + gpio_write(DATA_PORT, (Byte) 0); + + tapc_reset(); } + + LEAVEMSG; +} + + +/* Check that the JTAG interface is open. + If it is closed, 'error' is called. */ + +static void jtag_check_open (void) +{ + if (arc_jtag_ops.status == JTAG_CLOSED) + interface_is_closed(); } + +/* -------------------------------------------------------------------------- */ +/* 8) interface management */ +/* -------------------------------------------------------------------------- */ + +/* Report that the JTAG interface is closed. */ + static void -arc_jtag_reset_board (void) +interface_is_closed (void) { - char c = 5; - int auxval = 2 ; - check_and_raise_error_if_jtag_closed (); - - /* - Writing 9 did not work. But thats - what the manual says. Hmmm. - gpio_write (CONTROL_PORT, &c); - */ - - gpio_write ( CONTROL_PORT, &c); - c = 0xd; - gpio_write ( CONTROL_PORT, &c); - c = 5; - gpio_write ( CONTROL_PORT, &c); - - if (arc_jtag_write_aux_reg(0x5 , 2) == JTAG_WRITE_FAILURE) - error ("Failure writing to auxiliary register Debug\n"); - - c = 0; - gpio_write ( 0x378, &c); - c = 0x40; - gpio_write ( 0x378, &c); - c = 0; - gpio_write ( 0x378, &c); - - tapc_reset(); + error(_("JTAG connection is closed. " + "Use command 'target " ARC_TARGET_NAME "' first.")); } +/* Set up the function pointers in the arc_jtag_ops structure according to + whether the JTAG interface is open or closed. Note that if the interface is + closed, all the pointers point to the 'interface_is_closed' function - so any + attempt to invoke one of those operations results in an error; but if the + interface is open, they point to the appropriate operations (which may be + called without incurring the overhead of a check on the interface status). */ + +static void +set_interface (JTAG_Status status) +{ + arc_jtag_ops.status = status; + + if (status == JTAG_OPENED) + { + arc_jtag_ops.memory_read_word = jtag_read_word; + arc_jtag_ops.memory_write_word = jtag_write_word; + arc_jtag_ops.memory_read_chunk = jtag_read_chunk; + arc_jtag_ops.memory_write_chunk = jtag_write_chunk; + arc_jtag_ops.memory_write_pattern = jtag_write_pattern; + arc_jtag_ops.read_aux_reg = jtag_read_aux_reg; + arc_jtag_ops.write_aux_reg = jtag_write_aux_reg; + arc_jtag_ops.read_core_reg = jtag_read_core_reg; + arc_jtag_ops.write_core_reg = jtag_write_core_reg; + arc_jtag_ops.reset = jtag_reset; + } + else + { + typedef unsigned int (*Read_Word) (ARC_Address, ARC_Word*); + typedef unsigned int (*Write_Word) (ARC_Address, ARC_Word); + typedef unsigned int (*Transfer_Chunk) (ARC_Address, ARC_Byte*, unsigned int); + typedef unsigned int (*Write_Pattern) (ARC_Address, ARC_Word, unsigned int); + typedef JTAG_OperationStatus (*Read_Register) (ARC_RegisterNumber, ARC_RegisterContents*); + typedef JTAG_OperationStatus (*Write_Register) (ARC_RegisterNumber, ARC_RegisterContents); + + /* The type casts avoid "assignment from incompatible pointer type" warnings + at compile-time. */ + arc_jtag_ops.memory_read_word = (Read_Word) interface_is_closed; + arc_jtag_ops.memory_write_word = (Write_Word) interface_is_closed; + arc_jtag_ops.memory_read_chunk = (Transfer_Chunk) interface_is_closed; + arc_jtag_ops.memory_write_chunk = (Transfer_Chunk) interface_is_closed; + arc_jtag_ops.memory_write_pattern = (Write_Pattern) interface_is_closed; + arc_jtag_ops.read_aux_reg = (Read_Register) interface_is_closed; + arc_jtag_ops.write_aux_reg = (Write_Register) interface_is_closed; + arc_jtag_ops.read_core_reg = (Read_Register) interface_is_closed; + arc_jtag_ops.write_core_reg = (Write_Register) interface_is_closed; + arc_jtag_ops.reset = interface_is_closed; + } +} + + +/* -------------------------------------------------------------------------- */ +/* externally visible functions */ +/* -------------------------------------------------------------------------- */ + +/* Initialize the module. This function is called from the gdb core on start-up. */ + void _initialize_arc_jtag_ops (void) { - arc_jtag_ops.name=NULL; - arc_jtag_ops.jtag_open = arc_jtag_open; - arc_jtag_ops.jtag_close = arc_jtag_close; - arc_jtag_ops.jtag_memory_write = arc_jtag_write_chunk; - arc_jtag_ops.jtag_memory_read = arc_jtag_read_chunk; - arc_jtag_ops.jtag_memory_chunk_write = arc_jtag_write_chunk; - arc_jtag_ops.jtag_memory_chunk_read = arc_jtag_read_chunk; - arc_jtag_ops.jtag_write_aux_reg = arc_jtag_write_aux_reg; - arc_jtag_ops.jtag_read_aux_reg = arc_jtag_read_aux_reg; - arc_jtag_ops.jtag_read_core_reg = arc_jtag_read_core_reg; - arc_jtag_ops.jtag_write_core_reg = arc_jtag_write_core_reg; - arc_jtag_ops.jtag_wait = arc_jtag_wait; - arc_jtag_ops.jtag_reset_board = arc_jtag_reset_board; - arc_jtag_ops.jtag_status = JTAG_CLOSED ; + ENTERMSG; + + /* Initialize the arc_jtag_ops global variable. */ + + arc_jtag_ops.state_machine_debug = FALSE; + arc_jtag_ops.retry_count = 50; + + /* We want to be able to reset the board, and check whether it is connected, + regardless of the connection state. */ + arc_jtag_ops.open = jtag_open; + arc_jtag_ops.close = jtag_close; + arc_jtag_ops.check_open = jtag_check_open; + arc_jtag_ops.reset_board = jtag_reset_board; + + /* The JTAG interface is initially closed. */ + set_interface(JTAG_CLOSED); } + +/******************************************************************************/ diff --git a/gdb/arc-jtag-ops.h b/gdb/arc-jtag-ops.h index dbf49e605b8..d278acd303a 100644 --- a/gdb/arc-jtag-ops.h +++ b/gdb/arc-jtag-ops.h @@ -1,62 +1,126 @@ -/* Target dependent code for ARC700, for GDB, the GNU debugger. +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. - Copyright 2005 Free Software Foundation, Inc. + Copyright 2005, 2008, 2009 Free Software Foundation, Inc. Contributed by Codito Technologies Pvt. Ltd. (www.codito.com) - Authors: - Sameer Dhavale <sameer.dhavale@codito.com> - Soam Vasani <soam.vasani@codito.com> + Authors: + Sameer Dhavale <sameer.dhavale@codito.com> + Soam Vasani <soam.vasani@codito.com> + Richard Stuckey <richard.stuckey@arc.com> 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 + the Free Software Foundation; either version 3 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. -*/ + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This header file defines the JTAG interface to an ARC processor. */ +/* */ +/* Operations are provided for: */ +/* 1) controlling the interface */ +/* 2) reading/writing the core registers of the processor */ +/* 3) reading/writing the auxiliary registers of the processor */ +/* 4) reading/writing single words in the target memory */ +/* 5) reading/writing blocks in the target memory */ +/* */ +/* The addresses specified for the memory word read/write operations must */ +/* be word-aligned. Those specified for the memory block read/write */ +/* operations may have any alignment; these operations may transfer an */ +/* arbitrary number of bytes. */ +/* */ +/* Usage: */ +/* The module exports a global variable arc_jtag_ops which holds pointers */ +/* to the functions for the operations, as well as some state information.*/ +/* This variable is initialized by the module's initialization function */ +/* which must be called before any use is made of the module (N.B. the */ +/* call to this function is generated by the gdb build mechanism, so this */ +/* function should not be explicitly called). */ +/* */ +/* The variable arc_jtag_ops.retry_count controls how many repeated */ +/* attempts are made if a read/write operation fail; this variable is */ +/* initially set to 50. */ +/* */ +/* Debugging Facilities: */ +/* If the variable arc_jtag_ops.state_machine_debug is set to TRUE then */ +/* trace information will be output. */ +/* */ +/* Host/Target Byte Order: */ +/* The register contents returned by the read/write aux/core register */ +/* functions, or supplied to them, are in little-endian byte order. */ +/* */ +/* The memory contents returned by the read/write word/chunk/pattern */ +/* functions, or supplied to them, are in host byte order; the functions */ +/* perform whatever byte-swapping is required by the endiannness of the */ +/* target. */ +/* */ +/******************************************************************************/ + +#ifndef ARC_JTAG_OPS +#define ARC_JTAG_OPS + +/* ARC header files */ +#include "arc-support.h" + + +#define ARC_TARGET_NAME "arcjtag" + + +typedef enum +{ + JTAG_SUCCESS, + JTAG_READ_FAILURE, + JTAG_WRITE_FAILURE +} JTAG_OperationStatus; -#define ARC_NR_CORE_REGS 64 -#define MAX_ARC700_REG_SIZE 4 -#define JTAG_READ_FAILURE 0 -#define JTAG_WRITE_FAILURE 0 -enum arc_jtag_status - { +typedef enum +{ JTAG_OPENED, - JTAG_CLOSED, - JTAG_RUNNING - }; - -struct jtag_ops{ - char *name; - void (*jtag_open) (void); - void (*jtag_close) (void); - int (*jtag_memory_write) (unsigned int, unsigned int *, int); - int (*jtag_memory_chunk_write) (unsigned int, unsigned int *, int); - int (*jtag_memory_read) (unsigned int, unsigned int *, int); - int (*jtag_memory_chunk_read) (unsigned int, unsigned int *, int); - int (*jtag_read_aux_reg) (unsigned int, unsigned int *); - int (*jtag_write_aux_reg) (unsigned int, unsigned int); - int (*jtag_read_core_reg) (unsigned int, unsigned int *); - int (*jtag_write_core_reg) (unsigned int, unsigned int); - void (*jtag_wait) (void); - void (*jtag_reset_board) (void); - enum arc_jtag_status jtag_status; - int arc_jtag_state_machine_debug; -}; - - -#define IS_ARC700 (arc_get_architecture() == ARC700) -#define IS_ARC600 (arc_get_architecture() == ARC600) -#define IS_A5 (arc_get_architecture() == A5) -#define IS_A4 (arc_get_architecture() == A4) + JTAG_CLOSED +} JTAG_Status; + + +typedef struct +{ + JTAG_Status status; + unsigned int retry_count; + Boolean state_machine_debug; + + Boolean (*open) (ARC_RegisterNumber mem_subsys); + void (*close) (void); + void (*reset) (void); + void (*reset_board) (void); + void (*check_open) (void); + + /* These operations return the number of bytes read/written. */ + unsigned int (*memory_read_word) (ARC_Address address, ARC_Word *data); /* single word. */ + unsigned int (*memory_write_word) (ARC_Address address, ARC_Word data); /* single word. */ + unsigned int (*memory_read_chunk) (ARC_Address address, ARC_Byte *data, unsigned int words); /* block. */ + unsigned int (*memory_write_chunk) (ARC_Address address, ARC_Byte *data, unsigned int words); /* block. */ + unsigned int (*memory_write_pattern) (ARC_Address address, ARC_Word pattern, unsigned int words); /* block. */ + + JTAG_OperationStatus (*read_aux_reg) (ARC_RegisterNumber reg, ARC_RegisterContents *contents); + JTAG_OperationStatus (*write_aux_reg) (ARC_RegisterNumber reg, ARC_RegisterContents contents); + JTAG_OperationStatus (*read_core_reg) (ARC_RegisterNumber reg, ARC_RegisterContents *contents); + JTAG_OperationStatus (*write_core_reg) (ARC_RegisterNumber reg, ARC_RegisterContents contents); +} JTAG_Operations; + + +extern JTAG_Operations arc_jtag_ops; + + +#endif /* ARC_JTAG_OPS */ +/******************************************************************************/ diff --git a/gdb/arc-jtag-tdep.c b/gdb/arc-jtag-tdep.c deleted file mode 100644 index 84015387bb6..00000000000 --- a/gdb/arc-jtag-tdep.c +++ /dev/null @@ -1,638 +0,0 @@ -/* Target dependent code for ARC700, for GDB, the GNU debugger. - - Copyright 2005 Free Software Foundation, Inc. - - Contributed by Codito Technologies Pvt. Ltd. (www.codito.com) - - Authors: - Soam Vasani <soam.vasani@codito.com> - Ramana Radhakrishnan <ramana.radhakrishnan@codito.com> - - 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 <string.h> - -#include "defs.h" -#include "osabi.h" -#include "frame.h" -#include "regcache.h" -#include "gdb_assert.h" -#include "inferior.h" -#include "gdbcmd.h" -#include "reggroups.h" - -#include "arc-tdep.h" -#include "arc-jtag.h" - - -#ifdef ARC4_JTAG -/* brk */ -unsigned int a4_jtag_breakpoint_size = 4; -unsigned char a4_jtag_breakpoint_insn[4] = { 0x00, 0xfe, 0xff, 0x1f }; -#define A4_HALT_VALUE 0x02000000 -#else -/* brk_s */ -unsigned int arc700_jtag_breakpoint_size = 2; -unsigned char arc700_jtag_breakpoint_insn[2] = { 0xff, 0x7f }; -#endif - - - -struct arc_reg_info -{ - char *name ; - int hw_regno; - char *description; -#ifdef ARC4_JTAG - enum arc4_jtag_regnums gdbregno; -#else - enum arc700_jtag_regnums gdbregno; -#endif - enum ARCProcessorVersion arcVersionSupported; -}; - - - -static const char * -arc_jtag_register_name (int regno) -{ - static char jtag_names[][30] = { - "r0", "r1", "r2", "r3", "r4", "r5", "r6", - "r7", "r8", "r9", "r10", "r11", "r12", "r13", - "r14", "r15", "r16", "r17", "r18", "r19", "r20", - "r21", "r22", "r23", "r24", "r25", "r26", - - "fp", - "sp", - "ilink1", - "ilink2", - "blink", - - /* Extension core regs are 32..59 inclusive. */ - "r32", "r33", "r34", "r35", "r36", "r37", "r38", "r39", - "r40", "r41", "r42", "r43", "r44", "r45", "r46", "r47", "r48", "r49", - "r50", "r51", "r52", "r53", "r54", "r55", "r56", "r57", "r58", "r59", - - "lp_count", - - /* 61 is reserved, 62 is not a real register. */ - /*FIXMEA: The following 3 are supposed to be registers - that are used only to encode immediate values in A4*/ - "r61", - "r62", - - "pcl", - - /* Now the aux registers. */ - - "status", - "semaphore", - "lp_start", - "lp_end", - "identity", - "debug", - -#ifndef ARC4_JTAG - "pc", - "status32", - "status32_l1", - "status32_l2", - - "count0", - "control0", - "limit0", - "int_vector_base", - "aux_macmode", - "aux_irq_lv12", - - - "count1", - "control1", - "limit1", - "aux_irq_lev", - "aux_irq_hint", - "eret", - "erbta", - "erstatus", - "ecr", - "efa", - "icause1", - "icause2", - "aux_ienable", - "aux_itrigger", - "xpu", - "bta", - "bta_l1", - "bta_l2", - "aux_irq_pulse_cancel", - "aux_irq_pending", - - /* Build configuration registers. */ - "bcr_0", - "dccm_base_build", - "crc_base_build", - "bta_link_build", - "dvbf_build", - "tel_instr_build", - "bcr_6", - "memsubsys", - "vecbase_ac_build", - "p_base_address", - "bcr_a", - "bcr_b", - "bcr_c", - "bcr_d", - "bcr_e", - "mmu_build", - "arcangel_build", - "bcr_11", - "d_cache_build", - "madi_build", - "dccm_build", - "timer_build", - "ap_build", - "icache_build", - "iccm_build", - "dspram_build", - "mac_build", - "multiply_build", - "swap_build", - "norm_build", - "minmax_build", - "barrel_build", -#endif - - }; - - gdb_assert(ARRAY_SIZE (jtag_names) == NUM_REGS + NUM_PSEUDO_REGS); - gdb_assert(regno >=0 && regno < NUM_REGS + NUM_PSEUDO_REGS); - - return jtag_names[regno]; -} - -int -arc_jtag_register_reggroup_p (int regnum, struct reggroup *group) -{ - /* These registers don't exist, so they are not in any reggroup. */ - if ((regnum >= 32 && regnum <= 59) || (regnum == 61) || (regnum == 62)) - return 0; - - /* Which regs to save/restore ? */ - if ((group == save_reggroup || group == restore_reggroup)) - { - /* Save/restore: - 1. all core regs, except PCL (PCL is not writable) - 2. aux regs LP_START..LP_END (IDENTITY is not writable) - 3. aux regs PC_REGNUM..STATUS32_L2 - 3. aux regs ERET..EFA */ - return ( ( regnum >= 0 && regnum < ARC_PCL_REGNUM) - || ( regnum >= ARC_LP_START_REGNUM && regnum<= ARC_LP_END_REGNUM) -#ifdef ARC4_JTAG - || ( regnum == ARC_STATUS_REGNUM) -#else - || ( regnum >= ARC_PC_REGNUM && regnum <= ARC_STATUS32_L2_REGNUM) - || ( regnum >= ARC_ERET_REGNUM && regnum <= ARC_EFA_REGNUM) -#endif - ); - } - - return -1; -} - -static void -arc_jtag_print_registers_info (struct gdbarch *gdbarch, struct ui_file *file, - struct frame_info *frame, int regnum, int all) -{ - int i; - - if (regnum >= 0 ) - { - default_print_registers_info (gdbarch, file, frame, regnum, all); - return; - } - - /* if regnum < 0 , print all registers */ - - for (i=0; i <= 26; ++i) - default_print_registers_info (gdbarch, file, frame, i, all); - default_print_registers_info (gdbarch, file, frame, - ARC_FP_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, - ARC_SP_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, - ARC_ILINK1_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, - ARC_ILINK2_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, - ARC_BLINK_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, - ARC_LP_COUNT_REGNUM, all); - - /* now the aux registers */ - if (!all) - { - default_print_registers_info (gdbarch, file, frame, - ARC_LP_START_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, - ARC_LP_END_REGNUM, all); - -#ifndef ARC4_JTAG - default_print_registers_info (gdbarch, file,frame, - ARC_STATUS32_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, - ARC_BTA_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, - ARC_EFA_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, - ARC_ERET_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, - ARC_STATUS32_L1_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, - ARC_STATUS32_L2_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, - ARC_ERSTATUS_REGNUM, all); - - - /* PC */ - default_print_registers_info (gdbarch, file, frame, - ARC_PC_REGNUM, all); -#endif - } - else - { - /* This part needs cleaning up. */ - for (i = ARC_STATUS_REGNUM; -#ifndef ARC4_JTAG - i <= ARC_AUX_IRQ_PENDING_REGNUM; -#else /*FIXMEA*/ - i <= ARC_DEBUG_REGNUM; -#endif - i ++ ) - default_print_registers_info (gdbarch, file, frame , - i, all); - - - for (i = ARC_STATUS_REGNUM ; -#ifndef ARC4_JTAG - i <= ARC_AUX_IRQ_PENDING_REGNUM; -#else /*FIXMEA*/ - i <= ARC_DEBUG_REGNUM; -#endif - i ++ ) - default_print_registers_info (gdbarch, file, frame, - i, all); - -#ifndef ARC4_JTAG - for (i = ARC_BCR_1_REGNUM ; - i <= ARC_BCR_5_REGNUM ; - i ++ ) - default_print_registers_info (gdbarch, file , frame, - i , all); - - for (i = ARC_BCR_7_REGNUM ; - i <= ARC_BCR_9_REGNUM; - i ++ ) - default_print_registers_info (gdbarch, file, frame, - i , all); - - for (i = ARC_BCR_F_REGNUM; - i <= ARC_BCR_10_REGNUM; - i ++ ) - default_print_registers_info (gdbarch, file, frame , - i , all); - - for (i = ARC_BCR_12_REGNUM; - i <= ARC_BCR_1F_REGNUM; - i ++) - default_print_registers_info (gdbarch, file, frame , - i , all); -#endif //if no ARC4_JTAG - - - } -} - -/* Command: aux-read <from> <to> - - Read and display a range of aux registers. Some of the aux registers - (pc, debug, etc.) are part of the register set, but this is a more - general interface. - - We should eventually change this to use the ui_out stuff rather than - printf_filtered. */ -static void -arc_jtag_aux_read_command (char *arg, int from_tty) -{ - char *arg2 = 0; - struct expression *expr; - struct value *val; - struct cleanup *old_chain = 0; - int auxregno, auxregno2 = 0, nrregs; - unsigned int *buf; - int i, nrtransfered; - - if (!arg) - { - printf_filtered ("aux-read <REG-FROM> [<REG-TO>]\n"); - return; - } - - /* strip leading spaces */ - while(*arg == ' ') - arg++; - - /* two arguments ? */ - /* This assumes that the first arg cannot have spaces. (The disas command - also seems to work this way.) */ - arg2 = strchr (arg, ' '); - - /* get the second one */ - if (arg2) - { - struct expression *expr2; - struct value *val2; - - arg2[0] = 0; - arg2++; - - expr2 = parse_expression (arg2); - val2 = evaluate_expression (expr2); - xfree (expr2); - - auxregno2 = *(int *)(VALUE_CONTENTS (val2)); - } - - /* first arg */ - expr = parse_expression (arg); - val = evaluate_expression (expr); - old_chain = make_cleanup (free_current_contents, &expr); - - auxregno = *(int *)(VALUE_CONTENTS (val)); - - /* so, how many regs do we want ? */ - if (arg2) - { - if (auxregno2 < auxregno) - { - warning ("aux-read: %s < %s, showing one register", arg2, arg); - nrregs = 1; - } - else - nrregs = auxregno2 - auxregno + 1; - } - else - nrregs = 1; - - buf = xcalloc (nrregs, sizeof(int)); - make_cleanup (free_current_contents, &buf); - - /* Go get 'em ! */ - nrtransfered = target_read_aux_reg (buf, auxregno, nrregs); - if (nrtransfered <= 0) - { - do_cleanups (old_chain); - error ("aux-read: couldn't read any registers."); - } - else if (nrtransfered < nrregs) - { - warning ("aux-read: could only read %d registers", nrtransfered); - } - - gdb_assert (nrtransfered <= nrregs); - - /* Show them. */ - for (i = auxregno; i - auxregno < nrtransfered; ++i) - { - if ((i - auxregno) % 4 == 0) - printf_filtered("%s%08x: ", ((i - auxregno) ? "\n" : ""), i); - - printf_filtered ("%08x ", buf[i - auxregno]); - } - printf_filtered ("\n"); - - do_cleanups (old_chain); -} - -/* aux-write <regnum> = <value> - Write VALUE to aux register REGNUM. */ -static void -arc_jtag_aux_write_command (char *arg, int from_tty) -{ - char *value_arg = 0; - struct expression *regnum_expr, *value_expr; - struct value *regnum_val, *value_val; - struct cleanup *old_chain = 0; - unsigned int regnum, value; - int err; - - if (!arg) - { - printf_filtered ("aux-write <regnum> = <value>\n"); - return; - } - - value_arg = strchr(arg, '='); - if (!value_arg) - { - error ("aux-write: can't find second argument\n\ -Usage: aux-write <regnum> = <value>"); - return; - } - value_arg[0] = 0; - value_arg++; - - /* Regnum expression */ - regnum_expr = parse_expression (arg); - regnum_val = evaluate_expression (regnum_expr); - old_chain = make_cleanup (free_current_contents, ®num_expr); - regnum = *(unsigned int *)(VALUE_CONTENTS (regnum_val)); - - /* Value expression */ - value_expr = parse_expression (value_arg); - value_val = evaluate_expression (value_expr); - make_cleanup (free_current_contents, &value_expr); - value = *(unsigned int *)(VALUE_CONTENTS (value_val)); - - /* Write it. */ - err = target_write_aux_reg (&value, regnum, 1); - if (err != 1) - { - do_cleanups (old_chain); - error ("aux-write: couldn't write to register 0x%x", regnum); - } - - do_cleanups (old_chain); -} - -#ifdef ARC4_JTAG -// gdbarch_write_pc_ftype *write_pc; -/* - Write PC - Arguments: - 1.CORE_ADDR val : Contains the value to be written into PC. - 2.ptid_t ptid: Process id of the process. - - Returns: void - Description: FIXMEA: Update - Reads the status register - Inserts the value (upper 24 bit) into the bits - 0-23 in the status register - Write the status register - */ -void -a4_jtag_write_pc (CORE_ADDR val, ptid_t ptid) -{ - CORE_ADDR insert_val = val >> 2; - unsigned int buffer; - - - if(debug_arc_jtag_target_message) - printf_filtered ("\n -----***------------ a4_jtag_write_pc Entered ---*%%*#\n"); - - - target_read_aux_reg (&buffer, ARC_HW_STATUS_REGNUM, 1); - - if (!(buffer & A4_HALT_VALUE)) - { - if(debug_arc_jtag_target_message) - printf_filtered ("\n***** Halting Processor... *********\n"); - - buffer = buffer | A4_HALT_VALUE ; - target_write_aux_reg (&buffer, ARC_HW_STATUS_REGNUM, 1); - /* Now the A4 processor has halted*/ - } - - if(debug_arc_jtag_target_message) - printf_filtered (" \nWriting value %u to PC\n", val); - - - target_read_aux_reg (&buffer, ARC_HW_STATUS_REGNUM, 1); - if(debug_arc_jtag_target_message) - printf_filtered (" \nValue of Status Register before writing %d\ - \n Value of PC: 0x%x\n", buffer, buffer & 0x00ffffff); - - buffer = buffer & 0xff000000; - insert_val = insert_val & 0x00ffffff; - buffer = buffer | insert_val ; - - if(debug_arc_jtag_target_message) - printf_filtered (" \nValue of Status Register to be written %d\ - \n Value of PC: 0x%x\n", buffer, buffer & 0x00ffffff); - - // jtag_ops.jtag_write_aux_reg (ARC_STATUS_REGNUM, buffer); - target_write_aux_reg (&buffer, ARC_HW_STATUS_REGNUM, 1); - - if(debug_arc_jtag_target_message) - { - target_read_aux_reg (&buffer, ARC_HW_STATUS_REGNUM, 1); - printf_filtered (" \nValue of Status Register after reading again %d\ - \n Value of PC: 0x%x\n", buffer, buffer & 0x00ffffff); - } - - if(debug_arc_jtag_target_message) - printf_filtered ("\n -----***------------ a4_jtag_write_pc Leaving ---*%%*#\n"); - -} - - -/* - Read PC - Arguments: - 1.ptid_t ptid: Process id of the process. - - Returns: CORE_ADDR - Description: - Reads the status register - Extracts the PC value from it. - Right shift twice to get correct value of PC - return PC -*/ -CORE_ADDR -a4_jtag_read_pc (ptid_t ptid) -{ - unsigned int buffer; - - if (debug_arc_jtag_target_message) - printf_filtered ("\n Entering a4_jtag_read_pc ()"); - buffer = 0; - target_read_aux_reg (&buffer, ARC_HW_STATUS_REGNUM, 1); - if (debug_arc_jtag_target_message) - printf_filtered ("\n Value of Status Reg: 0x%x",buffer); - buffer = buffer & 0x00ffffff; - buffer = buffer << 2; - - if (debug_arc_jtag_target_message) - printf_filtered ("\n Leaving a4_jtag_read_pc ()\ - \n Value of Pc: 0x%x\n", buffer); - - return buffer; -} - -#endif // ARC4_JTAG - -ARCVariantsInfo arc_debug_processor_information; - -struct gdbarch * -arc_jtag_init (struct gdbarch *gdbarch) -{ - struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); - -#ifndef ARC4_JTAG - tdep->arc_breakpoint_size = arc700_jtag_breakpoint_size; - tdep->arc_breakpoint_insn = arc700_jtag_breakpoint_insn; -#else - tdep->arc_breakpoint_size = a4_jtag_breakpoint_size; - tdep->arc_breakpoint_insn = a4_jtag_breakpoint_insn; -#endif - - set_gdbarch_num_regs (gdbarch, ARC_NR_REGS); -#ifndef ARC4_JTAG - set_gdbarch_pc_regnum (gdbarch, ARC_PC_REGNUM); -#else - // set_gdbarch_pc_regnum (gdbarch, ARC_STATUS_REGNUM); - set_gdbarch_write_pc (gdbarch, a4_jtag_write_pc); - set_gdbarch_read_pc (gdbarch, a4_jtag_read_pc); -#endif - set_gdbarch_register_name (gdbarch, arc_jtag_register_name); - - set_gdbarch_print_registers_info (gdbarch, arc_jtag_print_registers_info); - - tdep->register_reggroup_p = arc_jtag_register_reggroup_p; - - tdep->lowest_pc = 0; - - tdep->sigtramp_p = NULL; - - tdep->arc_processor_variant_info = &arc_debug_processor_information; - - /* Auxillary register commands. */ - add_cmd ("arc-aux-read", class_vars, arc_jtag_aux_read_command, - "Read and show a range of auxillary registers.\n\ -Usage: arc-aux-read <REG-FROM> [<REG-TO>]\n\ -REG-FROM and REG-TO can be any expressions that evaluate to integers.\n\ -If REG-TO is not specified, one register is displayed.", - &cmdlist); - - add_cmd ("arc-aux-write", class_vars, arc_jtag_aux_write_command, - "Write to an auxillary register.\n\ -Usage: arc-aux-write <REG> = <VALUE>\n\ -REG and VALUE can be any expressions that evaluate to integers.", - &cmdlist); - - return gdbarch; -} - diff --git a/gdb/arc-jtag.c b/gdb/arc-jtag.c index 9a86b6d4c8d..02c8b6e7963 100644 --- a/gdb/arc-jtag.c +++ b/gdb/arc-jtag.c @@ -1,1297 +1,1173 @@ -/* Target dependent code for ARC700, for GDB, the GNU debugger. +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. - Copyright 2005 Free Software Foundation, Inc. + Copyright 2005, 2008, 2009 Free Software Foundation, Inc. Contributed by Codito Technologies Pvt. Ltd. (www.codito.com) - Authors: - Sameer Dhavale <sameer.dhavale@codito.com> - Ramana Radhakrishnan <ramana.radhakrishnan@codito.com> + Authors: + Sameer Dhavale <sameer.dhavale@codito.com> + Ramana Radhakrishnan <ramana.radhakrishnan@codito.com> + Richard Stuckey <richard.stuckey@arc.com> 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 + the Free Software Foundation; either version 3 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 <stdio.h> -#include <string.h> + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This module creates an instance of a gdb 'target_ops' structure which */ +/* contains information and operations for debugging a remote ARC target */ +/* with JTAG. */ +/* */ +/* It also registers a number of ARC-specific commands with gdb. */ +/* */ +/* Usage: */ +/* The module exports a function _initialize_arc_jtag: the call to this */ +/* function is generated by the gdb build mechanism, so this function */ +/* should not be explicitly called. */ +/* */ +/******************************************************************************/ + +/* system header files */ + +/* gdb header files */ #include "defs.h" #include "inferior.h" -#include "target.h" -#include "breakpoint.h" +#include "gdbcmd.h" +#include "objfiles.h" +#include "libiberty.h" +#include "gdb_assert.h" +/* ARC header files */ +#include "config/arc/tm-embed.h" +#include "arc-jtag.h" +#include "arc-gpio.h" #include "arc-tdep.h" +#include "arc-board.h" +#include "arc-jtag-ops.h" +#include "arc-elf32-tdep.h" +#include "arc-architecture.h" +#include "arc-registers.h" +#include "arc-jtag-actionpoints.h" -#include <unistd.h> -#include <stdlib.h> -#include <sys/io.h> -#include <sys/types.h> +/* -------------------------------------------------------------------------- */ +/* local types */ +/* -------------------------------------------------------------------------- */ -#include <assert.h> +typedef enum +{ + CLEAR_USER_BIT, + RESTORE_USER_BIT +} Status32Action; -#include "arc-jtag.h" -#include "arc-jtag-ops.h" -#include "gdbcore.h" -#include "gdbarch.h" -#include "regcache.h" -#include "command.h" -#include "gdbcmd.h" -#include <signal.h> -/* Flag to print debug messages from here. */ -/* FIXMEA: -static int debug_arc_jtag_target_message; -*/ +/* -------------------------------------------------------------------------- */ +/* local data */ +/* -------------------------------------------------------------------------- */ -#define ENTERMSG \ -do {\ -if(debug_arc_jtag_target_message) \ -printf_filtered ("--- entered %s:%s()\n", __FILE__, __FUNCTION__); \ -} while(0) - -#define ENTERARGS(fmt, args...) \ -do { \ -if(debug_arc_jtag_target_message) \ -printf_filtered ("--- entered %s:%s(" fmt ")\n", __FILE__, __FUNCTION__, args);\ -} while(0) - -#define LEAVEMSG \ -do { \ -if(debug_arc_jtag_target_message) \ -printf_filtered ("--- exited %s:%s()\n", __FILE__, __FUNCTION__); \ -} while(0) - -/* The number of times a memory read/write operation should be attempted - before returning an error. -*/ -#define MEMORY_TRANSFER_ATTEMPTS 10 +#define ARC_CONFIGURATION_COMMAND "arc-configuration" +#define ARC_RESET_BOARD_COMMAND "arc-reset-board" +#define ARC_LIST_ACTIONPOINTS_COMMAND "arc-list-actionpoints" +#define ARC_FSM_DEBUG_COMMAND "arcjtag-debug-statemachine" +#define ARC_JTAG_RETRY_COMMAND "arcjtag-retry-count" -/* defined in arc-jtag-ops.c */ -extern unsigned int arcjtag_retry_count; +#define ARC_CONFIGURATION_COMMAND_USAGE "Usage: info " ARC_CONFIGURATION_COMMAND "\n" +#define ARC_RESET_BOARD_COMMAND_USAGE "Usage: " ARC_RESET_BOARD_COMMAND "\n" +#define ARC_LIST_ACTIONPOINTS_COMMAND_USAGE "Usage: " ARC_LIST_ACTIONPOINTS_COMMAND "\n" -struct target_ops arc_debug_ops; -extern struct jtag_ops arc_jtag_ops; -static void arc_debug_interrupt (int signo); -static void arc_debug_interrupt_twice (int signo); -static void arc_print_processor_variant_info (void); -static int arc_debug_write_aux_register (int hwregno, int *buf); -static int arc_debug_read_aux_register (int hwregno, int *buf); -static int arc_debug_read_core_register (int hwregno, int *buf); +/* The gdb target operations structure for this target. */ +static struct target_ops jtag_target_ops; +/* A set of pointers to operations for reading/writing registers/memory in the + JTAG target. */ +static TargetOperations operations; -/* Register Mapping information between GDB regnums - and actual hardware register numbers. -*/ +/* The h/w register numbers of various auxiliary registers needed for + controlling the target processor. */ +static ARC_RegisterNumber lp_start_regnum; +static ARC_RegisterNumber lp_end_regnum; +static ARC_RegisterNumber icache_ivic_regnum; +static ARC_RegisterNumber icache_control_regnum; +static ARC_RegisterNumber dcache_ivdc_regnum; +static ARC_RegisterNumber dcache_control_regnum; -struct arc_reg_info -{ - char *name ; - enum arc_hw_regnums hw_regno; - char *description; -#ifdef ARC4_JTAG - enum arc4_jtag_regnums gdbregno; -#else - enum arc700_jtag_regnums gdbregno; -#endif - enum ARCProcessorVersion arcVersionSupported; -}; -#define RBCR(name, hwregno , desc, gdbregno, version) { #name, hwregno , desc , gdbregno , version } , +/* -------------------------------------------------------------------------- */ +/* external data */ +/* -------------------------------------------------------------------------- */ -#undef RAUX -struct arc_reg_info arc_bcr_reg_info [] = - { - #include "arc-regnums-defs.h" - }; +/* This declaration should be in the file breakpoint.h (a gdb core file). */ +extern struct breakpoint *breakpoint_chain; -#undef RAUX -#undef RBCR -#define RAUX(name, hwregno , desc, gdbregno, version) { #name , hwregno , desc , gdbregno , version } , -struct arc_reg_info arc_aux_reg_map[] = - { - #include "arc-regnums-defs.h" - }; +/* -------------------------------------------------------------------------- */ +/* local macros */ +/* -------------------------------------------------------------------------- */ +#define IS_ARC700 (arc_get_architecture(arc_read_jtag_aux_register) == ARC700) +#define IS_ARC600 (arc_get_architecture(arc_read_jtag_aux_register) == ARC600) -static void -arc_update_architecture(void) -{ - unsigned int idinfo; - unsigned short tmp; - struct gdbarch_tdep * tdep = gdbarch_tdep (current_gdbarch); - - if (IS_ARC700) { - tdep->arc_processor_variant_info->arcprocessorversion = ARC700; - set_gdbarch_decr_pc_after_break (current_gdbarch,0); - } - else if(IS_ARC600) { - tdep->arc_processor_variant_info->arcprocessorversion = ARC600; - set_gdbarch_decr_pc_after_break (current_gdbarch,2); - } - else if(IS_A5) { - warning ("A5 debugging is unsupported and may be buggy."); - tdep->arc_processor_variant_info->arcprocessorversion = A5; - } - else { - tdep->arc_processor_variant_info->arcprocessorversion = A4; - set_gdbarch_decr_pc_after_break (current_gdbarch,0); - } -} -/* Get us out of user mode. */ -static unsigned int -clear_status32_user_bit () -{ - int rd; -#ifndef ARC4_JTAG - if(arc_jtag_ops.jtag_read_aux_reg(ARC_HW_STATUS32_REGNUM, &rd) == JTAG_READ_FAILURE) - error("Failure reading auxillary register 0x%x:status32 register", ARC_HW_STATUS32_REGNUM); +/* -------------------------------------------------------------------------- */ +/* local functions */ +/* -------------------------------------------------------------------------- */ - if(arc_jtag_ops.jtag_write_aux_reg(ARC_HW_STATUS32_REGNUM, rd & ~0x80) == JTAG_READ_FAILURE) - error("Failure writing to auxillary register 0x%x:status32 register", ARC_HW_STATUS32_REGNUM); -#endif - return rd; -} +/* -------------------------------------------------------------------------- */ +/* 1) functions for reading/writing registers */ +/* -------------------------------------------------------------------------- */ -/* Restore a saved status32; use with clear_status32_user_bit(). */ -static void -restore_status32_user_bit (unsigned int status32) +/* Set UB bit in the DEBUG register: this allows brk_s instruction to work in + User mode. + + Returns TRUE if the operation is successful. */ + +static Boolean +set_debug_user_bit (ARC_RegisterContents extra_bits) { -#ifndef ARC4_JTAG - if(arc_jtag_ops.jtag_write_aux_reg(ARC_HW_STATUS32_REGNUM, status32) == JTAG_READ_FAILURE) - error("Failure writing to auxillary register 0x%x:status32 register", ARC_HW_STATUS32_REGNUM); -#endif + /* The DEBUG User bit exists only in the ARC700 variant. */ + if (IS_ARC700) + extra_bits |= DEBUG_USER; + + /* If we have extra bits to be set in the DEBUG register. */ + if (extra_bits != 0) + { + ARC_RegisterContents debug; + + if (arc_read_jtag_aux_register(arc_debug_regnum, &debug, TRUE)) + { + /* Set UB = 1. */ + ARC_RegisterContents new_debug = debug | extra_bits; + + /* Do the write only if it will change the register contents. */ + if (new_debug != debug) + return arc_write_jtag_aux_register(arc_debug_regnum, new_debug, TRUE); + } + else + return FALSE; + } + + return TRUE; } -/* UB bit in the debug register. It allows brk_s to work in user mode. */ + +/* Clear or restore the User bit in the STATUS32 auxiliary register. */ + static void -set_debug_user_bit () +change_status32 (Status32Action action) { - if(is_arc700 ()) + static ARC_RegisterContents status32 = 0; + + if (action == CLEAR_USER_BIT) { - /* set UB = 1 */ - unsigned int debug; - if (arc_jtag_ops.jtag_read_aux_reg (ARC_HW_DEBUG_REGNUM, &debug) == JTAG_READ_FAILURE) - error("Failure reading auxillary register 0x%x:debug register",ARC_HW_DEBUG_REGNUM); - debug |= 0x10000000; - arc_jtag_ops.jtag_write_aux_reg (ARC_HW_DEBUG_REGNUM, debug); + /* Get processor out of user mode. */ + + if (arc_read_jtag_aux_register(arc_status32_regnum, &status32, FALSE)) + { + /* If the User bit is actually set. */ + if (status32 & STATUS32_USER) + if (!arc_write_jtag_aux_register(arc_status32_regnum, + status32 & ~STATUS32_USER, FALSE)) + warning(_("can not clear User bit in STATUS32 auxiliary register")); + } + else + warning(_("can not read STATUS32 auxiliary register")); + } + else + { + /* If the User bit was actually cleared. */ + if (status32 & STATUS32_USER) + if (!arc_write_jtag_aux_register(arc_status32_regnum, status32, FALSE)) + warning(_("can not restore User bit in STATUS32 auxiliary register")); } } +/* -------------------------------------------------------------------------- */ +/* 2) functions for reading/writing mmeory */ +/* -------------------------------------------------------------------------- */ -static void -invalidateCaches (void) +/* These functions should NOT be used within this module: they are intended + purely for use by the arc-memory module for reading/writing multiple words + of data at word-aligned addresses. */ + +static unsigned int +read_words (ARC_Address address, + ARC_Byte *data, + unsigned int words) { - if(arc_jtag_ops.jtag_write_aux_reg ( ARC_HW_ICACHE_IVIC , 1)==JTAG_WRITE_FAILURE) - error("Failure writing 0x1 to auxillary register 0x%x:Icache invalidate\n",ARC_HW_ICACHE_IVIC); - if(arc_jtag_ops.jtag_write_aux_reg ( ARC_HW_DCACHE_IVIC , 1)==JTAG_WRITE_FAILURE) - error("Failure writing 0x1 to auxillary register 0x%x:Dcache invalidate\n",ARC_HW_DCACHE_IVIC); + DEBUG("reading %u words from 0x%08X on target board\n", words, address); + + gdb_assert(IS_WORD_ALIGNED(address)); + + return arc_jtag_ops.memory_read_chunk(address, data, words); } -static void -disableCaches (void) + +static unsigned int +write_words (ARC_Address address, + ARC_Byte *data, + unsigned int words) { - /* Disabling Icache */ - if(arc_jtag_ops.jtag_write_aux_reg( ARC_HW_ICACHE_CONTROL ,0x1)==JTAG_WRITE_FAILURE) - error("Failure writing 0x1 to auxillary register 0x%x:Icache control register\n",ARC_HW_ICACHE_CONTROL); - /* Disabling Dcache */ - if(arc_jtag_ops.jtag_write_aux_reg( ARC_HW_DCACHE_CONTROL ,0x1)==JTAG_WRITE_FAILURE) - error("Failure writing 0x1 to auxillary register 0x%x:Dcache control register\n",ARC_HW_DCACHE_CONTROL); -} + gdb_assert(IS_WORD_ALIGNED(address)); + DEBUG("writing %u words to 0x%08X on target board\n", words, address); -/* Function: arc_debug_open - * Parameters : - * 1. args : - * 2. from_tty: - * Returns : void - * Description: - 1. Connect to the jtag target . - 2. Read the number of action points supported. - 3. Read the configuration of action points. - 4. Set up internal data structures for number of hardware - breakpoints and watchpoints. - 5. Set the UB bit to 1 for ARC700 and not for ARC600. - * - */ + return arc_jtag_ops.memory_write_chunk(address, data, words); +} -void -arc_debug_open (char *args, int from_tty) + +static unsigned int +write_pattern (ARC_Address address, + ARC_Word pattern, + unsigned int words) { - ENTERARGS("%s", args); - target_preopen(from_tty); - - reopen_exec_file (); - reread_symbols (); - - unpush_target (&arc_debug_ops); - arc_jtag_ops.jtag_open(); - push_target (&arc_debug_ops); - - /* Call arc_update_architecture if opened successfully. */ - arc_update_architecture(); - /* Fixme :: Should these be in create_inferior or - some place else ?. We would not like these here - when attach starts working. - */ - disableCaches(); -#ifdef ARC4_JTAG - if(arc_jtag_ops.jtag_write_aux_reg (ARC_HW_STATUS_REGNUM, 0x02000000)==JTAG_WRITE_FAILURE) - error("Failure writing 0x0200 0000 to auxillary register 0x%x:status register\n",ARC_HW_STATUS_REGNUM); - - if (from_tty) - printf_filtered ("Connected to the arcjtag target.\n"); - -#else - if(arc_jtag_ops.jtag_write_aux_reg (ARC_HW_STATUS32_REGNUM, 0x1)==JTAG_WRITE_FAILURE) - - error("Failure writing 0x1 to auxillary register 0x%x:status32 register\n",ARC_HW_STATUS32_REGNUM); - - /* allow breakpoints in user mode. */ - set_debug_user_bit (); - - - if (from_tty) - printf_filtered ("Connected to the arcjtag target.\n"); -#endif + gdb_assert(IS_WORD_ALIGNED(address)); + + DEBUG("writing pattern 0x%08X repeated %u times to 0x%08X on target board\n", pattern, words, address); + + return arc_jtag_ops.memory_write_pattern(address, pattern, words); } -void arc_debug_close() + +/* -------------------------------------------------------------------------- */ +/* 3) functions for processor cache management */ +/* -------------------------------------------------------------------------- */ + +/* Invalidate the target processor's caches. */ + +static void +invalidate_caches (void) { - arc_jtag_ops.jtag_close(); + /* N.B. when invalidating the data caches, we must first set the DC_CTRL.IM + bit to 1 to ensure that any "dirty" lines in the cache get flushed + to main memory. */ + (void) arc_write_jtag_aux_register(dcache_control_regnum, DC_CTRL_IM, TRUE); + (void) arc_write_jtag_aux_register(icache_ivic_regnum, IC_IVIC_IV, TRUE); + (void) arc_write_jtag_aux_register(dcache_ivdc_regnum, DC_IVDC_IV, TRUE); } -/* Function: arc_debug_attach - * Parameters : - * 1. char *x: - * 2. int i: - * Returns : void - * Description: - * 1. attach without resetting the board - * 2. get all Board configuration registers of interest. - * if ARC700 set the UB bit to 1. (This is invalid in the - * ARC600). - */ +/* Disable the target processor's caches. */ -void -arc_debug_attach (char *x, int i) +static void +disable_caches (void) { - - ENTERMSG; + (void) arc_write_jtag_aux_register(icache_control_regnum, IC_CTRL_DC, TRUE); + (void) arc_write_jtag_aux_register(dcache_control_regnum, DC_CTRL_DC, TRUE); } -/* Function: arc_debug_attach - * Parameters : - * 1. char *x: - * 2. int i: - * Returns : void - * Description: - * 1. Detach without resetting the board. - */ -void -arc_debug_detach (char *x, int i) +/* -------------------------------------------------------------------------- */ +/* 4) functions for JTAG interface management */ +/* -------------------------------------------------------------------------- */ + +/* Open the JTAG interface to the debug target. */ + +static Boolean +open_JTAG_interface (int from_tty) { - - ENTERMSG; + /* This is somewhat inelegant, but commands read from scripts in the gdb + testsuite are regarded as though they were being input interactively + (i.e. from_tty is 1), and interactive queries may be made (such as + asking the user whether the program currently being debugged should be + killed first) - and these queries hang the tests! + + So, if the environment variable is set, assume that the gdb test suite is + being run, so that no such queries will be made. + + It is not possible to make this check in the top-level command handler + loop, as the output from some other commands (e.g. 'file') depend on the + from_tty parameter passed to them, and the gdb test scripts expect to get + the interactive version of the output! */ + target_preopen(from_tty && (getenv("ARC_GDB_TEST") == NULL)); + + gdb_assert(arc_jtag_ops.open != NULL); - /* Let it continue. */ - target_resume (inferior_ptid, 0, 0); + return arc_jtag_ops.open(arc_aux_find_register_number("MEMSUBSYS", ARC_HW_MEMSUBSYS_REGNUM)); } -/* Function: arc_debug_resume - * Parameters : - * 1. ptid_t ptid: - * 2. int step: 1 - single step , 0 run freely. - * 3. enum target_signal signal; - * Returns : void - * Description: - * 1. What about Pipecleaning? - * 2. Write 0 to the HALT bit in status32. - * 3. Send a signal (ignore) in this case. - * 4. if(step) use hardware single step on the ARC700. - * done by setting the IS bit in the debug register - * and clearing the halt bit in status32. - * +/* Close the JTAG interface to the debug target. + + Parameter: + resume: TRUE if program execution on the target should be allowed to resume */ -void -arc_debug_resume (ptid_t ptid, int step, enum target_signal signal) +static void +close_JTAG_interface (Boolean resume) { - - ENTERARGS("%d,%d,%d", ptid.pid, step, signal); - /* Make the inferior resume execution, sending a signal if necessary */ - unsigned int rd; - - /* Because breakpoints may have been set/removed. */ - invalidateCaches (); - - /* This bit is required if breakpoints are to be allowed in user mode. We - set it in target_open, but the operating system might clear it. So we - set it every time we resume. */ - set_debug_user_bit (); - - if(step) - { - - /* reading debug reg */ - if(arc_jtag_ops.jtag_read_aux_reg(ARC_HW_DEBUG_REGNUM,&rd)==JTAG_READ_FAILURE) - error("Failure reading auxillary register 0x%x:debug register",ARC_HW_DEBUG_REGNUM); -#ifdef ARC4_JTAG - rd |= 0x801; /*Setting the IS and the SS bit in the status register - for the A4 core to allow it to single step. */ -#else - /* Mask for Single Stepping changes for ARC600 and ARC700. */ - if(is_arc700()) - rd |= 0x800; - else - if(is_arc600()) - rd |= 0x801; -#endif - - /* Writing to IS bit in DEBUG register for - hardware single instruction stepping. */ - if(arc_jtag_ops.jtag_write_aux_reg(ARC_HW_DEBUG_REGNUM ,rd)==JTAG_WRITE_FAILURE) - error("Failure writing 0x%x to auxillary register 0x%x:debug register\n",rd,ARC_HW_DEBUG_REGNUM); - } - else - { - /* Restarting the processor by clearing the 'H' bit in the status register*/ -#ifdef ARC4_JTAG - /* reading the status reg */ - if(arc_jtag_ops.jtag_read_aux_reg(ARC_HW_STATUS_REGNUM,&rd)==JTAG_READ_FAILURE) - error("Failure reading auxillary register 0x%x:status register",ARC_HW_STATUS_REGNUM); - - rd = rd & ~(0x02000000); - - /* starting the halted processor */ - if(arc_jtag_ops.jtag_write_aux_reg(ARC_HW_STATUS_REGNUM,rd)==JTAG_WRITE_FAILURE) - error("Failure writing 0x%x to auxillary register 0x%x:status register\n",rd,ARC_HW_STATUS_REGNUM); -#else - /* reading the status32 reg */ - if(arc_jtag_ops.jtag_read_aux_reg(ARC_HW_STATUS32_REGNUM,&rd)==JTAG_READ_FAILURE) - error("Failure reading auxillary register 0x%x:status32 register",ARC_HW_STATUS32_REGNUM); - - rd = rd & ~(0x1); - - /* starting the halted processor */ - if(arc_jtag_ops.jtag_write_aux_reg(ARC_HW_STATUS32_REGNUM,rd)==JTAG_WRITE_FAILURE) - error("Failure writing 0x%x to auxillary register 0x%x:status32 register\n",rd,ARC_HW_STATUS32_REGNUM); -#endif - } + /* If we have a target connected. */ + if (arc_jtag_ops.status == JTAG_OPENED) + { + arc_elf32_close(resume); + + /* And close the connection. */ + arc_jtag_ops.close(); + } } -/* For the ^C signal handler. */ -static void (*ofunc) (int); -/* The command line interface's stop routine. This function is installed as - a signal handler for SIGINT. The first time a user requests a stop, we - call target_stop to send a break or ^C. If there is no response from the - target (it didn't stop when the user requested it), we ask the user if - he'd like to detach from the target. */ +/* -------------------------------------------------------------------------- */ +/* 5) functions for starting/stopping the processor */ +/* -------------------------------------------------------------------------- */ + +/* Start the processor by clearing the 'H' bit in the STATUS32 register. */ + static void -arc_debug_interrupt (int signo) +start_processor (void) { - /* If we get the signal twice, do something more drastic. */ - signal (signo, arc_debug_interrupt_twice); - - target_stop (); + ARC_RegisterContents status32; + + if (!arc_read_jtag_aux_register (arc_status32_regnum, &status32, FALSE) || + !arc_write_jtag_aux_register(arc_status32_regnum, status32 & ~STATUS32_HALT, FALSE)) + warning(_("can not clear Halt bit in STATUS32 auxiliary register - can not start processor")); } -/* The user typed ^C twice. */ + +/* Stop the processor by setting the 'FH' bit in the DEBUG register. */ + static void -arc_debug_interrupt_twice (int signo) +stop_processor (void) { - signal (signo, ofunc); + if (!arc_write_jtag_aux_register(arc_debug_regnum, DEBUG_FORCE_HALT, FALSE)) + warning(_("can not set Force Halt bit in DEBUG auxiliary register - can not halt processor")); +} + + +/* Try to halt the processor (if it is running) upon connection to the debug + target. Return TRUE if the processor is successfuly halted. */ - if (query ("Interrupted while waiting for the program.\n\ -Give up (and stop debugging it)? ")) +static Boolean +halt_processor_on_connection (void) +{ + Boolean warn_on_read_failure = TRUE; + Boolean inform_running = TRUE; + Boolean halt_attempted = FALSE; + unsigned int tries = 0; + + /* Unfortunately, if the gpio driver module has been installed on the host + machine, the gpio read/write operations appear to work even if the host + is NOT physically connected to the JTAG target! + + There does not appear to be any way of detecting that situation - all we + can do is bale out if we have not succeded in reading the STATUS32 register + after the required number of retries! */ + do { - target_mourn_inferior (); - throw_exception (RETURN_QUIT); + ARC_RegisterContents status = 0; + + /* Read the STATUS32 register here to check if the halt bit is set. */ + if (arc_read_jtag_aux_register(arc_status32_regnum, &status, warn_on_read_failure)) + { + if (status & STATUS32_HALT) + { + printf_filtered(_("Processor is halted.\n")); + return TRUE; + } + + if (inform_running) + { + /* We inform the user that the processor is running only once + (to avoid swamping the user with messages!). */ + printf_filtered(_("Processor is running. Trying to halt it...\n")); + inform_running = FALSE; + } + + stop_processor(); + halt_attempted = TRUE; + } + else + { + /* We give a warning only on the first read failure (otherwise the + user can get swamped with warnings!). */ + warn_on_read_failure = FALSE; + } + + /* Just in case we actually did fail to read/write the port. */ + if (gpio_port_error) + { + warning(_("error in accessing parallel port via " + GPIO_DEVICE + " - check connection to target board.")); + return FALSE; + } } + while (++tries <= arc_jtag_ops.retry_count); + + if (halt_attempted) + printf_filtered(_("Can not halt processor!\n")); + else + printf_filtered(_("Can not connect to processor!\n")); - signal (signo, arc_debug_interrupt); + return FALSE; } -/* Function: arc_debug_wait - * Parameters : - * 1. ptid_t ptid: - * 2. struct target_waitstatus *status: Indicates status at end - of wait for F.E. - * Returns : void - * Description: - * Poll status32 for the value of H bit. - * After H bit is set in status32. - * Wait till LD(load pending bit) in the DEBUG register - * is cleared. - * SH bit is set if flag instruction was used to halt the processor. - * BH bit is set if the ARCompact processor stopped due to - * a brk_s instruction. Set the target_waitstatus (signal) to SIGTRAP - * only in such a situation. - * - */ -ptid_t -arc_debug_wait (ptid_t ptid, struct target_waitstatus *status) -{ - unsigned int debug; - ENTERMSG; +/* -------------------------------------------------------------------------- */ +/* 6) local functions called from outside this module (from gdb) */ +/* -------------------------------------------------------------------------- */ - /* signal handler for Control-C. */ - ofunc = signal (SIGINT, arc_debug_interrupt); +/* Connect to the JTAG target. - arc_jtag_ops.jtag_wait(); - /* put the old function back. */ - signal (SIGINT, ofunc); + Parameters: + args : user arguments to the 'target' command + from_tty: non-zero if the 'target' command was issued at the terminal - /* If the SH ("self halt") bit is set, we stopped because of the flag - instruction, which is used by programs to exit. */ - if (arc_jtag_ops.jtag_read_aux_reg (ARC_HW_DEBUG_REGNUM, - &debug) == JTAG_READ_FAILURE) - { - error ("Failure reading from debug register"); - } + The arguments may be: + noreset | <xbf file> + + If a XBF file is specified, the target board FPGA is blasted as part of the + connection process. */ + +static void +arc_jtag_open (char *args, int from_tty) +{ + /* By default, reset the board, in case it has been left in a funny state by + the last connection. */ + Boolean reset_required = TRUE; + char *xbf_file = NULL; + FPGA_Status fpga; - /* SH bit of debug register */ - if (debug & ARC_DEBUG_REG_SH_BIT) + ENTERARGS("\"%s\" (%d)", (args) ? args : "", from_tty); + + if (args) { - int exitcode; - status->kind = TARGET_WAITKIND_EXITED; - - /* Exit code of the program. */ - if (arc_jtag_ops.jtag_read_core_reg (0, &exitcode) == JTAG_READ_FAILURE) - { - warning ("Failure reading from register r0, assuming exit code = 0"); - status->value.integer = 0; - } - status->value.integer = exitcode; + if (strcmp(args, "noreset") == 0) + reset_required = FALSE; + else + xbf_file = args; } - else + + /* Is the target board FPGA already configured? */ + fpga = arc_is_FPGA_configured(); + + switch (fpga) { - status->kind = TARGET_WAITKIND_STOPPED; - status->value.sig = TARGET_SIGNAL_TRAP; + case INACCESSIBLE: + /* A warning has already been given. */ + return; + + case UNCONFIGURED: + if (xbf_file == NULL) + { + warning(_("target FPGA is not configured; XBF file must be specified")); + return; + } + break; + + case CONFIGURED: + break; } -#ifndef ARC4_JTAG - /* Bug #1311 (ARC600): Setting a breakpoint on the last instruction of a - ZOL causes GDB to stop at LP_START. Detect this condition and warn the - user. */ - if (is_arc600 ()) + /* As far as we know, there is no program loaded on the target. */ + arc_program_is_loaded = FALSE; + + /* Find the h/w register numbers of various auxiliary registers that we need + for debugging. + + N.B. the gdb 'attach' command can attach only to an arcjtag target that + has been created (by this function) within the *same* debugging + session, i.e. the sequence of commands issued by the user is of the + form: + target arcjtag ... detach ... attach + + This means that we do not need to worry about finding these numbers + again on an 'attach', as they should be the same (they should really + be the same for *any* target, anyway - we are simply being paranoid + in looking them up, rather than having their numbers hard-coded, in + any case!). + + Of course, there are really pathological cases such as the user + blasting the (ARCangel) target with an XBF giving a different + processor configuration, or even physically disconnecting the target + from the host machine and connecting a different target, between + issuing the 'detach' and the 'attach' commands (and that could change + the target's actionpoint configuration, if nothing else!) - but if + the user wants to do that then that is his problem! */ + arc_elf32_find_register_numbers(); + + lp_start_regnum = arc_aux_find_register_number("LP_START", ARC_HW_LP_START_REGNUM); + lp_end_regnum = arc_aux_find_register_number("LP_END", ARC_HW_LP_END_REGNUM); + icache_ivic_regnum = arc_aux_find_register_number("IC_IVIC", ARC_HW_IC_IVIC_REGNUM); + icache_control_regnum = arc_aux_find_register_number("IC_CTRL", ARC_HW_IC_CTRL_REGNUM); + dcache_ivdc_regnum = arc_aux_find_register_number("DC_IVDC", ARC_HW_DC_IVDC_REGNUM); + dcache_control_regnum = arc_aux_find_register_number("DC_CTRL", ARC_HW_DC_CTRL_REGNUM); + + + /* Just to be sure that it is not in the target stack... */ + (void) unpush_target (&jtag_target_ops); + + /* Now try to open the JTAG interface. */ + if (open_JTAG_interface(from_tty)) { - unsigned int pc, lp_start, lp_end, lp_count; - arc_debug_read_core_register (ARC_LP_COUNT_REGNUM, &lp_count); - if (lp_count != 0) - { - arc_debug_read_aux_register (ARC_HW_PC_REGNUM, &pc); - arc_debug_read_aux_register (ARC_HW_LP_START_REGNUM, &lp_start); - - if (pc == lp_start) - { - extern struct breakpoint *breakpoint_chain; - struct breakpoint *b; - arc_debug_read_aux_register (ARC_HW_LP_END_REGNUM, &lp_end); - - for (b = breakpoint_chain; b; b = b->next) - { - /* lp_end is the address of the last instruction + the - size of the last instruction. We could use the - disassembler and find out the size, or just try both - possible sizes. */ - if ((b->enable_state == bp_enabled && !b->pending) && - b->loc->address == lp_end-4 || b->loc->address == lp_end-2) - { - warning ("Did you set a breakpoint on the last instruction of \n\ -a Zero Overhead Loop ? Such breakpoints do not work properly."); - } - } - } - } + /* If a reset is required, do it now, in case it is necessary to reset + the target clock sources to their defaults before trying to access + the target's auxiliary registers! */ + if (reset_required) + { + arc_reset_board(); + arc_jtag_ops.reset_board(); + } + + if (fpga == CONFIGURED) + { + /* If we are going to blast the board, don't bother halting the + processor first. */ + if ((xbf_file == NULL) && !halt_processor_on_connection()) + { + /* We could not halt the processor. */ + close_JTAG_interface(FALSE); + return; + } + } + + /* If we have been given an XBF file. */ + if (xbf_file) + { + /* Try to blast the board. + N.B. if the blasting operation fails for any reason, + arc_blast_board calls error and does not return! */ + arc_blast_board(xbf_file, from_tty); + } + + /* Get out of user mode so that we can access anything anywhere. */ + change_status32(CLEAR_USER_BIT); + + /* We do not know whether the target processor supports actionpoints until + after we have connected to it, as we have to read the AP_BUILD + configuration register to find that out. */ + (void) arc_initialize_actionpoint_ops(&jtag_target_ops); + + (void) push_target (&jtag_target_ops); + + if (!reset_required) + { + /* If we have been explicitly told NOT to reset the board, it is + most likely because we have connected to a target upon which a + program is running and we want to debug that program - so assume + we have a program ready for execution on the target. */ + target_mark_running(&jtag_target_ops); + arc_program_is_loaded = TRUE; + + /* Set to_has_execution back to 0; this stops the user getting the + + A program is being debugged already. + Are you sure you want to change the file? (y or n) n + + message on issuing the 'file' command after the connection. */ + current_target.to_has_execution = 0; + } + + if (from_tty) + printf_filtered (_("Connected to the " ARC_TARGET_NAME " target.\n")); } -#endif - return inferior_ptid; + else + error(_("Can not connect to target")); } -static unsigned int -arc_get_hw_regnum_mapping ( int regno ) -{ - int i; - - if (regno >= ARC_STATUS_REGNUM -#ifdef ARC4_JTAG - && regno <= ARC_DEBUG_REGNUM -#else - && regno <= ARC_AUX_IRQ_PENDING_REGNUM -#endif - ) - return arc_aux_reg_map[regno - ARC_STATUS_REGNUM].hw_regno; - - for ( i = 0 ; i < (sizeof(arc_bcr_reg_info) / sizeof (struct arc_reg_info)) ; i++) - { - if (regno == arc_bcr_reg_info[i].gdbregno) - return arc_bcr_reg_info[i].hw_regno; - } - return -1; +/* Close the connection to the target. */ + +static void +arc_jtag_close (int quitting) +{ + ENTERMSG; + close_JTAG_interface(FALSE); } -/* Function: arc_debug_fetch_regs. - * Parameters : - * 1. int regnum: Register number. If register number is -1.Fetch - * all the registers.Read all core registers here. - * Returns : void - * Description: - * Set up regcache_raw_supply(current_regcache,regno) - * - */ -void -arc_debug_fetch_regs (int regno) +/* Attach to the debug target without resetting the board. + + Parameters: + args : user arguments to the 'attach' command (ignored) + from_tty: non-zero if the 'attach' command was issued at the terminal +*/ + +static void +arc_jtag_attach (char *args, int from_tty) { - /* Read all core registers */ - ENTERARGS("%d",regno); - - int dummyvalue = 0xABCDABCD; - unsigned int hw_regno; - unsigned int read_buf; - - if( regno < ARC_NR_CORE_REGS ) - { - hw_regno = regno; - if(arc_jtag_ops.jtag_read_core_reg(regno,&read_buf)==JTAG_READ_FAILURE) - error("Failure reading from core register 0x%x\n",regno); - } - else - { -#ifndef ARC4_JTAG - if( regno > ARC_NR_REGS) - error("Invalid Register Number\n"); -#endif - - hw_regno = arc_get_hw_regnum_mapping (regno); - if(arc_jtag_ops.jtag_read_aux_reg(hw_regno,&read_buf)==JTAG_READ_FAILURE) - error("Failure reading auxillary register 0x%x",hw_regno); - } - - if(debug_arc_jtag_target_message) + ENTERARGS("\"%s\" (%d)", args, from_tty); + + /* Try to open the JTAG interface. */ + if (open_JTAG_interface(from_tty)) { - printf_filtered("HW_Regno=0x%x",hw_regno); - printf_filtered("Read Regno 0x%x the value 0x%x\n",hw_regno,read_buf); + /* Try to halt the processor (if it is running). */ + if (halt_processor_on_connection()) + { + /* Check that the processor architecture is correct. */ + ARCHITECTURE_CHECK(current_gdbarch, + (current_objfile) ? current_objfile->obfd : NULL); + + if (from_tty) + printf_filtered (_("Connected to the " ARC_TARGET_NAME " target.\n")); + } } - regcache_raw_supply(current_regcache,regno,&read_buf); - - LEAVEMSG; + else + error(_("Can not connect to target")); } -/* Function: arc_debug_fetch_regs. - * Parameters : - * 1. int regnum: Register number. If register number is -1.Fetch - * all the registers.Read all core registers here. - * Returns : void - * Description: - * Use deprecated register information for this or regcache_read_unsigned . - * FIXME: would need to change to use regcache_raw_supply instead. - */ +/* Detach from the debug target without resetting the board. + Parameters: + args : user arguments to the 'detach' command (ignored) + from_tty: non-zero if the 'detach' command was issued at the terminal +*/ -void -arc_debug_store_regs (int regno) +static void +arc_jtag_detach (char *args, int from_tty) { - /* write_all core registers */ - ENTERARGS("%d", regno); - unsigned int hw_regno; - unsigned int write_buf; - - if(debug_arc_jtag_target_message) - printf_filtered("\n%d",regno); - - regcache_raw_collect(current_regcache,regno,&write_buf); - if( regno < ARC_NR_CORE_REGS ) - { - if(arc_jtag_ops.jtag_write_core_reg(regno,write_buf)==JTAG_WRITE_FAILURE) - error("Failure writing 0x%x to core register 0x%x",write_buf,regno); - } - else - { -#ifndef ARC4_JTAG - if (regno > ARC_NR_REGS) - error ("Invalid register number \n"); -#endif - - hw_regno = arc_get_hw_regnum_mapping (regno); + ENTERMSG; + close_JTAG_interface(TRUE); +} - if(debug_arc_jtag_target_message) - printf_filtered("Writing to regno 0x%x the value 0x%x", - hw_regno,write_buf); - if(arc_jtag_ops.jtag_write_aux_reg(hw_regno,write_buf)==JTAG_WRITE_FAILURE) - error("Failure writing 0x%x to auxillary register 0x%x\n",write_buf,hw_regno); - } -} +/* Cause the inferior on the debug target to resume execution, sending a signal + if necessary. + Parameters: + ptid : the thread id of the thread to be resumed (ignored) + step : 1 means single step, 0 run freely. + signal: the number of the signal to be sent -/* Function: arc_debug_prepare_to_store. - * Parameters : - * 1. int regnum: Register number. If register number is -1.Fetch - * all the registers.Read all core registers here. - * Returns : void - * Description: - * Use deprecated register information for this. - * FIXME: would need to change to use regcache_raw_supply instead. - */ + N.B. signals are not supported. */ -/* This gets called just before store_regs */ -void -arc_debug_prepare_to_store (void) +static void +arc_jtag_resume (ptid_t ptid, int step, enum target_signal signal) { - /* does nothing . Why is this around ? */ - ENTERMSG; -} + ENTERARGS("%d, %d, %d", ptid.pid, step, signal); -/* Read or write memory */ + if (signal != TARGET_SIGNAL_0) + error(_("Signals are not supported by the " ARC_TARGET_NAME " target")); + /* If we cleared the User bit in the STATUS32 bit the last time that + execution halted, restore it now. */ + change_status32(RESTORE_USER_BIT); + /* Software breakpoints may have been set/removed, and data in main memory + may have been altered, so invalidate (and flush!) the instruction and + data caches before restarting! -/* Function: arc_debug_xfer_memory. - * Parameters : - * 1. int regnum: Register number. If register number is -1.Fetch - * all the registers.Read all core registers here. - * Returns : void - * Description: - * This has been superceded by target_xfer_memory_partial. - * - */ -int -arc_debug_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len, int write, - struct mem_attrib *attrib, struct target_ops *target) -{ - /* There is no xfer_memory . Its been deprecated in 6.3 .Replace - * this by target_xfer_memory_partial . - */ - ENTERARGS("memaddr=%lx, myaddr=%lx, len=%d, write=%d", - memaddr, (unsigned long)myaddr, len, write); - - return len; /* success */ -} + N.B. arc_jtag_open disabled the caches, so what is the point of doing this? + Also, invalidating a disabled cache when DC_CTRL.IM = 1 seems to have + the effect of overwriting valid data!!!!! */ +// invalidate_caches (); - + /* The DEBUG User bit must be set if breakpoints are to be allowed in user + mode. We could set it in target_open, but something (the user?) might clear it. + So we set it every time we resume (if stepping, we set the extra bit(s) we + need in the DEBUG register in the same operation). */ -LONGEST -arc_debug_xfer_partial (struct target_ops *ops, - enum target_object object, - const char *annex, - void *readbuf, - const void *writebuf, - ULONGEST offset, - LONGEST len) -{ - - int i=0, read_num=0, temp_len=0; - unsigned int small_buf; - char query_type; - ULONGEST temp_offset=0; - if(debug_arc_jtag_target_message) - printf("..Entered arc_debug_xfer_partial()...with offset 0x%x\n",(unsigned int)offset); - /* Handle memory */ - if (object == TARGET_OBJECT_MEMORY) + if (step) { - int saved_status32; - int xfered=0; - int attempts; - errno = 0; - - /* Get out of user mode so that we can read/write anything anywhere. */ - saved_status32 = clear_status32_user_bit (); - - if (writebuf != NULL) - { - char *buffer=(char *)xmalloc(4); - char *temp_buf = (char *)writebuf; - - /* Address alignment to integral multiple of four */ - temp_offset = offset; - temp_len = temp_offset % 4; - - i = 0; - if(temp_len) - { - temp_offset = offset - temp_len; - if(debug_arc_jtag_target_message) - { - printf("---- Aligning-----------\n"); - printf("calling write_chunk at 0x%x where \ -offset = 0x%x\n", - (unsigned int)temp_offset,(unsigned int)offset); - } - - attempts = 0; - do{ - if (attempts++ == MEMORY_TRANSFER_ATTEMPTS) - return 0; - xfered = arc_jtag_ops.jtag_memory_chunk_read(temp_offset, - (unsigned int *)buffer,4); - }while(xfered != 4); - - for(i=0;i<len && i<(4-temp_len);i++) - - buffer[i+temp_len]=temp_buf[i]; - - attempts = 0; - do{ - if (attempts++ == MEMORY_TRANSFER_ATTEMPTS) - return 0; - xfered = arc_jtag_ops.jtag_memory_chunk_write(temp_offset, - (unsigned int *)buffer,4); - }while(xfered != 4); - - - temp_buf = (char *)writebuf + i; - temp_offset = offset + i; - len = len - i; - } - if(len>0) - len =arc_jtag_ops.jtag_memory_chunk_write(temp_offset, - (unsigned int *)temp_buf,len); - if(debug_arc_jtag_target_message) - printf("...leaving arc_debug_xfer_partial() write.. \ -with return value %d",(int)len); - - restore_status32_user_bit (saved_status32); - return (len + i); - } - else - { - char *buffer=(char *)xmalloc(4); - char *temp_buf = (char *)readbuf; - /* Address alignment to integral multiple of four */ - temp_offset= offset; - temp_len= temp_offset % 4 ; - - i = 0; - if(temp_len) - { - temp_offset = offset - temp_len; - if(debug_arc_jtag_target_message) - { - printf("---- Aligning-----------\n"); - printf("calling read_chunk at 0x%x where offset =0x%x \n", - (unsigned int)temp_offset,(unsigned int)offset); - } - - attempts = 0; - do{ - if (attempts++ == MEMORY_TRANSFER_ATTEMPTS) - return 0; - xfered = arc_jtag_ops.jtag_memory_chunk_read(temp_offset,(unsigned int *)buffer,4); - }while(xfered != 4); - - for(i=0;i<len && i<(4-temp_len);i++) - temp_buf[i]=buffer[i+temp_len]; - - temp_buf = (char *)readbuf + i; - temp_offset = offset + i; - len = len - i; - } - if(len>0) - len = arc_jtag_ops.jtag_memory_chunk_read(temp_offset,(unsigned int *)temp_buf,len); - if(debug_arc_jtag_target_message) - { - printf("\nlen=%d",(int)len + temp_len); - printf("...leaving arc_debug_xfer_partial() read.. \ -with return value %d", - (int)len + temp_len); - } - - restore_status32_user_bit (saved_status32); - return (len + i); - - } + ARC_RegisterContents mask = 0; - } + DEBUG("setting DEBUG.IS bit for single-step\n"); - /* ARC auxillary registers: they are 32bits wide and are in a 32 bit - address space, although only part of the address space is used. */ - else if (object == ARC_TARGET_OBJECT_AUXREGS) - { - unsigned int regno; - - if (readbuf) - { - for (regno = offset; regno < offset+len; ++regno) - { - unsigned int rd; - - if (arc_jtag_ops.jtag_read_aux_reg (regno, &rd) == JTAG_READ_FAILURE) - { - return (regno - offset); - } - ((int *)readbuf)[regno - offset] = rd; - } - } - else if (writebuf) - { - for (regno = offset; regno < offset+len; ++regno) - { - if (arc_jtag_ops.jtag_write_aux_reg (regno, ((int*)writebuf)[regno - offset]) == - JTAG_WRITE_FAILURE) - { - return (regno - offset); - } - } - } - - /* success */ - return (LONGEST)len; + /* The mask for single-stepping differs between ARC600 and ARC700. */ + if (IS_ARC700) + mask = DEBUG_INSTRUCTION_STEP; + else + if (IS_ARC600) + mask = DEBUG_INSTRUCTION_STEP | DEBUG_SINGLE_STEP; + + /* Allow breakpoints in User mode, and set the IS bit in the DEBUG + register for hardware single instruction stepping. */ + if (!set_debug_user_bit (mask)) + error(_("Can not single-step one instruction")); } else { - printf("\nRequested target_object not yet supported with arc-jtag"); + /* Allow breakpoints in User mode (no extra bits required). */ + (void) set_debug_user_bit (0); + start_processor(); } - return -1; - + LEAVEMSG; } +/* Wait for execution on the target to halt (for whatever reason). + Parameters : + ptid : ignored + status: set to indicate status at end of the wait +*/ -void -arc_debug_files_info (struct target_ops *target) +static ptid_t +arc_jtag_wait (ptid_t ptid, struct target_waitstatus *status) { - /* Do nothing. Just say its a remote target */ - ENTERMSG; -} + ENTERMSG; + /* Execute the program on the target processor. */ + arc_elf32_execute(status, + NULL, + start_processor, + stop_processor); -/* Function: arc_debug_insert_breakpoint - * Parameters : - * 1. CORE_ADDR addr: Address for breakpoint. - * 2. char * contents: Contents for the breakpoint. - * Returns : int - * Description: - * See if you can insert a hardware breakpoint using the actionpoints - * interface. Use brk_s if architecture is ARC700 and you need to use - * a software breakpoint.The gdbarch breakpoint should be initialized to - * the right value if used with target_arc_debug. - * - */ + /* The target has now halted. */ + if (status->kind == TARGET_WAITKIND_EXITED) + target_mark_exited (&jtag_target_ops); -int -arc_debug_insert_breakpoint (CORE_ADDR addr, char *contents) -{ - - ENTERARGS("%x", (unsigned int)addr); -#ifndef ARC4_JTAG - unsigned int bp = 0x20207fff; /*FIXMEA: what does 0x2020 stand for ?*/ -#else - unsigned int bp = 0x1ffffe00; -#endif - unsigned int r; - int instr_size; - const unsigned char *breakpt_instr; - breakpt_instr=BREAKPOINT_FROM_PC(&addr,&instr_size); - - /* save the existing value */ - /* r==0 means the read succeeded */ - if(debug_arc_jtag_target_message) - printf_filtered ("instrcution size = %d and instruction 0x%x", - instr_size, *(unsigned int *)breakpt_instr); - r = target_read_memory (addr, contents, instr_size); - /* put the breakpoint */ - if(r==0) - r = target_write_memory (addr, (char *)&bp, instr_size); - return r; + /* Get out of user mode so that we can access anything anywhere. */ + change_status32(CLEAR_USER_BIT); + + /* Inform the actionpoints module that the target has halted. */ + arc_target_halted(); + + /* Bug #1311 (ARC600): Setting a breakpoint on the last instruction of a + ZOL causes GDB to stop at LP_START. Detect this condition and warn the + user. */ + if (IS_ARC600) + { + ARC_RegisterContents pc, lp_start, lp_end, lp_count; + + if (arc_read_jtag_core_register(ARC_LP_COUNT_REGNUM, &lp_count, TRUE) && (lp_count != 0) && + arc_read_jtag_aux_register (arc_pc_regnum, &pc, TRUE) && + arc_read_jtag_aux_register (lp_start_regnum, &lp_start, TRUE) && (pc == lp_start) && + arc_read_jtag_aux_register (lp_end_regnum, &lp_end, TRUE)) + { + struct breakpoint *b; + + for (b = breakpoint_chain; b != NULL; b = b->next) + { + /* lp_end is the address of the last instruction + the size of + the last instruction. We could use the disassembler and find + out the size, but it's easier just to try both possible sizes. */ + if ((b->enable_state == bp_enabled) && + (b->loc->address == lp_end - 4 || b->loc->address == lp_end - 2)) + { + warning(_("did you set a breakpoint on the last instruction of a" + "Zero Overhead Loop? Such breakpoints do not work properly.")); + } + } + } + } + + LEAVEMSG; + + return inferior_ptid; } -/* Function: arc_debug_remove_breakpoint. - * Parameters : - * 1. CORE_ADDR addr: Address. - * 2. char * contents : contents. - * Returns : int. - * Description: - * Write the old contents back for the breakpoint. - * - */ +/* This gets called just before store_regs. */ -int -arc_debug_remove_breakpoint (CORE_ADDR addr, char *contents) +static void +arc_jtag_prepare_to_store (struct regcache *regcache) { - ENTERARGS("%x, %lx", (unsigned int)addr, *(unsigned long *)contents); - - /* write the old value back */ -#ifdef ARC4_JTAG - return target_write_memory (addr, contents, 4); -#else - return target_write_memory (addr, contents, 2); -#endif + ENTERMSG; } +static void +arc_jtag_files_info (struct target_ops *target) +{ + /* Do nothing. */ + ENTERMSG; +} -/* Function: arc_debug_kill - * Parameters : void. - * Returns : void. - * Description: Heavy duty arsenal.Kill the process. - * Maybe we do a board reset and kill it. Write 1 to Halt - * in Status32. - */ +/* Heavy duty arsenal. Kill the process. + Maybe we should do a board reset and kill it. */ -void -arc_debug_kill (void) +static void +arc_jtag_kill (void) { ENTERMSG; - /* Do stuff */ - target_mourn_inferior (); } -/* Function: arc_debug_load - * Parameters : - * 1. char * args: Arguments. - * 2. int from_tty: Which terminal. - * Returns : void. - * Description: Load the program into jtag. + +/* Create the inferior that will be executed upon the target. + + Parameters : + exec_file: the executable file containing the program to be executed + args : the command line arguments to be passed to the program + env : the environment (name/value pairs) for the program + from_tty : ignored */ -void -arc_debug_load (char *args, int from_tty) +static void +arc_jtag_create_inferior (char *exec_file, char *args, char **env, int from_tty) { - /* Write to RAM of the ARC700 board by running through the sections .*/ - asection *bss_section; - CORE_ADDR bss_addr; - bfd_size_type bss_size; - char *zero_buf; - int target_errno; + arc_elf32_create_inferior(exec_file, args, env, &jtag_target_ops); - ENTERARGS("%s", args); + /* Why are the caches disabled anyway? Particularly as arc_jtag_resume + invalidates them before each restart? */ + disable_caches(); +} - generic_load(args, from_tty); - /* Zero the bss, if it exists. */ - bss_section = bfd_get_section_by_name (exec_bfd, ".bss"); - if (bss_section) - { - bss_addr = bfd_section_lma (exec_bfd, bss_section); - bss_size = bfd_get_section_size (bss_section); - zero_buf = (char *)xcalloc (bss_size, 1); - - if (debug_arc_jtag_target_message) - printf_filtered("%s: bss at %x, size = %x\n", __FUNCTION__, (unsigned int)bss_addr,(unsigned int)bss_size); - - target_errno = target_write_memory (bss_addr, zero_buf, bss_size); - free (zero_buf); - if (target_errno) - { - error ("load: error zeroing bss: %s\n", strerror(target_errno)); - } - } - else - { - if (debug_arc_jtag_target_message) - printf_filtered("%s: no bss\n", __FUNCTION__); - } - - clear_symtab_users(); +/* Mourn the inferior. */ + +static void +arc_jtag_mourn_inferior (void) +{ + ENTERMSG; + +// (void) unpush_target (&jtag_target_ops); + generic_mourn_inferior (); + current_target.to_has_execution = 0; } -/* Function: arc_debug_create_inferior - * Parameters : - * 1. char * exec_file: - * 2. char * args: - * 3. char ** env; - * Returns : void. - * Description: Set up sanity values for arc_debug_create_inferior. More thought - * needed for this. - */ +/* Check whether the given thread is alive. */ -void -arc_debug_create_inferior (char *exec_file, char *args, char **env,int dummy) +static int +arc_jtag_thread_alive (ptid_t ptid) { - ENTERARGS("%s,%s", exec_file, args); - - /* If no exec file handed to us, get it from the exec-file command - -- with a good, common error message if none is specified. */ - if (exec_file == 0) - exec_file = get_exec_file (1); - - /* We dont really have a PID or anything, but GDB uses this value to check - if the program is running. */ - inferior_ptid.pid = 42; - - clear_proceed_status(); - /* -1 means resume from current place - TARGET_SIGNAL_0 means dont give it any signal - Last arg should be true if you want to single step */ - //proceed ((CORE_ADDR) -1, TARGET_SIGNAL_0, 0); - proceed (bfd_get_start_address (exec_bfd), TARGET_SIGNAL_0, 0); + ENTERMSG; + + /* We only have one thread. */ + return 1; } -/* Function: arc_debug_mourn_inferior - * Parameters :void. - * Returns : void. - * Description: Set up sanity values for arc_debug_create_inferior. More thought - * needed for this. - */ +/* Check whether our debug target is runnable: return 1 if it is, 0 otherwise. */ -void -arc_debug_mourn_inferior (void) +static int +arc_jtag_can_run (void) { - ENTERMSG; + /* If we are connected to the JTAG i/f, and a program is loaded. */ + return (arc_jtag_ops.status == JTAG_OPENED) && arc_program_is_loaded; +} + - unpush_target (&arc_debug_ops); +/* We do not support asynchronous execution of the target program (i.e. commands + like 'run' or 'continue' or 'step' can not be executed in background mode + by appending a '&' to them) so we do not need to implement the target stop + operation (called by the 'interrupt' command); interrupting a running program + is handled by the Ctrl-C mechanism. */ - generic_mourn_inferior (); +#if 0 +static void +arc_jtag_stop (void) +{ + ENTERMSG; } +#endif -/* Function: arc_debug_mourn_inferior - * Parameters :ptid_t ptid. - * Returns : 1 always. - * Description: Checks for return values . - */ +/* -------------------------------------------------------------------------- */ +/* 7) local functions implementing commands */ +/* -------------------------------------------------------------------------- */ +/* Print processor-variant information. */ -int -arc_debug_thread_alive (ptid_t ptid) +static void +arc_print_processor_variant_info (char *arg, int from_tty) { - ENTERMSG; - return 1; + printf_filtered + (_("%s\n"), + arc_version_image(arc_get_architecture(arc_read_jtag_aux_register))); } -/* Function: arc_debug_stop - * Parameters: void - * Returns: void. - * Description: Stop the Processor. We stop by writing FH bit to Debug Register . - * write 1 to the FH bit in the Debug register after - * polling for the DEBUG register to have no loads pending . - */ -void -arc_debug_stop (void) -{ +/* Reset the target board. */ - ENTERMSG; - int val = 0x2; - /* Stop using the FH bit in the debug register. */ - arc_debug_write_aux_register (ARC_HW_DEBUG_REGNUM, &val); - +static void +arc_jtag_reset_board (char *arg, int from_tty) +{ + /* Make sure the GPIO interface is open. */ + if (gpio_open()) + { + printf_filtered(_("Attempting to reset target board...\n")); + + if (arc_jtag_ops.status == JTAG_OPENED) + { + /* Try to force the processor to halt. */ + stop_processor(); + } + + /* Try to reset the board. */ + arc_reset_board(); + arc_jtag_ops.reset_board(); + + if (arc_jtag_ops.status == JTAG_OPENED) + { + /* The ARC actionpoint registers are cleared upon reset, so it is + necessary to restore any actionpoints that were set. */ + if (!arc_restore_actionpoints_after_reset()) + warning(_("can not restore hardware actionpoints")); + } + } } -/* Read core register. Return 0 on success. */ -static int -arc_debug_read_core_register (int hwregno, int *buf) + +/* List the ARC target processor actionpoints. */ + +static void +arc_list_actionpoints (char *arg, int from_tty) { - int rd; - if(arc_jtag_ops.jtag_read_core_reg(hwregno,&rd)==JTAG_READ_FAILURE) - error("Failure reading auxillary register 0x%x",hwregno); - *buf = rd; - return 0; + /* gdb manages breakpoints by deleting them from the target as soon as it + has halted, then re-inserting them again immediately before execution is + resumed (no, I don't know why either, unless it is to make generating a + disassembly display easier by removing all the s/w b/ps from the code) - + so in order to display what actionpoints are currently in use, we must + temporarily re-insert the breakpoints! */ + insert_breakpoints(); + arc_display_actionpoints(); + (void) remove_breakpoints(); } -/* Read aux register. Return 0 on success. */ -static int -arc_debug_read_aux_register (int hwregno, int *buf) + +/* -------------------------------------------------------------------------- */ +/* 8) initialization functions */ +/* -------------------------------------------------------------------------- */ + +/* Initialize the JTAG target operations. */ + +static void +initialize_jtag_target_ops (void) { - int rd; - if(arc_jtag_ops.jtag_read_aux_reg(hwregno,&rd)==JTAG_READ_FAILURE) - error("Failure reading auxillary register 0x%x",hwregno); - *buf = rd; - return 0; + ENTERMSG; + + jtag_target_ops.to_data = &operations; + + jtag_target_ops.to_shortname = ARC_TARGET_NAME; + jtag_target_ops.to_longname = "Remote JTAG debug target (ARC Processors)"; + jtag_target_ops.to_doc = "Remote JTAG debug target (ARC Processors)"; + + jtag_target_ops.to_open = arc_jtag_open; + jtag_target_ops.to_close = arc_jtag_close; + jtag_target_ops.to_attach = arc_jtag_attach; + jtag_target_ops.to_detach = arc_jtag_detach; + jtag_target_ops.to_resume = arc_jtag_resume; + jtag_target_ops.to_wait = arc_jtag_wait; + + jtag_target_ops.to_fetch_registers = arc_elf32_fetch_registers; + jtag_target_ops.to_store_registers = arc_elf32_store_registers; + jtag_target_ops.to_prepare_to_store = arc_jtag_prepare_to_store; + jtag_target_ops.to_xfer_partial = arc_elf32_xfer_partial; + jtag_target_ops.to_files_info = arc_jtag_files_info; + + jtag_target_ops.to_insert_breakpoint = arc_elf32_insert_breakpoint; + jtag_target_ops.to_remove_breakpoint = arc_elf32_remove_breakpoint; + + jtag_target_ops.to_kill = arc_jtag_kill; + jtag_target_ops.to_load = arc_elf32_load_program; + + jtag_target_ops.to_create_inferior = arc_jtag_create_inferior; + jtag_target_ops.to_mourn_inferior = arc_jtag_mourn_inferior; + jtag_target_ops.to_thread_alive = arc_jtag_thread_alive; +// jtag_target_ops.to_stop = arc_jtag_stop; + jtag_target_ops.to_can_run = arc_jtag_can_run; + jtag_target_ops.to_terminal_inferior = NULL; + + jtag_target_ops.to_stratum = process_stratum; + + jtag_target_ops.to_has_all_memory = 1; + jtag_target_ops.to_has_memory = 1; + jtag_target_ops.to_has_stack = 0; /* Defer setting this until the program has been loaded. */ + jtag_target_ops.to_has_registers = 1; + jtag_target_ops.to_has_execution = 0; /* Defer setting this until the program has been started. */ + + jtag_target_ops.to_magic = OPS_MAGIC; } -/* Write aux register. Return 0 on success. */ -static int -arc_debug_write_aux_register (int hwregno, int *buf) + +/* -------------------------------------------------------------------------- */ +/* externally visible functions */ +/* -------------------------------------------------------------------------- */ + +/* Initialize the module. This function is called from the gdb core on start-up. */ + +void +_initialize_arc_jtag (void) { - if(arc_jtag_ops.jtag_write_aux_reg(hwregno, *buf)==JTAG_WRITE_FAILURE) - error("Failure writing 0x%x to auxillary register 0x%x\n",*buf,hwregno); - return 0; + ENTERMSG; + + operations.read_core_register = arc_read_jtag_core_register; + operations.write_core_register = arc_write_jtag_core_register; + operations.read_auxiliary_register = arc_read_jtag_aux_register; + operations.write_auxiliary_register = arc_write_jtag_aux_register; + operations.read_memory = read_words; + operations.write_memory = write_words; + operations.fill_memory = write_pattern; + + initialize_jtag_target_ops (); + add_target (&jtag_target_ops); + + /* Register ARC-specific commands with gdb. */ + + add_setshow_boolean_cmd(ARC_FSM_DEBUG_COMMAND, + no_class, + &arc_jtag_ops.state_machine_debug, + _("Set whether to print JTAG state machine debug messages.\n"), + _("Show whether to print JTAG state machine debug messages.\n"), + _("If set the JTAG state machine messages are printed.\n"), + NULL, + NULL, + &setlist, + &showlist); + + add_setshow_uinteger_cmd(ARC_JTAG_RETRY_COMMAND, + no_class, + &arc_jtag_ops.retry_count, + _("Set the number of attempts to be made for a JTAG operation.\n"), + _("Show the number of attempts to be made for a JTAG operation.\n"), + _("Indicates the number of times a JTAG operation is attempted before returning a failure.\n"), + NULL, + NULL, + &setlist, + &showlist); + + (void) add_cmd(ARC_CONFIGURATION_COMMAND, + class_info, + arc_print_processor_variant_info, + _("Show ARC configuration information.\n" + ARC_CONFIGURATION_COMMAND_USAGE), + &infolist); + + (void) add_cmd(ARC_RESET_BOARD_COMMAND, + class_obscure, + arc_jtag_reset_board, + _("Reset the board.\n" + ARC_RESET_BOARD_COMMAND_USAGE), + &cmdlist); + + (void) add_cmd(ARC_LIST_ACTIONPOINTS_COMMAND, + class_obscure, + arc_list_actionpoints, + _("List the processor actionpoints.\n" + ARC_LIST_ACTIONPOINTS_COMMAND_USAGE), + &cmdlist); } -/* Helper routine for commands added. */ -/* Print Processor Variant Info. */ -static void -arc_print_processor_variant_info (void) + +/* N.B. the core and auxiliary register contents read from or written to the + target via the arc-jtag-ops module are ALWAYS in little-endian format, + regardless of the endianness of the target processor. Given that the + values passed to/from the functions below are in host byte order, and + the host is little-endian (since the ARC gdb is currently built only + on an X86 Linux host), this means that no byte-swapping is required + here. This will require changing if the debugger is ever built upon a + big-endian host (such as a Sun). */ + + +/* Read a core register on the target. + + Parameters: + hw_regno : the ARC hardware number of the register + value : set to the contents of the register + warn_on_failure: TRUE if a warning should be issued if the read fails + + Result: TRUE if the register contents are read. */ + +Boolean +arc_read_jtag_core_register (ARC_RegisterNumber hw_regno, + ARC_RegisterContents *contents, + Boolean warn_on_failure) { - struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); - -#ifdef ARC4_JTAG - if (tdep->arc_processor_variant_info && - tdep->arc_processor_variant_info->arcprocessorversion == A4) - { - printf_filtered ("A4\n"); - } -#else - if (tdep->arc_processor_variant_info) - { - if(tdep->arc_processor_variant_info->arcprocessorversion == ARC700) - printf_filtered ("ARC700\n"); - else - printf_filtered ("ARC600\n"); - } -#endif - else + if (arc_jtag_ops.read_core_reg(hw_regno, contents) == JTAG_SUCCESS) { - printf_filtered ("ARC Processor Information not available \n"); + DEBUG("Read value 0x%08X from core register %d\n", *contents, hw_regno); + return TRUE; } + if (warn_on_failure) + arc_elf32_core_warning(ERROR_ON_READING_REGISTER, hw_regno); + return FALSE; } -static void -arc_print_bcr_regs (void) +/* Write a core register on the target. + + Parameters: + hw_regno : the ARC hardware number of the register + value : set to the contents of the register + warn_on_failure: TRUE if a warning should be issued if the write fails + + Result: TRUE if the register contents are written. */ + +Boolean +arc_write_jtag_core_register (ARC_RegisterNumber hw_regno, + ARC_RegisterContents contents, + Boolean warn_on_failure) { - int i = 0; - unsigned int bcrval = 0; - for ( i = 0 ; i < (sizeof(arc_bcr_reg_info) / sizeof (struct arc_reg_info)) ; i++) + if (arc_jtag_ops.write_core_reg(hw_regno, contents) == JTAG_SUCCESS) { - if(arc_jtag_ops.jtag_read_aux_reg (arc_bcr_reg_info[i].hw_regno, &bcrval)==JTAG_READ_FAILURE) - error("Failure reading auxillary register 0x%x",arc_bcr_reg_info[i].hw_regno); - printf_filtered ("[%02x] %-15s : 0x%02x\n",arc_bcr_reg_info[i].hw_regno, - arc_bcr_reg_info[i].name, bcrval ); + DEBUG("Written value 0x%08X to core register %d\n", contents, hw_regno); + return TRUE; } + if (warn_on_failure) + arc_elf32_core_warning(ERROR_ON_WRITING_REGISTER, hw_regno); + return FALSE; } -static void -arc_debug_jtag_reset_board (void) -{ - arc_jtag_ops.jtag_reset_board(); -} +/* Read an auxiliary register on the target. -/* Function: init_arc_debug_ops - * Parameters: void - * Returns: void. - * Description: Initialize the jtag operations. - */ + Parameters: + hw_regno : the ARC hardware number of the register + value : set to the contents of the register + warn_on_failure: TRUE if a warning should be issued if the read fails -void -init_arc_debug_ops (void) -{ - ENTERMSG; -#ifdef ARC4_JTAG - arc_debug_ops.to_shortname = "arcjtag"; - arc_debug_ops.to_longname = "Target for debugging an A4 board with JTAG."; - arc_debug_ops.to_doc = "Debug a remote A4 board via a JTAG"; /* to_doc */ -#else - arc_debug_ops.to_shortname = "arcjtag"; - arc_debug_ops.to_longname = "Target for debugging an ARC700 board with JTAG."; - arc_debug_ops.to_doc = "Debug a remote ARC700 board via a JTAG"; /* to_doc */ -#endif + Result: TRUE if the register contents are read. */ +Boolean +arc_read_jtag_aux_register (ARC_RegisterNumber hw_regno, + ARC_RegisterContents *contents, + Boolean warn_on_failure) +{ + if (arc_jtag_ops.read_aux_reg(hw_regno, contents) == JTAG_SUCCESS) + { + DEBUG("Read value 0x%08X from auxiliary register %d\n", *contents, hw_regno); + return TRUE; + } - arc_debug_ops.to_open = arc_debug_open; - arc_debug_ops.to_close = arc_debug_close; - arc_debug_ops.to_attach = arc_debug_attach; - arc_debug_ops.to_detach = arc_debug_detach; - arc_debug_ops.to_resume = arc_debug_resume; - arc_debug_ops.to_wait = arc_debug_wait; - - arc_debug_ops.to_fetch_registers = arc_debug_fetch_regs; - arc_debug_ops.to_store_registers = arc_debug_store_regs; - arc_debug_ops.to_prepare_to_store = arc_debug_prepare_to_store; - //arc_debug_ops.to_xfer_memory = arc_debug_xfer_memory; - arc_debug_ops.to_xfer_partial = arc_debug_xfer_partial; - arc_debug_ops.to_files_info = arc_debug_files_info; - arc_debug_ops.to_insert_breakpoint = arc_debug_insert_breakpoint; - arc_debug_ops.to_remove_breakpoint = arc_debug_remove_breakpoint; - arc_debug_ops.to_kill = arc_debug_kill; - arc_debug_ops.to_load = arc_debug_load; - - arc_debug_ops.to_create_inferior = arc_debug_create_inferior; - - arc_debug_ops.to_mourn_inferior = arc_debug_mourn_inferior; - arc_debug_ops.to_thread_alive = arc_debug_thread_alive; - arc_debug_ops.to_stop = arc_debug_stop; - - arc_debug_ops.to_terminal_inferior = NULL; - - - arc_debug_ops.to_stratum = process_stratum; - - arc_debug_ops.to_has_all_memory = 1; - arc_debug_ops.to_has_memory = 1; - arc_debug_ops.to_has_stack = 1; - arc_debug_ops.to_has_registers = 1; - arc_debug_ops.to_has_execution = 1; - - arc_debug_ops.to_magic = OPS_MAGIC; + if (warn_on_failure) + arc_elf32_aux_warning(ERROR_ON_READING_REGISTER, hw_regno); + return FALSE; } +/* Write an auxiliary register on the target. -void -_initialize_arc_debug (void) + Parameters: + hw_regno : the ARC hardware number of the register + value : the contents of the register + warn_on_failure: TRUE if a warning should be issued if the write fails + + Result: TRUE if the register contents are written. */ + +Boolean +arc_write_jtag_aux_register (ARC_RegisterNumber hw_regno, + ARC_RegisterContents contents, + Boolean warn_on_failure) { - ENTERMSG; - init_arc_debug_ops (); - add_target (&arc_debug_ops); - add_setshow_boolean_cmd("arcjtag-debug-target",no_class, - &debug_arc_jtag_target_message, - "Set whether to print arc jtag debug messages.\n", - "Show whether to print arc jtag debug messages.\n", - "If set the jtag debug messages from the target are \ -printed.\n", - "Whether to print debug jtag messages is %s.\n", - NULL,NULL,&setlist,&showlist); - - add_setshow_boolean_cmd("arcjtag-debug-statemachine",no_class, - &(arc_jtag_ops.arc_jtag_state_machine_debug), - "Set whether to print JTAG state machine \ -debug messages \n", - "Show whether to print JTAG state machine \ -debug messages \n", - "If set the JTAG state machine messages are \ -printed.\n", - "Whether to print JTAG state machine debug \ -messages is %s\n", - NULL,NULL,&setlist,&showlist); - - add_setshow_uinteger_cmd("arcjtag-retry-count",no_class, &arcjtag_retry_count, - "Set the number of attempts to be made for \ -a JTAG operation.\n", - "Show the number of attempts to be made for \ -a JTAG operation.\n", - "Indicates the number of times a JTAG operation \ -is attempted before returning a failure.\n", - "The number of times a JTAG operation is attempted \ -before returning a failure is %s.\n", - NULL, NULL, &setlist, &showlist); - - add_cmd ("arc-configuration", class_info, arc_print_processor_variant_info, - "Show ARC configuration information." , &infolist); - - add_cmd ("arc-bcr-registers", class_info, arc_print_bcr_regs, - "Show BCR Registers in the ARC Processor Variant", &infolist); - - add_cmd ("arc-reset-board", class_obscure, arc_debug_jtag_reset_board, - "Reset the board.", &cmdlist); + ARC_AuxRegisterDefinition *def = arc_find_aux_register_by_hw_number(hw_regno); + + if (def) + contents = arc_write_value(def, contents); + if (arc_jtag_ops.write_aux_reg(hw_regno, contents) == JTAG_SUCCESS) + { + DEBUG("Written value 0x%08X to auxiliary register %d\n", contents, hw_regno); + return TRUE; + } + + if (warn_on_failure) + arc_elf32_aux_warning(ERROR_ON_WRITING_REGISTER, hw_regno); + return FALSE; } + +/******************************************************************************/ diff --git a/gdb/arc-jtag.h b/gdb/arc-jtag.h index b770a53c6a3..68b747b3fc4 100644 --- a/gdb/arc-jtag.h +++ b/gdb/arc-jtag.h @@ -1,76 +1,64 @@ -/* Target dependent code for ARC700, for GDB, the GNU debugger. +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. - Copyright 2005 Free Software Foundation, Inc. + Copyright 2005, 2008, 2009 Free Software Foundation, Inc. Contributed by Codito Technologies Pvt. Ltd. (www.codito.com) - Authors: - Soam Vasani <soam.vasani@codito.com> + Authors: + Soam Vasani <soam.vasani@codito.com> + Richard Stuckey <richard.stuckey@arc.com> 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 + the Free Software Foundation; either version 3 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. -*/ - -#define ARC_DEBUG_REG_SH_BIT 0x40000000 - -#define RAUX(name, hwregno , desc, gdbregno, version) ARC_HW_##name##_REGNUM = hwregno , -#define RBCR(name, hwregno , desc, gdbregno, version) ARC_HW_##name##_REGNUM = hwregno , - - -enum arc_hw_regnums - { - #include "arc-regnums-defs.h" - /* Specific ARCAngel Registers for Caches. */ - ARC_HW_ICACHE_IVIC = 0x10 , /* Invalidate Cache. */ - ARC_HW_ICACHE_CONTROL = 0x11 , /* Disable ICache. ICache control. */ - ARC_HW_DCACHE_IVIC = 0x47, /* Invalidate Cache. */ - ARC_HW_DCACHE_CONTROL = 0x48 , /* Disable DCache. DCache Control. */ - }; - -#undef RBCR -#undef RAUX - -#define ARC_TARGET_OBJECT_AUXREGS -1 - -#define target_read_aux_reg(readbuf, offset, len) \ - (current_target.to_xfer_partial(¤t_target, \ - ARC_TARGET_OBJECT_AUXREGS, NULL, readbuf, NULL, offset, len)) - -#define target_write_aux_reg(writebuf, offset, len) \ - (current_target.to_xfer_partial(¤t_target, \ - ARC_TARGET_OBJECT_AUXREGS, NULL, NULL, writebuf, offset, len)) - - -static inline int -is_arc700 (void) -{ - struct gdbarch_tdep * tdep = gdbarch_tdep (current_gdbarch); - if(tdep->arc_processor_variant_info->arcprocessorversion == ARC700) - return 1; - return 0; - -} - -static inline int -is_arc600 (void) -{ - struct gdbarch_tdep * tdep = gdbarch_tdep (current_gdbarch); - if(tdep->arc_processor_variant_info->arcprocessorversion == ARC600) - return 1; - return 0; - -} - int debug_arc_jtag_target_message; + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This header file defines some operations provided by the ARC JTAG */ +/* module. */ +/* */ +/******************************************************************************/ + +#ifndef ARC_JTAG_H +#define ARC_JTAG_H + +/* ARC header files */ +#include "arc-support.h" + + +/* Operations for reading/writing core/auxiliary registers; these must be used + when access to the registers *specifically* via the JTAG i/f is required. + + N.B. the register contents returned by these functions, or supplied to them, + are in host byte order. */ + +Boolean arc_read_jtag_core_register (ARC_RegisterNumber hw_regno, + ARC_RegisterContents *contents, + Boolean warn_on_failure); + +Boolean arc_write_jtag_core_register (ARC_RegisterNumber hw_regno, + ARC_RegisterContents contents, + Boolean warn_on_failure); + +Boolean arc_read_jtag_aux_register (ARC_RegisterNumber hw_regno, + ARC_RegisterContents *contents, + Boolean warn_on_failure); + +Boolean arc_write_jtag_aux_register (ARC_RegisterNumber hw_regno, + ARC_RegisterContents contents, + Boolean warn_on_failure); + +#endif /* ARC_JTAG_H */ +/******************************************************************************/ diff --git a/gdb/arc-linux-tdep.c b/gdb/arc-linux-tdep.c index 14d34a668fa..479d8503545 100644 --- a/gdb/arc-linux-tdep.c +++ b/gdb/arc-linux-tdep.c @@ -1,506 +1,881 @@ -/* Target dependent code for ARC700, for GDB, the GNU debugger. +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. - Copyright 2005 Free Software Foundation, Inc. + Copyright 2005, 2008, 2009 Free Software Foundation, Inc. - Authors: - Soam Vasani <soam.vasani@codito.com> - Ramana Radhakrishnan <ramana.radhakrishnan@codito.com> + Contributed by Codito Technologies Pvt. Ltd. (www.codito.com) + + Authors: + Soam Vasani <soam.vasani@codito.com> + Ramana Radhakrishnan <ramana.radhakrishnan@codito.com> + Richard Stuckey <richard.stuckey@arc.com> 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 + the Free Software Foundation; either version 3 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 <string.h> + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This module provides support for the ARC processor family's target */ +/* dependencies which are specific to the arc-linux-uclibc configuration */ +/* of the ARC gdb. */ +/* */ +/* Functionality: */ +/* This module provides a number of operations, including: */ +/* */ +/* 1) a function which returns the name of a register, given its number */ +/* */ +/* 2) a function which determines whether a given register belongs to a */ +/* particular group (e.g. the group of registers which should be saved */ +/* and restored across a function call) */ +/* */ +/* 3) a function which prints out registers */ +/* */ +/* */ +/* Usage: */ +/* The module exports a function _initialize_arc_linux_tdep: the call to */ +/* this function is generated by the gdb build mechanism, so this function*/ +/* should not be explicitly called. */ +/* */ +/* Some of the operations provided by this module are registered with gdb */ +/* during initialization; gdb then calls them via function pointers, */ +/* rather than by name (this allows gdb to handle multiple target */ +/* architectures): */ +/* */ +/* set_gdbarch_XXX (gdbarch, <function>); */ +/* */ +/* */ +/* Register Numbering Scheme: */ +/* The N target processor registers are assigned gdb numbers which form a */ +/* contiguous range starting at 0. The scheme used is: */ +/* */ +/* 0 .. 26 : core registers R0 .. R26 */ +/* 27 : BTA (Branch Target Address) auxiliary register */ +/* 28 : LP_START auxiliary register */ +/* 29 : LP_END auxiliary register */ +/* 30 : LP_COUNT core register (R60) */ +/* 31 : STATUS32 auxiliary register */ +/* 32 : BLINK (Branch Link) core register (R31) */ +/* 33 : FP (Frame Pointer) core register (R27) */ +/* 34 : SP (Stack Pointer) core register (R28) */ +/* 35 : EFA (Exception Fault Address) auxiliary register */ +/* 36 : RET (Exception Return Address) auxiliary register */ +/* 37 : ORIG_R8 */ +/* 38 : STOP_PC */ +/* */ +/* N.B. 1) core registers R61 and R62 are not included in the scheme, as */ +/* R61 is reserved, and R62 is not a real register; */ +/* */ +/* 2) core registers R29 (ILINK1), R30 (ILINK2) and R63 (PCL) are */ +/* not included; */ +/* */ +/* 3) extension core registers R32 .. R59 are not included; */ +/* */ +/* 4) most auxiliary registers (including all Build Configuration */ +/* Registers) are not included. */ +/* */ +/******************************************************************************/ + +/* gdb header files */ #include "defs.h" #include "osabi.h" -#include "frame.h" #include "regcache.h" -#include "gdb_assert.h" #include "inferior.h" #include "reggroups.h" #include "solib-svr4.h" -#include "symtab.h" -#include "objfiles.h" #include "block.h" +#include "regset.h" +#include "dis-asm.h" +#include "opcode/arc.h" +#include "gdb_assert.h" +/* ARC header files */ +#include "config/arc/tm-linux.h" +#include "arc-linux-tdep.h" +#include "arc-support.h" #include "arc-tdep.h" -#include "regset.h" +#include "opcodes/arcompact-dis.h" -//#define ARC_DEBUG 1 -/* Default Breakpoint instructions used for - ARC700 Linux -*/ -unsigned int arc700_linux_breakpoint_size = 2; -unsigned char arc700_linux_breakpoint_insn[2] = { 0x3e,0x78 } ; +/* -------------------------------------------------------------------------- */ +/* local data */ +/* -------------------------------------------------------------------------- */ + +#define STATUS32_L 0x00000100 -static const char * -arc_linux_register_name (int regno) -{ - static char linux_names[][10] = {"r0", "r1", "r2", "r3", "r4", "r5", "r6", - "r7", "r8", "r9", "r10", "r11", "r12", "r13", - "r14", "r15", "r16", "r17", "r18", "r19", "r20", - "r21", "r22", "r23", "r24", "r25", "r26", - - "bta", - "lp_start", "lp_end", "lp_count", - "status32", "blink", - "fp", "sp", "efa", - /* linux-only registers */ - "ret", "orig_r8", "pc", - - /* pseudo-regs */ - "ilink1", "ilink2", "eret", - "status_l1", "status_l2", "erstatus" }; - - gdb_assert(ARRAY_SIZE (linux_names) == NUM_REGS + NUM_PSEUDO_REGS); - gdb_assert(regno >=0 && regno < NUM_REGS + NUM_PSEUDO_REGS); - - return linux_names[regno]; -} -/* - * The kernel stores only one of (ilink1,ilink2,eret). This is stored in - * the ret "register". ilink1 is stored when the kernel has been entered - * because of a level 1 interrupt, etc. - * - * Same story for (status_l1, status_l2, erstatus). - * - * This disambiguity has been fixed by adding orig_r8 to pt_regs. - * It will take the following values - - * 1. if an exception of any kind occurs then orig_r8 >= 0 - * 2. Int level 1 : -1 - * 3. Int level 2 : -2 - * - * Registers whose value we don't know are given the value zero. +/* Default breakpoint instruction used for ARC700 Linux. */ +static const unsigned char le_breakpoint_instruction[] = { 0x3e, 0x78 }; +static const unsigned char be_breakpoint_instruction[] = { 0x78, 0x3e }; + + +/* This array holds the object code of two instructions: + mov r8,nr_sigreturn + swi */ -static void -arc_linux_pseudo_register_read (struct gdbarch *gdbarch, struct regcache *regcache, - int regno, void *buf) +static const gdb_byte arc_sigtramp_insns[] = { 0x8a, 0x20, 0xc1, 0x1d, + 0x6f, 0x22, 0x3f, 0x00 }; + +#define SIGTRAMP_INSNS_LENGTH sizeof(arc_sigtramp_insns) + + +/* N.B. the array size is specified in the declaration so that the compiler + will warn of "excess elements in array initializer" if there is a + mismatch (but not of too few elements, unfortunately!). */ +static const char *register_names[ARC_NR_REGS + ARC_NR_PSEUDO_REGS] = { - int status32, ret, orig_r8; - regcache_cooked_read (current_regcache, ARC_ORIG_R8_REGNUM, &orig_r8); + "r0", "r1", "r2", "r3", "r4", "r5", "r6", + "r7", "r8", "r9", "r10", "r11", "r12", "r13", + "r14", "r15", "r16", "r17", "r18", "r19", "r20", + "r21", "r22", "r23", "r24", "r25", "r26", + + "bta", + "lp_start", + "lp_end", + "lp_count", + "status32", + "blink", + "fp", + "sp", + "efa", + + /* Linux-only registers. */ + "ret", + "orig_r8", + "pc", // stop pc + + /* Pseudo-regs. */ + "ilink1", + "ilink2", + "eret", + "status_l1", + "status_l2", + "erstatus" +}; - if(regno == ARC_ILINK1_REGNUM || - regno == ARC_ILINK2_REGNUM || - regno == ARC_ERET_REGNUM) - { - regcache_cooked_read (current_regcache, ARC_RET_REGNUM, &ret); - if(regno == ARC_ILINK1_REGNUM) - *((unsigned int *)buf) = ((orig_r8 == -1) ? ret : 0); - else if(regno == ARC_ILINK2_REGNUM) - *((unsigned int *)buf) = ((orig_r8 == -2) ? ret : 0); - else if(regno == ARC_ERET_REGNUM) - *((unsigned int *)buf) = ((orig_r8 >= 0) ? ret : 0); +/* Mapping between the general-purpose registers in `struct sigcontext' format + and GDB's register cache layout. + + arc_linux_sc_reg_offset[i] is the sigcontext offset of GDB regnum `i'. */ + +/* From <asm/sigcontext.h>. */ +static const int arc_linux_sc_reg_offset[ARC_NR_REGS] = +{ + 23 * BYTES_IN_REGISTER, /* r0 */ + 22 * BYTES_IN_REGISTER, /* r1 */ + 21 * BYTES_IN_REGISTER, /* r2 */ + 20 * BYTES_IN_REGISTER, /* r3 */ + 19 * BYTES_IN_REGISTER, /* r4 */ + 18 * BYTES_IN_REGISTER, /* r5 */ + 17 * BYTES_IN_REGISTER, /* r6 */ + 16 * BYTES_IN_REGISTER, /* r7 */ + 15 * BYTES_IN_REGISTER, /* r8 */ + 14 * BYTES_IN_REGISTER, /* r9 */ + 13 * BYTES_IN_REGISTER, /* r10 */ + 12 * BYTES_IN_REGISTER, /* r11 */ + 11 * BYTES_IN_REGISTER, /* r12 */ + REGISTER_NOT_PRESENT, /* r13 */ + REGISTER_NOT_PRESENT, /* r14 */ + REGISTER_NOT_PRESENT, /* r15 */ + REGISTER_NOT_PRESENT, /* r16 */ + REGISTER_NOT_PRESENT, /* r17 */ + REGISTER_NOT_PRESENT, /* r18 */ + REGISTER_NOT_PRESENT, /* r19 */ + REGISTER_NOT_PRESENT, /* r20 */ + REGISTER_NOT_PRESENT, /* r21 */ + REGISTER_NOT_PRESENT, /* r22 */ + REGISTER_NOT_PRESENT, /* r23 */ + REGISTER_NOT_PRESENT, /* r24 */ + REGISTER_NOT_PRESENT, /* r25 */ + 10 * BYTES_IN_REGISTER, /* r26 */ + 2 * BYTES_IN_REGISTER, /* bta */ + 3 * BYTES_IN_REGISTER, /* lp_start */ + 4 * BYTES_IN_REGISTER, /* lp_end */ + 5 * BYTES_IN_REGISTER, /* lp_count */ + 6 * BYTES_IN_REGISTER, /* status32 */ + 8 * BYTES_IN_REGISTER, /* blink */ + 9 * BYTES_IN_REGISTER, /* fp */ + 1 * BYTES_IN_REGISTER, /* sp */ + REGISTER_NOT_PRESENT, /* efa */ + 7 * BYTES_IN_REGISTER, /* ret */ + REGISTER_NOT_PRESENT, /* orig_r8 */ + REGISTER_NOT_PRESENT /* stop_pc */ +}; + + +/* arcompact_linux_core_reg_offsets[i] is the offset in the .reg section of GDB regnum i. + From include/asm-arc/user.h in the ARC Linux sources. */ + +static const int arcompact_linux_core_reg_offsets[ARC_NR_REGS] = +{ + 22 * BYTES_IN_REGISTER, /* r0 */ + 21 * BYTES_IN_REGISTER, /* r1 */ + 20 * BYTES_IN_REGISTER, /* r2 */ + 19 * BYTES_IN_REGISTER, /* r3 */ + 18 * BYTES_IN_REGISTER, /* r4 */ + 17 * BYTES_IN_REGISTER, /* r5 */ + 16 * BYTES_IN_REGISTER, /* r6 */ + 15 * BYTES_IN_REGISTER, /* r7 */ + 14 * BYTES_IN_REGISTER, /* r8 */ + 13 * BYTES_IN_REGISTER, /* r9 */ + 12 * BYTES_IN_REGISTER, /* r10 */ + 11 * BYTES_IN_REGISTER, /* r11 */ + 10 * BYTES_IN_REGISTER, /* r12 */ + 39 * BYTES_IN_REGISTER, /* r13 */ + 38 * BYTES_IN_REGISTER, /* r14 */ + 37 * BYTES_IN_REGISTER, /* r15 */ + 36 * BYTES_IN_REGISTER, /* r16 */ + 35 * BYTES_IN_REGISTER, /* r17 */ + 34 * BYTES_IN_REGISTER, /* r18 */ + 33 * BYTES_IN_REGISTER, /* r19 */ + 32 * BYTES_IN_REGISTER, /* r20 */ + 31 * BYTES_IN_REGISTER, /* r21 */ + 30 * BYTES_IN_REGISTER, /* r22 */ + 29 * BYTES_IN_REGISTER, /* r23 */ + 28 * BYTES_IN_REGISTER, /* r24 */ + 27 * BYTES_IN_REGISTER, /* r25 */ + 9 * BYTES_IN_REGISTER, /* r26 */ + 1 * BYTES_IN_REGISTER, /* bta */ + 2 * BYTES_IN_REGISTER, /* lp_start */ + 3 * BYTES_IN_REGISTER, /* lp_end */ + 4 * BYTES_IN_REGISTER, /* lp_count */ + 5 * BYTES_IN_REGISTER, /* status32 */ + 7 * BYTES_IN_REGISTER, /* blink */ + 8 * BYTES_IN_REGISTER, /* fp */ + 25 * BYTES_IN_REGISTER, /* sp */ + REGISTER_NOT_PRESENT, /* efa */ + 6 * BYTES_IN_REGISTER, /* ret */ + 24 * BYTES_IN_REGISTER, /* orig_r8 */ + 40 * BYTES_IN_REGISTER, /* stop_pc */ +}; + + +/* -------------------------------------------------------------------------- */ +/* forward declarations */ +/* -------------------------------------------------------------------------- */ + +static int arc_linux_binutils_reg_to_regnum (struct gdbarch *gdbarch, int reg); + + +/* -------------------------------------------------------------------------- */ +/* local macros */ +/* -------------------------------------------------------------------------- */ + +#define PRINT(regnum) \ + default_print_registers_info (gdbarch, file, frame, regnum, all) + + +/* -------------------------------------------------------------------------- */ +/* local functions */ +/* -------------------------------------------------------------------------- */ + +/* Returns TRUE if the instruction at PC is a branch (of any kind). + *fall_thru is set to the address of the next insn. + *target is set to the branch target. */ + +static Boolean +next_pc (CORE_ADDR pc, CORE_ADDR *fall_thru, CORE_ADDR *target) +{ + struct regcache *regcache = get_current_regcache(); + struct disassemble_info di; + struct arcDisState instr; + Boolean two_targets = FALSE; + + arc_initialize_disassembler(&di); + + /* So what is the instruction at the given PC? */ + instr = arcAnalyzeInstr(pc, &di); + + /* By default, the next instruction is the one immediately after the one at PC. */ + *fall_thru = pc + instr.instructionLen; + DEBUG("--- next_pc(%x) = %x, isBranch = %d, tcnt = %d [%x], flow = %s (%d), " + "reg for indirect jump = %d, nullifyMode = %s\n", + (unsigned int) pc, (unsigned int) *fall_thru, instr.isBranch, instr.tcnt, instr.targets[0], + (instr.flow == direct_jump || instr.flow == direct_call) ? "direct" : "indirect", + instr.flow, + instr.register_for_indirect_jump, + ((instr.nullifyMode == (char) BR_exec_always) ? "delay slot" : "no delay")); + + /* OK, it's a branch. */ + if ((Boolean) instr.isBranch) + { + two_targets = TRUE; + + /* If it's a direct jump or call, the destination address is encoded in + the instruction, so we got it by disassembling the instruction; + otherwise, it's an indirect jump to the address held in the register + named in the instruction, so we must read that register. */ + if (instr.flow == direct_jump || instr.flow == direct_call) + *target = (CORE_ADDR) instr.targets[0]; + else + regcache_cooked_read(regcache, + arc_linux_binutils_reg_to_regnum(current_gdbarch, + instr.register_for_indirect_jump), + (gdb_byte*) target); + + /* For instructions with delay slots, the fall thru is not the instruction + immediately after the branch instruction, but the one after that. */ + if (instr.nullifyMode == (char) BR_exec_always) + { + struct arcDisState instr_d = arcAnalyzeInstr(*fall_thru, &di); + + *fall_thru += instr_d.instructionLen; + } } - else if(regno == ARC_STATUS32_L1_REGNUM || - regno == ARC_STATUS32_L2_REGNUM || - regno == ARC_ERSTATUS_REGNUM) + + + /* Check for a zero-overhead loop. */ { - regcache_cooked_read (current_regcache, ARC_STATUS32_REGNUM, &status32); - - if(regno == ARC_STATUS32_L1_REGNUM) - *((unsigned int *)buf) = ((orig_r8 == -1) ? status32 : 0); - else if(regno == ARC_STATUS32_L2_REGNUM) - *((unsigned int *)buf) = ((orig_r8 == -2) ? status32 : 0); - else if(regno == ARC_ERSTATUS_REGNUM) - *((unsigned int *)buf) = ((orig_r8 >= 0) ? status32 : 0); + unsigned int lp_end, lp_start, lp_count, status32; + + regcache_cooked_read(regcache, ARC_LP_START_REGNUM, (gdb_byte*) &lp_start); + regcache_cooked_read(regcache, ARC_LP_END_REGNUM, (gdb_byte*) &lp_end); + regcache_cooked_read(regcache, ARC_LP_COUNT_REGNUM, (gdb_byte*) &lp_count); + regcache_cooked_read(regcache, ARC_STATUS32_REGNUM, (gdb_byte*) &status32); + + if (!(status32 & STATUS32_L) && *fall_thru == lp_end && lp_count > 1) + { + /* The instruction is in effect a jump back to the start of the loop. */ + two_targets = TRUE; + *target = lp_start; + } } - else - internal_error(__FILE__, __LINE__, "arc_pseudo_register_read: bad register number (%d)", regno); + + return two_targets; } + +/* Extract the register values found in the ABI GREGSET, storing their values in + regcache. */ + static void -arc_linux_pseudo_register_write (struct gdbarch *gdbarch, struct regcache *regcache, - int regno, const void *buf) +arcompact_linux_supply_gregset (struct regcache *regcache, + int regnum, + const void *gregs, + size_t size) { - /* none of our pseudo-regs are writable */ - internal_error(__FILE__, __LINE__, "arc_pseudo_register_write: bad register number"); + const bfd_byte *buf = gregs; + unsigned int reg; + + for (reg = 0; reg < ELEMENTS_IN_ARRAY(arcompact_linux_core_reg_offsets); reg++) + { + if (arcompact_linux_core_reg_offsets[reg] != REGISTER_NOT_PRESENT) + regcache_raw_supply (regcache, + (int) reg, + buf + arcompact_linux_core_reg_offsets[reg]); + } } -/* - * print registers in the correct order. - * - * Why not have the regnums in the right order in the first place ? - * Because some of the registers have to be pseudo-registers because of - * the way the kernel is written, and because gdb assumes that - * pseudo-registers have regnums greater than real register regnums. - */ -static void -arc_linux_print_registers_info (struct gdbarch *gdbarch, struct ui_file *file, - struct frame_info *frame, int regnum, int all) + +/* Return whether the frame preceding next_frame corresponds to a GNU/Linux + sigtramp routine. */ + +static Boolean +is_linux_sigtramp (struct frame_info *next_frame) +{ + /* Find the PC for that previous frame. */ + CORE_ADDR pc = frame_pc_unwind (next_frame); + gdb_byte buf[SIGTRAMP_INSNS_LENGTH]; + + /* Read the memory at that PC (this gives us the code without any s/w + breakpoints that may have been set in it). */ + if (!safe_frame_unwind_memory (next_frame, pc, buf, (int) SIGTRAMP_INSNS_LENGTH)) + /* Failed to unwind frame. */ + return FALSE; + + /* Is that code the sigtramp instruction sequence? */ + if (memcmp(buf, arc_sigtramp_insns, SIGTRAMP_INSNS_LENGTH) == 0) + return TRUE; + + /* No - look one instruction earlier in the code. */ + if (!safe_frame_unwind_memory (next_frame, pc - 4, buf, (int) SIGTRAMP_INSNS_LENGTH)) + /* Failed to unwind frame. */ + return FALSE; + + if (memcmp(buf, arc_sigtramp_insns, SIGTRAMP_INSNS_LENGTH) == 0) + return TRUE; + + return FALSE; +} + + +/* Assuming next_frame is a frame following a GNU/Linux sigtramp + routine, return the address of the associated sigcontext structure. */ + +static CORE_ADDR +linux_sigcontext_addr (struct frame_info *next_frame) { - int i; + gdb_byte buf[4]; + + frame_unwind_register (next_frame, ARC_SP_REGNUM, buf); + + return (CORE_ADDR) extract_unsigned_integer (buf, 4); +} + + +/* Determine whether the given register is a member of the given group. - if (regnum >= 0) + Returns 0, 1, or -1: + 0 means the register is not in the group. + 1 means the register is in the group. + -1 means the tdep has nothing to say about this register and group. */ + +static int +register_reggroup_p (int regnum, struct reggroup *group) +{ + if (system_reggroup) { - default_print_registers_info (gdbarch, file, frame, regnum, all); - return; + if (regnum == ARC_ORIG_R8_REGNUM || + regnum == ARC_EFA_REGNUM || + regnum == ARC_ERET_REGNUM || + regnum == ARC_ERSTATUS_REGNUM) + return 1; + } + else if (group == general_reggroup) + { + if (regnum == ARC_RET_REGNUM) + return 0; + + return (regnum == ARC_STATUS32_REGNUM) ? 0 : 1; } - /* print all registers */ - - /* r0..r26 */ - for (i=0; i <= 26; ++i) - default_print_registers_info (gdbarch, file, frame, i, all); - - default_print_registers_info (gdbarch, file, frame, ARC_FP_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, ARC_SP_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, ARC_ILINK1_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, ARC_ILINK2_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, ARC_BLINK_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, ARC_LP_COUNT_REGNUM, all); - - /* now the aux registers */ - - default_print_registers_info (gdbarch, file, frame, ARC_BTA_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, ARC_LP_START_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, ARC_LP_END_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, ARC_EFA_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, ARC_ERET_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, ARC_STATUS32_L1_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, ARC_STATUS32_L2_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, ARC_ERSTATUS_REGNUM, all); - - /* show the pc */ - default_print_registers_info (gdbarch, file, frame, ARC_STOP_PC_REGNUM, all); + /* Let the caller sort it out! */ + return -1; } -/* - * mov r8,nr_sigreturn - * swi - */ -static char arc_sigtramp_insn[] = { 0x8a, 0x20, 0xc1, 0x1d, 0x6f, 0x22, 0x3f, 0x00 }; +/* -------------------------------------------------------------------------- */ +/* local functions called from gdb */ +/* -------------------------------------------------------------------------- */ -/* Return whether the frame preceding NEXT_FRAME corresponds to a - GNU/Linux sigtramp routine. */ -static int -arc_linux_sigtramp_p (struct frame_info *next_frame) +/* The Linux kernel stores only one of (ilink1, ilink2, eret). This is stored + in the ret "register". ilink1 is stored when the kernel has been entered + because of a level 1 interrupt, etc. + + Same story for (status_l1, status_l2, erstatus). + + This disambiguity has been fixed by adding orig_r8 to pt_regs. + + FIXME: what is pt_regs???? + + It will take the following values - + 1. if an exception of any kind occurs then orig_r8 >= 0 + 2. Interrupt level 1 : orig == -1 + 3. Interrupt level 2 : orig == -2 + + Registers whose value we don't know are given the value zero. + + The only pseudo-registers are: + + ARC_ILINK1_REGNUM + ARC_ILINK2_REGNUM + ARC_ERET_REGNUM + ARC_STATUS32_L1_REGNUM + ARC_STATUS32_L2_REGNUM + ARC_ERSTATUS_REGNUM +*/ + +static void +arc_linux_pseudo_register_read (struct gdbarch *gdbarch, + struct regcache *regcache, + int gdb_regno, + gdb_byte *buf) { - CORE_ADDR pc = frame_pc_unwind (next_frame); - unsigned char buf[8]; + unsigned int* contents = (unsigned int *) buf; + unsigned int status32, ret; + int orig_r8; - if (!safe_frame_unwind_memory (next_frame, pc, buf, 8)) - return 0; + regcache_cooked_read (regcache, ARC_ORIG_R8_REGNUM, (gdb_byte*) &orig_r8); - if (memcmp(buf, arc_sigtramp_insn, 8) == 0) - return 1; - else + if (gdb_regno == ARC_ILINK1_REGNUM || + gdb_regno == ARC_ILINK2_REGNUM || + gdb_regno == ARC_ERET_REGNUM) { - pc -= 4; - - if (!safe_frame_unwind_memory (next_frame, pc, buf, 8)) - return 0; + regcache_cooked_read (regcache, ARC_RET_REGNUM, (gdb_byte*) &ret); - if (memcmp(buf, arc_sigtramp_insn, 8) == 0) - return 1; + if (gdb_regno == ARC_ILINK1_REGNUM) + *contents = ((orig_r8 == -1) ? ret : 0); + else if (gdb_regno == ARC_ILINK2_REGNUM) + *contents = ((orig_r8 == -2) ? ret : 0); + else // (gdb_regno == ARC_ERET_REGNUM) + *contents = ((orig_r8 >= 0) ? ret : 0); + + } + else if (gdb_regno == ARC_STATUS32_L1_REGNUM || + gdb_regno == ARC_STATUS32_L2_REGNUM || + gdb_regno == ARC_ERSTATUS_REGNUM) + { + regcache_cooked_read (regcache, ARC_STATUS32_REGNUM, (gdb_byte*) &status32); + + if (gdb_regno == ARC_STATUS32_L1_REGNUM) + *contents = ((orig_r8 == -1) ? status32 : 0); + else if (gdb_regno == ARC_STATUS32_L2_REGNUM) + *contents = ((orig_r8 == -2) ? status32 : 0); + else // (gdb_regno == ARC_ERSTATUS_REGNUM) + *contents = ((orig_r8 >= 0) ? status32 : 0); } + else + internal_error(__FILE__, __LINE__, _("%s: bad pseudo register number (%d)"), __FUNCTION__, gdb_regno); +} + - return 0; +static void +arc_linux_pseudo_register_write (struct gdbarch *gdbarch, + struct regcache *regcache, + int gdb_regno, + const gdb_byte *buf) +{ + /* None of our pseudo-regs are writable. */ + internal_error(__FILE__, __LINE__, _("%s: pseudo-registers are unwritable"), __FUNCTION__); } -/* Assuming NEXT_FRAME is a frame following a GNU/Linux sigtramp - routine, return the address of the associated sigcontext structure. */ -static CORE_ADDR -arc_linux_sigcontext_addr (struct frame_info *next_frame) + +/* Mapping from binutils/gcc register number to GDB register number ("regnum"). + N.B. registers such as ARC_FP_REGNUM, ARC_SP_REGNUM, etc., actually have + different GDB register numbers in the arc-elf32 and arc-linux-uclibc + configurations of the ARC gdb. */ + +static int +arc_linux_binutils_reg_to_regnum (struct gdbarch *gdbarch, int reg) { - char buf[4]; - CORE_ADDR sp; - - frame_unwind_register (next_frame, ARC_SP_REGNUM, buf); - sp = extract_unsigned_integer (buf, 4); + /* From gcc/config/arc/arc.h header file. */ + + if (reg >= 0 && reg <= 26) + return reg; + else if (reg == ARC_ABI_FRAME_POINTER) /* fp */ + return ARC_FP_REGNUM; + else if (reg == ARC_ABI_STACK_POINTER) /* sp */ + return ARC_SP_REGNUM; + else if (reg == 29) /* ilink1 */ + return ARC_ILINK1_REGNUM; + else if (reg == 30) /* ilink2 */ + return ARC_ILINK2_REGNUM; + else if (reg == 31) /* blink */ + return ARC_BLINK_REGNUM; + else if (IS_EXTENSION_CORE_REGISTER(reg)) /* reserved */ + ; + else if (reg == 60) /* lp_count */ + return ARC_LP_COUNT_REGNUM; +#if 0 + else if (reg == 61) /* reserved */ + ; + else if (reg == 62) /* no such register */ + ; + else if (reg == 63) /* PCL */ + ; +#endif + + warning(_("unmapped register #%d encountered"), reg); + return -1; +} + + +/* Print the contents of one, some or all registers. - return sp; + Print registers in the correct order. + Why not have the regnums in the right order in the first place? + Because some of the registers have to be pseudo-registers because of + the way the kernel is written, and because gdb assumes that + pseudo-registers have regnums greater than real register regnums. */ + +static void +arc_linux_print_registers_info (struct gdbarch *gdbarch, + struct ui_file *file, + struct frame_info *frame, + int regnum, + int all) +{ + if (regnum >= 0) + PRINT (regnum); + else /* If regnum < 0, print all registers. */ + { + int i; + + /* R0 .. R26 */ + for (i = 0; i <= 26; i++) PRINT (i); + + PRINT (ARC_FP_REGNUM ); + PRINT (ARC_SP_REGNUM ); + PRINT (ARC_ILINK1_REGNUM ); + PRINT (ARC_ILINK2_REGNUM ); + PRINT (ARC_BLINK_REGNUM ); + PRINT (ARC_LP_COUNT_REGNUM ); + + /* Now the auxiliary registers. */ + + PRINT (ARC_BTA_REGNUM ); + PRINT (ARC_LP_START_REGNUM ); + PRINT (ARC_LP_END_REGNUM ); + PRINT (ARC_EFA_REGNUM ); + PRINT (ARC_ERET_REGNUM ); + PRINT (ARC_STATUS32_L1_REGNUM); + PRINT (ARC_STATUS32_L2_REGNUM); + PRINT (ARC_ERSTATUS_REGNUM ); + + /* Show the PC. */ + PRINT (ARC_STOP_PC_REGNUM ); + } } -int -arc_linux_register_reggroup_p (int regnum, struct reggroup *group) + +/* Return the name of the given register. */ + +static const char* +arc_linux_register_name (struct gdbarch *gdbarch, int gdb_regno) { - if(regnum == ARC_ORIG_R8_REGNUM && group == system_reggroup) - return 1; + gdb_assert(ELEMENTS_IN_ARRAY(register_names) == (unsigned int) (ARC_NR_REGS + ARC_NR_PSEUDO_REGS)); - if(regnum == ARC_RET_REGNUM && group == general_reggroup) - return 0; + /* Oh, for a proper language with array bounds checking, like Ada... */ + gdb_assert(0 <= gdb_regno && gdb_regno < (int) ELEMENTS_IN_ARRAY(register_names)); - return -1; + return register_names[gdb_regno]; } -/* Mapping between the general-purpose registers in `struct - sigcontext' format and GDB's register cache layout. - arc_linux_sc_reg_offset[i] is the sigcontext offset of GDB regnum `i'. */ -/* From <asm/sigcontext.h>. */ -static int arc_linux_sc_reg_offset[] = +/* Determine whether the given register is read-only. */ + +static int +arc_linux_cannot_store_register (struct gdbarch *gdbarch, int gdb_regno) { - 23 * 4, /* r0 */ - 22 * 4, /* r1 */ - 21 * 4, /* r2 */ - 20 * 4, /* r3 */ - 19 * 4, /* r4 */ - 18 * 4, /* r5 */ - 17 * 4, /* r6 */ - 16 * 4, /* r7 */ - 15 * 4, /* r8 */ - 14 * 4, /* r9 */ - 13 * 4, /* r10 */ - 12 * 4, /* r11 */ - 11 * 4, /* r12 */ - -1, /* r13 */ - -1, /* r14 */ - -1, /* r15 */ - -1, /* r16 */ - -1, /* r17 */ - -1, /* r18 */ - -1, /* r19 */ - -1, /* r20 */ - -1, /* r21 */ - -1, /* r22 */ - -1, /* r23 */ - -1, /* r24 */ - -1, /* r25 */ - 10 * 4, /* r26 */ - 2 * 4, /* bta */ - 3 * 4, /* lp_start */ - 4 * 4, /* lp_end */ - 5 * 4, /* lp_count */ - 6 * 4, /* status32 */ - 8 * 4, /* blink */ - 9 * 4, /* fp */ - 1 * 4, /* sp */ - -1, /* efa */ - 7 * 4, /* ret */ - -1, /* orig_r8 */ - -1, /* stop_pc */ -}; + if (gdb_regno == ARC_EFA_REGNUM || + gdb_regno == ARC_ERET_REGNUM || + gdb_regno == ARC_STATUS32_L1_REGNUM || + gdb_regno == ARC_STATUS32_L2_REGNUM || + gdb_regno == ARC_ERSTATUS_REGNUM || + gdb_regno == ARC_ILINK1_REGNUM || + gdb_regno == ARC_ILINK2_REGNUM) + { + /* No warning should be printed. arc_cannot_store_register being + called does not imply that someone is actually writing to regnum. */ + + /* warning(_("writing to read-only register: %s"), gdbarch_register_name(gdbarch, gdb_regno)); */ + return 1; + } + + return 0; +} + + +/* This function is called just before we resume executing the inferior, if we + want to single-step it. We find the target(s) of the instruction about to + be executed and and place breakpoints there. */ + +static int +arc_linux_software_single_step (struct frame_info *frame) +{ + CORE_ADDR fall_thru, branch_target; + CORE_ADDR pc = get_frame_pc(frame); + Boolean two_breakpoints = next_pc(pc, &fall_thru, &branch_target); + + insert_single_step_breakpoint (fall_thru); + + if (two_breakpoints) + { + if (pc != branch_target) + insert_single_step_breakpoint (branch_target); + } + + /* Always returns true for now. */ + return 1; +} /* Set the program counter for process PTID to PC. */ static void -arc700_linux_write_pc (CORE_ADDR pc, ptid_t ptid) +arc_linux_write_pc (struct regcache *regcache, CORE_ADDR pc) { - ULONGEST val; - write_register_pid (PC_REGNUM, pc, ptid); - - /* We must be careful with modifying the program counter. If we - just interrupted a system call, the kernel might try to restart - it when we resume the inferior. On restarting the system call, - the kernel will try backing up the program counter even though it - no longer points at the system call. This typically results in a - SIGSEGV or SIGILL. We can prevent this by writing `-1' in the - "orig_r8" pseudo-register. - - Note that "orig_r8" is saved when setting up a dummy call frame. - This means that it is properly restored when that frame is - popped, and that the interrupted system call will be restarted - when we resume the inferior on return from a function call from - within GDB. In all other cases the system call will not be - restarted. */ - write_register_pid (ARC_ORIG_R8_REGNUM, -3, ptid); + regcache_cooked_write_unsigned (regcache, ARC_PC_REGNUM, pc); + + /* We must be careful with modifying the program counter. If we + just interrupted a system call, the kernel might try to restart + it when we resume the inferior. On restarting the system call, + the kernel will try backing up the program counter even though it + no longer points at the system call. This typically results in a + SIGSEGV or SIGILL. We can prevent this by writing `-1' in the + "orig_r8" pseudo-register. + + Note that "orig_r8" is saved when setting up a dummy call frame. + This means that it is properly restored when that frame is + popped, and that the interrupted system call will be restarted + when we resume the inferior on return from a function call from + within GDB. In all other cases the system call will not be + restarted. */ + + // FIXME: why -3 and not -1? -3 does not appear to be a defined valued for + // orig_r8 (i.e. -2, -1 or >= 0) - perhaps it means "none of these"? + regcache_cooked_write_signed (regcache, ARC_ORIG_R8_REGNUM, -3); } /* See the comments for SKIP_SOLIB_RESOLVER at the top of infrun.c. - This is called on every single step thru the PLT and runtime resolver. + This is called on every single step through the PLT and runtime resolver. This function: - 1) decides whether a PLT has sent us into the linker to resolve - a function reference, and - 2) if so, tells us where to set a temporary breakpoint that will - trigger when the dynamic linker is done. */ + 1) decides whether a PLT has sent us into the linker to resolve + a function reference, and + 2) if so, tells us where to set a temporary breakpoint that will + trigger when the dynamic linker is done. */ -CORE_ADDR +static CORE_ADDR arc_linux_skip_solib_resolver (struct gdbarch *gdbarch, CORE_ADDR pc) { - /* For uClibc 0.9.26. - - An unresolved PLT entry points to "__dl_linux_resolve", which calls - "__dl_linux_resolver" to do the resolving and then eventually jumps to - the function. - - So we look for the symbol `_dl_linux_resolver', and if we are there, - we set a breakpoint at the return address, and continue. */ - - /* lookup_minimal_symbol didn't work, for some reason. */ - struct symbol *resolver - = lookup_symbol_global ("_dl_linux_resolver", 0, VAR_DOMAIN, 0); - -#ifdef ARC_DEBUG - printf("--- arc_linux_skip_solib_resolver: pc = %x, resolver at %x\n", pc, - resolver ? BLOCK_START (SYMBOL_BLOCK_VALUE (resolver)) : 0); -#endif - - if (resolver && (BLOCK_START (SYMBOL_BLOCK_VALUE (resolver))) == pc) - { - return frame_pc_unwind (get_current_frame ()); - } + /* For uClibc 0.9.26. - return 0; -} + An unresolved PLT entry points to "__dl_linux_resolve", which calls + "__dl_linux_resolver" to do the resolving and then eventually jumps to + the function. + So we look for the symbol `_dl_linux_resolver', and if we are there, + gdb sets a breakpoint at the return address, and continues. */ -/* arcompact_linux_core_reg_offsets[i] is the offset in the .reg section of GDB - regnum i . - - From include/asm-arc/user.h in the ARC Linux sources. */ -static int arcompact_linux_core_reg_offsets[] = { - 22 * 4, /* r0 */ - 21 * 4, /* r1 */ - 20 * 4, /* r2 */ - 19 * 4, /* r3 */ - 18 * 4, /* r4 */ - 17 * 4, /* r5 */ - 16 * 4, /* r6 */ - 15 * 4, /* r7 */ - 14 * 4, /* r8 */ - 13 * 4, /* r9 */ - 12 * 4, /* r10 */ - 11 * 4, /* r11 */ - 10 * 4, /* r12 */ - 39 * 4, /* r13 */ - 38 * 4, /* r14 */ - 37 * 4, /* r15 */ - 36 * 4, /* r16 */ - 35 * 4, /* r17 */ - 34 * 4, /* r18 */ - 33 * 4, /* r19 */ - 32 * 4, /* r20 */ - 31 * 4, /* r21 */ - 30 * 4, /* r22 */ - 29 * 4, /* r23 */ - 28 * 4, /* r24 */ - 27 * 4, /* r25 */ - 9 * 4, /* r26 */ - 1 * 4, /* bta */ - 2 * 4, /* lp_start */ - 3 * 4, /* lp_end */ - 4 * 4, /* lp_count */ - 5 * 4, /* status32 */ - 7 * 4, /* blink */ - 8 * 4, /* fp */ - 25 * 4, /* sp */ - -1, /* efa */ - 6 * 4, /* ret */ - 24 * 4, /* orig_r8 */ - 40 * 4, /* stop_pc */ -}; + /* Lookup_minimal_symbol didn't work, for some reason. */ + struct symbol *resolver = + lookup_symbol_global ("_dl_linux_resolver", 0, 0, VAR_DOMAIN, 0); -/* Extract the register values found in the ABI GREGSET, storing their - values in REGCACHE. */ -static void -arcompact_linux_supply_gregset (struct regcache *regcache, - int regnum, const void *gregs, size_t size) -{ - int regi; - int arc_num_gprs = ARRAY_SIZE (arcompact_linux_core_reg_offsets); - const bfd_byte *buf = gregs; + DEBUG((resolver == NULL) ? "--- %s : pc = %x, no resolver found" + : "--- %s : pc = %x, resolver at %x\n", + __FUNCTION__, + (unsigned int) pc, + (unsigned int) ((resolver == NULL) ? 0 : BLOCK_START (SYMBOL_BLOCK_VALUE (resolver)))); - for (regi = 0; regi < arc_num_gprs; regi++) - { - if (arcompact_linux_core_reg_offsets[regi] > 0) - regcache_raw_supply (regcache, regi, - buf + arcompact_linux_core_reg_offsets[regi]); - } + if ((resolver != NULL) && (BLOCK_START (SYMBOL_BLOCK_VALUE (resolver))) == pc) + /* Find the return address. */ + return frame_pc_unwind (get_current_frame ()); + + /* No breakpoint is required. */ + return 0; } -/* Call the right architecture variant's supply_gregset function. For now, - we only have ARCompact. */ + +/* Call the right architecture variant's supply_gregset function. For now, we + have only ARCompact. */ + static void arc_linux_supply_gregset (const struct regset *regset, - struct regcache *regcache, - int regnum, const void *gregs, size_t size) + struct regcache *regcache, + int regnum, + const void *gregs, + size_t size) { - arcompact_linux_supply_gregset (regcache, regnum, gregs, size); + arcompact_linux_supply_gregset (regcache, regnum, gregs, size); } + /* Functions for handling core files. The first element is a parameter to pass the rest of the functions. We don't need it. supply_gregset is for reading the core file. collect_regset, which we haven't defined, would be for writing the core file. */ -static struct regset arc_linux_gregset = { - NULL, arc_linux_supply_gregset -}; -/* This is called through gdbarch. */ static const struct regset * arc_linux_regset_from_core_section (struct gdbarch *core_arch, - const char *sect_name, size_t sect_size) + const char *sect_name, + size_t sect_size) { - if (strcmp (sect_name, ".reg") == 0) - return &arc_linux_gregset; + static const struct regset arc_linux_gregset = + { + NULL, // descr + arc_linux_supply_gregset, // supply_regset + NULL, // collect_regset + NULL // arch + }; + + if (strcmp (sect_name, ".reg") == 0) + return &arc_linux_gregset; - return NULL; + return NULL; } -/* Add the signal stuff to gdbarch->tdep. */ + +/* Initialize for this ABI. */ + static void arc_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) { - struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); - - tdep->sigtramp_p = arc_linux_sigtramp_p; - tdep->sigcontext_addr = arc_linux_sigcontext_addr; - tdep->sc_reg_offset = arc_linux_sc_reg_offset; - tdep->sc_num_regs = ARRAY_SIZE (arc_linux_sc_reg_offset); - - tdep->arc_breakpoint_size = arc700_linux_breakpoint_size; - tdep->arc_breakpoint_insn = arc700_linux_breakpoint_insn; - - set_gdbarch_num_regs (gdbarch, ARC_NR_REGS); - set_gdbarch_num_pseudo_regs (gdbarch, ARC_NR_PSEUDO_REGS); - - set_gdbarch_pc_regnum (gdbarch, ARC_STOP_PC_REGNUM); - set_gdbarch_register_name (gdbarch, arc_linux_register_name); - - set_gdbarch_software_single_step (gdbarch, arc_software_single_step); - - set_gdbarch_write_pc (gdbarch, arc700_linux_write_pc); - - tdep->pc_regnum_in_sigcontext = ARC_RET_REGNUM; + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + + /* Fill in target-dependent info in ARC-private structure. */ + + tdep->is_sigtramp = is_linux_sigtramp; + tdep->sigcontext_addr = linux_sigcontext_addr; + tdep->sc_reg_offset = arc_linux_sc_reg_offset; + tdep->sc_num_regs = ELEMENTS_IN_ARRAY(arc_linux_sc_reg_offset); + tdep->pc_regnum_in_sigcontext = ARC_RET_REGNUM; + + tdep->le_breakpoint_instruction = le_breakpoint_instruction; + tdep->be_breakpoint_instruction = be_breakpoint_instruction; + tdep->breakpoint_size = (unsigned int) sizeof(le_breakpoint_instruction); + + tdep->register_reggroup_p = register_reggroup_p; + + tdep->lowest_pc = 0x74; // FIXME: why this? + tdep->processor_variant_info = NULL; + + /* Pass target-dependent info to gdb. */ + + /* ARC_NR_REGS and ARC_NR_PSEUDO_REGS are defined in the tm.h configuration file. */ + set_gdbarch_pc_regnum (gdbarch, ARC_STOP_PC_REGNUM); + set_gdbarch_num_regs (gdbarch, ARC_NR_REGS); + set_gdbarch_num_pseudo_regs (gdbarch, ARC_NR_PSEUDO_REGS); + set_gdbarch_print_registers_info (gdbarch, arc_linux_print_registers_info); + set_gdbarch_register_name (gdbarch, arc_linux_register_name); + set_gdbarch_cannot_store_register (gdbarch, arc_linux_cannot_store_register); + set_gdbarch_dwarf2_reg_to_regnum (gdbarch, arc_linux_binutils_reg_to_regnum); + + set_gdbarch_decr_pc_after_break (gdbarch, 0); + set_gdbarch_software_single_step (gdbarch, arc_linux_software_single_step); + set_gdbarch_write_pc (gdbarch, arc_linux_write_pc); + set_gdbarch_pseudo_register_read (gdbarch, arc_linux_pseudo_register_read); + set_gdbarch_pseudo_register_write (gdbarch, arc_linux_pseudo_register_write); + set_gdbarch_regset_from_core_section (gdbarch, arc_linux_regset_from_core_section); + set_gdbarch_skip_solib_resolver (gdbarch, arc_linux_skip_solib_resolver); + + /* GNU/Linux uses SVR4-style shared libraries. */ + set_solib_svr4_fetch_link_map_offsets (gdbarch, svr4_ilp32_fetch_link_map_offsets); +} - set_gdbarch_pseudo_register_read (gdbarch, arc_linux_pseudo_register_read); - set_gdbarch_pseudo_register_write (gdbarch, arc_linux_pseudo_register_write); +/* -------------------------------------------------------------------------- */ +/* externally visible functions */ +/* -------------------------------------------------------------------------- */ - set_gdbarch_print_registers_info (gdbarch, arc_linux_print_registers_info); +/* Initialize the module. This function is called from the gdb core on start-up. */ - tdep->register_reggroup_p = arc_linux_register_reggroup_p; - - tdep->lowest_pc = 0x74; +void +_initialize_arc_linux_tdep (void) +{ + /* Register a handler with gdb for the Linux O/S ABI variant for the ARC + processor architecture, providing an initialization function; + 'bfd_arch_arc' is an enumeration value specifically denoting the ARC + architecture. */ + gdbarch_register_osabi (bfd_arch_arc, + 0, // machine (irrelevant) + GDB_OSABI_LINUX, + arc_linux_init_abi); +} - tdep->arc_processor_variant_info = NULL; - set_gdbarch_regset_from_core_section (gdbarch, - arc_linux_regset_from_core_section); - /* GNU/Linux uses SVR4-style shared libraries. */ - set_solib_svr4_fetch_link_map_offsets - (gdbarch, svr4_ilp32_fetch_link_map_offsets); - set_gdbarch_skip_solib_resolver (gdbarch, arc_linux_skip_solib_resolver); -} +/* This function is required simply to avoid an undefined symbol at linkage. */ void -_initialize_arc_linux_tdep (void) +arc_check_pc_defined (struct gdbarch *gdbarch) { - gdbarch_register_osabi (bfd_arch_arc, 0, GDB_OSABI_LINUX, - arc_linux_init_abi); } + +/******************************************************************************/ diff --git a/gdb/arc-linux-tdep.h b/gdb/arc-linux-tdep.h new file mode 100644 index 00000000000..af1aa0e6df6 --- /dev/null +++ b/gdb/arc-linux-tdep.h @@ -0,0 +1,51 @@ +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. + + Copyright 2008, 2009 Free Software Foundation, Inc. + + Contributed by ARC International (www.arc.com) + + Authors: + Richard Stuckey <richard.stuckey@arc.com> + + 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 3 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, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This header file defines the type and operations for the Linux-uclibc */ +/* variant of the ARC debugger. */ +/* */ +/******************************************************************************/ + +#ifndef ARC_LINUX_TDEP_H +#define ARC_LINUX_TDEP_H + +/* gdb header files */ +#include "defs.h" +#include "gdbarch.h" + + +/* Complete the structure definition here. */ +struct arc_variant_info +{ + /* There is currently no variant-specific info. */ +}; + + +void arc_check_pc_defined (struct gdbarch *gdbarch); + +#endif /* ARC_LINUX_TDEP_H */ +/******************************************************************************/ diff --git a/gdb/arc-memory.c b/gdb/arc-memory.c new file mode 100644 index 00000000000..806efb0d3c2 --- /dev/null +++ b/gdb/arc-memory.c @@ -0,0 +1,445 @@ +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. + + Copyright 2009 Free Software Foundation, Inc. + + Contributed by ARC International (www.arc.com) + + Author: + Richard Stuckey <richard.stuckey@arc.com> + + 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 3 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, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This module implements operations for reading data from and writing */ +/* data to target memory. */ +/* */ +/* The operations may be required to transfer arbitrary amounts of data */ +/* to/from arbitrary addresses in memory; however, the supplied transfer */ +/* operations must be assumed to be able only to transfer multiple words */ +/* of data to/from word-aligned addresses. */ +/* */ +/* This gives the general case: */ +/* */ +/* word boundaries */ +/* | */ +/* ------------------------------------------------- */ +/* | | | | | | */ +/* ----------------------------------------------------------- */ +/* | g g L L | W W W W | W W W W | W W W W | T T e e | */ +/* ----------------------------------------------------------- */ +/* ^ */ +/* |-------------------------------------| */ +/* | len */ +/* addr */ +/* */ +/* where addr is the base address of the data to be transferred */ +/* len is the number of bytes of data to be transferred */ +/* W denotes a byte that can be transfered in a whole word */ +/* L denotes a leading byte */ +/* T denotes a trailing byte */ +/* g denotes a gap byte */ +/* e denotes a end gap byte */ +/* */ +/* There may be 0 .. BYTES_IN_WORD - 1 leading bytes, 0 or more whole */ +/* words, and 0 .. BYTES_IN_WORD - 1 trailing bytes. If the given address */ +/* is word-aligned, there is no gap and hence no leading bytes. */ +/* */ +/* There is also a pathological case: */ +/* */ +/* word boundaries */ +/* | */ +/* --------- */ +/* | | */ +/* ----------------------------------------------------------- */ +/* | g B B e | */ +/* ----------------------------------------------------------- */ +/* ^ */ +/* |-| */ +/* | len */ +/* addr */ +/* */ +/* where 1 .. BYTES_IN_WORD - 2 bytes of data in the middle of a word */ +/* must be transfered. */ +/* */ +/* In a write operation, it is necessary to preserve the contents of the */ +/* gap and end gap bytes, if any - this is done by first reading the word */ +/* which contains those bytes, constructing a new word which contains */ +/* those bytes and the new bytes, and writing the new value back to that */ +/* location. */ +/* */ +/******************************************************************************/ + +/* system header files */ + +/* gdb header files */ +#include "defs.h" + +/* ARC header files */ +#include "arc-memory.h" +#include "arc-tdep.h" + + +/* -------------------------------------------------------------------------- */ +/* local types */ +/* -------------------------------------------------------------------------- */ + +typedef struct +{ + unsigned int leading_bytes; + unsigned int trailing_bytes; + unsigned int words; +} Layout; + + +/* -------------------------------------------------------------------------- */ +/* local functions */ +/* -------------------------------------------------------------------------- */ + +/* Split a memory chunk up into its layout - see diagram in file header. */ + +static Layout +split (ARC_Address addr, unsigned int bytes) +{ + unsigned int gap = addr % BYTES_IN_WORD; + Layout layout; + + layout.leading_bytes = (gap == 0) ? 0 : (BYTES_IN_WORD - gap); + layout.trailing_bytes = (addr + bytes) % BYTES_IN_WORD; + layout.words = (bytes - layout.leading_bytes + - layout.trailing_bytes) / BYTES_IN_WORD; + + DEBUG("%u leading bytes, %u words, %u trailing bytes\n", + layout.leading_bytes, layout.words, layout.trailing_bytes); + + return layout; +} + + +/* Read part of a word of data from memory; the given address must be word-aligned. */ + +static unsigned int +read_partial_word (TargetOperations *ops, + ARC_Address addr, + ARC_Byte *data, + unsigned int bytes, + unsigned int offset) /* Offset of required bytes within word. */ +{ + ARC_Byte word[BYTES_IN_WORD]; + + /* Read the word: only some bytes of this are required. */ + if (ops->read_memory(addr, word, 1) > 0) + { + unsigned int i; + + for (i = 0; i < bytes; i++) + data[i] = word[offset + i]; + + /* Have read the specified number of bytes. */ + return bytes; + } + + /* Failed: no data read. */ + return 0; +} + + +/* Write part of a word of data to memory; the given address must be word-aligned. */ + +static unsigned int +write_partial_word (TargetOperations *ops, + ARC_Address addr, + ARC_Byte *data, + unsigned int bytes, + unsigned int offset) /* Offset of required bytes within word. */ +{ + ARC_Byte word[BYTES_IN_WORD]; + + /* First read the word of memory that will be overwritten. */ + if (ops->read_memory(addr, word, 1) > 0) + { + unsigned int i; + + /* Next replace the bytes in that word that are to be written. */ + for (i = 0; i < bytes; i++) + word[offset + i] = data[i]; + + /* Now write it! */ + if (ops->write_memory(addr, word, 1) > 0) + /* Have written the specified number of bytes. */ + return bytes; + } + + /* Failed: no data written. */ + return 0; +} + + +/* -------------------------------------------------------------------------- */ +/* externally visible functions */ +/* -------------------------------------------------------------------------- */ + +/* Read a chunk of data from target memory. + Returns number of bytes read. */ + +unsigned int +arc_read_memory (TargetOperations *ops, + ARC_Address address, + ARC_Byte *data, + unsigned int bytes) +{ + unsigned int gap = address % BYTES_IN_WORD; + unsigned int total_read = 0; + + ENTERARGS("address 0x%08X, bytes %u", address, bytes); + + /* Special fast case for reading a single word. */ + if (gap == 0 && bytes == BYTES_IN_WORD) + { + DEBUG("read single word\n"); + + /* N.B. assumes that 'data' is word-aligned, or that host does not care! */ + total_read = ops->read_memory(address, data, 1); + } + /* Pathological case: bytes in middle of word. */ + else if (gap > 0 && gap + bytes < BYTES_IN_WORD) + { + DEBUG("read pathological\n"); + + total_read = read_partial_word(ops, + address - gap, /* Word-aligned address. */ + data, + bytes, + gap); + } + else /* The general case. */ + { + Layout chunk = split(address, bytes); + + if (chunk.leading_bytes > 0) + { + /* Read the first few bytes. */ + total_read = read_partial_word(ops, + address - gap, /* Word-aligned addres. */ + data, + chunk.leading_bytes, + gap); + data += chunk.leading_bytes; + address += chunk.leading_bytes; + } + + if (chunk.words > 0) + { + unsigned int bytes_read = ops->read_memory(address, data, chunk.words); + + total_read += bytes_read; + address += bytes_read; + data += bytes_read; + } + + if (chunk.trailing_bytes > 0) + { + /* Read the last few bytes of data. */ + total_read += read_partial_word(ops, + address, // Word-aligned address. */ + data, + chunk.trailing_bytes, + 0); + } + } + + DEBUG("read %u bytes\n", total_read); + + return total_read; +} + + +/* Write a chunk of data to target memory. + Returns number of bytes written. */ + +unsigned int +arc_write_memory (TargetOperations *ops, + ARC_Address address, + ARC_Byte *data, + unsigned int bytes) +{ + unsigned int gap = address % BYTES_IN_WORD; + unsigned int total_written = 0; + + ENTERARGS("address 0x%08X, bytes %u", address, bytes); + + /* Useful debugging code: just change 0 to 1. */ + if (0) + { + unsigned int i; + for (i = 0; i < bytes; i++) + { + DEBUG("%02X", data[i]); + if ((i + 1) % 16 == 0) + DEBUG("\n"); + } + DEBUG("\n"); + } + + /* Special fast case for writing a single word. */ + if (gap == 0 && bytes == BYTES_IN_WORD) + { + DEBUG("write single word (%02X %02X %02X %02X)\n", data[0], data[1], data[2], data[3]); + + total_written = ops->write_memory(address, data, 1); + } + /* Pathological case: bytes in middle of word. */ + else if (gap > 0 && gap + bytes < BYTES_IN_WORD) + { + DEBUG("write pathological\n"); + + total_written = write_partial_word(ops, + address - gap, /* Word-aligned address. */ + data, + bytes, + gap); + } + else /* general case */ + { + Layout chunk = split(address, bytes); + + if (chunk.leading_bytes > 0) + { + /* Write the first few bytes. */ + total_written = write_partial_word(ops, + address - gap, /* Word-aligned address. */ + data, + chunk.leading_bytes, + gap); + data += chunk.leading_bytes; + address += chunk.leading_bytes; + } + + if (chunk.words > 0) + { + unsigned int bytes_written = ops->write_memory(address, data, chunk.words); + + total_written += bytes_written; + address += bytes_written; + data += bytes_written; + } + + if (chunk.trailing_bytes > 0) + { + /* Write the last few bytes of data. */ + total_written += write_partial_word(ops, + address, /* Word-aligned address. */ + data, + chunk.trailing_bytes, + 0); + } + } + + DEBUG("written %u bytes\n", total_written); + + return total_written; +} + + +/* Write a repeated pattern of data to memory; + the start of each pattern is always word-aligned, so if the given address is + not word-aligned, the first partial word written will contain trailing bytes + of the pattern. */ + +unsigned int +arc_write_pattern (TargetOperations *ops, + ARC_Address address, + ARC_Word pattern, + unsigned int bytes) +{ + unsigned int gap = address % BYTES_IN_WORD; + unsigned int total_written = 0; + + ENTERARGS("address 0x%08X, pattern 0x%08X, bytes %u", address, pattern, bytes); + + /* Special fast case for writing a single word. */ + if (gap == 0 && bytes == BYTES_IN_WORD) + { + DEBUG("write single word (%08X)\n", pattern); + + total_written = ops->write_memory(address, (ARC_Byte*) &pattern, 1); + } + /* Pathological case: bytes in middle of word. */ + else if (gap > 0 && gap + bytes < BYTES_IN_WORD) + { + DEBUG("write pathological\n"); + + total_written = write_partial_word(ops, + address - gap, /* Word-aligned address. */ + ((ARC_Byte*) &pattern) + gap , + bytes, + gap); + } + else /* General case. */ + { + Layout chunk = split(address, bytes); + + if (chunk.leading_bytes > 0) + { + /* Write the first few bytes. */ + total_written = write_partial_word(ops, + address - gap, /* Word-aligned address. */ + ((ARC_Byte*) &pattern) + gap , + chunk.leading_bytes, + gap); + + address += chunk.leading_bytes; + } + + /* If we have been given a fill_memory operation. */ + if (ops->fill_memory) + { + /* Write the complete words of data in one go. */ + unsigned int bytes_written = ops->fill_memory(address, pattern, chunk.words); + + total_written += bytes_written; + address += bytes_written; + } + else + { + /* Write all the complete words of data, one word at a time. */ + while (chunk.words--) + { + unsigned int bytes_written = ops->write_memory(address, (ARC_Byte*) &pattern, 1); + + total_written += bytes_written; + address += bytes_written; + } + } + + if (chunk.trailing_bytes > 0) + { + /* Write the last few bytes of data. */ + total_written += write_partial_word(ops, + address, /* Word-aligned address. */ + ((ARC_Byte*) &pattern), + chunk.trailing_bytes, + 0); + } + } + + DEBUG("written %u bytes\n", total_written); + + return total_written; +} + +/******************************************************************************/ diff --git a/gdb/arc-memory.h b/gdb/arc-memory.h new file mode 100644 index 00000000000..110d5cba75b --- /dev/null +++ b/gdb/arc-memory.h @@ -0,0 +1,64 @@ +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. + + Copyright 2009 Free Software Foundation, Inc. + + Contributed by ARC International (www.arc.com) + + Authors: + Richard Stuckey <richard.stuckey@arc.com> + + 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 3 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, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This header file defines operations for reading data from, and writing */ +/* data to, target memory. */ +/* */ +/* The data transferred may be of any length, and its start address may */ +/* have any alignment. */ +/* */ +/* These operations must be supplied with more basic operations which can */ +/* read or write an arbitrary number of complete words of data to or from */ +/* word-aligned addresses. */ +/* */ +/* Optionally, an operation which can write an arbitrary number of copies */ +/* of a word-sized pattern to memory, starting at a word-aligned address, */ +/* may be supplied for use by the arc_write_pattern operation; this is an */ +/* optimisation for improving performance: if it is not supplied, the */ +/* arc_write_pattern operation will use repeated calls of the supplied */ +/* 'write_words' operation. */ +/* */ +/* A pointer to user-defined data may be passed to these operations: it */ +/* will be passed to the basic operations when they are invoked (this */ +/* allows any required context data to be passed to those operations). */ +/* */ +/******************************************************************************/ + +#ifndef ARC_MEMORY +#define ARC_MEMORY + +/* ARC header files */ +#include "arc-support.h" + + +unsigned int arc_read_memory (TargetOperations *ops, ARC_Address address, ARC_Byte *data, unsigned int bytes); +unsigned int arc_write_memory (TargetOperations *ops, ARC_Address address, ARC_Byte *data, unsigned int bytes); +unsigned int arc_write_pattern (TargetOperations *ops, ARC_Address address, ARC_Word pattern, unsigned int bytes); + + +#endif /* ARC_MEMORY */ +/******************************************************************************/ diff --git a/gdb/arc-registers.c b/gdb/arc-registers.c new file mode 100644 index 00000000000..c0f7710cbb6 --- /dev/null +++ b/gdb/arc-registers.c @@ -0,0 +1,2566 @@ +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. + + Copyright 2008, 2009 Free Software Foundation, Inc. + + Contributed by ARC International (www.arc.com) + + Author: + Richard Stuckey <richard.stuckey@arc.com> + + 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 3 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, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This module implements operations for manipulating the ARC processor */ +/* core registers and auxiliary registers. */ +/* */ +/******************************************************************************/ + +/* system header files */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> + +/* gdb header files */ +#include "defs.h" +#include "gdbcmd.h" +#include "regcache.h" +#include "objfiles.h" +#include "inferior.h" +#include "target.h" +#include "xml-support.h" +#include "gdb_assert.h" + +/* ARC header files */ +#include "arc-registers.h" +#include "arc-architecture.h" +#include "arc-tdep.h" +#include "arc-elf32-tdep.h" +#include "config/arc/tm-embed.h" + +/* ARC simulator header files */ +#include "sim/arc/arc-sim-registers.h" + + +/* -------------------------------------------------------------------------- */ +/* local types */ +/* -------------------------------------------------------------------------- */ + +typedef struct field_meaning +{ + char *description; + ARC_RegisterContents value; +} ARC_FieldMeaning; + + +typedef struct field_definition +{ + char *name; + char *description; + unsigned int offset; + unsigned int size; + RegisterAccess access; + ARC_RegisterContents value_for_write; + Boolean fixed; + ARC_FieldMeaning *meanings; + unsigned int meaning_count; +} ARC_FieldDefinition; + + +/* Complete the type (declared in the header file) here. */ +struct aux_register_definition +{ + char *name; + char *description; + ARC_RegisterNumber number; /* The number in the auxiliary register space. */ + int gdb_regno; + Boolean is_BCR; + ARC_Word mask; + RegisterAccess access; + ARC_FieldDefinition *fields; + unsigned int field_count; + unsigned int longest_field_name; + unsigned int max_bits_in_field; +}; + + +/* An instance of this structure is used to pass state data + between the parsing routines. + + The information is accumulated into the 'info' * field; + as each register or field description is parsed, the + information is held in the 'reg' or 'field' field before + validation is performed. If the information is valid, + it is copied into the 'info' structure. */ +typedef struct parsing_data +{ + const char *filename; + ARC_RegisterInfo *info; + ARC_AuxRegisterDefinition *currentRegister; + ARC_FieldDefinition *currentField; + ARC_RegisterContents maxFieldContents; + ARC_AuxRegisterDefinition reg; + ARC_FieldDefinition field; +} ParsingData; + + +/* -------------------------------------------------------------------------- */ +/* forward declarations */ +/* -------------------------------------------------------------------------- */ + +static gdb_xml_element_start_handler start_architecture; +static gdb_xml_element_end_handler end_architecture; +static gdb_xml_element_start_handler start_feature; +static gdb_xml_element_start_handler start_target; +static gdb_xml_element_start_handler start_auxregister; +static gdb_xml_element_start_handler start_bcr; +static gdb_xml_element_start_handler start_ecr; +static gdb_xml_element_start_handler start_field; +static gdb_xml_element_start_handler start_bcrfield; +static gdb_xml_element_start_handler start_meaning; + + +/* -------------------------------------------------------------------------- */ +/* externally visible data */ +/* -------------------------------------------------------------------------- */ + +/* This indicates whether a 'register architecture changed' event must be sent, + but has not yet been sent. */ +Boolean arc_pending_register_architecture_change_event; + + +/* -------------------------------------------------------------------------- */ +/* local data */ +/* -------------------------------------------------------------------------- */ + +#define REG_READ_FILE_COMMAND "arc-reg-read-file" +#define REG_READ_EXTRA_FILE_COMMAND "arc-reg-read-extra-file" +#define AUX_REG_READ_COMMAND "arc-aux-read" +#define AUX_REG_WRITE_COMMAND "arc-aux-write" +#define AUX_REG_SHOW_COMMAND "arc-aux-show" +#define AUX_LIST_REGISTER_COMMAND "arc-aux-list" +#define ARC_BCR_COMMAND "arc-bcr-registers" + +#define REG_READ_FILE_COMMAND_USAGE "Usage: " REG_READ_FILE_COMMAND " <FILE>\n" +#define REG_READ_EXTRA_FILE_COMMAND_USAGE "Usage: " REG_READ_EXTRA_FILE_COMMAND " <FILE>\n" +#define AUX_REG_READ_COMMAND_USAGE "Usage: " AUX_REG_READ_COMMAND " <REG-FROM> [ <REG-TO> ]\n" +#define AUX_REG_WRITE_COMMAND_USAGE "Usage: " AUX_REG_WRITE_COMMAND " <REG> = <VALUE>\n" +#define AUX_REG_SHOW_COMMAND_USAGE "Usage: " AUX_REG_SHOW_COMMAND " [ <REG> ] \n" +#define AUX_LIST_REGISTER_COMMAND_USAGE "Usage: " AUX_LIST_REGISTER_COMMAND " [ <REG> ]\n" +#define ARC_BCR_COMMAND_USAGE "Usage: info " ARC_BCR_COMMAND "\n" + + +#define ELEMENT_END_MARKER { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL } +#define ATTRIBUTE_END_MARKER { NULL, GDB_XML_AF_NONE, NULL, NULL } + + +/* Special values for the name and description fields. */ +static const char *NO_DESCRIPTION = ""; +static const char *RESERVED = "<reserved>"; +static const char *UNUSED = "<unused>"; + + +/* The elements and attributes of an XML target description. */ + +/* A handler_data for access values. */ + +static const struct gdb_xml_enum enums_access[] = +{ + { "RO", READ_ONLY }, + { "RW", READ_WRITE }, + { "WO", WRITE_ONLY }, + { NULL, 0 } +}; + + +static const struct gdb_xml_attribute aux_register_attributes[] = +{ + { "name", GDB_XML_AF_NONE, NULL, NULL }, + { "description", GDB_XML_AF_NONE, NULL, NULL }, + { "number", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL }, + { "mask", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL }, + { "access", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_enum, enums_access }, + ATTRIBUTE_END_MARKER +}; + + +static const struct gdb_xml_attribute core_register_attributes[] = +{ + { "number", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL }, + { "mask", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL }, + { "access", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_enum, enums_access }, + ATTRIBUTE_END_MARKER +}; + + +static const struct gdb_xml_attribute field_attributes[] = +{ + { "name", GDB_XML_AF_NONE, NULL, NULL }, + { "description", GDB_XML_AF_OPTIONAL, NULL, NULL }, + { "onwrite", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL }, + { "offset", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL }, + { "size", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL }, + { "access", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_enum, enums_access }, + ATTRIBUTE_END_MARKER +}; + + +static const struct gdb_xml_attribute bcrfield_attributes[] = +{ + { "name", GDB_XML_AF_NONE, NULL, NULL }, + { "description", GDB_XML_AF_OPTIONAL, NULL, NULL }, + { "offset", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL }, + { "size", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL }, + ATTRIBUTE_END_MARKER +}; + + +static const struct gdb_xml_attribute meaning_attributes[] = +{ + { "description", GDB_XML_AF_NONE, NULL, NULL }, + { "value", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL }, + ATTRIBUTE_END_MARKER +}; + + +static const struct gdb_xml_attribute feature_attributes[] = +{ + { "name", GDB_XML_AF_NONE, NULL, NULL }, + ATTRIBUTE_END_MARKER +}; + + +static const struct gdb_xml_element field_children[] = +{ + { "meaning", meaning_attributes, NULL, GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE, start_meaning, NULL }, + ELEMENT_END_MARKER +}; + + +static const struct gdb_xml_element auxregister_children[] = +{ + { "field", field_attributes, field_children, GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE, start_field, NULL }, + ELEMENT_END_MARKER +}; + + +static const struct gdb_xml_element bcr_children[] = +{ + { "bcrfield", bcrfield_attributes, NULL, GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE, start_bcrfield, NULL }, + ELEMENT_END_MARKER +}; + + +static const struct gdb_xml_element feature_children[] = +{ + { "auxregister", aux_register_attributes, auxregister_children, GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE, start_auxregister, NULL }, + { "bcr", aux_register_attributes, bcr_children, GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE, start_bcr, NULL }, + { "ecr", core_register_attributes, NULL, GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE, start_ecr, NULL }, + ELEMENT_END_MARKER +}; + + +static const struct gdb_xml_attribute target_attributes[] = +{ + { "version", GDB_XML_AF_NONE, NULL, NULL }, + ATTRIBUTE_END_MARKER +}; + + +static const struct gdb_xml_element target_children[] = +{ + { "architecture", NULL, NULL, GDB_XML_EF_OPTIONAL, start_architecture, end_architecture }, + { "feature", feature_attributes, feature_children, + GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE, + start_feature, NULL }, + ELEMENT_END_MARKER +}; + + +static const struct gdb_xml_element elements[] = +{ + { "target", target_attributes, target_children, GDB_XML_EF_NONE, start_target, NULL }, + ELEMENT_END_MARKER +}; + + +/* -------------------------------------------------------------------------- */ +/* local macros */ +/* -------------------------------------------------------------------------- */ + +#define NAME_IS(ident) (strcmp(name, ident) == 0) + +#define INFO_OF(gdbarch) &gdbarch_tdep (gdbarch)->processor_variant_info->registers + +#define EXTRACT(argument, type, result) \ +{ \ + struct expression *expr = parse_expression(argument); \ + struct value *val = evaluate_expression(expr); \ + struct cleanup *chain = make_cleanup(free_current_contents, &expr); \ + \ + result = *(type*) (value_contents (val)); \ + do_cleanups (chain); \ +} + +#define FIND_REGISTER_DEFINITION_SUCH_THAT(condition) \ +{ \ + ARC_RegisterInfo *info = find_info(TRUE); \ + unsigned int i; \ + \ + for (i = 0; i < info->aux_register_count; i++) \ + { \ + ARC_AuxRegisterDefinition *def = &info->aux_registers[i]; \ + \ + if (condition) \ + return def; \ + } \ + \ + return NULL; \ +} + + +/* -------------------------------------------------------------------------- */ +/* local functions for XML file parsing */ +/* -------------------------------------------------------------------------- */ + +/* Return a string corresponding to the given access. */ + +static const char* +RegisterAccess_Image (RegisterAccess val) +{ + switch (val) + { + case READ_ONLY : return "read-only"; + case READ_WRITE: return "read/write"; + case WRITE_ONLY: return "write-only"; + default : return "???"; + } +} + + +static void +initialize_register (ARC_AuxRegisterDefinition *reg) +{ + reg->name = NULL; + reg->number = 0; + reg->gdb_regno = 0; + reg->is_BCR = FALSE; + reg->description = (char*) NO_DESCRIPTION; + reg->mask = 0xFFFFFFFF; + reg->access = READ_WRITE; + reg->field_count = 0; + reg->fields = NULL; + reg->longest_field_name = 0; + reg->max_bits_in_field = 0; +} + + +static void +initialize_field (ARC_FieldDefinition *field, RegisterAccess access) +{ + field->name = NULL; + field->description = (char*) NO_DESCRIPTION; + field->value_for_write = 0; + field->fixed = FALSE; + field->size = 1; + field->offset = 0; + field->access = access; + field->meanings = NULL; + field->meaning_count = 0; +} + + +static void +initialize_meaning (ARC_FieldMeaning *meaning) +{ + meaning->description = (char*) NO_DESCRIPTION; + meaning->value = 0; +} + + +/* Return TRUE if the given register fields overlap within the register. */ + +static Boolean +overlaps(ARC_FieldDefinition *field1, ARC_FieldDefinition *field2) +{ + unsigned int field1_start = field1->offset; + unsigned int field1_end = field1_start + field1->size - 1; + unsigned int field2_start = field2->offset; + unsigned int field2_end = field2_start + field2->size - 1; + + return !(field2_end < field1_start || field1_end < field2_start); +} + + +/* Release all the storage allocated to hold the given register information, + and re-initialize it. */ + +static void +free_register_set (ARC_RegisterInfo *info) +{ + if (info->aux_registers) + { + unsigned int i; + + for (i = 0; i < info->aux_register_count; i++) + { + ARC_AuxRegisterDefinition *r = &info->aux_registers[i]; + + if (r->name != UNUSED) + xfree (r->name); + if (r->description != NO_DESCRIPTION) + xfree (r->description); + + if (r->fields) + { + unsigned int j; + + for (j = 0; j < r->field_count; j++) + { + ARC_FieldDefinition *f = &r->fields[j]; + + if (f->name != RESERVED) + xfree (f->name); + + if (f->description != NO_DESCRIPTION) + xfree (f->description); + + if (f->meanings) + { + unsigned int k; + + for (k = 0; k < f->meaning_count; k++) + { + ARC_FieldMeaning *m = &f->meanings[k]; + + if (m->description != NO_DESCRIPTION) + xfree (m->description); + } + + xfree(f->meanings); + } + } + } + + xfree(r->fields); + } + + xfree(info->aux_registers); + + arc_initialize_aux_reg_info(info); + } +} + + +/* This function reads the contents of a file. + + Parameters: + filename : the name of the file to be accessed + baton : the path to the directory containing the file + + Result: + If the file contents were successfully read, a pointer to a buffer + containing those contents; otherwise NULL. */ + +static char* +read_file_contents (const char *filename, void *baton) +{ + char *name = (char*) filename; + const char *dirname = baton; + char *contents = NULL; + int fd; + + if ((dirname != NULL) && (*dirname != '\0')) + { + name = concat (dirname, "/", filename, NULL); + + if (name == NULL) + { + /* N.B. this does not return */ + nomem (0); + } + } + + if ((fd = open (name, O_RDONLY)) != -1) + { + struct stat stat; + int status; + + if ((status = fstat(fd, &stat)) != -1) + { + size_t size = (size_t) stat.st_size; + + /* Allocate buffer to hold the file contents; note that this space + is deliberately made larger than required, so that it is possible + to add extra characters to the end of the data. */ + if ((contents = xmalloc (size + 10))) + { + ssize_t bytes; + + if ((bytes = read (fd, contents, size)) == (ssize_t) size) + { + /* Append an explicit end-of-line to the data, in case there + was not one there already; also, explicitly NUL-terminate + the data so that it is a valid string. */ + contents[bytes] = '\n'; + contents[bytes + 1] = '\0'; + } + else + { + warning (_("can not read contents of file '%s': %s"), + name, strerror (errno)); + + /* Cannot trust the contents of the buffer. */ + xfree (contents); + contents = NULL; + } + + if ((status = close (fd)) != 0) + warning (_("can not close file '%s': %s"), name, + strerror (errno)); + } + else + warning (_("can not allocate buffer to hold contents of file '%s'"), name); + } + else + warning (_("can not get size of file '%s': %s"), name, + strerror (errno)); + } + else + warning (_("can not open file '%s': %s"), name, strerror (errno)); + + if (name != filename) + xfree (name); + + return contents; +} + + +/* This function is called by the XML parser when the start of a <target> element is seen. */ + +static void +start_target (struct gdb_xml_parser *parser, + const struct gdb_xml_element *element, + void *user_data, + VEC(gdb_xml_value_s) *attributes) +{ +// ParsingData* data = user_data; + char *version = VEC_index (gdb_xml_value_s, attributes, 0)->value; + + if (strcmp (version, "1.0") != 0) + gdb_xml_error (parser, + _("Registers description has unsupported version \"%s\""), + version); + + DEBUG("target started\n"); +} + + +/* This function is called by the XML parser when the start of an <architecture> element is seen. */ + +static void +start_architecture (struct gdb_xml_parser *parser, + const struct gdb_xml_element *element, + void *user_data, + VEC(gdb_xml_value_s) *attributes) +{ + DEBUG("architecture started\n"); +} + + +/* This function is called by the XML parser when the end of an <architecture> element is seen. */ + +static void +end_architecture (struct gdb_xml_parser *parser, + const struct gdb_xml_element *element, + void *user_data, + const char *body_text) +{ + ParsingData *data = user_data; + + if (body_text[0] != '\0') + { + data->info->processor = arc_version(body_text); + + if (data->info->processor == UNSUPPORTED_ARCHITECTURE) + warning(_("unknown target architecture '%s' in XML file '%s'\n"), + body_text, data->filename); + } +} + + +/* This function is called by the XML parser when the start of a <feature> element is seen. */ + +static void +start_feature (struct gdb_xml_parser *parser, + const struct gdb_xml_element *element, + void *user_data, + VEC(gdb_xml_value_s) *attributes) +{ + DEBUG("feature started\n"); +} + + +/* This function checks the XML description of an auxiliary register for + correctness, and if it is correct, adds a definition of this register to the + register information that is currently being constructed. + + Parameters: + parser : the XML parser being used + element : a pointer to the parse structure + user_data : a pointer to the parsing data + attributes: a vector containing the attributes of the register from the XML + is_BCR : TRUE if the register is a Build Configuration Register + */ + +static void +add_aux_register (struct gdb_xml_parser *parser, + const struct gdb_xml_element *element, + void *user_data, + VEC(gdb_xml_value_s) *attributes, + Boolean is_BCR) +{ + ParsingData *data = user_data; + ARC_RegisterInfo *info = data->info; + ARC_AuxRegisterDefinition *reg = &data->reg; + struct gdb_xml_value *attrs = VEC_address (gdb_xml_value_s, attributes); + unsigned int length = VEC_length (gdb_xml_value_s, attributes); + unsigned int i; + Boolean add = TRUE; + + initialize_register(reg); + + /* Look at all the register attributes. */ + for (i = 0; i < length; i++) + { + const char *name = attrs[i].name; + void *value = attrs[i].value; + + if (NAME_IS("name")) reg->name = value; + else if (NAME_IS("description")) reg->description = xstrdup(value); + else if (NAME_IS("number")) reg->number = (ARC_RegisterNumber) *(ULONGEST*) value; + else if (NAME_IS("mask")) reg->mask = (ARC_Word) *(ULONGEST*) value; + else if (NAME_IS("access")) reg->access = (RegisterAccess) *(ULONGEST*) value; + } + + if (strcasecmp(reg->name, "unused") == 0) + reg->name = (char*) UNUSED; + else + reg->name = xstrdup (reg->name); + + if (is_BCR) + { + reg->is_BCR = is_BCR; + reg->access = READ_ONLY; + } + + /* Sanity checking. */ + for (i = 0; i < info->aux_register_count; i++) + { + if (reg->name != UNUSED) + if (strcasecmp(reg->name, info->aux_registers[i].name) == 0) + { + warning(_("auxiliary register with name '%s' already defined in file %s"), + reg->name, data->filename); + add = FALSE; + } + + if (reg->number == info->aux_registers[i].number) + { + warning(_("auxiliary register with number %u already defined in file %s"), + reg->number, data->filename); + add = FALSE; + } + } + + if (add) + { + unsigned int name_length = (unsigned int) strlen(reg->name); + + if (name_length > info->max_name_length) + info->max_name_length = name_length; + + info->aux_register_count++; + info->aux_registers = xrealloc(info->aux_registers, + info->aux_register_count * sizeof(ARC_AuxRegisterDefinition)); + + if (info->aux_registers == NULL) + { + /* N.B. this does not return. */ + nomem (0); + } + + /* Copy the register description into the array, and make it the current + register that will be referred to when parsing any fields. */ + data->currentRegister = &info->aux_registers[info->aux_register_count - 1]; + *data->currentRegister = *reg; + } + else + /* Do not copy it into the array, but still make it the current register. */ + data->currentRegister = reg; +} + + +/* This function is called by the XML parser when the start of a <auxregister> element is seen. */ + +static void +start_auxregister (struct gdb_xml_parser *parser, + const struct gdb_xml_element *element, + void *user_data, + VEC(gdb_xml_value_s) *attributes) +{ + add_aux_register(parser, element, user_data, attributes, FALSE); +} + + +/* This function is called by the XML parser when the start of a <bcr> element is seen. */ + +static void +start_bcr (struct gdb_xml_parser *parser, + const struct gdb_xml_element *element, + void *user_data, + VEC(gdb_xml_value_s) *attributes) +{ + add_aux_register(parser, element, user_data, attributes, TRUE); +} + + +/* This function is called by the XML parser when the start of a <ecr> element + is seen. It checks the XML description of an extension core register for + correctness, and if it is correct, adds a definition of this register to the + register information that is currently being constructed. + + Parameters: + parser : the XML parser being used + element : a pointer to the parse structure + user_data : a pointer to the parsing data + attributes: a vector containing the attributes of the register from the XML + */ + +static void +start_ecr (struct gdb_xml_parser *parser, + const struct gdb_xml_element *element, + void *user_data, + VEC(gdb_xml_value_s) *attributes) +{ + ParsingData *data = user_data; + ARC_RegisterInfo *info = data->info; + struct gdb_xml_value *attrs = VEC_address (gdb_xml_value_s, attributes); + unsigned int length = VEC_length (gdb_xml_value_s, attributes); + ARC_RegisterNumber number = 0; + ARC_Word mask = 0xFFFFFFFF; + RegisterAccess access = READ_WRITE; + unsigned int i; + + /* Look at all the register attributes. */ + for (i = 0; i < length; i++) + { + const char *name = attrs[i].name; + void *value = attrs[i].value; + + if (NAME_IS("number")) number = (ARC_RegisterNumber) *(ULONGEST*) value; + else if (NAME_IS("mask")) mask = (ARC_Word) *(ULONGEST*) value; + else if (NAME_IS("access")) access = (RegisterAccess) *(ULONGEST*) value; + } + + /* Sanity checking. */ + if (IS_EXTENSION_CORE_REGISTER(number)) + { + ARC_CoreRegisterDefinition *reg = &info->core_registers[number]; + + if (reg->exists) + { + warning(_("extension core register with number %d already defined in file %s"), + number, data->filename); + } + else + { + reg->mask = mask; + reg->access = access; + reg->exists = TRUE; + } + } + else + warning(_("extension core register with invalid number %d defined in file %s"), + number, data->filename); +} + + +/* This function checks the XML description of an auxiliary register field for + correctness, and if it is correct, adds a definition of this field to the + register information that is currently being constructed. + + Parameters: + parser : the XML parser being used + element : a pointer to the parse structure + user_data : a pointer to the parsing data + attributes: a vector containing the attributes of the field from the XML + is_BCR : TRUE if the field is part of a Build Configuration Register + */ + +static void +add_field (struct gdb_xml_parser *parser, + const struct gdb_xml_element *element, + void *user_data, + VEC(gdb_xml_value_s) *attributes, + Boolean is_BCR) +{ + ParsingData *data = user_data; + ARC_AuxRegisterDefinition *reg = data->currentRegister; + ARC_FieldDefinition *field = &data->field; + struct gdb_xml_value *attrs = VEC_address (gdb_xml_value_s, attributes); + unsigned int length = VEC_length (gdb_xml_value_s, attributes); + unsigned int i; + Boolean add = TRUE; + + /* By default, a field has the same access as the register that contains it + (though a particular field in a R/W register might be R/O or W/O). */ + initialize_field(field, reg->access); + + /* Look at all the field attributes. */ + for (i = 0; i < length; i++) + { + const char *name = attrs[i].name; + void *value = attrs[i].value; + + if (NAME_IS("name")) field->name = value; + else if (NAME_IS("description")) field->description = xstrdup(value); + else if (NAME_IS("offset")) field->offset = (unsigned int) *(ULONGEST*) value; + else if (NAME_IS("size")) field->size = (unsigned int) *(ULONGEST*) value; + else if (NAME_IS("access")) field->access = (RegisterAccess) *(ULONGEST*) value; + else if (NAME_IS("onwrite")) { field->value_for_write = (ARC_RegisterContents) *(ULONGEST*) value; + field->fixed = TRUE; } + } + + if (strcasecmp(field->name, "reserved") == 0) + field->name = (char*) RESERVED; + else + field->name = xstrdup (field->name); + + /* Sanity checking. */ + if (is_BCR) + { + /* BCRs are, by definition, read-only. */ + field->access = READ_ONLY; + } + else + { + if (field->access != WRITE_ONLY && reg->access == WRITE_ONLY) + { + warning (_("field '%s' is readable in write-only auxiliary register '%s' in file %s"), + field->name, reg->name, data->filename); + add = FALSE; + } + else if (field->access != READ_ONLY && reg->access == READ_ONLY) + { + warning (_("field '%s' is writable in read-only auxiliary register '%s' in file %s"), + field->name, reg->name, data->filename); + add = FALSE; + } + } + + if (field->size == 0) + { + warning(_("field '%s' contains no bits in auxiliary register '%s' in file %s"), + field->name, reg->name, data->filename); + add = FALSE; + } + + if (field->offset > BITS_IN_REGISTER - 1) + { + warning(_("field '%s' offset > %u in auxiliary register '%s' in file %s"), + field->name, BITS_IN_REGISTER - 1, reg->name, data->filename); + add = FALSE; + } + + if (field->offset + field->size > BITS_IN_REGISTER) + { + warning(_("field '%s' is too wide in auxiliary register '%s' in file %s"), + field->name, reg->name, data->filename); + add = FALSE; + } + + for (i = 0; i < reg->field_count; i++) + { + ARC_FieldDefinition *f = ®->fields[i]; + + if (field->name != RESERVED) + if (strcasecmp(field->name, f->name) == 0) + { + warning(_("field '%s' already defined in auxiliary register '%s' in file %s"), + field->name, reg->name, data->filename); + add = FALSE; + } + + if (overlaps(field, f)) + { + warning(_("field '%s' overlaps field '%s' in auxiliary register '%s' in file %s"), + field->name, f->name, reg->name, data->filename); + add = FALSE; + } + } + + data->maxFieldContents = 0; + + for (i = 0; i < field->size; i++) + data->maxFieldContents = (data->maxFieldContents << 1) + 1; + + if (field->value_for_write > data->maxFieldContents) + { + warning(_("value on write %u is too large for %u-bit reserved field in auxiliary register '%s' in file %s"), + field->value_for_write, field->size, reg->name, data->filename); + add = FALSE; + } + + if (add) + { + /* Keep track of the longest field name, and the most number of bits in a field. */ + unsigned int len = (unsigned int) strlen(field->name); + + if (len > reg->longest_field_name) + reg->longest_field_name = len; + + if (field->size > reg->max_bits_in_field) + reg->max_bits_in_field = field->size; + + reg->field_count++; + reg->fields = xrealloc(reg->fields, + reg->field_count * sizeof(ARC_FieldDefinition)); + + if (reg->fields == NULL) + { + /* N.B. this does not return. */ + nomem (0); + } + + /* Copy the field description into the array, and make it the current + field that will be referred to when parsing any meanings. */ + data->currentField = ®->fields[reg->field_count - 1]; + *data->currentField = *field; + } + else + /* Do not copy it into the array, but still make it the current field. */ + data->currentField = field; +} + + +/* This function is called by the XML parser when the start of a <field> element is seen. */ + +static void +start_field (struct gdb_xml_parser *parser, + const struct gdb_xml_element *element, + void *user_data, + VEC(gdb_xml_value_s) *attributes) +{ + add_field(parser, element, user_data, attributes, FALSE); +} + + +/* This function is called by the XML parser when the start of a <bcrfield> element is seen. */ + +static void +start_bcrfield (struct gdb_xml_parser *parser, + const struct gdb_xml_element *element, + void *user_data, + VEC(gdb_xml_value_s) *attributes) +{ + add_field(parser, element, user_data, attributes, TRUE); +} + + +/* This function is called by the XML parser when the start of a <meaning> + element is seen. It checks the XML description of the meaning for + correctness, and if it is correct, adds a definition of this meaning to the + register information that is currently being constructed. + + Parameters: + parser : the XML parser being used + element : a pointer to the parse structure + user_data : a pointer to the parsing data + attributes: a vector containing the attributes of the meaning from the XML + */ + +static void +start_meaning (struct gdb_xml_parser *parser, + const struct gdb_xml_element *element, + void *user_data, + VEC(gdb_xml_value_s) *attributes) +{ + ParsingData *data = user_data; + ARC_AuxRegisterDefinition *reg = data->currentRegister; + ARC_FieldDefinition *field = data->currentField; + ARC_FieldMeaning meaning; + struct gdb_xml_value *attrs = VEC_address (gdb_xml_value_s, attributes); + unsigned int length = VEC_length (gdb_xml_value_s, attributes); + unsigned int i; + Boolean add = TRUE; + + initialize_meaning(&meaning); + + /* Look at all the meaning attributes. */ + for (i = 0; i < length; i++) + { + const char *name = attrs[i].name; + void *value = attrs[i].value; + + if (NAME_IS("description")) meaning.description = xstrdup(value); + else if (NAME_IS("value")) meaning.value = (ARC_RegisterContents) *(ULONGEST*) value; + } + + if (meaning.value > data->maxFieldContents) + { + warning(_("value %u is too large for field '%s' in auxiliary register '%s' in file %s"), + meaning.value, field->name, reg->name, data->filename); + add = FALSE; + } + + /* Sanity checking. */ + for (i = 0; i < field->meaning_count; i++) + { + ARC_FieldMeaning *m = &field->meanings[i]; + + if (strcmp(meaning.description, m->description) == 0) + { + warning(_("meaning '%s' already defined for field '%s' in auxiliary register '%s' in file %s"), + meaning.description, field->name, reg->name, data->filename); + add = FALSE; + } + + if (meaning.value == m->value) + { + warning(_("value %u already has meaning for field '%s' in auxiliary register '%s' in file %s"), + meaning.value, field->name, reg->name, data->filename); + add = FALSE; + } + } + + if (add) + { + field->meaning_count++; + field->meanings = xrealloc(field->meanings, + field->meaning_count * sizeof(ARC_FieldMeaning)); + + if (field->meanings == NULL) + { + /* N.B. this does not return. */ + nomem (0); + } + + field->meanings[field->meaning_count - 1] = meaning; + } +} + + +/* This function is passed to qsort to sort the auxiliary registers into a + canonical order such that BCRs come after the non-BCR aux registers, but + otherwise, the registers are ordered by increasing number, with the + exception that the IDENTITY register must come first. */ + +static int +compare_auxiliary_registers(const void *p1, const void *p2) +{ + ARC_AuxRegisterDefinition* reg1 = (ARC_AuxRegisterDefinition*) p1; + ARC_AuxRegisterDefinition* reg2 = (ARC_AuxRegisterDefinition*) p2; + + if (reg1->is_BCR && !reg2->is_BCR) + return 1; + + if (!reg1->is_BCR && reg2->is_BCR) + return -1; + + if (!reg1->is_BCR && !reg2->is_BCR) + { + if (strcmp(reg1->name, "IDENTITY") == 0) + return -1; + if (strcmp(reg2->name, "IDENTITY") == 0) + return 1; + } + + return (reg1->number < reg2->number) ? -1 : 1; +} + + +/* This function assigns the gdb register numbers to the target processor's + registers, using the scheme defined in arc-elf32-tdep.c. */ + +static void +assign_gdb_register_numbers (struct gdbarch *gdbarch, ARC_RegisterInfo *info) +{ + unsigned int i; + int pc = -1; + + /* Core registers first. */ + for (i = 0; i < ELEMENTS_IN_ARRAY(info->core_registers); i++) + { + ARC_CoreRegisterDefinition *reg = &info->core_registers[i]; + + /* If this core register exists in the target, we have one more core + register in total; and the gdb number of this register is that number + less 1. */ + if (reg->exists) + reg->gdb_regno = (int) info->core_register_count++; + } + + info->first_aux_gdb_regno = (int) info->core_register_count; + + /* Then the auxiliary registers. */ + for (i = 0; i < info->aux_register_count; i++) + { + ARC_AuxRegisterDefinition *reg = &info->aux_registers[i]; + + reg->gdb_regno = info->first_aux_gdb_regno + i; + + /* Is this the PC? */ + if (strcasecmp(reg->name, "PC") == 0) + { + pc = reg->gdb_regno; + info->PC_number = pc; + } + } + + /* We must tell gdb that the total number of registers has changed. */ + set_gdbarch_num_regs (gdbarch, (int) (info->core_register_count + + info->aux_register_count)); + + /* The file may have contained a description of the PC, so we must tell gdb + what its number is. + N.B. if the 'replace' parameter to read_XML_file was true and the file + did NOT contain a description of the PC, we now no longer know which + auxiliary register is the PC, so we must set gdb's PC number back to + -1! */ + set_gdbarch_pc_regnum(gdbarch, pc); + + if (pc >= 0) + { + DEBUG("PC is reg #%d (arch %p)\n", pc, gdbarch); + + /* So PC is defined - so remove the guard. */ + arc_aux_pc_guard(gdbarch, FALSE); + } +} + + +/* Parse the XML file containing the register definitions. + + Parameters: + document : the XML document to be parsed (i.e. the XML text) + fetcher : a function which can be used to read an XML document from a file + fetcher_baton : the directory containing the XML file(s) + data : a pointer to a structure used to pass state data + between the parsing routines. */ + +static void +parse_XML (const char *document, + xml_fetch_another fetcher, + void *fetcher_baton, + ParsingData *data) + +{ + struct cleanup *back_to; + struct gdb_xml_parser *parser; + char *expanded_text; + + /* Expand all XInclude directives. */ + expanded_text = xml_process_xincludes (_("aux registers description"), + document, fetcher, fetcher_baton, 0); + if (expanded_text == NULL) + { + warning (_("can not load XML auxiliary registers description")); + return; + } + + back_to = make_cleanup (null_cleanup, NULL); + parser = gdb_xml_create_parser_and_cleanup (_("aux registers description"), + elements, data); + + /* Do the parsing; the DTD file defines the schema to be used by the XML. */ + gdb_xml_use_dtd (parser, "arc-registers.dtd"); + + (void) make_cleanup (xfree, expanded_text); + + if (gdb_xml_parse (parser, expanded_text) != 0) + warning (_("can not load XML auxiliary registers description")); + + do_cleanups (back_to); +} + + +/* Read an XML file containing the register definitions. + + Parameters: + filename : the path to the file + gdbarch : the architecture for which the register set is are being defined + replace : TRUE if any existing set of register defintions should be deleted first + inform : TRUE if a message should be output say that the file has been read + check : TRUE if an architectural check is to be performed once the file has been read + + Result: TRUE if the file was successfully read and its contents parsed. */ + +static Boolean +read_XML_file (const char *filename, + struct gdbarch *gdbarch, + Boolean replace, + Boolean inform, + Boolean check) +{ + char *xml = read_file_contents(filename, NULL); + + DEBUG("reading XML file: %s\n", filename); + + if (xml) + { + ARC_RegisterInfo *info = INFO_OF(gdbarch); + struct cleanup *back_to = make_cleanup (xfree, xml); + char *dirname = ldirname (filename); + ParsingData data; + + memset (&data, 0, sizeof (ParsingData)); + + if (replace) + free_register_set(info); + + info->processor = NO_ARCHITECTURE; + + data.info = info; + data.filename = filename; + + if (dirname != NULL) + (void) make_cleanup (xfree, dirname); + + parse_XML (xml, read_file_contents, dirname, &data); + do_cleanups (back_to); + + if (inform) + printf_filtered(_("Register definitions read from file %s\n"), filename); + + if (check) + ARCHITECTURE_CHECK(gdbarch, + (current_objfile) ? current_objfile->obfd : NULL); + + /* Sort the auxiliary registers into a canonical order: this allows + entries in the XML file to be in any order without affecting the gdb + register numbers assigned to them; in particular, it aids in ensuring + that when the xISS is being used as a remote debug target gdb and the + xISS agree upon the order in which register contents are held in the + RSP 'G' (set all registers) packet and the 'g' (get all registers) + response packet. */ + qsort(info->aux_registers, + (size_t) info->aux_register_count, + sizeof (ARC_AuxRegisterDefinition), + compare_auxiliary_registers); + + /* Now that we know all of the core and auxiliary registers in this + target, we can assign gdb register numbers to them. */ + assign_gdb_register_numbers(gdbarch, info); + + /* We can send the event now only if current_gdbarch is not NULL, or + it could cause an error elsewhere where gdbarch_num_regs or + gdbarch_num_pseudo_regs is used (e.g. in setup_architecture_data in + gdbtk/generic/gdbtk-register.c). */ + if (current_gdbarch == NULL) + arc_pending_register_architecture_change_event = TRUE; + else + reg_architecture_changed_event(); + + return TRUE; + } + + return FALSE; +} + + +/* Try to find a file containing the default AUX register definitions; look in + 1) the current working directory + 2) the user's home directory + + Parameters: + gdbarch : the architecture for which the register set is are being defined + inform : TRUE if a message should be output say that the file has been read + check : TRUE if an architectural check is to be performed once the file has been read +*/ + +static void +read_default_file (struct gdbarch *gdbarch, Boolean inform, Boolean check) +{ + ENTERARGS("inform = %d, check = %d", inform, check); + +#define DESCR "auxiliary registers definition file " + + if (access(REGISTER_DEFINITION_FILE, F_OK) == 0) + { + if (!read_XML_file(REGISTER_DEFINITION_FILE, gdbarch, FALSE, inform, check)) + error(_("Can not read " DESCR REGISTER_DEFINITION_FILE)); + } + else + { + const char *home_dir = getenv ("HOME"); + + if (home_dir) + { + char *home_file = xstrprintf (_("%s/%s"), home_dir, REGISTER_DEFINITION_FILE); + + if (access(home_file, F_OK) == 0) + { + if (!read_XML_file(home_file, gdbarch, FALSE, inform, check)) + error(_("Can not read " DESCR " %s"), home_file); + } + else + warning(_("can not find " DESCR REGISTER_DEFINITION_FILE " in either current directory or $HOME")); + + xfree (home_file); + } + else + warning(_("HOME environment variable is not set - can not find " DESCR REGISTER_DEFINITION_FILE)); + } +} + + +/* -------------------------------------------------------------------------- */ +/* miscellaneous local functions */ +/* -------------------------------------------------------------------------- */ + +/* This is a callback which is called from gdb when it writes the PC. + It is used to "guard" the PC. */ + +static CORE_ADDR +get_pc (struct regcache *regcache) +{ + struct gdbarch *gdbarch = get_regcache_arch (regcache); + + /* This does not return if PC is not defined. */ + arc_aux_check_pc_defined(gdbarch); + + /* So PC is defined - so remove the guard. */ + arc_aux_pc_guard(gdbarch, FALSE); + + /* Now read and return the PC. */ + return read_pc(); +} + + +/* This is a callback which is called from gdb when it reads the PC. + It is used to "guard" the PC. */ + +static void +set_pc (struct regcache *regcache, CORE_ADDR val) +{ + struct gdbarch *gdbarch = get_regcache_arch (regcache); + + /* This does not return if PC is not defined. */ + arc_aux_check_pc_defined(gdbarch); + + /* So PC is defined - so remove the guard. */ + arc_aux_pc_guard(gdbarch, FALSE); + + write_pc(val); +} + + +/* Try to find the register information for the current architecture. + + Parameter: + must_be_defined : TRUE if the register information must be known + + Result: a pointer to the info; NULL if there is none as yet. */ + +static ARC_RegisterInfo* +find_info (Boolean must_be_defined) +{ + gdb_assert (current_gdbarch != NULL); + + { + ARC_RegisterInfo *info = INFO_OF(current_gdbarch); + + /* If we have no aux register info. */ + if (must_be_defined && info->aux_register_count == 0) + { + /* Try to get it. */ + read_default_file(current_gdbarch, FALSE, FALSE); + + /* No, could not get it. */ + if (info->aux_register_count == 0) + error(_("No auxiliary registers have yet been defined for this target")); + } + + return info; + } +} + + +/* Map a gdb register number to the ARC processor hardware number, and + determine the class of the register (as known to the built-in simulator). */ + +static void +simulator_mapping (int gdb_regno, + int *hw_regno, + ARC_RegisterClass *reg_class) +{ + /* Just in case gdb_regno is invalid. */ + *hw_regno = -1; + *reg_class = ARC_UNKNOWN_REGISTER; + + if (arc_is_core_register(gdb_regno)) + { + *hw_regno = (int) arc_core_register_number(gdb_regno); + *reg_class = ARC_CORE_REGISTER; + } + else if (gdb_regno == arc_aux_pc_number(current_gdbarch)) + { + /* The hw_regno is irrelevant here. */ + *reg_class = ARC_PROGRAM_COUNTER; + } + else + { + ARC_AuxRegisterDefinition *def = arc_find_aux_register_by_gdb_number(gdb_regno); + + if (def) + { + *hw_regno = (int) arc_aux_hw_register_number(def); + *reg_class = ARC_AUX_REGISTER; + } + } +} + + +/* -------------------------------------------------------------------------- */ +/* local functions for supporting commands */ +/* -------------------------------------------------------------------------- */ + +/* This function determines the h/w register number of an auxiliary register + from a command argument provided by a user. The argument might be a register + name, a number, or an expression to be evaluated. */ + +static ARC_RegisterNumber +extractRegisterNumber (char *arg) +{ + ARC_AuxRegisterDefinition *def = arc_find_aux_register_by_name(arg); + ARC_RegisterNumber num; + + /* Is it a register name? */ + if (def) + num = arc_aux_hw_register_number(def); + else + { + int regnum; + + /* Is it some expression? */ + EXTRACT(arg, int, regnum) + + if (regnum < 0) + error(_("Register number '%s < 0"), arg); + + num = (ARC_RegisterNumber) regnum; + } + + return num; +} + + +/* This function finds the definition of an auxiliary register from a command + argument provided by a user. The argument might be a register name, a number, + or an expression to be evaluated. + + NULL is returned if the argumenmt does not identify a defined auxiliary + register. */ + +static ARC_AuxRegisterDefinition* +find_aux_register (char *arg) +{ + ARC_AuxRegisterDefinition* def = arc_find_aux_register_by_name(arg); + + /* Is it not a register name? */ + if (def == NULL) + { + int regnum; + + /* Is it some expression? */ + EXTRACT(arg, int, regnum) + + if (regnum < 0) + error(_("Register number '%s < 0"), arg); + + def = arc_find_aux_register_by_hw_number((ARC_RegisterNumber) regnum); + } + + return def; +} + + +/* This function list the description (but not the contents) of an auxiliary + register. + + Parameters: + r : a pointer to the register definition + full: TRUE if full information is to be listed. +*/ + +static void +list_register (ARC_AuxRegisterDefinition *r, Boolean full) +{ + printf_filtered("%s", r->name); + if (r->is_BCR) + printf_filtered(_(" (BCR)")); + printf_filtered(_("\n")); + + if (r->description != NO_DESCRIPTION) + printf_filtered(_(" description: %s\n"), r->description); + printf_filtered(_(" number : 0x%x\n"), r->number); + + if (full) + { + /* The gdb number of the register is internal information which would + be meaningless to the user; the mask is probably not useful either. */ + printf_filtered(_(" gdb number : %d\n"), r->gdb_regno); + printf_filtered(_(" mask : %08X\n"), r->mask); + } + + if (!r->is_BCR) + printf_filtered(_(" access : %s\n"), RegisterAccess_Image(r->access)); + + if (r->fields) + { + unsigned int j; + + printf_filtered(_(" fields\n")); + + for (j = 0; j < r->field_count; j++) + { + ARC_FieldDefinition *f = &r->fields[j]; + + printf_filtered(_(" %s\n"), f->name); + if (f->description != NO_DESCRIPTION) + printf_filtered(_(" description: %s\n"), f->description); + printf_filtered(_(" position : %u:%u\n"), f->offset, f->size); + printf_filtered(_(" access : %s\n"), RegisterAccess_Image(f->access)); + + if (f->meanings) + { + unsigned int k; + + printf_filtered(_(" field meanings\n")); + + for (k = 0; k < f->meaning_count; k++) + { + ARC_FieldMeaning *m = &f->meanings[k]; + + printf_filtered(_(" %x ==> %s\n"), m->value, m->description); + } + } + } + } + + printf_filtered(_("\n")); +} + + +/* This function list the descriptions (but not the contents) of all auxiliary + registers. + + Parameter: + full: TRUE if full information is to be listed. +*/ + +static void +list_registers (Boolean full) +{ + ARC_RegisterInfo *info = find_info(TRUE); + unsigned int i; + + for (i = 0; i < info->aux_register_count; i++) + { + ARC_AuxRegisterDefinition *def = info->aux_registers + i; + + if (def->name != UNUSED) + list_register(def, full); + } +} + + +/* This function reads the contents of an auxiliary register on the target. + + Parameters: + def : the definition of the register + value : set to the contents of the register + warn_on_failure: TRUE if a warning should be issued if the read fails + + Result: TRUE if the register contents are read. */ + +static Boolean +read_aux_register (ARC_AuxRegisterDefinition *def, + ARC_RegisterContents *value, + Boolean warn_on_failure) +{ + int gdb_regno = arc_aux_gdb_register_number(def); + struct regcache *regcache = get_current_regcache(); + + /* Read the register contents from the target to the register cache, + then collect the register value from the cache. */ + target_fetch_registers(regcache, gdb_regno); + regcache_raw_collect (regcache, gdb_regno, value); + + /* Unfortunately, the target_fetch_registers operation does not give us an + indication of success or failure. */ + return TRUE; +} + + +/* This function writes the contents of an auxiliary register on the target. + + Parameters: + def : the definition of the register + value : the contents of the register + warn_on_failure: TRUE if a warning should be issued if the write fails + + Result: TRUE if the register contents are written. */ + +static Boolean +write_aux_register (ARC_AuxRegisterDefinition *def, + ARC_RegisterContents value, + Boolean warn_on_failure) +{ + int gdb_regno = arc_aux_gdb_register_number(def); + struct regcache *regcache = get_current_regcache(); + ARC_RegisterContents written = arc_write_value(def, value); + + /* Supply the register value to the register cache, then write it from the + cache to the target. */ + regcache_raw_supply (regcache, gdb_regno, &written); + target_store_registers(regcache, gdb_regno); + + /* If the value we actually wrote to the target is not the same as the value + we were given (because the register has fields that must have particular + values when written). */ + if (written != value) + { + DEBUG("%s auxiliary register value %08X written as %08X\n", + arc_aux_register_name(def), value, written); + + /* Put the value we were actually given into the register cache, so if + the user then displays the register contents he will see the + unmodified value. */ + regcache_raw_supply (regcache, gdb_regno, &value); + } + + /* Unfortunately, the target_store_registers operation does not give us an + indication of success or failure. */ + return TRUE; +} + + +/* This function is passed to the arc_all_aux_registers iterator. + It is called for each auxiliary register defined for the current architecture; + if the register is a Build Configuration Register, and it is not unused, the + register's contents are read from the target and printed. */ + +static void +print_bcr (ARC_AuxRegisterDefinition *def, void *data) +{ + if (arc_aux_is_BCR(def) && !arc_aux_is_unused(def)) + { + ARC_RegisterNumber bcr = arc_aux_hw_register_number(def); + ARC_RegisterContents bcr_value; + + if (read_aux_register (def, &bcr_value, TRUE)) + printf_filtered(_("[%02x] %-16s : 0x%02x\n"), + bcr, arc_aux_register_name(def), bcr_value); + } +} + + +/* This function may be passed to the arc_all_aux_registers iterator. + It reads the contents of the register whose definition is given from + the target and prints those contents. */ + +static void +show_one_aux_register (ARC_AuxRegisterDefinition *def, void *data) +{ + ARC_RegisterNumber reg_no = arc_aux_hw_register_number(def); + ARC_RegisterContents contents; + + DEBUG("try to read aux reg %u\n", reg_no); + + if (read_aux_register(def, &contents, TRUE)) + arc_print_aux_register(def, contents); +} + + +/* Read the register definitions from a file. + + Parameters: + filename : the path to the file + gdbarch : the architecture for which the register set is are being defined + replace : TRUE if any existing set of register defintions should be deleted first + inform : TRUE if a message should be output say that the file has been read + check : TRUE if an architectural check is to be performed once the file has been read +*/ +static Boolean +read_aux_regs_file (const char *filename, + struct gdbarch *gdbarch, + Boolean replace, + Boolean inform, + Boolean check) +{ + /* Try to read the register descriptions from the file. */ + if (read_XML_file(filename, gdbarch, replace, inform, check)) + return TRUE; + + printf_filtered(_("can not read file '%s'\n"), filename); + return FALSE; +} + + +/* -------------------------------------------------------------------------- */ +/* local functions implementing commands */ +/* -------------------------------------------------------------------------- */ + +/* Command: <command> <from> [ <to> ] + + Read and display a range of auxiliary registers. + + We should eventually change this to use the ui_out stuff rather than + printf_filtered. */ + +static void +arc_aux_reg_read_command (char *arg, int from_tty) +{ + char *arg2; + ARC_RegisterNumber first_regnum, last_regnum, r; + char format[40]; + + if (!arg) + { + printf_filtered (_(AUX_REG_READ_COMMAND_USAGE)); + return; + } + + /* Strip leading spaces. */ + while (*arg == ' ') + arg++; + + /* This assumes that the first arg cannot have spaces (the disas command + also seems to work this way). */ + arg2 = strchr (arg, ' '); + + /* Are there two arguments? */ + if (arg2) + { + /* Split the input string up. */ + arg2[0] = (char) 0; + arg2++; + } + + /* First arg. */ + first_regnum = extractRegisterNumber(arg); + + /* So, how many regs do we want? */ + if (arg2) + { + last_regnum = extractRegisterNumber(arg2); + + if (last_regnum < first_regnum) + { + warning(_(AUX_REG_READ_COMMAND ": %s < %s, showing one register"), arg2, arg); + last_regnum = first_regnum; + } + } + else + last_regnum = first_regnum; + + DEBUG("try to read aux regs %d .. %d\n", first_regnum, last_regnum); + + (void) snprintf(format, sizeof(format), + _("0x%%08x %%-%us: %%08X\n"), + arc_aux_register_max_name_length() + 1); + + for (r = first_regnum; r <= last_regnum; r++) + { + ARC_AuxRegisterDefinition *def = arc_find_aux_register_by_hw_number(r); + + /* If the aux register exists, and is used. */ + if ((def != NULL) && !arc_aux_is_unused(def)) + { + ARC_RegisterContents contents; + + DEBUG("try to read aux reg %u\n", r); + + if (read_aux_register(def, &contents, TRUE)) + printf_filtered (format, r, arc_aux_register_name(def), contents); + } + } +} + + +/* Command: <command> <reg> = <value> + + Write VALUE to auxiliary register REG. + + We should eventually change this to use the ui_out stuff rather than + printf_filtered. */ +static void +arc_aux_reg_write_command (char *arg, int from_tty) +{ + char *value_arg; + char *p; + ARC_RegisterNumber regnum; + ARC_RegisterContents value; + ARC_AuxRegisterDefinition *def; + + if (!arg) + { + printf_filtered (_(AUX_REG_WRITE_COMMAND_USAGE)); + return; + } + + p = strchr(arg, '='); + + if (p == NULL) + { + printf_filtered (_(AUX_REG_WRITE_COMMAND ": no second argument\n" AUX_REG_WRITE_COMMAND_USAGE)); + return; + } + + /* Split up the input string. */ + value_arg = p + 1; + p--; + while (*p == ' ') p--; + p[1] = '\0'; + + /* Register expression. */ + regnum = extractRegisterNumber(arg); + + /* Value expression. */ + EXTRACT(value_arg, ARC_RegisterContents, value) + + def = arc_find_aux_register_by_hw_number(regnum); + + if (def == NULL) + warning(_("no such auxiliary register: %s"), arg); + else + { + DEBUG("try to write aux reg %d = 0x%08X\n", regnum, value); + + /* Write it. */ + (void) write_aux_register(def, value, TRUE); + } +} + + +/* Command: <command> [ <reg> ] + + Display the values of one or all of the auxiliary registers. + + We should eventually change this to use the ui_out stuff rather than + printf_filtered. */ + +static void +arc_aux_reg_show_command (char *arg, int from_tty) +{ + if (arg) + { + ARC_AuxRegisterDefinition *def = find_aux_register(arg); + + if (def) + show_one_aux_register(def, NULL); + else + printf_filtered(_("There is no auxiliary register named '%s'\n"), arg); + } + else + /* list them all */ + arc_all_aux_registers(show_one_aux_register, NULL); +} + + +/* Command: <command> [ <file> ] + + Read a definition of a set of auxiliary registers from an XML file. + + We should eventually change this to use the ui_out stuff rather than + printf_filtered. */ + +static void +arc_aux_reg_file_read_command (char *arg, int from_tty) +{ + if (!arg) + { + printf_filtered (REG_READ_FILE_COMMAND_USAGE); + return; + } + + /* The new set replaces the existing set (if any). */ + (void) read_aux_regs_file(arg, current_gdbarch, TRUE, TRUE, TRUE); +} + + +/* Command: <command> <file> + + Read a definition of a set of auxiliary registers from an XML file. + + We should eventually change this to use the ui_out stuff rather than + printf_filtered. */ + +static void +arc_aux_reg_file_read_extra_command (char *arg, int from_tty) +{ + if (!arg) + { + printf_filtered (REG_READ_EXTRA_FILE_COMMAND_USAGE); + return; + } + + /* The new set is added to the existing set (if any). */ + (void) read_aux_regs_file(arg, current_gdbarch, FALSE, TRUE, TRUE); +} + + +/* Command: <command> [ <reg> ] + + Display a description of one or all auxiliary registers. + + We should eventually change this to use the ui_out stuff rather than + printf_filtered. */ + +static void +arc_aux_reg_list_command (char *arg, int from_tty) +{ + if (arg) + { + ARC_AuxRegisterDefinition *def = find_aux_register(arg); + + if (def) + list_register(def, FALSE); + else + printf_filtered(_("There is no auxiliary register named '%s'\n"), arg); + } + else + /* List them all. */ + list_registers(FALSE); +} + + +/* Command: <command> + + Display the Build Configuration Registers. + + We should eventually change this to use the ui_out stuff rather than + printf_filtered. */ + +static void +arc_print_bcr_regs (char *arg, int from_tty) +{ + arc_all_aux_registers(print_bcr, NULL); +} + + +/* -------------------------------------------------------------------------- */ +/* externally visible functions */ +/* -------------------------------------------------------------------------- */ + +/* Initialize the given register information to the base configuration: + 1) core registers exist + 2) extension core registers do not exist + 3) no aux registers + */ + +void +arc_initialize_aux_reg_info (ARC_RegisterInfo *info) +{ + unsigned int i; + + info->processor = NO_ARCHITECTURE; + info->aux_registers = NULL; + info->aux_register_count = 0; + info->max_name_length = 0; + info->PC_number = -1; + info->first_aux_gdb_regno = 0; + info->core_register_count = 0; + + /* All possible core registers. */ + for (i = 0; i < ELEMENTS_IN_ARRAY(info->core_registers); i++) + { + ARC_CoreRegisterDefinition *reg = &info->core_registers[i]; + + reg->exists = TRUE; + reg->mask = 0xFFFFFFFF; + reg->access = READ_WRITE; + } + + /* We do not yet know if we have any extension registers. */ + for (i = ARC_FIRST_EXTENSION_CORE_REGISTER; i <= ARC_LAST_EXTENSION_CORE_REGISTER; i++) + info->core_registers[i].exists = FALSE; + + /* R61 is reserved, R62 is not a real register. */ + info->core_registers[61].exists = FALSE; + info->core_registers[62].exists = FALSE; + + /* PCL is read-only */ + info->core_registers[63].access = READ_ONLY; +} + + +/* Read the XML registers definition for the given architecture from the default file. */ + +void +arc_read_default_aux_registers (struct gdbarch *gdbarch) +{ + ENTERMSG; + read_default_file(gdbarch, FALSE, FALSE); +} + + + +/* This function sets up or remaoves a "guard" on the PC */ + +void +arc_aux_pc_guard (struct gdbarch *gdbarch, Boolean on) +{ + set_gdbarch_read_pc (gdbarch, (on) ? get_pc : NULL); + set_gdbarch_write_pc(gdbarch, (on) ? set_pc : NULL); +} + + +/* Check whether the PC aux register is defined for the given architecture. */ + +void +arc_aux_check_pc_defined (struct gdbarch *gdbarch) +{ + ENTERARGS("target %s", current_target.to_shortname); + + if (gdbarch == NULL) + gdbarch = current_gdbarch; + + if (arc_aux_pc_number(gdbarch) < 0) + error(_("There is no auxiliary register description for the PC (Program Counter)")); +} + + +/* Return the gdb register number of the PC (Program Counter); -1 if the PC is not defined. */ + +int +arc_aux_pc_number (struct gdbarch *gdbarch) +{ + ARC_RegisterInfo *info = INFO_OF(gdbarch); + + return (info) ? info->PC_number : -1; +} + + +/* Find the register definition of the given aux register (identified by name). */ + +ARC_AuxRegisterDefinition* +arc_find_aux_register_by_name (const char *name) +{ + FIND_REGISTER_DEFINITION_SUCH_THAT(strcasecmp(name, def->name) == 0) +} + + +/* Find the register definition of the given aux register (identified by hardware register number). */ + +ARC_AuxRegisterDefinition* +arc_find_aux_register_by_hw_number (ARC_RegisterNumber hw_regno) +{ + FIND_REGISTER_DEFINITION_SUCH_THAT(hw_regno == def->number) +} + + +/* Find the register definition of the given aux register (identified by gdb register number). */ + +ARC_AuxRegisterDefinition* +arc_find_aux_register_by_gdb_number (int gdb_regno) +{ + /* N.B. the elements in the info->aux_registers array have strictly increasing + gdb numbers starting at info->first_aux_gdb_regno, so we can index the array + instead of searching it. */ + ARC_RegisterInfo *info = find_info(TRUE); + int index = gdb_regno - info->first_aux_gdb_regno; + + if (0 <= index && index < (int) info->aux_register_count) + { + ARC_AuxRegisterDefinition *def = info->aux_registers + index; + + /* Just to be sure we have found the right element. */ + gdb_assert(def->gdb_regno == gdb_regno); + + return def; + } + + return NULL; +} + + +/* Return the hardware register number of the given named auxiliary register; + if no register set is currently defined, or there is no register of that + name in the set, return the given default number. */ + +ARC_RegisterNumber +arc_aux_find_register_number (const char *name, + ARC_RegisterNumber defaultNumber) +{ + if (arc_aux_regs_defined(current_gdbarch)) + { + ARC_AuxRegisterDefinition *def = arc_find_aux_register_by_name(name); + + if (def != NULL) + return arc_aux_hw_register_number(def); + } + + warning(_("there is no auxiliary register description for the %s register - " + "using 0x%x for the register number"), name, defaultNumber); + return defaultNumber; +} + + +/* Return the hardware register number of the given core register. */ + +ARC_RegisterNumber +arc_core_register_number (int gdb_regno) +{ + ARC_RegisterInfo *info = find_info(TRUE); + unsigned int i; + + /* The lower-numbered set of non-extension core registers (i.e. excluding + R60 .. R63) have fixed gdb numbers which are the same as the h/w number. */ + if (gdb_regno < ARC_FIRST_EXTENSION_CORE_REGISTER) + return (ARC_RegisterNumber) gdb_regno; + + /* Scan the rest of the array. */ + for (i = ARC_FIRST_EXTENSION_CORE_REGISTER; i < ELEMENTS_IN_ARRAY(info->core_registers); i++) + { + ARC_CoreRegisterDefinition *def = &info->core_registers[i]; + + if (def->exists) + if (gdb_regno == def->gdb_regno) + return (ARC_RegisterNumber) (i); + } + + /* Too large to be the number of a core register. */ + return ARC_MAX_CORE_REGS + 1000; +} + + +/* Return the gdb register number of the given core register. */ + +int +arc_core_register_gdb_number (ARC_RegisterNumber hw_regno) +{ + ARC_RegisterInfo *info = find_info(TRUE); + ARC_CoreRegisterDefinition *def = &info->core_registers[hw_regno]; + + gdb_assert(def->exists); + + return def->gdb_regno; +} + + +/* Print the aux register whose definition and contents are given. + The field and meaning information in the definition is used to + give a detailed display of the register. */ + +void +arc_print_aux_register (ARC_AuxRegisterDefinition *def, + ARC_RegisterContents contents) + +{ + printf_filtered (_("%s : %08x\n"), def->name, contents); + + if (def->fields) + { + unsigned int i; + char format[128]; + + printf_filtered(_(" fields\n")); + + /* Create a format string such as + + " %-10s: %8s" + + so that the field name is left-justified, and the field value is + right-justified. */ + (void) snprintf(format, sizeof(format), + _(" %%-%us: %%%us"), + def->longest_field_name, def->max_bits_in_field); + + for (i = 0; i < def->field_count; i++) + { + ARC_FieldDefinition *f = &def->fields[i]; + ARC_RegisterContents val = contents >> f->offset; + ARC_RegisterContents val2 = val; + ARC_RegisterContents mask = 0; + char bits[BITS_IN_REGISTER]; + char *p = &bits[BITS_IN_REGISTER - 1]; + unsigned int b; + + *p = '\0'; + + /* Build up a string representing the bits of the field, starting + with the least significant bit, which will be the rightmost digit + displayed; at the same time, construct a mask of 1-bits of the + same size as the field. */ + for (b = 0; b < f->size; b++) + { + p--; + *p = (val & 1) ? '1' : '0'; + val >>= 1; + + mask <<= 1; + mask++; + } + + printf_filtered(format, f->name, p); + + if (f->meanings) + { + unsigned int j; + + val2 &= mask; + + /* Look for a meaning for this particular value of the field. */ + for (j = 0; j < f->meaning_count; j++) + { + ARC_FieldMeaning *m = &f->meanings[j]; + + if (val2 == m->value) + { + printf_filtered(_(" (%s)"), m->description); + break; + } + } + } + + printf_filtered(_("\n")); + } + } + + printf_filtered(_("\n")); +} + + +/* Return the gdb register number of the given aux register. */ + +int +arc_aux_gdb_register_number (ARC_AuxRegisterDefinition *def) +{ + return def->gdb_regno; +} + + +/* Return the hardware register number of the given aux register. */ + +ARC_RegisterNumber +arc_aux_hw_register_number (ARC_AuxRegisterDefinition *def) +{ + return def->number; +} + + +/* Return the access mode (R/W, RO 0r WO) of the given aux register. */ + +RegisterAccess +arc_aux_register_access (ARC_AuxRegisterDefinition *def) +{ + return def->access; +} + + +/* Return TRUE if the given aux register is not used in the processor architecture, + i.e. there is a "place-holder" definition of that register (possibly a BCR) in + the XML file, but that register does not actually exist on the target. */ + +Boolean +arc_aux_is_unused (ARC_AuxRegisterDefinition *def) +{ + return (def->name == UNUSED); +} + + +/* Return TRUE if the given aux register is a BCR (Build Configuration Register). */ + +Boolean +arc_aux_is_BCR (ARC_AuxRegisterDefinition *def) +{ + return def->is_BCR; +} + + +/* Return the name of the given aux register. */ + +const char* +arc_aux_register_name (ARC_AuxRegisterDefinition *def) +{ + return def->name; +} + + +/* Return the access mode (R/W, RO 0r WO) of the given core register. */ + +RegisterAccess +arc_core_register_access (ARC_RegisterNumber hw_regno) +{ + ARC_RegisterInfo *info = find_info(TRUE); + ARC_CoreRegisterDefinition *def = &info->core_registers[hw_regno]; + + gdb_assert(def->exists); + + return def->access; +} + + +/* Return the name of the given auxiliary register. */ + +const char* +arc_aux_register_name_of (ARC_RegisterNumber hw_regno) +{ + ARC_AuxRegisterDefinition *def = arc_find_aux_register_by_hw_number(hw_regno); + + return (def) ? def->name : "<no such register>"; +} + + +/* Return TRUE if the given register is a core register. */ + +Boolean +arc_is_core_register (int gdb_regno) +{ + ARC_RegisterInfo *info = find_info(TRUE); + + return (gdb_regno < info->first_aux_gdb_regno); +} + + +/* Iterate across all the aux registers; for each one, call the supplied + function, passing it the defintion of the register and the supplied data. */ + +void +arc_all_aux_registers (ARC_AuxRegisterFunction function, void *data) +{ + ARC_RegisterInfo *info = find_info(TRUE); + unsigned int i; + + for (i = 0; i < info->aux_register_count; i++) + function(info->aux_registers + i, data); +} + + +/* Return the length of the longest aux register name in the current architecture. */ +unsigned int +arc_aux_register_max_name_length (void) +{ + ARC_RegisterInfo *info = find_info(TRUE); + + return info->max_name_length; +} + + +/* Return the number of aux registers defined for the given architecture; + 0 if no register set has yet been defined. */ + +unsigned int +arc_aux_register_count (struct gdbarch *gdbarch) +{ + ARC_RegisterInfo *info = INFO_OF(gdbarch); + + return (info) ? info->aux_register_count : 0; +} + + +/* Return the number of core registers defined for the given architecture; + ARC_NUM_STANDARD_CORE_REGS if no register set has yet been defined. */ + +unsigned int +arc_core_register_count (struct gdbarch *gdbarch) +{ + ARC_RegisterInfo *info = INFO_OF(gdbarch); + + return (info) ? info->core_register_count : ARC_NUM_STANDARD_CORE_REGS; +} + + +/* Return TRUE if the register set has been defined for the given architecture. */ + +Boolean +arc_aux_regs_defined (struct gdbarch *gdbarch) +{ + ARC_RegisterInfo *info = INFO_OF(gdbarch); + + return (info->aux_register_count > 0 && info->aux_registers != NULL); +} + + +/* Return the architectural version of the register set associated with the + given architecture. */ + +ARC_ProcessorVersion +arc_aux_architecture (struct gdbarch *gdbarch) +{ + ARC_RegisterInfo *info = INFO_OF(gdbarch); + + return info->processor; +} + + +/* Compute the value to be written to an auxiliary register so that any fields it + has contain the values that they are required to have by the ARC architectural + specification. + + Parameters: + def : the definition of the aux register + value : the value to be adjusted + + Result: the adjusted register contents. */ + +ARC_RegisterContents +arc_write_value (ARC_AuxRegisterDefinition *def, + ARC_RegisterContents value) +{ + unsigned int i; + + for (i = 0; i < def->field_count; i++) + { + ARC_FieldDefinition *field = &def->fields[i]; + + if (field->fixed) + { + value &= ~(((1 << field->size) - 1) << field->offset); + value |= (field->value_for_write << field->offset); + } + } + + return value; +} + + +/* Adjust the value to be written to a register so that any fields it has contain + the values that they are required to have by the ARC architectural specification. + + Parameters: + gdb_regno : the number of the aux register + buffer : the value to be adjusted + */ + +void +arc_convert_aux_contents_for_write (int gdb_regno, void *buffer) +{ + ARC_AuxRegisterDefinition *def = arc_find_aux_register_by_gdb_number(gdb_regno); + + /* Is it an auxiliary register? If not, do nothing. */ + if (def) + { + ARC_RegisterContents old; + ARC_RegisterContents new; + + memcpy(&old, buffer, BYTES_IN_REGISTER); + new = arc_write_value(def, old); + + if (new != old) + { + DEBUG("*** converted register %s value from %08X to %08X\n", + arc_aux_register_name(def), old, new); + memcpy(buffer, &new, BYTES_IN_REGISTER); + } + } +} + + +/* Initialize the module. This function is called from the gdb core on start-up. */ + +void +_initialize_arc_aux_regs (void) +{ + arc_pending_register_architecture_change_event = FALSE; + + /* If this module is being built with a test driver. */ +#ifdef STANDALONE_TEST + /* N.B. it would be better to set this up in the test driver, but that + causes problems when linking! */ + struct gdbarch_info info; + static ARC_VariantsInfo variant; + struct gdbarch_tdep *tdep = malloc (sizeof (struct gdbarch_tdep)); + struct gdbarch *gdbarch = gdbarch_alloc (&info, tdep); + + tdep->processor_variant_info = &variant; + current_gdbarch = gdbarch; +#endif + + (void) add_cmd (REG_READ_FILE_COMMAND, + class_files, + arc_aux_reg_file_read_command, + _("Read a file describing a set of auxiliary registers.\n" + REG_READ_FILE_COMMAND_USAGE + "<FILE> is an XML file containing the auxiliary register definitions.\n"), + &cmdlist); + + (void) add_cmd (REG_READ_EXTRA_FILE_COMMAND, + class_files, + arc_aux_reg_file_read_extra_command, + _("Read a file describing an additional set of auxiliary registers.\n" + REG_READ_EXTRA_FILE_COMMAND_USAGE + "<FILE> is an XML file containing the auxiliary register definitions.\n"), + &cmdlist); + + (void) add_cmd (AUX_LIST_REGISTER_COMMAND, + class_vars, + arc_aux_reg_list_command, + _("Show a description of one or all auxiliary registers.\n" + AUX_LIST_REGISTER_COMMAND_USAGE + "<REG> is the name of an auxiliary register.\n"), + &cmdlist); + + (void) add_cmd(ARC_BCR_COMMAND, + class_info, + arc_print_bcr_regs, + _("Show Build Configuration Registers in the ARC processor variant.\n" + ARC_BCR_COMMAND_USAGE), + &infolist); + + (void) add_cmd (AUX_REG_READ_COMMAND, + class_vars, + arc_aux_reg_read_command, + _("Read and show a range of auxiliary registers.\n" + AUX_REG_READ_COMMAND_USAGE + "REG-FROM and REG-TO are the names or numbers of the registers.\n" + "If REG-TO is not specified, one register is displayed.\n"), + &cmdlist); + + (void) add_cmd (AUX_REG_WRITE_COMMAND, + class_vars, + arc_aux_reg_write_command, + _("Write to an auxiliary register.\n" + AUX_REG_WRITE_COMMAND_USAGE + "REG is the name or number of the register.\n" + "VALUE can be any expression that evaluates to an integer.\n"), + &cmdlist); + + (void) add_cmd (AUX_REG_SHOW_COMMAND, + class_vars, + arc_aux_reg_show_command, + _("Read and show one or all auxiliary registers.\n" + AUX_REG_SHOW_COMMAND_USAGE + "<REG> is the name of an auxiliary register.\n"), + &cmdlist); + + /* Provide the built-in simulator with a functions that it can use to map + from gdb register numbers to h/w register numbers, and set the fields + of aux registers to any values that they may be required to have on write. */ + arc_set_register_mapping(&simulator_mapping); + arc_set_aux_register_conversion(arc_convert_aux_contents_for_write); +} + +/******************************************************************************/ diff --git a/gdb/arc-registers.h b/gdb/arc-registers.h new file mode 100644 index 00000000000..9fb6521351a --- /dev/null +++ b/gdb/arc-registers.h @@ -0,0 +1,226 @@ +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. + + Copyright 2008, 2009 Free Software Foundation, Inc. + + Contributed by ARC International (www.arc.com) + + Authors: + Richard Stuckey <richard.stuckey@arc.com> + + 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 3 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, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This header file defines operations for manipulating the ARC processor */ +/* core registers and auxiliary registers. */ +/* */ +/******************************************************************************/ + +#ifndef ARC_REGISTERS_H +#define ARC_REGISTERS_H + +/* gdb header files */ +#include "defs.h" +#include "gdbarch.h" + +/* ARC header files */ +#include "arc-support.h" +#include "arc-tdep.h" +#include "arc-architecture.h" + + +#define REGISTER_DEFINITION_FILE "arc-registers.xml" + + +/* ARC aux registers with known (well-defined) numbers. */ +#define ARC_HW_LP_START_REGNUM (ARC_RegisterNumber) 0x2 +#define ARC_HW_LP_END_REGNUM (ARC_RegisterNumber) 0x3 +#define ARC_HW_IDENTITY_REGNUM (ARC_RegisterNumber) 0x4 +#define ARC_HW_DEBUG_REGNUM (ARC_RegisterNumber) 0x5 +#define ARC_HW_PC_REGNUM (ARC_RegisterNumber) 0x6 +#define ARC_HW_STATUS32_REGNUM (ARC_RegisterNumber) 0xA + +/* Specific ARCangel aux registers for caches. */ +#define ARC_HW_IC_IVIC_REGNUM (ARC_RegisterNumber) 0x10 /* Invalidate ICache. */ +#define ARC_HW_IC_CTRL_REGNUM (ARC_RegisterNumber) 0x11 /* Disable ICache. */ +#define ARC_HW_DC_IVDC_REGNUM (ARC_RegisterNumber) 0x47 /* Invalidate DCache. */ +#define ARC_HW_DC_CTRL_REGNUM (ARC_RegisterNumber) 0x48 /* Disable DCache. */ + +/* BCRs. */ +#define ARC_HW_MEMSUBSYS_REGNUM (ARC_RegisterNumber) 0x67 +#define ARC_HW_AP_BUILD_REGNUM (ARC_RegisterNumber) 0x76 + +/* Auxiliary registers for actionpoint 0 (there are up to 8 sets of these). */ +#define ARC_HW_AMV0_REGNUM (ARC_RegisterNumber) 0x220 +#define ARC_HW_AMM0_REGNUM (ARC_RegisterNumber) 0x221 +#define ARC_HW_AC0_REGNUM (ARC_RegisterNumber) 0x222 + + +/* Bit masks for use with the cache-related auxiliary registers. */ +#define IC_IVIC_IV 0x00000001 +#define DC_IVDC_IV 0x00000001 +#define IC_CTRL_DC 0x00000001 +#define DC_CTRL_DC 0x00000001 +#define DC_CTRL_IM 0x00000040 + + +/* Bit masks for use with the auxiliary DEBUG, IDENTITY, STATUS32 and AP_BUILD registers. */ +#define DEBUG_SH 0x40000000 +#define DEBUG_BH 0x20000000 +#define DEBUG_USER 0x10000000 +#define DEBUG_ACTIONPOINT_HALT 0x00000004 +#define DEBUG_ACTIONPOINT_STATUS 0x000007F8 +#define DEBUG_ACTIONPOINT_STATUS_SHIFT 3 +#define DEBUG_FORCE_HALT 0x00000002 +#define DEBUG_INSTRUCTION_STEP 0x00000800 +#define DEBUG_SINGLE_STEP 0x00000001 +#define DEBUG_LOAD_PENDING 0x80000000 +#define IDENTITY_ARCVER 0x000000FF +#define STATUS32_USER 0x00000080 +#define STATUS32_L 0x00000100 +#define STATUS32_HALT 0x00000001 +#define AP_BUILD_VERSION 0x000000FF +#define AP_BUILD_TYPE 0x00000F00 +#define AP_BUILD_TYPE_SHIFT 8 + + +typedef enum register_access +{ + READ_ONLY, + READ_WRITE, + WRITE_ONLY +} RegisterAccess; + + +struct core_register_definition +{ + int gdb_regno; + ARC_Word mask; + RegisterAccess access; + Boolean exists; +}; + + +/* The type 'struct aux_register_definition' is incomplete: it is private to this + module. */ +typedef struct aux_register_definition ARC_AuxRegisterDefinition; +typedef struct core_register_definition ARC_CoreRegisterDefinition; + + +/* This type is essentially private: no access to any of its fields should + be performed outside of this module. */ +typedef struct _register_info +{ + ARC_ProcessorVersion processor; + ARC_AuxRegisterDefinition *aux_registers; + unsigned int aux_register_count; + int first_aux_gdb_regno; + unsigned int max_name_length; + int PC_number; + ARC_CoreRegisterDefinition core_registers[ARC_MAX_CORE_REGS]; + unsigned int core_register_count; +} ARC_RegisterInfo; + + +typedef void (*ARC_AuxRegisterFunction)(ARC_AuxRegisterDefinition *def, void *data); + + +extern Boolean arc_pending_register_architecture_change_event; + + +/* Initialization functions. */ + +void arc_initialize_aux_reg_info (ARC_RegisterInfo *info); + +void arc_read_default_aux_registers (struct gdbarch *gdbarch); + + +/* PC-related functions. */ + +void arc_aux_pc_guard (struct gdbarch *gdbarch, Boolean on); + +void arc_aux_check_pc_defined (struct gdbarch *gdbarch); + +int arc_aux_pc_number (struct gdbarch *gdbarch); + + +/* Output functions. */ + +void arc_print_aux_register (ARC_AuxRegisterDefinition *def, + ARC_RegisterContents contents); + + +/* Search functions. */ + +ARC_AuxRegisterDefinition* arc_find_aux_register_by_name (const char *name); + +ARC_AuxRegisterDefinition* arc_find_aux_register_by_gdb_number (int gdb_regno); + +ARC_AuxRegisterDefinition* arc_find_aux_register_by_hw_number (ARC_RegisterNumber hw_regno); + +ARC_RegisterNumber arc_aux_find_register_number (const char *name, + ARC_RegisterNumber defaultNumber); + +ARC_RegisterNumber arc_core_register_number (int gdb_regno); + +int arc_core_register_gdb_number (ARC_RegisterNumber hw_regno); + +const char* arc_aux_register_name_of (ARC_RegisterNumber hw_regno); + +Boolean arc_is_core_register (int gdb_regno); + + +/* Accessor functions. */ + +int arc_aux_gdb_register_number (ARC_AuxRegisterDefinition *def); + +ARC_RegisterNumber arc_aux_hw_register_number (ARC_AuxRegisterDefinition *def); + +RegisterAccess arc_aux_register_access (ARC_AuxRegisterDefinition *def); + +Boolean arc_aux_is_unused (ARC_AuxRegisterDefinition *def); + +Boolean arc_aux_is_BCR (ARC_AuxRegisterDefinition *def); + +const char* arc_aux_register_name (ARC_AuxRegisterDefinition *def); + +RegisterAccess arc_core_register_access (ARC_RegisterNumber regno); + + +/* Iterator/summary functions. */ + +void arc_all_aux_registers (ARC_AuxRegisterFunction function, void *data); + +unsigned int arc_aux_register_max_name_length (void); + +unsigned int arc_aux_register_count (struct gdbarch *gdbarch); + +unsigned int arc_core_register_count (struct gdbarch *gdbarch); + +Boolean arc_aux_regs_defined (struct gdbarch *gdbarch); + +ARC_ProcessorVersion arc_aux_architecture (struct gdbarch *gdbarch); + +/* Register contents conversion functions. */ + +ARC_RegisterContents arc_write_value (ARC_AuxRegisterDefinition *def, ARC_RegisterContents value); + +void arc_convert_aux_contents_for_write (int gdb_regno, void *buffer); + + +#endif /* ARC_REGISTERS_H */ +/******************************************************************************/ diff --git a/gdb/arc-regnums-defs.h b/gdb/arc-regnums-defs.h index 6071ee43def..bc872954d21 100644 --- a/gdb/arc-regnums-defs.h +++ b/gdb/arc-regnums-defs.h @@ -1,108 +1,125 @@ -/* Target dependent code for ARC700, for GDB, the GNU debugger. +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. - Copyright 2005 Free Software Foundation, Inc. + Copyright 2005, 2008 Free Software Foundation, Inc. Contributed by Codito Technologies Pvt. Ltd. (www.codito.com) - Authors: - Ramana Radhakrishnan <ramana.radhakrishnan@codito.com> + Authors: + Ramana Radhakrishnan <ramana.radhakrishnan@codito.com> + Richard Stuckey <richard.stuckey@arc.com> 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 + the Free Software Foundation; either version 3 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. -*/ + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This file defines register numbers for ARC processors. */ +/* */ +/* N.B. this file is NOT a module header file and must NOT have an inclusion */ +/* guard: it may be included at multiple points in a module. */ +/* */ +/* THIS FILE IS NO LONGER USED!!!! */ +/* */ +/******************************************************************************/ + +#ifndef STANDALONE_TEST +#error This file is no longer used +#endif + +/* Auxiliary Registers. */ #ifdef RAUX -#ifdef ARC4_JTAG - -RAUX ( STATUS , 0x0, "Status Regnum " , ARC_STATUS_REGNUM , A4 ) -RAUX ( SEMAPHORE , 0x1, "Semaphore Regnum ", ARC_SEMAPHORE_REGNUM , A4 ) -RAUX ( LP_START , 0x2, "Loop Start" , ARC_LP_START_REGNUM , A4 ) -RAUX ( LP_END , 0x3, "Loop End", ARC_LP_END_REGNUM , A4 ) -RAUX ( IDENTITY , 0x4, "Identity", ARC_IDENTITY_REGNUM , A4 ) -RAUX ( DEBUG , 0x5, "Debug" , ARC_DEBUG_REGNUM , A4 ) - -#else - -RAUX ( STATUS , 0x0, "Status Regnum (obsolete)" , ARC_STATUS_REGNUM , ARCompact ) -RAUX ( SEMAPHORE , 0x1, "Semaphore Regnum ", ARC_SEMAPHORE_REGNUM , ARCompact ) -RAUX ( LP_START , 0x2, "Loop Start" , ARC_LP_START_REGNUM , ARCompact ) -RAUX ( LP_END , 0x3, "Loop End", ARC_LP_END_REGNUM , ARCompact ) -RAUX ( IDENTITY , 0x4, "Identity", ARC_IDENTITY_REGNUM , ARCompact ) -RAUX ( DEBUG , 0x5, "Debug" , ARC_DEBUG_REGNUM , ARCompact ) -RAUX ( PC, 0x6,"PC" , ARC_PC_REGNUM ,ARCompact ) -RAUX ( STATUS32 ,0xA,"STATUS32", ARC_STATUS32_REGNUM , ARCompact ) -RAUX ( STATUS32_L1 , 0xB, "STATUS32 register in case of L1 interrupts" ,ARC_STATUS32_L1_REGNUM , ARCompact ) -RAUX ( STATUS32_L2 , 0xC, "STATUS32 register in case of L2 interrupts" ,ARC_STATUS32_L2_REGNUM , ARCompact ) -RAUX ( COUNT0 , 0x21, "Processor Timer 1 Count Value", ARC_COUNT0_REGNUM , ARCompact ) -RAUX ( CONTROL0 , 0x22, "Processor Timer 1 Control Value" , ARC_CONTROL0_REGNUM, ARCompact) -RAUX ( LIMIT0 , 0x23, "Processor Timer 1 Limit Value" , ARC_LIMIT0_REGNUM, ARCompact ) -RAUX ( INT_VECTOR_BASE , 0x25, "Interrupt Vector Base Register", ARC_INT_VECTOR_BASE_REGNUM , ARCompact ) -RAUX ( AUX_IRQ_MACMODE , 0x41, "Aux IRQ MAC Mode " , ARC_AUX_MACMODE_REGNUM , ARCompact ) -RAUX ( AUX_IRQ_LV12 , 0x42, "Aux IRQ Level 2 " , ARC_AUX_IRQ_LV12_REGNUM , ARCompact ) -RAUX ( COUNT1 , 0x100, "Processor Timer 1 Count Value", ARC_COUNT1_REGNUM , ARCompact ) -RAUX ( CONTROL1 , 0x101, "Processor Timer 1 Control Value" , ARC_CONTROL1_REGNUM , ARCompact) -RAUX ( LIMIT1 , 0x102, "Processor Timer 1 Limit Value", ARC_LIMIT1_REGNUM , ARCompact) -RAUX ( AUX_IRQ_LEV , 0x200, "Interrupt Level programming. ", ARC_AUX_IRQ_LEV_REGNUM , ARCompact) -RAUX ( AUX_IRQ_HINT , 0x201, "Software Triggered Interrupt" , ARC_AUX_IRQ_HINT_REGNUM, ARCompact ) -RAUX ( ERET , 0x400, "Exception Return " , ARC_ERET_REGNUM, ARC700 ) -RAUX ( ERBTA , 0x401, "Exception BTA ", ARC_ERBTA_REGNUM, ARC700 ) -RAUX ( ERSTATUS , 0x402, "Exception Return Status" , ARC_ERSTATUS_REGNUM, ARC700 ) -RAUX ( ECR , 0x403, "Exception Cause Register" , ARC_ECR_REGNUM, ARC700 ) -RAUX ( EFA , 0x404, "Exception Fault Address" , ARC_EFA_REGNUM, ARC700 ) -RAUX ( ICAUSE1 , 0x40A, "Interrupt Cause (Level 1)", ARC_ICAUSE1_REGNUM,ARC700 ) -RAUX ( ICAUSE2 , 0x40B, "Interrupt Cause (Level 2)", ARC_ICAUSE2_REGNUM, ARC700) -RAUX ( AUX_IENABLE , 0x40C, "Interrupt Mask Programming", ARC_AUX_IENABLE_REGNUM, ARC700 ) -RAUX ( AUX_ITRIGGER , 0x40D, "Interrupt Sensitivity Programming", ARC_AUX_ITRIGGER_REGNUM, ARC700 ) -RAUX ( XPU , 0x410, "User Mode Extension Permissions", ARC_XPU_REGNUM, ARC700 ) -RAUX ( BTA , 0x412, "Branch Target Address", ARC_BTA_REGNUM, ARC700 ) -RAUX ( BTA_L1 , 0x413, "Branch Target Address in Level 1", ARC_BTA_L1_REGNUM, ARC700 ) -RAUX ( BTA_L2 , 0x414, "Branch Target Address in Level 2", ARC_BTA_L2_REGNUM, ARC700 ) -RAUX ( AUX_IRQ_PULSE_CANCEL , 0x415, "Interrupt Pulse Cancel", ARC_AUX_IRQ_PULSE_CANCEL_REGNUM, ARC700 ) -RAUX ( AUX_IRQ_PENDING , 0x416, "Interrupt Pending Register", ARC_AUX_IRQ_PENDING_REGNUM, ARC700 ) -#endif // ARC4_JTAG + +RAUX ( STATUS, 0x0, "Status (obsolete)", ARC_STATUS_REGNUM, 0xFEFFFFFF, RO, ARCompact ) +RAUX ( SEMAPHORE, 0x1, "Semaphore", ARC_SEMAPHORE_REGNUM, 0x0000000F, RW, ARCompact ) +RAUX ( LP_START, 0x2, "Loop Start", ARC_LP_START_REGNUM, 0xFFFFFFFE, RW, ARCompact ) +RAUX ( LP_END, 0x3, "Loop End", ARC_LP_END_REGNUM, 0xFFFFFFFE, RW, ARCompact ) +RAUX ( IDENTITY, 0x4, "Identity", ARC_IDENTITY_REGNUM, 0xFFFFFFFF, RO, ARCompact ) +RAUX ( DEBUG, 0x5, "Debug", ARC_DEBUG_REGNUM, 0xF0800802, RO, ARCompact ) +RAUX ( PC, 0x6, "PC", ARC_PC_REGNUM, 0xFFFFFFFE, RO, ARCompact ) +RAUX ( STATUS32, 0xA, "STATUS32", ARC_STATUS32_REGNUM, 0x00001FFF, RO, ARCompact ) +RAUX ( STATUS32_L1, 0xB, "STATUS32 register in case of L1 interrupts", ARC_STATUS32_L1_REGNUM, 0x00001FFE, RW, ARCompact ) +RAUX ( STATUS32_L2, 0xC, "STATUS32 register in case of L2 interrupts", ARC_STATUS32_L2_REGNUM, 0x00001FFE, RW, ARCompact ) +RAUX ( COUNT0, 0x21, "Processor Timer 1 Count Value", ARC_COUNT0_REGNUM, 0xFFFFFFFF, RW, ARCompact ) +RAUX ( CONTROL0, 0x22, "Processor Timer 1 Control Value", ARC_CONTROL0_REGNUM, 0x0000000F, RW, ARCompact ) +RAUX ( LIMIT0, 0x23, "Processor Timer 1 Limit Value", ARC_LIMIT0_REGNUM, 0xFFFFFFFF, RW, ARCompact ) +RAUX ( INT_VECTOR_BASE, 0x25, "Interrupt Vector Base Register", ARC_INT_VECTOR_BASE_REGNUM, 0xFFFFFC00, RW, ARCompact ) +RAUX ( AUX_MACMODE, 0x41, "Aux MAC Mode", ARC_AUX_MACMODE_REGNUM, 0xFFFFFFFF, RW, ARCompact ) +RAUX ( AUX_IRQ_LV12, 0x43, "Aux IRQ Level 2", ARC_AUX_IRQ_LV12_REGNUM, 0x00000003, RW, ARCompact ) +RAUX ( COUNT1, 0x100, "Processor Timer 1 Count Value", ARC_COUNT1_REGNUM, 0xFFFFFFFF, RW, ARCompact ) +RAUX ( CONTROL1, 0x101, "Processor Timer 1 Control Value", ARC_CONTROL1_REGNUM, 0x0000000F, RW, ARCompact ) +RAUX ( LIMIT1, 0x102, "Processor Timer 1 Limit Value", ARC_LIMIT1_REGNUM, 0xFFFFFFFF, RW, ARCompact ) +RAUX ( AUX_IRQ_LEV, 0x200, "Interrupt Level programming", ARC_AUX_IRQ_LEV_REGNUM, 0xFFFFFFF8, RW, ARCompact ) +RAUX ( AUX_IRQ_HINT, 0x201, "Software Triggered Interrupt", ARC_AUX_IRQ_HINT_REGNUM, 0x0000001F, RW, ARCompact ) +RAUX ( ERET, 0x400, "Exception Return", ARC_ERET_REGNUM, 0xFFFFFFFE, RW, ARC700 ) +RAUX ( ERBTA, 0x401, "Exception BTA", ARC_ERBTA_REGNUM, 0xFFFFFFFE, RW, ARC700 ) +RAUX ( ERSTATUS, 0x402, "Exception Return Status", ARC_ERSTATUS_REGNUM, 0x00001FFE, RW, ARC700 ) +RAUX ( ECR, 0x403, "Exception Cause Register", ARC_ECR_REGNUM, 0x00FFFFFF, RO, ARC700 ) +RAUX ( EFA, 0x404, "Exception Fault Address", ARC_EFA_REGNUM, 0xFFFFFFFF, RW, ARC700 ) +RAUX ( ICAUSE1, 0x40A, "Interrupt Cause (Level 1)", ARC_ICAUSE1_REGNUM, 0x0000001F, RO, ARC700 ) +RAUX ( ICAUSE2, 0x40B, "Interrupt Cause (Level 2)", ARC_ICAUSE2_REGNUM, 0x0000001F, RO, ARC700 ) +RAUX ( AUX_IENABLE, 0x40C, "Interrupt Mask Programming", ARC_AUX_IENABLE_REGNUM, 0xFFFFFFF8, RW, ARC700 ) +RAUX ( AUX_ITRIGGER, 0x40D, "Interrupt Sensitivity Programming", ARC_AUX_ITRIGGER_REGNUM, 0xFFFFFFF8, RW, ARC700 ) +RAUX ( XPU, 0x410, "User Mode Extension Permissions", ARC_XPU_REGNUM, 0xFFFFFFFF, RW, ARC700 ) +RAUX ( BTA, 0x412, "Branch Target Address", ARC_BTA_REGNUM, 0xFFFFFFFE, RO, ARC700 ) +RAUX ( BTA_L1, 0x413, "Branch Target Address in Level 1", ARC_BTA_L1_REGNUM, 0xFFFFFFFE, RW, ARC700 ) +RAUX ( BTA_L2, 0x414, "Branch Target Address in Level 2", ARC_BTA_L2_REGNUM, 0xFFFFFFFE, RW, ARC700 ) +RAUX ( AUX_IRQ_PULSE_CANCEL, 0x415, "Interrupt Pulse Cancel", ARC_AUX_IRQ_PULSE_CANCEL_REGNUM, 0xFFFFFFFA, WO, ARC700 ) +RAUX ( AUX_IRQ_PENDING, 0x416, "Interrupt Pending Register", ARC_AUX_IRQ_PENDING_REGNUM, 0xFFFFFFF8, RO, ARC700 ) + #endif // RAUX + +/* Build Configuration Registers. */ + #ifdef RBCR -#ifndef ARC4_JTAG - -RBCR ( DCCM_BASE_BUILD , 0x61, "Base address for DCCM.", ARC_BCR_1_REGNUM, ARCompact) -RBCR ( CRC_BASE_BUILD , 0x62, "BCRBCR for CRC Unit.", ARC_BCR_2_REGNUM , ARCompact) -RBCR ( BTA_LINK_BUILD , 0x63, "Interrupt Link Registers Available for BTA",ARC_BCR_3_REGNUM, ARCompact ) -RBCR ( DVBF_BUILD , 0x64, "BCRBCR for Dual Viterbi Instruction.",ARC_BCR_4_REGNUM, ARCompact ) -RBCR ( TEL_INSTR_BUILD , 0x65, "BCRBCR for Extended Arithmetic Instructions. ",ARC_BCR_5_REGNUM, ARCompact) -RBCR ( MEMSUBSYS , 0x67, "BCRBCR for Memory Subsystem. ",ARC_BCR_7_REGNUM, ARCompact) -RBCR ( VECBASE_AC_BUILD ,0x68, "BCRBCR for Interrupt Vector Base. ", ARC_BCR_8_REGNUM,ARCompact) -RBCR ( P_BASE_ADDRESS , 0x69, "Peripheral Base Address" , ARC_BCR_9_REGNUM , ARCompact) -RBCR ( MMU_BUILD , 0x6F, "MMU Build. " , ARC_BCR_F_REGNUM, ARCompact) -RBCR ( ARCANGEL_BUILD , 0x70, "ARC Angel Build config. ", ARC_BCR_10_REGNUM, ARCompact) -RBCR ( D_CACHE_BUILD , 0x72, "D Cache Build Config. ", ARC_BCR_12_REGNUM , ARCompact ) -RBCR ( MADI_BUILD , 0x73 , "Multiple ARC Debug Interface. " , ARC_BCR_13_REGNUM , ARCompact) -RBCR ( DCCM_BUILD , 0x74, "BCRBCR for DCCM.(Data Closely coupled Memory", ARC_BCR_14_REGNUM, ARCompact) -RBCR ( TIMER_BUILD , 0x75, "BCRBCR for Timers. " , ARC_BCR_15_REGNUM , ARCompact) -RBCR ( AP_BUILD, 0x76, "Actionpoints build. ", ARC_BCR_16_REGNUM , ARCompact ) -RBCR ( ICACHE_BUILD , 0x77, "Instruction Cache BCR", ARC_BCR_17_REGNUM , ARCompact ) -RBCR ( ICCM_BUILD , 0x78, "ICCM BCRBCR (Instruction Closely Coupled Memory.", ARC_BCR_18_REGNUM , ARCompact) -RBCR ( DSPRAM_BUILD , 0x79, "DSP RAM Build", ARC_BCR_19_REGNUM , ARCompact) -RBCR ( MAC_BUILD , 0x7A, "MAC Unit Build", ARC_BCR_1A_REGNUM , ARCompact) -RBCR ( MULTIPLY_BUILD , 0x7B, "(32 X 32) Multiply Unit Build", ARC_BCR_1B_REGNUM , ARCompact) -RBCR ( SWAP_BUILD , 0x7C, "SWAP Build", ARC_BCR_1C_REGNUM , ARCompact) -RBCR ( NORM_BUILD , 0x7D ,"NORM Unit Build", ARC_BCR_1D_REGNUM, ARCompact) -RBCR ( MINMAX_BUILD , 0x7E, "Minmax Unit Build", ARC_BCR_1E_REGNUM, ARCompact) -RBCR ( BARREL_BUILD , 0x7F, "Barrel Shifter Build", ARC_BCR_1F_REGNUM , ARCompact) - - -#endif // ARC4_JTAG -#endif // RBCR + +RBCR ( UNUSED_0, 0x60, "unused", ARC_BCR_0_REGNUM, 0xFFFFFFFF, UU, ARCompact ) +RBCR ( DCCM_BASE_BUILD, 0x61, "Base address for DCCM", ARC_BCR_1_REGNUM, 0xFFFFFFFF, RO, ARCompact ) +RBCR ( CRC_BASE_BUILD, 0x62, "BCR for CRC Unit", ARC_BCR_2_REGNUM, 0xFFFFFFFF, RO, ARCompact ) +RBCR ( BTA_LINK_BUILD, 0x63, "Interrupt Link Registers Available for BTA", ARC_BCR_3_REGNUM, 0xFFFFFFFF, RO, ARCompact ) +RBCR ( DVBF_BUILD, 0x64, "BCR for Dual Viterbi Instruction", ARC_BCR_4_REGNUM, 0xFFFFFFFF, RO, ARCompact ) +RBCR ( TEL_INSTR_BUILD, 0x65, "BCR for Extended Arithmetic Instructions", ARC_BCR_5_REGNUM, 0xFFFFFFFF, RO, ARCompact ) +RBCR ( UNUSED_6, 0x66, "unused", ARC_BCR_6_REGNUM, 0xFFFFFFFF, UU, ARCompact ) +RBCR ( MEMSUBSYS, 0x67, "BCR for Memory Subsystem", ARC_BCR_7_REGNUM, 0xFFFFFFFF, RO, ARCompact ) +RBCR ( VECBASE_AC_BUILD, 0x68, "BCR for Interrupt Vector Base", ARC_BCR_8_REGNUM, 0xFFFFFFFF, RO, ARCompact ) +RBCR ( P_BASE_ADDRESS, 0x69, "Peripheral Base Address", ARC_BCR_9_REGNUM, 0xFFFFFFFF, RO, ARCompact ) +RBCR ( UNUSED_A, 0x6A, "unused", ARC_BCR_A_REGNUM, 0xFFFFFFFF, UU, ARCompact ) +RBCR ( UNUSED_B, 0x6B, "unused", ARC_BCR_B_REGNUM, 0xFFFFFFFF, UU, ARCompact ) +RBCR ( UNUSED_C, 0x6C, "unused", ARC_BCR_C_REGNUM, 0xFFFFFFFF, UU, ARCompact ) +RBCR ( UNUSED_D, 0x6D, "unused", ARC_BCR_D_REGNUM, 0xFFFFFFFF, UU, ARCompact ) +RBCR ( UNUSED_E, 0x6E, "unused", ARC_BCR_E_REGNUM, 0xFFFFFFFF, UU, ARCompact ) +RBCR ( MMU_BUILD, 0x6F, "MMU Build", ARC_BCR_F_REGNUM, 0xFFFFFFFF, RO, ARCompact ) +RBCR ( ARCANGEL_BUILD, 0x70, "ARC Angel Build Config", ARC_BCR_10_REGNUM, 0xFFFFFFFF, RO, ARCompact ) +RBCR ( UNUSED_11, 0x6E, "unused", ARC_BCR_11_REGNUM, 0xFFFFFFFF, UU, ARCompact ) +RBCR ( D_CACHE_BUILD, 0x72, "D Cache Build Config", ARC_BCR_12_REGNUM, 0xFFFFFFFF, RO, ARCompact ) +RBCR ( MADI_BUILD, 0x73, "Multiple ARC Debug Interface", ARC_BCR_13_REGNUM, 0xFFFFFFFF, RO, ARCompact ) +RBCR ( DCCM_BUILD, 0x74, "BCR for DCCM (Data Closely Coupled Memory)", ARC_BCR_14_REGNUM, 0xFFFFFFFF, RO, ARCompact ) +RBCR ( TIMER_BUILD, 0x75, "BCR for Timers", ARC_BCR_15_REGNUM, 0xFFFFFFFF, RO, ARCompact ) +RBCR ( AP_BUILD, 0x76, "Actionpoints build", ARC_BCR_16_REGNUM, 0xFFFFFFFF, RO, ARCompact ) +RBCR ( ICACHE_BUILD, 0x77, "Instruction Cache BCR", ARC_BCR_17_REGNUM, 0xFFFFFFFF, RO, ARCompact ) +RBCR ( ICCM_BUILD, 0x78, "ICCM BCR (Instruction Closely Coupled Memory)", ARC_BCR_18_REGNUM, 0xFFFFFFFF, RO, ARCompact ) +RBCR ( DSPRAM_BUILD, 0x79, "DSP RAM Build", ARC_BCR_19_REGNUM, 0xFFFFFFFF, RO, ARCompact ) +RBCR ( MAC_BUILD, 0x7A, "MAC Unit Build", ARC_BCR_1A_REGNUM, 0xFFFFFFFF, RO, ARCompact ) +RBCR ( MULTIPLY_BUILD, 0x7B, "(32 X 32) Multiply Unit Build", ARC_BCR_1B_REGNUM, 0xFFFFFFFF, RO, ARCompact ) +RBCR ( SWAP_BUILD, 0x7C, "SWAP Build", ARC_BCR_1C_REGNUM, 0xFFFFFFFF, RO, ARCompact ) +RBCR ( NORM_BUILD, 0x7D, "NORM Unit Build", ARC_BCR_1D_REGNUM, 0xFFFFFFFF, RO, ARCompact ) +RBCR ( MINMAX_BUILD, 0x7E, "Minmax Unit Build", ARC_BCR_1E_REGNUM, 0xFFFFFFFF, RO, ARCompact ) +RBCR ( BARREL_BUILD, 0x7F, "Barrel Shifter Build", ARC_BCR_1F_REGNUM, 0xFFFFFFFF, RO, ARCompact ) + +#endif // RBCR + +/******************************************************************************/ diff --git a/gdb/arc-remote-fileio.c b/gdb/arc-remote-fileio.c new file mode 100644 index 00000000000..5bb22eafe31 --- /dev/null +++ b/gdb/arc-remote-fileio.c @@ -0,0 +1,741 @@ +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. + + Copyright 2008, 2009 Free Software Foundation, Inc. + + Contributed by ARC International (www.arc.com) + + Author: + Richard Stuckey <richard.stuckey@arc.com> + + 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 3 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, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This module implements facilities for intercepting I/O (and other) */ +/* operations attempted on an ARC target and performing them on the host, */ +/* using a RPC (Remote Procedure Call) mechanism. */ +/* */ +/* Mechanism: */ +/* When interception is enabled, this module sets a breakpoint at the */ +/* entry point of each operation which is to be intercepted; it finds the */ +/* entry points of named routines in the C runtime library code by using */ +/* gdb's symbol lookup facilities. */ +/* */ +/* When a breakpoint is triggered on the target, the target monitoring */ +/* loop calls the function 'arc_check_interception_breakpoint' to check */ +/* whether the breakpoint triggered is an interception breakpoint; this */ +/* function will return a code indicating either */ +/* */ +/* a) the breakpoint is an interception breakpoint, the interception */ +/* has been performed and execution of the target program should be */ +/* resumed; or */ +/* */ +/* b) the breakpoint is an interception breakpoint, but the intercepted */ +/* routine is 'exit' and execution should not be resumed; or */ +/* */ +/* c) the breakpoint is not an interception breakpoint, so execution */ +/* should not be resumed and the trigger should be reported to gdb. */ +/* */ +/* In case a), this module then reads the routine's parameters from the */ +/* target's registers, performs whatever conversions are required, and */ +/* constructs a gdb RSP File I/O extension 'F' message which it passes to */ +/* the gdb target_fileio module, which performs the requested operation */ +/* on the host machine. */ +/* */ +/* The target_fileio module is passed a set of operations which allow it */ +/* to read data from target memory, write data to target memory, and */ +/* return a result value (and possibly a error code) to the intercepted */ +/* routine. The result value is written into the target's R0 register; */ +/* the error code (if any) is written into the location of the 'errno' */ +/* variable. */ +/* */ +/* Finally, this module copies the routine return address from the BLINK */ +/* register to the PC register - this ensures that when execution of the */ +/* target is resumed, control returns to the code after the call to the */ +/* intercepted routine. */ +/* */ +/* Notes: */ +/* 1) the set of routines to be intercepted, and the parameters to these */ +/* routines, is defined by a table (see below) - so it is simple to */ +/* add more routines to the set; */ +/* */ +/* 2) the 'open' syscall (see man open(2)) has a 'flags' parameter which */ +/* is a bit mask; unfortunately, the bits differ in meaning between */ +/* the host and the elf-32 target program, so the parameter must be */ +/* converted before it can be passed to the target_fileio module; */ +/* */ +/* 3) the 'fstat' syscall (see man fstat(2)) has an out parameter which */ +/* is a 'struct stat' structure (i.e. the address of such a structure */ +/* is a parameter to the syscall): the target_fileio module writes the */ +/* data of the structure to that location in target memory; however, */ +/* the structure does not have the same layout on host and target, so */ +/* the structure must be converted before it can be written to the */ +/* target; */ +/* */ +/* 4) the interception breakpoints are not handled by the core gdb */ +/* breakpoint mechanism; hence, they are not listed by the 'info break'*/ +/* command, and can not be (accidentally) deleted by the user; though */ +/* they could be handled by gdb, that would require the introduction */ +/* of a new "invisible" breakpoint type, and hence more changes to */ +/* supposedly generic code; */ +/* */ +/* 5) it would be more elegant (from one perspective) to intecept these */ +/* operations by placing a breakpoint at the interrupt vector location */ +/* of the 'swi' (SoftWare Interrupt) handler: only one breakpoint */ +/* would then be required, and all syscalls would be intercepted; */ +/* however, this module would then have to simulate a "return from */ +/* exception" in order to resume target execution, which would be more */ +/* complex than the "restart at return address" method currently used. */ +/* */ +/* Restrictions: */ +/* 1) this module places s/w breakpoints at the entry points; therefore, */ +/* the mechanism will not work with programs that are loaded into read */ +/* -only memory; */ +/* */ +/* 2) this mechanism will probably not work if the user sets breakpoints */ +/* on the entry points of the intercepted routines - there will be a */ +/* conflict! */ +/* */ +/******************************************************************************/ + +/* system header files */ +#include <stdio.h> +#include <string.h> +#include <signal.h> + +/* gdb header files */ +#include "defs.h" +#include "symtab.h" +#include "frame.h" +#include "block.h" +#include "target.h" +#include "target-fileio.h" +#include "exceptions.h" +#include "gdb/fileio.h" + +/* ARC header files */ +#include "arc-remote-fileio.h" +#include "config/arc/tm-embed.h" + + +/* -------------------------------------------------------------------------- */ +/* local types */ +/* -------------------------------------------------------------------------- */ + +#define MAX_SYSCALL_PARAMS 4 + +/* These are the intercepted routines. */ +typedef enum +{ + READ_CALL, + WRITE_CALL, + OPEN_CALL, + CLOSE_CALL, + LSEEK_CALL, + FSTAT_CALL, + GETTIMEOFDAY_CALL, + EXIT_CALL +} SystemCall; + + +struct lib_function +{ + SystemCall call; + const char *name; + const char *format; + Boolean bp_is_set; + unsigned int param_count; + ARC_RegisterNumber param_register[MAX_SYSCALL_PARAMS]; + struct bp_target_info breakpoint; +}; + + +/* This structure defines a memory image of the 'stat' structure as it is + represented on the ARC target. */ +struct arc_stat +{ + ARC_Byte fst_dev [4]; + ARC_Byte fst_ino [4]; + ARC_Byte fst_mode [2]; + ARC_Byte fst_nlink [2]; + ARC_Byte fst_uid [2]; + ARC_Byte fst_gid [2]; + ARC_Byte fst_rdev [4]; + ARC_Byte fst_size [4]; + ARC_Byte fst_blksize[4]; + ARC_Byte fst_blocks [4]; + ARC_Byte fst_atime [8]; + ARC_Byte fst_mtime [8]; + ARC_Byte fst_ctime [8]; +}; + + +/* -------------------------------------------------------------------------- */ +/* local data */ +/* -------------------------------------------------------------------------- */ + +/* The %x specifiers in the format strings in this table correspond to the + parameters to the intercepted functions; the register number of the target + register in which the parameter is passed is given by the corresponding + entry in the param_register array. + + N.B. the special value of SL as the number of the register in which a + parameter is passed indicates that the preceding parameter was the + address of a string, and the length of that string (including the + terminating NUL) is required here. + + F<n> indicates that the parameter in register <n> is a word of flag + bits which must be handled specially! + + X indicates that the array element is not used. */ + +#define SL 1000 +#define F2 1002 +#define X 9999 + +static struct lib_function functions[] = +{ + { READ_CALL, "_read_r", "Fread,%x,%x,%x", FALSE, 3, {1, 2, 3, X} }, + { WRITE_CALL, "_write_r", "Fwrite,%x,%x,%x", FALSE, 3, {1, 2, 3, X} }, + { OPEN_CALL, "_open_r", "Fopen,%x/%x,%x,%x", FALSE, 4, {1, SL, F2, 3} }, + { CLOSE_CALL, "_close_r", "Fclose,%x", FALSE, 1, {1, X, X, X} }, + { LSEEK_CALL, "_lseek_r", "Flseek,%x,%x,%x", FALSE, 3, {1, 2, 3, X} }, + { FSTAT_CALL, "_fstat_r", "Ffstat,%x,%x", FALSE, 2, {1, 2, X, X} }, + { GETTIMEOFDAY_CALL, "_gettimeofday_r", "Fgettimeofday,%x,%x", FALSE, 2, {1, 2, X, X} }, + { EXIT_CALL, "_exit_r", NULL, FALSE, 1, {1, X, X, X} } +}; + + +/* A pointer to the set of target operations for the current target. */ +static TargetOperations *target_operations; + +/* TRUE if the operation currently being intercepted has NOT been interrupted + by the user typing a Ctrl-C. */ +static Boolean not_interrupted; + +/* For the Ctrl-C signal handler. */ +static void (*old_signal_handler) (int); + + +/* -------------------------------------------------------------------------- */ +/* local functions */ +/* -------------------------------------------------------------------------- */ + +/* Read a number of bytes of data from the given address in target memory. */ + +static int +read_bytes (CORE_ADDR memaddr, gdb_byte *myaddr, int len) +{ + DEBUG("reading %d bytes from %x\n", len, (unsigned int) memaddr); + + return (int) target_read (¤t_target, TARGET_OBJECT_MEMORY, NULL, myaddr, memaddr, len); +} + + +/* Read a number of bytes of data from the given address in target memory. */ + +static int +write_bytes (CORE_ADDR memaddr, gdb_byte *myaddr, int len) +{ + DEBUG("writing %d bytes to %x\n", len, (unsigned int) memaddr); + + return (int) target_write (¤t_target, TARGET_OBJECT_MEMORY, NULL, myaddr, memaddr, len); +} + + +/* Perform the reply to the intercepted operation: set up the result of the call + and the error code (if any) so that the intercepted operation receives them + just as though the operation had really been performed upon the target. */ + +static void +reply (int retcode, int error) +{ + /* Ignore any Ctrl-Cs while performing the reply. */ + (void) signal (SIGINT, SIG_IGN); + + DEBUG("reply: retcode = %d, error = %d\n", retcode, error); + + /* If an error has occurred. */ + if (retcode == -1) + { + ARC_RegisterContents errno_address; + + if (error == FILEIO_EINTR) + { + DEBUG("*** interrupted by user!\n"); + /* Set the global flag so that it can be tested later. */ + not_interrupted = FALSE; + return; + } + + /* Read the address of the 'errno' variable from R0. */ + if (target_operations->read_core_register(0, &errno_address, TRUE)) + /* Write the error number into the 'errno' variable. */ + (void) write_bytes((CORE_ADDR) errno_address, (gdb_byte*) &error, BYTES_IN_WORD); + } + + /* Write the return code into the function result register R0. */ + (void) target_operations->write_core_register(0, (ARC_RegisterContents) retcode, TRUE); +} + + +/* Copy a number of bytes of data from one buffer to another. Note that the + buffers are not necessarily of the same size. Perform endianess byte-swapping + if necessary. */ + +static void +copy_bytes (char *from, size_t from_size, + ARC_Byte *to, size_t to_size, + Boolean target_is_big_endian) +{ + /* We can not copy more data than we have been given in the source buffer, + or for which there is room in the destination buffer. */ + unsigned int bytes = (unsigned int) ((from_size > to_size) ? to_size : from_size); + unsigned int i; + + /* N.B. 1) the fio_stat structure created by target-fileio.c has the values + in big-endian byte order; so if the ARC target is little-endian + we must reverse the order; + + 2) the fields in the fio_stat structure may be smaller (or larger!) + than the corresponding fields in the ARC target structure - so we + copy the *least* significant bytes of the fields, on the grounds + that the most significant bytes are probably just sign-extensions! */ + for (i = 0; i < bytes; i++) + to[i] = (ARC_Byte) ((target_is_big_endian) ? from[i] + : from[from_size - 1 - i]); +} + + +/* Write a 'stat' structure to the target at a given address in memory. + 'myaddr' points to a fio_stat structure created by the target-fileio module; + this structure is meant for use in the RSP protocol, and is designed for + independence of host/target systems - therefore, we must create an equivalent + structure which is ARC-specific, and write that structure to the target. + + Return the number of bytes of data written. */ + +static int +write_fstat (CORE_ADDR memaddr, gdb_byte *myaddr, int len) +{ + Boolean target_is_big_endian = (gdbarch_byte_order (current_gdbarch) == BFD_ENDIAN_BIG); + struct fio_stat *fst = (struct fio_stat*) myaddr; + struct arc_stat ast; + + memset(&ast, 0, sizeof(ast)); + +#define COPY(from, to) copy_bytes(from, sizeof(from), to, sizeof(to), target_is_big_endian) + + COPY(fst->fst_dev, ast.fst_dev); + COPY(fst->fst_ino, ast.fst_ino); + COPY(fst->fst_mode, ast.fst_mode); + COPY(fst->fst_nlink, ast.fst_nlink); + COPY(fst->fst_uid, ast.fst_uid); + COPY(fst->fst_gid, ast.fst_gid); + COPY(fst->fst_rdev, ast.fst_rdev); + COPY(fst->fst_size, ast.fst_size); + COPY(fst->fst_blksize, ast.fst_blksize); + COPY(fst->fst_blocks, ast.fst_blocks); + COPY(fst->fst_atime, ast.fst_atime); + COPY(fst->fst_mtime, ast.fst_mtime); + COPY(fst->fst_ctime, ast.fst_ctime); + + return write_bytes(memaddr, (gdb_byte*) &ast, (int) sizeof(ast)); +} + + +/* Find the address of the entry point of the given routine in the target program. + Return 0 if the entry point can not be found, or is not a function. */ + +static CORE_ADDR +findEntryPoint (const char *routine) +{ + CORE_ADDR entry = 0; + int is_a_field_of_this; + struct symbol *sym = lookup_symbol (routine, + get_selected_block (0), + VAR_DOMAIN, + &is_a_field_of_this, + (struct symtab **) NULL); + if (sym) + { + if (SYMBOL_CLASS (sym) == LOC_BLOCK) + entry = BLOCK_START (SYMBOL_BLOCK_VALUE (sym)); + else + warning(_("%s is not a function"), routine); + } + else + warning(_("can not find entry point of function %s"), routine); + + return entry; +} + + +/* Insert a s/w breakpoint in the target program code at the entry point of the + given library function. */ + +static void +insert_breakpoint (struct lib_function *f) +{ + if (!f->bp_is_set) + { + if (arc_elf32_insert_breakpoint(&f->breakpoint) == 0) + f->bp_is_set = TRUE; + else + warning(_("can not set breakpoint at entrypoint of %s"), f->name); + } +} + + +/* Remove a s/w breakpoint from the target program code at the entry point of the + given library function. */ + +static void +remove_breakpoint (struct lib_function *f) +{ + if (f->bp_is_set) + { + if (arc_elf32_remove_breakpoint(&f->breakpoint) == 0) + f->bp_is_set = FALSE; + else + warning(_("can not unset breakpoint at entrypoint of %s"), f->name); + } +} + + +/* This function handles any Ctrl-C typed by the user whilst the interception of + an operation is in progress. */ + +static void +Ctrl_C_signal_handler (int signo) +{ + /* Ignore any more Ctrl-Cs. */ + (void) signal (SIGINT, SIG_IGN); + + /* We must use the gdb exception mechanism since the target_fileio_request + function calls catch_exceptions, and if we do something else (like a long + jump) here, gdb's cleanup list would be left in an inconsistent state! */ + DEBUG("*** throwing RETURN_QUIT...\n"); + deprecated_throw_reason (RETURN_QUIT); +} + + +/* This function is called from the gdb target-fileio module: it sets up this + module's handler for Ctrl-C interrupts. */ + +static void +set_Ctrl_C_signal_handler (void) +{ + old_signal_handler = signal (SIGINT, Ctrl_C_signal_handler); +} + + +/* This function finds the length of a C string stored in target memory at the + given address. */ + +static unsigned int +find_string_length (ARC_Address address) +{ + unsigned int length = 0; + + while (TRUE) + { + gdb_byte buf[65]; + int bytes = read_bytes((CORE_ADDR) address, + buf, + (int) sizeof(buf) - 1); + int i; + + for (i = 0; i < bytes; i++) + if (buf[i] == (gdb_byte) '\0') + return (unsigned int) (length + i + 1); + + address += bytes; + length += bytes; + } +} + + +/* Convert flags to target syscall to what they "should" be! */ + +static ARC_RegisterContents +convert_flags (ARC_RegisterContents flags) +{ + ARC_RegisterContents result = flags; + +/* See gcc/src/newlib/libc/sys/arc/sys/fcntl.h */ + +/* The following values have been changed for uclibc compatibility. */ +#define _FAPPEND 0x0400 /* append (writes guaranteed at the end) */ +#define _FASYNC 0x2000 /* signal pgrp when data ready */ +#define _FCREAT 0x0040 /* open with file create */ +#define _FTRUNC 0x0200 /* open with truncation */ +#define _FEXCL 0x0080 /* error on open if file exists */ +#define _FSYNC 0x1000 /* do all writes synchronously */ +#define _FNONBLOCK 0x0800 /* non blocking I/O (POSIX style) */ + +#define REMOVE(flag) if (flags & _F ## flag) result &= ~ _F ## flag +#define ADD(flag) if (flags & _F ## flag) result |= FILEIO_O_ ## flag + + /* N.B. all "old" bits most be removed from the result word before all + "new" bits are added, in case the old and new sets intersect! */ + REMOVE(APPEND); +// REMOVE(ASYNC); // no equivalent flag in gdb/fileio.h + REMOVE(CREAT); + REMOVE(TRUNC); + REMOVE(EXCL); +// REMOVE(SYNC); // no equivalent flag in gdb/fileio.h +// REMOVE(NONBLOCK); // no equivalent flag in gdb/fileio.h + ADD(APPEND); +// ADD(ASYNC); // no equivalent flag in gdb/fileio.h + ADD(CREAT); + ADD(TRUNC); + ADD(EXCL); +// ADD(SYNC); // no equivalent flag in gdb/fileio.h +// ADD(NONBLOCK); // no equivalent flag in gdb/fileio.h + + return result; +} + + +/* Perform the interception of the given library function. + Return TRUE if the interception is completed successfully, + FALSE if it is interrupted by the user. */ + +static Boolean +perform_interception (struct lib_function *f) +{ + ARC_RegisterContents params [MAX_SYSCALL_PARAMS]; + char request[MAX_SYSCALL_PARAMS * 9 + 40]; + unsigned int i; + + /* These operations allow the target_fileio module to read data from target + memory, write data to target memory, and return a result value (and + possibly a error code) to the intercepted routine. + + N.B. if the syscsall is 'fstat', we pass a special write function + which converts the 'struct stat' structure to target layout before + writing it to target memory. */ + struct file_io_operations io_operations = + { + read_bytes, + (f->call == FSTAT_CALL) ? write_fstat : write_bytes, + reply, + set_Ctrl_C_signal_handler + }; + + /* Evaluate the parameters to be passed to the RPC request. */ + for (i = 0; i < f->param_count; i++) + { + ARC_RegisterNumber reg = f->param_register[i]; + + if (reg == SL) + params[i] = find_string_length((ARC_Address) params[i - 1]); + else if (reg == F2) + { + ARC_RegisterContents flags; + + (void) target_operations->read_core_register(2, &flags, TRUE); + params[i] = convert_flags(flags); + } + else + (void) target_operations->read_core_register(reg, ¶ms[i], TRUE); + } + + /* Do not close the target program's stdin, stdout or stderr streams on the + host: it is possible that the program may be re-loaded and re-run on the + target in the same debugging session (so re-initializing its I/O system) + so it may try to read/write those streams again - instead, just tell the + target that the close succeeded. */ + if (f->call == CLOSE_CALL) + { + int fd = (int) params[0]; + + if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO) + { + reply(0, 0); + DEBUG("*** RPC close of stream %d ignored\n", fd); + return TRUE; + } + } + + /* Parameters which are extra to those required by the format will simply be + ignored. */ + (void) snprintf(request, sizeof(request), f->format, + params[0], params[1], params[2], params[3]); + + DEBUG("RPC request: %s\n", request); + + /* the interception might be interrupted by the user typing Ctrl-C whilst + the interception is in progress; if that happens, this flag will be set + to FALSE. */ + not_interrupted = TRUE; + + /* Make the RPC request. */ + target_fileio_request(request, &io_operations); + + (void) signal (SIGINT, old_signal_handler); + + /* If the call was not interrupted, the interception has been performed. */ + return not_interrupted; +} + + +/* -------------------------------------------------------------------------- */ +/* externally visible functions */ +/* -------------------------------------------------------------------------- */ + +/* Set the state of the I/O interception mechanism: + ON : set breakpoints on all the functions to be intercepted + OFF : clear breakpoints from all the intercepted functions + RESET: mark the breakpoints as not being set (if a new program has been + downloaded to the target, the s/w breakpoints in the old program + have been lost, and so should not be removed). */ + +void +arc_set_IO_interception (TargetOperations *operations, + InterceptionState state) +{ + unsigned int i; + + DEBUG("*** interception: %s\n", (state == INTERCEPTION_RESET) ? "RESET" : + (state == INTERCEPTION_ON) ? "ON" : + "OFF"); + + target_operations = operations; + + for (i = 0; i < ELEMENTS_IN_ARRAY(functions); i++) + { + struct lib_function* f = &functions[i]; + + switch (state) + { + case INTERCEPTION_RESET: + f->bp_is_set = FALSE; + break; + + case INTERCEPTION_ON: + /* Set a breakpoint on the entry point of the function. */ + f->breakpoint.placed_address = findEntryPoint(f->name); + + if (f->breakpoint.placed_address != 0) + { + DEBUG("intercept 0x%08X : %s\n", (unsigned int) f->breakpoint.placed_address, f->name); + insert_breakpoint(f); + } + break; + + case INTERCEPTION_OFF: + if (f->breakpoint.placed_address != 0) + { + remove_breakpoint(f); + f->breakpoint.placed_address = 0; + } + break; + } + } +} + + +/* This function is called when the execution of the target program has been + halted by a breakpoint trigger. It checks whether the breakpoint that has + been triggered is at the entry point of an intercepted function, and, if so, + performs the required interception. + + Returns: + INTERCEPTION_RESUME : interception has been performed, execution should be resumed + INTERCEPTION_HALT : the program is halted (no interception has been performed) + INTERCEPTION_EXIT : the program has exited + INTERCEPTION_INTERRUPT : the interception has been interrupted by the user + + If the program has exited, the 'exitcode' parameter is set to the program's exit code. */ + +InterceptionAction +arc_check_interception_breakpoint (int *exitcode) +{ + ARC_RegisterContents pc; + + ENTERMSG; + + *exitcode = 0; + + /* Get the current execution point from the PCL, rather than the PC - this + gives the same result on both ARC700 and ARC600 targets. */ + if (target_operations->read_core_register(ARC_PCL_REGNUM, &pc, TRUE)) + { + unsigned int i; + + DEBUG("checking for interception at 0x%08X\n", pc); + + /* Look at each of the intercepted operations. */ + for (i = 0; i < ELEMENTS_IN_ARRAY(functions); i++) + { + struct lib_function *f = &functions[i]; + + if (f->breakpoint.placed_address == (CORE_ADDR) pc) + { + DEBUG("intercepted function %s\n", f->name); + + if (f->call == EXIT_CALL) + { + ARC_RegisterContents code; + + /* The exit code is in parameter register R1. */ + if (target_operations->read_core_register(1, &code, TRUE)) + *exitcode = (int) code; + + return INTERCEPTION_EXIT; + } + else + { + /* If the interception is performed. */ + if (perform_interception(f)) + { + ARC_RegisterContents blink; + + /* Copy BLINK to PC, so that when execution is re-started, + control will return to the point after the call of the + intercepted function. */ + if (target_operations->read_core_register (ARC_BLINK_REGNUM, &blink, TRUE) && + target_operations->write_auxiliary_register(arc_pc_regnum, blink, TRUE)) + { + DEBUG("copied BLINK (%x) to PC (was %x)\n", blink, pc); + return INTERCEPTION_RESUME; + } + + /* If we couldn't set PC, fall through. */ + } + else + { + /* The interception has been interrupted by a Ctrl-C + from the user - do not change the PC, as we want + execution to resume at the same point in the code, + so that the I/O request will be performed (and + intercepted) again: this e.g. allows the user to + break into a program that is in a tight loop doing + reads or writes. */ + return INTERCEPTION_INTERRUPT; + } + } + } + } + } + + return INTERCEPTION_HALT; +} + +/******************************************************************************/ diff --git a/gdb/arc-remote-fileio.h b/gdb/arc-remote-fileio.h new file mode 100644 index 00000000000..7a2d76c5c02 --- /dev/null +++ b/gdb/arc-remote-fileio.h @@ -0,0 +1,64 @@ +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. + + Copyright 2008, 2009 Free Software Foundation, Inc. + + Contributed by ARC International (www.arc.com) + + Author: + Richard Stuckey <richard.stuckey@arc.com> + + 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 3 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, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This header file defines facilities for intercepting I/O (and other) */ +/* operations attempted on an ARC target and performing them on the host, */ +/* using a RPC (Remote Procedure Call) mechanism. */ +/* */ +/******************************************************************************/ + +#ifndef ARC_REMOTE_FILEIO +#define ARC_REMOTE_FILEIO + +/* ARC header files */ +#include "arc-support.h" + + +typedef enum +{ + INTERCEPTION_RESUME, + INTERCEPTION_HALT, + INTERCEPTION_EXIT, + INTERCEPTION_INTERRUPT +} InterceptionAction; + + +typedef enum +{ + INTERCEPTION_RESET, + INTERCEPTION_ON, + INTERCEPTION_OFF +} InterceptionState; + + +void arc_set_IO_interception (TargetOperations *operations, + InterceptionState state); + +InterceptionAction arc_check_interception_breakpoint (int *exitcode); + +#endif /* ARC_REMOTE_FILEIO */ +/******************************************************************************/ diff --git a/gdb/arc-support.h b/gdb/arc-support.h new file mode 100644 index 00000000000..76ff0bfea36 --- /dev/null +++ b/gdb/arc-support.h @@ -0,0 +1,136 @@ +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. + + Copyright 2008, 2009 Free Software Foundation, Inc. + + Contributed by ARC International (www.arc.com) + + Authors: + Richard Stuckey <richard.stuckey@arc.com> + + 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 3 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, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This header file defines some useful types and constants, and macros */ +/* for use in debugging. */ +/* */ +/******************************************************************************/ + +#ifndef ARC_SUPPORT_H +#define ARC_SUPPORT_H + + +#define ARC_DEBUG 1 + +#ifdef ARC_DEBUG +#define DEBUG(...) if (arc_debug_target) fprintf_unfiltered(gdb_stdlog, __VA_ARGS__) +#define ENTERMSG DEBUG("--- entered %s:%s()\n", __FILE__, __FUNCTION__) +#define ENTERARGS(fmt, args...) DEBUG("--- entered %s:%s(" fmt ")\n", __FILE__, __FUNCTION__, args) +#define LEAVEMSG DEBUG("--- exited %s:%s()\n", __FILE__, __FUNCTION__) +#else +#define DEBUG(...) +#define ENTERMSG +#define ENTERARGS(fmt, args...) +#define LEAVEMSG +#endif + + +/* N.B. it must be possible to build some ARC modules without the rest of gdb so + that they can be exercised by stand-alone test drivers. */ +#ifdef STANDALONE_TEST +#define error(...) { printf(__VA_ARGS__); printf("\n"); } +#define warning(...) { printf(__VA_ARGS__); printf("\n"); } +#define printf_filtered(...) printf(__VA_ARGS__) +#define printf_unfiltered(...) printf(__VA_ARGS__) +#define fprintf_unfiltered(STR, ...) printf(__VA_ARGS__) +#define internal_error(...) abort() +#endif + + +/* Useful Boolean type and constants. */ +typedef int Boolean; + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif + + +/* Useful types for machine-related quantities. + + N.B. the type ARC_RegisterNumber does not denote gdb register numbers; + instead, it denotes ARC processor hardware numbers, which are not + the same. */ + +typedef unsigned int ARC_RegisterNumber; +typedef unsigned int ARC_RegisterContents; +typedef unsigned int ARC_Address; +typedef unsigned long long int ARC_Doubleword; +typedef unsigned int ARC_Word; +typedef unsigned short int ARC_Halfword; +typedef unsigned char ARC_Byte; + + +/* Types for machine-access functions. */ +typedef Boolean (*ReadRegisterFunction)(ARC_RegisterNumber hw_regno, + ARC_RegisterContents *contents, + Boolean warn_on_failure); + +typedef Boolean (*WriteRegisterFunction)(ARC_RegisterNumber hw_regno, + ARC_RegisterContents contents, + Boolean warn_on_failure); + + +typedef unsigned int (*MemoryTransferFunction)(ARC_Address address, + ARC_Byte *data, /* May be not word-aligned. */ + unsigned int amount); + +typedef unsigned int (*MemoryFillFunction) (ARC_Address address, + ARC_Word pattern, + unsigned int amount); + +typedef struct +{ + ReadRegisterFunction read_core_register; + WriteRegisterFunction write_core_register; + ReadRegisterFunction read_auxiliary_register; + WriteRegisterFunction write_auxiliary_register; + MemoryTransferFunction read_memory; + MemoryTransferFunction write_memory; + MemoryFillFunction fill_memory; +} TargetOperations; + + +/* Sizes of machine quantities. */ +#define BYTES_IN_WORD 4 +#define BYTES_IN_REGISTER 4 +#define BITS_IN_BYTE 8 +#define BITS_IN_WORD 32 +#define BITS_IN_ADDRESS 32 +#define BITS_IN_REGISTER 32 + + +/* Useful macros. */ +#define ELEMENTS_IN_ARRAY(arr) (unsigned int) (sizeof(arr) / sizeof(arr[0])) + +#define IS_WORD_ALIGNED(addr) ((addr) % BYTES_IN_WORD == 0) + + +#endif /* ARC_SUPPORT_H */ +/******************************************************************************/ diff --git a/gdb/arc-tdep.c b/gdb/arc-tdep.c index 8b542579ad3..f912f896dec 100644 --- a/gdb/arc-tdep.c +++ b/gdb/arc-tdep.c @@ -1,1999 +1,2591 @@ -/* Target dependent code for ARC700, for GDB, the GNU debugger. +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. - Copyright 2005 Free Software Foundation, Inc. + Copyright 2005, 2008, 2009 Free Software Foundation, Inc. Contributed by Codito Technologies Pvt. Ltd. (www.codito.com) - Authors: - Soam Vasani <soam.vasani@codito.com> - Ramana Radhakrishnan <ramana.radhakrishnan@codito.com> + Authors: + Soam Vasani <soam.vasani@codito.com> + Ramana Radhakrishnan <ramana.radhakrishnan@codito.com> + Richard Stuckey <richard.stuckey@arc.com> 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 + the Free Software Foundation; either version 3 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. -*/ + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This module provides support for the ARC processor family's target */ +/* dependencies. In particular, it has knowledge of the processor ABI. */ +/* */ +/* See */ +/* ARCompact Instruction Set Architecture */ +/* Programmer's Reference */ +/* (5115-018) */ +/* */ +/* for a description of ARC processor architecture, and */ +/* */ +/* System V ABI Supplement */ +/* 4093-004 */ +/* */ +/* for a complete definition of the ABI. */ +/* */ +/* */ +/* Stack Frame Layout: */ +/* This shows the layout of the stack frame for the general case of a */ +/* function call; a given function might not have a variable number of */ +/* arguments or local variables, or might not save any registers, so it */ +/* would not have the corresponding frame areas. Additionally, a leaf */ +/* function (i.e. one which calls no other functions) does not need to */ +/* save the contents of the BLINK register (which holds its return */ +/* address), and a function might not have a frame pointer. */ +/* */ +/* N.B. the stack grows downward, so SP points below FP in memory; SP */ +/* always points to the last used word on the stack, not the first */ +/* one. */ +/* */ +/* | | | */ +/* | arg word N | | caller's */ +/* | : | | frame */ +/* | arg word 10 | | */ +/* | arg word 9 | | */ +/* old SP ---> |-----------------------| -- */ +/* | var arg word 8 | | */ +/* | : | | */ +/* | var arg word P+1 | | */ +/* |-----------------------| | */ +/* | | | */ +/* | callee-saved | | */ +/* | registers | | */ +/* | | | */ +/* |-----------------------| | */ +/* | saved blink (*) | | */ +/* |-----------------------| | callee's */ +/* | saved FP | | frame */ +/* FP ---> |-----------------------| | */ +/* | | | */ +/* | local | | */ +/* | variables | | */ +/* | | | */ +/* | register | | */ +/* | spill area | | */ +/* | | | */ +/* | outgoing args | | */ +/* | | | */ +/* SP ---> |-----------------------| -- */ +/* | | */ +/* | unused | */ +/* | | */ +/* | */ +/* | */ +/* V */ +/* downwards */ +/* */ +/* The list of arguments to be passed to a function is considered to be a */ +/* sequence of N words (as though all the parameters were stored in order */ +/* in memory with each parameter occupying an integral number of words). */ +/* Words 1 .. 8 are passed in registers 0 .. 7; if the function has more */ +/* than 8 words of arguments then words 9 .. N are passed on the stack in */ +/* the caller's frame. */ +/* */ +/* If the function has a variable number of arguments, e.g. it has a form */ +/* such as */ +/* function(p1, p2, ...); */ +/* */ +/* and P words are required to hold the values of the named parameters */ +/* (which are passed in registers 0 .. P-1), then the remaining 8 - P */ +/* words passed in registers P .. 7 are spilled into the top of the frame */ +/* so that the anonymous parameter words occupy a continous region. */ +/* */ +/* (*) if saved. */ +/* */ +/* Usage: */ +/* The module exports a function _initialize_arc_tdep: the call to this */ +/* function is generated by the gdb build mechanism, so this function */ +/* should not be explicitly called. */ +/* */ +/* The operations provided by this module are registered with gdb during */ +/* initialization; gdb then calls them via function pointers, rather than */ +/* by name (this allows gdb to handle multiple target architectures): */ +/* */ +/* set_gdbarch_XXX (gdbarch, <function>); */ +/* */ +/* */ +/* Build Configuration: */ +/* The ARC gdb may be built in two different configurations, according to */ +/* the nature of the target that it is to debug: */ +/* */ +/* 1) arc-elf32: */ +/* for debugging 'bare-metal' builds of user code (i.e. built with */ +/* newlib) */ +/* */ +/* ARC-specific modules: */ +/* arc-tdep */ +/* arc-elf32-tdep */ +/* arc-xiss */ +/* arc-jtag */ +/* arc-jtag-ops */ +/* arc-jtag-actionpoints */ +/* arc-gpio */ +/* arc-remote-fileio */ +/* arc-registers */ +/* arc-architecture */ +/* arc-board */ +/* arc-arguments */ +/* arc-memory */ +/* arc-inst-tracing */ +/* */ +/* 2) arc-linux-uclibc: */ +/* for deugging user mode Linux applications, via communication to */ +/* the remote gdbserver process, running on Linux for ARC700 */ +/* */ +/* ARC-specific modules: */ +/* arc-tdep */ +/* arc-linux-tdep */ +/* */ +/* This module provides operations which are common to both; operations */ +/* which are specific to one, or which have different variants in each */ +/* configuration, are provided by the other modules. */ +/* */ +/* */ +/* Debug Targets: */ +/* The ARC gdb supports a number of debug targets. These are: */ +/* */ +/* arc-elf32-gdb */ +/* built-in simulator 'target sim' */ +/* ARCangel 4 h/w emulator 'target arcjtag' */ +/* dynamically loaded xISS simulator 'target arcxiss' */ +/* separately executing xISS simulator 'target remote' */ +/* */ +/* arc-linux-uclibc-gdb */ +/* gdbserver running on ARC Linux 'target remote' */ +/* */ +/* It should, in theory, be possible for either debugger to connect to */ +/* any remote target which supports the gdb Remote Serial Protocol. */ +/* However, any such target MUST agree with the debugger on the register */ +/* numbering scheme that is used, as this controls the order of register */ +/* contents held in the RSP 'G' (set all registers) packet and the 'g' */ +/* (get all registers) response packet, as well as the register numbers */ +/* used in the 'P' (set one register) and 'p' (get one register) packets, */ +/* and in the 'T' stop reply packet. The schemes used by each debugger */ +/* are defined in the arc-elf32-tdep and arc-linux-tdep modules. */ +/* */ +/******************************************************************************/ + +/* system header files */ #include <string.h> +#include <stdio.h> +#include <ctype.h> +#include <byteswap.h> +/* gdb header files */ #include "defs.h" +#include "config.h" #include "arch-utils.h" #include "dis-asm.h" -#include "gdbtypes.h" #include "frame.h" #include "frame-unwind.h" -#include "target.h" -#include "breakpoint.h" #include "inferior.h" #include "regcache.h" #include "reggroups.h" #include "trad-frame.h" #include "dwarf2-frame.h" -#include "gdbtypes.h" -#include "gdb_assert.h" #include "gdbcore.h" #include "observer.h" #include "osabi.h" +#include "gdbcmd.h" +#include "block.h" +#include "dictionary.h" +#include "language.h" +#include "demangle.h" +#include "objfiles.h" +#include "gdb_assert.h" -#include "opcode/arc.h" - -#include "arc-tdep.h" - +/* ARC header files */ -//#define ARC_DEBUG 1 +/* N.B. one and only one of ARC_ELF32_TARGET and ARC_LINUX_TARGET must be defined! */ -#if ARC_DEBUG -# define ENTERMSG printf ("--- entered %s:%s()\n", __FILE__, __FUNCTION__) -# define ENTERARGS(fmt, args...) printf ("--- entered %s:%s(" fmt ")\n", __FILE__, __FUNCTION__, args) -# define LEAVEMSG printf ("--- exited %s:%s()\n", __FILE__, __FUNCTION__) +#ifdef ARC_ELF32_TARGET +#ifdef ARC_LINUX_TARGET +#error ARC build is not correctly configured (both flags set) #else -# define ENTERMSG -# define ENTERARGS(fmt, args...) -# define LEAVEMSG +#include "config/arc/tm-embed.h" +#endif +#else +#ifdef ARC_LINUX_TARGET +#include "config/arc/tm-linux.h" +#else +#error ARC build is not correctly configured (no flag set) +#endif #endif -#define ARC_STATUS32_A1 0x8 -#define ARC_STATUS32_A2 0x10 -#define ARC_STATUS32_AE 0x20 -#define ARC_STATUS32_L 0x100 +#include "opcode/arc.h" +#include "opcodes/arc-dis.h" +#include "opcodes/arc-ext.h" +#include "opcodes/arcompact-dis.h" +#include "arc-support.h" +#include "arc-tdep.h" -static CORE_ADDR arc_frame_align (struct gdbarch *gdbarch, CORE_ADDR sp); -/* The frame unwind cache for the ARC - */ +/* -------------------------------------------------------------------------- */ +/* local types */ +/* -------------------------------------------------------------------------- */ -struct arc_unwind_cache +typedef struct { - /* blink offset in stack */ - int blink_offset ; + const char *name; + CORE_ADDR address; + Boolean is_argument; + Boolean is_callee; + Boolean is_array; + unsigned int size; + unsigned int element_size; +} LocalVariable; - /* Caller's PC */ - CORE_ADDR prev_pc; - - /* The stack pointer at the time this frame was created; i.e. the - caller's stack pointer when this function was called. It is used - to identify this frame. */ - - CORE_ADDR prev_sp; - /* The frame base */ - CORE_ADDR frame_base; - /* Frame size */ - int framesize; - - /* offset of sp from the stack frame base */ - LONGEST sp_offset; - /* offset of fp from the stack frame base */ - LONGEST fp_offset; - /* Is this a leaf function */ - int is_leaf ; - /* Is there a frame pointer */ - int uses_fp; - - - /* Offsets for each register in the stack frame */ - struct trad_frame_saved_reg *saved_regs; -}; +/* The frame unwind cache for the ARC. */ +typedef struct +{ + /* BLINK save location offset from previous SP (-ve value). */ + int blink_save_offset_from_prev_sp; + /* The stack pointer at the time this frame was created; i.e. the + caller's stack pointer when this function was called. It is used + to identify this frame. */ + CORE_ADDR prev_sp; + /* The frame base (as held in FP). + N.B. this is NOT the address of the lowest word in the frame! */ + CORE_ADDR frame_base; -/* Function Prototypes */ + /* Change in SP from previous SP (-ve value) - this is computed by scanning + the prologue of the function: initially 0, it is updated for each + instruction which changes SP (either explicitly by a subtraction from SP + or implicitly by a push operation), so at each point in the prologue it + gives the difference between the previous SP (i.e. before the function + was called) and the current SP at that point; at the end of the prologue + it holds the total change in SP, i.e. the size of the frame. */ + LONGEST delta_sp; -static CORE_ADDR arc_unwind_sp (struct gdbarch *gdbarch, - struct frame_info *next_frame); + /* Offset of old stack pointer from frame base (+ve value). */ + LONGEST old_sp_offset_from_fp; -static CORE_ADDR arc_unwind_pc (struct gdbarch *gdbarch, - struct frame_info *next_frame); + /* Is this a leaf function? */ + Boolean is_leaf; + /* Is there a frame pointer? */ + Boolean uses_fp; -static struct arc_unwind_cache * arc_frame_unwind_cache (struct frame_info *next_frame, - void ** this_prologue); + /* Offsets for each register in the stack frame. */ + struct trad_frame_saved_reg *saved_regs; + unsigned int saved_regs_mask; +} UnwindCache; -static CORE_ADDR arc_scan_prologue (CORE_ADDR pc, - struct frame_info *next_frame, - struct arc_unwind_cache *info); +/* -------------------------------------------------------------------------- */ +/* local data */ +/* -------------------------------------------------------------------------- */ -static int arc_binutils_reg_to_regnum (int reg); +#define DEBUG_COMMAND "arc-debug" +#define SHOW_FRAME_COMMAND "arc-show-frame" +#define SHOW_FRAME_COMMAND_USAGE "Usage: " SHOW_FRAME_COMMAND " [ <FRAME> ]\n" -extern struct arcDisState arcAnalyzeInstr ( bfd_vma address,disassemble_info* info ); -extern struct arcDisState a4AnalyzeInstr ( bfd_vma address,disassemble_info* info ); +#define NEW_LINE _("\n") -/* defined in opcodes, but there's no header file with this prototype... */ -disassembler_ftype arcompact_get_disassembler (void *); -/* Standard register type for the ARC platform . - * It would be builtin_type_uint32 until - * we consider the DSP extensions - */ +/* -------------------------------------------------------------------------- */ +/* externally visible data */ +/* -------------------------------------------------------------------------- */ -static struct type * -arc_register_type (struct gdbarch *gdbarch, int regnum) -{ - return builtin_type_uint32; -} +/* Global debug flag. */ +Boolean arc_debug_target; -void printFrameInfo(struct arc_unwind_cache * info) -{ -#ifdef ARC_DEBUG - printf("-------------------\n"); - printf("%lx \n",info ); - printf("prev_sp = %lx \n",info->prev_sp); - printf("prev_pc = %lx \n",info->prev_pc); - printf("frame_base is %lx \n",info->frame_base); - printf("framesize is %lx \n",info->framesize); - printf("Blink offset %lx \n",info->blink_offset); - printf("sp_offset = %lx \n",info->sp_offset ); - printf("fp_offset is %lx \n",info->fp_offset); - printf("is_leaf = %d, uses_fp=%d",info->is_leaf, info->uses_fp); -#endif -} +/* -------------------------------------------------------------------------- */ +/* local macros */ +/* -------------------------------------------------------------------------- */ -/* Print the instruction state returned - by the disassembler . Used for internal - debugging only -*/ +#define WORD_ALIGNED(addr) ((addr) & ~(BYTES_IN_WORD - 1)) +#define WORDS_OCCUPIED(bytes) (((bytes) + BYTES_IN_WORD - 1) / BYTES_IN_WORD) +#define ROUND_UP_TO_WORDS(bytes) (WORDS_OCCUPIED(bytes) * BYTES_IN_WORD) -void printInsnState(struct arcDisState state) -{ -#ifdef ARC_DEBUG - printf("---------------------------------\n"); - printf("Instruction Length %d\n", state.instructionLen); - printf("Opcode [0x%x] : Cond [%x]\n", state._opcode, state._cond); - printf("Words 1 [%lx] : 2 [%lx]\n", state.words[0], state.words[1]); - printf("ea present [%x] : memload [%x]\n", state._ea_present, state._mem_load); - printf("Load length [%d]:\n", state._load_len); - printf("Address writeback [%d]\n", state._addrWriteBack); - printf("ea reg1 is [%x] offset [%x] \n", state.ea_reg1, state._offset); - printf("ea reg2 is [%x] \n", state.ea_reg2); - printf("operands buffer is %s \n", state.operandBuffer); - printf("SourceType is %d \n",state.sourceType); - printf("Flow is %d\n",state.flow); - printf("Branch is %d,'%c'\n",state.isBranch, state.isBranch); -#endif -} -/* Scan the prologue and update the - * corresponding frame cache for the frame unwinder for unwinding - * frames without debug info . In such a situation GDB attempts to - * parse the prologue for this purpose . This currently would attempt - * to parse the prologue generated by our gcc 2.95 .(We should support - * Metaware generated binaries at some suitable point of time ) - * This function is called with the pc where gdb stopped , the next_frame - * to be filled in (if need be?) and the existing cached info . - - * scan_prologue is called by our unwinder as well - * as from skip_prologue in the case that it cannot detect - * the end of the prologue. next_frame is set to NULL if we are called from - * arc_skip_prologue in an attempt to discover the end of the prologue. In - * such a case we don't fill the frame info that is passed to us :-) - - * Todos. - * 1. Support 32 bit normal frames generated by GCC 2.95 . - * 2. Support 16 and 32 bit mixed frames generated by GCC 2.95 - * 3. Support 32 bit normal variadic function frames by GCC 2.95 - * 4. Support 32 bit normal frames from GCC 3.4.x with variadic args - * 5. Support 16 and 32 bit normal frames from GCC 3.4.x with variadic args - * 6. Support 16 and 32 bit mixed frames generated by GCC 3.4.x - * 7. Support Metaware generated prologues .( The difference is - * in the use of thunks to identify the saving and restoring of - * callee saves :-) May have to do some hackery even in next_pc. - * since the call is going to create its own set of problems - * with our stack setup :-( - * We attempt to use the disassembler interface from the opcodes - * library to do our disassembling . - - * The usual 32 bit normal - * gcc -O0 prologue looks like this. - - * Complete Prologue for all GCC frames (Cases #1 to #6 in Todos above) - - * sub sp, sp, limm ; space for variadic arguments. - * st.a blink, [sp,-4] ; push blink (if not a leaf function) - * sub sp, sp , limm ; (optional space creation for callee saves ) - * st r13, [sp] ; pushes of all callee saves. - * st r14, [sp,4] ; pushes of more callee saves. - * XXXX - * st.a fp , [sp,-4] ; push fp (if fp has to be saved ) - * mov fp , sp ; Set the current frame up correctly - * sub sp , sp , #immediate ; Create space for local vars on the stack. - */ +/* Macros to be used with disassembling the prologue and update the frame info. + The *FI macros are to update the frame info and the ACT macros are to + actually do the action on a corresponding match. */ +#define IS_INSTRUCTION(insn_name, search_string) !strcmp(insn_name, search_string) +#define CHECK_OPERAND_STRING_AND_ACT(target_check, search_string, action) \ + if (strstr(target_check, search_string) == target_check) \ + { \ + action; \ + return TRUE; \ + } -/* Macros to be used with disassembling the prologue - * and update the frame info.The *FI macros are to update - * the frame info and the ACT macros are to actually do the - * action on a corresponding match. - * -*/ -#define CHECKOPDSTRING(targetcheck,searchstring) \ - if(strstr(targetcheck,searchstring) == targetcheck) \ - {continue;} - -#define CHECKOPDSTRINGANDACT(targetcheck,searchstring,action) \ - if(strstr(targetcheck,searchstring) == targetcheck) \ - {\ - action;\ - continue;} - - -/* The frame info changes by changing the decrementing - the sp_offset and setting the leaf function to be NIL; - Also the offset of the blink register from the previous - value of sp is calculated. Finally this can be updated - as - info->blink_offset = info-> prev_sp + info->blink_offset ; - Also the addition below is coz the offsets are usually negative -*/ -#define PUSHBLINKACT do { \ - if(info) \ - { \ - info->sp_offset += current_instr._offset; \ - info->blink_offset = info->sp_offset ; \ - info->is_leaf = 0;\ - }}while(0); - +/* The frame info changes by changing the decrementing the delta_sp and setting + the leaf function flag to be False (if this function prologue is saving blink + then it must be going to call another function - so it can not be a leaf!); + also the offset of the blink register save location from the previous value + of sp is recorded. This will eventually used to compute the address of the + save location: -#define ISPUSHBLINK(state) CHECKOPDSTRING(state.operandBuffer,"blink") -#define ISPUSHBLINKFI(state) CHECKOPDSTRINGANDACT(state.operandBuffer,"blink",PUSHBLINKACT) + <blink saved address> = <prev sp> + <blink offset from prev sp> + The addition (+=) below is because the sp offset and the instruction offset + are negative - so incrementing the sp offset by the instruction offset is + actually making the sp offset more negative, correctly reflecting that SP + is moving further down the downwards-growing stack. */ -#define PUSHFPACT do { \ - if(info) \ - { \ - info->sp_offset += current_instr._offset ; \ - info->fp_offset = -info->sp_offset; \ - }}while(0); - -#define ISPUSHFP(state) CHECKOPDSTRING(state.operandBuffer,"fp") -#define ISPUSHFPFI(state) CHECKOPDSTRINGANDACT(state.operandBuffer,"fp",PUSHFPACT) -#define ISINSTRUCTION(insnname,searchstring) !strcmp(insnname,searchstring) +#define PUSH_BLINK(offset) \ + { \ + info->delta_sp += offset; \ + info->blink_save_offset_from_prev_sp = (int) info->delta_sp; \ + info->is_leaf = FALSE; \ + } +#define PUSH_BLINK_ACT \ + do { \ + if (info) PUSH_BLINK(instr->_offset) \ + } while (0); -#define UPDATEFPACT do {\ - if(info) {\ - info->uses_fp = 1;\ - }}while(0); +#define IS_PUSH_BLINK_FI(state) CHECK_OPERAND_STRING_AND_ACT(state->operandBuffer, "blink", PUSH_BLINK_ACT) +/* At the point that that FP is pushed onto the stack (so saving the dynamic + link chain pointer to the previous frame), at the address that will be the + base of the new frame, we know the offset of SP from the previous SP - so the + offset of the old SP from the new frame base is known (the -ve delta_sp is + negated to give the +ve old_sp_offset_from_fp). */ +#define PUSH_FP_ACT do { \ + if (info) \ + { \ + info->delta_sp += instr->_offset; \ + info->old_sp_offset_from_fp = -info->delta_sp; \ + }} while (0); -#define ISUPDATEFPFI(state) \ - if(ISINSTRUCTION(state.instrBuffer,"mov")) \ -{ \ - CHECKOPDSTRINGANDACT(state.operandBuffer,"fp,sp",UPDATEFPACT); \ -} +#define IS_PUSH_FP_FI(state) CHECK_OPERAND_STRING_AND_ACT(state->operandBuffer, "fp", PUSH_FP_ACT) +#define UPDATE_FP_ACT do { \ + if (info) \ + info->uses_fp = TRUE; \ + } while (0); -#define ISUPDATEFP(state) \ - if(ISINSTRUCTION(state.instrBuffer,"mov")) \ -{ \ - CHECKOPDSTRING(state.operandBuffer,"fp,sp") \ -} +#define IS_UPDATE_FP_FI(state) \ + if (IS_INSTRUCTION(state->instrBuffer, "mov")) \ + { \ + CHECK_OPERAND_STRING_AND_ACT(state->operandBuffer, "fp,sp", UPDATE_FP_ACT); \ + } +#define UPDATE_STACK_SPACE(state) do { \ + if (info) { \ + /* Eat up sp,sp. */ \ + int immediate = atoi(state->operandBuffer + 6); \ + info->delta_sp -= immediate; \ + }} while (0); -#define ISSUBSP(state) \ -if(ISINSTRUCTION(state.instrBuffer,"sub"))\ -{ \ - CHECKOPDSTRING(state.operandBuffer,"sp,sp") \ + +#define IS_SUB_SP_FI(state) \ + if (IS_INSTRUCTION(state->instrBuffer, "sub") || \ + IS_INSTRUCTION(state->instrBuffer, "sub_s")) \ + { \ + CHECK_OPERAND_STRING_AND_ACT(state->operandBuffer, "sp,sp", UPDATE_STACK_SPACE(state)) \ + } + + +/* -------------------------------------------------------------------------- */ +/* forward declarations */ +/* -------------------------------------------------------------------------- */ + +static CORE_ADDR scan_prologue (CORE_ADDR entrypoint, + struct frame_info *next_frame, + UnwindCache *info); + + +/* -------------------------------------------------------------------------- */ +/* local debug functions */ +/* -------------------------------------------------------------------------- */ + +/* Print information for a frame. */ + +static void +printFrameInfo (const char *message, + UnwindCache *info, + Boolean addresses_known) +{ + unsigned int i; + + DEBUG("-------------------\n"); + DEBUG("%s (info = %p)\n", message, info); + DEBUG("prev_sp = %lx\n", (long unsigned int) info->prev_sp); + DEBUG("frame_base = %lx\n", (long unsigned int) info->frame_base); + DEBUG("blink offset = %d\n", info->blink_save_offset_from_prev_sp); + DEBUG("delta_sp = %d\n", (int) info->delta_sp); + DEBUG("old_sp_offset_from_fp = %d\n", (int) info->old_sp_offset_from_fp); + DEBUG("is_leaf = %d, uses_fp = %d\n", info->is_leaf, info->uses_fp); + + for (i = ARC_ABI_FIRST_CALLEE_SAVED_REGISTER; i < ARC_ABI_LAST_CALLEE_SAVED_REGISTER; i++) + { + if (info->saved_regs_mask & (1 << i)) + DEBUG("saved register R%02d %s 0x%lx\n", + i, + (addresses_known) ? "address" : "offset", + (unsigned long) info->saved_regs[i].addr); + } + DEBUG("-------------------\n"); } -#define UPDATESTACKSPACE(state) do { \ - if(info){ \ -/* Eat up sp,sp */ \ - int tmp = atoi(state.operandBuffer + 6); \ - info->sp_offset -= tmp; \ - }}while(0); + +static const char* +ARC_Debugger_OperandType_Image (enum ARC_Debugger_OperandType value) +{ + switch (value) + { + case ARC_LIMM : return "LIMM"; + case ARC_SHIMM : return "SHIMM"; + case ARC_REGISTER : return "REGISTER"; + case ARCOMPACT_REGISTER: return "COMPACT REGISTER"; + case ARC_UNDEFINED : return "UNDEFINED"; + } + return "?"; +} -#define ISSUBSPFI(state) \ -if(ISINSTRUCTION(state.instrBuffer,"sub") \ - || ISINSTRUCTION(state.instrBuffer,"sub_s"))\ -{ \ - CHECKOPDSTRINGANDACT(state.operandBuffer,"sp,sp",UPDATESTACKSPACE(state)) \ +/* Print the instruction state returned by the disassembler. + Used for internal debugging only. */ + +static void +printInsnState (struct arcDisState state) +{ + DEBUG("---------------------------------\n"); + DEBUG("Instruction Length %d\n", state.instructionLen); + DEBUG("Opcode [0x%x] : Cond [%x]\n", state._opcode, state._cond); + DEBUG("Words 1 [%lx] : 2 [%lx]\n", state.words[0], state.words[1]); + DEBUG("Ea present [%x] : memload [%x]\n", state._ea_present, state._mem_load); + DEBUG("Load Length [%d]:\n", state._load_len); + DEBUG("Address Writeback [%d]\n", state._addrWriteBack); + DEBUG("EA reg1 is [%x] offset [%x]\n", state.ea_reg1, state._offset); + DEBUG("EA reg2 is [%x]\n", state.ea_reg2); + DEBUG("Instr buffer is %s\n", state.instrBuffer); + DEBUG("Operand buffer is %s\n", state.operandBuffer); + DEBUG("SourceType is %s\n", ARC_Debugger_OperandType_Image(state.sourceType)); + DEBUG("Source operand is %u\n", state.source_operand.registerNum); /* All fields of the union have same type. */ + DEBUG("Flow is %d\n", state.flow); + DEBUG("Branch is %d\n", state.isBranch); + DEBUG("---------------------------------\n"); } -/*Function to scan the prologue of a A4 binary - -ARCtangent-A4 Prolog - The stack back-trace data structure is a 16-byte structure which is - used to save the return register (blink, 4 bytes), the frame pointer - register (fp, 4-bytes) and 8-bytes is reserved. - - The compiler-generated prolog code does the following: - --> Allocates space for register arguments in case of variadic function - (functions with variable argument lists) - --> Saves the return address register (blink) - --> Saves the caller's frame pointer (fp), if required, and - sets the new frame pointer to this location - --> Decrements the stack pointer to account for the new stack frame - --> Saves required non-volatile general-purpose registers into the - register save area - --> Stores the arguments above the stack back-trace data structure - - - Demo Patterns: - st blink,[sp,4] ; Saves the return address - st fp,[sp] ; Saves the callers frame pointer - mov fp,sp ; Saves - sub sp,sp,24 - -0xa 538e7e20 sub sp,sp,32 ; Space for variadic args -0x2 100e3e04 st blink,[sp,4] ; Saves the return address -0x2 100e3600 st fp,[sp] ; Saves the callers frame pointer -0xc 636e3800 mov fp,sp ; Resets stack pointer to fp -0xa 538e7e18 sub sp,sp,24 ; Decrements sp to add for new - ; stack frame -0x2 100d81fc st r0,[fp,-4] ; Stores the args -0x2 100d83f8 st r1,[fp,-8] ; ----"------- - ... -*/ +/* -------------------------------------------------------------------------- */ +/* local functions for the disassembler */ +/* -------------------------------------------------------------------------- */ -/* FIXMEA: -called from arc_skip_prologue as - skip_pc = arc_scan_prologue(pc,NULL,NULL); - Then it is supposed to return the first valid pc - after the prologue +/* Wrapper for the target_read_memory function. */ - Prologue analysis does the rest... - Currently our scan prologue does not - support getting input for the frame unwinder +static int +read_memory_for_disassembler (bfd_vma memaddr, + bfd_byte *myaddr, + unsigned int length, + struct disassemble_info *info) // unused +{ + return target_read_memory((CORE_ADDR) memaddr, (gdb_byte*) myaddr, (int) length); +} - - pc = frame_func_unwind(next_frame); - arc_scan_prologue (pc, next_frame, info); -*/ +/* This is a callback function which gets called by gdb whenever the current + object file changes. */ -#ifdef ARC4_JTAG -static CORE_ADDR -a4_scan_prologue (CORE_ADDR pc, struct frame_info *next_frame, - struct arc_unwind_cache *info) +static void +set_disassembler (struct objfile *objfile) { - /* End of prologue */ - CORE_ADDR prologue_ends_pc = pc; - int i = 0; - struct arcDisState current_instr, instr_in_delay; - int insn_length; - - /* Initializations to use the opcodes - * library . - */ - - struct disassemble_info di; - - unsigned int saved_regs_mask = 0; - /* An arbitrary length on the length of the - prologue. If next_frame is NULL this means that there was - no debug info and we are called from arc_skip_prologue - */ - /*FIXMEANOW: pc + 64 is probably the max size of the prologue*/ - CORE_ADDR final_pc = (next_frame)?frame_pc_unwind(next_frame):pc+(16*4); - - - - - if (info) + if (objfile) { - /* All functions start as leaf functions until - we identify push blink - */ - info->is_leaf = 1; + /* The ARC libopcodes wants obfd so that it can find out what CPU + extensions are defined in the file. */ + set_gdbarch_print_insn(current_gdbarch, arcompact_get_disassembler(objfile->obfd)); +// dump_ARC_extmap(); } - - - - /* Initializations to use the opcodes - * library . - */ - init_disassemble_info(&di, gdb_stderr, fprintf_unfiltered); - di.arch = gdbarch_bfd_arch_info(current_gdbarch)->arch; - di.mach = gdbarch_bfd_arch_info(current_gdbarch)->mach; - di.endian = gdbarch_byte_order(current_gdbarch); - di.read_memory_func = target_read_memory; - - - for (prologue_ends_pc= pc; - prologue_ends_pc< final_pc; - prologue_ends_pc += current_instr.instructionLen ) /*FIXMEA: This could as - well be 4 */ +} + + +/* This function is supplied to gdb as the disassembler until such time as we do + have a disassembler available. */ + +static int +dummy_disassembler (bfd_vma address, disassemble_info *info) +{ + error(_("No disassembly operation yet available (no executable file loaded)")); + return 0; +} + + +/* -------------------------------------------------------------------------- */ +/* local functions for decoding call chains */ +/* -------------------------------------------------------------------------- */ + +/* Simple utility function to create a new frame cache structure. */ + +static UnwindCache* +create_cache (struct frame_info *next_frame) +{ + UnwindCache *cache = FRAME_OBSTACK_ZALLOC (UnwindCache); + + /* Zero all fields. */ + cache->blink_save_offset_from_prev_sp = 0; + cache->prev_sp = 0; + cache->frame_base = 0; + cache->delta_sp = 0; + cache->old_sp_offset_from_fp = 0; + cache->is_leaf = FALSE; + cache->uses_fp = FALSE; + + /* Allocate space for saved register info. */ + cache->saved_regs = trad_frame_alloc_saved_regs (next_frame); + + return cache; +} + + +/* Compute the previous frame's stack pointer (which is also the frame's ID's + stack address), and this frame's base pointer. */ + +static void +find_previous_stack_pointer (UnwindCache *info, + struct frame_info *next_frame) +{ + ENTERARGS("next_frame = %p", next_frame); + + /* If the frame has a frame pointer. */ + if (info->uses_fp) { - current_instr = a4AnalyzeInstr(prologue_ends_pc, &di); - printInsnState(current_instr); -#ifdef ARC_DEBUG - printf("Prologue PC: %d\n", prologue_ends_pc); - printf("Final PC: %d\n", final_pc); -#endif + ULONGEST this_base; + unsigned int i; + + /* The SP was moved to the FP. This indicates that a new frame + was created. Get THIS frame's FP value by unwinding it from + the next frame. The old contents of FP were saved in the location + at the base of this frame, so this also gives us the address of + the FP save location. */ + this_base = frame_unwind_register_unsigned(next_frame, ARC_FP_REGNUM); + info->frame_base = (CORE_ADDR) this_base; + info->saved_regs[ARC_FP_REGNUM].addr = (long long) this_base; + + /* The previous SP is the current frame base + the difference between + that frame base and the previous SP. */ + info->prev_sp = info->frame_base + (CORE_ADDR) info->old_sp_offset_from_fp; + + for (i = ARC_ABI_FIRST_CALLEE_SAVED_REGISTER; i < ARC_ABI_LAST_CALLEE_SAVED_REGISTER; i++) + { + /* If this register has been saved, add the previous stack pointer + to the offset from the previous stack pointer at which the + register was saved, so giving the address at which it was saved. */ + if (info->saved_regs_mask & (1 << i)) + { + info->saved_regs[i].addr += info->prev_sp; + +#ifdef DUMP_SAVED_REGISTERS + /* This is a really useful debugging aid: we can debug a test + program which loads known values into the callee-saved + registers, then calls another function which uses those + registers (and hence must save them) then hits a breakpoint; + traversing the stack chain (e.g. with the 'where' command) + should then execute this code, and we should see those known + values being dumped, so showing that we have got the right + addresses for the save locations! */ + { + unsigned int contents; + DEBUG("saved R%02d is at 0x%lx\n", i, (long unsigned int) info->saved_regs[i].addr); - if (current_instr._opcode == 0x2) - { - // Saves the return address st blink,[sp,4] 0x100e3e04 - // Save the callers fp st fp,[sp] 0x100e3600 - // Saves the args st rX,[fp, #imm] 0x100d8xxx - if (current_instr.ea_reg1 == 28) - { - if( strstr(current_instr.operandBuffer,"blink") == current_instr.operandBuffer) - { - if(info) - { - info->sp_offset += current_instr._offset; - // info->blink_offset = info->sp_offset ; - info->blink_offset = current_instr._offset; - info->is_leaf = 0; - -#ifdef ARC_DEBUG - printf("Blink instruction:\n"); - printFrameInfo(info); -#endif - } - continue; - } - else - if(strstr(current_instr.operandBuffer,"fp") == current_instr.operandBuffer) - { - if(info) - { -/* info->sp_offset += current_instr._offset ; */ -/* info->fp_offset = info->sp_offset; */ - info->fp_offset = 0; - } - continue; - } - } - else if (current_instr.ea_reg1 == 27) - { - /* Saving of arguments onto the stack using the - frame pointer (r27). */ - if(info) - { - // Save regs offsets - } -#ifdef ARC_DEBUG - printf(" Saving registers onto stack\n%s\n",current_instr.operandBuffer); -#endif - continue; - } - // ISPUSHBLINK(current_instr); - } - else if (current_instr._opcode == 0xc) - { - // Resets stack pointer to fp - // 0x636e3800 - // 636e3800 mov fp,sp - if (current_instr.words[0] == 0x636e3800) - { - if (info) - { - info->uses_fp = 1; - } - continue; - } - } - else if (current_instr._opcode == 0xa) - { - // Decrements stackpointer to add for new stack frame - // 0x538e7e18 sub sp,sp,#imm - // 538e7e20 sub sp,sp,32 - if( current_instr.words[0] == 0x538e7e20) - { - //sub sp,sp, 32 //// variadic - if (info) - { - int tmp = atoi(current_instr.operandBuffer + 6); - info->sp_offset -= tmp; - } - continue; - } - else if((current_instr.words[0] & 0xffffff00) == 0x538e7e00) - { - // sub sp,sp,xx - if(info) - { - int tmp = atoi(current_instr.operandBuffer + 6); - info->sp_offset -= tmp; - } - continue; - } - } - - /* Found a instruction that is not in - the prologue*/ -#ifdef ARC_DEBUG - printf("End of Prologue reached \n"); + if (target_read_memory((CORE_ADDR) info->saved_regs[i].addr, + (gdb_byte*) &contents, + BYTES_IN_REGISTER) == 0) + { + DEBUG("saved R%02d contents: 0x%0x\n", i, contents); + } + } #endif - break; + } + } } - - /* Means we were called from skip_prologue */ - if((next_frame == NULL)&& (info == NULL)) + else { - return prologue_ends_pc; - } + ULONGEST this_sp; + /* Get the stack pointer for this frame by getting the saved SP + from the next frame. */ + this_sp = frame_unwind_register_unsigned (next_frame, ARC_SP_REGNUM); - info->framesize = -info->sp_offset; - /* Compute the previous frame's stack pointer (which is also the - frame's ID's stack address), and this frame's base pointer. */ - if(info->uses_fp) - { + /* The previous SP is this frame's SP plus the known difference between + the previous SP and this frame's SP (the delta_sp is negated as it is + a negative quantity). */ + info->prev_sp = (CORE_ADDR) (this_sp + (ULONGEST) (-info->delta_sp)); - ULONGEST this_base; - /* The SP was moved to the FP. This indicates that a new frame - was created. Get THIS frame's FP value by unwinding it from - the next frame. */ - frame_unwind_unsigned_register(next_frame, ARC_FP_REGNUM, - &this_base); - info->frame_base = this_base; - info->saved_regs[ARC_FP_REGNUM].addr = info->frame_base; - - /* The previous sp is the current frame base + the offset of the - fp in the current frame */ - info->prev_sp = info->frame_base + info->fp_offset; - for(i = 13; i < 26 ; i++ ) - { - if(saved_regs_mask & (1 << i)) - info->saved_regs[i].addr += info->frame_base ; - } - - printFrameInfo(info); - + /* Assume that the FP is this frame's SP. */ + info->frame_base = (CORE_ADDR) this_sp; } - else + + /* If the function owning this frame is not a leaf function. */ + if (!info->is_leaf) { - ULONGEST this_base; - /* Assume that the FP is this frame's SP but with that pushed - stack space added back. */ - frame_unwind_unsigned_register (next_frame, ARC_SP_REGNUM, &this_base); - info->frame_base = this_base; - - /* In such a case it would be the previous SP + the size of the current frame */ - info->prev_sp = info->frame_base + info->framesize; - + /* Usually blink is saved above the callee save registers and below the + space created for variable arguments. The quantity + + info->blink_save_offset_from_prev_sp + + is negative, so adding it to the the previous SP gives the address of + a location further down the stack from that SP. */ + info->saved_regs[ARC_BLINK_REGNUM].addr = + (LONGEST) (info->prev_sp + info->blink_save_offset_from_prev_sp); } - +} + - if(!info->is_leaf) +/* The workhorse : frame_unwind_cache for the ARC700 target. */ + +static UnwindCache * +frame_unwind_cache (struct frame_info *next_frame, + void **this_prologue_cache) +{ + ENTERMSG; + + if ((*this_prologue_cache) == NULL) { + CORE_ADDR entrypoint = frame_func_unwind(next_frame, NORMAL_FRAME); + UnwindCache *cache = create_cache(next_frame); - /* Usually blink is saved before the callee save registers and - below the space created for variadic arguments . We maintain - info->blink_offset as negative when we stored it initially - */ - info->saved_regs[ARC_BLINK_REGNUM].addr = info->prev_sp + info->blink_offset; -#ifdef ARC_DEBUG - printf("blink offset is [%x] \n",info->blink_offset); -#endif + /* Return the newly-created cache. */ + *this_prologue_cache = cache; + + /* Prologue analysis does the rest... */ + + /* Currently our scan prologue does not support getting input for the + frame unwinder. */ + (void) scan_prologue(entrypoint, next_frame, cache); } - - /* The PC is found in blink (the actual register or located on the stack). */ - // FIXMEA: - //info->saved_regs[ARC_STATUS_REGNUM] |= (info->saved_regs[ARC_BLINK_REGNUM] & 0xffffff)>>2; - info->saved_regs[ARC_STATUS_REGNUM] = info->saved_regs[ARC_BLINK_REGNUM]; - return prologue_ends_pc; + return *this_prologue_cache; } -#endif -static CORE_ADDR -arc_scan_prologue (CORE_ADDR pc, struct frame_info *next_frame, - struct arc_unwind_cache *info) + +/* -------------------------------------------------------------------------- */ +/* local functions for decoding function prologues */ +/* -------------------------------------------------------------------------- */ + +/* This function determines whether the given register, which is being saved + by a function prologue on the stack at a known offset from the current SP, + is a callee-saved register. If it is, the information in the frame unwind + cache is updated. */ + +static Boolean +is_callee_saved_register (unsigned int reg, + int offset, + UnwindCache * info) { -#ifdef ARC4_JTAG -#ifdef ARC_DEBUG - printf("\narc_scan_prologue called\n"); -#endif -#else - /* End of prologue */ - CORE_ADDR prologue_ends_pc = pc; - int i = 0; - struct arcDisState current_instr, instr_in_delay; - int insn_length; - - /* Initializations to use the opcodes - * library . - */ - - struct disassemble_info di; - - unsigned int saved_regs_mask = 0; - /* An arbitrary length on the length of the - prologue. If next_frame is NULL this means that there was - no debug info and we are called from arc_skip_prologue - */ - CORE_ADDR final_pc = (next_frame)?frame_pc_unwind(next_frame):pc+64; - - - - - if (info) - { - /* All functions start as leaf functions until - we identify push blink - */ - info->is_leaf = 1; - - } - - - - /* Initializations to use the opcodes - * library . - */ - init_disassemble_info(&di, gdb_stderr, fprintf_unfiltered); - di.arch = gdbarch_bfd_arch_info(current_gdbarch)->arch; - di.mach = gdbarch_bfd_arch_info(current_gdbarch)->mach; - di.endian = gdbarch_byte_order(current_gdbarch); - di.read_memory_func = target_read_memory; - - - for(prologue_ends_pc= pc; prologue_ends_pc< final_pc; - prologue_ends_pc += current_instr.instructionLen ) + if (ARC_ABI_FIRST_CALLEE_SAVED_REGISTER <= reg && reg <= ARC_ABI_LAST_CALLEE_SAVED_REGISTER) { - current_instr = arcAnalyzeInstr(prologue_ends_pc, &di); - printInsnState(current_instr); - /* Might be a push or a pop */ - if(current_instr._opcode == 0x3) - { - if(current_instr._addrWriteBack) - { - /* This is a st.a */ - if((current_instr.ea_reg1 == 28) && - (current_instr._offset == -4)) - { - - /* This is a push something at sp */ - /* Is it a push of the blink */ - ISPUSHBLINKFI(current_instr); - /* Is it a push for fp */ - ISPUSHFPFI(current_instr); - - } - - } - else - { - /* Is this a store of some register onto - the stack using the stack pointer.*/ - if(current_instr.ea_reg1 == 28) - { - if(current_instr.sourceType == ARC_REGISTER ) - { - /* R13..R26 are the callee saved registers. [R27 (fp) - is also a callee saved register, but it's usually - pushed using st.a and so handled in the st.a case - above.] */ - if((current_instr.source_operand.registerNum > 12 - && current_instr.source_operand.registerNum <= 26)) - { - if(info) - { - printFrameInfo(info); - /* Save up the offsets for the correct instruction */ - info->saved_regs[current_instr.source_operand.registerNum].addr - = - info->sp_offset - current_instr._offset; - saved_regs_mask |= (1 << current_instr.source_operand.registerNum); - } - continue; - } - - } - - } - /* Is this the store of some register on the - stack using the frame pointer. We check - for argument registers getting saved and - restored. - */ - if(current_instr.ea_reg1 == 27) - if((current_instr.source_operand.registerNum <= 7)) - { - /* Saving argument registers.Don't save them in saved_regs, just skip. - */ - continue; - } - - - - } - } - - if(current_instr._opcode == 0x4) - { - /* A major opcode 0x4 instruction */ - /* We are usually interested in a - mov or a sub */ - ISUPDATEFPFI(current_instr); - ISSUBSPFI(current_instr); - } - if(current_instr._opcode == 0x18) - { - /* sub_s sp,sp,constant */ - ISSUBSPFI(current_instr); - /* push_s blink */ - if(strcmp(current_instr.instrBuffer,"push_s") == 0) - { - if(strcmp(current_instr.operandBuffer,"blink") == 0) - { - if(info) - { - info->sp_offset += 4; - info->blink_offset = info->sp_offset ; - info->is_leaf = 0; - } - continue; - } - } - } - - /* If we reach here . we have - * reached end of the prologue - */ - break; - - } - - /* Means we were called from skip_prologue */ - if((next_frame == NULL)&& (info == NULL)) - { - return prologue_ends_pc; + DEBUG("register R%02u saved\n", reg); + + if (info) + { + /* We can not determine the address of the location in the stack + frame in which the register was saved, as we do not (yet) know + the frame or stack pointers for the frame; so the most we can do + is to record the offset from the old SP of that location, which + we can compute as we know the offset of SP from the old SP, and + the offset of the location from SP (which is the offset in the + store instruction). + + N.B. the stack grows downward, so the store offset is positive, + but the delta-SP is negative, so the save offset is also + negative. + + | | + old sp ------> |------------| + / | | \ + : | | : + : | | : -ve + : | | : save offset + : |------------| : + -ve : | save loc | / + delta sp : |------------| <--- store address + : / | | + : +ve : | | + : store : | | + : offset : | | + \ \ | | + sp' ---------> | | + | | + | | + |------------| <---- frame base + | | + | | + | | + | | | + | + V + downwards + + where sp' is the stack pointer at the current point in the code */ + + info->saved_regs[reg].addr = info->delta_sp + offset; + + /* We now know that this register has been saved, so set the + corresponding bit in the save mask. */ + info->saved_regs_mask |= (1 << reg); + + printFrameInfo("after callee register save", info, FALSE); + + return TRUE; + } } - - - info->framesize = -info->sp_offset; - /* Compute the previous frame's stack pointer (which is also the - frame's ID's stack address), and this frame's base pointer. */ - if(info->uses_fp) + + return FALSE; +} + + +/* This function determines whether the given disassembled instruction may be + part of a function prologue. If it is, the information in the frame unwind + cache may be updated. */ + +static Boolean +is_in_prologue (UnwindCache *info, struct arcDisState *instr) +{ + /* Might be a push or a pop */ + if (instr->_opcode == 0x3) { + if (instr->_addrWriteBack != (char) 0) + { + /* This is a st.a instruction. */ + if (instr->ea_reg1 == ARC_ABI_STACK_POINTER) + { + if (instr->_offset == -4) + { + /* This is a push something at SP. */ + /* Is it a push of the blink? */ + IS_PUSH_BLINK_FI(instr); + + /* Is it a push for fp? */ + IS_PUSH_FP_FI(instr); + } + else + { + if (instr->sourceType == ARC_REGISTER ) + { + /* st.a <reg>, [sp,<offset>] */ + + if (is_callee_saved_register(instr->source_operand.registerNum, + instr->_offset, + info)) + { + /* This is a push onto the stack, so change delta_sp. */ + info->delta_sp += instr->_offset; + return TRUE; + } + } + } + } + } + else + { + if (instr->sourceType == ARC_REGISTER ) + { + /* Is this a store of some register onto the stack using the + stack pointer? */ + if (instr->ea_reg1 == ARC_ABI_STACK_POINTER) + { + /* st <reg>, [sp,offset] */ + + if (is_callee_saved_register(instr->source_operand.registerNum, + instr->_offset, + info)) + /* This is NOT a push onto the stack, so do not change delta_sp. */ + return TRUE; + } - ULONGEST this_base; - /* The SP was moved to the FP. This indicates that a new frame - was created. Get THIS frame's FP value by unwinding it from - the next frame. */ - frame_unwind_unsigned_register(next_frame, ARC_FP_REGNUM, - &this_base); - info->frame_base = this_base; - info->saved_regs[ARC_FP_REGNUM].addr = info->frame_base; - - /* The previous sp is the current frame base + the offset of the - fp in the current frame */ - info->prev_sp = info->frame_base + info->fp_offset; - for(i = 13; i < 26 ; i++ ) - { - if(saved_regs_mask & (1 << i)) - info->saved_regs[i].addr += info->frame_base ; - } - - printFrameInfo(info); - + /* Is this the store of some register on the stack using the + frame pointer? We check for argument registers getting saved + and restored. */ + if (instr->ea_reg1 == ARC_ABI_FRAME_POINTER) + { + if (IS_ARGUMENT_REGISTER(instr->source_operand.registerNum)) + { + /* Saving argument registers. Don't set the bits in the + saved mask, just skip. */ + return TRUE; + } + } + } + } } - else + + else if (instr->_opcode == 0x4) { - ULONGEST this_base; - /* Assume that the FP is this frame's SP but with that pushed - stack space added back. */ - frame_unwind_unsigned_register (next_frame, ARC_SP_REGNUM, &this_base); - info->frame_base = this_base; - - /* In such a case it would be the previous SP + the size of the current frame */ - info->prev_sp = info->frame_base + info->framesize; - + /* A major opcode 0x4 instruction. */ + /* We are usually interested in a mov or a sub. */ + IS_UPDATE_FP_FI(instr); + IS_SUB_SP_FI(instr); } - - if(!info->is_leaf) + else if (instr->_opcode == 0x18) { + /* sub_s sp,sp,constant */ + IS_SUB_SP_FI(instr); - /* Usually blink is saved before the callee save registers and - below the space created for variadic arguments . We maintain - info->blink_offset as negative when we stored it initially - */ - info->saved_regs[ARC_BLINK_REGNUM].addr = info->prev_sp + info->blink_offset; -#ifdef ARC_DEBUG - printf("blink offset is [%x] \n",info->blink_offset); -#endif + /* push_s blink */ + if (strcmp(instr->instrBuffer, "push_s") == 0) + { + if (strcmp(instr->operandBuffer, "blink") == 0) + { + if (info) + { + /* SP is decremented by the push_s instruction (before it + stores blink at the stack location addressed by SP). */ + PUSH_BLINK(-BYTES_IN_REGISTER) + } + return TRUE; + } + } + else if (strcmp(instr->instrBuffer, "st_s") == 0) + { + unsigned int reg; + int offset; + + if (sscanf(instr->operandBuffer, "r%u,[sp,%d]", ®, &offset) == 2) + { + /* st_s <reg>,[sp,<offset>] */ + + if (is_callee_saved_register(reg, offset, info)) + /* This is NOT a push onto the stack, so do not change delta_sp. */ + return TRUE; + } + } } - - /* The PC is found in blink (the actual register or located on the stack). */ - info->saved_regs[PC_REGNUM] = info->saved_regs[ARC_BLINK_REGNUM]; - /*info->saved_regs[ARC_PC_REGNUM] = info->saved_regs[ARC_BLINK_REGNUM];*/ - return prologue_ends_pc; -#endif + + return FALSE; } -/* Skip the prologue for the function at pc. - * This is done by checking from the line - * information picked up during dwarf reading - * FIXME: more stuff to be added when we - * parse the prologue. - */ +/* Scan the prologue and update the corresponding frame cache for the frame + unwinder for unwinding frames without debug info. In such a situation GDB + attempts to parse the prologue for this purpose. This currently would attempt + to parse the prologue generated by our gcc 2.95 compiler (we should support + Metaware generated binaries at some suitable point of time). + + This function is called with: + entrypoint : the address of the functon entry point + next_frame : the next frame to be filled in (if need be) + info : the existing cached info. + + Returns: the address of the first instruction after the prologue. + + This function is called by our unwinder as well as from arc_skip_prologue + in the case that it cannot detect the end of the prologue. + + 'next_frame' and 'info' are NULL if this function is called from + arc_skip_prologue in an attempt to discover the end of the prologue. + In this case we don't fill in the 'info' structure that is passed in. + + TODOs: + 1. Support 32 bit normal frames generated by GCC 2.95 + 2. Support 16 and 32 bit mixed frames generated by GCC 2.95 + 3. Support 32 bit normal variadic function frames by GCC 2.95 + 4. Support 32 bit normal frames from GCC 3.4.x with variadic args + 5. Support 16 and 32 bit normal frames from GCC 3.4.x with variadic args + 6. Support 16 and 32 bit mixed frames generated by GCC 3.4.x + 7. Support Metaware generated prologues + (The difference is in the use of thunks to identify the saving and + restoring of callee saves: may have to do some hackery even in + next_pc, since the call is going to create its own set of problems + with our stack setup). + + We attempt to use the disassembler interface from the opcodes library to do + our disassembling. + + The usual 32 bit normal gcc -O0 prologue looks like this: + + Complete Prologue for all GCC frames (Cases #1 to #6 in TODOs above): + + sub sp, sp, limm ; space for variadic arguments + st.a blink, [sp,-4] ; push blink (if not a leaf function) - decrements sp + sub sp, sp , limm ; (optional space creation for callee saves) + st r13, [sp] ; push of first callee saved register + st r14, [sp,4] ; push of next callee saved register + ... + st.a fp , [sp,-4] ; push fp (if fp has to be saved) - decrements sp + mov fp , sp ; set the current frame up correctly + sub sp , sp , #immediate ; create space for local vars on the stack */ + +/* 3 instructions before and after callee saves, and max number of saves; assume each is 4-byte inst. */ +#define MAX_PROLOGUE_LENGTH ((6 + (ARC_ABI_LAST_CALLEE_SAVED_REGISTER - ARC_ABI_FIRST_CALLEE_SAVED_REGISTER + 1)) * 4) static CORE_ADDR -arc_skip_prologue (CORE_ADDR pc) +scan_prologue (CORE_ADDR entrypoint, + struct frame_info *next_frame, + UnwindCache *info) { - //#ifdef ARC4_JTAG -#ifdef ARC_DEBUG - printf ("\narc_skip_prologue called\n"); -#endif - // FIXMEA: cleanup #else - unsigned long inst; - unsigned long addend = 4; - CORE_ADDR skip_pc = pc; - CORE_ADDR func_addr, func_end = 0; - char *func_name; - struct symtab_and_line sal; + ENTERARGS("next_frame = %p, info = %p", next_frame, info); - /* If we're in a dummy frame, don't even try to skip the prologue. */ - if (deprecated_pc_in_call_dummy (pc)) - return pc; + { + /* Will be set to end of prologue. */ + CORE_ADDR prologue_ends_pc = entrypoint; + struct disassemble_info di; + + /* An arbitrary limit on the length of the prologue. If next_frame is + NULL this means that there was no debug info and we are called from + arc_skip_prologue; otherwise, if we know the frame, we can find the + pc within the function. + + N.B. that pc will usually be after the end of the prologue, but + it could actually be within the prologue (i.e. execution has + halted within the prologue, e.g. at a breakpoint); in that + case, do NOT go beyond that pc, as the instructions at the + pc and after have not been executed yet, so have had no effect! */ + CORE_ADDR final_pc = (next_frame) ? frame_pc_unwind(next_frame) + : entrypoint + MAX_PROLOGUE_LENGTH; + + if (info) + { + /* Assume that the function is a leaf function until we find out + that it is not (i.e. when we find the 'push blink' instruction + in the prologue). */ + info->is_leaf = TRUE; - /* See what the symbol table says. */ + /* No registers known to be saved, as yet. */ + info->saved_regs_mask = 0; + } - if (find_pc_partial_function (pc, &func_name, &func_addr, &func_end)) - { - struct symbol *sym; + /* Initializations to use the opcodes library. */ + arc_initialize_disassembler(&di); + + DEBUG("Prologue PC: %lx\n", (unsigned long) prologue_ends_pc); + DEBUG("Final PC: %lx\n", (unsigned long) final_pc); - /* Found a function. */ - sym = lookup_symbol (func_name, NULL, VAR_DOMAIN, NULL, NULL); - if (sym && SYMBOL_LANGUAGE (sym) != language_asm) + /* Look at each instruction in the prologue. */ + while (prologue_ends_pc < final_pc) { - /* Don't use this trick for assembly source files. */ - sal = find_pc_line (func_addr, 0); - if ((sal.line != 0) && (sal.end < func_end)) - return sal.end; + struct arcDisState current_instr = arcAnalyzeInstr(prologue_ends_pc, &di); + + printInsnState(current_instr); + + /* If this instruction is in the prologue, fields in the info will be updated, + and the saved registers mask may be updated. */ + if (!is_in_prologue(info, ¤t_instr)) + { + /* Found a instruction that is not in the prologue. */ + DEBUG("End of Prologue reached \n"); + break; + } + + prologue_ends_pc += current_instr.instructionLen; } - } - -#ifdef ARC4_JTAG - skip_pc = a4_scan_prologue(pc, NULL, NULL); -#else - skip_pc = arc_scan_prologue(pc,NULL,NULL); -#endif - return skip_pc; /* End of prologue */ - //#endif -} + /* Means we were not called from arc_skip_prologue. */ + if (!((next_frame == NULL) && (info == NULL))) + { + printFrameInfo("after prologue", info, FALSE); -/* Breakpoint from pc. Return whatever is in the tdep - * structure. The tdep structure is changed depending - * on the correct target / architecture chosen. - */ + find_previous_stack_pointer(info, next_frame); -static const unsigned char * -arc_breakpoint_from_pc (CORE_ADDR *pcptr, int *lenptr) -{ + /* The PC is found in blink (the actual register or located on the stack). */ + info->saved_regs[ARC_PC_REGNUM] = info->saved_regs[ARC_BLINK_REGNUM]; - struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); - *lenptr = tdep->arc_breakpoint_size; - return tdep->arc_breakpoint_insn; + printFrameInfo("after previous SP found", info, TRUE); + } + + return prologue_ends_pc; + } } -/* Assuming NEXT_FRAME->prev is a dummy, return the frame ID of that - dummy frame. The frame ID's base needs to match the TOS value - saved by save_dummy_frame_tos(), and the PC match the dummy frame's - breakpoint. */ +/* -------------------------------------------------------------------------- */ +/* local functions for handling function return values */ +/* -------------------------------------------------------------------------- */ -static struct frame_id -arc_unwind_dummy_id (struct gdbarch *gdbarch, struct frame_info *next_frame) -{ - return frame_id_build (arc_unwind_sp (gdbarch, next_frame), - frame_pc_unwind (next_frame)); -} +/* This function gets the return value of a function from the registers used to + return it, according to the convention used by the ABI. + + Parameters: + type : the information for the return type of the function + regcache : the register cache holding the register contents + valbuf : a buffer to be filled with the return value +*/ + +static void +extract_return_value (struct type *type, + struct regcache *regcache, + gdb_byte *valbuf) -/* The workhorse : frame_unwind_cache for the ARC700 platform . - */ -static struct arc_unwind_cache * -arc_frame_unwind_cache (struct frame_info *next_frame, - void **this_prologue_cache) { - //#ifdef ARC4_JTAG -#ifdef ARC_DEBUG - printf ("\narc_frame_unwind_cache called\n "); -#endif - //#else - CORE_ADDR pc; - struct arc_unwind_cache *info; - int i; - - - if ((*this_prologue_cache)) - return (*this_prologue_cache); - - info = FRAME_OBSTACK_ZALLOC (struct arc_unwind_cache); - (*this_prologue_cache) = info; - info->saved_regs = trad_frame_alloc_saved_regs (next_frame); - - /* Zero all fields. */ - info->blink_offset = 0; - info->prev_pc = 0; - info->prev_sp = 0; - info->frame_base = 0; - info->framesize = 0; - info->sp_offset = 0; - info->fp_offset = 0; - info->prev_pc = 0; - info->is_leaf = 0; - info->uses_fp = 0; - - /* Prologue analysis does the rest... */ - /* Currently our scan prologue does not - * support getting input for the frame unwinder - */ - - pc = frame_func_unwind(next_frame); -#ifdef ARC4_JTAG - a4_scan_prologue (pc, next_frame, info); -#else - arc_scan_prologue (pc, next_frame, info); -#endif + unsigned int len = TYPE_LENGTH (type); + + ENTERMSG; + + if (len <= BYTES_IN_REGISTER) + { + ULONGEST val; + + /* Get the return value from one register. */ + regcache_cooked_read_unsigned (regcache, ARC_ABI_RETURN_REGNUM, &val); + store_unsigned_integer (valbuf, (int) len, val); + + DEBUG("returning 0x%08lX\n", (unsigned long) val); + } + else if (len <= BYTES_IN_REGISTER * 2) + { + ULONGEST low, high; + + /* Get the return value from two registers. */ + regcache_cooked_read_unsigned (regcache, ARC_ABI_RETURN_LOW_REGNUM, &low); + regcache_cooked_read_unsigned (regcache, ARC_ABI_RETURN_HIGH_REGNUM, &high); - return info; - //#endif + store_unsigned_integer (valbuf, BYTES_IN_REGISTER, low); + store_unsigned_integer (valbuf + BYTES_IN_REGISTER, (int) len - BYTES_IN_REGISTER, high); + + DEBUG("returning 0x%08lX%08lX\n", + (unsigned long) high, (unsigned long) low); + } + else + error(_("%s: type length %u too large"), __FUNCTION__, len); } +/* This function loads the return value of a function into the registers used to + return it, according to the convention used by the ABI. -/* - * Construct frame id for the normal frame - */ + Parameters: + type : the information for the return type of the function + regcache : the register cache holding the register contents + valbuf : a buffer holding the return value +*/ static void -arc_frame_this_id (struct frame_info *next_frame, - void **this_prologue_cache, - struct frame_id *this_id) +store_return_value (struct type *type, + struct regcache *regcache, + const gdb_byte *valbuf) { - // FIXMEA: cleanup #ifdef ARC4_JTAG -#ifdef ARC_DEBUG - printf ("\n arc_frame_this_id called()\n "); -#endif - //#else + unsigned int len = TYPE_LENGTH (type); + + ENTERMSG; - struct arc_unwind_cache *info - = arc_frame_unwind_cache (next_frame, this_prologue_cache); - CORE_ADDR base; - CORE_ADDR func; - struct frame_id id; + if (len <= BYTES_IN_REGISTER) + { + ULONGEST val; - /* The FUNC is easy. */ - func = frame_func_unwind (next_frame); + /* Put the return value into one register. */ + val = extract_unsigned_integer (valbuf, (int) len); + regcache_cooked_write_unsigned (regcache, ARC_ABI_RETURN_REGNUM, val); - /* This is meant to halt the backtrace at the entry point (_start). */ - if (func <= gdbarch_tdep (current_gdbarch)->lowest_pc) - return; - - /* Hopefully the prologue analysis either correctly determined the - frame's base (which is the SP from the previous frame), or set - that base to "NULL". */ - base = info->prev_sp; - if (base == 0) - return; + DEBUG("storing 0x%08lX\n", (unsigned long) val); + } + else if (len <= BYTES_IN_REGISTER * 2) + { + ULONGEST low, high; - id = frame_id_build (base, func); + /* Put the return value into two registers. */ + low = extract_unsigned_integer (valbuf, BYTES_IN_REGISTER); + high = extract_unsigned_integer (valbuf + BYTES_IN_REGISTER, (int) len - BYTES_IN_REGISTER); - (*this_id) = id; - //#endif + regcache_cooked_write_unsigned (regcache, ARC_ABI_RETURN_LOW_REGNUM, low); + regcache_cooked_write_unsigned (regcache, ARC_ABI_RETURN_HIGH_REGNUM, high); + DEBUG("storing 0x%08lX%08lX\n", + (unsigned long) high, (unsigned long) low); + } + else + error(_("arc_store_return_value: type length too large.")); } -/* - * Unwind and obtain the register information - */ -static void -arc_frame_prev_register (struct frame_info *next_frame, - void **this_prologue_cache, - int regnum, int *optimizedp, - enum lval_type *lvalp, CORE_ADDR *addrp, - int *realnump, void *bufferp) +/* -------------------------------------------------------------------------- */ +/* local functions for handling the stack frame */ +/* -------------------------------------------------------------------------- */ + +/* This is copied from file stack.c in the gdb sources. + It identifies a frame from information (such as frame number) given by the + user (in the frame_exp parameter). */ + +static struct frame_info* +parse_frame_specification_1 (const char *frame_exp, + const char *message, + int *selected_frame_p) { - // FIXMEA: - //#ifdef ARC4_JTAG -#ifdef ARC_DEBUG - printf ("\n arc_frame_prev_register() called for regnum %d\n ",regnum ); -#endif - //#else - struct arc_unwind_cache *info - = arc_frame_unwind_cache (next_frame, this_prologue_cache); + int numargs = 0; + struct value *args[4]; + CORE_ADDR addrs[ARRAY_SIZE (args)]; + if (frame_exp) + { + while (TRUE) + { + char *addr_string; + struct cleanup *cleanup; + const char *p; - /* If we are asked to unwind the PC, then we need to return blink - instead. The saved value of PC points into this frame's - prologue, not the next frame's resume location. */ -#ifdef ARC4_JTAG - if (regnum == ARC_STATUS_REGNUM) -#else - if (regnum == PC_REGNUM) -#endif - regnum = ARC_BLINK_REGNUM; + /* Skip leading white space. */ + while (isspace (*frame_exp)) + frame_exp++; + + if (*frame_exp == '\0') + break; + + /* Parse the argument, extract it, save it. */ + for (p = frame_exp; (*p != '\0') && !isspace (*p); p++); + + addr_string = savestring (frame_exp, (size_t) (p - frame_exp)); + frame_exp = p; + cleanup = make_cleanup (xfree, addr_string); + + /* NOTE: Parse and evaluate expression, but do not use + functions such as parse_and_eval_long or + parse_and_eval_address to also extract the value. + Instead value_as_long and value_as_address are used. + This avoids problems with expressions that contain + side-effects. */ + if (numargs >= (int) ARRAY_SIZE (args)) + error (_("Too many args in frame specification")); + + args[numargs++] = parse_and_eval (addr_string); + + do_cleanups (cleanup); + } + } - /* SP is generally not saved to the stack, but this frame is - identified by NEXT_FRAME's stack pointer at the time of the call. - The value was already reconstructed into PREV_SP. */ - if (regnum == ARC_SP_REGNUM) + /* If no args, default to the selected frame. */ + if (numargs == 0) { - *lvalp = not_lval; - if (bufferp) - store_unsigned_integer (bufferp, 4, info->prev_sp); - return; + if (selected_frame_p != NULL) + (*selected_frame_p) = 1; + return get_selected_frame (message); } + /* None of the remaining use the selected frame. */ + if (selected_frame_p != NULL) + (*selected_frame_p) = 0; - trad_frame_get_prev_register (next_frame, info->saved_regs, regnum, - optimizedp, lvalp, addrp, realnump, bufferp); + /* Assume the single arg[0] is an integer, and try using that to + select a frame relative to current. */ + if (numargs == 1) + { + int level = (int) value_as_long (args[0]); + struct frame_info *fid = find_relative_frame (get_current_frame (), &level); + if (level == 0) + /* find_relative_frame was successful. */ + return fid; + } -#ifdef ARC_DEBUG - printf("-*-*-*\n Regnum =%d, realnump=%d,%d \n",regnum, (char *)(bufferp), *((char*)bufferp)); -#endif - //#endif + /* Convert each value into a corresponding address. */ + { + int i; + for (i = 0; i < numargs; i++) + addrs[i] = value_as_address (args[i]); + } + + /* Assume that the single arg[0] is an address, use that to identify + a frame with a matching ID. Should this also accept stack/pc or + stack/pc/special. */ + if (numargs == 1) + { + struct frame_id id = frame_id_build_wild (addrs[0]); + struct frame_info *fid; + + /* If (s)he specifies the frame with an address, he deserves + what (s)he gets. Still, give the highest one that matches. + (NOTE: cagney/2004-10-29: Why highest, or outer-most, I don't + know). */ + for (fid = get_current_frame (); + fid != NULL; + fid = get_prev_frame (fid)) + { + if (frame_id_eq (id, get_frame_id (fid))) + { + while (frame_id_eq (id, frame_unwind_id (fid))) + fid = get_prev_frame (fid); + return fid; + } + } + } + + /* We couldn't identify the frame as an existing frame, but + perhaps we can create one with a single argument. */ + if (numargs == 1) + return create_new_frame (addrs[0], 0); + else if (numargs == 2) + return create_new_frame (addrs[0], addrs[1]); + else + error (_("Too many args in frame specification")); } +/* Return an array (and count) of the local variables and parameters declared + in the function which owns the given frame. + Parameters: + frame : the frame information for the function + variables: a pointer to an existing array of variable information (may be NULL) + count : (IN/OUT) the total number of variables found + callee : TRUE if the function is a callee of another function -static const struct frame_unwind arc_frame_unwind = { - NORMAL_FRAME, - arc_frame_this_id, - arc_frame_prev_register -}; + Result: + A pointer to an array containing one element for each parameter, and, if + 'callee' is FALSE, each local variable, of the function. */ -const struct frame_unwind * -arc_frame_sniffer (struct frame_info *next_frame) +static LocalVariable* +find_variables (struct frame_info *frame, + LocalVariable *variables, + unsigned int *count, + Boolean callee) { - return &arc_frame_unwind; + struct block *block = get_frame_block (frame, 0); + unsigned int vars = *count; + + while (block) + { + struct dict_iterator iter; + struct symbol *sym; + + ALL_BLOCK_SYMBOLS (block, iter, sym) + { + Boolean is_arg = (SYMBOL_CLASS(sym) == LOC_COMPUTED_ARG); + CORE_ADDR addr; + + switch (SYMBOL_CLASS (sym)) + { + case LOC_COMPUTED: + case LOC_COMPUTED_ARG: + addr = SYMBOL_OPS (sym)->get_variable_address(sym, frame); + + /* For callees, we are only interested in the arguments. */ + if ((addr != 0) && (!callee || is_arg)) + { + struct type *type = SYMBOL_TYPE (sym); + + vars++; + variables = xrealloc(variables, sizeof(LocalVariable) * vars); + + if (variables) + { + LocalVariable *var = &variables[vars - 1]; + + var->name = SYMBOL_PRINT_NAME (sym); + var->address = addr; + var->is_callee = callee; + var->is_argument = is_arg; + var->is_array = (TYPE_CODE (type) == TYPE_CODE_ARRAY); + var->size = SYMBOL_OPS (sym)->get_variable_size(sym, frame); + + if (var->is_array) + var->element_size = TYPE_LENGTH (TYPE_TARGET_TYPE (type)); + } + else + { + *count = 0; + return NULL; + } + } + break; + + default: + /* Ignore symbols which are not locals. */ + break; + } + } + + /* After handling the function's top-level block, stop. Don't continue + to its superblock, the block of per-file symbols. */ + if (BLOCK_FUNCTION (block)) + break; + block = BLOCK_SUPERBLOCK (block); + } + + *count = vars; + return variables; } -/* read-only registers */ -static int -arc_cannot_store_register (int regno) +/* Return an array (and count) of the local variables declared in the function + which owns the given frame, and also those declared in the function which is + the callee (if any) of that function in the current call chain. */ + +static LocalVariable* +find_local_variables (struct frame_info *frame, unsigned int *count) { - if( -#ifndef ARC4_JTAG - regno == ARC_EFA_REGNUM || - regno == ARC_ERET_REGNUM || - regno == ARC_STATUS32_L1_REGNUM || - regno == ARC_STATUS32_L2_REGNUM || - regno == ARC_ERSTATUS_REGNUM || -#endif - regno == ARC_ILINK1_REGNUM || - regno == ARC_ILINK2_REGNUM - ) + struct frame_info *callee = get_next_frame(frame); + LocalVariable *variables; + + *count = 0; + + variables = find_variables(frame, NULL, count, FALSE); + + if (callee) + variables = find_variables(callee, variables, count, TRUE); + + return variables; +} + + +/* Try to add the name of a local variable or function parameter to a line of + output, if a given address lies within the range of locations occupied by + that data item. + + Parameters: + line : the line in which any output is to be placed + location : the address of a location on the stack + variables : an array of local variables and parameters + num_variables : the number of elements in the array + is_following_element : set to TRUE if the location lies within an + array element, and it is not the 0th element + elements_are_word_sized: set to TRUE if the array element is word-sized + + Returns TRUE if the given location holds a local variable or parameter + (i.e. information has been added to the line of output). */ + +static Boolean +add_local_name (char *line, + CORE_ADDR location, + LocalVariable *variables, + unsigned int num_variables, + Boolean *is_following_element, + Boolean *elements_are_word_sized) +{ + unsigned int i; + + *is_following_element = FALSE; + *elements_are_word_sized = FALSE; + + /* Look at each of the local variables / parameters in the array. */ + for (i = 0; i < num_variables; i++) { - /* No warning should be printed. arc_cannot_store_register being - called does not imply that someone is actually writing to regnum. */ + LocalVariable *var = &variables[i]; + int index = -1; - /*warning("writing to read-only register: %s\n", arc_register_name(regno));*/ - return 1; - } - return 0; + /* is the variable an array? */ + if (var->is_array) + { + /* If we know the size of the array, and the size of its elements. */ + if (var->size > 0 && var->element_size > 0) + { + /* What is the offset of the given stack location from the start + of the array? */ + int offset = (int) ((long int) location - (long int) var->address); + + /* Does that offset actually lie within the array? */ + if (0 <= offset && offset < (int) var->size) + { + /* Compute the index of the array element which contains the + location. */ + index = offset / var->element_size; + } + + if (var->element_size == BYTES_IN_WORD) + *elements_are_word_sized = FALSE; + } + } + + /* If the variable starts in the given location, or the variable is an + array and one of its elements contains the location. */ + if (var->address == location || index >= 0) + { + int n; + + /* What is the variable? */ + if (var->is_callee) + n = sprintf(line, _("callee parameter")); + else if (var->is_argument) + n = sprintf(line, _("parameter")); + else + n = sprintf(line, _("local variable")); + + line[n] = ' '; + n++; + n += sprintf(line + n, _("'%s'"), var->name); + line[n] = ' '; + + /* If it is an array element. */ + if (index >= 0) + { + /* Add the array element index to the output. */ + (void) sprintf(line + n, _("[%u]"), index); + + /* Is it an element which follows another element? */ + *is_following_element = (index > 0); + } + else if (var->size > BYTES_IN_WORD) + { + /* It occupies more than one word. */ + (void) sprintf(line + n + 1, _("(%u words)"), + WORDS_OCCUPIED(var->size)); + } + + return TRUE; + } + } + + return FALSE; } -/* Returns true if the insn at PC is a branch. *fall_thru is the address of - the next insn. *target is the branch target. */ -static int -arc_next_pc(CORE_ADDR pc, CORE_ADDR *fall_thru, CORE_ADDR *target) + +/* Try to identify the given frame, and output that identification. */ + +static void +identify_frame (struct frame_info *frame) { -#ifdef ARC4_JTAG -#ifdef ARC_DEBUG - printf ("\narc_next_pc called\n"); -#endif -#else - struct arcDisState instr, instr_d; - int insn_length; - struct disassemble_info di; - int two_targets = 0; - - init_disassemble_info(&di, NULL, NULL); - di.arch = gdbarch_bfd_arch_info(current_gdbarch)->arch; - di.mach = gdbarch_bfd_arch_info(current_gdbarch)->mach; - di.endian = gdbarch_byte_order(current_gdbarch); - di.read_memory_func = target_read_memory; - - instr = arcAnalyzeInstr(pc, &di); - - *fall_thru = pc + instr.instructionLen; - -#ifdef ARC_DEBUG - printf("--- arc_next_pc(%x) = %x, isBranch = %d, tcnt = %d [%x], flow = %s (%d), " - "reg for indirect jump = %d, nullifyMode = %s\n", - pc, *fall_thru, instr.isBranch, instr.tcnt, instr.targets[0], - (instr.flow == direct_jump || instr.flow == direct_call) ? "direct" : "indirect", - instr.flow, - instr.register_for_indirect_jump, - (instr.nullifyMode == BR_exec_always ? "delay slot" : "no delay")); -#endif + enum language func_lang = language_unknown; + char *func_name = NULL; + char *demangled = NULL; + struct symbol *func; + struct symtab_and_line sal; - if(instr.isBranch) + find_frame_sal (frame, &sal); + func = get_frame_function (frame); + + /* Have we found the function owning the frame? */ + if (func) { - two_targets = 1; - - if(instr.flow == direct_jump || instr.flow == direct_call) - *target = instr.targets[0]; - else - regcache_cooked_read(current_regcache, - arc_binutils_reg_to_regnum(instr.register_for_indirect_jump), - target); + func_name = DEPRECATED_SYMBOL_NAME (func); + func_lang = SYMBOL_LANGUAGE (func); } + else + { + struct minimal_symbol *msymbol; - /* for instructions with delay slots, the fall thru is not the instruction - immediately after the branch instruction, but the one after that */ - if(instr.isBranch && instr.nullifyMode == BR_exec_always) + /* Try to find the symbol most closely associated with the PC + corresponding to the frame. */ + msymbol = lookup_minimal_symbol_by_pc (get_frame_pc (frame)); + + if (msymbol != NULL) + { + func_name = DEPRECATED_SYMBOL_NAME (msymbol); + func_lang = SYMBOL_LANGUAGE (msymbol); + } + } + + /* Have we found a name? */ + if (func_name) { - instr_d = arcAnalyzeInstr(*fall_thru, &di); - *fall_thru += instr_d.instructionLen; + /* If user wants to see raw output, no problem. + (demangle is a global flag which can be set by user command). */ + if (demangle) + { + demangled = language_demangle (language_def (func_lang), + func_name, + DMGL_ANSI | DMGL_PARAMS); + + /* If the demangler fails, try the demangled name from the symbol + table. That'll have parameters, but that's preferable to + displaying a mangled name. */ + if (demangled == NULL) + { + if (func == NULL) + func_name = _("<unknown function>"); + else + func_name = SYMBOL_PRINT_NAME (func); + } + else + func_name = demangled; + } } + else + func_name = _("<unknown function>"); - /* zero-overhead loops: - if(status32[L] == 0 && next_pc == lp_end && lp_count > 1) - next_pc = lp_start; - */ - { - unsigned int lp_end, lp_start, lp_count, status32; - - regcache_cooked_read(current_regcache, ARC_LP_START_REGNUM, &lp_start); - regcache_cooked_read(current_regcache, ARC_LP_END_REGNUM, &lp_end); - regcache_cooked_read(current_regcache, ARC_LP_COUNT_REGNUM, &lp_count); -#ifndef ARC4_JTAG - regcache_cooked_read(current_regcache, ARC_STATUS32_REGNUM, &status32); -#endif + printf_filtered(_("Frame of function: %s"), func_name); + if (sal.symtab) + printf_filtered(_(" (%s:%d)"), sal.symtab->filename, sal.line); + printf_filtered(NEW_LINE); - if( !(status32 & ARC_STATUS32_L) && *fall_thru == lp_end && lp_count > 1) - { - two_targets = 1; - *target = lp_start; - } - } - - return two_targets; -#endif // + if (demangled != NULL) + xfree (demangled); } -/* this is called with insert_breakpoints_p = 1 before single-stepping and - with insert_breakpoints_p = 0 after the step */ -void -arc_software_single_step(enum target_signal ignore, int insert_breakpoints_p) + +/* -------------------------------------------------------------------------- */ +/* local functions called from gdb */ +/* -------------------------------------------------------------------------- */ + +/* Standard register type for the ARC platform. + It would be builtin_type_uint32 until we consider the DSP extensions. */ + +static struct type * +arc_register_type (struct gdbarch *gdbarch, int regnum) { -#ifdef ARC4_JTAG -#ifdef ARC_DEBUG - printf ("\narc_software_single_step called\n" ); -#endif -#else - typedef char binsn_quantum[BREAKPOINT_MAX]; - - static CORE_ADDR fall_thru, branch_target; - static binsn_quantum break_mem[2]; - static char two_breakpoints; - CORE_ADDR pc; - - { -#ifdef ARC_DEBUG - unsigned int efa, ret; - regcache_cooked_read(current_regcache, ARC_EFA_REGNUM, &efa); - // regcache_cooked_read(current_regcache, ARC_RET_REGNUM, &ret); - - printf("--- arc_software_single_step, efa = %x, ret = %x, (%s)\n", efa, ret, - (insert_breakpoints_p ? "add" : "remove")); -#endif - } - - if (insert_breakpoints_p) - { - pc = read_pc (); - two_breakpoints = arc_next_pc (pc, &fall_thru, &branch_target); - - if (two_breakpoints && branch_target == pc) - { - warning ("Cannot single-step branch-to-self or single instruction zero overhead loop,\n" - " Stepping across it."); - /* Don't insert/remove the branch-target breakpoint. */ - two_breakpoints = 0; - } - - target_insert_breakpoint (fall_thru, break_mem[0]); - if(two_breakpoints) - target_insert_breakpoint (branch_target, break_mem[1]); - } - else + return builtin_type_uint32; +} + + +/* Skip the prologue for the function at pc. + Returns the address of the first instruction after the prologue. */ + +static CORE_ADDR +arc_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc) +{ + CORE_ADDR function_addr, function_end = 0; + char *function_name; + + /* This is done by checking from the line information read from the DWARF, + if possible; otherwise, we scan the function prologue to find its end. */ + + ENTERMSG; + + /* If we're in a dummy frame, don't even try to skip the prologue. */ + if (deprecated_pc_in_call_dummy (pc)) + return pc; + + /* See what the symbol table says. */ + if (find_pc_partial_function (pc, &function_name, &function_addr, &function_end)) { - target_remove_breakpoint (fall_thru, break_mem[0]); - if(two_breakpoints) - target_remove_breakpoint (branch_target, break_mem[1]); + /* Found a function. */ + struct symbol *sym = lookup_symbol (function_name, NULL, VAR_DOMAIN, NULL, NULL); + + if ((sym != NULL) && SYMBOL_LANGUAGE (sym) != language_asm) + { + /* Don't use this trick for assembly source files. */ + struct symtab_and_line sal = find_pc_line (function_addr, 0); + + if ((sal.line != 0) && (sal.end < function_end)) + return sal.end; + } } -#endif + + /* Find the address of the first instruction after the prologue by scanning + through it - no other information is needed, so pass NULL for the other + parameters. */ + return scan_prologue(pc, NULL, NULL); } -/* - * mapping from binutils/gcc register number to - * GDB register number ("regnum") - */ -static int -arc_binutils_reg_to_regnum (int reg) + +/* Construct frame id for the normal frame. */ + +static void +arc_frame_this_id (struct frame_info *next_frame, + void **this_prologue_cache, + struct frame_id *this_id) { -#ifdef ARC4_JTAG - if (reg >= 0 && reg <= 26) - return reg; - else if (reg == 27) /* fp */ - return ARC_FP_REGNUM; - else if (reg == 28) /* sp */ - return ARC_SP_REGNUM; - else if (reg == 29) /* ilink1 */ - return ARC_ILINK1_REGNUM; - else if (reg == 30) /* ilink2 */ - return ARC_ILINK2_REGNUM; - else if (reg == 31) /* blink */ - return ARC_BLINK_REGNUM; + ENTERMSG; -#else - /* from gcc/config/arc/arc.h */ - - if (reg >= 0 && reg <= 26) - return reg; - else if (reg == 27) /* fp */ - return ARC_FP_REGNUM; - else if (reg == 28) /* sp */ - return ARC_SP_REGNUM; - else if (reg == 29) /* ilink1 */ - return ARC_ILINK1_REGNUM; - else if (reg == 30) /* ilink2 */ - return ARC_ILINK2_REGNUM; - else if (reg == 31) /* blink */ - return ARC_BLINK_REGNUM; - else if (reg >= 32 && reg <= 59) /* reserved */ - ; - else if (reg == 60) /* lp_count */ - return ARC_LP_COUNT_REGNUM; - else if (reg == 61) /* reserved */ - ; - else if (reg == 62) /* no such register */ - ; -/* else if (reg == 63) /\* PCL *\/ */ -/* return ARC_RET_REGNUM; */ + /* FIXME: to what should *this_id be set if the frame base can not be found? */ + + { + /* Find the entry point of the function which owns the frame. */ + CORE_ADDR entrypoint = frame_func_unwind (next_frame, NORMAL_FRAME); + + /* This is meant to halt the backtrace at the entry point (_start) + (it assumes that there is no code at a lower address). */ + if (entrypoint > gdbarch_tdep (current_gdbarch)->lowest_pc) + { + UnwindCache *info = frame_unwind_cache (next_frame, this_prologue_cache); + CORE_ADDR base = info->prev_sp; + + /* Hopefully the prologue analysis either correctly determined the + frame's base (which is the SP from the previous frame), or set + that base to "NULL". */ + if (base != 0) + { + /* Build the ID from the frame base address. */ + *this_id = frame_id_build (base, entrypoint); + +#if 0 + printf("*** Frame ID: %x ==> (%x %x %x)\n", + base, + this_id->stack_addr, + this_id->code_addr, + this_id->special_addr); #endif - warning ("Unmapped register #%d encountered\n", reg); - return -1; + } + } + } } +/* Unwind and obtain the register information. */ + static void -arc_add_reggroups (struct gdbarch *gdbarch) +arc_frame_prev_register (struct frame_info *next_frame, + void **this_prologue_cache, + int regnum, + int *optimized, + enum lval_type *lval, + CORE_ADDR *addr, + int *realnum, + gdb_byte *buffer) { - reggroup_add (gdbarch, general_reggroup); - reggroup_add (gdbarch, all_reggroup); - reggroup_add (gdbarch, system_reggroup); + ENTERARGS("regnum %d ", regnum); + + { + UnwindCache *info = frame_unwind_cache (next_frame, this_prologue_cache); + + /* If we are asked to unwind the PC, then we need to return blink + instead: the saved value of PC points into this frame's function's prologue, + not the next frame's function's resume location. */ + if (regnum == ARC_PC_REGNUM) + regnum = ARC_BLINK_REGNUM; + + /* SP is generally not saved to the stack, but this frame is identified + by next_frame's stack pointer at the time of the call. The value was + already reconstructed into prev_sp. */ + if (regnum == ARC_SP_REGNUM) + { + /* This value is not an L-value, i.e. it can not be changed, because + it is implicit in the structure of the call-chain. */ + *lval = not_lval; + + if (buffer) + store_unsigned_integer (buffer, BYTES_IN_REGISTER, info->prev_sp); + return; + } + + trad_frame_get_prev_register (next_frame, + info->saved_regs, + regnum, + optimized, + lval, + addr, + realnum, + buffer); + + DEBUG("-*-*-*\n Regnum = %d, buffer = %p\n", regnum, buffer); + } } -int -arc_register_reggroup_p (struct gdbarch *gdbarch, int regnum, - struct reggroup *group) + +/* This function is passed to gdb to enable it to unwind frames. */ + +static const struct frame_unwind * +arc_frame_sniffer (struct frame_info *next_frame) { - struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); - int tdep_answer; - - tdep_answer = tdep->register_reggroup_p (regnum, group); - if(tdep_answer != -1) - return tdep_answer; - - if (group == all_reggroup) - return 1; - else if (group == save_reggroup || group == restore_reggroup) + static const struct frame_unwind arc_frame_unwind = { - /* don't save/restore read-only registers. */ - return (!arc_cannot_store_register(regnum)); - } - else if (group == general_reggroup) + NORMAL_FRAME, // type + arc_frame_this_id, // this_id + arc_frame_prev_register, // prev_register + NULL, // unwind_data + NULL, // sniffer + NULL, // prev_pc + NULL // dealloc_cache + }; + + return &arc_frame_unwind; +} + + +/* Get the breakpoint which is appropriate for address at which it is to be set. + + Return whatever is in the ARC-private tdep structure (this has been set up + according to the correct target / architecture chosen). + + Fortunately, the ARC processor does not have separate instruction sets (like + the ARM's normal 32-bit and 16-bit Thumb instructions), so the bp instruction + to be used does not depend on the address (although the ARC does have both + 16- and 32-bit instructions, they may be freely intermixed). */ + +static const unsigned char * +arc_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pcptr, int *lenptr) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + int end = gdbarch_byte_order (gdbarch); + + /* Return the breakpoint instruction length. */ + *lenptr = (int) tdep->breakpoint_size; + + /* Return the breakpoint instruction code. */ + if (end == BFD_ENDIAN_LITTLE) + return tdep->le_breakpoint_instruction; + if (end == BFD_ENDIAN_BIG) + return tdep->be_breakpoint_instruction; + + internal_error (__FILE__, __LINE__, "target endianness is not known"); +} + + +/* Determine whether the given register is a member of the given group. + + Returns 0, 1, or -1: + 0 means the register is not in the group. + 1 means the register is in the group. + -1 means the tdep has nothing to say about this register and group. */ + +static int +arc_register_reggroup_p (struct gdbarch *gdbarch, + int regnum, + struct reggroup *group) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + int tdep_answer = tdep->register_reggroup_p (regnum, group); + + /* If the configuration-specific tdep knows about this register. */ + if (tdep_answer != -1) + return tdep_answer; + + if (group == all_reggroup) + return 1; + + if (group == save_reggroup || group == restore_reggroup) { -#ifndef ARC4_JTAG - if (regnum == ARC_STATUS32_REGNUM) - return 0; -#endif - return 1; + /* Don't save/restore read-only registers. */ + return (!gdbarch_cannot_store_register(current_gdbarch, regnum)); } - else + + if (group == system_reggroup) { - internal_error(__FILE__, __LINE__, "bad register group"); + if (regnum == ARC_ILINK1_REGNUM || + regnum == ARC_ILINK2_REGNUM) + return 1; + + return 0; } + + internal_error(__FILE__, __LINE__, _("bad register group")); + return 0; } +/* This function is used by the DWARF-2 frame sniffer. + It is the architecture-specific register state initialization function: + it tells gdb how to find certain registers in the DWARF-2 Call Frame Information. */ static void -arc_dwarf2_frame_init_reg (struct gdbarch *gdbarch, int regnum, - struct dwarf2_frame_state_reg *reg) +arc_dwarf2_frame_init_reg (struct gdbarch *gdbarch, + int regnum, + struct dwarf2_frame_state_reg *reg, + struct frame_info *info) { -#ifdef ARC4_JTAG - // FIXMEA: Clean up. if ( debug_arc_jtag_target_message) -#ifdef ARC_DEBUG - printf ("\n arc_dwarf2_frame_init_reg called.\n Regno no:%d,0x%x\n",regnum,regnum); -#endif +// ENTERARGS("Regno no:%d, 0x%x", regnum, (unsigned int) regnum); + + /* Make sure that we know the number of the PC! */ + arc_check_pc_defined(gdbarch); + /* The return address column. */ - if (regnum == ARC_STATUS_REGNUM) - reg->how = DWARF2_FRAME_REG_RA; - - /* The call frame address. */ - if (regnum == ARC_SP_REGNUM) - reg->how = DWARF2_FRAME_REG_CFA; + if (regnum == ARC_PC_REGNUM) + reg->how = DWARF2_FRAME_REG_RA; -#else - /* The return address column. */ - if (regnum == PC_REGNUM) - reg->how = DWARF2_FRAME_REG_RA; - - /* The call frame address. */ - if (regnum == ARC_SP_REGNUM) - reg->how = DWARF2_FRAME_REG_CFA; -#endif + /* The call frame address. */ + if (regnum == ARC_SP_REGNUM) + reg->how = DWARF2_FRAME_REG_CFA; } + +/* Unwind the frame to find the previous frame's PC. */ + static CORE_ADDR arc_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame) { - ULONGEST pc; -#ifdef ARC4_JTAG - frame_unwind_unsigned_register (next_frame, ARC_STATUS_REGNUM, &pc); - pc = pc & 0x00ffffff; - pc = pc << 2; -#else - frame_unwind_unsigned_register (next_frame, PC_REGNUM, &pc); -#endif - return pc; + ULONGEST pc; + + /* Make sure that we know the number of the PC! */ + arc_check_pc_defined(gdbarch); + + pc = frame_unwind_register_unsigned (next_frame, ARC_PC_REGNUM); + + DEBUG("unwind PC: 0x%08lx\n", (unsigned long) pc); + + return (CORE_ADDR) pc; } + +/* Unwind the frame to find the previous frame's SP. */ + static CORE_ADDR arc_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame) { - ULONGEST sp; - frame_unwind_unsigned_register (next_frame, SP_REGNUM, &sp); - return sp; -} + ULONGEST sp; + sp = frame_unwind_register_unsigned (next_frame, ARC_SP_REGNUM); + DEBUG("unwind SP: 0x%08lx\n", (unsigned long) sp); -static void -arc_extract_return_value (struct type *type, struct regcache *regcache, - void *valbuf) -{ - //#ifdef ARC4_JTAG -#ifdef ARC_DEBUG - printf ("\narc_extract_return_value called\n"); -#endif - //#else - ULONGEST val; - int len = TYPE_LENGTH (type); - - if (len <= 4) - { - /* Get the return value from R0. */ - regcache_cooked_read_unsigned (regcache, ARC_RETURN1_REGNUM, &val); - store_unsigned_integer (valbuf, len, val); - } - else if (len <= 8) - { - /* Get the return value from R0 and R1. */ - /* R0 holds the lower-order bytes */ - regcache_cooked_read_unsigned (regcache, ARC_RETURN1_REGNUM, &val); - store_unsigned_integer (valbuf, 4, val); - regcache_cooked_read_unsigned (regcache, ARC_RETURN2_REGNUM, &val); - store_unsigned_integer ((char *)valbuf + 4, len - 4, val); - } - else - error ("arc_extract_return_value: type length too large"); - //#endif + return (CORE_ADDR) sp; } -static void -arc_store_return_value (struct type *type, struct regcache *regcache, - const void *valbuf) -{ - //#ifdef ARC4_JTAG -#ifdef ARC_DEBUG - printf ("\narc_store_return_value called\n "); -#endif - //#else - ULONGEST val; - int len = TYPE_LENGTH (type); - - if (len <= 4) - { - /* Put the return value in R0. */ - val = extract_unsigned_integer (valbuf, len); - regcache_cooked_write_unsigned (regcache, ARC_RETURN1_REGNUM, val); - } - else if (len <= 8) - { - /* Put the return value in R10 and R11. */ - val = extract_unsigned_integer (valbuf, 4); - regcache_cooked_write_unsigned (regcache, ARC_RETURN1_REGNUM, val); - val = extract_unsigned_integer ((char *)valbuf + 4, len - 4); - regcache_cooked_write_unsigned (regcache, ARC_RETURN2_REGNUM, val); - } - else - error ("arc_store_return_value: type length too large."); - //#endif -} +/* This function returns the convention used by the ABI for returning a result + of the given type from a function; it may also be required to: + + a) set the return value (this is for the situation where the debugger user + has issued a "return <value>" command to request immediate return from + the current function with the given result; or + + b) get the return value ((this is for the situation where the debugger user + has executed a "call <function>" command to execute the specified + function in the target program, and that function has a non-void result + which must be returned to the user. + + Parameters: + gdbarch : gdbarch structure for the backend to use if needed + valtype : the information for the return type of the function + regcache: the register cache to be used for altered register values + readbuf : if non-NULL, read the return value into this buffer + writebuf: if non-NULL, write the return value from this buffer + */ static enum return_value_convention -arc_return_value (struct gdbarch *gdbarch, struct type *valtype, - struct regcache *regcache, void *readbuf, - const void *writebuf) +arc_return_value (struct gdbarch *gdbarch, + struct type *valtype, + struct regcache *regcache, + gdb_byte *readbuf, + const gdb_byte *writebuf) { - //#ifdef ARC4_JTAG -#ifdef ARC_DEBUG - printf ("\narc_return_value called"); -#endif - //#else - /* This will change with the ABI */ - int struct_return = (TYPE_CODE (valtype) == TYPE_CODE_STRUCT || - TYPE_CODE (valtype) == TYPE_CODE_UNION || - TYPE_LENGTH (valtype) > 8); - - - if (writebuf != NULL) - { - gdb_assert (!struct_return); - arc_store_return_value (valtype, regcache, writebuf); - } + ENTERARGS("readbuf = %p, writebuf = %p", readbuf, writebuf); - if (readbuf != NULL) { - gdb_assert (!struct_return); - arc_extract_return_value (valtype, regcache, readbuf); + /* If the return type is a struct, or a union, or would occupy more + than two registers, the ABI uses the "struct return convention": the + calling function passes a hidden first parameter to the callee (in R0). + That parameter is the address at which the value being returned + should be stored. Otherwise, the result is returned in registers. */ + Boolean is_struct_return = (TYPE_CODE (valtype) == TYPE_CODE_STRUCT || + TYPE_CODE (valtype) == TYPE_CODE_UNION || + TYPE_LENGTH (valtype) > 2 * BYTES_IN_REGISTER); + + /* case a) */ + if (writebuf != NULL) + { + /* gdb should not ask us to set a struct return value: it should + know the struct return location and write the value there + itself. */ + gdb_assert (!is_struct_return); + store_return_value (valtype, regcache, writebuf); + } + + /* case b) */ + if (readbuf != NULL) + { + /* gdb should not ask us to get a struct return value: it should + know the struct return location and read the value from there + itself. */ + gdb_assert (!is_struct_return); + extract_return_value (valtype, regcache, readbuf); + } + + return (is_struct_return) ? RETURN_VALUE_STRUCT_CONVENTION + : RETURN_VALUE_REGISTER_CONVENTION; } +} + + +/* Assuming that next_frame->prev is a dummy, return the frame ID of that dummy + frame. The frame ID's base needs to match the TOS value saved by + save_dummy_frame_tos() (!!!! WHAT IS THIS???), and the PC to match the dummy + frame's breakpoint. */ - if (struct_return) - return RETURN_VALUE_STRUCT_CONVENTION; - else - return RETURN_VALUE_REGISTER_CONVENTION; - //#endif +static struct frame_id +arc_unwind_dummy_id (struct gdbarch *gdbarch, struct frame_info *next_frame) +{ + return frame_id_build (arc_unwind_sp (gdbarch, next_frame), + frame_pc_unwind (next_frame)); } -/* Signal Trampoline Frame Unwinder. These - * unwinders allow frame unwinding to happen - * from within signal handlers. - */ -static struct arc_unwind_cache * +/* Signal Trampoline Frame Unwinder. + These unwinders allow frame unwinding to happen from within signal handlers. */ + +static UnwindCache * arc_sigtramp_frame_cache (struct frame_info *next_frame, - void **this_cache) + void **this_cache) { - // FIXMEA: cleanup#ifdef ARC4_JTAG -#ifdef ARC_DEBUG - printf ("\narc_sigtramp_frame_cache called"); -#endif - //#else - struct arc_unwind_cache *cache; - struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); - CORE_ADDR addr; - char buf[4]; - - if (*this_cache) - return *this_cache; + ENTERMSG; - cache = FRAME_OBSTACK_ZALLOC (struct arc_unwind_cache); - (*this_cache) = cache; - cache->saved_regs = trad_frame_alloc_saved_regs (next_frame); - - /* Zero all fields. */ - cache->blink_offset = 0; - cache->prev_pc = 0; - cache->prev_sp = 0; - cache->frame_base = 0; - cache->framesize = 0; - cache->sp_offset = 0; - cache->fp_offset = 0; - cache->prev_pc = 0; - cache->is_leaf = 0; - cache->uses_fp = 0; - - - frame_unwind_register (next_frame, SP_REGNUM, buf); - cache->frame_base = extract_unsigned_integer (buf, 4); - - addr = tdep->sigcontext_addr (next_frame); - if (tdep->sc_reg_offset) + if (*this_cache == NULL) { - int i; - - for (i = 0; i < tdep->sc_num_regs; i++) - if (tdep->sc_reg_offset[i] != -1) - cache->saved_regs[i].addr = addr + tdep->sc_reg_offset[i]; + struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); + UnwindCache *cache = create_cache(next_frame); + gdb_byte buf[BYTES_IN_REGISTER]; + + *this_cache = cache; + + /* Get the stack pointer and use it as the frame base. */ + frame_unwind_register (next_frame, ARC_SP_REGNUM, buf); + cache->frame_base = (CORE_ADDR) extract_unsigned_integer (buf, BYTES_IN_REGISTER); + + /* If the ARC-private target-dependent info has a table of offsets of + saved register contents within a O/S signal context structure. */ + if (tdep->sc_reg_offset) + { + /* Find the address of the sigcontext structure. */ + CORE_ADDR addr = tdep->sigcontext_addr (next_frame); + unsigned int i; + + /* For each register, if its contents have been saved within the + sigcontext structure, determine the address of those contents. */ + for (i = 0; i < tdep->sc_num_regs; i++) + if (tdep->sc_reg_offset[i] != REGISTER_NOT_PRESENT) + cache->saved_regs[i].addr = (LONGEST) (addr + tdep->sc_reg_offset[i]); + } } - return cache; - //#endif + return *this_cache; } + +/* Construct id for the given frame. */ + static void -arc_sigtramp_frame_this_id (struct frame_info *next_frame, void **this_cache, - struct frame_id *this_id) +arc_sigtramp_frame_this_id (struct frame_info *next_frame, + void **this_cache, + struct frame_id *this_id) { - //FIXMEA: cleanup #ifdef ARC4_JTAG -#ifdef ARC_DEBUG - printf ("\narc_sigtramp_frame_this_id called"); -#endif - //#else - struct arc_unwind_cache *cache = - arc_sigtramp_frame_cache (next_frame, this_cache); - - (*this_id) = frame_id_build (cache->frame_base, frame_pc_unwind (next_frame)); - //#endif + UnwindCache *cache; + + ENTERMSG; + + cache = arc_sigtramp_frame_cache (next_frame, this_cache); + + *this_id = frame_id_build (cache->frame_base, frame_pc_unwind (next_frame)); } + +/* Retrieve the value of the register in the frame. */ + static void arc_sigtramp_frame_prev_register (struct frame_info *next_frame, - void **this_cache, - int regnum, int *optimizedp, - enum lval_type *lvalp, CORE_ADDR *addrp, - int *realnump, void *valuep) + void **this_cache, + int regnum, + int *optimizedp, + enum lval_type *lvalp, + CORE_ADDR *addrp, + int *realnump, + gdb_byte *valuep) { - // FIXMEA: cleanup#ifdef ARC4_JTAG -#ifdef ARC_DEBUG - printf ("\narc_sigtramp_frame_prev_register called"); -#endif - //#else - struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); - - /* Make sure we've initialized the cache. */ - struct arc_unwind_cache *cache = - arc_sigtramp_frame_cache (next_frame, this_cache); + ENTERMSG; - /* on a signal, the PC is in ret */ -#ifdef ARC4_JTAG - if (regnum == ARC_STATUS_REGNUM) -#else - if(regnum == PC_REGNUM) -#endif - regnum = tdep->pc_regnum_in_sigcontext; - - trad_frame_get_prev_register (next_frame, cache->saved_regs, regnum, - optimizedp, lvalp, addrp, realnump, valuep); - //#endif + { + struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); + + /* Make sure we've initialized the cache. */ + UnwindCache *cache = arc_sigtramp_frame_cache (next_frame, this_cache); + + /* On a signal, the PC is in ret. */ + if (regnum == ARC_PC_REGNUM) + regnum = tdep->pc_regnum_in_sigcontext; + + trad_frame_get_prev_register (next_frame, + cache->saved_regs, + regnum, + optimizedp, + lvalp, + addrp, + realnump, + valuep); + } } -static const struct frame_unwind arc_sigtramp_frame_unwind = -{ - SIGTRAMP_FRAME, - arc_sigtramp_frame_this_id, - arc_sigtramp_frame_prev_register -}; - -const struct frame_unwind * +static const struct frame_unwind * arc_sigtramp_frame_sniffer (struct frame_info *next_frame) { - //FIXMEA: cleanup#ifdef ARC4_JTAG -#ifdef ARC_DEBUG - printf ("\narc_sigtramp_frame_sniffer called() "); -#endif - //#else - CORE_ADDR pc = frame_pc_unwind (next_frame); - struct gdbarch_tdep *tdep = gdbarch_tdep (get_frame_arch (next_frame)); + ENTERMSG; - /* We shouldn't even bother if we don't have a sigcontext_addr - handler. */ - if (tdep->sigcontext_addr == NULL) - return NULL; - - if (tdep->sigtramp_p != NULL) { - if (tdep->sigtramp_p (next_frame)) - { - return &arc_sigtramp_frame_unwind; - } - } + struct gdbarch_tdep *tdep = gdbarch_tdep (get_frame_arch (next_frame)); - return NULL; - //#endif -} + /* We don't even bother if we don't have a sigcontext_addr handler. */ + if ((tdep->sigcontext_addr != NULL) && + (tdep->is_sigtramp != NULL) && + tdep->is_sigtramp (next_frame)) + { + static const struct frame_unwind arc_sigtramp_frame_unwind = + { + SIGTRAMP_FRAME, // type + arc_sigtramp_frame_this_id, // this_id + arc_sigtramp_frame_prev_register, // prev_register + NULL, // unwind_data + NULL, // sniffer + NULL, // prev_pc + NULL // dealloc_cache + }; + + return &arc_sigtramp_frame_unwind; + } + return NULL; + } +} -/* Allow calls to be made to functions defined in the debuggee. - a.k.a dummy calls -*/ -/* When arguments must be pushed onto the stack, they go on in reverse - order. The below implements a FILO (stack) to do this. - Copied from d10v-tdep.c. */ +/* Allow calls to be made to functions defined in the debuggee (A.K.A dummy calls). -struct stack_item -{ - int len; - struct stack_item *prev; - void *data; -}; + Parameters: + gdbarch : gdbarch structure for the backend to use if needed. + function : the function to be called + regcache : the register cache to be used for altered register values + bp_addr : return address for the breakpoint. + nargs : the number of arguments to the function + args : the arguments to be passed to the function + sp : current value of SP. + struct_return: 1 if structures are returned by the function. + struct_addr : hidden address for returning a struct. */ -static struct stack_item * -push_stack_item (struct stack_item *prev, void *contents, int len) -{ - struct stack_item *si; - si = xmalloc (sizeof (struct stack_item)); - si->data = xmalloc (len); - si->len = len; - si->prev = prev; - memcpy (si->data, contents, len); - return si; -} +static CORE_ADDR +arc_push_dummy_call (struct gdbarch *gdbarch, + struct value *function, + struct regcache *regcache, + CORE_ADDR bp_addr, + int nargs, + struct value **args, + CORE_ADDR sp, + int struct_return, + CORE_ADDR struct_addr) -static struct stack_item * -pop_stack_item (struct stack_item *si) { - struct stack_item *dead = si; - si = si->prev; - xfree (dead->data); - xfree (dead); - return si; -} + int arg_reg = ARC_ABI_FIRST_ARGUMENT_REGISTER; + ENTERARGS("nargs = %d, struct_return = %d", nargs, struct_return); + /* Push the return address. */ + regcache_cooked_write_unsigned (regcache, ARC_BLINK_REGNUM, bp_addr); + DEBUG("set BLINK = %X\n", (unsigned int) bp_addr); -/* arc_push_dummy_call : - * gdbarch : gdbarch structure for the backend to use if needed. - * function : - * regcache : - * bp_addr : Return address for the breakpoint. - * sp : Current value of sp. - * struct_return: struct_return is 1 if structures are returned by - * the function. - * struct_addr: Hidden address for returning a struct. - */ + /* Are we returning a value using a structure return instead of a normal + value return? If so, struct_addr is the address of the reserved space + for the return structure to be written on the stack, and that address + is passed to that function as a hidden first argument. */ + if (struct_return) + { + /* Pass the return address in the first argument register. */ + regcache_cooked_write_unsigned (regcache, arg_reg, struct_addr); -static CORE_ADDR -arc_push_dummy_call(struct gdbarch *gdbarch, struct value *function, - struct regcache *regcache, CORE_ADDR bp_addr, int nargs, - struct value **args, CORE_ADDR sp, int struct_return, - CORE_ADDR struct_addr) + DEBUG("struct return address 0x%08lX passed in R%d", struct_addr, arg_reg); -{ - //#ifdef ARC4_JTAG -#ifdef ARC_DEBUG - printf ("\narc_push_dummy_call called"); -#endif - // #else - int stack_alloc; - int stack_offset; - int argreg; - int argnum; - - CORE_ADDR regval; - struct stack_item *si = NULL; - - - /* Push the return address. */ -#ifdef ARC4_JTAG - CORE_ADDR modified_bp_addr; - modified_bp_addr = arc_debug_fetch_regs(ARC_STATUS_REGNUM); - regcache_raw_collect(regcache, ARC_STATUS_REGNUM, &modified_bp_addr); - modified_bp_addr = modified_bp_addr & 0xff000000; - bp_addr = bp_addr >>2; - modified_bp_addr |= bp_addr; - regcache_cooked_write_unsigned (regcache, ARC_BLINK_REGNUM, modified_bp_addr); -#else - regcache_cooked_write_unsigned (regcache, ARC_BLINK_REGNUM, bp_addr); -#endif + arg_reg++; + } - /* Are we returning a value using a structure return or a normal value - return? struct_addr is the address of the reserved space for the return - structure to be written on the stack. - */ - /* FIXME:: Ramana :: What about 4 byte structures returned in r0 as - claimed by Metaware. - */ - - /* Now load as many as possible of the first arguments into registers, - and push the rest onto the stack. */ - argreg = ARC_ARG0_REGNUM; - - if (struct_return) + /* If the function has arguments. */ + if (nargs > 0) { - regcache_cooked_write_unsigned (regcache, ARC_ARG0_REGNUM, struct_addr); - argreg++; -#ifdef ARC4_JTAG - sp = sp - 16; -#endif - } + Boolean big_endian = gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG; + unsigned int total_space = 0; + gdb_byte *memory_image; + gdb_byte *data; + int i; + + /* How much space do the arguments occupy in total? + N.B. must round each argument's size up to an integral number of words. */ + for (i = 0; i < nargs; i++) + { + unsigned int len = TYPE_LENGTH (value_type (args[i])); + unsigned int space = ROUND_UP_TO_WORDS(len); - stack_offset = 0; + total_space += space; - for (argnum = 0; argnum < nargs; argnum++) - { - int len; - char *val; - int reg_demand; - int i; - - len = TYPE_LENGTH (VALUE_TYPE (args[argnum])); - val = (char *) VALUE_CONTENTS (args[argnum]); - - /* How may registers worth of storage do we need for this argument? */ - reg_demand = (len / 4) + (len % 4 != 0 ? 1 : 0); - - if (argreg + reg_demand - 1 <= ARC_ARG7_REGNUM) + DEBUG("function arg[%d]: %d bytes -> %d\n", i, len, space); + } + + /* Allocate a buffer to hold a memory image of the arguments. */ + memory_image = XCALLOC((size_t) total_space, gdb_byte); + if (memory_image == NULL) { - /* Data passed by value. Fits in available register(s). */ - for (i = 0; i < reg_demand; i++) - { - regcache_cooked_write_unsigned (regcache, argreg, - *(unsigned long *) val); - argreg++; - val += 4; - } + /* could not do the call! */ + return 0; } - else if (argreg <= ARC_ARG7_REGNUM) + + /* Now copy all of the arguments into the buffer, correctly aligned. */ + data = memory_image; + for (i = 0; i < nargs; i++) { - /* Data passed by value. Does not fit in available register(s). - Use the register(s) first, then the stack. */ - for (i = 0; i < reg_demand; i++) - { - if (argreg <= ARC_ARG7_REGNUM) - { - regcache_cooked_write_unsigned (regcache, argreg, - *(unsigned long *) val); - argreg++; - val += 4; - } - else - { - /* Push item for later so that pushed arguments - come in the right order. */ - si = push_stack_item (si, val, 4); - val += 4; - } - } + unsigned int len = TYPE_LENGTH (value_type (args[i])); + unsigned int space = ROUND_UP_TO_WORDS(len); + gdb_byte* actual = data; + unsigned int w; + + /* Parameters smaller than a word are normally aligned by gcc to the + least significant byte, so if we have a big-endian target we put + the data at the end of the word rather than at the start. */ + if ((len < BYTES_IN_WORD) && big_endian) + actual += BYTES_IN_WORD - len; + + (void) memcpy(actual, value_contents (args[i]), (size_t) len); + + DEBUG("function arg[%d] =", i); + for (w = 0; w < space / BYTES_IN_WORD; w++) + DEBUG(" 0x%08X", *((int*) data + w)); + DEBUG("\n"); + + data += space; } - else if (len > (2 * 4)) + + /* Now load as much as possible of the memory image into registers. */ + data = memory_image; + while (arg_reg <= ARC_ABI_LAST_ARGUMENT_REGISTER) { - /* FIXME */ - internal_error (__FILE__, __LINE__, "We don't do this"); + ARC_RegisterContents contents = *(ARC_RegisterContents*) data; + + DEBUG("passing 0x%08X in register R%d\n", (unsigned int) contents, arg_reg); + + /* Convert to target byte order if necessary. */ + if (HOST_AND_TARGET_ENDIANNESS_DIFFER(gdbarch)) + { + contents = __bswap_32(contents); + DEBUG("byte-swapped to 0x%08X\n", contents); + } + + regcache_cooked_write_unsigned (regcache, + arg_reg, + (ULONGEST) contents); + + data += BYTES_IN_REGISTER; + total_space -= BYTES_IN_REGISTER; + + /* If all the data is now in registers. */ + if (total_space == 0) + break; + + arg_reg++; } - else + + /* If there is any data left, push it onto the stack (in a single write operation). */ + if (total_space > 0) { - /* Data passed by value. No available registers. Put it on - the stack. */ - si = push_stack_item (si, val, len); + DEBUG("passing %d bytes on stack\n", total_space); + + sp -= total_space; + write_memory (sp, data, (int) total_space); } - } - while (si) - { - /* fp_arg must be word-aligned (i.e., don't += len) to match - the function prologue. */ - sp = (sp - si->len) & ~3; -#ifdef ARC4_JTAG - write_memory (sp + 16, si->data, si->len); -#else - write_memory (sp, si->data, si->len); -#endif - si = pop_stack_item (si); + xfree(memory_image); } - /* Finally, update the SP register. */ - regcache_cooked_write_unsigned (regcache, ARC_SP_REGNUM, sp); + /* Finally, update the SP register. */ + regcache_cooked_write_unsigned (regcache, ARC_SP_REGNUM, sp); + + DEBUG("set SP = %X\n", (unsigned int) sp); - return sp; - //#endif + return sp; } -/* Align Frame */ + +/* Align frame. */ + static CORE_ADDR arc_frame_align (struct gdbarch *gdbarch, CORE_ADDR sp) { - /* Align to the normal alignment on the stack). */ - return sp & ~3; + /* Align to the normal alignment on the stack). */ + return WORD_ALIGNED(sp); } /* Print interesting information about the floating point processor (if present) or emulator. */ + static void arc_print_float_info (struct gdbarch *gdbarch, struct ui_file *file, - struct frame_info *frame, const char *args) + struct frame_info *frame, const char *args) { - printf("Software FPU \n"); + printf(_("Software FPU\n")); } /* Set the main_name to "_main" if required. This is set as an observer of inferior_created. */ + static void arc_set_main_name (struct target_ops *objfile, int from_tty) { - struct minimal_symbol *umainsym, *mainsym; - - /* Old ARC toolchains prepend an underscore to symbol names. If there is - an _main but no main, then we're probably debugging a binary that was - made with the old toolchain. */ - umainsym = lookup_minimal_symbol ("_main", NULL, NULL); - mainsym = lookup_minimal_symbol ("main", NULL, NULL); - if(umainsym && !mainsym) + /* Old ARC toolchains prepend an underscore to symbol names. If there is + an _main but no main, then we're probably debugging a binary that was + made with the old toolchain. */ + struct minimal_symbol *umainsym = lookup_minimal_symbol ("_main", NULL, NULL); + struct minimal_symbol *mainsym = lookup_minimal_symbol ("main", NULL, NULL); + + if ((umainsym != NULL) && (mainsym == NULL)) { - set_main_name ("_main"); + set_main_name ("_main"); } - /* If we don't have any symbols, the default, i.e. "main", will get used. */ + /* If we don't have any symbols, the default, i.e. "main", will get used. */ } -/* The following piece of code is borrowed from d10v */ -static void -a4_address_to_pointer (struct type *type, void *buf, CORE_ADDR addr) +/* This initialization function is called by gdb. + See gdbarch.h for a description of its parameters. */ + +static struct gdbarch* +arc_gdbarch_init (struct gdbarch_info info, + struct gdbarch_list *arches) { -#ifdef ARC4_JTAG - if (TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_FUNC - || TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_METHOD) - store_unsigned_integer (buf, TYPE_LENGTH (type), (addr>>2) & 0xffffff); - else - store_unsigned_integer (buf, TYPE_LENGTH (type), addr); + /* Allocate the ARC-private target-dependent information structure, and the + gdb target-independent information structure. */ + struct gdbarch_tdep *tdep = xmalloc (sizeof (struct gdbarch_tdep)); + struct gdbarch *gdbarch = gdbarch_alloc (&info, tdep); + + memset(tdep, 0, sizeof(*tdep)); + + /* Put stuff in gdbarch. */ + + /* Breakpoint manipulation. */ + set_gdbarch_breakpoint_from_pc (gdbarch, arc_breakpoint_from_pc); + set_gdbarch_decr_pc_after_break (gdbarch, 0); + + info.osabi = CONFIG_OSABI; + gdbarch_init_osabi(info, gdbarch); + + /* Characters are unsigned by default. */ + set_gdbarch_char_signed (gdbarch, 0); + + set_gdbarch_sp_regnum (gdbarch, ARC_SP_REGNUM); + set_gdbarch_register_type (gdbarch, arc_register_type); + set_gdbarch_print_float_info (gdbarch, arc_print_float_info); + + /* Advance PC across function entry code. */ + set_gdbarch_skip_prologue (gdbarch, arc_skip_prologue); + + /* Hook in the Dwarf-2 frame sniffer. */ + dwarf2_frame_set_init_reg (gdbarch, arc_dwarf2_frame_init_reg); + frame_unwind_append_sniffer (gdbarch, dwarf2_frame_sniffer); + + /* Signal frames. */ + frame_unwind_append_sniffer (gdbarch, arc_sigtramp_frame_sniffer); + + /* The stack grows downward. */ + set_gdbarch_inner_than (gdbarch, core_addr_lessthan); + set_gdbarch_unwind_pc (gdbarch, arc_unwind_pc); + set_gdbarch_unwind_sp (gdbarch, arc_unwind_sp); + set_gdbarch_unwind_dummy_id (gdbarch, arc_unwind_dummy_id); + frame_unwind_append_sniffer (gdbarch, arc_frame_sniffer); + set_gdbarch_return_value (gdbarch, arc_return_value); + + /* Add the ARC register groups. */ + reggroup_add (gdbarch, general_reggroup); + reggroup_add (gdbarch, all_reggroup); + reggroup_add (gdbarch, system_reggroup); + + set_gdbarch_register_reggroup_p (gdbarch, arc_register_reggroup_p); + + set_gdbarch_cannot_step_breakpoint (gdbarch, 1); + set_gdbarch_frame_align(gdbarch, arc_frame_align); + + /* Dummy Frame handling. */ + set_gdbarch_push_dummy_call (gdbarch, arc_push_dummy_call); + set_gdbarch_call_dummy_location (gdbarch, AT_ENTRY_POINT); + + /* N.B. we do not want to call arcompact_get_disassembler at this point, as we do + not as yet have an executable file, so do not know whether we want + the disassembler for the A4 architecture (still supported by the + opcodes library!) or the one for the ARCompact architecture; also, + it is better to pass the arcompact_get_disassembler function a valid bfd + structure, rather than a faked one, so that it can read the ARC- + specific sections in the ELF file (for whatever reason...). + + So just set up a dummy disassembler at this point (gdb requires that + *some* disassembler is defined for an architecture), and set up a + callback which will set up the appropriate disassembler once an + executable file has been selected for debugging. */ + +#if 0 + { + /* The arc libopcodes wants abfd so that it can find out what CPU + extensions are there. */ + bfd abfd; + + abfd.sections = NULL; + + set_gdbarch_print_insn(gdbarch, arcompact_get_disassembler(&abfd)); + } #endif + set_gdbarch_print_insn(gdbarch, dummy_disassembler); + (void) observer_attach_new_objfile(set_disassembler); + + /* Set main_name to _main if necessary. Ideally we'd want a hook that + gets called when symbols are loaded, but it seems there isn't one; so + we'll use this. This will not work if the user does "target remote + ..." and then "add-symbol-file ...". */ + (void) observer_attach_inferior_created (arc_set_main_name); + + /* Initialize the target-dependent modules (if any). */ + CONFIG_INIT_TDEP + + /* Return a pointer to the new object - indicates that a new architecture + has been created. */ + return gdbarch; } -static CORE_ADDR -a4_pointer_to_address (struct type *type, const void *buf) + +static void +arc_dump_tdep (struct gdbarch *current_gdbarch, struct ui_file *file) { -#ifdef ARC4_JTAG - CORE_ADDR addr = extract_unsigned_integer (buf, TYPE_LENGTH(type)); - /* Is it a code address? */ - if (TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_FUNC - || TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_METHOD - || TYPE_CODE_SPACE (TYPE_TARGET_TYPE (type))) - return ((addr<<2) & 0x2ffffff); - else - return addr; -#endif + /* Do nothing. */ } -static struct gdbarch * -arc_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) + +/* -------------------------------------------------------------------------- */ +/* local functions implementing commands */ +/* -------------------------------------------------------------------------- */ + +/* <command> [ <frame> ] + Display annotated stack frame. */ + +static void +arc_show_stack_frame_command (char *arg, int from_tty) { - struct gdbarch_tdep *tdep; - struct gdbarch *gdbarch; + struct frame_info *fi; + gdb_byte value[MAX_REGISTER_SIZE]; + CORE_ADDR pc, fp, sp, old_sp; + CORE_ADDR addr; + enum lval_type lval; + int optimized; + int realnum; + int frame_size; + unsigned int *frame_contents; + LocalVariable *variables; + unsigned int num_variables; + + /* Find the frame. */ + fi = parse_frame_specification_1 (arg, _("No stack."), NULL); + gdb_assert(fi != NULL); + + /* Find out PC, FP and SP for the frame. */ + pc = get_frame_pc(fi); + sp = get_frame_sp(fi); + get_frame_register(fi, ARC_FP_REGNUM, value); + fp = (CORE_ADDR) extract_unsigned_integer (value, BYTES_IN_REGISTER); + + DEBUG("*** PC = %x, FP = %x, SP = %x\n", (unsigned int) pc, (unsigned int) fp, (unsigned int) sp); + + if (pc == 0) + { + warning(_("invalid frame")); + return; + } - tdep = xmalloc (sizeof (struct gdbarch_tdep)); - gdbarch = gdbarch_alloc (&info, tdep); - - /* Fixme :: Worry about default initialization of breakpoints - for the ARC platform. In our case currently this is handled - out of arc-linux-tdep.c for default arc linux breakpoints. - */ + frame_register_unwind (fi, ARC_SP_REGNUM, &optimized, &lval, &addr, &realnum, value); + old_sp = (CORE_ADDR) extract_unsigned_integer (value, BYTES_IN_REGISTER); - info.osabi = CONFIG_OSABI; - gdbarch_init_osabi(info, gdbarch); - - /* Put stuff in gdbarch. */ + /* Find the local variables and parameters. */ + variables = find_local_variables(fi, &num_variables); - /* Characters are unsigned by default */ - set_gdbarch_char_signed (gdbarch, 0); + printf_filtered(NEW_LINE); + identify_frame(fi); - set_gdbarch_print_float_info (gdbarch, arc_print_float_info); - set_gdbarch_sp_regnum (gdbarch, ARC_SP_REGNUM); - set_gdbarch_register_type (gdbarch, arc_register_type); + DEBUG(_("\n\n*** FRAME: 0x%x .. 0x%x\n\n"), (unsigned int) sp, (unsigned int) (old_sp - 1)); - set_gdbarch_cannot_store_register (gdbarch, arc_cannot_store_register); + frame_size = (int) (old_sp - sp); + frame_contents = xmalloc((size_t) frame_size); - /* Advance PC across function entry code. */ - set_gdbarch_skip_prologue (gdbarch, arc_skip_prologue); + if (frame_contents) + { + /* Read all of the frame's contents from target memory. */ + if (target_read_memory(sp, (gdb_byte*) frame_contents, frame_size) == 0) + { + int numregs = ARC_TOTAL_REGS; + int i = frame_size / BYTES_IN_WORD - 1; + unsigned int the_same = 0; + unsigned int previous_word = 0; + Boolean first_word = TRUE; + Boolean is_following_element; + Boolean elements_are_word_sized; - - /* Hook in the Dwarf-2 frame sniffer. */ - set_gdbarch_dwarf2_reg_to_regnum (gdbarch, arc_binutils_reg_to_regnum); - dwarf2_frame_set_init_reg (gdbarch, arc_dwarf2_frame_init_reg); - frame_unwind_append_sniffer (gdbarch, dwarf2_frame_sniffer); + addr = old_sp - BYTES_IN_WORD; - /* signal frames */ - frame_unwind_append_sniffer (gdbarch, arc_sigtramp_frame_sniffer); + printf_filtered(NEW_LINE); + /* Traverse the frame from high address to low address, one word at a time. */ + while (i >= 0) + { + unsigned int this_word = frame_contents[i]; + Boolean print = first_word; /* Always print the first word. */ + char line[256]; + int n; + /* Start with a blank line. */ + (void) memset(line, (int) ' ', sizeof(line)); - /* The stack grows downward. */ - set_gdbarch_inner_than (gdbarch, core_addr_lessthan); + /* Does FP and/or SP address this particular stack location? */ + if (addr == sp && fp == sp) + { + (void) strcpy(line + 14, _("SP/FP ===> ")); + line[25] = ' '; + print = TRUE; + } + else if (addr == sp) + { + (void) strcpy(line + 17, _("SP ===> ")); + line[25] = ' '; + print = TRUE; + } + else if (addr == fp) + { + (void) strcpy(line + 17, _("FP ===> ")); + line[25] = ' '; + print = TRUE; + } - set_gdbarch_unwind_pc (gdbarch, arc_unwind_pc); - set_gdbarch_unwind_sp (gdbarch, arc_unwind_sp); - set_gdbarch_unwind_dummy_id (gdbarch, arc_unwind_dummy_id); - frame_unwind_append_sniffer (gdbarch, arc_frame_sniffer); - + /* Does this stack location hold a local variable or parameter? */ + if ((add_local_name(line + 38, + addr, + variables, + num_variables, + &is_following_element, + &elements_are_word_sized))) + { + /* Yes - so we want to print it (but this may be revised below!). */ + print = TRUE; + } + else + { + int r; + + /* Does this location hold a saved register? */ + for (r = 0; r < numregs; r++) + { + if (r != ARC_SP_REGNUM && + gdbarch_register_reggroup_p (current_gdbarch, r, all_reggroup)) + { + CORE_ADDR saved_addr; + + /* Find out the location of the saved register without + fetching the corresponding value. */ + frame_register_unwind (fi, r, &optimized, &lval, &saved_addr, &realnum, NULL); + + if (!optimized && lval == lval_memory && saved_addr == addr) + { + (void) sprintf(line + 38, _("saved register '%s'"), gdbarch_register_name(current_gdbarch, r)); + print = TRUE; + break; + } + } + } + } - set_gdbarch_return_value (gdbarch, arc_return_value); + /* Do we think we need to print out the line? */ + if (print) + { + /* If the location is a non-zeroth word-sized array element, + and this word is the same as the previous word. */ + if (is_following_element && elements_are_word_sized && (this_word == previous_word)) + { + /* No need to print it - one more word the same! */ + the_same++; + print = FALSE; + } + } + else + { + /* N.B. this will not be the first word (since print is FALSE) */ + + /* This location does not hold anything "interesting", but + if this word is not the same as the previous word, we + want to print it. */ + if (this_word == previous_word) + the_same++; + else + print = TRUE; + } - /* Add the arc register groups. */ - arc_add_reggroups (gdbarch); - set_gdbarch_register_reggroup_p (gdbarch, arc_register_reggroup_p); - - /* Breakpoint manipulation. */ - set_gdbarch_breakpoint_from_pc (gdbarch, arc_breakpoint_from_pc); - set_gdbarch_frame_align(gdbarch,arc_frame_align); + /* OK, now we really know whether we need to print out the line. */ + if (print) + { + n = sprintf(line, _("0x%08X:"), (unsigned int) addr); + line[n] = ' '; + + n = sprintf(line + 25, "%08X", this_word); + line[25 + n] = ' '; + + n = (int) sizeof(line) - 1; + while (line[n] == ' ') n--; + line[n + 1] = '\0'; + + /* If we did not print the previous word because it was the + same as the word before, but not the same as any preceding + words, print it now (there is no point in outputting a + message which says simply "... 1 words omitted" - we might + just as well print out the word! */ + if (the_same == 1) + printf_filtered(_("0x%08X: %08X\n"), (unsigned int) addr + BYTES_IN_WORD, previous_word); + else if (the_same != 0) + /* 2 or more words the same... */ + printf_filtered(_("... %u words omitted\n"), the_same); + + /* Now print out the line with the current word (and other information). */ + printf_filtered(_("%s\n"), line); + + /* No words omitted. */ + the_same = 0; + } - /* Dummy Frame handling */ - set_gdbarch_push_dummy_call (gdbarch, arc_push_dummy_call); - set_gdbarch_call_dummy_location (gdbarch,AT_ENTRY_POINT); - - /* Disassembly. */ - { - /* the arc libopcodes wants abfd so that it can find out what CPU - extensions are there */ - bfd abfd; - abfd.sections = NULL; + first_word = FALSE; + previous_word = this_word; + addr -= BYTES_IN_WORD; + i--; + } -#ifndef ARC4_JTAG - set_gdbarch_print_insn(gdbarch, arcompact_get_disassembler(&abfd)); -#else - set_gdbarch_print_insn(gdbarch, arc_get_disassembler(&abfd)); -#endif - } + printf_filtered(NEW_LINE); + } -#ifdef ARC4_JTAG - set_gdbarch_address_to_pointer (gdbarch, a4_address_to_pointer); - set_gdbarch_pointer_to_address (gdbarch, a4_pointer_to_address); -#endif - //#ifndef ARC4_JTAG - /* Set main_name to _main if necessary. Ideally we'd want a hook that - gets called when symbols are loaded, but it seems there isn't one; so - we'll use this. This will not work if the user does "target remote - ..." and then "add-symbol-file ..." */ - observer_attach_inferior_created (arc_set_main_name); - //#endif - -#ifdef ARC4_JTAG - // set_gdbarch_write_pc (gdbarch, a4_write_pc); -#endif + xfree(frame_contents); + } - CONFIG_INIT_TDEP (gdbarch); - - return gdbarch; + xfree(variables); } -static void -arc_dump_tdep (struct gdbarch *current_gdbarch, struct ui_file *file) + +/* -------------------------------------------------------------------------- */ +/* externally visible functions */ +/* -------------------------------------------------------------------------- */ + +/* Initialize the module. This function is called from the gdb core on start-up. */ + +void +_initialize_arc_tdep (void) { + /* Register the ARC processor architecture with gdb, providing an + initialization function and a dump function; 'bfd_arch_arc' is + an enumeration value specifically denoting the ARC architecture. */ + gdbarch_register(bfd_arch_arc, arc_gdbarch_init, arc_dump_tdep); + + /* Register ARC-specific commands with gdb. */ + + add_setshow_boolean_cmd(DEBUG_COMMAND, + no_class, + &arc_debug_target, + _("Set whether to print ARC debug messages.\n"), + _("Show whether to print ARC debug messages.\n"), + _("If set debug messages are printed.\n"), + NULL, + NULL, + &setlist, + &showlist); + + (void) add_cmd(SHOW_FRAME_COMMAND, + class_obscure, + arc_show_stack_frame_command, + _("Display the stack frame with annotation.\n" + SHOW_FRAME_COMMAND_USAGE + "<FRAME> may be the number or address of a frame.\n"), + &cmdlist); } + +/* Initialize the info structure to enable the disassembler to be used. */ + void -_initialize_arc_tdep (void) +arc_initialize_disassembler (struct disassemble_info *info) { - gdbarch_register (bfd_arch_arc, arc_gdbarch_init, arc_dump_tdep); + /* N.B. this type cast is not strictly correct: the return types differ! */ + init_disassemble_info(info, gdb_stdout, (fprintf_ftype) fprintf_filtered); + info->arch = gdbarch_bfd_arch_info(current_gdbarch)->arch; + info->mach = gdbarch_bfd_arch_info(current_gdbarch)->mach; + info->endian = gdbarch_byte_order (current_gdbarch); + info->read_memory_func = read_memory_for_disassembler; } +/******************************************************************************/ diff --git a/gdb/arc-tdep.h b/gdb/arc-tdep.h index 4251eb41203..8f330727128 100644 --- a/gdb/arc-tdep.h +++ b/gdb/arc-tdep.h @@ -1,109 +1,148 @@ -/* Target dependent code for ARC700, for GDB, the GNU debugger. +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. - Copyright 2005 Free Software Foundation, Inc. + Copyright 2005, 2008, 2009 Free Software Foundation, Inc. Contributed by Codito Technologies Pvt. Ltd. (www.codito.com) - Authors: - Soam Vasani <soam.vasani@codito.com> - Ramana Radhakrishnan <ramana.radhakrishnan@codito.com> + Authors: + Soam Vasani <soam.vasani@codito.com> + Ramana Radhakrishnan <ramana.radhakrishnan@codito.com> + Richard Stuckey <richard.stuckey@arc.com> 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 + the Free Software Foundation; either version 3 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. -*/ -enum arc700_api_regnums - { - ARC_ARG0_REGNUM = 0, - ARC_ARG1_REGNUM = 1, - ARC_ARG2_REGNUM = 2, - ARC_ARG3_REGNUM = 3, - ARC_ARG4_REGNUM = 4, - ARC_ARG5_REGNUM = 5, - ARC_ARG6_REGNUM = 6, - ARC_ARG7_REGNUM = 7, - - /* When a return value is stored in registers, is in either r0 or in - (r1,r0). Used in arc_extract_return_value */ - ARC_RETURN1_REGNUM = 0, - ARC_RETURN2_REGNUM = 1 - }; - - - -enum ARCProcessorVersion - { - UNSUPPORTED, - ARCompact, - ARC600, - ARC700, - A5, - A4, - }; - - -enum ARCExtensionsSupportedInformation - { - ARC700_MMU - }; - - - -typedef struct ARCProcessorInformation -{ - enum ARCProcessorVersion arcprocessorversion; - enum ARCExtensionsSupportedInformation extensionsSupported; + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This header file defines some target-dependent information which is */ +/* specific to the ARC gdb port. */ +/* */ +/******************************************************************************/ + +#ifndef ARC_TDEP_H +#define ARC_TDEP_H + +/* ARC header files */ +#include "arc-support.h" + + +#define ARC_PC_REGNUM (gdbarch_pc_regnum (current_gdbarch)) +#define ARC_NUM_REGS (gdbarch_num_regs (current_gdbarch)) +#define ARC_NUM_PSEUDO_REGS (gdbarch_num_pseudo_regs (current_gdbarch)) +#define ARC_TOTAL_REGS (ARC_NUM_REGS + ARC_NUM_PSEUDO_REGS) + + +#define ARC_MAX_CORE_REGS 64 +#define ARC_FIRST_EXTENSION_CORE_REGISTER 32 +#define ARC_LAST_EXTENSION_CORE_REGISTER 59 +#define ARC_NUM_EXTENSION_CORE_REGS (ARC_LAST_EXTENSION_CORE_REGISTER - ARC_FIRST_EXTENSION_CORE_REGISTER + 1) +#define ARC_NUM_STANDARD_CORE_REGS (ARC_MAX_CORE_REGS - ARC_NUM_EXTENSION_CORE_REGS) + + +#define IS_EXTENSION_CORE_REGISTER(hw_regnum) (ARC_FIRST_EXTENSION_CORE_REGISTER <= (hw_regnum) && (hw_regnum) <= ARC_LAST_EXTENSION_CORE_REGISTER) + + +/* ARC processor ABI-related registers: + R0 .. R7 are the registers used to pass arguments in function calls + R13 .. R26 are the callee-saved registers + when a return value is stored in registers it is in either R0 or in the pair (R0,R1). */ + +#define ARC_ABI_GLOBAL_POINTER 26 +#define ARC_ABI_FRAME_POINTER 27 +#define ARC_ABI_STACK_POINTER 28 -}ARCVariantsInfo; +#define ARC_ABI_FIRST_CALLEE_SAVED_REGISTER 13 +#define ARC_ABI_LAST_CALLEE_SAVED_REGISTER 26 + +#define ARC_ABI_FIRST_ARGUMENT_REGISTER 0 +#define ARC_ABI_LAST_ARGUMENT_REGISTER 7 +#define ARC_ABI_REGISTER_PARAMETER_SPACE ((ARC_ABI_LAST_ARGUMENT_REGISTER - ARC_ABI_FIRST_ARGUMENT_REGISTER + 1) * BYTES_IN_REGISTER) + +#define ARC_ABI_RETURN_REGNUM 0 +#define ARC_ABI_RETURN_LOW_REGNUM 0 +#define ARC_ABI_RETURN_HIGH_REGNUM 1 + +#define IS_ARGUMENT_REGISTER(hw_regnum) (ARC_ABI_FIRST_ARGUMENT_REGISTER <= (hw_regnum) && (hw_regnum) <= ARC_ABI_LAST_ARGUMENT_REGISTER) + + +/* This type is completed in the files arc-elf32-tdep.h and arc-linux-tdep.h, + as apppropriate for the arc-elf2 and arc-uclinux builds of gdb. */ +typedef struct arc_variant_info ARC_VariantsInfo; + + +#define REGISTER_NOT_PRESENT (-1) // special value for sc_reg_offset[reg] + + +/* N.B. this assumes that the host is little-endian! */ +#define HOST_AND_TARGET_ENDIANNESS_DIFFER(arch) (gdbarch_byte_order (arch) == BFD_ENDIAN_BIG) + + +/* This structure holds target-dependent information. + + N.B. this type is used in the target-independent gdb code, but it is treated + as an opaque (or private) type: the only use of it is by pointers to + objects of this type (passed as parameters or returned as results, or + held in other structures); it is only the ARC-specific modules that + have knowledge of the structure of this type and access its fields. */ struct gdbarch_tdep { - /* Detect sigtramp. */ - int (*sigtramp_p) (struct frame_info *); - - /* Get address of sigcontext for sigtramp. */ - CORE_ADDR (*sigcontext_addr) (struct frame_info *); - - /* Offset of registers in `struct sigcontext'. */ - int *sc_reg_offset; - int sc_num_regs; - - /* In our linux target, gdbarch_pc_regnum points to stop_pc, which is a - register that's made-up by the kernel and does not actually exist. - stop_pc is NOT saved in the sigcontext; what is saved is the ret - "register". Since ret is a linux-only register, it's regnum is visible - only in arc-linux-tdep.c; hence initialize pc_regnum_in_sigcontext in - arc-linux-tdep.c. */ - int pc_regnum_in_sigcontext; - - /* Returns false, true, or -1; -1 means the tdep has nothing to say about this - register and group. */ - int (*register_reggroup_p) (int, struct reggroup *); - - /* Breakpoint instruction to be used */ - unsigned char * arc_breakpoint_insn; - int arc_breakpoint_size; - - /* For stopping backtraces. */ - CORE_ADDR lowest_pc; - - /* ARC Processor variant information. */ - ARCVariantsInfo * arc_processor_variant_info ; - + /* Detect sigtramp. */ + Boolean (*is_sigtramp) (struct frame_info*); + + /* Get address of sigcontext for sigtramp. */ + CORE_ADDR (*sigcontext_addr) (struct frame_info*); + + /* Offset of registers in `struct sigcontext'. */ + const int *sc_reg_offset; + unsigned int sc_num_regs; + + /* In our linux target, gdbarch_pc_regnum points to stop_pc, which is a + register that is made up by the kernel and does not actually exist. + stop_pc is NOT saved in the sigcontext; what is saved is the ret + "register". Since ret is a linux-only register, its regnum is visible + only in arc-linux-tdep.c; hence initialize pc_regnum_in_sigcontext in + arc-linux-tdep.c. */ + int pc_regnum_in_sigcontext; + + /* Returns 0, 1, or -1: + 0 means the register is not in the group. + 1 means the register is in the group. + -1 means the tdep has nothing to say about this register and group. */ + int (*register_reggroup_p) (int regnum, struct reggroup *group); + + /* Breakpoint instruction to be used. */ + const unsigned char *be_breakpoint_instruction; + const unsigned char *le_breakpoint_instruction; + unsigned int breakpoint_size; + + /* For stopping backtraces. */ + CORE_ADDR lowest_pc; + + /* ARC processor variant information (may be NULL). */ + ARC_VariantsInfo *processor_variant_info; }; -void arc_software_single_step(enum target_signal ignore, int insert_breakpoints_p); +/* Utility functions used by other ARC-specific modules. */ + +void arc_initialize_disassembler (struct disassemble_info *info); + +/* A global debug flag. */ +extern Boolean arc_debug_target; +#endif /* ARC_TDEP_H */ +/******************************************************************************/ diff --git a/gdb/arc-xiss.c b/gdb/arc-xiss.c new file mode 100755 index 00000000000..2319f36d2a2 --- /dev/null +++ b/gdb/arc-xiss.c @@ -0,0 +1,1787 @@ +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. + + Copyright 2009 Free Software Foundation, Inc. + + Contributed by ARC International (www.arc.com) + + Authors: + Richard Stuckey <richard.stuckey@arc.com> + + 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 3 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, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This module creates an instance of a gdb 'target_ops' structure which */ +/* contains information and operations for debugging using the ARC xISS */ +/* (Fast Instruction Set Simulator) as the target. */ +/* */ +/* Instruction Tracing: */ +/* Two different kind of instruction tracing are supported: */ +/* */ +/* 1) a minimal trace which records only the address of each instruction */ +/* which is executed; */ +/* */ +/* 2) a more detailed trace which includes the ordinal number of the */ +/* instruction in the trace, condition code settings, instruction */ +/* address, instruction disassembly, and the values of source and */ +/* destination operands. */ +/* */ +/* The minimal trace is recorded by the xISS in an internal buffer whose */ +/* size may be determined by the gdb user; this buffer wraps around, so */ +/* that only the addresses of the most recently executed instructions are */ +/* held. This data may then be saved to a file for subsequent inspection. */ +/* The trace data is written in an encoding which gives a reduction in */ +/* size of approximately 80% compared to the raw data. */ +/* */ +/* See the manual */ +/* */ +/* ARC GDB-Insight */ +/* GNU Debugger With Insight GUI */ +/* Getting Started */ +/* 6009-001 */ +/* */ +/* for full information on how to use these trace facilities. */ +/* */ +/* Usage: */ +/* The module exports a function _initialize_arc_xiss: the call to this */ +/* function is generated by the gdb build mechanism, so this function */ +/* should not be explicitly called. */ +/* */ +/******************************************************************************/ + +/* gdb configuration file */ +#include "config.h" + +/* This macro should be defined in the configuration file only if the arcint.h + file is available. */ +#ifdef HAVE_LIBXISS + +/* system header files */ +#include <unistd.h> +#include <errno.h> +#include <dlfcn.h> + +/* gdb header files */ +#include "defs.h" +#include "inferior.h" +#include "disasm.h" +#include "gdbcmd.h" +#include "objfiles.h" +#include "libiberty.h" +#include "completer.h" +#include "gdb_assert.h" + +/* ARC header files */ +#include "config/arc/tm-embed.h" +#include "arc-xiss.h" +#include "arc-tdep.h" +#include "arc-elf32-tdep.h" +#include "arc-architecture.h" +#include "arc-inst-tracing.h" +#include "arc-registers.h" + +/* This must be defined before arcint.h is included. */ +#define OEM_USE_OF_DEBUGGER_HEADER_FILES 1 + +/* This defines the interface by which the xISS is accessed. */ +#include "arcint.h" + + +/* -------------------------------------------------------------------------- */ +/* local types */ +/* -------------------------------------------------------------------------- */ + +typedef struct ARC* (*ARC_Interface)(void); + +typedef enum +{ + XISS_HALTED, + XISS_STEPPED, + XISS_TO_BE_RUN +} ExecutionStatus; + + +/* N.B. this is taken from arcint.cpp; there must be a better way of doing this! */ +struct ARC +{ + ARC_functab *pftp; /* Pointer to interface function table. */ +}; + +typedef void* WatchpointCookie; + +typedef struct _association +{ + WatchpointCookie cookie; + CORE_ADDR addr; + int length; + int type; +} Association; + + +/* -------------------------------------------------------------------------- */ +/* local data */ +/* -------------------------------------------------------------------------- */ + +#define ARC_TARGET_NAME "arcxiss" +#define ARC_INTERFACE "get_ARC_interface" + +#define SAVE_TRACE_TO_FILE_COMMAND "arc-xiss-save-trace" +#define EMPTY_TRACE_BUFFER_COMMAND "arc-xiss-empty-trace-buffer" +#define LIST_TRACE_COMMAND "arc-xiss-list-trace" + +#define SAVE_TRACE_TO_FILE_COMMAND_USAGE "Usage: " SAVE_TRACE_TO_FILE_COMMAND " <FILE>\n" +#define EMPTY_TRACE_BUFFER_COMMAND_USAGE "Usage: " EMPTY_TRACE_BUFFER_COMMAND "\n" +#define LIST_TRACE_COMMAND_USAGE "Usage: " LIST_TRACE_COMMAND " [ FROM=<VALUE> ] [ TO=<VALUE> ] [ <FILE> ]\n" + + +/* N.B. the xISS does not currently use the context parameter supplied for + memory read/write operations, so its value is irrelevant. */ +#define XISS_CONTEXT 0 + + +/* The gdb target operations structure for this target. */ +static struct target_ops xISS_target_ops; + +/* A set of pointers to operations for reading/writing registers/memory in the + xISS target. */ +static TargetOperations operations; + +/* Handles for access to the xISS target. */ +static void *xISS_DLL_handle; +static struct ARC *xiss_instance; +static ARC_functab *xISS_functions; + +/* The status of program execution on the xISS target. */ +static ExecutionStatus xISS_executionStatus; + +/* Data for handling instruction tracing. */ +static Boolean xISS_trace_instructions; +static char *xiss_trace_file; +static int xiss_trace_buffer_size; + +/* This is the table for associating watchpoint details with watchpoint cookies. */ +static Association *associations; +static unsigned int num_associations; + +/* The address of data to which access caused a watchpoint to trigger. */ +static CORE_ADDR stopped_data_address; + + +/* -------------------------------------------------------------------------- */ +/* local macros */ +/* -------------------------------------------------------------------------- */ + +#define EXTRACT(argument, type, result) \ +{ \ + struct expression *expr = parse_expression(argument); \ + struct value *val = evaluate_expression(expr); \ + struct cleanup *chain = make_cleanup(free_current_contents, &expr); \ + \ + result = *(type*) (value_contents (val)); \ + result ## _specified = TRUE; \ + do_cleanups (chain); \ +} + +/* This macro must cope with the cases: + var = value + var =value + var= value + var=value */ +#define GET_PARAMETER(var) \ + if (strncasecmp(argv[i], #var "=", sizeof(#var)) == 0) \ + { \ + if (argv[i][sizeof(#var)] == '\0') \ + { \ + i++; \ + if (argv[i] == NULL) \ + invalid = TRUE; \ + else \ + { \ + EXTRACT(argv[i], Ordinal, var) \ + i++; \ + } \ + } \ + else \ + { \ + EXTRACT(argv[i] + sizeof(#var), Ordinal, var) \ + i++; \ + } \ + } \ + else if (strcasecmp(argv[i], #var) == 0) \ + { \ + i++; \ + if (argv[i] == NULL) \ + invalid = TRUE; \ + else \ + { \ + if (argv[i][0] == '=') \ + { \ + if (argv[i][1] == '\0') \ + { \ + i++; \ + if (argv[i] == NULL) \ + invalid = TRUE; \ + else \ + { \ + EXTRACT(argv[i], Ordinal, var) \ + i++; \ + } \ + } \ + else \ + { \ + EXTRACT(argv[i] + 1, Ordinal, var) \ + i++; \ + } \ + } \ + else \ + invalid = TRUE; \ + } \ + } + + +#define CHECK_RANGE(source) \ +{ \ + if (from_specified) \ + { \ + if (from < first || from > last) \ + { \ + warning(_("%s contains instruction range %llu .. %llu - using %llu as FROM"), \ + source, first, last, first); \ + gdb_flush(gdb_stderr); \ + from = first; \ + } \ + } \ + else \ + from = first; \ + \ + if (to_specified) \ + { \ + if (to < first || to > last) \ + { \ + warning(_("%s contains instruction range %llu .. %llu - using %llu as TO"), \ + source, first, last, last); \ + gdb_flush(gdb_stderr); \ + to = last; \ + } \ + } \ + else \ + to = last; \ +} + + +#define XISS_PROPERTY(key,value) xISS_functions->process_property(xiss_instance, key, value) + +#define IS_SUPPORTED(flag) ((xISS_functions->supports_feature(xiss_instance) & (flag)) != 0) + +#define XISS_AUX_REG(hw_regno) ((ARC_REG_TYPE) hw_regno + (ARC_REG_TYPE) AUX_BASE) +#define XISS_CORE_REG(hw_regno) ((ARC_REG_TYPE) hw_regno) + + +/* -------------------------------------------------------------------------- */ +/* local functions */ +/* -------------------------------------------------------------------------- */ + +/* -------------------------------------------------------------------------- */ +/* 1) functions for watchpoint table */ +/* -------------------------------------------------------------------------- */ + +/* These functions implement a linear lookup table which associate an xISS + watchpoint cookie with the watchpoint's address, length and type; this is + not very efficient, but is quite sufficient for relatively small numbers of + watchpoints. If we should need to handle large numbers of watchpoints + efficiently, a more sophisticated data structure may be required. */ + + +/* Add an association to the table. */ + +static void +associate (WatchpointCookie cookie, CORE_ADDR addr, int length, int type) +{ + Association *new = NULL; + unsigned int i; + + for (i = 0; i < num_associations; i++) + { + Association *association = &associations[i]; + + if (association->cookie == NULL) + { + new = association; + break; + } + } + + if (new == NULL) + { + Association *new_associations = xrealloc(associations, sizeof(Association) * (num_associations + 1)); + + if (new_associations == NULL) + nomem(0); + + associations = new_associations; + new = &new_associations[num_associations]; + num_associations++; + } + + new->cookie = cookie; + new->addr = addr; + new->length = length; + new->type = type; +} + + +/* Remove an association from the table, returning the cookie. */ + +static WatchpointCookie +disassociate (CORE_ADDR addr, int length, int type) +{ + unsigned int i; + + for (i = 0; i < num_associations; i++) + { + Association *association = &associations[i]; + + if (association->addr == addr && + association->length == length && + association->type == type) + { + WatchpointCookie cookie = association->cookie; + + association->cookie = NULL; + return cookie; + } + } + + return NULL; +} + + +/* Find the address associated with a cookie. */ + +static CORE_ADDR +find (WatchpointCookie cookie) +{ + unsigned int i; + + for (i = 0; i < num_associations; i++) + { + Association *association = &associations[i]; + + if (association->cookie == cookie) + return association->addr; + } + + return 0; +} + + +/* -------------------------------------------------------------------------- */ +/* 2) functions for reading/writing registers */ +/* -------------------------------------------------------------------------- */ + +/* N.B. the register contents returned by these functions, or supplied to them, + are in host byte order - the arcint.cpp interface to the xISS requires + this. */ + +/* Read a core register on the target. + + Parameters: + hw_regno : the ARC hardware number of the register + value : set to the contents of the register + warn_on_failure: TRUE if a warning should be issued if the read fails + + Result: TRUE if the register contents are read. */ + +static Boolean +read_xiss_core_register (ARC_RegisterNumber hw_regno, + ARC_RegisterContents *contents, + Boolean warn_on_failure) +{ + unsigned long value; + int result = xISS_functions->read_reg(xiss_instance, XISS_CORE_REG(hw_regno), &value); + + if (result == 1) + { + *contents = (ARC_RegisterContents) value; + DEBUG("Read value 0x%08X from core register %d\n", *contents, hw_regno); + return TRUE; + } + + if (warn_on_failure) + arc_elf32_core_warning(ERROR_ON_READING_REGISTER, hw_regno); + return FALSE; +} + + +/* Write a core register on the target. + + Parameters: + hw_regno : the ARC hardware number of the register + value : set to the contents of the register + warn_on_failure: TRUE if a warning should be issued if the write fails + + Result: TRUE if the register contents are written. */ + +static Boolean +write_xiss_core_register (ARC_RegisterNumber hw_regno, + ARC_RegisterContents contents, + Boolean warn_on_failure) +{ + int result = xISS_functions->write_reg(xiss_instance, XISS_CORE_REG(hw_regno), (unsigned long) contents); + + if (result == 1) + { + DEBUG("Written value 0x%08X to core register %d\n", contents, hw_regno); + return TRUE; + } + + if (warn_on_failure) + arc_elf32_core_warning(ERROR_ON_WRITING_REGISTER, hw_regno); + return FALSE; +} + + +/* Read an auxiliary register on the target. + + Parameters: + hw_regno : the ARC hardware number of the register + value : set to the contents of the register + warn_on_failure: TRUE if a warning should be issued if the read fails + + Result: TRUE if the register contents are read. */ + +static Boolean +read_xiss_aux_register (ARC_RegisterNumber hw_regno, + ARC_RegisterContents *contents, + Boolean warn_on_failure) +{ + unsigned long value; + int result = xISS_functions->read_reg(xiss_instance, XISS_AUX_REG(hw_regno), &value); + + if (result == 1) + { + *contents = (ARC_RegisterContents) value; + DEBUG("Read value 0x%08X from auxiliary register %d\n", *contents, hw_regno); + return TRUE; + } + + if (warn_on_failure) + arc_elf32_aux_warning(ERROR_ON_READING_REGISTER, hw_regno); + return FALSE; +} + + +/* Write an auxiliary register on the target. + + Parameters: + hw_regno : the ARC hardware number of the register + value : the contents of the register + warn_on_failure: TRUE if a warning should be issued if the write fails + + Result: TRUE if the register contents are written. */ + +static Boolean +write_xiss_aux_register (ARC_RegisterNumber hw_regno, + ARC_RegisterContents contents, + Boolean warn_on_failure) +{ + ARC_AuxRegisterDefinition *def = arc_find_aux_register_by_hw_number(hw_regno); + int result; + + if (def) + contents = arc_write_value(def, contents); + + result = xISS_functions->write_reg(xiss_instance, XISS_AUX_REG(hw_regno), (unsigned long) contents); + + if (result == 1) + { + DEBUG("Written value 0x%08X to auxiliary register %d\n", contents, hw_regno); + return TRUE; + } + + if (warn_on_failure) + arc_elf32_aux_warning(ERROR_ON_WRITING_REGISTER, hw_regno); + return FALSE; +} + + +/* -------------------------------------------------------------------------- */ +/* 3) functions for reading/writing memory */ +/* -------------------------------------------------------------------------- */ + +/* These functions should NOT be used within this module: they are intended + purely for use by the arc-memory module for reading/writing multiple words + of data at word-aligned addresses. */ + +static unsigned int +read_words (ARC_Address address, + ARC_Byte *data, + unsigned int words) +{ + DEBUG("reading %u words from 0x%08X in xISS\n", words, address); + + gdb_assert(IS_WORD_ALIGNED(address)); + + return (unsigned int) xISS_functions->read_memory + (xiss_instance, + (unsigned long) address, + data, + (unsigned long) words * BYTES_IN_WORD, + XISS_CONTEXT); +} + + +static unsigned int +write_words (ARC_Address address, + ARC_Byte *data, + unsigned int words) +{ + gdb_assert(IS_WORD_ALIGNED(address)); + + DEBUG("writing %u words to 0x%08X in xISS\n", words, address); + + return (unsigned int) xISS_functions->write_memory + (xiss_instance, + (unsigned long) address, + data, + (unsigned long) words * BYTES_IN_WORD, + XISS_CONTEXT); +} + + +static unsigned int +write_pattern (ARC_Address address, + ARC_Word pattern, + unsigned int words) +{ + gdb_assert(IS_WORD_ALIGNED(address)); + + DEBUG("writing pattern 0x%08X repeated %u times to 0x%08X in xISS\n", pattern, words, address); + + return (unsigned int) xISS_functions->fill_memory + (xiss_instance, + (unsigned long) address, + &pattern, + BYTES_IN_WORD, + (unsigned int) words, + XISS_CONTEXT); +} + + +/* -------------------------------------------------------------------------- */ +/* 4) functions for xISS interface management */ +/* -------------------------------------------------------------------------- */ + +/* Open the interface to the debug target. + + Parameters: + systemfile: the .xis file defining the simulated target + from_tty : non-zero if the 'target' command was issued at the terminal +*/ + +static Boolean +open_xISS_interface (const char *systemfile, int from_tty) +{ + const char *xiss_home = getenv("XISS_HOME"); + char library[FILENAME_MAX]; + ARC_Interface arc_interface; + + if (xiss_home == NULL) + error(_("Environment variable XISS_HOME is not set")); + + /* Construct the path to the xISS .so file. */ + (void) snprintf(library, sizeof(library), "%s/lib/libxiss.so", xiss_home); + + /* Try to dynamically load the shared object library containing the xISS. */ + xISS_DLL_handle = dlopen(library, RTLD_LAZY); + + if (xISS_DLL_handle == NULL) + { + const char *diagnostic = dlerror(); + + if (strstr(diagnostic, library)) + error(_("Can not open xISS shared library %s"), diagnostic); + else + error(_("Can not open xISS shared library %s: %s"), library, diagnostic); + } + + /* Find the function in the library which will create an instacne of the xISS. */ + arc_interface = dlsym(xISS_DLL_handle, ARC_INTERFACE); + + if (arc_interface == NULL) + error(_("Can not find function %s in xISS shared library %s"), ARC_INTERFACE, library); + + /* Create an xISS instance. */ + xiss_instance = arc_interface(); + + if (xiss_instance == NULL) + error(_("Can not create instance of xISS")); + + /* Get a pointer to the table of functions provided by the interface. */ + xISS_functions = xiss_instance->pftp; + + DEBUG("xISS interface : %s\n", xISS_functions->id(xiss_instance)); + DEBUG("xISS interface version: %d\n", xISS_functions->version(xiss_instance)); + + /* Tell the xISS what system file to use to define the simulated target. */ + if (XISS_PROPERTY("xiss_sys", systemfile) != 1) + error(_("xISS could not process 'xiss_sys' property")); + + if (IS_SUPPORTED(ARC_FEATURE_fill_memory)) + operations.fill_memory = write_pattern; + + (void) xISS_functions->prepare_for_new_program(xiss_instance, 1); + + /* This is somewhat inelegant, but commands read from scripts in the gdb + testsuite are regarded as though they were being input interactively + (i.e. from_tty is 1), and interactive queries may be made (such as + asking the user whether the program currently being debugged should be + killed first) - and these queries hang the tests! + + So, if the environment variable is set, assume that the gdb test suite is + being run, so that no such queries will be made. + + It is not possible to make this check in the top-level command handler + loop, as the output from some other commands (e.g. 'file') depend on the + from_tty parameter passed to them, and the gdb test scripts expect to get + the interactive version of the output! */ + target_preopen(from_tty && (getenv("ARC_GDB_TEST") == NULL)); + + return TRUE; +} + + +/* Close the JTAG interface to the debug target. + + Parameter: + resume: TRUE if program execution on the target should be allowed to resume. */ + +static void +close_xISS_interface (Boolean resume) +{ + /* If we have a target connected. */ + if (xiss_instance != NULL) + { + /* It is meaningless to resume execution of the xISS. */ + arc_elf32_close(FALSE); + + /* If we are doing instruction tracing. */ + if (xiss_trace_file) + { + /* Ensure that the trace file is closed. */ + if (XISS_PROPERTY("trace_file", "") != 1) + error(_("xISS could not process 'trace_file' property")); + } + + /* Close the connection. */ + xISS_functions->destroy(xiss_instance); + + /* Close the library. */ + if (dlclose(xISS_DLL_handle) != 0) + warning(_("error on closing xISS shared library: %s"), dlerror()); + + xiss_instance = NULL; + xISS_functions = NULL; + xISS_DLL_handle = NULL; + } +} + + +/* -------------------------------------------------------------------------- */ +/* 5) functions for starting/stopping the processor */ +/* -------------------------------------------------------------------------- */ + +/* Run the xISS for whatever quantum of instructions that it executes. */ +static void +run_processor (void) +{ + int result = xISS_functions->run(xiss_instance); + + if (result == 0) + warning(_("could not run")); +} + + +/* -------------------------------------------------------------------------- */ +/* 6) local functions called from outside this module (from gdb) */ +/* -------------------------------------------------------------------------- */ + +/* Connect to the xISS target. + + Parameters: + args : user arguments to the 'target' command + from_tty: non-zero if the 'target' command was issued at the terminal + + The arguments may be: <XIS_system_file> + + If the name of a system file is not specified, a file 'default.xis' is + assumed. */ + +static void +arc_xISS_open (char *args, int from_tty) +{ + char *systemfile = (args) ? args : "default.xis"; + + ENTERARGS("\"%s\" (%d)", (args) ? args : "", from_tty); + + if (access(systemfile, R_OK) != 0) + { + char *file = getenv("XISS_SYSTEM_FILE"); + + if (file == NULL) + error(_("Invalid xISS system file '%s': %s"), systemfile, strerror(errno)); + else + if (access(file, R_OK) != 0) + error(_("Invalid xISS system file '%s': %s"), file, strerror(errno)); + else + systemfile = file; + } + + arc_program_is_loaded = FALSE; + + xISS_trace_instructions = FALSE; + xiss_trace_file = NULL; + xISS_executionStatus = XISS_HALTED; + xiss_trace_buffer_size = 0; + + num_associations = 0; + associations = NULL; + + /* Find any well-known aux register numbers that we will need. */ + arc_elf32_find_register_numbers(); + + /* Just to be sure that it is not in the target stack... */ + (void) unpush_target (&xISS_target_ops); + + /* Now try to open the xISS interface. */ + if (open_xISS_interface(systemfile, from_tty)) + { + (void) push_target (&xISS_target_ops); + + if (from_tty) + printf_filtered (_("Connected to the " ARC_TARGET_NAME " target.\n")); + } + else + error(_("Can not connect to target")); +} + + +/* Close the connection to the target. */ + +static void +arc_xISS_close (int quitting) /* Ignored. */ +{ + ENTERMSG; + + close_xISS_interface(FALSE); + + xfree(associations); + associations = NULL; + num_associations = 0; +} + + +/* Cause the inferior on the debug target to resume execution, sending a signal + if necessary. + + Parameters: + ptid : the thread id of the thread to be resumed (ignored) + step : 1 means single step, 0 run freely. + signal: the number of the signal to be sent + + N.B. signals are not supported. */ + +static void +arc_xISS_resume (ptid_t ptid, int step, enum target_signal signal) +{ + ENTERARGS("%d, %d, %d", ptid.pid, step, signal); + + if (signal != TARGET_SIGNAL_0) + error(_("Signals are not supported by the " ARC_TARGET_NAME " target")); + + if (step) + { + int result = xISS_functions->step(xiss_instance); + + if (result == 0) + error(_("Can not single-step")); + else + xISS_executionStatus = XISS_STEPPED; + } + else + xISS_executionStatus = XISS_TO_BE_RUN; + + LEAVEMSG; +} + + +/* Wait for execution on the target to halt (for whatever reason). + + Parameters : + ptid : ignored + status: set to indicate status at end of the wait +*/ + +static ptid_t +arc_xISS_wait (ptid_t ptid, struct target_waitstatus *status) +{ + ENTERARGS("xISS execution status: %d", xISS_executionStatus); + + /* What was done the last time that execution was resumed? */ + switch (xISS_executionStatus) + { + case XISS_STEPPED: + DEBUG("processor has stepped\n"); + status->kind = TARGET_WAITKIND_STOPPED; + status->value.sig = TARGET_SIGNAL_TRAP; + break; + + case XISS_TO_BE_RUN: + arc_elf32_execute(status, + run_processor, + NULL, + NULL); + + if (status->kind == TARGET_WAITKIND_EXITED) + target_mark_exited (&xISS_target_ops); + break; + + case XISS_HALTED: + break; + } + + LEAVEMSG; + + return inferior_ptid; +} + + +/* This gets called just before store_regs. */ + +static void +arc_xISS_prepare_to_store (struct regcache *regcache) +{ + ENTERMSG; +} + + +static void +arc_xISS_files_info (struct target_ops *target) +{ + /* Do nothing. */ + ENTERMSG; +} + + +/* Heavy duty arsenal. Kill the process. */ + +static void +arc_xISS_kill (void) +{ + ENTERMSG; + + target_mourn_inferior (); +} + + +/* Load the program into memory via the xISS interface. */ + +static void +arc_xISS_load (char *args, int from_tty) +{ + /* The program will be downloaded to the simulator. */ + (void) xISS_functions->prepare_for_new_program(xiss_instance, 1); + + arc_elf32_load_program(args, from_tty); + + /* Tell xISS that program is fully loaded. */ + if (XISS_PROPERTY("download_complete", "1") != 1) + error(_("xISS could not process 'download_complete' property")); + + /* We now have a program ready for execution on the target. */ +} + + +/* Create the inferior that will be executed upon the target. + + Parameters : + exec_file: the executable file containing the program to be executed + args : the command line arguments to be passed to the program + env : the environment (name/value pairs) for the program + from_tty : ignored + */ + +static void +arc_xISS_create_inferior (char *exec_file, char *args, char **env, int from_tty) +{ + arc_elf32_create_inferior(exec_file, args, env, &xISS_target_ops); +} + + +/* Mourn the inferior. */ + +static void +arc_xISS_mourn_inferior (void) +{ + ENTERMSG; + +// (void) unpush_target (&xISS_target_ops); + + /* N.B. we must delete all breakpoints from the target here: the gdb core + function generic_mourn_inferior marks all breakpoints as not being + inserted on the target, with the result that subsequent calls to + remove_breakpoints will NOT remove any breakpoints that are set on + the target; this means that if target execution is re-started, gdb + will attempt to re-insert the breakpoints, which causes a problem + with software breakpoints: the target insert_breakpoint function + reads the code at the b/p address (which is the s/w b/p instruction) + and saves it as the "overwritten" code - so when the breakpoint is + subsequently removed, the b/p instruction is written back to the + b/p address again! That is not what is desired... */ + (void) remove_breakpoints(); + generic_mourn_inferior(); + current_target.to_has_execution = 0; +} + + +/* Check whether the given thread is alive. */ + +static int +arc_xISS_thread_alive (ptid_t ptid) +{ + ENTERMSG; + + /* We only have one thread. */ + return 1; +} + + +/* Check whether our debug target is runnable: return 1 if it is, 0 otherwise. */ + +static int arc_xISS_can_run (void) +{ + /* If we are connected to the xISS i/f, and a program is loaded. */ + return (xiss_instance != NULL) && arc_program_is_loaded; +} + + +/* We do not support asynchronous execution of the target program (i.e. commands + like 'run' or 'continue' or 'step' can not be executed in background mode + by appending a '&' to them) so we do not need to implement the target stop + operation (called by the 'interrupt' command); interrupting a running program + is handled by the Ctrl-C mechanism. */ + +#if 0 +static void +arc_xISS_stop (void) +{ + ENTERMSG; +} +#endif + + +/* Check if we can set a "hardware" watchpoint of type TYPE. TYPE is + one of bp_hardware_watchpoint, bp_read_watchpoint, bp_write_watchpoint, or + bp_hardware_breakpoint. COUNT is the number of such watchpoints used so far + (including this one). OTHERTYPE is the total number of "hardware" breakpoints + and watchpoints of other types that are "already" set + (0 if type == bp_hardware_breakpoint). + + Result: 0 if hardware watchpoints are not supported + -1 if there are not enough hardware watchpoints + 1 if there are enough hardware watchpoints + + N.B. this is not what is stated in target.h, but it does conform to the use + made of this function's result in breakpoint.c! */ + +static int +arc_xISS_can_use_hw_breakpoint (int type, int count, int othertype) +{ + ENTERARGS("type %d, count %d", type, count); + + switch (type) + { + case bp_hardware_watchpoint: + case bp_read_watchpoint: + case bp_watchpoint: /* This means bp_write_watchpoint. */ + return 1; + case bp_hardware_breakpoint: + return 0; + default: + return 0; + } +} + + +/* Insert a "hardware" watchpoint on the target. + + Parameters: + addr : the start address of the region of memory to be watched + length: the length in bytes of the region of memory + type : 0 => write, 1 => read, 2 => read/write + + Returns 0 for success, -1 for failure. */ + +static int +arc_xISS_insert_watchpoint (CORE_ADDR addr, int length, int type) +{ + WatchpointCookie cookie; + unsigned int options; + + ENTERARGS("0x%08X:%d %d", (unsigned int) addr, length, type); + + gdb_assert(length > 0); + + switch (type) + { + case 0: + options = ARC_WATCHPOINT_write; + break; + case 1: + options = ARC_WATCHPOINT_read; + break; + case 2: + options = ARC_WATCHPOINT_read | ARC_WATCHPOINT_write; + break; + default: + internal_error (__FILE__, __LINE__, _("invalid watchpoint type: %d"), type); + } + + if (xISS_functions->set_mem_watchpoint2(xiss_instance, (unsigned long) addr, length, options, &cookie) == 1) + { + associate(cookie, addr, length, type); + return 0; + } + + return 1; +} + + +/* Remove a "hardware" watchpoint from the target. + + Parameters: + addr : the start address of the region of memory being watched + length: the length in bytes of the region of memory + type : 0 => write, 1 => read, 2 => read/write + + Returns 0 for success, non-zero for failure. */ + +static int +arc_xISS_remove_watchpoint (CORE_ADDR addr, int length, int type) +{ + WatchpointCookie cookie = disassociate(addr, length, type); + + ENTERARGS("0x%x:%d %d", (unsigned int) addr, length, type); + + if (cookie != NULL) + return (xISS_functions->remove_watchpoint(xiss_instance, cookie) == 1) ? 0 : 1; + return 1; +} + + +/* Returns non-zero if the execution of the target program has been stopped by + the trigger of a "hardware" watchpoint (i.e. on memory read or write), zero + otherwise. */ + +static int +arc_xISS_stopped_by_watchpoint (void) +{ + WatchpointCookie cookie; + + ENTERMSG; + + if (xISS_functions->stopped_at_watchpoint2(xiss_instance, &cookie) == 1) + { + /* Regrettably, the arcint i/f does not provide a well-defined means for + finding out the address of the data which was accessed - to do this + we have had to define a special property which returns the required + address! */ + int result = XISS_PROPERTY("watchpoint", "return_hit_address"); + + if (result == 0) + { + DEBUG("xISS could not process 'watchpoint/return_hit_address' property"); + + /* The most we can do now is retrieve the start address of the + watchpoint which was triggered... + N.B. we must retrieve the watchpoint address here, rather than in + the arc_xISS_stopped_data_address function, as gdb deletes + all breakpoint and watchpoints from the target as soon as + execution is halted, which removes the cookie from the table! */ + stopped_data_address = find(cookie); + } + else + stopped_data_address = (CORE_ADDR) result; + + return 1; + } + + return 0; +} + + +/* Get the address of the data that was read/written causing a h/w watchpoint to + trigger; the address is returned in the '*addr' parameter. + Returns 0 for failure, non-zero for success. */ + +static int +arc_xISS_stopped_data_address (struct target_ops *ops, CORE_ADDR *addr) +{ + ENTERMSG; + + DEBUG("data addr: 0x%08X\n", (unsigned int) stopped_data_address); + *addr = stopped_data_address; + + return 1; +} + + +/* Can a h/w watchpoint 'length' bytes long be set at address 'addr' in target memory? */ + +static int +arc_xISS_region_ok_for_hw_watchpoint (CORE_ADDR addr, int length) +{ + /* As far as we know, we can set a h/w watchpoint anywhere. */ + return 1; +} + + +/* -------------------------------------------------------------------------- */ +/* 7) functions for instruction tracing */ +/* -------------------------------------------------------------------------- */ + +/* Display the instruction at the given address and ordinal position in the + instruction trace. */ + +static void +display_instruction (Ordinal ordinal, unsigned int address) +{ + /* N.B. must use fprintf_filtered here, rather than fprintf_unfiltered, as + gdb_print_insn calls fprintf_filtered, and if we mix the use of the + two we can get garbled output! */ + fprintf_filtered(gdb_stdout, "<0x%016llX> 0x%08X ", ordinal, address); + (void) gdb_print_insn((CORE_ADDR) address, gdb_stdout, NULL); + fprintf_filtered(gdb_stdout, "\n"); +} + + +/* Get the ordinal position of the first instruction in the trace buffer in the + sequence of executed instructions from the xISS; return 0 if this number cannot + be obtained. */ + +static Ordinal +get_instruction_trace_start (void) +{ + /* N.B. XISS_PROPERTY returns 0 if the property is not handled. */ + int start_low = XISS_PROPERTY("instruction_trace", "return_start_count_low"); + int start_high = XISS_PROPERTY("instruction_trace", "return_start_count_high"); + Ordinal start = ((Ordinal) start_high) << 32 | (Ordinal) start_low; + + DEBUG("get_instruction_trace_start : %llu\n", start); + + return start; +} + + +/* Encode the instruction trace buffer retrieved from the xISS: the buffer + 'trace' contains 'length' entries which are all PC values. The data is + recorded in the currently open trace file in a packed format. */ + +static void +encode_instruction_trace (unsigned int trace[], int length) +{ + unsigned int lastPC = trace[0]; + int i; + + /* Store the first entry in the buffer as an absolute address. */ + arc_encode_PC(ABSOLUTE_31_BITS, lastPC / 2); + + /* Look at all the remaining entries, comparing each with the previous one. */ + for (i = 1; i < length; i++) + { + unsigned int thisPC = trace[i]; + int delta = (int) thisPC - (int) lastPC; + + /* Encode the difference in the PC as a relative offset, if possible. + Offsets of 0 (branch to current address), 2, 4, 6 and 8 (instruction + sizes + optional immediate size) are handled as special cases. */ + + if (delta == 0) + arc_encode_PC(NO_CHANGE, 0); + else if (delta == 2) + arc_encode_PC(PLUS_16_BITS, 0); + else if (delta == 4) + arc_encode_PC(PLUS_32_BITS, 0); + else if (delta == 6) + arc_encode_PC(PLUS_48_BITS, 0); + else if (delta == 8) + arc_encode_PC(PLUS_64_BITS, 0); + else if (0 < delta && delta <= MAX_DELTA) + arc_encode_PC(DELTA_16_BIT_POSITIVE, ENCODE_DELTA(delta)); + else if (0 > delta && delta >= -MAX_DELTA) + arc_encode_PC(DELTA_16_BIT_NEGATIVE, ENCODE_DELTA(-delta)); + else + arc_encode_PC(ABSOLUTE_31_BITS, thisPC / 2); + + lastPC = thisPC; + } + + /* Useful tracing code: dump buffer as raw binary. */ +#if 0 + { + int binary = open("trace.binary", O_CREAT | O_WRONLY, 0666); + + if (binary == -1) + warning(_("could not open file trace.binary")); + else + { + (void) write(binary, trace, length * sizeof(unsigned int)); + (void) close(binary); + } + } +#endif +} + + +/* Decode the instruction trace buffer retrieved from a file. This is the inverse + of the encoding performed by the 'encode_instruction_trace' function. + The instruction at each PC value decoded is disassembled and displayed only if + its ordinal position in the trace is in the range given by the 'from' and 'to' + parameters. */ + +static void +decode_instruction_trace (Ordinal from, Ordinal to, Ordinal ordinal) +{ + ARC_ProgramCounterEncoding encoding; + unsigned int thisPC = 0; + unsigned int lastPC = 0; + unsigned int value; + + while (arc_decode_PC(&encoding, &value)) + { + switch (encoding) + { + case NO_CHANGE: + break; + case PLUS_16_BITS: + thisPC += 2; + break; + case PLUS_32_BITS: + thisPC += 4; + break; + case PLUS_48_BITS: + thisPC += 6; + break; + case PLUS_64_BITS: + thisPC += 8; + break; + case DELTA_16_BIT_POSITIVE: + thisPC += DECODE_DELTA(value); + break; + case DELTA_16_BIT_NEGATIVE: + thisPC -= DECODE_DELTA(value); + break; + case ABSOLUTE_31_BITS: + thisPC = value * 2; + break; + } + + if (from <= ordinal && ordinal <= to) + display_instruction(ordinal, thisPC); + + lastPC = thisPC; + ordinal++; + } +} + + +/* -------------------------------------------------------------------------- */ +/* 8) helper routines for added commands */ +/* -------------------------------------------------------------------------- */ + +/* Check that the xISS target is actually connected. */ + +static void +check_connected (void) +{ + if (xiss_instance == NULL) + error(_("Target " ARC_TARGET_NAME " is not connected")); +} + + +/* Set the size of the buffer to be used by the xISS for instruction trace data. */ + +static void +set_trace_buffer_size (int size) +{ + char sz[20]; + + (void) snprintf(sz, sizeof(sz), "%d", size); + + if (XISS_PROPERTY("trace_buffer_size", sz) != 1) + error(_("xISS could not process 'trace_buffer_size' property")); +} + + +/* Tell the xISS to switch instruction tracing on or off. */ + +static void +set_xiss_trace (char *args, + int from_tty, + struct cmd_list_element *e) +{ + check_connected(); + + if (XISS_PROPERTY("trace", (xISS_trace_instructions) ? "on" : "off") != 1) + error(_("xISS could not process 'trace' property")); +} + + +/* Show the status of xISS instruction tracing. */ + +static void +show_xiss_trace (struct ui_file *file, + int from_tty, + struct cmd_list_element *c, + const char *value) +{ + /* Value will be either "on" or "off". */ + fprintf_filtered(file, + _("xISS instruction tracing is %s.\n"), + value); +} + + +/* Tell the xISS to write the instruction trace to the file whose name is held + in the xiss_trace_file global variable. */ + +static void +set_xiss_trace_file (char *args, + int from_tty, + struct cmd_list_element *e) +{ + check_connected(); + + if (xiss_trace_file) + { + if (XISS_PROPERTY("trace_file", xiss_trace_file) != 1) + error(_("xISS could not process 'trace_file' property")); + } +} + + +/* Show the name of the trace file (if any) which is currently receiving + instruction tracing output from the xISS. */ + +static void +show_xiss_trace_file (struct ui_file *file, + int from_tty, + struct cmd_list_element *c, + const char *value) +{ + if (*value == '\0') + fprintf_filtered(file, + _("No output file is set for xISS instruction tracing.\n")); + else + fprintf_filtered(file, + _("The output file for xISS instruction tracing is '%s'.\n"), + value); +} + + +/* Set the size of the buffer used by the xISS for instruction trace data. */ + +static void +set_xiss_trace_buffer_size (char *args, + int from_tty, + struct cmd_list_element *e) +{ + check_connected(); + + if (xiss_trace_buffer_size < 0) + error(_("Trace buffer size must be non-negative")); + + set_trace_buffer_size(xiss_trace_buffer_size); +} + + +/* Show the size of the buffer used by the xISS for instruction trace data. */ + +static void +show_xiss_trace_buffer_size (struct ui_file *file, + int from_tty, + struct cmd_list_element *c, + const char *value) +{ + fprintf_filtered(file, + _("The buffer size for xISS instruction tracing is %s entries.\n"), + value); +} + + +/* -------------------------------------------------------------------------- */ +/* 9) local functions implementing commands */ +/* -------------------------------------------------------------------------- */ + +/* Command: <command> <filename> + + Save the contents of the xISS instruction trace buffer to the named file. + + We should eventually change this to use the ui_out stuff rather than + printf_filtered. */ + +static void +arc_save_trace_to_file_command (char *arg, int from_tty) +{ + int count; + + if (!arg) + { + printf_filtered (_(SAVE_TRACE_TO_FILE_COMMAND_USAGE)); + return; + } + + check_connected(); + + count = xISS_functions->instruction_trace_count(xiss_instance); + + if (count) + { + unsigned int *buffer; + + if (access(arg, F_OK) == 0) + if (!query(_("File already exists. Do you wish to overwrite it?"))) + return; + + printf_unfiltered(_("%u instructions in trace buffer\n"), count); + + buffer = xmalloc(count * sizeof(unsigned int)); + + if (buffer) + { + Ordinal first = get_instruction_trace_start(); + + xISS_functions->get_instruction_traces(xiss_instance, buffer); + + if (arc_start_encoding(arg, first)) + { + encode_instruction_trace(buffer, count); + arc_stop_encoding(first + (Ordinal) count - 1); + } + + xfree(buffer); + } + else + warning(_("can not allocate buffer to hold instruction trace data")); + } + else + warning(_("no instruction trace data available")); +} + + + +/* Command: <command> + + Discard the contents of the xISS instruction trace buffer. */ + +static void +arc_empty_trace_buffer (char *arg, int from_tty) +{ + if (arg) + { + printf_filtered (_(EMPTY_TRACE_BUFFER_COMMAND_USAGE)); + return; + } + + check_connected(); + + set_trace_buffer_size(0); + set_trace_buffer_size(xiss_trace_buffer_size); +} + + +/* Command: <command> [ FROM=<from> ] [ TO=<to> ] [ <FILE> ] + + Display some or all of the instruction trace, either from the xISS trace + buffer or from a named file. */ + +static void +arc_list_trace (char *arg, int from_tty) +{ + char *file = NULL; + Boolean from_specified = FALSE; + Boolean to_specified = FALSE; + Ordinal from = 0; + Ordinal to = 0; + Ordinal first; + Ordinal last; + + /* Do we have arguments to the command? */ + if (arg) + { + char **argv = buildargv (arg); + Boolean invalid = FALSE; + int i = 0; + + if (argv == NULL) + nomem (0); + + while (argv[i] != NULL) + { +// printf("argv[%d] = %s\n", i, argv[i]); + + GET_PARAMETER(from) + else + GET_PARAMETER(to) + else + { + /* Assume the argument is the file name. */ + file = xstrdup(argv[i]); + i++; + } + } + + freeargv(argv); + + if (invalid) + { + printf_filtered (_(LIST_TRACE_COMMAND_USAGE)); + return; + } + + DEBUG("FROM = %llu\n", from); + DEBUG("TO = %llu\n", to); + + if (from > to) + error("FROM (%lld) > TO (%lld)", from, to); + } + + /* If we must get the instruction trace from a file. */ + if (file) + { + /* Try to open the named file and start decoding its contents. */ + if (arc_start_decoding(file, &first, &last)) + { + CHECK_RANGE("file") + decode_instruction_trace(from, to, first); + arc_stop_decoding(); + } + + xfree(file); + } + else + { + unsigned int count; + + check_connected(); + + /* Get the number of entries in the xISS instruction trace buffer. */ + count = (unsigned int) xISS_functions->instruction_trace_count(xiss_instance); + + if (count > 0) + { + unsigned int *buffer = xmalloc(count * sizeof(unsigned int)); + + if (buffer) + { + Ordinal i; + + first = get_instruction_trace_start(); + last = first + (Ordinal) count - 1; + + CHECK_RANGE("trace buffer") + + /* Get the contents of the xISS instruction trace buffer. */ + xISS_functions->get_instruction_traces(xiss_instance, buffer); + + /* Display the required range of the trace. */ + for (i = from; i <= to; i++) + display_instruction(i, buffer[(int) (i - from)]); + + xfree(buffer); + } + else + warning(_("can not allocate buffer to hold instruction trace data")); + } + else + warning(_("no instruction trace data available")); + } +} + + +/* -------------------------------------------------------------------------- */ +/* 10) initialization functions */ +/* -------------------------------------------------------------------------- */ + +/* Initialize the xISS target operations. */ + +static void +initialize_xISS_target_ops (void) +{ + ENTERMSG; + + xISS_target_ops.to_data = &operations; + + xISS_target_ops.to_shortname = ARC_TARGET_NAME; + xISS_target_ops.to_longname = "xISS debug target (ARC Processors)"; + xISS_target_ops.to_doc = "xISS (Fast Instruction Set Simulator) debug target (ARC Processors)"; + + xISS_target_ops.to_open = arc_xISS_open; + xISS_target_ops.to_close = arc_xISS_close; + xISS_target_ops.to_resume = arc_xISS_resume; + xISS_target_ops.to_wait = arc_xISS_wait; + + xISS_target_ops.to_fetch_registers = arc_elf32_fetch_registers; + xISS_target_ops.to_store_registers = arc_elf32_store_registers; + xISS_target_ops.to_prepare_to_store = arc_xISS_prepare_to_store; + xISS_target_ops.to_xfer_partial = arc_elf32_xfer_partial; + xISS_target_ops.to_files_info = arc_xISS_files_info; + + xISS_target_ops.to_can_use_hw_breakpoint = arc_xISS_can_use_hw_breakpoint; +// xISS_target_ops.to_insert_hw_breakpoint = arc_xISS_insert_hw_breakpoint; +// xISS_target_ops.to_remove_hw_breakpoint = arc_xISS_remove_hw_breakpoint; + xISS_target_ops.to_insert_watchpoint = arc_xISS_insert_watchpoint; + xISS_target_ops.to_remove_watchpoint = arc_xISS_remove_watchpoint; + xISS_target_ops.to_stopped_by_watchpoint = arc_xISS_stopped_by_watchpoint; + xISS_target_ops.to_stopped_data_address = arc_xISS_stopped_data_address; + xISS_target_ops.to_region_ok_for_hw_watchpoint = arc_xISS_region_ok_for_hw_watchpoint; + + xISS_target_ops.to_insert_breakpoint = arc_elf32_insert_breakpoint; + xISS_target_ops.to_remove_breakpoint = arc_elf32_remove_breakpoint; + + xISS_target_ops.to_kill = arc_xISS_kill; + xISS_target_ops.to_load = arc_xISS_load; + + xISS_target_ops.to_create_inferior = arc_xISS_create_inferior; + xISS_target_ops.to_mourn_inferior = arc_xISS_mourn_inferior; + xISS_target_ops.to_thread_alive = arc_xISS_thread_alive; +// xISS_target_ops.to_stop = arc_xISS_stop; + xISS_target_ops.to_can_run = arc_xISS_can_run; + xISS_target_ops.to_terminal_inferior = NULL; + + xISS_target_ops.to_stratum = process_stratum; + + xISS_target_ops.to_has_all_memory = 1; + xISS_target_ops.to_has_memory = 1; + xISS_target_ops.to_has_stack = 0; /* Defer setting this until the program has been loaded. */ + xISS_target_ops.to_has_registers = 1; + xISS_target_ops.to_has_execution = 0; /* Defer setting this until the program has been started. */ + + xISS_target_ops.to_magic = OPS_MAGIC; +} +#endif /* HAVE_LIBXISS */ + + +/* -------------------------------------------------------------------------- */ +/* externally visible functions */ +/* -------------------------------------------------------------------------- */ + +/* Initialize the module. This function is called from the gdb core on start-up. */ + +/* N.B. the initialization function must be defined even if the rest of this + module is excluded, as the call to it from the gdb start-up code is + generated by the build mechanism without regard to any conditional + compilation! */ + +void +_initialize_arc_xiss (void) +{ +#ifdef HAVE_LIBXISS + struct cmd_list_element *cmnd; + + ENTERMSG; + + operations.read_core_register = read_xiss_core_register; + operations.write_core_register = write_xiss_core_register; + operations.read_auxiliary_register = read_xiss_aux_register; + operations.write_auxiliary_register = write_xiss_aux_register; + operations.read_memory = read_words; + operations.write_memory = write_words; + operations.fill_memory = NULL; + + initialize_xISS_target_ops (); + add_target (&xISS_target_ops); + + /* Register ARC-specific commands with gdb. */ + + (void) add_setshow_boolean_cmd + ("arc-xiss-trace", + class_trace, + &xISS_trace_instructions, + _("Set whether the xISS should trace instructions.\n"), + _("Show whether the xISS should trace instructions.\n"), + NULL, + set_xiss_trace, + show_xiss_trace, + &setlist, + &showlist); + + add_setshow_optional_filename_cmd + ("arc-xiss-trace-file", + class_trace, + &xiss_trace_file, + _("Set the output file for xISS instruction tracing.\n"), + _("Show the output file for xISS instruction tracing.\n"), + NULL, + set_xiss_trace_file, + show_xiss_trace_file, + &setlist, + &showlist); + + (void) add_setshow_zinteger_cmd + ("arc-xiss-trace-buffer-size", + class_trace, + &xiss_trace_buffer_size, + _("Set the size of the trace buffer for xISS instruction tracing.\n"), + _("Show the size of the trace buffer for xISS instruction tracing.\n"), + NULL, + set_xiss_trace_buffer_size, + show_xiss_trace_buffer_size, + &setlist, + &showlist); + + cmnd = add_cmd + (SAVE_TRACE_TO_FILE_COMMAND, + class_trace, + arc_save_trace_to_file_command, + _("Save the contents of the xISS instruction trace buffer to a file.\n" + SAVE_TRACE_TO_FILE_COMMAND_USAGE + "<FILE> is a file to hold the xISS instruction trace buffer contents.\n"), + &cmdlist); + set_cmd_completer (cmnd, filename_completer); + + (void) add_cmd + (EMPTY_TRACE_BUFFER_COMMAND, + class_trace, + arc_empty_trace_buffer, + _("Empty xISS instruction trace buffer.\n" + EMPTY_TRACE_BUFFER_COMMAND_USAGE), + &cmdlist); + + cmnd = add_cmd + (LIST_TRACE_COMMAND, + class_trace, + arc_list_trace, + _("Display xISS instruction trace.\n" + LIST_TRACE_COMMAND_USAGE), + &cmdlist); + set_cmd_completer (cmnd, filename_completer); + +#endif /* HAVE_LIBXISS */ +} + +/******************************************************************************/ diff --git a/gdb/arc-xiss.h b/gdb/arc-xiss.h new file mode 100755 index 00000000000..a79030b2bc1 --- /dev/null +++ b/gdb/arc-xiss.h @@ -0,0 +1,41 @@ +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. + + Copyright 2009 Free Software Foundation, Inc. + + Contributed by ARC International (www.arc.com) + + Authors: + Richard Stuckey <richard.stuckey@arc.com> + + 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 3 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, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This header file defines some operations provided by the ARC xISS */ +/* module. */ +/* */ +/******************************************************************************/ + +#ifndef ARC_XISS_H +#define ARC_XISS_H + + +/* Currently none. */ + + +#endif /* ARC_XISS_H */ +/******************************************************************************/ diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index 019f4c8ac3e..3f5981d2963 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -93,6 +93,10 @@ static int can_use_hardware_watchpoint (struct value *); static void break_command_1 (char *, int, int); +// begin ARC +static void watch_range_command_1 (unsigned int, unsigned int, int, int); +// end ARC + static void mention (struct breakpoint *); struct breakpoint *set_raw_breakpoint (struct symtab_and_line, enum bptype); @@ -842,6 +846,13 @@ update_watchpoint (struct breakpoint *b, int reparse) struct bp_location *loc; bpstat bs; +// begin ARC + /* If this is a range watchpoint. */ + if (b->exp == NULL) + /* There is no need to change it. */ + return; +// end ARC + unlink_locations_from_global_list (b); for (loc = b->loc; loc;) { @@ -1017,6 +1028,9 @@ insert_bp_location (struct bp_location *bpt, /* Initialize the target-specific information. */ memset (&bpt->target_info, 0, sizeof (bpt->target_info)); bpt->target_info.placed_address = bpt->address; +// begin ARC + bpt->target_info.range = bpt->length; +// end ARC if (bpt->loc_type == bp_loc_software_breakpoint || bpt->loc_type == bp_loc_hardware_breakpoint) @@ -2103,6 +2117,30 @@ top: do_cleanups (old_chain); } +// begin ARC +static void +check_range (struct breakpoint *bp, enum async_reply_reason reason) +{ + CORE_ADDR addr; + + if (target_stopped_data_address (¤t_target, &addr)) + { + if (addr >= bp->loc->address && + addr < bp->loc->address + bp->loc->length) + { + if (ui_out_is_mi_like_p (uiout)) + ui_out_field_string + (uiout, "reason", + async_reason_lookup (reason)); + mention (bp); + ui_out_text(uiout, "triggered by access at address "); + ui_out_field_core_addr(uiout, "", addr); + ui_out_text (uiout, "\n"); + } + } +} +// end ARC + /* This is the normal print function for a bpstat. In the future, much of this logic could (should?) be moved to bpstat_stop_status, by having it set different print_it values. @@ -2221,76 +2259,103 @@ print_it_typical (bpstat bs) case bp_watchpoint: case bp_hardware_watchpoint: - if (bs->old_val != NULL) - { - annotate_watchpoint (b->number); - if (ui_out_is_mi_like_p (uiout)) - ui_out_field_string - (uiout, "reason", - async_reason_lookup (EXEC_ASYNC_WATCHPOINT_TRIGGER)); - mention (b); - ui_out_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "value"); - ui_out_text (uiout, "\nOld value = "); - value_print (bs->old_val, stb->stream, 0, Val_pretty_default); - ui_out_field_stream (uiout, "old", stb); - ui_out_text (uiout, "\nNew value = "); - value_print (b->val, stb->stream, 0, Val_pretty_default); - ui_out_field_stream (uiout, "new", stb); - do_cleanups (ui_out_chain); - ui_out_text (uiout, "\n"); - value_free (bs->old_val); - bs->old_val = NULL; - } +// begin ARC + if (b->exp) + { +// end ARC + if (bs->old_val != NULL) + { + annotate_watchpoint (b->number); + if (ui_out_is_mi_like_p (uiout)) + ui_out_field_string + (uiout, "reason", + async_reason_lookup (EXEC_ASYNC_WATCHPOINT_TRIGGER)); + mention (b); + ui_out_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "value"); + ui_out_text (uiout, "\nOld value = "); + value_print (bs->old_val, stb->stream, 0, Val_pretty_default); + ui_out_field_stream (uiout, "old", stb); + ui_out_text (uiout, "\nNew value = "); + value_print (b->val, stb->stream, 0, Val_pretty_default); + ui_out_field_stream (uiout, "new", stb); + do_cleanups (ui_out_chain); + ui_out_text (uiout, "\n"); + value_free (bs->old_val); + bs->old_val = NULL; + } +// begin ARC + } + else + check_range(b, EXEC_ASYNC_WATCHPOINT_TRIGGER); +// end ARC /* More than one watchpoint may have been triggered. */ return PRINT_UNKNOWN; break; case bp_read_watchpoint: - if (ui_out_is_mi_like_p (uiout)) - ui_out_field_string - (uiout, "reason", - async_reason_lookup (EXEC_ASYNC_READ_WATCHPOINT_TRIGGER)); - mention (b); - ui_out_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "value"); - ui_out_text (uiout, "\nValue = "); - value_print (b->val, stb->stream, 0, Val_pretty_default); - ui_out_field_stream (uiout, "value", stb); - do_cleanups (ui_out_chain); - ui_out_text (uiout, "\n"); +// begin ARC + if (b->exp) + { +// end ARC + if (ui_out_is_mi_like_p (uiout)) + ui_out_field_string + (uiout, "reason", + async_reason_lookup (EXEC_ASYNC_READ_WATCHPOINT_TRIGGER)); + mention (b); + ui_out_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "value"); + ui_out_text (uiout, "\nValue = "); + value_print (b->val, stb->stream, 0, Val_pretty_default); + ui_out_field_stream (uiout, "value", stb); + do_cleanups (ui_out_chain); + ui_out_text (uiout, "\n"); +// begin ARC + } + else + check_range(b, EXEC_ASYNC_READ_WATCHPOINT_TRIGGER); +// end ARC return PRINT_UNKNOWN; break; case bp_access_watchpoint: - if (bs->old_val != NULL) - { - annotate_watchpoint (b->number); - if (ui_out_is_mi_like_p (uiout)) - ui_out_field_string - (uiout, "reason", - async_reason_lookup (EXEC_ASYNC_ACCESS_WATCHPOINT_TRIGGER)); - mention (b); - ui_out_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "value"); - ui_out_text (uiout, "\nOld value = "); - value_print (bs->old_val, stb->stream, 0, Val_pretty_default); - ui_out_field_stream (uiout, "old", stb); - value_free (bs->old_val); - bs->old_val = NULL; - ui_out_text (uiout, "\nNew value = "); - } - else - { - mention (b); - if (ui_out_is_mi_like_p (uiout)) - ui_out_field_string - (uiout, "reason", - async_reason_lookup (EXEC_ASYNC_ACCESS_WATCHPOINT_TRIGGER)); - ui_out_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "value"); - ui_out_text (uiout, "\nValue = "); - } - value_print (b->val, stb->stream, 0,Val_pretty_default); - ui_out_field_stream (uiout, "new", stb); - do_cleanups (ui_out_chain); - ui_out_text (uiout, "\n"); +// begin ARC + if (b->exp) + { +// end ARC + if (bs->old_val != NULL) + { + annotate_watchpoint (b->number); + if (ui_out_is_mi_like_p (uiout)) + ui_out_field_string + (uiout, "reason", + async_reason_lookup (EXEC_ASYNC_ACCESS_WATCHPOINT_TRIGGER)); + mention (b); + ui_out_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "value"); + ui_out_text (uiout, "\nOld value = "); + value_print (bs->old_val, stb->stream, 0, Val_pretty_default); + ui_out_field_stream (uiout, "old", stb); + value_free (bs->old_val); + bs->old_val = NULL; + ui_out_text (uiout, "\nNew value = "); + } + else + { + mention (b); + if (ui_out_is_mi_like_p (uiout)) + ui_out_field_string + (uiout, "reason", + async_reason_lookup (EXEC_ASYNC_ACCESS_WATCHPOINT_TRIGGER)); + ui_out_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "value"); + ui_out_text (uiout, "\nValue = "); + } + value_print (b->val, stb->stream, 0,Val_pretty_default); + ui_out_field_stream (uiout, "new", stb); + do_cleanups (ui_out_chain); + ui_out_text (uiout, "\n"); +// begin ARC + } + else + check_range(b, EXEC_ASYNC_ACCESS_WATCHPOINT_TRIGGER); +// end ARC return PRINT_UNKNOWN; break; @@ -2529,6 +2594,11 @@ watchpoint_check (void *p) b = bs->breakpoint_at->owner; +// begin ARC + if (b->exp == NULL) + return WP_VALUE_CHANGED; +// end ARC + if (b->exp_valid_block == NULL) within_current_scope = 1; else @@ -2667,7 +2737,17 @@ bpstat_stop_status (CORE_ADDR bp_addr, ptid_t ptid) && b->type != bp_catch_exec) /* a non-watchpoint bp */ { if (bl->address != bp_addr) /* address doesn't match */ - continue; +// begin ARC + { + /* is the address within the b/p range? */ + if (bp_addr < b->loc->address || + bp_addr > b->loc->address + b->loc->length - 1) +// end ARC + continue; +// begin ARC + } +// end ARC + if (overlay_debugging /* unmapped overlay section */ && section_is_overlay (bl->section) && !section_is_mapped (bl->section)) @@ -2764,86 +2844,107 @@ bpstat_stop_status (CORE_ADDR bp_addr, ptid_t ptid) struct value *v; int must_check_value = 0; - if (b->type == bp_watchpoint) - /* For a software watchpoint, we must always check the - watched value. */ - must_check_value = 1; - else if (b->watchpoint_triggered == watch_triggered_yes) - /* We have a hardware watchpoint (read, write, or access) - and the target earlier reported an address watched by - this watchpoint. */ - must_check_value = 1; - else if (b->watchpoint_triggered == watch_triggered_unknown - && b->type == bp_hardware_watchpoint) - /* We were stopped by a hardware watchpoint, but the target could - not report the data address. We must check the watchpoint's - value. Access and read watchpoints are out of luck; without - a data address, we can't figure it out. */ - must_check_value = 1; - - if (must_check_value) - { - char *message = xstrprintf ("Error evaluating expression for watchpoint %d\n", - b->number); - struct cleanup *cleanups = make_cleanup (xfree, message); - int e = catch_errors (watchpoint_check, bs, message, - RETURN_MASK_ALL); - do_cleanups (cleanups); - switch (e) +// begin ARC + if (b->exp == NULL) + { + CORE_ADDR addr; + + if (!target_stopped_data_address (¤t_target, &addr)) + continue; + + if (addr >= b->loc->address && + addr < b->loc->address + b->loc->length) + { + /* Stop. */ + ++(b->hit_count); + } + } + else + { +// end ARC + if (b->type == bp_watchpoint) + /* For a software watchpoint, we must always check the + watched value. */ + must_check_value = 1; + else if (b->watchpoint_triggered == watch_triggered_yes) + /* We have a hardware watchpoint (read, write, or access) + and the target earlier reported an address watched by + this watchpoint. */ + must_check_value = 1; + else if (b->watchpoint_triggered == watch_triggered_unknown + && b->type == bp_hardware_watchpoint) + /* We were stopped by a hardware watchpoint, but the target could + not report the data address. We must check the watchpoint's + value. Access and read watchpoints are out of luck; without + a data address, we can't figure it out. */ + must_check_value = 1; + + if (must_check_value) { - case WP_DELETED: - /* We've already printed what needs to be printed. */ - bs->print_it = print_it_done; - /* Stop. */ - break; - case WP_VALUE_CHANGED: - if (b->type == bp_read_watchpoint) - { - /* Don't stop: read watchpoints shouldn't fire if - the value has changed. This is for targets - which cannot set read-only watchpoints. */ - bs->print_it = print_it_noop; - bs->stop = 0; - continue; - } - ++(b->hit_count); - break; - case WP_VALUE_NOT_CHANGED: - if (b->type == bp_hardware_watchpoint - || b->type == bp_watchpoint) - { - /* Don't stop: write watchpoints shouldn't fire if - the value hasn't changed. */ - bs->print_it = print_it_noop; - bs->stop = 0; - continue; - } - /* Stop. */ - ++(b->hit_count); - break; - default: - /* Can't happen. */ - case 0: - /* Error from catch_errors. */ - printf_filtered (_("Watchpoint %d deleted.\n"), b->number); - if (b->related_breakpoint) - b->related_breakpoint->disposition = disp_del_at_next_stop; - b->disposition = disp_del_at_next_stop; - /* We've already printed what needs to be printed. */ - bs->print_it = print_it_done; - break; + char *message = xstrprintf ("Error evaluating expression for watchpoint %d\n", + b->number); + struct cleanup *cleanups = make_cleanup (xfree, message); + int e = catch_errors (watchpoint_check, bs, message, + RETURN_MASK_ALL); + do_cleanups (cleanups); + switch (e) + { + case WP_DELETED: + /* We've already printed what needs to be printed. */ + bs->print_it = print_it_done; + /* Stop. */ + break; + case WP_VALUE_CHANGED: + if (b->type == bp_read_watchpoint) + { + /* Don't stop: read watchpoints shouldn't fire if + the value has changed. This is for targets + which cannot set read-only watchpoints. */ + bs->print_it = print_it_noop; + bs->stop = 0; + continue; + } + ++(b->hit_count); + break; + case WP_VALUE_NOT_CHANGED: + if (b->type == bp_hardware_watchpoint + || b->type == bp_watchpoint) + { + /* Don't stop: write watchpoints shouldn't fire if + the value hasn't changed. */ + bs->print_it = print_it_noop; + bs->stop = 0; + continue; + } + /* Stop. */ + ++(b->hit_count); + break; + default: + /* Can't happen. */ + case 0: + /* Error from catch_errors. */ + printf_filtered (_("Watchpoint %d deleted.\n"), b->number); + if (b->related_breakpoint) + b->related_breakpoint->disposition = disp_del_at_next_stop; + b->disposition = disp_del_at_next_stop; + /* We've already printed what needs to be printed. */ + bs->print_it = print_it_done; + break; + } } - } - else /* must_check_value == 0 */ - { - /* This is a case where some watchpoint(s) triggered, but - not at the address of this watchpoint, or else no - watchpoint triggered after all. So don't print - anything for this watchpoint. */ - bs->print_it = print_it_noop; - bs->stop = 0; - continue; - } + else /* must_check_value == 0 */ + { + /* This is a case where some watchpoint(s) triggered, but + not at the address of this watchpoint, or else no + watchpoint triggered after all. So don't print + anything for this watchpoint. */ + bs->print_it = print_it_noop; + bs->stop = 0; + continue; + } +// begin ARC + } +// end ARC } else { @@ -3464,14 +3565,31 @@ print_one_breakpoint_location (struct breakpoint *b, case bp_hardware_watchpoint: case bp_read_watchpoint: case bp_access_watchpoint: - /* Field 4, the address, is omitted (which makes the columns - not line up too nicely with the headers, but the effect - is relatively readable). */ - if (addressprint) - ui_out_field_skip (uiout, "addr"); - annotate_field (5); - print_expression (b->exp, stb->stream); - ui_out_field_stream (uiout, "what", stb); +// begin ARC + if (b->exp) + { +// end ARC + /* Field 4, the address, is omitted (which makes the columns + not line up too nicely with the headers, but the effect + is relatively readable). */ + if (addressprint) + ui_out_field_skip (uiout, "addr"); + annotate_field (5); + print_expression (b->exp, stb->stream); + ui_out_field_stream (uiout, "what", stb); +// begin ARC + } + else + { + /* exp_string has format "<address>:<bytes>" */ + char* colon = strchr(b->exp_string, ':'); + *colon = 0; + ui_out_field_string(uiout, "addr", b->exp_string); + ui_out_field_string(uiout, "what", colon+1); + ui_out_text(uiout, "-byte range"); + *colon = ':'; + } +// end ARC break; case bp_catch_load: @@ -3546,7 +3664,18 @@ print_one_breakpoint_location (struct breakpoint *b, if (b->loc == NULL || loc->shlib_disabled) ui_out_field_string (uiout, "addr", "<PENDING>"); else - ui_out_field_core_addr (uiout, "addr", loc->address); +// begin ARC + { +// end ARC + ui_out_field_core_addr (uiout, "addr", loc->address); +// begin ARC + if (b->exp == NULL && b->loc->length > 0) + { + ui_out_field_int(uiout, "what", b->loc->length); + ui_out_text(uiout, "-byte range "); + } + } +// end ARC } annotate_field (5); if (!header_of_multiple) @@ -4764,7 +4893,14 @@ mention (struct breakpoint *b) ui_out_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "wpt"); ui_out_field_int (uiout, "number", b->number); ui_out_text (uiout, ": "); - print_expression (b->exp, stb->stream); +// begin ARC + if (b->exp) +// end ARC + print_expression (b->exp, stb->stream); +// begin ARC + else + ui_out_text (uiout, b->exp_string); +// end ARC ui_out_field_stream (uiout, "exp", stb); do_cleanups (ui_out_chain); break; @@ -4773,7 +4909,14 @@ mention (struct breakpoint *b) ui_out_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "hw-rwpt"); ui_out_field_int (uiout, "number", b->number); ui_out_text (uiout, ": "); - print_expression (b->exp, stb->stream); +// begin ARC + if (b->exp) +// end ARC + print_expression (b->exp, stb->stream); +// begin ARC + else + ui_out_text (uiout, b->exp_string); +// end ARC ui_out_field_stream (uiout, "exp", stb); do_cleanups (ui_out_chain); break; @@ -4782,7 +4925,14 @@ mention (struct breakpoint *b) ui_out_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "hw-awpt"); ui_out_field_int (uiout, "number", b->number); ui_out_text (uiout, ": "); - print_expression (b->exp, stb->stream); +// begin ARC + if (b->exp) +// end ARC + print_expression (b->exp, stb->stream); +// begin ARC + else + ui_out_text (uiout, b->exp_string); +// end ARC ui_out_field_stream (uiout, "exp", stb); do_cleanups (ui_out_chain); break; @@ -4850,6 +5000,10 @@ mention (struct breakpoint *b) { printf_filtered (" at "); fputs_filtered (paddress (b->loc->address), gdb_stdout); +// begin ARC + if (b->exp == NULL && b->loc->length > 0) + printf_filtered(" covering %u bytes", b->loc->length); +// end ARC } if (b->source_file) printf_filtered (": file %s, line %d.", @@ -5536,12 +5690,28 @@ break_command (char *arg, int from_tty) break_command_1 (arg, 0, from_tty); } +// begin ARC +void +watch_range_command (unsigned int address, unsigned int bytes, int accessflag, int from_tty) +{ + watch_range_command_1 (address, bytes, accessflag, from_tty); +} +// end ARC + void tbreak_command (char *arg, int from_tty) { break_command_1 (arg, BP_TEMPFLAG, from_tty); } +// begin ARC +void +hbreak_command_wrapper (char *arg, int from_tty) +{ + hbreak_command (arg, from_tty); +} +// end ARC + static void hbreak_command (char *arg, int from_tty) { @@ -5837,6 +6007,81 @@ watch_command_1 (char *arg, int accessflag, int from_tty) mention (b); } + +// begin ARC +/* accessflag: hw_write: watch write, + hw_read: watch read, + hw_access: watch access (read or write) + hw_execute: execute access */ +static void +watch_range_command_1 (unsigned int address, unsigned int bytes, int accessflag, int from_tty) +{ + struct breakpoint *b; + struct symtab_and_line sal; + int i, other_type_used, target_resources_ok; + enum bptype bp_type; + enum target_hw_bp_type wp_type; + char exp[50]; + + (void) sprintf(exp, "0x%08X:%u", address, bytes); + + init_sal (&sal); /* initialize to zeroes */ + + if (accessflag == hw_read) + { + bp_type = bp_read_watchpoint; + wp_type = hw_read; + } + else if (accessflag == hw_access) + { + bp_type = bp_access_watchpoint; + wp_type = hw_access; + } + else if (accessflag == hw_write) + { + bp_type = bp_hardware_watchpoint; + wp_type = hw_write; + } + else + { + bp_type = bp_hardware_breakpoint; + wp_type = hw_execute; + } + + i = hw_watchpoint_used_count (bp_type, &other_type_used); + target_resources_ok = + TARGET_CAN_USE_HARDWARE_WATCHPOINT (bp_type, i, other_type_used); + if (target_resources_ok == 0 && bp_type != bp_hardware_watchpoint) + error (_("Target does not support this type of hardware watchpoint.")); + if (target_resources_ok < 0 && bp_type != bp_hardware_watchpoint) + error (_("Target can only support one kind of HW watchpoint at a time.")); + + + /* Now set up the breakpoint. */ + b = set_raw_breakpoint (sal, bp_type); + set_breakpoint_count (breakpoint_count + 1); + b->number = breakpoint_count; + b->disposition = disp_donttouch; + b->exp = NULL; + b->exp_valid_block = NULL; + b->exp_string = xstrdup(exp); + b->val = NULL; + //b->cond = NULL; 6.8 has removed this element? + b->cond_string = 0; + b->loc->address = (CORE_ADDR) address; + b->loc->requested_address = b->loc->address; + b->loc->length = bytes; + + b->loc->watchpoint_type = wp_type; + + memset (&b->watchpoint_frame, 0, sizeof (b->watchpoint_frame)); + + mention (b); +} +// end ARC + + + /* Return count of locations need to be watched and can be handled in hardware. If the watchpoint can not be handled in hardware return zero. */ @@ -7911,8 +8156,18 @@ insert_single_step_breakpoint (CORE_ADDR next_pc) *bpt_p = deprecated_insert_raw_breakpoint (next_pc); if (*bpt_p == NULL) - error (_("Could not insert single-step breakpoint at 0x%s"), - paddr_nz (next_pc)); + { + /* richards/2008/10/27 ARC bug fix: if setting the (second) + * b/p failed, we must unset the first + * + * gdb bug: 9649 + */ + if (single_step_breakpoints[0] != NULL) + remove_single_step_breakpoints (); + + error (_("Could not insert single-step breakpoint at 0x%s"), + paddr_nz (next_pc)); + } } /* Remove and delete any breakpoints used for software single step. */ diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h index 41730c0b311..a741f862d6a 100644 --- a/gdb/breakpoint.h +++ b/gdb/breakpoint.h @@ -175,6 +175,11 @@ enum target_hw_bp_type struct bp_target_info { +// begin ARC + /* Range breakpoints added */ + unsigned int range; +//end ARC + /* Address at which the breakpoint was placed. This is normally the same as ADDRESS from the bp_location, except when adjustment happens in gdbarch_breakpoint_from_pc. The most common form of @@ -272,8 +277,8 @@ struct bp_location is not a special value for this field. Valid for all types except bp_loc_other. */ CORE_ADDR address; - - /* For hardware watchpoints, the size of data ad ADDRESS being watches. */ + + /* For hardware watchpoints, the size of data at ADDRESS being watched. */ int length; /* Type of hardware watchpoint. */ @@ -702,6 +707,10 @@ extern void breakpoint_clear_ignore_counts (void); extern void break_command (char *, int); +// begin ARC +extern void watch_range_command (unsigned int, unsigned int, int, int); +//end ARC + extern void hbreak_command_wrapper (char *, int); extern void thbreak_command_wrapper (char *, int); extern void rbreak_command_wrapper (char *, int); diff --git a/gdb/cli/cli-script.c b/gdb/cli/cli-script.c index e6fbe3f1397..dac43454d8c 100644 --- a/gdb/cli/cli-script.c +++ b/gdb/cli/cli-script.c @@ -287,8 +287,15 @@ execute_user_command (struct cmd_list_element *c, char *args) cmdlines = c->user_commands; if (cmdlines == 0) + { /* Null command */ +// begin ARC + /* If there are no user commands to be executed, execute the + callback function instead. */ + c->func(c, NULL, 0); +// end ARC return; + } if (++user_call_depth > max_user_call_depth) error (_("Max user call depth exceeded -- command aborted.")); diff --git a/gdb/config.in b/gdb/config.in index b6aba7d2852..f5fa97bac80 100644 --- a/gdb/config.in +++ b/gdb/config.in @@ -45,6 +45,12 @@ /* Define to the default OS ABI for this configuration. */ #undef GDB_OSABI_DEFAULT +/* targetfile */ +#undef GDB_TM_FILE + +/* hostfile */ +#undef GDB_XM_FILE + /* Define to 1 if you have `alloca', as a function or macro. */ #undef HAVE_ALLOCA @@ -152,6 +158,15 @@ /* Define if you have the expat library. */ #undef HAVE_LIBEXPAT +/* Define if you have the ARC xISS library. */ +#undef HAVE_LIBXISS + +/* Define if you are building the ARC bare-board debugger. */ +#undef ARC_ELF32_TARGET + +/* Define if you are building the ARC Linux debugger. */ +#undef ARC_LINUX_TARGET + /* Define to 1 if you have the `m' library (-lm). */ #undef HAVE_LIBM diff --git a/gdb/config/arc/a4-jtag.mt b/gdb/config/arc/a4-jtag.mt deleted file mode 100644 index e3907baec94..00000000000 --- a/gdb/config/arc/a4-jtag.mt +++ /dev/null @@ -1,3 +0,0 @@ -# Target: ARC embedded system -TDEPFILES= arc-tdep.o arc-jtag.o arc-jtag-tdep.o arc-jtag-ops.o -DEPRECATED_TM_FILE= tm-a4-jtag.h diff --git a/gdb/config/arc/arc.mt b/gdb/config/arc/arc.mt deleted file mode 100644 index cb98a49d01b..00000000000 --- a/gdb/config/arc/arc.mt +++ /dev/null @@ -1,4 +0,0 @@ -# Target: arc processor -TDEPFILES= arc-tdep.o monitor.o arc-rom.o dsrec.o remote-arc-sdi.o -SIM_OBS = remote-sim.o -SIM = ../sim/arc/libsim.a diff --git a/gdb/config/arc/embed.mt b/gdb/config/arc/embed.mt deleted file mode 100644 index a6d47a23052..00000000000 --- a/gdb/config/arc/embed.mt +++ /dev/null @@ -1,3 +0,0 @@ -# Target: ARC embedded system -TDEPFILES= arc-tdep.o arc-jtag.o arc-jtag-tdep.o arc-jtag-ops.o -DEPRECATED_TM_FILE= tm-embed.h diff --git a/gdb/config/arc/linux.mt b/gdb/config/arc/linux.mt deleted file mode 100644 index 0c5f000a9e3..00000000000 --- a/gdb/config/arc/linux.mt +++ /dev/null @@ -1,3 +0,0 @@ -# Target: ARC based machine running GNU/Linux -DEPRECATED_TM_FILE= tm-linux.h -TDEPFILES= arc-tdep.o arc-linux-tdep.o solib.o solib-svr4.o solib-legacy.o corelow.o diff --git a/gdb/config/arc/tm-a4-jtag.h b/gdb/config/arc/tm-a4-jtag.h deleted file mode 100644 index dc5ab71c16f..00000000000 --- a/gdb/config/arc/tm-a4-jtag.h +++ /dev/null @@ -1,103 +0,0 @@ -#define ARC4_JTAG 1 -#define CONFIG_OSABI GDB_OSABI_UNKNOWN - -struct gdbarch *arc_jtag_init (struct gdbarch *gdbarch); -#define CONFIG_INIT_TDEP arc_jtag_init - -/* The core regnums here are the same as the hardware register numbers. We - cannot do that for aux registers, because the aux regs on the h/w do not - have contiguous numbers. */ -enum arc4_jtag_regnums - { - ARC_FP_REGNUM = 27, - ARC_SP_REGNUM , - ARC_ILINK1_REGNUM , - ARC_ILINK2_REGNUM , - ARC_BLINK_REGNUM , - /* Extension core regs are 32..59 inclusive. */ - ARC_LP_COUNT_REGNUM = 60, - /* 61 is reserved, 62 is not a real register. */ - ARC_PCL_REGNUM = 63, - - /* Now the aux registers. */ - - ARC_STATUS_REGNUM = 64, - ARC_SEMAPHORE_REGNUM , - ARC_LP_START_REGNUM , - ARC_LP_END_REGNUM , - ARC_IDENTITY_REGNUM , - ARC_DEBUG_REGNUM , -#ifndef ARC4_JTAG - ARC_PC_REGNUM , - ARC_STATUS32_REGNUM , - ARC_STATUS32_L1_REGNUM , - ARC_STATUS32_L2_REGNUM , - - ARC_COUNT0_REGNUM , - ARC_CONTROL0_REGNUM , - ARC_LIMIT0_REGNUM , - ARC_INT_VECTOR_BASE_REGNUM , - ARC_AUX_MACMODE_REGNUM , - ARC_AUX_IRQ_LV12_REGNUM , - - ARC_COUNT1_REGNUM , - ARC_CONTROL1_REGNUM , - ARC_LIMIT1_REGNUM , - ARC_AUX_IRQ_LEV_REGNUM , - ARC_AUX_IRQ_HINT_REGNUM , - ARC_ERET_REGNUM , - ARC_ERBTA_REGNUM , - ARC_ERSTATUS_REGNUM , - ARC_ECR_REGNUM , - ARC_EFA_REGNUM , - ARC_ICAUSE1_REGNUM , - ARC_ICAUSE2_REGNUM , - ARC_AUX_IENABLE_REGNUM , - ARC_AUX_ITRIGGER_REGNUM , - ARC_XPU_REGNUM , - ARC_BTA_REGNUM , - ARC_BTA_L1_REGNUM , - ARC_BTA_L2_REGNUM , - ARC_AUX_IRQ_PULSE_CANCEL_REGNUM , - ARC_AUX_IRQ_PENDING_REGNUM , - - /* Build configuration registers. */ - ARC_BCR_0_REGNUM , - ARC_BCR_1_REGNUM , - ARC_BCR_2_REGNUM , - ARC_BCR_3_REGNUM , - ARC_BCR_4_REGNUM , - ARC_BCR_5_REGNUM , - ARC_BCR_6_REGNUM , - ARC_BCR_7_REGNUM , - ARC_BCR_8_REGNUM , - ARC_BCR_9_REGNUM , - ARC_BCR_A_REGNUM , - ARC_BCR_B_REGNUM , - ARC_BCR_C_REGNUM , - ARC_BCR_D_REGNUM , - ARC_BCR_E_REGNUM , - ARC_BCR_F_REGNUM , - ARC_BCR_10_REGNUM , - ARC_BCR_11_REGNUM , - ARC_BCR_12_REGNUM , - - ARC_BCR_13_REGNUM , - ARC_BCR_14_REGNUM , - ARC_BCR_15_REGNUM , - ARC_BCR_16_REGNUM , - ARC_BCR_17_REGNUM , - ARC_BCR_18_REGNUM , - ARC_BCR_19_REGNUM , - ARC_BCR_1A_REGNUM , - ARC_BCR_1B_REGNUM , - ARC_BCR_1C_REGNUM , - ARC_BCR_1D_REGNUM , - ARC_BCR_1E_REGNUM , - ARC_BCR_1F_REGNUM , - -#endif - ARC_NR_REGS - - }; - diff --git a/gdb/config/arc/tm-embed.h b/gdb/config/arc/tm-embed.h index 9ac1c1adac1..5cd7271fc23 100644 --- a/gdb/config/arc/tm-embed.h +++ b/gdb/config/arc/tm-embed.h @@ -1,101 +1,77 @@ +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. -#define CONFIG_OSABI GDB_OSABI_UNKNOWN + Copyright 2005, 2008, 2009 Free Software Foundation, Inc. -struct gdbarch *arc_jtag_init (struct gdbarch *gdbarch); -#define CONFIG_INIT_TDEP arc_jtag_init + Contributed by ARC International (www.arc.com) -/* The core regnums here are the same as the hardware register numbers. We - cannot do that for aux registers, because the aux regs on the h/w do not - have contiguous numbers. */ + Authors: + Codito Technologies Pvt. Ltd. + Richard Stuckey <richard.stuckey@arc.com> + + 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 3 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, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This header file defines register numbers for the arc-elf32 */ +/* configuration of the ARC gdb. */ +/* */ +/******************************************************************************/ + +#ifndef ARC_TM_EMBED_H +#define ARC_TM_EMBED_H + +#include "gdbarch.h" +#include "arc-elf32-tdep.h" +#include "arc-registers.h" + + +#define CONFIG_OSABI GDB_OSABI_UNKNOWN + +#define CONFIG_INIT_TDEP (void) arc_elf32_initialize (gdbarch, arches); + + +/* These are hardware register numbers (i.e. NOT gdb register numbers). */ enum arc700_jtag_regnums - { - ARC_FP_REGNUM = 27, - ARC_SP_REGNUM , - ARC_ILINK1_REGNUM , - ARC_ILINK2_REGNUM , - ARC_BLINK_REGNUM , - /* Extension core regs are 32..59 inclusive. */ +{ + /* Regnums 0 .. 26 are R0 .. R26 */ + + ARC_FP_REGNUM = 27, + ARC_SP_REGNUM, + ARC_ILINK1_REGNUM, + ARC_ILINK2_REGNUM, + ARC_BLINK_REGNUM, + + /* Extension core regs are R32 .. R59 inclusive. */ + ARC_LP_COUNT_REGNUM = 60, + /* 61 is reserved, 62 is not a real register. */ - ARC_PCL_REGNUM = 63, - - /* Now the aux registers. */ - - ARC_STATUS_REGNUM = 64, - ARC_SEMAPHORE_REGNUM , - ARC_LP_START_REGNUM , - ARC_LP_END_REGNUM , - ARC_IDENTITY_REGNUM , - ARC_DEBUG_REGNUM , - ARC_PC_REGNUM , - ARC_STATUS32_REGNUM , - ARC_STATUS32_L1_REGNUM , - ARC_STATUS32_L2_REGNUM , - - ARC_COUNT0_REGNUM , - ARC_CONTROL0_REGNUM , - ARC_LIMIT0_REGNUM , - ARC_INT_VECTOR_BASE_REGNUM , - ARC_AUX_MACMODE_REGNUM , - ARC_AUX_IRQ_LV12_REGNUM , - - ARC_COUNT1_REGNUM , - ARC_CONTROL1_REGNUM , - ARC_LIMIT1_REGNUM , - ARC_AUX_IRQ_LEV_REGNUM , - ARC_AUX_IRQ_HINT_REGNUM , - ARC_ERET_REGNUM , - ARC_ERBTA_REGNUM , - ARC_ERSTATUS_REGNUM , - ARC_ECR_REGNUM , - ARC_EFA_REGNUM , - ARC_ICAUSE1_REGNUM , - ARC_ICAUSE2_REGNUM , - ARC_AUX_IENABLE_REGNUM , - ARC_AUX_ITRIGGER_REGNUM , - ARC_XPU_REGNUM , - ARC_BTA_REGNUM , - ARC_BTA_L1_REGNUM , - ARC_BTA_L2_REGNUM , - ARC_AUX_IRQ_PULSE_CANCEL_REGNUM , - ARC_AUX_IRQ_PENDING_REGNUM , - - /* Build configuration registers. */ - ARC_BCR_0_REGNUM , - ARC_BCR_1_REGNUM , - ARC_BCR_2_REGNUM , - ARC_BCR_3_REGNUM , - ARC_BCR_4_REGNUM , - ARC_BCR_5_REGNUM , - ARC_BCR_6_REGNUM , - ARC_BCR_7_REGNUM , - ARC_BCR_8_REGNUM , - ARC_BCR_9_REGNUM , - ARC_BCR_A_REGNUM , - ARC_BCR_B_REGNUM , - ARC_BCR_C_REGNUM , - ARC_BCR_D_REGNUM , - ARC_BCR_E_REGNUM , - ARC_BCR_F_REGNUM , - ARC_BCR_10_REGNUM , - ARC_BCR_11_REGNUM , - ARC_BCR_12_REGNUM , - - ARC_BCR_13_REGNUM , - ARC_BCR_14_REGNUM , - ARC_BCR_15_REGNUM , - ARC_BCR_16_REGNUM , - ARC_BCR_17_REGNUM , - ARC_BCR_18_REGNUM , - ARC_BCR_19_REGNUM , - ARC_BCR_1A_REGNUM , - ARC_BCR_1B_REGNUM , - ARC_BCR_1C_REGNUM , - ARC_BCR_1D_REGNUM , - ARC_BCR_1E_REGNUM , - ARC_BCR_1F_REGNUM , - - - ARC_NR_REGS - }; + ARC_PCL_REGNUM = 63, + + /* end marker: this is not a register, but its integer value gives the number + * of registers + */ + ARC_REG_END_MARKER +}; + + +#define ARC_NR_PSEUDO_REGS 0 +#define ARC_NR_REGS (int) (arc_core_register_count(gdbarch) + \ + arc_aux_register_count (gdbarch)) +#endif /* ARC_TM_EMBED_H */ +/******************************************************************************/ diff --git a/gdb/config/arc/tm-linux.h b/gdb/config/arc/tm-linux.h index 18b12402fb0..15b0d9e8d0a 100644 --- a/gdb/config/arc/tm-linux.h +++ b/gdb/config/arc/tm-linux.h @@ -1,35 +1,87 @@ -#include "config/tm-linux.h" +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. -#define CONFIG_OSABI GDB_OSABI_LINUX + Copyright 2005, 2008, 2009 Free Software Foundation, Inc. + + Contributed by ARC International (www.arc.com) + + Authors: + Codito Technologies Pvt. Ltd. + Richard Stuckey <richard.stuckey@arc.com> + + 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 3 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, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This header file defines register numbers for the arc-linux-uclibc */ +/* configuration of the ARC gdb. */ +/* */ +/******************************************************************************/ + +#ifndef ARC_TM_LINUX_H +#define ARC_TM_LINUX_H + +#include "arc-linux-tdep.h" + + +#define CONFIG_OSABI GDB_OSABI_LINUX /* Do nothing. */ -#define CONFIG_INIT_TDEP {} +#define CONFIG_INIT_TDEP {} + enum arc700_linux_regnums - { - /* Regnums 0..26 are R0..R26 */ - ARC_BTA_REGNUM = 27, - ARC_LP_START_REGNUM = 28, - ARC_LP_END_REGNUM = 29, - ARC_LP_COUNT_REGNUM = 30, - ARC_STATUS32_REGNUM = 31, - ARC_BLINK_REGNUM = 32, - ARC_FP_REGNUM = 33, - ARC_SP_REGNUM = 34, - ARC_EFA_REGNUM = 35, - ARC_RET_REGNUM = 36, - ARC_ORIG_R8_REGNUM = 37, - ARC_STOP_PC_REGNUM = 38 - }; - -#define ARC_NR_REGS 39 - -/* Pseudo-regs. */ -#define ARC_ILINK1_REGNUM (NUM_REGS) -#define ARC_ILINK2_REGNUM (NUM_REGS+1) -#define ARC_ERET_REGNUM (NUM_REGS+2) -#define ARC_STATUS32_L1_REGNUM (NUM_REGS+3) -#define ARC_STATUS32_L2_REGNUM (NUM_REGS+4) -#define ARC_ERSTATUS_REGNUM (NUM_REGS+5) - -#define ARC_NR_PSEUDO_REGS 6 +{ + /* Regnums 0 .. 26 are R0 .. R26 */ + ARC_BTA_REGNUM = 27, + ARC_LP_START_REGNUM = 28, + ARC_LP_END_REGNUM = 29, + ARC_LP_COUNT_REGNUM = 30, + ARC_STATUS32_REGNUM = 31, + ARC_BLINK_REGNUM = 32, + ARC_FP_REGNUM = 33, + ARC_SP_REGNUM = 34, + ARC_EFA_REGNUM = 35, + ARC_RET_REGNUM = 36, + ARC_ORIG_R8_REGNUM = 37, + ARC_STOP_PC_REGNUM = 38, + + /* end marker: this is not a register, but its integer value gives the number + * of registers + */ + ARC_REG_END_MARKER +}; + + +/* Pseudo-registers. */ + +enum arc700_linux_pseudo_regnums +{ + ARC_ILINK1_REGNUM = (int) ARC_REG_END_MARKER, + ARC_ILINK2_REGNUM, + ARC_ERET_REGNUM, + ARC_STATUS32_L1_REGNUM, + ARC_STATUS32_L2_REGNUM, + ARC_ERSTATUS_REGNUM +}; + + +#define ARC_NR_PSEUDO_REGS 6 +#define ARC_NR_REGS (int) ARC_REG_END_MARKER + + +#endif /* ARC_TM_LINUX_H */ +/******************************************************************************/ diff --git a/gdb/configure b/gdb/configure index e0f36b23f48..c39040b7cba 100755 --- a/gdb/configure +++ b/gdb/configure @@ -313,7 +313,7 @@ ac_includes_default="\ ac_subdirs_all="$ac_subdirs_all gdbtk" ac_subdirs_all="$ac_subdirs_all multi-ice" ac_subdirs_all="$ac_subdirs_all gdbserver" -ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS MAINTAINER_MODE_TRUE MAINTAINER_MODE_FALSE MAINT CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP EGREP build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os USE_NLS LIBINTL LIBINTL_DEP INCINTL XGETTEXT GMSGFMT POSUB CATALOGS DATADIRNAME INSTOBJEXT GENCAT CATOBJEXT localedir PACKAGE subdirs TARGET_OBS AWK INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA LN_S RANLIB ac_ct_RANLIB YACC AR ac_ct_AR DLLTOOL ac_ct_DLLTOOL WINDRES ac_ct_WINDRES MIG ac_ct_MIG READLINE READLINE_DEPS READLINE_CFLAGS HAVE_LIBEXPAT LIBEXPAT LTLIBEXPAT ALLOCA CONFIG_LDFLAGS TARGET_SYSTEM_ROOT TARGET_SYSTEM_ROOT_DEFINE WARN_CFLAGS WERROR_CFLAGS SER_HARDWIRE WIN32LIBS LIBGUI GUI_CFLAGS_X WIN32LDAPP TCL_VERSION TCL_MAJOR_VERSION TCL_MINOR_VERSION TCL_CC TCL_DEFS TCL_SHLIB_CFLAGS TCL_SHLIB_LD TCL_SHLIB_LD_LIBS TCL_SHLIB_SUFFIX TCL_DL_LIBS TCL_LD_FLAGS TCL_LD_SEARCH_FLAGS TCL_CC_SEARCH_FLAGS TCL_COMPAT_OBJS TCL_RANLIB TCL_BUILD_LIB_SPEC TCL_LIB_SPEC TCL_LIB_VERSIONS_OK TK_VERSION TK_DEFS TK_BUILD_INCLUDES TK_XINCLUDES TK_XLIBSW TK_BUILD_LIB_SPEC TK_LIB_SPEC TCLHDIR TKHDIR ITCLHDIR ITKHDIR ITCL_VERSION ITCL_DEFS ITCL_BUILD_INCLUDES ITCL_BUILD_LIB_SPEC ITCL_LIB_SPEC ITK_VERSION ITK_DEFS ITK_BUILD_INCLUDES ITK_BUILD_LIB_SPEC ITK_LIB_SPEC X_CFLAGS X_LDFLAGS X_LIBS TCL_DEPS TK_DEPS ITCLLIB ITCL_DEPS ITKLIB ITK_DEPS GDBTKLIBS GDBTK_CFLAGS GDBTK_SRC_DIR SIM SIM_OBS ENABLE_CFLAGS PROFILE_CFLAGS CONFIG_OBS CONFIG_DEPS CONFIG_SRCS CONFIG_ALL CONFIG_CLEAN CONFIG_INSTALL CONFIG_UNINSTALL target_subdir frags nm_h LIBICONV LIBOBJS LTLIBOBJS' +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS MAINTAINER_MODE_TRUE MAINTAINER_MODE_FALSE MAINT CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP EGREP build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os USE_NLS LIBINTL LIBINTL_DEP INCINTL XGETTEXT GMSGFMT POSUB CATALOGS DATADIRNAME INSTOBJEXT GENCAT CATOBJEXT localedir PACKAGE subdirs TARGET_OBS AWK INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA LN_S RANLIB ac_ct_RANLIB YACC AR ac_ct_AR DLLTOOL ac_ct_DLLTOOL WINDRES ac_ct_WINDRES MIG ac_ct_MIG READLINE READLINE_DEPS READLINE_CFLAGS HAVE_LIBEXPAT LIBEXPAT LTLIBEXPAT XISS_INCLUDES ALLOCA CONFIG_LDFLAGS TARGET_SYSTEM_ROOT TARGET_SYSTEM_ROOT_DEFINE WARN_CFLAGS WERROR_CFLAGS SER_HARDWIRE WIN32LIBS LIBGUI GUI_CFLAGS_X WIN32LDAPP TCL_VERSION TCL_MAJOR_VERSION TCL_MINOR_VERSION TCL_CC TCL_DEFS TCL_SHLIB_CFLAGS TCL_SHLIB_LD TCL_SHLIB_LD_LIBS TCL_SHLIB_SUFFIX TCL_DL_LIBS TCL_LD_FLAGS TCL_LD_SEARCH_FLAGS TCL_CC_SEARCH_FLAGS TCL_COMPAT_OBJS TCL_RANLIB TCL_BUILD_LIB_SPEC TCL_LIB_SPEC TCL_LIB_VERSIONS_OK TK_VERSION TK_DEFS TK_BUILD_INCLUDES TK_XINCLUDES TK_XLIBSW TK_BUILD_LIB_SPEC TK_LIB_SPEC TCLHDIR TKHDIR ITCLHDIR ITKHDIR ITCL_VERSION ITCL_DEFS ITCL_BUILD_INCLUDES ITCL_BUILD_LIB_SPEC ITCL_LIB_SPEC ITK_VERSION ITK_DEFS ITK_BUILD_INCLUDES ITK_BUILD_LIB_SPEC ITK_LIB_SPEC X_CFLAGS X_LDFLAGS X_LIBS TCL_DEPS TK_DEPS ITCLLIB ITCL_DEPS ITKLIB ITK_DEPS GDBTKLIBS GDBTK_CFLAGS GDBTK_SRC_DIR SIM SIM_OBS ENABLE_CFLAGS PROFILE_CFLAGS CONFIG_OBS CONFIG_DEPS CONFIG_SRCS CONFIG_ALL CONFIG_CLEAN CONFIG_INSTALL CONFIG_UNINSTALL target_subdir frags nm_h LIBICONV LIBOBJS LTLIBOBJS' ac_subst_files='host_makefile_frag' # Initialize some variables set by options. @@ -884,6 +884,7 @@ Optional Packages: --with-gnu-ld assume the C compiler uses GNU ld default=no --with-libexpat-prefix[=DIR] search for libexpat in DIR/include and DIR/lib --without-libexpat-prefix don't search for libexpat in includedir and libdir + --with-xiss include xiss support (auto/yes/no) --without-included-regex don't use included regex; this is the default on systems with version 2 of the GNU C library (use with caution on other system) @@ -5573,9 +5574,179 @@ _ACEOF fi +echo "$as_me:$LINENO: checking for inflate" >&5 +echo $ECHO_N "checking for inflate... $ECHO_C" >&6 +if test "${ac_cv_func_inflate+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define inflate to an innocuous variant, in case <limits.h> declares inflate. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define inflate innocuous_inflate + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char inflate (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef inflate + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char inflate (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_inflate) || defined (__stub___inflate) +choke me +#else +char (*f) () = inflate; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != inflate; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_inflate=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_func_inflate=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_func_inflate" >&5 +echo "${ECHO_T}$ac_cv_func_inflate" >&6 +if test $ac_cv_func_inflate = yes; then + : +else + +echo "$as_me:$LINENO: checking for inflate in -lz" >&5 +echo $ECHO_N "checking for inflate in -lz... $ECHO_C" >&6 +if test "${ac_cv_lib_z_inflate+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lz $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char inflate (); +int +main () +{ +inflate (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_z_inflate=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_z_inflate=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_z_inflate" >&5 +echo "${ECHO_T}$ac_cv_lib_z_inflate" >&6 +if test $ac_cv_lib_z_inflate = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBZ 1 +_ACEOF + + LIBS="-lz $LIBS" + +fi + +fi + + # We need to link with -lw to get `wctype' on Solaris before Solaris # 2.6. Solaris 2.6 and beyond have this function in libc, and have a -# libw that some versions of the GNU linker cannot hanle (GNU ld 2.9.1 +# libw that some versions of the GNU linker cannot handle (GNU ld 2.9.1 # is known to have this problem). Therefore we avoid libw if we can. echo "$as_me:$LINENO: checking for wctype" >&5 echo $ECHO_N "checking for wctype... $ECHO_C" >&6 @@ -7228,6 +7399,52 @@ done fi fi + + +# Check whether --with-xiss or --without-xiss was given. +if test "${with_xiss+set}" = set; then + withval="$with_xiss" + +else + with_xiss=auto +fi; +echo "$as_me:$LINENO: checking whether to use xiss" >&5 +echo $ECHO_N "checking whether to use xiss... $ECHO_C" >&6 +echo "$as_me:$LINENO: result: $with_xiss" >&5 +echo "${ECHO_T}$with_xiss" >&6 + +if test "${with_xiss}" = yes; then + HAVE_LIBXISS=yes + +cat >>confdefs.h <<\_ACEOF +#define HAVE_LIBXISS 1 +_ACEOF + + XISS_INCLUDES="-I${with_xiss_prefix}/include" + +else + { echo "$as_me:$LINENO: WARNING: xiss support disabled; target arcxiss will be unavailable." >&5 +echo "$as_me: WARNING: xiss support disabled; target arcxiss will be unavailable." >&2;} + HAVE_LIBXISS=no +fi + + +if test "${target}" = "arc-unknown-elf32"; then + +cat >>confdefs.h <<\_ACEOF +#define ARC_ELF32_TARGET 1 +_ACEOF + +fi +if test "${target}" = "arc-unknown-linux-uclibc"; then + +cat >>confdefs.h <<\_ACEOF +#define ARC_LINUX_TARGET 1 +_ACEOF + +fi + + # ------------------------- # # Checks for header files. # # ------------------------- # @@ -22997,7 +23214,7 @@ OLD_LIBS=$LIBS CFLAGS="$CFLAGS -I${srcdir}/../include -I../bfd -I${srcdir}/../bfd" LDFLAGS="$LDFLAGS -L../bfd -L../libiberty" intl=`echo $LIBINTL | sed 's,${top_builddir}/,,g'` -LIBS="$LIBS -lbfd -liberty $intl" +LIBS="$LIBS -lbfd -liberty $intl -lz" echo "$as_me:$LINENO: checking for ELF support in BFD" >&5 echo $ECHO_N "checking for ELF support in BFD... $ECHO_C" >&6 if test "${gdb_cv_var_elf+set}" = set; then @@ -24235,7 +24452,7 @@ ac_x_header_dirs=' /usr/openwin/share/include' if test "$ac_x_includes" = no; then - # Guess where to find include files, by looking for Intrinsic.h. + # Guess where to find include files, by looking for Xlib.h. # First, try using that file with no special directory specified. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ @@ -24243,7 +24460,7 @@ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ -#include <X11/Intrinsic.h> +#include <X11/Xlib.h> _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 @@ -24270,7 +24487,7 @@ else sed 's/^/| /' conftest.$ac_ext >&5 for ac_dir in $ac_x_header_dirs; do - if test -r "$ac_dir/X11/Intrinsic.h"; then + if test -r "$ac_dir/X11/Xlib.h"; then ac_x_includes=$ac_dir break fi @@ -24284,18 +24501,18 @@ if test "$ac_x_libraries" = no; then # See if we find them without any special options. # Don't add to $LIBS permanently. ac_save_LIBS=$LIBS - LIBS="-lXt $LIBS" + LIBS="-lX11 $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ -#include <X11/Intrinsic.h> +#include <X11/Xlib.h> int main () { -XtMalloc (0) +XrmInitialize () ; return 0; } @@ -25524,6 +25741,7 @@ s,@READLINE_CFLAGS@,$READLINE_CFLAGS,;t t s,@HAVE_LIBEXPAT@,$HAVE_LIBEXPAT,;t t s,@LIBEXPAT@,$LIBEXPAT,;t t s,@LTLIBEXPAT@,$LTLIBEXPAT,;t t +s,@XISS_INCLUDES@,$XISS_INCLUDES,;t t s,@ALLOCA@,$ALLOCA,;t t s,@CONFIG_LDFLAGS@,$CONFIG_LDFLAGS,;t t s,@TARGET_SYSTEM_ROOT@,$TARGET_SYSTEM_ROOT,;t t diff --git a/gdb/configure.ac b/gdb/configure.ac index 39246aab825..8380c042241 100644 --- a/gdb/configure.ac +++ b/gdb/configure.ac @@ -381,9 +381,12 @@ AC_CHECK_TOOL(MIG, mig) # We might need to link with -lm; most simulators need it. AC_CHECK_LIB(m, main) +AC_CHECK_FUNC(inflate, [], + [AC_CHECK_LIB(z, inflate)]) + # We need to link with -lw to get `wctype' on Solaris before Solaris # 2.6. Solaris 2.6 and beyond have this function in libc, and have a -# libw that some versions of the GNU linker cannot hanle (GNU ld 2.9.1 +# libw that some versions of the GNU linker cannot handle (GNU ld 2.9.1 # is known to have this problem). Therefore we avoid libw if we can. AC_CHECK_FUNC(wctype, [], [AC_CHECK_LIB(w, wctype)]) @@ -477,6 +480,32 @@ else fi fi + +AC_ARG_WITH(xiss, + AS_HELP_STRING([--with-xiss], [include xiss support (auto/yes/no)]), + [], [with_xiss=auto]) +AC_MSG_CHECKING([whether to use xiss]) +AC_MSG_RESULT([$with_xiss]) + +if test "${with_xiss}" = yes; then + HAVE_LIBXISS=yes + AC_DEFINE(HAVE_LIBXISS, 1, [Define if you have the xISS library.]) + XISS_INCLUDES="-I${with_xiss_prefix}/include" + AC_SUBST(XISS_INCLUDES) +else + AC_MSG_WARN([xiss support disabled; target arcxiss will be unavailable.]) + HAVE_LIBXISS=no +fi + + +if test "${target}" = "arc-unknown-elf32"; then + AC_DEFINE(ARC_ELF32_TARGET, 1, [Define if you are building the ARC bare-board debugger.]) +fi +if test "${target}" = "arc-unknown-linux-uclibc"; then + AC_DEFINE(ARC_LINUX_TARGET, 1, [Define if you are building the ARC Linux debugger.]) +fi + + # ------------------------- # # Checks for header files. # # ------------------------- # @@ -1414,7 +1443,7 @@ OLD_LIBS=$LIBS CFLAGS="$CFLAGS -I${srcdir}/../include -I../bfd -I${srcdir}/../bfd" LDFLAGS="$LDFLAGS -L../bfd -L../libiberty" intl=`echo $LIBINTL | sed 's,${top_builddir}/,,g'` -LIBS="$LIBS -lbfd -liberty $intl" +LIBS="$LIBS -lbfd -liberty $intl -lz" AC_CACHE_CHECK([for ELF support in BFD], gdb_cv_var_elf, [AC_TRY_LINK( [#include <stdlib.h> diff --git a/gdb/configure.tgt b/gdb/configure.tgt index ccfe674f80d..6a15d395cff 100644 --- a/gdb/configure.tgt +++ b/gdb/configure.tgt @@ -61,15 +61,19 @@ alpha*-*-*) ;; arc*-*-linux*) + # Target: ARC Linux based targets gdb_target_obs="arc-tdep.o arc-linux-tdep.o solib.o solib-svr4.o \ - solib-legacy.o corelow.o" + corelow.o" build_gdbserver=yes ;; -arc-a4-*) - gdb_target_obs="arc-tdep.o arc-jtag.o arc-jtag-tdep.o arc-jtag-ops.o" - ;; + arc*-*-*) - gdb_target_obs="arc-tdep.o arc-jtag.o arc-jtag-tdep.o arc-jtag-ops.o" + # Target: ARC elf32 based target + gdb_target_obs="arc-tdep.o arc-jtag.o arc-elf32-tdep.o arc-jtag-ops.o \ + arc-jtag-actionpoints.o arc-remote-fileio.o arc-xiss.o \ + arc-registers.o arc-architecture.o arc-board.o \ + arc-memory.o arc-arguments.o arc-gpio.o arc-inst-tracing.o" + gdb_sim=../sim/arc/libsim.a ;; am33_2.0*-*-linux*) diff --git a/gdb/doc/observer.texi b/gdb/doc/observer.texi index e4c99d8e086..f1a9de56bd4 100644 --- a/gdb/doc/observer.texi +++ b/gdb/doc/observer.texi @@ -98,6 +98,23 @@ The inferior has stopped for real. The target's register contents have changed. @end deftypefun +@deftypefun void target_pre_connect (struct target_ops *@var{target}) +Connection to the target is about to be performed. +@end deftypefun + +@deftypefun void target_post_connect (struct target_ops *@var{target}) +Ccnnection to the target has just been performed. +@end deftypefun + +@deftypefun void target_post_disconnect (struct target_ops *@var{target}) +Disconnection from the target has just been performed. +@end deftypefun + +@deftypefun void target_updated (struct target_ops *@var{target}) +The target has been updated. +@end deftypefun + + @deftypefun void executable_changed (void *@var{unused_args}) The executable being debugged by GDB has changed: The user decided to debug a different program, or the program he was debugging has diff --git a/gdb/dwarf2-frame.c b/gdb/dwarf2-frame.c index 8db1b686a18..c4cd9f3d3ef 100644 --- a/gdb/dwarf2-frame.c +++ b/gdb/dwarf2-frame.c @@ -592,10 +592,6 @@ bad CFI data; mismatched DW_CFA_restore_state at 0x%s"), paddr (fs->pc)); fs->regs.reg[reg].loc.offset = -offset; break; - case DW_CFA_MWARC_info: - /* Ignored. */ - insn_ptr = read_uleb128 (insn_ptr, insn_end, &utmp); - break; default: internal_error (__FILE__, __LINE__, _("Unknown CFI encountered.")); } @@ -1719,8 +1715,6 @@ decode_frame_entry_1 (struct comp_unit *unit, gdb_byte *start, int eh_frame_p) augmentation += 2; } - if (augmentation[0] == 'H' && augmentation [1] == 'C') - augmentation += 2; cie->code_alignment_factor = read_unsigned_leb128 (unit->abfd, buf, &bytes_read); buf += bytes_read; diff --git a/gdb/dwarf2loc.c b/gdb/dwarf2loc.c index 7deb08e25ba..bf589a2d840 100644 --- a/gdb/dwarf2loc.c +++ b/gdb/dwarf2loc.c @@ -265,8 +265,89 @@ dwarf2_evaluate_loc_desc (struct symbol *var, struct frame_info *frame, return retval; } +// begin ARC +static CORE_ADDR +dwarf2_find_address (struct symbol *var, struct frame_info *frame, + gdb_byte *data, unsigned short size, + struct objfile *objfile) +{ + struct gdbarch *arch = get_frame_arch (frame); + struct value *retval; + struct dwarf_expr_baton baton; + struct dwarf_expr_context *ctx; + CORE_ADDR address; + + /* optimised out */ + if (size == 0) + return 0; + + baton.frame = frame; + baton.objfile = objfile; + + ctx = new_dwarf_expr_context (); + ctx->baton = &baton; + ctx->read_reg = dwarf_expr_read_reg; + ctx->read_mem = dwarf_expr_read_mem; + ctx->get_frame_base = dwarf_expr_frame_base; + ctx->get_tls_address = dwarf_expr_tls_address; + + dwarf_expr_eval (ctx, data, size); + if (ctx->num_pieces > 0) + { + /* what should we do here? */ + address = 0; + } + else if (ctx->in_reg) + address = 0; + else + address = dwarf_expr_fetch (ctx, 0); + + free_dwarf_expr_context (ctx); + + return address; +} +static unsigned int +dwarf2_find_size (struct symbol *var, struct frame_info *frame, + gdb_byte *data, unsigned short size, + struct objfile *objfile) +{ + struct gdbarch *arch = get_frame_arch (frame); + struct dwarf_expr_baton baton; + struct dwarf_expr_context *ctx; + unsigned int sz; + + /* optimised out */ + if (size == 0) + return 0; + + baton.frame = frame; + baton.objfile = objfile; + + ctx = new_dwarf_expr_context (); + ctx->baton = &baton; + ctx->read_reg = dwarf_expr_read_reg; + ctx->read_mem = dwarf_expr_read_mem; + ctx->get_frame_base = dwarf_expr_frame_base; + ctx->get_tls_address = dwarf_expr_tls_address; + + dwarf_expr_eval (ctx, data, size); + if (ctx->num_pieces > 0) + { + /* what should we do here? */ + sz = 0; + } + else if (ctx->in_reg) + sz = 0; + else + sz = TYPE_LENGTH (SYMBOL_TYPE (var)); + + free_dwarf_expr_context (ctx); + + return sz; +} +// end ARC /* Helper functions and baton for dwarf2_loc_desc_needs_frame. */ @@ -434,6 +515,30 @@ locexpr_read_variable (struct symbol *symbol, struct frame_info *frame) return val; } +// begin ARC +/* Return the address of SYMBOL in FRAME using the DWARF-2 expression + evaluator to calculate the location. */ +static CORE_ADDR +locexpr_get_variable_address (struct symbol *symbol, struct frame_info *frame) +{ + struct dwarf2_locexpr_baton *dlbaton = SYMBOL_LOCATION_BATON (symbol); + + return dwarf2_find_address (symbol, frame, dlbaton->data, dlbaton->size, + dlbaton->objfile); +} + +/* Return the size of SYMBOL in FRAME using the DWARF-2 expression + evaluator to calculate the location. */ +static unsigned int +locexpr_get_variable_size(struct symbol *symbol, struct frame_info *frame) +{ + struct dwarf2_locexpr_baton *dlbaton = SYMBOL_LOCATION_BATON (symbol); + + return dwarf2_find_size (symbol, frame, dlbaton->data, dlbaton->size, + dlbaton->objfile); +} +// end ARC + /* Return non-zero iff we need a frame to evaluate SYMBOL. */ static int locexpr_read_needs_frame (struct symbol *symbol) @@ -516,6 +621,10 @@ locexpr_tracepoint_var_ref (struct symbol * symbol, struct agent_expr * ax, evaluator. */ const struct symbol_ops dwarf2_locexpr_funcs = { locexpr_read_variable, +// begin ARC + locexpr_get_variable_address, + locexpr_get_variable_size, +// end ARC locexpr_read_needs_frame, locexpr_describe_location, locexpr_tracepoint_var_ref @@ -551,6 +660,46 @@ loclist_read_variable (struct symbol *symbol, struct frame_info *frame) return val; } +// begin ARC +/* Return the address of SYMBOL in FRAME using the DWARF-2 expression + evaluator to calculate the location. */ +static CORE_ADDR +loclist_get_variable_address (struct symbol *symbol, struct frame_info *frame) +{ + struct dwarf2_loclist_baton *dlbaton = SYMBOL_LOCATION_BATON (symbol); + struct value *val; + gdb_byte *data; + size_t size; + + data = find_location_expression (dlbaton, &size, + frame ? get_frame_address_in_block (frame) + : 0); + if (data == NULL) + return 0; + + return dwarf2_find_address(symbol, frame, data, size, dlbaton->objfile); +} + +/* Return the size of SYMBOL in FRAME using the DWARF-2 expression + evaluator to calculate the location. */ +static unsigned int +loclist_get_variable_size(struct symbol *symbol, struct frame_info *frame) +{ + struct dwarf2_loclist_baton *dlbaton = SYMBOL_LOCATION_BATON (symbol); + struct value *val; + gdb_byte *data; + size_t size; + + data = find_location_expression (dlbaton, &size, + frame ? get_frame_address_in_block (frame) + : 0); + if (data == NULL) + return 0; + + return dwarf2_find_size(symbol, frame, data, size, dlbaton->objfile); +} +// end ARC + /* Return non-zero iff we need a frame to evaluate SYMBOL. */ static int loclist_read_needs_frame (struct symbol *symbol) @@ -594,6 +743,10 @@ loclist_tracepoint_var_ref (struct symbol * symbol, struct agent_expr * ax, evaluator and location lists. */ const struct symbol_ops dwarf2_loclist_funcs = { loclist_read_variable, +// begin ARC + loclist_get_variable_address, + loclist_get_variable_size, +// end ARC loclist_read_needs_frame, loclist_describe_location, loclist_tracepoint_var_ref diff --git a/gdb/exec.c b/gdb/exec.c index 7648b0317a6..2ffeba4dc91 100644 --- a/gdb/exec.c +++ b/gdb/exec.c @@ -31,7 +31,7 @@ #include "value.h" #include "exec.h" #include "observer.h" -#include "arch-utils.c" +#include "arch-utils.h" /* richards ARC 23/9/08: change .c to .h gdb bug: 9888 */ #include <fcntl.h> #include "readline/readline.h" diff --git a/gdb/features/arc-a5-cpu.xml b/gdb/features/arc-a5-cpu.xml new file mode 100644 index 00000000000..7ff602b23b6 --- /dev/null +++ b/gdb/features/arc-a5-cpu.xml @@ -0,0 +1,258 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> + +<!DOCTYPE target SYSTEM "gdb-target.dtd"> +<target version="1.0"> +<architecture>A5</architecture> +<feature name="org.gnu.gdb.arc.registers"> + + <auxregister name="STATUS" description="Status (obsolete)" number="0x0" mask ="0xFEFFFFFF" access="RO"> + <field name="PC" description="program counter (PC[25:2])" size="24" offset="0" access="RO" /> + <field name="H" description="halt bit" size="1" offset="25" access="RO" > + <meaning value="1" description="processor is halted"/> + </field> + <field name="E1" description="interrupt mask bit" size="1" offset="26" access="RO" /> + <field name="E2" description="interrupt mask bit" size="1" offset="27" access="RO" /> + <field name="V" description="overflow flag" size="1" offset="28" access="RO" /> + <field name="C" description="carry flag" size="1" offset="29" access="RO" /> + <field name="N" description="negative flag" size="1" offset="30" access="RO" /> + <field name="Z" description="zero flag" size="1" offset="31" access="RO" /> + </auxregister> + + <auxregister name="SEMAPHORE" description="Semaphore" number="0x1" mask ="0x0000000F" access="RW"> + <field name="S0" description="inter-process/host semaphore 0" size="1" offset="0" access="RW" > + <meaning value="1" description="semaphore has been obtained"/> + </field> + <field name="S1" description="inter-process/host semaphore 1" size="1" offset="1" access="RW" > + <meaning value="1" description="semaphore has been obtained"/> + </field> + <field name="S2" description="inter-process/host semaphore 2" size="1" offset="2" access="RW" > + <meaning value="1" description="semaphore has been obtained"/> + </field> + <field name="S3" description="inter-process/host semaphore 3" size="1" offset="3" access="RW" > + <meaning value="1" description="semaphore has been obtained"/> + </field> + </auxregister> + + <auxregister name="LP_START" description="Loop Start" number="0x2" mask ="0xFFFFFFFE" access="RW" > + <field name="reserved" onwrite="0" size="1" offset="0" access="RW" /> + </auxregister> + + <auxregister name="LP_END" description="Loop End" number="0x3" mask ="0xFFFFFFFE" access="RW" > + <field name="reserved" onwrite="0" size="1" offset="0" access="RW" /> + </auxregister> + + <auxregister name="IDENTITY" description="Identity" number="0x4" mask ="0xFFFFFFFF" access="RO"> + <field name="CHIPID" description="unique chip identifier" size="16" offset="16" access="RO" /> + <field name="ARCNUM" description="additional identity number" size= "8" offset= "8" access="RO" /> + <field name="ARCVER" description="basecase version number" size= "8" offset= "0" access="RO" /> + </auxregister> + + <auxregister name="DEBUG" description="Debug" number="0x5" mask ="0xF0800802" access="RW"> + <field name ="FH" description="force halt" size="1" offset="1" access="WO"/> + <field name ="IS" description="single step" size="1" offset="11" access="RW"> + <meaning value="1" description="single instruction stepping is in force"/> + </field> + <field name ="ZZ" description="sleep mode" size="1" offset="23" access="RO"> + <meaning value="1" description="processor is in sleep mode"/> + </field> + <field name ="BH" description="breakpoint halt" size="1" offset="29" access="RO"> + <meaning value="1" description="a BRK instruction has been triggered"/> + </field> + <field name ="SH" description="self halt" size="1" offset="30" access="RO"> + <meaning value="1" description="processor has halted itself"/> + </field> + <field name ="LD" description="load pending" size="1" offset="31" access="RO"> + <meaning value="1" description="a delayed load is waiting to complete"/> + </field> + +<!-- for testing + <field name ="LD" description="duplicate!" size="1" offset="10" access="RO"/> + <field name ="OV" description="overlap!" size="3" offset="27" access="RO"/> + <field name ="BG" description="too big!" size="8" offset="27" access="RO"/> + --> + </auxregister> + + <auxregister name="PC" description="program counter" number="0x6" mask ="0xFFFFFFFE" access="RW"/> + + <auxregister name="STATUS32" description="status flags" number="0xA" mask ="0x00001FFF" access="RW" > + <field name="H" description="halt bit" size="1" offset= "0" access="RO" > + <meaning value="1" description="processor is halted"/> + </field> + <field name="E1" description="level 1 interrupt mask bit" size="1" offset= "1" access="RW" > + <meaning value="0" description="level 1 interrupts are enabled"/> + <meaning value="1" description="level 1 interrupts are disabled"/> + </field> + <field name="E2" description="level 2 interrupt mask bit" size="1" offset= "2" access="RW" > + <meaning value="0" description="level 2 interrupts are enabled"/> + <meaning value="1" description="level 2 interrupts are disabled"/> + </field> + <field name="V" description="overflow flag" size="1" offset= "8" access="RO" /> + <field name="C" description="carry flag" size="1" offset= "9" access="RO" /> + <field name="N" description="negative flag" size="1" offset="10" access="RO" /> + <field name="Z" description="zero flag" size="1" offset="11" access="RO" /> + </auxregister> + + <auxregister name="STATUS32_L1" description="STATUS32 register in case of L1 interrupts" number="0xB" mask ="0x00001FFE" access="RW" > + <field name="reserved" onwrite="0" size="1" offset="0" access="RW" /> + <field name="E1" description="level 1 interrupt mask bit" size="1" offset= "1" access="RW" > + <meaning value="0" description="level 1 interrupts are enabled"/> + <meaning value="1" description="level 1 interrupts are disabled"/> + </field> + <field name="E2" description="level 2 interrupt mask bit" size="1" offset= "2" access="RW" > + <meaning value="0" description="level 2 interrupts are enabled"/> + <meaning value="1" description="level 2 interrupts are disabled"/> + </field> + <field name="V" description="overflow flag" size="1" offset= "8" access="RO" /> + <field name="C" description="carry flag" size="1" offset= "9" access="RO" /> + <field name="N" description="negative flag" size="1" offset="10" access="RO" /> + <field name="Z" description="zero flag" size="1" offset="11" access="RO" /> + </auxregister> + + <auxregister name="STATUS32_L2" description="STATUS32 register in case of L2 interrupts" number="0xC" mask ="0x00001FFE" access="RW" > + <field name="reserved" onwrite="0" size="1" offset="0" access="RW" /> + <field name="E1" description="level 1 interrupt mask bit" size="1" offset= "1" access="RW" > + <meaning value="0" description="level 1 interrupts are enabled"/> + <meaning value="1" description="level 1 interrupts are disabled"/> + </field> + <field name="E2" description="level 2 interrupt mask bit" size="1" offset= "2" access="RW" > + <meaning value="0" description="level 2 interrupts are enabled"/> + <meaning value="1" description="level 2 interrupts are disabled"/> + </field> + <field name="V" description="overflow flag" size="1" offset= "8" access="RO" /> + <field name="C" description="carry flag" size="1" offset= "9" access="RO" /> + <field name="N" description="negative flag" size="1" offset="10" access="RO" /> + <field name="Z" description="zero flag" size="1" offset="11" access="RO" /> + </auxregister> + + <auxregister name="COUNT0" description="Processor Timer 0 Count Value" number="0x21" mask ="0xFFFFFFFF" access="RW"/> + + <auxregister name="CONTROL0" description="Processor Timer 0 Control Value" number="0x22" mask ="0x0000000F" access="RW" > + <field name="IE" description="Interrupt Enable flag" size="1" offset="0" access="RW" > + <meaning value="1" description="interrupt will be generated after timer reaches limit condition"/> + </field> + <field name="NH" description="Not Halted mode flag" size="1" offset="1" access="RW" > + <meaning value="0" description="timer counts every clock cycle"/> + <meaning value="1" description="timer counts clock cycles only when processor is running"/> + </field> + <field name="W" description="Watchdog mode flag" size="1" offset="2" access="RW" > + <meaning value="1" description="system Reset signal will be generated after timer reaches limit condition"/> + </field> + </auxregister> + + <auxregister name="LIMIT0" description="Processor Timer 0 Limit Value" number="0x23" mask ="0xFFFFFFFF" access="RW"/> + <auxregister name="INT_VECTOR_BASE" description="Interrupt Vector Base Register" number="0x25" mask ="0xFFFFFC00" access="RW"/> + + <auxregister name="AUX_MACMODE" description="Aux MAC Mode" number="0x41" mask ="0xFFFFFFFF" access="RW" > + <field name="CS" description="" size="1" offset="1" access="RW" > + </field> + <field name="S2" description="channel 2 saturation flag" size="1" offset="4" access="RO" > + <meaning value="1" description="channel 2 has saturated"/> + </field> + <field name="S1" description="channel 1 saturation flag" size="1" offset="9" access="RO" > + <meaning value="1" description="channel 1 has saturated"/> + </field> + </auxregister> + + <auxregister name="AUX_IRQ_LV12" description="Aux IRQ Level 2" number="0x43" mask ="0x00000003" access="RW" > + <field name="L1" description="level 1 interrupt status bit" size="1" offset="0" access="RW" > + <meaning value="1" description="level 1 interrupt has been taken"/> + </field> + <field name="L2" description="level 2 interrupt status bit" size="1" offset="1" access="RW" > + <meaning value="1" description="level 2 interrupt has been taken"/> + </field> + </auxregister> + + <auxregister name="COUNT1" description="Processor Timer 1 Count Value" number="0x100" mask ="0xFFFFFFFF" access="RW"/> + + <auxregister name="CONTROL1" description="Processor Timer 1 Control Value" number="0x101" mask ="0x0000000F" access="RW" > + <field name="IE" description="Interrupt Enable flag" size="1" offset="0" access="RW" > + <meaning value="1" description="interrupt will be generated after timer reaches limit condition"/> + </field> + <field name="NH" description="Not Halted mode flag" size="1" offset="1" access="RW" > + <meaning value="0" description="timer counts every clock cycle"/> + <meaning value="1" description="timer counts clock cycles only when processor is running"/> + </field> + <field name="W" description="Watchdog mode flag" size="1" offset="2" access="RW" > + <meaning value="1" description="system Reset signal will be generated after timer reaches limit condition"/> + </field> + </auxregister> + + <auxregister name="LIMIT1" description="Processor Timer 1 Limit Value" number="0x102" mask ="0xFFFFFFFF" access="RW"/> + + <auxregister name="AUX_IRQ_LEV" description="Interrupt Level programming" number="0x200" mask ="0xFFFFFFF8" access="RW" > + <field name="reserved" onwrite="0" size="3" offset="0" access="RW" /> + </auxregister> + + <auxregister name="AUX_IRQ_HINT" description="Software Triggered Interrupt" number="0x201" mask ="0x0000001F" access="RW"/> + + <!-- cache control registers --> + + <!-- N.B. are the Invalidation registers write-only? --> + + <auxregister name="IC_IVIC" description="Instruction Cache Invalidation Register" number="0x10" mask ="0xFFFFFFFF" access="WO"/> + <auxregister name="IC_CTRL" description="Instruction Cache Control Register" number="0x11" mask ="0xFFFFFFFF" access="RW"/> + <auxregister name="DC_IVDC" description="Data Cache Invalidation Register" number="0x47" mask ="0xFFFFFFFF" access="WO"/> + <auxregister name="DC_CTRL" description="Data Cache Control Register" number="0x48" mask ="0xFFFFFFFF" access="RW"/> + + + <!-- actionpoint 0 registers --> + + <auxregister name="AMV0" description="Actionpoint 0 Match Value Register" number="0x220" mask ="0xFFFFFFFF" access="RW"/> + <auxregister name="AMM0" description="Actionpoint 0 Match Mask Register" number="0x221" mask ="0xFFFFFFFF" access="RW"/> + <auxregister name="AC0" description="Actionpoint 0 Control Register" number="0x222" mask ="0xFFFFFFFF" access="RW"/> + + + <!-- Build Configuration Registers --> + + <bcr name="BCR_VER" description="Build Configuration Registers Version" number="0x60" mask="0xFFFFFFFF"/> + <bcr name="DCCM_BASE_BUILD" description="Base Address for Data Closely Coupled Memory" number="0x61" mask="0xFFFFFFFF"/> + <bcr name="CRC_BASE_BUILD" description="CRC Unit BCR" number="0x62" mask="0xFFFFFFFF"/> + <bcr name="DVBF_BUILD" description="Dual Viterbi Instruction BCR" number="0x64" mask="0xFFFFFFFF"/> + <bcr name="TEL_INSTR_BUILD" description="Extended Arithmetic Instructions BCR" number="0x65" mask="0xFFFFFFFF"/> + <bcr name="unused" description="unused" number="0x66" mask="0xFFFFFFFF"/> + <bcr name="MEMSUBSYS" description="Memory Subsystem BCR" number="0x67" mask="0xFFFFFFFF"/> + <bcr name="VECBASE_AC_BUILD" description="Interrupt Vector Base BCR" number="0x68" mask="0xFFFFFFFF"/> + <bcr name="P_BASE_ADDRESS" description="Peripheral Base Address" number="0x69" mask="0xFFFFFFFF"/> + <bcr name="unused" description="unused" number="0x6A" mask="0xFFFFFFFF"/> + <bcr name="unused" description="unused" number="0x6B" mask="0xFFFFFFFF"/> + <bcr name="unused" description="unused" number="0x6C" mask="0xFFFFFFFF"/> + <bcr name="unused" description="unused" number="0x6D" mask="0xFFFFFFFF"/> + <bcr name="unused" description="unused" number="0x6E" mask="0xFFFFFFFF"/> + <bcr name="MMU_BUILD" description="Memory Management Unit BCR" number="0x6F" mask="0xFFFFFFFF"/> + <bcr name="ARCANGEL_BUILD" description="ARCAngel BCR" number="0x70" mask="0xFFFFFFFF"/> + <bcr name="unused" description="unused" number="0x71" mask="0xFFFFFFFF"/> + <bcr name="DCACHE_BUILD" description="Data Cache BCR" number="0x72" mask="0xFFFFFFFF"/> + <bcr name="MADI_BUILD" description="Multiple ARC Debug Interface" number="0x73" mask="0xFFFFFFFF"/> + <bcr name="DCCM_BUILD" description="Data Closely Coupled Memory BCR" number="0x74" mask="0xFFFFFFFF"/> + <bcr name="TIMER_BUILD" description="Timers BCR" number="0x75" mask="0xFFFFFFFF"/> + <bcr name="AP_BUILD" description="Actionpoints BCR" number="0x76" mask="0xFFFFFFFF"/> + <bcr name="ICACHE_BUILD" description="Instruction Cache BCR" number="0x77" mask="0xFFFFFFFF"/> + <bcr name="ICCM_BUILD" description="Instruction Closely Coupled Memory BCR" number="0x78" mask="0xFFFFFFFF"/> + <bcr name="DSPRAM_BUILD" description="DSP RAM BCR" number="0x79" mask="0xFFFFFFFF"/> + <bcr name="MAC_BUILD" description="MAC Unit BCR" number="0x7A" mask="0xFFFFFFFF"/> + <bcr name="MULTIPLY_BUILD" description="(32 X 32) Multiply Unit BCR" number="0x7B" mask="0xFFFFFFFF"/> + <bcr name="SWAP_BUILD" description="SWAP BCR" number="0x7C" mask="0xFFFFFFFF"/> + <bcr name="NORM_BUILD" description="NORM Unit BCR" number="0x7D" mask="0xFFFFFFFF"/> + <bcr name="MINMAX_BUILD" description="Minmax Unit BCR" number="0x7E" mask="0xFFFFFFFF"/> + <bcr name="BARREL_BUILD" description="Barrel Shifter BCR" number="0x7F" mask="0xFFFFFFFF"/> + +<!-- for testing + + <auxregister name="TEST" description="any old bucket of bits" number="0x99999" mask ="0xFFFFFFFF" access="RW"> + <field name ="AA" description="" size="1" offset="5" access="RO"> + <meaning value="0" description="value is 0"/> + <meaning value="1" description="value is 1"/> + </field> + <field name ="BBBB" description="" size="3" offset="8" access="RO"> + <meaning value="0" description="value is 0"/> + <meaning value="1" description="value is 1"/> + <meaning value="3" description="value is 3"/> + <meaning value="7" description="value is 7"/> + </field> + <field name ="CCCCCCCCC" description="" size="8" offset="17" access="RO"> + </field> + </auxregister> + --> + +</feature> +</target> diff --git a/gdb/features/arc-registers.dtd b/gdb/features/arc-registers.dtd new file mode 100644 index 00000000000..b050fa0d3a7 --- /dev/null +++ b/gdb/features/arc-registers.dtd @@ -0,0 +1,71 @@ +<!-- Copyright (C) 2008, 2009 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!-- The root element of an ARC registers description is <target>. --> + +<!-- Auxiliary Registers --> + +<!ELEMENT target (architecture?, feature*)> +<!ATTLIST target + version CDATA #FIXED "1.0"> + +<!ELEMENT architecture (#PCDATA)> + +<!ELEMENT feature (auxregister*,bcr*,ecr*)> +<!ATTLIST feature + name ID #REQUIRED> + +<!ELEMENT auxregister (field*)> +<!ATTLIST auxregister + name CDATA #REQUIRED + description CDATA #IMPLIED + number CDATA #REQUIRED + mask CDATA #IMPLIED + access (RO | RW | WO) 'RW' + > + +<!ELEMENT field (meaning*)> +<!ATTLIST field + name CDATA #REQUIRED + description CDATA #IMPLIED + onwrite CDATA #IMPLIED + offset CDATA #REQUIRED + size CDATA #REQUIRED + access (RO | RW | WO) 'RW' + > + +<!ELEMENT meaning EMPTY> +<!ATTLIST meaning + description CDATA #REQUIRED + value CDATA #REQUIRED + > + +<!-- Build Configuration Registers --> + +<!ELEMENT bcr (bcrfield*)> +<!ATTLIST bcr + name CDATA #REQUIRED + description CDATA #IMPLIED + number CDATA #REQUIRED + mask CDATA #IMPLIED + > + +<!ELEMENT bcrfield > +<!ATTLIST bcrfield + name CDATA #REQUIRED + description CDATA #IMPLIED + offset CDATA #REQUIRED + size CDATA #REQUIRED + > + +<!-- Extension Core Registers --> + +<!ELEMENT ecr> +<!ATTLIST ecr + number CDATA #REQUIRED + mask CDATA #IMPLIED + access (RO | RW | WO) 'RW' + > diff --git a/gdb/features/arc600-cpu.xml b/gdb/features/arc600-cpu.xml new file mode 100644 index 00000000000..1ecaee4ef86 --- /dev/null +++ b/gdb/features/arc600-cpu.xml @@ -0,0 +1,266 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> + +<!DOCTYPE target SYSTEM "gdb-target.dtd"> +<target version="1.0"> +<architecture>ARC600</architecture> +<feature name="org.gnu.gdb.arc.registers"> + + <auxregister name="STATUS" description="Status (obsolete)" number="0x0" mask ="0xFEFFFFFF" access="RO"> + <field name="PC" description="program counter (PC[25:2])" size="24" offset="0" access="RO" /> + <field name="H" description="halt bit" size="1" offset="25" access="RO" > + <meaning value="1" description="processor is halted"/> + </field> + <field name="E1" description="interrupt mask bit" size="1" offset="26" access="RO" /> + <field name="E2" description="interrupt mask bit" size="1" offset="27" access="RO" /> + <field name="V" description="overflow flag" size="1" offset="28" access="RO" /> + <field name="C" description="carry flag" size="1" offset="29" access="RO" /> + <field name="N" description="negative flag" size="1" offset="30" access="RO" /> + <field name="Z" description="zero flag" size="1" offset="31" access="RO" /> + </auxregister> + + <auxregister name="SEMAPHORE" description="Semaphore" number="0x1" mask ="0x0000000F" access="RW"> + <field name="S0" description="inter-process/host semaphore 0" size="1" offset="0" access="RW" > + <meaning value="1" description="semaphore has been obtained"/> + </field> + <field name="S1" description="inter-process/host semaphore 1" size="1" offset="1" access="RW" > + <meaning value="1" description="semaphore has been obtained"/> + </field> + <field name="S2" description="inter-process/host semaphore 2" size="1" offset="2" access="RW" > + <meaning value="1" description="semaphore has been obtained"/> + </field> + <field name="S3" description="inter-process/host semaphore 3" size="1" offset="3" access="RW" > + <meaning value="1" description="semaphore has been obtained"/> + </field> + </auxregister> + + <auxregister name="LP_START" description="Loop Start" number="0x2" mask ="0xFFFFFFFE" access="RW" > + <field name="reserved" onwrite="0" size="1" offset="0" access="RW" /> + </auxregister> + + <auxregister name="LP_END" description="Loop End" number="0x3" mask ="0xFFFFFFFE" access="RW" > + <field name="reserved" onwrite="0" size="1" offset="0" access="RW" /> + </auxregister> + + <auxregister name="IDENTITY" description="Identity" number="0x4" mask ="0xFFFFFFFF" access="RO"> + <field name="CHIPID" description="unique chip identifier" size="16" offset="16" access="RO" /> + <field name="ARCNUM" description="additional identity number" size= "8" offset= "8" access="RO" /> + <field name="ARCVER" description="basecase version number" size= "8" offset= "0" access="RO" /> + </auxregister> + + <auxregister name="DEBUG" description="Debug" number="0x5" mask ="0xF0800802" access="RW"> + <field name ="FH" description="force halt" size="1" offset="1" access="WO"/> + <field name ="IS" description="single step" size="1" offset="11" access="RW"> + <meaning value="1" description="single instruction stepping is in force"/> + </field> + <field name ="ZZ" description="sleep mode" size="1" offset="23" access="RO"> + <meaning value="1" description="processor is in sleep mode"/> + </field> + <field name ="BH" description="breakpoint halt" size="1" offset="29" access="RO"> + <meaning value="1" description="a BRK instruction has been triggered"/> + </field> + <field name ="SH" description="self halt" size="1" offset="30" access="RO"> + <meaning value="1" description="processor has halted itself"/> + </field> + <field name ="LD" description="load pending" size="1" offset="31" access="RO"> + <meaning value="1" description="a delayed load is waiting to complete"/> + </field> + +<!-- for testing + <field name ="LD" description="duplicate!" size="1" offset="10" access="RO"/> + <field name ="OV" description="overlap!" size="3" offset="27" access="RO"/> + <field name ="BG" description="too big!" size="8" offset="27" access="RO"/> + --> + </auxregister> + + <auxregister name="PC" description="program counter" number="0x6" mask ="0xFFFFFFFE" access="RW"/> + + <auxregister name="STATUS32" description="status flags" number="0xA" mask ="0x00001FFF" access="RW" > + <field name="H" description="halt bit" size="1" offset= "0" access="RO" > + <meaning value="1" description="processor is halted"/> + </field> + <field name="E1" description="level 1 interrupt mask bit" size="1" offset= "1" access="RW" > + <meaning value="0" description="level 1 interrupts are enabled"/> + <meaning value="1" description="level 1 interrupts are disabled"/> + </field> + <field name="E2" description="level 2 interrupt mask bit" size="1" offset= "2" access="RW" > + <meaning value="0" description="level 2 interrupts are enabled"/> + <meaning value="1" description="level 2 interrupts are disabled"/> + </field> + <field name="V" description="overflow flag" size="1" offset= "8" access="RO" /> + <field name="C" description="carry flag" size="1" offset= "9" access="RO" /> + <field name="N" description="negative flag" size="1" offset="10" access="RO" /> + <field name="Z" description="zero flag" size="1" offset="11" access="RO" /> + </auxregister> + + <auxregister name="STATUS32_L1" description="STATUS32 register in case of L1 interrupts" number="0xB" mask ="0x00001FFE" access="RW" > + <field name="reserved" onwrite="0" size="1" offset="0" access="RW" /> + <field name="E1" description="level 1 interrupt mask bit" size="1" offset= "1" access="RW" > + <meaning value="0" description="level 1 interrupts are enabled"/> + <meaning value="1" description="level 1 interrupts are disabled"/> + </field> + <field name="E2" description="level 2 interrupt mask bit" size="1" offset= "2" access="RW" > + <meaning value="0" description="level 2 interrupts are enabled"/> + <meaning value="1" description="level 2 interrupts are disabled"/> + </field> + <field name="V" description="overflow flag" size="1" offset= "8" access="RO" /> + <field name="C" description="carry flag" size="1" offset= "9" access="RO" /> + <field name="N" description="negative flag" size="1" offset="10" access="RO" /> + <field name="Z" description="zero flag" size="1" offset="11" access="RO" /> + </auxregister> + + <auxregister name="STATUS32_L2" description="STATUS32 register in case of L2 interrupts" number="0xC" mask ="0x00001FFE" access="RW" > + <field name="reserved" onwrite="0" size="1" offset="0" access="RW" /> + <field name="E1" description="level 1 interrupt mask bit" size="1" offset= "1" access="RW" > + <meaning value="0" description="level 1 interrupts are enabled"/> + <meaning value="1" description="level 1 interrupts are disabled"/> + </field> + <field name="E2" description="level 2 interrupt mask bit" size="1" offset= "2" access="RW" > + <meaning value="0" description="level 2 interrupts are enabled"/> + <meaning value="1" description="level 2 interrupts are disabled"/> + </field> + <field name="V" description="overflow flag" size="1" offset= "8" access="RO" /> + <field name="C" description="carry flag" size="1" offset= "9" access="RO" /> + <field name="N" description="negative flag" size="1" offset="10" access="RO" /> + <field name="Z" description="zero flag" size="1" offset="11" access="RO" /> + </auxregister> + + <auxregister name="COUNT0" description="Processor Timer 0 Count Value" number="0x21" mask ="0xFFFFFFFF" access="RW"/> + + <auxregister name="CONTROL0" description="Processor Timer 0 Control Value" number="0x22" mask ="0x0000000F" access="RW" > + <field name="IE" description="Interrupt Enable flag" size="1" offset="0" access="RW" > + <meaning value="1" description="interrupt will be generated after timer reaches limit condition"/> + </field> + <field name="NH" description="Not Halted mode flag" size="1" offset="1" access="RW" > + <meaning value="0" description="timer counts every clock cycle"/> + <meaning value="1" description="timer counts clock cycles only when processor is running"/> + </field> + <field name="W" description="Watchdog mode flag" size="1" offset="2" access="RW" > + <meaning value="1" description="system Reset signal will be generated after timer reaches limit condition"/> + </field> + <field name="IP" description="Interrupt Pending flag" size="1" offset="3" access="RW" > + <meaning value="0" description="timer 0 interrupt line is low" /> + <meaning value="1" description="timer 0 interrupt line is high" /> + </field> + </auxregister> + + <auxregister name="LIMIT0" description="Processor Timer 0 Limit Value" number="0x23" mask ="0xFFFFFFFF" access="RW"/> + <auxregister name="INT_VECTOR_BASE" description="Interrupt Vector Base Register" number="0x25" mask ="0xFFFFFC00" access="RW"/> + + <auxregister name="AUX_MACMODE" description="Aux MAC Mode" number="0x41" mask ="0xFFFFFFFF" access="RW" > + <field name="CS" description="" size="1" offset="1" access="RW" > + </field> + <field name="S2" description="channel 2 saturation flag" size="1" offset="4" access="RO" > + <meaning value="1" description="channel 2 has saturated"/> + </field> + <field name="S1" description="channel 1 saturation flag" size="1" offset="9" access="RO" > + <meaning value="1" description="channel 1 has saturated"/> + </field> + </auxregister> + + <auxregister name="AUX_IRQ_LV12" description="Aux IRQ Level 2" number="0x43" mask ="0x00000003" access="RW" > + <field name="L1" description="level 1 interrupt status bit" size="1" offset="0" access="RW" > + <meaning value="1" description="level 1 interrupt has been taken"/> + </field> + <field name="L2" description="level 2 interrupt status bit" size="1" offset="1" access="RW" > + <meaning value="1" description="level 2 interrupt has been taken"/> + </field> + </auxregister> + + <auxregister name="COUNT1" description="Processor Timer 1 Count Value" number="0x100" mask ="0xFFFFFFFF" access="RW"/> + + <auxregister name="CONTROL1" description="Processor Timer 1 Control Value" number="0x101" mask ="0x0000000F" access="RW" > + <field name="IE" description="Interrupt Enable flag" size="1" offset="0" access="RW" > + <meaning value="1" description="interrupt will be generated after timer reaches limit condition"/> + </field> + <field name="NH" description="Not Halted mode flag" size="1" offset="1" access="RW" > + <meaning value="0" description="timer counts every clock cycle"/> + <meaning value="1" description="timer counts clock cycles only when processor is running"/> + </field> + <field name="W" description="Watchdog mode flag" size="1" offset="2" access="RW" > + <meaning value="1" description="system Reset signal will be generated after timer reaches limit condition"/> + </field> + <field name="IP" description="Interrupt Pending flag" size="1" offset="3" access="RW" > + <meaning value="0" description="timer 1 interrupt line is low" /> + <meaning value="1" description="timer 1 interrupt line is high" /> + </field> + </auxregister> + + <auxregister name="LIMIT1" description="Processor Timer 1 Limit Value" number="0x102" mask ="0xFFFFFFFF" access="RW"/> + + <auxregister name="AUX_IRQ_LEV" description="Interrupt Level programming" number="0x200" mask ="0xFFFFFFF8" access="RW" > + <field name="reserved" onwrite="0" size="3" offset="0" access="RW" /> + </auxregister> + + <auxregister name="AUX_IRQ_HINT" description="Software Triggered Interrupt" number="0x201" mask ="0x0000001F" access="RW"/> + + <!-- cache control registers --> + + <!-- N.B. are the Invalidation registers write-only? --> + + <auxregister name="IC_IVIC" description="Instruction Cache Invalidation Register" number="0x10" mask ="0xFFFFFFFF" access="WO"/> + <auxregister name="IC_CTRL" description="Instruction Cache Control Register" number="0x11" mask ="0xFFFFFFFF" access="RW"/> + <auxregister name="DC_IVDC" description="Data Cache Invalidation Register" number="0x47" mask ="0xFFFFFFFF" access="WO"/> + <auxregister name="DC_CTRL" description="Data Cache Control Register" number="0x48" mask ="0xFFFFFFFF" access="RW"/> + + + <!-- actionpoint 0 registers --> + + <auxregister name="AMV0" description="Actionpoint 0 Match Value Register" number="0x220" mask ="0xFFFFFFFF" access="RW"/> + <auxregister name="AMM0" description="Actionpoint 0 Match Mask Register" number="0x221" mask ="0xFFFFFFFF" access="RW"/> + <auxregister name="AC0" description="Actionpoint 0 Control Register" number="0x222" mask ="0xFFFFFFFF" access="RW"/> + + + <!-- Build Configuration Registers --> + + <bcr name="BCR_VER" description="Build Configuration Registers Version" number="0x60" mask="0xFFFFFFFF"/> + <bcr name="DCCM_BASE_BUILD" description="Base Address for Data Closely Coupled Memory" number="0x61" mask="0xFFFFFFFF"/> + <bcr name="CRC_BASE_BUILD" description="CRC Unit BCR" number="0x62" mask="0xFFFFFFFF"/> + <bcr name="DVBF_BUILD" description="Dual Viterbi Instruction BCR" number="0x64" mask="0xFFFFFFFF"/> + <bcr name="TEL_INSTR_BUILD" description="Extended Arithmetic Instructions BCR" number="0x65" mask="0xFFFFFFFF"/> + <bcr name="unused" description="unused" number="0x66" mask="0xFFFFFFFF"/> + <bcr name="MEMSUBSYS" description="Memory Subsystem BCR" number="0x67" mask="0xFFFFFFFF"/> + <bcr name="VECBASE_AC_BUILD" description="Interrupt Vector Base BCR" number="0x68" mask="0xFFFFFFFF"/> + <bcr name="P_BASE_ADDRESS" description="Peripheral Base Address" number="0x69" mask="0xFFFFFFFF"/> + <bcr name="unused" description="unused" number="0x6A" mask="0xFFFFFFFF"/> + <bcr name="unused" description="unused" number="0x6B" mask="0xFFFFFFFF"/> + <bcr name="unused" description="unused" number="0x6C" mask="0xFFFFFFFF"/> + <bcr name="unused" description="unused" number="0x6D" mask="0xFFFFFFFF"/> + <bcr name="RF_BUILD" description="Core Registers BCR" number="0x6E" mask="0xFFFFFFFF"/> + <bcr name="MMU_BUILD" description="Memory Management Unit BCR" number="0x6F" mask="0xFFFFFFFF"/> + <bcr name="ARCANGEL_BUILD" description="ARCAngel BCR" number="0x70" mask="0xFFFFFFFF"/> + <bcr name="unused" description="unused" number="0x71" mask="0xFFFFFFFF"/> + <bcr name="DCACHE_BUILD" description="Data Cache BCR" number="0x72" mask="0xFFFFFFFF"/> + <bcr name="MADI_BUILD" description="Multiple ARC Debug Interface" number="0x73" mask="0xFFFFFFFF"/> + <bcr name="DCCM_BUILD" description="Data Closely Coupled Memory BCR" number="0x74" mask="0xFFFFFFFF"/> + <bcr name="TIMER_BUILD" description="Timers BCR" number="0x75" mask="0xFFFFFFFF"/> + <bcr name="AP_BUILD" description="Actionpoints BCR" number="0x76" mask="0xFFFFFFFF"/> + <bcr name="ICACHE_BUILD" description="Instruction Cache BCR" number="0x77" mask="0xFFFFFFFF"/> + <bcr name="ICCM_BUILD" description="Instruction Closely Coupled Memory BCR" number="0x78" mask="0xFFFFFFFF"/> + <bcr name="DSPRAM_BUILD" description="DSP RAM BCR" number="0x79" mask="0xFFFFFFFF"/> + <bcr name="MAC_BUILD" description="MAC Unit BCR" number="0x7A" mask="0xFFFFFFFF"/> + <bcr name="MULTIPLY_BUILD" description="(32 X 32) Multiply Unit BCR" number="0x7B" mask="0xFFFFFFFF"/> + <bcr name="SWAP_BUILD" description="SWAP BCR" number="0x7C" mask="0xFFFFFFFF"/> + <bcr name="NORM_BUILD" description="NORM Unit BCR" number="0x7D" mask="0xFFFFFFFF"/> + <bcr name="MINMAX_BUILD" description="Minmax Unit BCR" number="0x7E" mask="0xFFFFFFFF"/> + <bcr name="BARREL_BUILD" description="Barrel Shifter BCR" number="0x7F" mask="0xFFFFFFFF"/> + +<!-- for testing + + <auxregister name="TEST" description="any old bucket of bits" number="0x99999" mask ="0xFFFFFFFF" access="RW"> + <field name ="AA" description="" size="1" offset="5" access="RO"> + <meaning value="0" description="value is 0"/> + <meaning value="1" description="value is 1"/> + </field> + <field name ="BBBB" description="" size="3" offset="8" access="RO"> + <meaning value="0" description="value is 0"/> + <meaning value="1" description="value is 1"/> + <meaning value="3" description="value is 3"/> + <meaning value="7" description="value is 7"/> + </field> + <field name ="CCCCCCCCC" description="" size="8" offset="17" access="RO"> + </field> + </auxregister> + --> + +</feature> +</target> diff --git a/gdb/features/arc700-cpu.xml b/gdb/features/arc700-cpu.xml new file mode 100644 index 00000000000..5fa97d23356 --- /dev/null +++ b/gdb/features/arc700-cpu.xml @@ -0,0 +1,416 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> + +<!DOCTYPE target SYSTEM "gdb-target.dtd"> +<target version="1.0"> +<architecture>ARC700</architecture> +<feature name="org.gnu.gdb.arc.registers"> + + <auxregister name="STATUS" description="Status (obsolete)" number="0x0" mask ="0xFEFFFFFF" access="RO"> + <field name="PC" description="program counter (PC[25:2])" size="24" offset="0" access="RO" /> + <field name="H" description="halt bit" size="1" offset="25" access="RO" > + <meaning value="1" description="processor is halted"/> + </field> + <field name="E1" description="interrupt mask bit" size="1" offset="26" access="RO" /> + <field name="E2" description="interrupt mask bit" size="1" offset="27" access="RO" /> + <field name="V" description="overflow flag" size="1" offset="28" access="RO" /> + <field name="C" description="carry flag" size="1" offset="29" access="RO" /> + <field name="N" description="negative flag" size="1" offset="30" access="RO" /> + <field name="Z" description="zero flag" size="1" offset="31" access="RO" /> + </auxregister> + + <auxregister name="LP_START" description="Loop Start" number="0x2" mask ="0xFFFFFFFE" access="RW" > + <field name="reserved" onwrite="0" size="1" offset="0" access="RW" /> + </auxregister> + + <auxregister name="LP_END" description="Loop End" number="0x3" mask ="0xFFFFFFFE" access="RW" > + <field name="reserved" onwrite="0" size="1" offset="0" access="RW" /> + </auxregister> + + <auxregister name="IDENTITY" description="Identity" number="0x4" mask ="0xFFFFFFFF" access="RO"> + <field name="CHIPID" description="unique chip identifier" size="16" offset="16" access="RO" /> + <field name="ARCNUM" description="additional identity number" size= "8" offset= "8" access="RO" /> + <field name="ARCVER" description="basecase version number" size= "8" offset= "0" access="RO" /> + </auxregister> + + <auxregister name="DEBUG" description="Debug" number="0x5" mask ="0xF0800802" access="RW"> + <field name ="FH" description="force halt" size="1" offset="1" access="WO"/> + <field name ="IS" description="single step" size="1" offset="11" access="RW"> + <meaning value="1" description="single instruction stepping is in force"/> + </field> + <field name ="ZZ" description="sleep mode" size="1" offset="23" access="RO"> + <meaning value="1" description="processor is in sleep mode"/> + </field> + <field name ="UB" description="user mode BRK" size="1" offset="28" access="RW"> + <meaning value="0" description="BRK instructions can not be used in User mode"/> + <meaning value="1" description="BRK instructions can be used in User mode"/> + </field> + <field name ="BH" description="breakpoint halt" size="1" offset="29" access="RO"> + <meaning value="1" description="a BRK instruction has been triggered"/> + </field> + <field name ="SH" description="self halt" size="1" offset="30" access="RO"> + <meaning value="1" description="processor has halted itself"/> + </field> + <field name ="LD" description="load pending" size="1" offset="31" access="RO"> + <meaning value="1" description="a delayed load is waiting to complete"/> + </field> + +<!-- for testing + <field name ="LD" description="duplicate!" size="1" offset="10" access="RO"/> + <field name ="OV" description="overlap!" size="3" offset="27" access="RO"/> + <field name ="BG" description="too big!" size="8" offset="27" access="RO"/> + --> + </auxregister> + + <auxregister name="PC" description="program counter" number="0x6" mask ="0xFFFFFFFE" access="RW"/> + + <auxregister name="STATUS32" description="status flags" number="0xA" mask ="0x00001FFF" access="RW" > + <field name="H" description="halt bit" size="1" offset= "0" access="RO" > + <meaning value="1" description="processor is halted"/> + </field> + <field name="E1" description="level 1 interrupt mask bit" size="1" offset= "1" access="RW" > + <meaning value="0" description="level 1 interrupts are enabled"/> + <meaning value="1" description="level 1 interrupts are disabled"/> + </field> + <field name="E2" description="level 2 interrupt mask bit" size="1" offset= "2" access="RW" > + <meaning value="0" description="level 2 interrupts are enabled"/> + <meaning value="1" description="level 2 interrupts are disabled"/> + </field> + <field name="A1" description="level 1 interrupt bit" size="1" offset= "3" access="RO" > + <meaning value="1" description="a level 1 interrupt service routine is active"/> + </field> + <field name="A2" description="level 2 interrupt bit" size="1" offset= "4" access="RO" > + <meaning value="1" description="a level 2 interrupt service routine is active"/> + </field> + <field name="AE" description="exception bit" size="1" offset= "5" access="RO" > + <meaning value="1" description="an exception is active"/> + </field> + <field name="DE" description="delay slot bit" size="1" offset= "6" access="RO" > + <meaning value="1" description="instruction addressed by PC32 is in the delay slot of a taken branch"/> + </field> + <field name="U" description="User mode bit" size="1" offset= "7" access="RW" > + <meaning value="0" description="processor is in Kernel mode"/> + <meaning value="1" description="processor is in User mode"/> + </field> + <field name="V" description="overflow flag" size="1" offset= "8" access="RO" /> + <field name="C" description="carry flag" size="1" offset= "9" access="RO" /> + <field name="N" description="negative flag" size="1" offset="10" access="RO" /> + <field name="Z" description="zero flag" size="1" offset="11" access="RO" /> + <field name="L" description="zero overhead loop bit" size="1" offset="12" access="RO" > + <meaning value="0" description="zero overhead loop mechanism is enabled"/> + <meaning value="1" description="zero overhead loop mechanism is disabled"/> + </field> + </auxregister> + + <auxregister name="STATUS32_L1" description="STATUS32 register in case of L1 interrupts" number="0xB" mask ="0x00001FFE" access="RW" > + <field name="reserved" onwrite="0" size="1" offset="0" access="RW" /> + <field name="E1" description="level 1 interrupt mask bit" size="1" offset= "1" access="RW" > + <meaning value="0" description="level 1 interrupts are enabled"/> + <meaning value="1" description="level 1 interrupts are disabled"/> + </field> + <field name="E2" description="level 2 interrupt mask bit" size="1" offset= "2" access="RW" > + <meaning value="0" description="level 2 interrupts are enabled"/> + <meaning value="1" description="level 2 interrupts are disabled"/> + </field> + <field name="A1" description="level 1 interrupt bit" size="1" offset= "3" access="RO" > + <meaning value="1" description="a level 1 interrupt service routine is active"/> + </field> + <field name="A2" description="level 2 interrupt bit" size="1" offset= "4" access="RO" > + <meaning value="1" description="a level 2 interrupt service routine is active"/> + </field> + <field name="AE" description="exception bit" size="1" offset= "5" access="RO" > + <meaning value="1" description="an exception is active"/> + </field> + <field name="DE" description="delay slot bit" size="1" offset= "6" access="RO" > + <meaning value="1" description="instruction addressed by PC32 is in the delay slot of a taken branch"/> + </field> + <field name="U" description="User mode bit" size="1" offset= "7" access="RW" > + <meaning value="0" description="processor is in Kernel mode"/> + <meaning value="1" description="processor is in User mode"/> + </field> + <field name="V" description="overflow flag" size="1" offset= "8" access="RO" /> + <field name="C" description="carry flag" size="1" offset= "9" access="RO" /> + <field name="N" description="negative flag" size="1" offset="10" access="RO" /> + <field name="Z" description="zero flag" size="1" offset="11" access="RO" /> + <field name="L" description="zero overhead loop bit" size="1" offset="12" access="RO" > + <meaning value="0" description="zero overhead loop mechanism is enabled"/> + <meaning value="1" description="zero overhead loop mechanism is disabled"/> + </field> + </auxregister> + + <auxregister name="STATUS32_L2" description="STATUS32 register in case of L2 interrupts" number="0xC" mask ="0x00001FFE" access="RW" > + <field name="reserved" onwrite="0" size="1" offset="0" access="RW" /> + <field name="E1" description="level 1 interrupt mask bit" size="1" offset= "1" access="RW" > + <meaning value="0" description="level 1 interrupts are enabled"/> + <meaning value="1" description="level 1 interrupts are disabled"/> + </field> + <field name="E2" description="level 2 interrupt mask bit" size="1" offset= "2" access="RW" > + <meaning value="0" description="level 2 interrupts are enabled"/> + <meaning value="1" description="level 2 interrupts are disabled"/> + </field> + <field name="A1" description="level 1 interrupt bit" size="1" offset= "3" access="RO" > + <meaning value="1" description="a level 1 interrupt service routine is active"/> + </field> + <field name="A2" description="level 2 interrupt bit" size="1" offset= "4" access="RO" > + <meaning value="1" description="a level 2 interrupt service routine is active"/> + </field> + <field name="AE" description="exception bit" size="1" offset= "5" access="RO" > + <meaning value="1" description="an exception is active"/> + </field> + <field name="DE" description="delay slot bit" size="1" offset= "6" access="RO" > + <meaning value="1" description="instruction addressed by PC32 is in the delay slot of a taken branch"/> + </field> + <field name="U" description="User mode bit" size="1" offset= "7" access="RW" > + <meaning value="0" description="processor is in Kernel mode"/> + <meaning value="1" description="processor is in User mode"/> + </field> + <field name="V" description="overflow flag" size="1" offset= "8" access="RO" /> + <field name="C" description="carry flag" size="1" offset= "9" access="RO" /> + <field name="N" description="negative flag" size="1" offset="10" access="RO" /> + <field name="Z" description="zero flag" size="1" offset="11" access="RO" /> + <field name="L" description="zero overhead loop bit" size="1" offset="12" access="RO" > + <meaning value="0" description="zero overhead loop mechanism is enabled" /> + <meaning value="1" description="zero overhead loop mechanism is disabled" /> + </field> + </auxregister> + + <auxregister name="COUNT0" description="Processor Timer 0 Count Value" number="0x21" mask ="0xFFFFFFFF" access="RW"/> + + <auxregister name="CONTROL0" description="Processor Timer 0 Control Value" number="0x22" mask ="0x0000000F" access="RW" > + <field name="IE" description="Interrupt Enable flag" size="1" offset="0" access="RW" > + <meaning value="1" description="interrupt will be generated after timer reaches limit condition"/> + </field> + <field name="NH" description="Not Halted mode flag" size="1" offset="1" access="RW" > + <meaning value="0" description="timer counts every clock cycle"/> + <meaning value="1" description="timer counts clock cycles only when processor is running"/> + </field> + <field name="W" description="Watchdog mode flag" size="1" offset="2" access="RW" > + <meaning value="1" description="system Reset signal will be generated after timer reaches limit condition"/> + </field> + </auxregister> + + <auxregister name="LIMIT0" description="Processor Timer 0 Limit Value" number="0x23" mask ="0xFFFFFFFF" access="RW"/> + <auxregister name="INT_VECTOR_BASE" description="Interrupt Vector Base Register" number="0x25" mask ="0xFFFFFC00" access="RW"/> + + <auxregister name="AUX_MACMODE" description="Aux MAC Mode" number="0x41" mask ="0xFFFFFFFF" access="RW" > + <field name="CS" description="" size="1" offset="1" access="RW" > + </field> + <field name="S2" description="channel 2 saturation flag" size="1" offset="4" access="RO" > + <meaning value="1" description="channel 2 has saturated"/> + </field> + <field name="S1" description="channel 1 saturation flag" size="1" offset="9" access="RO" > + <meaning value="1" description="channel 1 has saturated"/> + </field> + </auxregister> + + <auxregister name="AUX_IRQ_LV12" description="Aux IRQ Level 2" number="0x43" mask ="0x00000003" access="RW" > + <field name="L1" description="level 1 interrupt status bit" size="1" offset="0" access="RW" > + <meaning value="1" description="level 1 interrupt has been taken"/> + </field> + <field name="L2" description="level 2 interrupt status bit" size="1" offset="1" access="RW" > + <meaning value="1" description="level 2 interrupt has been taken"/> + </field> + </auxregister> + + <auxregister name="COUNT1" description="Processor Timer 1 Count Value" number="0x100" mask ="0xFFFFFFFF" access="RW"/> + + <auxregister name="CONTROL1" description="Processor Timer 1 Control Value" number="0x101" mask ="0x0000000F" access="RW" > + <field name="IE" description="Interrupt Enable flag" size="1" offset="0" access="RW" > + <meaning value="1" description="interrupt will be generated after timer reaches limit condition"/> + </field> + <field name="NH" description="Not Halted mode flag" size="1" offset="1" access="RW" > + <meaning value="0" description="timer counts every clock cycle"/> + <meaning value="1" description="timer counts clock cycles only when processor is running"/> + </field> + <field name="W" description="Watchdog mode flag" size="1" offset="2" access="RW" > + <meaning value="1" description="system Reset signal will be generated after timer reaches limit condition"/> + </field> + </auxregister> + + <auxregister name="LIMIT1" description="Processor Timer 1 Limit Value" number="0x102" mask ="0xFFFFFFFF" access="RW"/> + + <auxregister name="AUX_IRQ_LEV" description="Interrupt Level programming" number="0x200" mask ="0xFFFFFFF8" access="RW" > + <field name="reserved" onwrite="0" size="3" offset="0" access="RW" /> + </auxregister> + + <auxregister name="AUX_IRQ_HINT" description="Software Triggered Interrupt" number="0x201" mask ="0x0000001F" access="RW"/> + + <auxregister name="ERET" description="Exception Return" number="0x400" mask ="0xFFFFFFFE" access="RW" > + <field name="reserved" onwrite="0" size="1" offset="0" access="RW" /> + </auxregister> + + <auxregister name="ERBTA" description="Exception BTA" number="0x401" mask ="0xFFFFFFFE" access="RW"/> + + <auxregister name="ERSTATUS" description="Exception Return Status" number="0x402" mask ="0x00001FFE" access="RW" > + <field name="reserved" onwrite="0" size="1" offset="0" access="RW" /> + </auxregister> + + <auxregister name="ECR" description="Exception Cause Register" number="0x403" mask ="0x00FFFFFF" access="RO"> + <field name="Vector Number" description="" size="8" offset="16" access="RO" /> + <field name="Cause Code" description="exact cause of exception" size="8" offset="8" access="RO" /> + <field name="Parameter" description="additional information" size="8" offset="0" access="RO" /> + </auxregister> + <auxregister name="EFA" description="Exception Fault Address" number="0x404" mask ="0xFFFFFFFF" access="RW"/> + <auxregister name="ICAUSE1" description="Interrupt Cause (Level 1)" number="0x40A" mask ="0x0000001F" access="RO"/> + <auxregister name="ICAUSE2" description="Interrupt Cause (Level 2)" number="0x40B" mask ="0x0000001F" access="RO"/> + + <auxregister name="AUX_IENABLE" description="Interrupt Mask Programming" number="0x40C" mask ="0xFFFFFFF8" access="RW" > + <field name="reserved" onwrite="7" size="3" offset="0" access="RW" /> + </auxregister> + + <auxregister name="AUX_ITRIGGER" description="Interrupt Sensitivity Programming" number="0x40D" mask ="0xFFFFFFF8" access="RW" > + <field name="reserved" onwrite="7" size="3" offset="0" access="RW" /> + </auxregister> + + <auxregister name="XPU" description="User Mode Extension Permissions" number="0x410" mask ="0xFFFFFFFF" access="RW"/> + + <auxregister name="BTA" description="Branch Target Address" number="0x412" mask ="0xFFFFFFFE" access="RO" > + <field name="reserved" onwrite="0" size="1" offset="0" access="RO" /> + </auxregister> + + <auxregister name="BTA_L1" description="Branch Target Address in Level 1" number="0x413" mask ="0xFFFFFFFE" access="RW" > + <field name="reserved" onwrite="0" size="1" offset="0" access="RW" /> + </auxregister> + + <auxregister name="BTA_L2" description="Branch Target Address in Level 2" number="0x414" mask ="0xFFFFFFFE" access="RW" > + <field name="reserved" onwrite="0" size="1" offset="0" access="RW" /> + </auxregister> + + <auxregister name="AUX_IRQ_PULSE_CANCEL" description="Interrupt Pulse Cancel" number="0x415" mask ="0xFFFFFFFA" access="WO" > + <field name="reserved" onwrite="0" size="1" offset="0" access="WO" /> + <field name="reserved" onwrite="0" size="1" offset="2" access="WO" /> + </auxregister> + + <auxregister name="AUX_IRQ_PENDING" description="Interrupt Pending Register" number="0x416" mask ="0xFFFFFFF8" access="RO"/> + + + <!-- cache control registers --> + + <auxregister name="IC_IVIC" description="Instruction Cache Invalidation Register" number="0x10" mask ="0xFFFFFFFF" access="WO"/> + <auxregister name="IC_CTRL" description="Instruction Cache Control Register" number="0x11" mask ="0x0000003F" access="RW" > + <field name="DC" description="enables/disables the cache" size="1" offset="0" access="RW" > + <meaning value="0" description="cache is enabled" /> + <meaning value="1" description="cache is disabled" /> + </field> + <field name="EB" description="enables/disables cache bypass" size="1" offset="1" access="RO" > + <meaning value="0" description="bypass is disabled" /> + <meaning value="1" description="bypass is enabled" /> + </field> + <field name="reserved" onwrite="0" size="1" offset="2" /> + <field name="SB" description="success of last cache operation" size="1" offset="3" access="RW" > + <meaning value="0" description="last cache operation failed" /> + <meaning value="1" description="last cache operation succeeded" /> + </field> + <field name="RA" description="replacement algorithm" size="1" offset="4" access="RO" > + <meaning value="0" description="random replacement" /> + </field> + <field name="AT" description="address debug type" size="1" offset="5" access="RW" > + <meaning value="0" description="Direct Cache RAM Access" /> + <meaning value="1" description="Cache Controlled RAM Access" /> + </field> + <field name="reserved" onwrite="0" size="26" offset="6" /> + </auxregister> + <auxregister name="DC_IVDC" description="Data Cache Invalidation Register" number="0x47" mask ="0x00000001" access="WO" > + <field name="IV" description="invalidates the entire cache" size="1" offset="0" access="WO" > + <meaning value="0" description="no action" /> + <meaning value="1" description="invalidate the cache" /> + </field> + <field name="reserved" onwrite="0" size="31" offset="1" /> + </auxregister> + <auxregister name="DC_CTRL" description="Data Cache Control Register" number="0x48" mask ="0x000001FF" access="RW" > + <field name="DC" description="enables/disables the cache" size="1" offset="0" access="RW" > + <meaning value="0" description="cache is enabled" /> + <meaning value="1" description="cache is disabled" /> + </field> + <field name="EB" description="enables/disables cache bypass" size="1" offset="1" access="RO" > + <meaning value="0" description="bypass is disabled" /> + <meaning value="1" description="bypass is enabled" /> + </field> + <field name="SB" description="success of last cache operation" size="1" offset="2" access="RW" > + <meaning value="0" description="last cache operation failed" /> + <meaning value="1" description="last cache operation succeeded" /> + </field> + <field name="RA" description="replacement algorithm" size="2" offset="3" access="RO" > + <meaning value="0" description="random replacement" /> + </field> + <field name="AT" description="address debug type" size="1" offset="5" access="RW" > + <meaning value="0" description="Direct Cache RAM Access" /> + <meaning value="1" description="Cache Controlled RAM Access" /> + </field> + <field name="IM" description="invalidate mode" size="1" offset="6" access="RW" > + <meaning value="0" description="invalidate data cache only" /> + <meaning value="1" description="invalidate data cache and flush dirty entries" /> + </field> + <field name="LM" description="lock mode" size="1" offset="7" access="RW" > + <meaning value="0" description="disable flush on locked entry" /> + <meaning value="1" description="enable flush on locked entry" /> + </field> + <field name="FS" description="flush status" size="1" offset="8" access="RO" > + <meaning value="0" description="idle" /> + <meaning value="1" description="flush operation is in progress" /> + </field> + <field name="reserved" onwrite="0" size="23" offset="9" /> + </auxregister> + + + <!-- actionpoint 0 registers --> + + <auxregister name="AMV0" description="Actionpoint 0 Match Value Register" number="0x220" mask ="0xFFFFFFFF" access="RW"/> + <auxregister name="AMM0" description="Actionpoint 0 Match Mask Register" number="0x221" mask ="0xFFFFFFFF" access="RW"/> + <auxregister name="AC0" description="Actionpoint 0 Control Register" number="0x222" mask ="0xFFFFFFFF" access="RW"/> + + + <!-- Build Configuration Registers --> + + <bcr name="BCR_VER" description="Build Configuration Registers Version" number="0x60" mask="0xFFFFFFFF"/> + <bcr name="DCCM_BASE_BUILD" description="Base Address for Data Closely Coupled Memory" number="0x61" mask="0xFFFFFFFF"/> + <bcr name="CRC_BASE_BUILD" description="CRC Unit BCR" number="0x62" mask="0xFFFFFFFF"/> + <bcr name="BTA_LINK_BUILD" description="Interrupt Link Registers available for BTA" number="0x63" mask="0xFFFFFFFF"/> + <bcr name="DVBF_BUILD" description="Dual Viterbi Instruction BCR" number="0x64" mask="0xFFFFFFFF"/> + <bcr name="TEL_INSTR_BUILD" description="Extended Arithmetic Instructions BCR" number="0x65" mask="0xFFFFFFFF"/> + <bcr name="unused" description="unused" number="0x66" mask="0xFFFFFFFF"/> + <bcr name="MEMSUBSYS" description="Memory Subsystem BCR" number="0x67" mask="0xFFFFFFFF"/> + <bcr name="VECBASE_AC_BUILD" description="Interrupt Vector Base BCR" number="0x68" mask="0xFFFFFFFF"/> + <bcr name="P_BASE_ADDRESS" description="Peripheral Base Address" number="0x69" mask="0xFFFFFFFF"/> + <bcr name="unused" description="unused" number="0x6A" mask="0xFFFFFFFF"/> + <bcr name="unused" description="unused" number="0x6B" mask="0xFFFFFFFF"/> + <bcr name="unused" description="unused" number="0x6C" mask="0xFFFFFFFF"/> + <bcr name="unused" description="unused" number="0x6D" mask="0xFFFFFFFF"/> + <bcr name="RF_BUILD" description="Core Registers BCR" number="0x6E" mask="0xFFFFFFFF"/> + <bcr name="MMU_BUILD" description="Memory Management Unit BCR" number="0x6F" mask="0xFFFFFFFF"/> + <bcr name="ARCANGEL_BUILD" description="ARCAngel BCR" number="0x70" mask="0xFFFFFFFF"/> + <bcr name="unused" description="unused" number="0x71" mask="0xFFFFFFFF"/> + <bcr name="DCACHE_BUILD" description="Data Cache BCR" number="0x72" mask="0xFFFFFFFF"/> + <bcr name="MADI_BUILD" description="Multiple ARC Debug Interface" number="0x73" mask="0xFFFFFFFF"/> + <bcr name="DCCM_BUILD" description="Data Closely Coupled Memory BCR" number="0x74" mask="0xFFFFFFFF"/> + <bcr name="TIMER_BUILD" description="Timers BCR" number="0x75" mask="0xFFFFFFFF"/> + <bcr name="AP_BUILD" description="Actionpoints BCR" number="0x76" mask="0xFFFFFFFF"/> + <bcr name="ICACHE_BUILD" description="Instruction Cache BCR" number="0x77" mask="0xFFFFFFFF"/> + <bcr name="ICCM_BUILD" description="Instruction Closely Coupled Memory BCR" number="0x78" mask="0xFFFFFFFF"/> + <bcr name="DSPRAM_BUILD" description="DSP RAM BCR" number="0x79" mask="0xFFFFFFFF"/> + <bcr name="MAC_BUILD" description="MAC Unit BCR" number="0x7A" mask="0xFFFFFFFF"/> + <bcr name="MULTIPLY_BUILD" description="(32 X 32) Multiply Unit BCR" number="0x7B" mask="0xFFFFFFFF"/> + <bcr name="SWAP_BUILD" description="SWAP BCR" number="0x7C" mask="0xFFFFFFFF"/> + <bcr name="NORM_BUILD" description="NORM Unit BCR" number="0x7D" mask="0xFFFFFFFF"/> + <bcr name="MINMAX_BUILD" description="Minmax Unit BCR" number="0x7E" mask="0xFFFFFFFF"/> + <bcr name="BARREL_BUILD" description="Barrel Shifter BCR" number="0x7F" mask="0xFFFFFFFF"/> + +<!-- for testing + + <auxregister name="TEST" description="any old bucket of bits" number="0x99999" mask ="0xFFFFFFFF" access="RW"> + <field name ="AA" description="" size="1" offset="5" access="RO"> + <meaning value="0" description="value is 0"/> + <meaning value="1" description="value is 1"/> + </field> + <field name ="BBBB" description="" size="3" offset="8" access="RO"> + <meaning value="0" description="value is 0"/> + <meaning value="1" description="value is 1"/> + <meaning value="3" description="value is 3"/> + <meaning value="7" description="value is 7"/> + </field> + <field name ="CCCCCCCCC" description="" size="8" offset="17" access="RO"> + </field> + </auxregister> + --> + +</feature> +</target> diff --git a/gdb/gdb-events.c b/gdb/gdb-events.c index d6884751dad..f24ece5feeb 100644 --- a/gdb/gdb-events.c +++ b/gdb/gdb-events.c @@ -122,6 +122,18 @@ architecture_changed_event (void) current_event_hooks->architecture_changed (); } +// begin ARC +void +reg_architecture_changed_event (void) +{ + if (gdb_events_debug) + fprintf_unfiltered (gdb_stdlog, "reg_architecture_changed_event\n"); + if (!current_event_hooks->reg_architecture_changed) + return; + current_event_hooks->reg_architecture_changed (); +} +// end ARC + struct gdb_events * deprecated_set_gdb_event_hooks (struct gdb_events *vector) { @@ -148,6 +160,9 @@ enum gdb_event tracepoint_delete, tracepoint_modify, architecture_changed, +// begin ARC + reg_architecture_changed, +// end ARC nr_gdb_events }; @@ -271,6 +286,16 @@ queue_architecture_changed (void) append (event); } +// begin ARC +static void +queue_reg_architecture_changed (void) +{ + struct event *event = XMALLOC (struct event); + event->type = reg_architecture_changed; + append (event); +} +// end ARC + void gdb_events_deliver (struct gdb_events *vector) { @@ -319,6 +344,11 @@ gdb_events_deliver (struct gdb_events *vector) case architecture_changed: vector->architecture_changed (); break; +// begin ARC + case reg_architecture_changed: + vector->reg_architecture_changed (); + break; +// end ARC } delivering_events = event->next; xfree (event); @@ -337,6 +367,9 @@ _initialize_gdb_events (void) queue_event_hooks.tracepoint_delete = queue_tracepoint_delete; queue_event_hooks.tracepoint_modify = queue_tracepoint_modify; queue_event_hooks.architecture_changed = queue_architecture_changed; +// begin ARC + queue_event_hooks.reg_architecture_changed = queue_reg_architecture_changed; +// end ARC add_setshow_zinteger_cmd ("event", class_maintenance, &gdb_events_debug, _("\ diff --git a/gdb/gdb-events.h b/gdb/gdb-events.h index 9c38c0afb6f..5a6dd4af0bf 100644 --- a/gdb/gdb-events.h +++ b/gdb/gdb-events.h @@ -55,6 +55,9 @@ typedef void (gdb_events_tracepoint_create_ftype) (int number); typedef void (gdb_events_tracepoint_delete_ftype) (int number); typedef void (gdb_events_tracepoint_modify_ftype) (int number); typedef void (gdb_events_architecture_changed_ftype) (void); +// begin ARC +typedef void (gdb_events_reg_architecture_changed_ftype) (void); +// end ARC /* gdb-events: object. */ @@ -68,6 +71,9 @@ struct gdb_events gdb_events_tracepoint_delete_ftype *tracepoint_delete; gdb_events_tracepoint_modify_ftype *tracepoint_modify; gdb_events_architecture_changed_ftype *architecture_changed; +// begin ARC + gdb_events_reg_architecture_changed_ftype *reg_architecture_changed; +// end ARC }; @@ -81,6 +87,9 @@ extern void tracepoint_create_event (int number); extern void tracepoint_delete_event (int number); extern void tracepoint_modify_event (int number); extern void architecture_changed_event (void); +// begin ARC +extern void reg_architecture_changed_event (void); +// end ARC /* Install custom gdb-events hooks. */ extern struct gdb_events *deprecated_set_gdb_event_hooks (struct gdb_events *vector); diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in index d967ff5dca0..f7fe7d47c93 100644 --- a/gdb/gdbserver/Makefile.in +++ b/gdb/gdbserver/Makefile.in @@ -330,7 +330,7 @@ spu-low.o: spu-low.c $(server_h) reg-arc.o : reg-arc.c $(regdef_h) reg-arc.c : $(srcdir)/../regformats/reg-arc.dat $(regdat_sh) - sh $(regdat_sh) $(srcdir)/../regformats/reg-arc.dat reg-arc.c + $(SHELL) $(regdat_sh) $(srcdir)/../regformats/reg-arc.dat reg-arc.c reg-arm.o : reg-arm.c $(regdef_h) reg-arm.c : $(srcdir)/../regformats/reg-arm.dat $(regdat_sh) $(SHELL) $(regdat_sh) $(srcdir)/../regformats/reg-arm.dat reg-arm.c diff --git a/gdb/gdbserver/build_gdbserver.sh b/gdb/gdbserver/build_gdbserver.sh new file mode 100755 index 00000000000..bdf886f695e --- /dev/null +++ b/gdb/gdbserver/build_gdbserver.sh @@ -0,0 +1,6 @@ +#Bash script to build gdbserver +export CFLAGS="-mA7 -static -O0" +export LDFLAGS="-mA7" +export CC=arc-linux-uclibc-gcc +./configure --host=i386-redhat-linux-gnu --target=arc-linux-uclibc +make diff --git a/gdb/gdbserver/configure.srv b/gdb/gdbserver/configure.srv index c0d246b5463..c3c2d56eb8b 100644 --- a/gdb/gdbserver/configure.srv +++ b/gdb/gdbserver/configure.srv @@ -26,11 +26,16 @@ srv_hostio_err_objs="hostio-errno.o" # Input is taken from the "${target}" variable. case "${target}" in - arc*-*-linux*) srv_regobj=reg-arc.o - srv_tgtobj="linux-low.o linux-arc-low.o" - srv_linux_usrregs=yes - srv_linux_thread_db=yes - ;; + arc*-*-linux*) srv_regobj=reg-arc.o + srv_tgtobj="linux-low.o linux-arc-low.o" + srv_linux_usrregs=yes + srv_linux_thread_db=yes + ;; + arc*-*) srv_regobj=reg-arc.o + srv_tgtobj="linux-low.o linux-arc-low.o" + srv_linux_usrregs=yes + srv_linux_thread_db=yes + ;; arm*-*-linux*) srv_tgtobj="linux-low.o linux-arm-low.o" srv_linux_usrregs=yes srv_linux_regsets=yes diff --git a/gdb/gdbserver/linux-arc-low.c b/gdb/gdbserver/linux-arc-low.c index 28c7ead3deb..c5faa1df4a5 100644 --- a/gdb/gdbserver/linux-arc-low.c +++ b/gdb/gdbserver/linux-arc-low.c @@ -113,7 +113,7 @@ arc_breakpoint_at (CORE_ADDR where) { unsigned long insn; - (*the_target->read_memory) (where, (char *) &insn, arc_breakpoint_len); + (*the_target->read_memory) (where, (unsigned char *) &insn, arc_breakpoint_len); if (insn == arc_breakpoint) return 1; @@ -141,7 +141,7 @@ struct linux_target_ops the_low_target = { arc_cannot_store_register, arc_get_pc, arc_set_pc, - (const char *) &arc_breakpoint, + (const unsigned char *) &arc_breakpoint, arc_breakpoint_len, arc_reinsert_addr, 0, diff --git a/gdb/gdbserver/proc-service.c b/gdb/gdbserver/proc-service.c index 065bfdf3885..fc7bc19fbe5 100644 --- a/gdb/gdbserver/proc-service.c +++ b/gdb/gdbserver/proc-service.c @@ -28,6 +28,7 @@ #include "linux-low.h" #include "gdb_proc_service.h" +typedef unsigned long paddr_t; typedef struct ps_prochandle *gdb_ps_prochandle_t; typedef void *gdb_ps_read_buf_t; @@ -61,7 +62,7 @@ gregset_info(void) ps_err_e ps_pglobal_lookup (gdb_ps_prochandle_t ph, const char *obj, - const char *name, psaddr_t *sym_addr) + const char *name, paddr_t *sym_addr) { CORE_ADDR addr; char *uscorename; @@ -89,7 +90,8 @@ ps_pglobal_lookup (gdb_ps_prochandle_t ph, const char *obj, return PS_NOSYM; } - *sym_addr = (psaddr_t) (unsigned long) addr; + *sym_addr = (paddr_t) (unsigned long) addr; + return PS_OK; } diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c index 496334cacf0..2e049ee22a4 100644 --- a/gdb/gdbserver/remote-utils.c +++ b/gdb/gdbserver/remote-utils.c @@ -169,9 +169,7 @@ remote_open (char *name) } #endif -/* #ifdef HAVE_SGTTY */ - /* soam */ -#if 0 +#ifdef HAVE_SGTTY { struct sgttyb sg; @@ -773,8 +771,7 @@ getpkt (char *buf) c1 = fromhex (readchar ()); c2 = fromhex (readchar ()); -/* if (csum == (c1 << 4) + c2) */ - if(1) /* (soam) for testing */ + if (csum == (c1 << 4) + c2) break; fprintf (stderr, "Bad checksum, sentsum=0x%x, csum=0x%x, buf=%s\n", diff --git a/gdb/gpio.h b/gdb/gpio.h deleted file mode 100644 index 86aa4cab8a9..00000000000 --- a/gdb/gpio.h +++ /dev/null @@ -1,38 +0,0 @@ -struct GPIO_ioctl { - // This is used for general input and output in the same ioctl. - // inlen is replaced by the number of input bytes consumed. - // inlen is always even and represents a number of pairs: - // [0/1/2,value]: write value to port_base+0/1/2. - // [0x80/0x81/0x82, --]: read value from port_base+0/1/2 - // and append result to outbuf. - // Thus one can intermix read and write in the same ioctl. - unsigned inlen; char *inbuf; - // outlen is replaced by # of output bytes written. - unsigned outlen; char *outbuf; - }; - -// IO control numbers - -// Linux kernel uses 0x54XX for special purposes. Avoid such. -// We'll pick large numbers. -// We don't use the linux convention of dividing the IOC into a bunch -// of bit fields. We can always switch to this later, as the -// IOC 0 returns the driver version (which IOC value will never change). - -#define GPIO_IOC_VERSION 0 // returns version -#define GPIO_IOC_BASE 0xaa3a0000 // Intended for use on ARCangel 3! - -// Switch base address of parallel port. 0x3f8 is assumed. -// WARNING! You can write on any port whatsoever with this driver. -// BE CAREFUL! -#define GPIO_IOC_SET_PORT_BASE (GPIO_IOC_BASE+1) // cmd, arg=port base - -// General input/output ioctl. See GPIO_ioctl struct. -#define GPIO_IOC_DO_IO (GPIO_IOC_BASE+2) // cmd, arg=GPIO_ioctl * - -// For emergency purposes in case the driver is goofed up. -#define GPIO_IOC_HARDRESET (GPIO_IOC_BASE+3) // cmd, no arg - -// Do you have an antiquated parallel port? You might need to ask -// the driver to use outb_p and inb_p (_p = pause). Default is not to. -#define GPIO_IOC_SET_PAUSE (GPIO_IOC_BASE+4) // arg = 1 => use pause; o/wise not. diff --git a/gdb/infcmd.c b/gdb/infcmd.c index 972f9e22324..4e76260972a 100644 --- a/gdb/infcmd.c +++ b/gdb/infcmd.c @@ -1603,15 +1603,25 @@ default_print_registers_info (struct gdbarch *gdbarch, continue; } - /* If the register name is empty, it is undefined for this - processor, so don't display anything. */ - if (gdbarch_register_name (gdbarch, i) == NULL - || *(gdbarch_register_name (gdbarch, i)) == '\0') - continue; - - fputs_filtered (gdbarch_register_name (gdbarch, i), file); - print_spaces_filtered (15 - strlen (gdbarch_register_name - (gdbarch, i)), file); + { + /* richards ARC 29/10/27 gdb bug: 9884 + call the gdbarch_register_name once only! + */ + const char* name = gdbarch_register_name (gdbarch, i); + + /* If the register name is empty, it is undefined for this + processor, so don't display anything. */ + if (name == NULL || *name == '\0') + continue; + + fputs_filtered (name, file); + + /* richards ARC 29/9/08 gdb bug: 9885 + Some ARC register names are longer than 15 chars! + There should be a gdbarch_max_register_name_length function + which could be called here instead of using an integer literal. */ + print_spaces_filtered (22 - strlen (name), file); + } /* Get the data in raw format. */ if (! frame_register_read (frame, i, buffer)) diff --git a/gdb/remote-fileio.c b/gdb/remote-fileio.c index e51f27249b5..9a961f47d18 100644 --- a/gdb/remote-fileio.c +++ b/gdb/remote-fileio.c @@ -24,441 +24,14 @@ #include "gdbcmd.h" #include "remote.h" #include "gdb/fileio.h" -#include "gdb_wait.h" -#include "gdb_stat.h" #include "exceptions.h" #include "remote-fileio.h" +#include "target-fileio.h" -#include <fcntl.h> -#include <sys/time.h> -#ifdef __CYGWIN__ -#include <sys/cygwin.h> /* For cygwin_conv_to_full_posix_path. */ -#endif #include <signal.h> -static struct { - int *fd_map; - int fd_map_size; -} remote_fio_data; - -#define FIO_FD_INVALID -1 -#define FIO_FD_CONSOLE_IN -2 -#define FIO_FD_CONSOLE_OUT -3 - -static int remote_fio_system_call_allowed = 0; - -static int -remote_fileio_init_fd_map (void) -{ - int i; - - if (!remote_fio_data.fd_map) - { - remote_fio_data.fd_map = (int *) xmalloc (10 * sizeof (int)); - remote_fio_data.fd_map_size = 10; - remote_fio_data.fd_map[0] = FIO_FD_CONSOLE_IN; - remote_fio_data.fd_map[1] = FIO_FD_CONSOLE_OUT; - remote_fio_data.fd_map[2] = FIO_FD_CONSOLE_OUT; - for (i = 3; i < 10; ++i) - remote_fio_data.fd_map[i] = FIO_FD_INVALID; - } - return 3; -} - -static int -remote_fileio_resize_fd_map (void) -{ - int i = remote_fio_data.fd_map_size; - - if (!remote_fio_data.fd_map) - return remote_fileio_init_fd_map (); - remote_fio_data.fd_map_size += 10; - remote_fio_data.fd_map = - (int *) xrealloc (remote_fio_data.fd_map, - remote_fio_data.fd_map_size * sizeof (int)); - for (; i < remote_fio_data.fd_map_size; i++) - remote_fio_data.fd_map[i] = FIO_FD_INVALID; - return remote_fio_data.fd_map_size - 10; -} - -static int -remote_fileio_next_free_fd (void) -{ - int i; - - for (i = 0; i < remote_fio_data.fd_map_size; ++i) - if (remote_fio_data.fd_map[i] == FIO_FD_INVALID) - return i; - return remote_fileio_resize_fd_map (); -} - -static int -remote_fileio_fd_to_targetfd (int fd) -{ - int target_fd = remote_fileio_next_free_fd (); - remote_fio_data.fd_map[target_fd] = fd; - return target_fd; -} - -static int -remote_fileio_map_fd (int target_fd) -{ - remote_fileio_init_fd_map (); - if (target_fd < 0 || target_fd >= remote_fio_data.fd_map_size) - return FIO_FD_INVALID; - return remote_fio_data.fd_map[target_fd]; -} - -static void -remote_fileio_close_target_fd (int target_fd) -{ - remote_fileio_init_fd_map (); - if (target_fd >= 0 && target_fd < remote_fio_data.fd_map_size) - remote_fio_data.fd_map[target_fd] = FIO_FD_INVALID; -} - -static int -remote_fileio_oflags_to_host (long flags) -{ - int hflags = 0; - - if (flags & FILEIO_O_CREAT) - hflags |= O_CREAT; - if (flags & FILEIO_O_EXCL) - hflags |= O_EXCL; - if (flags & FILEIO_O_TRUNC) - hflags |= O_TRUNC; - if (flags & FILEIO_O_APPEND) - hflags |= O_APPEND; - if (flags & FILEIO_O_RDONLY) - hflags |= O_RDONLY; - if (flags & FILEIO_O_WRONLY) - hflags |= O_WRONLY; - if (flags & FILEIO_O_RDWR) - hflags |= O_RDWR; -/* On systems supporting binary and text mode, always open files in - binary mode. */ -#ifdef O_BINARY - hflags |= O_BINARY; -#endif - return hflags; -} - -static mode_t -remote_fileio_mode_to_host (long mode, int open_call) -{ - mode_t hmode = 0; - - if (!open_call) - { - if (mode & FILEIO_S_IFREG) - hmode |= S_IFREG; - if (mode & FILEIO_S_IFDIR) - hmode |= S_IFDIR; - if (mode & FILEIO_S_IFCHR) - hmode |= S_IFCHR; - } - if (mode & FILEIO_S_IRUSR) - hmode |= S_IRUSR; - if (mode & FILEIO_S_IWUSR) - hmode |= S_IWUSR; - if (mode & FILEIO_S_IXUSR) - hmode |= S_IXUSR; -#ifdef S_IRGRP - if (mode & FILEIO_S_IRGRP) - hmode |= S_IRGRP; -#endif -#ifdef S_IWGRP - if (mode & FILEIO_S_IWGRP) - hmode |= S_IWGRP; -#endif -#ifdef S_IXGRP - if (mode & FILEIO_S_IXGRP) - hmode |= S_IXGRP; -#endif - if (mode & FILEIO_S_IROTH) - hmode |= S_IROTH; -#ifdef S_IWOTH - if (mode & FILEIO_S_IWOTH) - hmode |= S_IWOTH; -#endif -#ifdef S_IXOTH - if (mode & FILEIO_S_IXOTH) - hmode |= S_IXOTH; -#endif - return hmode; -} - -static LONGEST -remote_fileio_mode_to_target (mode_t mode) -{ - mode_t tmode = 0; - - if (S_ISREG(mode)) - tmode |= FILEIO_S_IFREG; - if (S_ISDIR(mode)) - tmode |= FILEIO_S_IFDIR; - if (S_ISCHR(mode)) - tmode |= FILEIO_S_IFCHR; - if (mode & S_IRUSR) - tmode |= FILEIO_S_IRUSR; - if (mode & S_IWUSR) - tmode |= FILEIO_S_IWUSR; - if (mode & S_IXUSR) - tmode |= FILEIO_S_IXUSR; -#ifdef S_IRGRP - if (mode & S_IRGRP) - tmode |= FILEIO_S_IRGRP; -#endif -#ifdef S_IWRGRP - if (mode & S_IWGRP) - tmode |= FILEIO_S_IWGRP; -#endif -#ifdef S_IXGRP - if (mode & S_IXGRP) - tmode |= FILEIO_S_IXGRP; -#endif - if (mode & S_IROTH) - tmode |= FILEIO_S_IROTH; -#ifdef S_IWOTH - if (mode & S_IWOTH) - tmode |= FILEIO_S_IWOTH; -#endif -#ifdef S_IXOTH - if (mode & S_IXOTH) - tmode |= FILEIO_S_IXOTH; -#endif - return tmode; -} - -static int -remote_fileio_errno_to_target (int error) -{ - switch (error) - { - case EPERM: - return FILEIO_EPERM; - case ENOENT: - return FILEIO_ENOENT; - case EINTR: - return FILEIO_EINTR; - case EIO: - return FILEIO_EIO; - case EBADF: - return FILEIO_EBADF; - case EACCES: - return FILEIO_EACCES; - case EFAULT: - return FILEIO_EFAULT; - case EBUSY: - return FILEIO_EBUSY; - case EEXIST: - return FILEIO_EEXIST; - case ENODEV: - return FILEIO_ENODEV; - case ENOTDIR: - return FILEIO_ENOTDIR; - case EISDIR: - return FILEIO_EISDIR; - case EINVAL: - return FILEIO_EINVAL; - case ENFILE: - return FILEIO_ENFILE; - case EMFILE: - return FILEIO_EMFILE; - case EFBIG: - return FILEIO_EFBIG; - case ENOSPC: - return FILEIO_ENOSPC; - case ESPIPE: - return FILEIO_ESPIPE; - case EROFS: - return FILEIO_EROFS; - case ENOSYS: - return FILEIO_ENOSYS; - case ENAMETOOLONG: - return FILEIO_ENAMETOOLONG; - } - return FILEIO_EUNKNOWN; -} - -static int -remote_fileio_seek_flag_to_host (long num, int *flag) -{ - if (!flag) - return 0; - switch (num) - { - case FILEIO_SEEK_SET: - *flag = SEEK_SET; - break; - case FILEIO_SEEK_CUR: - *flag = SEEK_CUR; - break; - case FILEIO_SEEK_END: - *flag = SEEK_END; - break; - default: - return -1; - } - return 0; -} - -static int -remote_fileio_extract_long (char **buf, LONGEST *retlong) -{ - char *c; - int sign = 1; - - if (!buf || !*buf || !**buf || !retlong) - return -1; - c = strchr (*buf, ','); - if (c) - *c++ = '\0'; - else - c = strchr (*buf, '\0'); - while (strchr ("+-", **buf)) - { - if (**buf == '-') - sign = -sign; - ++*buf; - } - for (*retlong = 0; **buf; ++*buf) - { - *retlong <<= 4; - if (**buf >= '0' && **buf <= '9') - *retlong += **buf - '0'; - else if (**buf >= 'a' && **buf <= 'f') - *retlong += **buf - 'a' + 10; - else if (**buf >= 'A' && **buf <= 'F') - *retlong += **buf - 'A' + 10; - else - return -1; - } - *retlong *= sign; - *buf = c; - return 0; -} - -static int -remote_fileio_extract_int (char **buf, long *retint) -{ - int ret; - LONGEST retlong; - - if (!retint) - return -1; - ret = remote_fileio_extract_long (buf, &retlong); - if (!ret) - *retint = (long) retlong; - return ret; -} - -static int -remote_fileio_extract_ptr_w_len (char **buf, CORE_ADDR *ptrval, int *length) -{ - char *c; - LONGEST retlong; - - if (!buf || !*buf || !**buf || !ptrval || !length) - return -1; - c = strchr (*buf, '/'); - if (!c) - return -1; - *c++ = '\0'; - if (remote_fileio_extract_long (buf, &retlong)) - return -1; - *ptrval = (CORE_ADDR) retlong; - *buf = c; - if (remote_fileio_extract_long (buf, &retlong)) - return -1; - *length = (int) retlong; - return 0; -} - -/* Convert to big endian */ -static void -remote_fileio_to_be (LONGEST num, char *buf, int bytes) -{ - int i; - - for (i = 0; i < bytes; ++i) - buf[i] = (num >> (8 * (bytes - i - 1))) & 0xff; -} - -static void -remote_fileio_to_fio_uint (long num, fio_uint_t fnum) -{ - remote_fileio_to_be ((LONGEST) num, (char *) fnum, 4); -} - -static void -remote_fileio_to_fio_mode (mode_t num, fio_mode_t fnum) -{ - remote_fileio_to_be (remote_fileio_mode_to_target(num), (char *) fnum, 4); -} - -static void -remote_fileio_to_fio_time (time_t num, fio_time_t fnum) -{ - remote_fileio_to_be ((LONGEST) num, (char *) fnum, 4); -} - -static void -remote_fileio_to_fio_long (LONGEST num, fio_long_t fnum) -{ - remote_fileio_to_be (num, (char *) fnum, 8); -} - -static void -remote_fileio_to_fio_ulong (LONGEST num, fio_ulong_t fnum) -{ - remote_fileio_to_be (num, (char *) fnum, 8); -} - -static void -remote_fileio_to_fio_stat (struct stat *st, struct fio_stat *fst) -{ - LONGEST blksize; - - /* `st_dev' is set in the calling function */ - remote_fileio_to_fio_uint ((long) st->st_ino, fst->fst_ino); - remote_fileio_to_fio_mode (st->st_mode, fst->fst_mode); - remote_fileio_to_fio_uint ((long) st->st_nlink, fst->fst_nlink); - remote_fileio_to_fio_uint ((long) st->st_uid, fst->fst_uid); - remote_fileio_to_fio_uint ((long) st->st_gid, fst->fst_gid); - remote_fileio_to_fio_uint ((long) st->st_rdev, fst->fst_rdev); - remote_fileio_to_fio_ulong ((LONGEST) st->st_size, fst->fst_size); -#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE - blksize = st->st_blksize; -#else - blksize = 512; -#endif - remote_fileio_to_fio_ulong (blksize, fst->fst_blksize); -#if HAVE_STRUCT_STAT_ST_BLOCKS - remote_fileio_to_fio_ulong ((LONGEST) st->st_blocks, fst->fst_blocks); -#else - /* FIXME: This is correct for DJGPP, but other systems that don't - have st_blocks, if any, might prefer 512 instead of st_blksize. - (eliz, 30-12-2003) */ - remote_fileio_to_fio_ulong (((LONGEST) st->st_size + blksize - 1) - / blksize, - fst->fst_blocks); -#endif - remote_fileio_to_fio_time (st->st_atime, fst->fst_atime); - remote_fileio_to_fio_time (st->st_mtime, fst->fst_mtime); - remote_fileio_to_fio_time (st->st_ctime, fst->fst_ctime); -} - -static void -remote_fileio_to_fio_timeval (struct timeval *tv, struct fio_timeval *ftv) -{ - remote_fileio_to_fio_time (tv->tv_sec, ftv->ftv_sec); - remote_fileio_to_fio_long (tv->tv_usec, ftv->ftv_usec); -} static int remote_fio_ctrl_c_flag = 0; -static int remote_fio_no_longjmp = 0; #if defined (HAVE_SIGACTION) && defined (SA_RESTART) static struct sigaction remote_fio_sa; @@ -508,7 +81,7 @@ remote_fileio_ctrl_c_signal_handler (int signo) { remote_fileio_sig_set (SIG_IGN); remote_fio_ctrl_c_flag = 1; - if (!remote_fio_no_longjmp) + if (!target_fio_no_longjmp) deprecated_throw_reason (RETURN_QUIT); remote_fileio_sig_set (remote_fileio_ctrl_c_signal_handler); } @@ -543,30 +116,6 @@ remote_fileio_reply (int retcode, int error) putpkt (buf); } -static void -remote_fileio_ioerror (void) -{ - remote_fileio_reply (-1, FILEIO_EIO); -} - -static void -remote_fileio_badfd (void) -{ - remote_fileio_reply (-1, FILEIO_EBADF); -} - -static void -remote_fileio_return_errno (int retcode) -{ - remote_fileio_reply (retcode, - retcode < 0 ? remote_fileio_errno_to_target (errno) : 0); -} - -static void -remote_fileio_return_success (int retcode) -{ - remote_fileio_reply (retcode, 0); -} /* Wrapper function for remote_write_bytes() which has the disadvantage to write only one packet, regardless of the requested number of bytes to @@ -586,791 +135,11 @@ remote_fileio_write_bytes (CORE_ADDR memaddr, gdb_byte *myaddr, int len) return ret; } -static void -remote_fileio_func_open (char *buf) -{ - CORE_ADDR ptrval; - int length, retlength; - long num; - int flags, fd; - mode_t mode; - char *pathname; - struct stat st; - - /* 1. Parameter: Ptr to pathname / length incl. trailing zero */ - if (remote_fileio_extract_ptr_w_len (&buf, &ptrval, &length)) - { - remote_fileio_ioerror (); - return; - } - /* 2. Parameter: open flags */ - if (remote_fileio_extract_int (&buf, &num)) - { - remote_fileio_ioerror (); - return; - } - flags = remote_fileio_oflags_to_host (num); - /* 3. Parameter: open mode */ - if (remote_fileio_extract_int (&buf, &num)) - { - remote_fileio_ioerror (); - return; - } - mode = remote_fileio_mode_to_host (num, 1); - - /* Request pathname using 'm' packet */ - pathname = alloca (length); - retlength = remote_read_bytes (ptrval, (gdb_byte *) pathname, length); - if (retlength != length) - { - remote_fileio_ioerror (); - return; - } - - /* Check if pathname exists and is not a regular file or directory. If so, - return an appropriate error code. Same for trying to open directories - for writing. */ - if (!stat (pathname, &st)) - { - if (!S_ISREG (st.st_mode) && !S_ISDIR (st.st_mode)) - { - remote_fileio_reply (-1, FILEIO_ENODEV); - return; - } - if (S_ISDIR (st.st_mode) - && ((flags & O_WRONLY) == O_WRONLY || (flags & O_RDWR) == O_RDWR)) - { - remote_fileio_reply (-1, FILEIO_EISDIR); - return; - } - } - - remote_fio_no_longjmp = 1; - fd = open (pathname, flags, mode); - if (fd < 0) - { - remote_fileio_return_errno (-1); - return; - } - - fd = remote_fileio_fd_to_targetfd (fd); - remote_fileio_return_success (fd); -} - -static void -remote_fileio_func_close (char *buf) -{ - long num; - int fd; - - /* Parameter: file descriptor */ - if (remote_fileio_extract_int (&buf, &num)) - { - remote_fileio_ioerror (); - return; - } - fd = remote_fileio_map_fd ((int) num); - if (fd == FIO_FD_INVALID) - { - remote_fileio_badfd (); - return; - } - - remote_fio_no_longjmp = 1; - if (fd != FIO_FD_CONSOLE_IN && fd != FIO_FD_CONSOLE_OUT && close (fd)) - remote_fileio_return_errno (-1); - remote_fileio_close_target_fd ((int) num); - remote_fileio_return_success (0); -} static void -remote_fileio_func_read (char *buf) +set_ctrl_c_signal_handler(void) { - long target_fd, num; - LONGEST lnum; - CORE_ADDR ptrval; - int fd, ret, retlength; - gdb_byte *buffer; - size_t length; - off_t old_offset, new_offset; - - /* 1. Parameter: file descriptor */ - if (remote_fileio_extract_int (&buf, &target_fd)) - { - remote_fileio_ioerror (); - return; - } - fd = remote_fileio_map_fd ((int) target_fd); - if (fd == FIO_FD_INVALID) - { - remote_fileio_badfd (); - return; - } - /* 2. Parameter: buffer pointer */ - if (remote_fileio_extract_long (&buf, &lnum)) - { - remote_fileio_ioerror (); - return; - } - ptrval = (CORE_ADDR) lnum; - /* 3. Parameter: buffer length */ - if (remote_fileio_extract_int (&buf, &num)) - { - remote_fileio_ioerror (); - return; - } - length = (size_t) num; - - switch (fd) - { - case FIO_FD_CONSOLE_OUT: - remote_fileio_badfd (); - return; - case FIO_FD_CONSOLE_IN: - { - static char *remaining_buf = NULL; - static int remaining_length = 0; - - buffer = (gdb_byte *) xmalloc (32768); - if (remaining_buf) - { - remote_fio_no_longjmp = 1; - if (remaining_length > length) - { - memcpy (buffer, remaining_buf, length); - memmove (remaining_buf, remaining_buf + length, - remaining_length - length); - remaining_length -= length; - ret = length; - } - else - { - memcpy (buffer, remaining_buf, remaining_length); - xfree (remaining_buf); - remaining_buf = NULL; - ret = remaining_length; - } - } - else - { - ret = ui_file_read (gdb_stdtargin, (char *) buffer, 32767); - remote_fio_no_longjmp = 1; - if (ret > 0 && (size_t)ret > length) - { - remaining_buf = (char *) xmalloc (ret - length); - remaining_length = ret - length; - memcpy (remaining_buf, buffer + length, remaining_length); - ret = length; - } - } - } - break; - default: - buffer = (gdb_byte *) xmalloc (length); - /* POSIX defines EINTR behaviour of read in a weird way. It's allowed - for read() to return -1 even if "some" bytes have been read. It - has been corrected in SUSv2 but that doesn't help us much... - Therefore a complete solution must check how many bytes have been - read on EINTR to return a more reliable value to the target */ - old_offset = lseek (fd, 0, SEEK_CUR); - remote_fio_no_longjmp = 1; - ret = read (fd, buffer, length); - if (ret < 0 && errno == EINTR) - { - new_offset = lseek (fd, 0, SEEK_CUR); - /* If some data has been read, return the number of bytes read. - The Ctrl-C flag is set in remote_fileio_reply() anyway */ - if (old_offset != new_offset) - ret = new_offset - old_offset; - } - break; - } - - if (ret > 0) - { - retlength = remote_fileio_write_bytes (ptrval, buffer, ret); - if (retlength != ret) - ret = -1; /* errno has been set to EIO in remote_fileio_write_bytes() */ - } - - if (ret < 0) - remote_fileio_return_errno (-1); - else - remote_fileio_return_success (ret); - - xfree (buffer); -} - -static void -remote_fileio_func_write (char *buf) -{ - long target_fd, num; - LONGEST lnum; - CORE_ADDR ptrval; - int fd, ret, retlength; - gdb_byte *buffer; - size_t length; - - /* 1. Parameter: file descriptor */ - if (remote_fileio_extract_int (&buf, &target_fd)) - { - remote_fileio_ioerror (); - return; - } - fd = remote_fileio_map_fd ((int) target_fd); - if (fd == FIO_FD_INVALID) - { - remote_fileio_badfd (); - return; - } - /* 2. Parameter: buffer pointer */ - if (remote_fileio_extract_long (&buf, &lnum)) - { - remote_fileio_ioerror (); - return; - } - ptrval = (CORE_ADDR) lnum; - /* 3. Parameter: buffer length */ - if (remote_fileio_extract_int (&buf, &num)) - { - remote_fileio_ioerror (); - return; - } - length = (size_t) num; - - buffer = (gdb_byte *) xmalloc (length); - retlength = remote_read_bytes (ptrval, buffer, length); - if (retlength != length) - { - xfree (buffer); - remote_fileio_ioerror (); - return; - } - - remote_fio_no_longjmp = 1; - switch (fd) - { - case FIO_FD_CONSOLE_IN: - remote_fileio_badfd (); - xfree (buffer); - return; - case FIO_FD_CONSOLE_OUT: - ui_file_write (target_fd == 1 ? gdb_stdtarg : gdb_stdtargerr, - (char *) buffer, length); - gdb_flush (target_fd == 1 ? gdb_stdtarg : gdb_stdtargerr); - ret = length; - break; - default: - ret = write (fd, buffer, length); - if (ret < 0 && errno == EACCES) - errno = EBADF; /* Cygwin returns EACCESS when writing to a R/O file.*/ - break; - } - - if (ret < 0) - remote_fileio_return_errno (-1); - else - remote_fileio_return_success (ret); - - xfree (buffer); -} - -static void -remote_fileio_func_lseek (char *buf) -{ - long num; - LONGEST lnum; - int fd, flag; - off_t offset, ret; - - /* 1. Parameter: file descriptor */ - if (remote_fileio_extract_int (&buf, &num)) - { - remote_fileio_ioerror (); - return; - } - fd = remote_fileio_map_fd ((int) num); - if (fd == FIO_FD_INVALID) - { - remote_fileio_badfd (); - return; - } - else if (fd == FIO_FD_CONSOLE_IN || fd == FIO_FD_CONSOLE_OUT) - { - remote_fileio_reply (-1, FILEIO_ESPIPE); - return; - } - - /* 2. Parameter: offset */ - if (remote_fileio_extract_long (&buf, &lnum)) - { - remote_fileio_ioerror (); - return; - } - offset = (off_t) lnum; - /* 3. Parameter: flag */ - if (remote_fileio_extract_int (&buf, &num)) - { - remote_fileio_ioerror (); - return; - } - if (remote_fileio_seek_flag_to_host (num, &flag)) - { - remote_fileio_reply (-1, FILEIO_EINVAL); - return; - } - - remote_fio_no_longjmp = 1; - ret = lseek (fd, offset, flag); - - if (ret == (off_t) -1) - remote_fileio_return_errno (-1); - else - remote_fileio_return_success (ret); -} - -static void -remote_fileio_func_rename (char *buf) -{ - CORE_ADDR old_ptr, new_ptr; - int old_len, new_len, retlength; - char *oldpath, *newpath; - int ret, of, nf; - struct stat ost, nst; - - /* 1. Parameter: Ptr to oldpath / length incl. trailing zero */ - if (remote_fileio_extract_ptr_w_len (&buf, &old_ptr, &old_len)) - { - remote_fileio_ioerror (); - return; - } - - /* 2. Parameter: Ptr to newpath / length incl. trailing zero */ - if (remote_fileio_extract_ptr_w_len (&buf, &new_ptr, &new_len)) - { - remote_fileio_ioerror (); - return; - } - - /* Request oldpath using 'm' packet */ - oldpath = alloca (old_len); - retlength = remote_read_bytes (old_ptr, (gdb_byte *) oldpath, old_len); - if (retlength != old_len) - { - remote_fileio_ioerror (); - return; - } - - /* Request newpath using 'm' packet */ - newpath = alloca (new_len); - retlength = remote_read_bytes (new_ptr, (gdb_byte *) newpath, new_len); - if (retlength != new_len) - { - remote_fileio_ioerror (); - return; - } - - /* Only operate on regular files and directories */ - of = stat (oldpath, &ost); - nf = stat (newpath, &nst); - if ((!of && !S_ISREG (ost.st_mode) && !S_ISDIR (ost.st_mode)) - || (!nf && !S_ISREG (nst.st_mode) && !S_ISDIR (nst.st_mode))) - { - remote_fileio_reply (-1, FILEIO_EACCES); - return; - } - - remote_fio_no_longjmp = 1; - ret = rename (oldpath, newpath); - - if (ret == -1) - { - /* Special case: newpath is a non-empty directory. Some systems - return ENOTEMPTY, some return EEXIST. We coerce that to be - always EEXIST. */ - if (errno == ENOTEMPTY) - errno = EEXIST; -#ifdef __CYGWIN__ - /* Workaround some Cygwin problems with correct errnos. */ - if (errno == EACCES) - { - if (!of && !nf && S_ISDIR (nst.st_mode)) - { - if (S_ISREG (ost.st_mode)) - errno = EISDIR; - else - { - char oldfullpath[PATH_MAX + 1]; - char newfullpath[PATH_MAX + 1]; - int len; - - cygwin_conv_to_full_posix_path (oldpath, oldfullpath); - cygwin_conv_to_full_posix_path (newpath, newfullpath); - len = strlen (oldfullpath); - if (newfullpath[len] == '/' - && !strncmp (oldfullpath, newfullpath, len)) - errno = EINVAL; - else - errno = EEXIST; - } - } - } -#endif - - remote_fileio_return_errno (-1); - } - else - remote_fileio_return_success (ret); -} - -static void -remote_fileio_func_unlink (char *buf) -{ - CORE_ADDR ptrval; - int length, retlength; - char *pathname; - int ret; - struct stat st; - - /* Parameter: Ptr to pathname / length incl. trailing zero */ - if (remote_fileio_extract_ptr_w_len (&buf, &ptrval, &length)) - { - remote_fileio_ioerror (); - return; - } - /* Request pathname using 'm' packet */ - pathname = alloca (length); - retlength = remote_read_bytes (ptrval, (gdb_byte *) pathname, length); - if (retlength != length) - { - remote_fileio_ioerror (); - return; - } - - /* Only operate on regular files (and directories, which allows to return - the correct return code) */ - if (!stat (pathname, &st) && !S_ISREG (st.st_mode) && !S_ISDIR (st.st_mode)) - { - remote_fileio_reply (-1, FILEIO_ENODEV); - return; - } - - remote_fio_no_longjmp = 1; - ret = unlink (pathname); - - if (ret == -1) - remote_fileio_return_errno (-1); - else - remote_fileio_return_success (ret); -} - -static void -remote_fileio_func_stat (char *buf) -{ - CORE_ADDR statptr, nameptr; - int ret, namelength, retlength; - char *pathname; - LONGEST lnum; - struct stat st; - struct fio_stat fst; - - /* 1. Parameter: Ptr to pathname / length incl. trailing zero */ - if (remote_fileio_extract_ptr_w_len (&buf, &nameptr, &namelength)) - { - remote_fileio_ioerror (); - return; - } - - /* 2. Parameter: Ptr to struct stat */ - if (remote_fileio_extract_long (&buf, &lnum)) - { - remote_fileio_ioerror (); - return; - } - statptr = (CORE_ADDR) lnum; - - /* Request pathname using 'm' packet */ - pathname = alloca (namelength); - retlength = remote_read_bytes (nameptr, (gdb_byte *) pathname, namelength); - if (retlength != namelength) - { - remote_fileio_ioerror (); - return; - } - - remote_fio_no_longjmp = 1; - ret = stat (pathname, &st); - - if (ret == -1) - { - remote_fileio_return_errno (-1); - return; - } - /* Only operate on regular files and directories */ - if (!ret && !S_ISREG (st.st_mode) && !S_ISDIR (st.st_mode)) - { - remote_fileio_reply (-1, FILEIO_EACCES); - return; - } - if (statptr) - { - remote_fileio_to_fio_stat (&st, &fst); - remote_fileio_to_fio_uint (0, fst.fst_dev); - - retlength = remote_fileio_write_bytes (statptr, - (gdb_byte *) &fst, sizeof fst); - if (retlength != sizeof fst) - { - remote_fileio_return_errno (-1); - return; - } - } - remote_fileio_return_success (ret); -} - -static void -remote_fileio_func_fstat (char *buf) -{ - CORE_ADDR ptrval; - int fd, ret, retlength; - long target_fd; - LONGEST lnum; - struct stat st; - struct fio_stat fst; - struct timeval tv; - - /* 1. Parameter: file descriptor */ - if (remote_fileio_extract_int (&buf, &target_fd)) - { - remote_fileio_ioerror (); - return; - } - fd = remote_fileio_map_fd ((int) target_fd); - if (fd == FIO_FD_INVALID) - { - remote_fileio_badfd (); - return; - } - /* 2. Parameter: Ptr to struct stat */ - if (remote_fileio_extract_long (&buf, &lnum)) - { - remote_fileio_ioerror (); - return; - } - ptrval = (CORE_ADDR) lnum; - - remote_fio_no_longjmp = 1; - if (fd == FIO_FD_CONSOLE_IN || fd == FIO_FD_CONSOLE_OUT) - { - remote_fileio_to_fio_uint (1, fst.fst_dev); - st.st_mode = S_IFCHR | (fd == FIO_FD_CONSOLE_IN ? S_IRUSR : S_IWUSR); - st.st_nlink = 1; -#ifdef HAVE_GETUID - st.st_uid = getuid (); -#else - st.st_uid = 0; -#endif -#ifdef HAVE_GETGID - st.st_gid = getgid (); -#else - st.st_gid = 0; -#endif - st.st_rdev = 0; - st.st_size = 0; -#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE - st.st_blksize = 512; -#endif -#if HAVE_STRUCT_STAT_ST_BLOCKS - st.st_blocks = 0; -#endif - if (!gettimeofday (&tv, NULL)) - st.st_atime = st.st_mtime = st.st_ctime = tv.tv_sec; - else - st.st_atime = st.st_mtime = st.st_ctime = (time_t) 0; - ret = 0; - } - else - ret = fstat (fd, &st); - - if (ret == -1) - { - remote_fileio_return_errno (-1); - return; - } - if (ptrval) - { - remote_fileio_to_fio_stat (&st, &fst); - - retlength = remote_fileio_write_bytes (ptrval, (gdb_byte *) &fst, sizeof fst); - if (retlength != sizeof fst) - { - remote_fileio_return_errno (-1); - return; - } - } - remote_fileio_return_success (ret); -} - -static void -remote_fileio_func_gettimeofday (char *buf) -{ - LONGEST lnum; - CORE_ADDR ptrval; - int ret, retlength; - struct timeval tv; - struct fio_timeval ftv; - - /* 1. Parameter: struct timeval pointer */ - if (remote_fileio_extract_long (&buf, &lnum)) - { - remote_fileio_ioerror (); - return; - } - ptrval = (CORE_ADDR) lnum; - /* 2. Parameter: some pointer value... */ - if (remote_fileio_extract_long (&buf, &lnum)) - { - remote_fileio_ioerror (); - return; - } - /* ...which has to be NULL */ - if (lnum) - { - remote_fileio_reply (-1, FILEIO_EINVAL); - return; - } - - remote_fio_no_longjmp = 1; - ret = gettimeofday (&tv, NULL); - - if (ret == -1) - { - remote_fileio_return_errno (-1); - return; - } - - if (ptrval) - { - remote_fileio_to_fio_timeval (&tv, &ftv); - - retlength = remote_fileio_write_bytes (ptrval, (gdb_byte *) &ftv, sizeof ftv); - if (retlength != sizeof ftv) - { - remote_fileio_return_errno (-1); - return; - } - } - remote_fileio_return_success (ret); -} - -static void -remote_fileio_func_isatty (char *buf) -{ - long target_fd; - int fd; - - /* Parameter: file descriptor */ - if (remote_fileio_extract_int (&buf, &target_fd)) - { - remote_fileio_ioerror (); - return; - } - remote_fio_no_longjmp = 1; - fd = remote_fileio_map_fd ((int) target_fd); - remote_fileio_return_success (fd == FIO_FD_CONSOLE_IN || - fd == FIO_FD_CONSOLE_OUT ? 1 : 0); -} - -static void -remote_fileio_func_system (char *buf) -{ - CORE_ADDR ptrval; - int ret, length, retlength; - char *cmdline = NULL; - - /* Parameter: Ptr to commandline / length incl. trailing zero */ - if (remote_fileio_extract_ptr_w_len (&buf, &ptrval, &length)) - { - remote_fileio_ioerror (); - return; - } - - if (length) - { - /* Request commandline using 'm' packet */ - cmdline = alloca (length); - retlength = remote_read_bytes (ptrval, (gdb_byte *) cmdline, length); - if (retlength != length) - { - remote_fileio_ioerror (); - return; - } - } - - /* Check if system(3) has been explicitely allowed using the - `set remote system-call-allowed 1' command. If length is 0, - indicating a NULL parameter to the system call, return zero to - indicate a shell is not available. Otherwise fail with EPERM. */ - if (!remote_fio_system_call_allowed) - { - if (!length) - remote_fileio_return_success (0); - else - remote_fileio_reply (-1, FILEIO_EPERM); - return; - } - - remote_fio_no_longjmp = 1; - ret = system (cmdline); - - if (!length) - remote_fileio_return_success (ret); - else if (ret == -1) - remote_fileio_return_errno (-1); - else - remote_fileio_return_success (WEXITSTATUS (ret)); -} - -static struct { - char *name; - void (*func)(char *); -} remote_fio_func_map[] = { - { "open", remote_fileio_func_open }, - { "close", remote_fileio_func_close }, - { "read", remote_fileio_func_read }, - { "write", remote_fileio_func_write }, - { "lseek", remote_fileio_func_lseek }, - { "rename", remote_fileio_func_rename }, - { "unlink", remote_fileio_func_unlink }, - { "stat", remote_fileio_func_stat }, - { "fstat", remote_fileio_func_fstat }, - { "gettimeofday", remote_fileio_func_gettimeofday }, - { "isatty", remote_fileio_func_isatty }, - { "system", remote_fileio_func_system }, - { NULL, NULL } -}; - -static int -do_remote_fileio_request (struct ui_out *uiout, void *buf_arg) -{ - char *buf = buf_arg; - char *c; - int idx; - remote_fileio_sig_set (remote_fileio_ctrl_c_signal_handler); - - c = strchr (++buf, ','); - if (c) - *c++ = '\0'; - else - c = strchr (buf, '\0'); - for (idx = 0; remote_fio_func_map[idx].name; ++idx) - if (!strcmp (remote_fio_func_map[idx].name, buf)) - break; - if (!remote_fio_func_map[idx].name) /* ERROR: No such function. */ - return RETURN_ERROR; - remote_fio_func_map[idx].func (c); - return 0; } /* Close any open descriptors, and reinitialize the file mapping. */ @@ -1378,85 +147,28 @@ do_remote_fileio_request (struct ui_out *uiout, void *buf_arg) void remote_fileio_reset (void) { - int ix; - - for (ix = 0; ix != remote_fio_data.fd_map_size; ix++) - { - int fd = remote_fio_data.fd_map[ix]; - - if (fd >= 0) - close (fd); - } - if (remote_fio_data.fd_map) - { - free (remote_fio_data.fd_map); - remote_fio_data.fd_map = NULL; - remote_fio_data.fd_map_size = 0; - } + target_fileio_reset(); } void remote_fileio_request (char *buf) { - int ex; + static struct file_io_operations operations = + { + remote_read_bytes, + remote_fileio_write_bytes, + remote_fileio_reply, + set_ctrl_c_signal_handler + }; remote_fileio_sig_init (); - - remote_fio_ctrl_c_flag = 0; - remote_fio_no_longjmp = 0; - - ex = catch_exceptions (uiout, do_remote_fileio_request, (void *)buf, - RETURN_MASK_ALL); - switch (ex) - { - case RETURN_ERROR: - remote_fileio_reply (-1, FILEIO_ENOSYS); - break; - case RETURN_QUIT: - remote_fileio_reply (-1, FILEIO_EINTR); - break; - default: - break; - } - + target_fileio_request(buf, &operations); remote_fileio_sig_exit (); } -static void -set_system_call_allowed (char *args, int from_tty) -{ - if (args) - { - char *arg_end; - int val = strtoul (args, &arg_end, 10); - if (*args && *arg_end == '\0') - { - remote_fio_system_call_allowed = !!val; - return; - } - } - error (_("Illegal argument for \"set remote system-call-allowed\" command")); -} - -static void -show_system_call_allowed (char *args, int from_tty) -{ - if (args) - error (_("Garbage after \"show remote system-call-allowed\" command: `%s'"), args); - printf_unfiltered ("Calling host system(3) call from target is %sallowed\n", - remote_fio_system_call_allowed ? "" : "not "); -} - void initialize_remote_fileio (struct cmd_list_element *remote_set_cmdlist, struct cmd_list_element *remote_show_cmdlist) { - add_cmd ("system-call-allowed", no_class, - set_system_call_allowed, - _("Set if the host system(3) call is allowed for the target."), - &remote_set_cmdlist); - add_cmd ("system-call-allowed", no_class, - show_system_call_allowed, - _("Show if the host system(3) call is allowed for the target."), - &remote_show_cmdlist); + initialize_target_fileio(remote_set_cmdlist, remote_show_cmdlist); } diff --git a/gdb/remote-sim.c b/gdb/remote-sim.c index 918eab856f3..c3a84437018 100644 --- a/gdb/remote-sim.c +++ b/gdb/remote-sim.c @@ -469,13 +469,17 @@ gdbsim_create_inferior (char *exec_file, char *args, char **env, int from_tty) } else argv = NULL; - sim_create_inferior (gdbsim_desc, exec_bfd, argv, env); - inferior_ptid = pid_to_ptid (42); - target_mark_running (&gdbsim_ops); - insert_breakpoints (); /* Needed to get correct instruction in cache */ + // ARC 12/01/09 check return status + // GDB Bug #9734 + if (sim_create_inferior (gdbsim_desc, exec_bfd, argv, env) == SIM_RC_OK) + { + inferior_ptid = pid_to_ptid (42); + target_mark_running (&gdbsim_ops); + insert_breakpoints (); /* Needed to get correct instruction in cache */ - clear_proceed_status (); + clear_proceed_status (); + } } /* The open routine takes the rest of the parameters from the command, diff --git a/gdb/stack.c b/gdb/stack.c index 374848b5c3e..f151dc15036 100644 --- a/gdb/stack.c +++ b/gdb/stack.c @@ -831,7 +831,8 @@ parse_frame_specification_1 (const char *frame_exp, const char *message, { int i; for (i = 0; i < numargs; i++) - addrs[i] = value_as_address (args[0]); + addrs[i] = value_as_address (args[i]); // ARC BUG FIX 29/9/08 + // see http://sourceware.org/ml/gdb-patches/2009-01/msg00476.html } /* Assume that the single arg[0] is an address, use that to identify @@ -892,8 +893,29 @@ frame_info (char *addr_exp, int from_tty) const char *pc_regname; int selected_frame_p; struct gdbarch *gdbarch; + CORE_ADDR pc; fi = parse_frame_specification_1 (addr_exp, "No stack.", &selected_frame_p); + + /* richards ARC 22/9/2008 + * Try to detect that an invalid frame has been selected (e.g. a frame + * number has been given, but there is no such frame on the stack); + * N.B. this works for the ARC gdb port, but 0 might be a valid code + * address on other processors, so this needs more investigation! + * + * We should not try to submit this fix to the FSF until we know that it + * is generally valid. + * + * gdb bug: 9458 + * ARC bug: 95315 + */ + pc = get_frame_pc (fi); + if (pc == 0) + { + warning("invalid frame"); + return; + } + gdbarch = get_frame_arch (fi); /* Name of the value returned by get_frame_pc(). Per comments, "pc" @@ -914,7 +936,7 @@ frame_info (char *addr_exp, int from_tty) func = get_frame_function (fi); /* FIXME: cagney/2002-11-28: Why bother? Won't sal.symtab contain the same value? */ - s = find_pc_symtab (get_frame_pc (fi)); + s = find_pc_symtab (pc); if (func) { /* It seems appropriate to use SYMBOL_PRINT_NAME() here, to diff --git a/gdb/symtab.h b/gdb/symtab.h index c19e74124c2..0d751a7008e 100644 --- a/gdb/symtab.h +++ b/gdb/symtab.h @@ -574,6 +574,26 @@ struct symbol_ops struct value *(*read_variable) (struct symbol * symbol, struct frame_info * frame); +// begin ARC + /* Return the address of the variable SYMBOL, relative to the stack + frame FRAME. If the variable has been optimized out, return + zero. + + Iff `read_needs_frame (SYMBOL)' is zero, then FRAME may be zero. */ + + CORE_ADDR (*get_variable_address) (struct symbol * symbol, + struct frame_info * frame); + + /* Return the size of the variable SYMBOL, relative to the stack + frame FRAME. If the variable has been optimized out, return + zero. + + Iff `read_needs_frame (SYMBOL)' is zero, then FRAME may be zero. */ + + unsigned int (*get_variable_size) (struct symbol * symbol, + struct frame_info * frame); +// end ARC + /* Return non-zero if we need a frame to find the value of the SYMBOL. */ int (*read_needs_frame) (struct symbol * symbol); diff --git a/gdb/target-fileio.c b/gdb/target-fileio.c new file mode 100644 index 00000000000..fdc64c9baa7 --- /dev/null +++ b/gdb/target-fileio.c @@ -0,0 +1,1344 @@ +/* Target File-I/O communications + + Copyright (C) 2003, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. + + 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 3 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, see <http://www.gnu.org/licenses/>. */ + +#include "defs.h" +#include "gdb_string.h" +#include "gdbcmd.h" +#include "gdb/fileio.h" +#include "gdb_wait.h" +#include "gdb_stat.h" +#include "exceptions.h" +#include "target-fileio.h" + +#include <fcntl.h> +#include <sys/time.h> +#ifdef __CYGWIN__ +#include <sys/cygwin.h> /* For cygwin_conv_to_full_posix_path. */ +#endif + +static struct { + int *fd_map; + int fd_map_size; +} target_fio_data; + +#define FIO_FD_INVALID -1 +#define FIO_FD_CONSOLE_IN -2 +#define FIO_FD_CONSOLE_OUT -3 + +static int target_fio_system_call_allowed = 0; + +static int +target_fileio_init_fd_map (void) +{ + int i; + + if (!target_fio_data.fd_map) + { + target_fio_data.fd_map = (int *) xmalloc (10 * sizeof (int)); + target_fio_data.fd_map_size = 10; + target_fio_data.fd_map[0] = FIO_FD_CONSOLE_IN; + target_fio_data.fd_map[1] = FIO_FD_CONSOLE_OUT; + target_fio_data.fd_map[2] = FIO_FD_CONSOLE_OUT; + for (i = 3; i < 10; ++i) + target_fio_data.fd_map[i] = FIO_FD_INVALID; + } + return 3; +} + +static int +target_fileio_resize_fd_map (void) +{ + int i = target_fio_data.fd_map_size; + + if (!target_fio_data.fd_map) + return target_fileio_init_fd_map (); + target_fio_data.fd_map_size += 10; + target_fio_data.fd_map = + (int *) xrealloc (target_fio_data.fd_map, + target_fio_data.fd_map_size * sizeof (int)); + for (; i < target_fio_data.fd_map_size; i++) + target_fio_data.fd_map[i] = FIO_FD_INVALID; + return target_fio_data.fd_map_size - 10; +} + +static int +target_fileio_next_free_fd (void) +{ + int i; + + for (i = 0; i < target_fio_data.fd_map_size; ++i) + if (target_fio_data.fd_map[i] == FIO_FD_INVALID) + return i; + return target_fileio_resize_fd_map (); +} + +static int +target_fileio_fd_to_targetfd (int fd) +{ + int target_fd =target_fileio_next_free_fd (); + target_fio_data.fd_map[target_fd] = fd; + return target_fd; +} + +static int +target_fileio_map_fd (int target_fd) +{ + target_fileio_init_fd_map (); + if (target_fd < 0 || target_fd >= target_fio_data.fd_map_size) + return FIO_FD_INVALID; + return target_fio_data.fd_map[target_fd]; +} + +static void +target_fileio_close_target_fd (int target_fd) +{ + target_fileio_init_fd_map (); + if (target_fd >= 0 && target_fd < target_fio_data.fd_map_size) + target_fio_data.fd_map[target_fd] = FIO_FD_INVALID; +} + +static int +target_fileio_oflags_to_host (long flags) +{ + int hflags = 0; + + if (flags & FILEIO_O_CREAT) + hflags |= O_CREAT; + if (flags & FILEIO_O_EXCL) + hflags |= O_EXCL; + if (flags & FILEIO_O_TRUNC) + hflags |= O_TRUNC; + if (flags & FILEIO_O_APPEND) + hflags |= O_APPEND; + if (flags & FILEIO_O_RDONLY) + hflags |= O_RDONLY; + if (flags & FILEIO_O_WRONLY) + hflags |= O_WRONLY; + if (flags & FILEIO_O_RDWR) + hflags |= O_RDWR; +/* On systems supporting binary and text mode, always open files in + binary mode. */ +#ifdef O_BINARY + hflags |= O_BINARY; +#endif + return hflags; +} + +static mode_t +target_fileio_mode_to_host (long mode, int open_call) +{ + mode_t hmode = 0; + + if (!open_call) + { + if (mode & FILEIO_S_IFREG) + hmode |= S_IFREG; + if (mode & FILEIO_S_IFDIR) + hmode |= S_IFDIR; + if (mode & FILEIO_S_IFCHR) + hmode |= S_IFCHR; + } + if (mode & FILEIO_S_IRUSR) + hmode |= S_IRUSR; + if (mode & FILEIO_S_IWUSR) + hmode |= S_IWUSR; + if (mode & FILEIO_S_IXUSR) + hmode |= S_IXUSR; +#ifdef S_IRGRP + if (mode & FILEIO_S_IRGRP) + hmode |= S_IRGRP; +#endif +#ifdef S_IWGRP + if (mode & FILEIO_S_IWGRP) + hmode |= S_IWGRP; +#endif +#ifdef S_IXGRP + if (mode & FILEIO_S_IXGRP) + hmode |= S_IXGRP; +#endif + if (mode & FILEIO_S_IROTH) + hmode |= S_IROTH; +#ifdef S_IWOTH + if (mode & FILEIO_S_IWOTH) + hmode |= S_IWOTH; +#endif +#ifdef S_IXOTH + if (mode & FILEIO_S_IXOTH) + hmode |= S_IXOTH; +#endif + return hmode; +} + +static LONGEST +target_fileio_mode_to_target (mode_t mode) +{ + mode_t tmode = 0; + + if (S_ISREG(mode)) + tmode |= FILEIO_S_IFREG; + if (S_ISDIR(mode)) + tmode |= FILEIO_S_IFDIR; + if (S_ISCHR(mode)) + tmode |= FILEIO_S_IFCHR; + if (mode & S_IRUSR) + tmode |= FILEIO_S_IRUSR; + if (mode & S_IWUSR) + tmode |= FILEIO_S_IWUSR; + if (mode & S_IXUSR) + tmode |= FILEIO_S_IXUSR; +#ifdef S_IRGRP + if (mode & S_IRGRP) + tmode |= FILEIO_S_IRGRP; +#endif +#ifdef S_IWRGRP + if (mode & S_IWGRP) + tmode |= FILEIO_S_IWGRP; +#endif +#ifdef S_IXGRP + if (mode & S_IXGRP) + tmode |= FILEIO_S_IXGRP; +#endif + if (mode & S_IROTH) + tmode |= FILEIO_S_IROTH; +#ifdef S_IWOTH + if (mode & S_IWOTH) + tmode |= FILEIO_S_IWOTH; +#endif +#ifdef S_IXOTH + if (mode & S_IXOTH) + tmode |= FILEIO_S_IXOTH; +#endif + return tmode; +} + +static int +target_fileio_errno_to_target (int error) +{ + switch (error) + { + case EPERM: + return FILEIO_EPERM; + case ENOENT: + return FILEIO_ENOENT; + case EINTR: + return FILEIO_EINTR; + case EIO: + return FILEIO_EIO; + case EBADF: + return FILEIO_EBADF; + case EACCES: + return FILEIO_EACCES; + case EFAULT: + return FILEIO_EFAULT; + case EBUSY: + return FILEIO_EBUSY; + case EEXIST: + return FILEIO_EEXIST; + case ENODEV: + return FILEIO_ENODEV; + case ENOTDIR: + return FILEIO_ENOTDIR; + case EISDIR: + return FILEIO_EISDIR; + case EINVAL: + return FILEIO_EINVAL; + case ENFILE: + return FILEIO_ENFILE; + case EMFILE: + return FILEIO_EMFILE; + case EFBIG: + return FILEIO_EFBIG; + case ENOSPC: + return FILEIO_ENOSPC; + case ESPIPE: + return FILEIO_ESPIPE; + case EROFS: + return FILEIO_EROFS; + case ENOSYS: + return FILEIO_ENOSYS; + case ENAMETOOLONG: + return FILEIO_ENAMETOOLONG; + } + return FILEIO_EUNKNOWN; +} + +static int +target_fileio_seek_flag_to_host (long num, int *flag) +{ + if (!flag) + return 0; + switch (num) + { + case FILEIO_SEEK_SET: + *flag = SEEK_SET; + break; + case FILEIO_SEEK_CUR: + *flag = SEEK_CUR; + break; + case FILEIO_SEEK_END: + *flag = SEEK_END; + break; + default: + return -1; + } + return 0; +} + +static int +target_fileio_extract_long (char **buf, LONGEST *retlong) +{ + char *c; + int sign = 1; + + if (!buf || !*buf || !**buf || !retlong) + return -1; + c = strchr (*buf, ','); + if (c) + *c++ = '\0'; + else + c = strchr (*buf, '\0'); + while (strchr ("+-", **buf)) + { + if (**buf == '-') + sign = -sign; + ++*buf; + } + for (*retlong = 0; **buf; ++*buf) + { + *retlong <<= 4; + if (**buf >= '0' && **buf <= '9') + *retlong += **buf - '0'; + else if (**buf >= 'a' && **buf <= 'f') + *retlong += **buf - 'a' + 10; + else if (**buf >= 'A' && **buf <= 'F') + *retlong += **buf - 'A' + 10; + else + return -1; + } + *retlong *= sign; + *buf = c; + return 0; +} + +static int +target_fileio_extract_int (char **buf, long *retint) +{ + int ret; + LONGEST retlong; + + if (!retint) + return -1; + ret =target_fileio_extract_long (buf, &retlong); + if (!ret) + *retint = (long) retlong; + return ret; +} + +static int +target_fileio_extract_ptr_w_len (char **buf, CORE_ADDR *ptrval, int *length) +{ + char *c; + LONGEST retlong; + + if (!buf || !*buf || !**buf || !ptrval || !length) + return -1; + c = strchr (*buf, '/'); + if (!c) + return -1; + *c++ = '\0'; + if (target_fileio_extract_long (buf, &retlong)) + return -1; + *ptrval = (CORE_ADDR) retlong; + *buf = c; + if (target_fileio_extract_long (buf, &retlong)) + return -1; + *length = (int) retlong; + return 0; +} + +/* Convert to big endian */ +static void +target_fileio_to_be (LONGEST num, char *buf, int bytes) +{ + int i; + + for (i = 0; i < bytes; ++i) + buf[i] = (num >> (8 * (bytes - i - 1))) & 0xff; +} + +static void +target_fileio_to_fio_uint (long num, fio_uint_t fnum) +{ + target_fileio_to_be ((LONGEST) num, (char *) fnum, 4); +} + +static void +target_fileio_to_fio_mode (mode_t num, fio_mode_t fnum) +{ + target_fileio_to_be (target_fileio_mode_to_target(num), (char *) fnum, 4); +} + +static void +target_fileio_to_fio_time (time_t num, fio_time_t fnum) +{ + target_fileio_to_be ((LONGEST) num, (char *) fnum, 4); +} + +static void +target_fileio_to_fio_long (LONGEST num, fio_long_t fnum) +{ + target_fileio_to_be (num, (char *) fnum, 8); +} + +static void +target_fileio_to_fio_ulong (LONGEST num, fio_ulong_t fnum) +{ + target_fileio_to_be (num, (char *) fnum, 8); +} + +static void +target_fileio_to_fio_stat (struct stat *st, struct fio_stat *fst) +{ + LONGEST blksize; + + /* `st_dev' is set in the calling function */ + target_fileio_to_fio_uint ((long) st->st_ino, fst->fst_ino); + target_fileio_to_fio_mode (st->st_mode, fst->fst_mode); + target_fileio_to_fio_uint ((long) st->st_nlink, fst->fst_nlink); + target_fileio_to_fio_uint ((long) st->st_uid, fst->fst_uid); + target_fileio_to_fio_uint ((long) st->st_gid, fst->fst_gid); + target_fileio_to_fio_uint ((long) st->st_rdev, fst->fst_rdev); + target_fileio_to_fio_ulong ((LONGEST) st->st_size, fst->fst_size); +#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE + blksize = st->st_blksize; +#else + blksize = 512; +#endif + target_fileio_to_fio_ulong (blksize, fst->fst_blksize); +#if HAVE_STRUCT_STAT_ST_BLOCKS + target_fileio_to_fio_ulong ((LONGEST) st->st_blocks, fst->fst_blocks); +#else + /* FIXME: This is correct for DJGPP, but other systems that don't + have st_blocks, if any, might prefer 512 instead of st_blksize. + (eliz, 30-12-2003) */ + target_fileio_to_fio_ulong (((LONGEST) st->st_size + blksize - 1) + / blksize, + fst->fst_blocks); +#endif + target_fileio_to_fio_time (st->st_atime, fst->fst_atime); + target_fileio_to_fio_time (st->st_mtime, fst->fst_mtime); + target_fileio_to_fio_time (st->st_ctime, fst->fst_ctime); +} + +static void +target_fileio_to_fio_timeval (struct timeval *tv, struct fio_timeval *ftv) +{ + target_fileio_to_fio_time (tv->tv_sec, ftv->ftv_sec); + target_fileio_to_fio_long (tv->tv_usec, ftv->ftv_usec); +} + +int target_fio_no_longjmp = 0; + + +static void +target_fileio_func_open (char *buf, struct file_io_operations *operations) +{ + CORE_ADDR ptrval; + int length, retlength; + long num; + int flags, fd; + mode_t mode; + char *pathname; + struct stat st; + + /* 1. Parameter: Ptr to pathname / length incl. trailing zero */ + if (target_fileio_extract_ptr_w_len (&buf, &ptrval, &length)) + { + operations->ioerror (); + return; + } + /* 2. Parameter: open flags */ + if (target_fileio_extract_int (&buf, &num)) + { + operations->ioerror (); + return; + } + flags =target_fileio_oflags_to_host (num); + /* 3. Parameter: open mode */ + if (target_fileio_extract_int (&buf, &num)) + { + operations->ioerror (); + return; + } + mode =target_fileio_mode_to_host (num, 1); + + /* Read pathname */ + pathname = alloca (length); + retlength = operations->read_bytes (ptrval, (gdb_byte *) pathname, length); + if (retlength != length) + { + operations->ioerror (); + return; + } + + /* Check if pathname exists and is not a regular file or directory. If so, + return an appropriate error code. Same for trying to open directories + for writing. */ + if (!stat (pathname, &st)) + { + if (!S_ISREG (st.st_mode) && !S_ISDIR (st.st_mode)) + { + operations->reply (-1, FILEIO_ENODEV); + return; + } + if (S_ISDIR (st.st_mode) + && ((flags & O_WRONLY) == O_WRONLY || (flags & O_RDWR) == O_RDWR)) + { + operations->reply (-1, FILEIO_EISDIR); + return; + } + } + + target_fio_no_longjmp = 1; + fd = open (pathname, flags, mode); + if (fd < 0) + { + operations->return_errno (-1); + return; + } + + fd =target_fileio_fd_to_targetfd (fd); + operations->return_success (fd); +} + +static void +target_fileio_func_close (char *buf, struct file_io_operations *operations) +{ + long num; + int fd; + + /* Parameter: file descriptor */ + if (target_fileio_extract_int (&buf, &num)) + { + operations->ioerror (); + return; + } + fd =target_fileio_map_fd ((int) num); + if (fd == FIO_FD_INVALID) + { + operations->badfd (); + return; + } + + target_fio_no_longjmp = 1; + if (fd != FIO_FD_CONSOLE_IN && fd != FIO_FD_CONSOLE_OUT && close (fd)) + operations->return_errno (-1); + target_fileio_close_target_fd ((int) num); + operations->return_success (0); +} + +static void +target_fileio_func_read (char *buf, struct file_io_operations *operations) +{ + long target_fd, num; + LONGEST lnum; + CORE_ADDR ptrval; + int fd, ret, retlength; + gdb_byte *buffer; + size_t length; + off_t old_offset, new_offset; + + /* 1. Parameter: file descriptor */ + if (target_fileio_extract_int (&buf, &target_fd)) + { + operations->ioerror (); + return; + } + fd =target_fileio_map_fd ((int) target_fd); + if (fd == FIO_FD_INVALID) + { + operations->badfd (); + return; + } + /* 2. Parameter: buffer pointer */ + if (target_fileio_extract_long (&buf, &lnum)) + { + operations->ioerror (); + return; + } + ptrval = (CORE_ADDR) lnum; + /* 3. Parameter: buffer length */ + if (target_fileio_extract_int (&buf, &num)) + { + operations->ioerror (); + return; + } + length = (size_t) num; + + switch (fd) + { + case FIO_FD_CONSOLE_OUT: + operations->badfd (); + return; + case FIO_FD_CONSOLE_IN: + { + static char *remaining_buf = NULL; + static int remaining_length = 0; + + buffer = (gdb_byte *) xmalloc (32768); + if (remaining_buf) + { + target_fio_no_longjmp = 1; + if (remaining_length > length) + { + memcpy (buffer, remaining_buf, length); + memmove (remaining_buf, remaining_buf + length, + remaining_length - length); + remaining_length -= length; + ret = length; + } + else + { + memcpy (buffer, remaining_buf, remaining_length); + xfree (remaining_buf); + remaining_buf = NULL; + ret = remaining_length; + } + } + else + { + ret = ui_file_read (gdb_stdtargin, (char *) buffer, 32767); + target_fio_no_longjmp = 1; + if (ret > 0 && (size_t)ret > length) + { + remaining_buf = (char *) xmalloc (ret - length); + remaining_length = ret - length; + memcpy (remaining_buf, buffer + length, remaining_length); + ret = length; + } + } + } + break; + default: + buffer = (gdb_byte *) xmalloc (length); + /* POSIX defines EINTR behaviour of read in a weird way. It's allowed + for read() to return -1 even if "some" bytes have been read. It + has been corrected in SUSv2 but that doesn't help us much... + Therefore a complete solution must check how many bytes have been + read on EINTR to return a more reliable value to the target */ + old_offset = lseek (fd, 0, SEEK_CUR); + target_fio_no_longjmp = 1; + ret = read (fd, buffer, length); + if (ret < 0 && errno == EINTR) + { + new_offset = lseek (fd, 0, SEEK_CUR); + /* If some data has been read, return the number of bytes read. + The Ctrl-C flag is set in target_fileio_reply() anyway */ + if (old_offset != new_offset) + ret = new_offset - old_offset; + } + break; + } + + if (ret > 0) + { + retlength = operations->write_bytes (ptrval, buffer, ret); + if (retlength != ret) + ret = -1; /* errno has been set to EIO in remote_write_bytes() */ + } + + if (ret < 0) + operations->return_errno (-1); + else + operations->return_success (ret); + + xfree (buffer); +} + +static void +target_fileio_func_write (char *buf, struct file_io_operations *operations) +{ + long target_fd, num; + LONGEST lnum; + CORE_ADDR ptrval; + int fd, ret, retlength; + gdb_byte *buffer; + size_t length; + + /* 1. Parameter: file descriptor */ + if (target_fileio_extract_int (&buf, &target_fd)) + { + operations->ioerror (); + return; + } + fd =target_fileio_map_fd ((int) target_fd); + if (fd == FIO_FD_INVALID) + { + operations->badfd (); + return; + } + /* 2. Parameter: buffer pointer */ + if (target_fileio_extract_long (&buf, &lnum)) + { + operations->ioerror (); + return; + } + ptrval = (CORE_ADDR) lnum; + /* 3. Parameter: buffer length */ + if (target_fileio_extract_int (&buf, &num)) + { + operations->ioerror (); + return; + } + length = (size_t) num; + + buffer = (gdb_byte *) xmalloc (length); + retlength = operations->read_bytes (ptrval, buffer, length); + if (retlength != length) + { + xfree (buffer); + operations->ioerror (); + return; + } + + target_fio_no_longjmp = 1; + switch (fd) + { + case FIO_FD_CONSOLE_IN: + operations->badfd (); + xfree (buffer); + return; + case FIO_FD_CONSOLE_OUT: + ui_file_write (target_fd == 1 ? gdb_stdtarg : gdb_stdtargerr, + (char *) buffer, length); + gdb_flush (target_fd == 1 ? gdb_stdtarg : gdb_stdtargerr); + ret = length; + break; + default: + ret = write (fd, buffer, length); + if (ret < 0 && errno == EACCES) + errno = EBADF; /* Cygwin returns EACCESS when writing to a R/O file.*/ + break; + } + + if (ret < 0) + operations->return_errno (-1); + else + operations->return_success (ret); + + xfree (buffer); +} + +static void +target_fileio_func_lseek (char *buf, struct file_io_operations *operations) +{ + long num; + LONGEST lnum; + int fd, flag; + off_t offset, ret; + + /* 1. Parameter: file descriptor */ + if (target_fileio_extract_int (&buf, &num)) + { + operations->ioerror (); + return; + } + fd =target_fileio_map_fd ((int) num); + if (fd == FIO_FD_INVALID) + { + operations->badfd (); + return; + } + else if (fd == FIO_FD_CONSOLE_IN || fd == FIO_FD_CONSOLE_OUT) + { + operations->reply (-1, FILEIO_ESPIPE); + return; + } + + /* 2. Parameter: offset */ + if (target_fileio_extract_long (&buf, &lnum)) + { + operations->ioerror (); + return; + } + offset = (off_t) lnum; + /* 3. Parameter: flag */ + if (target_fileio_extract_int (&buf, &num)) + { + operations->ioerror (); + return; + } + if (target_fileio_seek_flag_to_host (num, &flag)) + { + operations->reply (-1, FILEIO_EINVAL); + return; + } + + target_fio_no_longjmp = 1; + ret = lseek (fd, offset, flag); + + if (ret == (off_t) -1) + operations->return_errno (-1); + else + operations->return_success (ret); +} + +static void +target_fileio_func_rename (char *buf, struct file_io_operations *operations) +{ + CORE_ADDR old_ptr, new_ptr; + int old_len, new_len, retlength; + char *oldpath, *newpath; + int ret, of, nf; + struct stat ost, nst; + + /* 1. Parameter: Ptr to oldpath / length incl. trailing zero */ + if (target_fileio_extract_ptr_w_len (&buf, &old_ptr, &old_len)) + { + operations->ioerror (); + return; + } + + /* 2. Parameter: Ptr to newpath / length incl. trailing zero */ + if (target_fileio_extract_ptr_w_len (&buf, &new_ptr, &new_len)) + { + operations->ioerror (); + return; + } + + /* Request oldpath */ + oldpath = alloca (old_len); + retlength = operations->read_bytes (old_ptr, (gdb_byte *) oldpath, old_len); + if (retlength != old_len) + { + operations->ioerror (); + return; + } + + /* Request newpath */ + newpath = alloca (new_len); + retlength = operations->read_bytes (new_ptr, (gdb_byte *) newpath, new_len); + if (retlength != new_len) + { + operations->ioerror (); + return; + } + + /* Only operate on regular files and directories */ + of = stat (oldpath, &ost); + nf = stat (newpath, &nst); + if ((!of && !S_ISREG (ost.st_mode) && !S_ISDIR (ost.st_mode)) + || (!nf && !S_ISREG (nst.st_mode) && !S_ISDIR (nst.st_mode))) + { + operations->reply (-1, FILEIO_EACCES); + return; + } + + target_fio_no_longjmp = 1; + ret = rename (oldpath, newpath); + + if (ret == -1) + { + /* Special case: newpath is a non-empty directory. Some systems + return ENOTEMPTY, some return EEXIST. We coerce that to be + always EEXIST. */ + if (errno == ENOTEMPTY) + errno = EEXIST; +#ifdef __CYGWIN__ + /* Workaround some Cygwin problems with correct errnos. */ + if (errno == EACCES) + { + if (!of && !nf && S_ISDIR (nst.st_mode)) + { + if (S_ISREG (ost.st_mode)) + errno = EISDIR; + else + { + char oldfullpath[PATH_MAX + 1]; + char newfullpath[PATH_MAX + 1]; + int len; + + cygwin_conv_to_full_posix_path (oldpath, oldfullpath); + cygwin_conv_to_full_posix_path (newpath, newfullpath); + len = strlen (oldfullpath); + if (newfullpath[len] == '/' + && !strncmp (oldfullpath, newfullpath, len)) + errno = EINVAL; + else + errno = EEXIST; + } + } + } +#endif + + operations->return_errno (-1); + } + else + operations->return_success (ret); +} + +static void +target_fileio_func_unlink (char *buf, struct file_io_operations *operations) +{ + CORE_ADDR ptrval; + int length, retlength; + char *pathname; + int ret; + struct stat st; + + /* Parameter: Ptr to pathname / length incl. trailing zero */ + if (target_fileio_extract_ptr_w_len (&buf, &ptrval, &length)) + { + operations->ioerror (); + return; + } + /* Request pathname */ + pathname = alloca (length); + retlength = operations->read_bytes (ptrval, (gdb_byte *) pathname, length); + if (retlength != length) + { + operations->ioerror (); + return; + } + + /* Only operate on regular files (and directories, which allows to return + the correct return code) */ + if (!stat (pathname, &st) && !S_ISREG (st.st_mode) && !S_ISDIR (st.st_mode)) + { + operations->reply (-1, FILEIO_ENODEV); + return; + } + + target_fio_no_longjmp = 1; + ret = unlink (pathname); + + if (ret == -1) + operations->return_errno (-1); + else + operations->return_success (ret); +} + +static void +target_fileio_func_stat (char *buf, struct file_io_operations *operations) +{ + CORE_ADDR statptr, nameptr; + int ret, namelength, retlength; + char *pathname; + LONGEST lnum; + struct stat st; + struct fio_stat fst; + + /* 1. Parameter: Ptr to pathname / length incl. trailing zero */ + if (target_fileio_extract_ptr_w_len (&buf, &nameptr, &namelength)) + { + operations->ioerror (); + return; + } + + /* 2. Parameter: Ptr to struct stat */ + if (target_fileio_extract_long (&buf, &lnum)) + { + operations->ioerror (); + return; + } + statptr = (CORE_ADDR) lnum; + + /* Request pathname */ + pathname = alloca (namelength); + retlength = operations->read_bytes (nameptr, (gdb_byte *) pathname, namelength); + if (retlength != namelength) + { + operations->ioerror (); + return; + } + + target_fio_no_longjmp = 1; + ret = stat (pathname, &st); + + if (ret == -1) + { + operations->return_errno (-1); + return; + } + /* Only operate on regular files and directories */ + if (!ret && !S_ISREG (st.st_mode) && !S_ISDIR (st.st_mode)) + { + operations->reply (-1, FILEIO_EACCES); + return; + } + if (statptr) + { + target_fileio_to_fio_stat (&st, &fst); + target_fileio_to_fio_uint (0, fst.fst_dev); + + retlength = operations->write_bytes (statptr, + (gdb_byte *) &fst, sizeof fst); + if (retlength != sizeof fst) + { + operations->return_errno (-1); + return; + } + } + operations->return_success (ret); +} + +static void +target_fileio_func_fstat (char *buf, struct file_io_operations *operations) +{ + CORE_ADDR ptrval; + int fd, ret, retlength; + long target_fd; + LONGEST lnum; + struct stat st; + struct fio_stat fst; + struct timeval tv; + + /* 1. Parameter: file descriptor */ + if (target_fileio_extract_int (&buf, &target_fd)) + { + operations->ioerror (); + return; + } + fd =target_fileio_map_fd ((int) target_fd); + if (fd == FIO_FD_INVALID) + { + operations->badfd (); + return; + } + /* 2. Parameter: Ptr to struct stat */ + if (target_fileio_extract_long (&buf, &lnum)) + { + operations->ioerror (); + return; + } + ptrval = (CORE_ADDR) lnum; + + // ARC bug fix 25/11/2008 + memset(&st, 0, sizeof(st)); + memset(&fst, 0, sizeof(fst)); + + target_fio_no_longjmp = 1; + if (fd == FIO_FD_CONSOLE_IN || fd == FIO_FD_CONSOLE_OUT) + { + target_fileio_to_fio_uint (1, fst.fst_dev); + st.st_mode = S_IFCHR | (fd == FIO_FD_CONSOLE_IN ? S_IRUSR : S_IWUSR); + st.st_nlink = 1; +#ifdef HAVE_GETUID + st.st_uid = getuid (); +#else + st.st_uid = 0; +#endif +#ifdef HAVE_GETGID + st.st_gid = getgid (); +#else + st.st_gid = 0; +#endif + st.st_rdev = 0; + st.st_size = 0; +#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE + st.st_blksize = 512; +#endif +#if HAVE_STRUCT_STAT_ST_BLOCKS + st.st_blocks = 0; +#endif + + /* N.B. st.st_ino is not set! */ + + if (!gettimeofday (&tv, NULL)) + st.st_atime = st.st_mtime = st.st_ctime = tv.tv_sec; + else + st.st_atime = st.st_mtime = st.st_ctime = (time_t) 0; + ret = 0; + } + else { + ret = fstat (fd, &st); + target_fileio_to_fio_uint (st.st_dev, fst.fst_dev); // ARC bug fix 10/11/2008 gdb bug: 9655 + } + + if (ret == -1) + { + operations->return_errno (-1); + return; + } + if (ptrval) + { + target_fileio_to_fio_stat (&st, &fst); + + retlength = operations->write_bytes (ptrval, (gdb_byte *) &fst, sizeof fst); + if (retlength != sizeof fst) + { + operations->return_errno (-1); + return; + } + } + operations->return_success (ret); +} + +static void +target_fileio_func_gettimeofday (char *buf, struct file_io_operations *operations) +{ + LONGEST lnum; + CORE_ADDR ptrval; + int ret, retlength; + struct timeval tv; + struct fio_timeval ftv; + + /* 1. Parameter: struct timeval pointer */ + if (target_fileio_extract_long (&buf, &lnum)) + { + operations->ioerror (); + return; + } + ptrval = (CORE_ADDR) lnum; + /* 2. Parameter: some pointer value... */ + if (target_fileio_extract_long (&buf, &lnum)) + { + operations->ioerror (); + return; + } + /* ...which has to be NULL */ + if (lnum) + { + operations->reply (-1, FILEIO_EINVAL); + return; + } + + target_fio_no_longjmp = 1; + ret = gettimeofday (&tv, NULL); + + if (ret == -1) + { + operations->return_errno (-1); + return; + } + + if (ptrval) + { + target_fileio_to_fio_timeval (&tv, &ftv); + + retlength = operations->write_bytes (ptrval, (gdb_byte *) &ftv, sizeof ftv); + if (retlength != sizeof ftv) + { + operations->return_errno (-1); + return; + } + } + operations->return_success (ret); +} + +static void +target_fileio_func_isatty (char *buf, struct file_io_operations *operations) +{ + long target_fd; + int fd; + + /* Parameter: file descriptor */ + if (target_fileio_extract_int (&buf, &target_fd)) + { + operations->ioerror (); + return; + } + target_fio_no_longjmp = 1; + fd =target_fileio_map_fd ((int) target_fd); + operations->return_success (fd == FIO_FD_CONSOLE_IN || + fd == FIO_FD_CONSOLE_OUT ? 1 : 0); +} + +static void +target_fileio_func_system (char *buf, struct file_io_operations *operations) +{ + CORE_ADDR ptrval; + int ret, length, retlength; + char *cmdline = NULL; + + /* Parameter: Ptr to commandline / length incl. trailing zero */ + if (target_fileio_extract_ptr_w_len (&buf, &ptrval, &length)) + { + operations->ioerror (); + return; + } + + if (length) + { + /* Request commandline */ + cmdline = alloca (length); + retlength = operations->read_bytes (ptrval, (gdb_byte *) cmdline, length); + if (retlength != length) + { + operations->ioerror (); + return; + } + } + + /* Check if system(3) has been explicitely allowed using the + `set remote system-call-allowed 1' command. If length is 0, + indicating a NULL parameter to the system call, return zero to + indicate a shell is not available. Otherwise fail with EPERM. */ + if (!target_fio_system_call_allowed) + { + if (!length) + operations->return_success (0); + else + operations->reply (-1, FILEIO_EPERM); + return; + } + + target_fio_no_longjmp = 1; + ret = system (cmdline); + + if (!length) + operations->return_success (ret); + else if (ret == -1) + operations->return_errno (-1); + else + operations->return_success (WEXITSTATUS (ret)); +} + +static struct { + char *name; + void (*func)(char *, struct file_io_operations *); +} target_fio_func_map[] = { + { "open", target_fileio_func_open }, + { "close", target_fileio_func_close }, + { "read", target_fileio_func_read }, + { "write", target_fileio_func_write }, + { "lseek", target_fileio_func_lseek }, + { "rename", target_fileio_func_rename }, + { "unlink", target_fileio_func_unlink }, + { "stat", target_fileio_func_stat }, + { "fstat", target_fileio_func_fstat }, + { "gettimeofday", target_fileio_func_gettimeofday }, + { "isatty", target_fileio_func_isatty }, + { "system", target_fileio_func_system }, + { NULL, NULL } +}; + +struct request_args { + char *buf; + struct file_io_operations *operations; +}; + +static int +do_target_fileio_request (struct ui_out *uiout, void *args) +{ + struct request_args* request = (struct request_args*) args; + char *buf = request->buf; + char *c; + int idx; + + request->operations->set_ctrl_c_signal_handler(); + + c = strchr (++buf, ','); + if (c) + *c++ = '\0'; + else + c = strchr (buf, '\0'); + for (idx = 0; target_fio_func_map[idx].name; ++idx) + if (!strcmp (target_fio_func_map[idx].name, buf)) + break; + if (!target_fio_func_map[idx].name) /* ERROR: No such function. */ + return RETURN_ERROR; + target_fio_func_map[idx].func (c, request->operations); + return 0; +} + +/* Close any open descriptors, and reinitialize the file mapping. */ + +void +target_fileio_reset (void) +{ + int ix; + + for (ix = 0; ix != target_fio_data.fd_map_size; ix++) + { + int fd = target_fio_data.fd_map[ix]; + + if (fd >= 0) + close (fd); + } + if (target_fio_data.fd_map) + { + free (target_fio_data.fd_map); + target_fio_data.fd_map = NULL; + target_fio_data.fd_map_size = 0; + } +} + + +void +target_fileio_request (char *buf, struct file_io_operations *operations) +{ + struct request_args args = {buf, operations}; + int ex; + + target_fio_no_longjmp = 0; + + ex = catch_exceptions (uiout, do_target_fileio_request, (void *)&args, + RETURN_MASK_ALL); + switch (ex) + { + case RETURN_ERROR: + operations->reply (-1, FILEIO_ENOSYS); + break; + case RETURN_QUIT: + operations->reply (-1, FILEIO_EINTR); + break; + default: + break; + } +} + +static void +set_system_call_allowed (char *args, int from_tty) +{ + if (args) + { + char *arg_end; + int val = strtoul (args, &arg_end, 10); + if (*args && *arg_end == '\0') + { + target_fio_system_call_allowed = !!val; + return; + } + } + error (_("Illegal argument for \"set remote system-call-allowed\" command")); +} + +static void +show_system_call_allowed (char *args, int from_tty) +{ + if (args) + error (_("Garbage after \"show remote system-call-allowed\" command: `%s'"), args); + printf_unfiltered ("Calling host system(3) call from target is %sallowed\n", + target_fio_system_call_allowed ? "" : "not "); +} + +void +initialize_target_fileio (struct cmd_list_element *set_cmdlist, + struct cmd_list_element *show_cmdlist) +{ + add_cmd ("system-call-allowed", no_class, + set_system_call_allowed, + _("Set if the host system(3) call is allowed for the target."), + &set_cmdlist); + add_cmd ("system-call-allowed", no_class, + show_system_call_allowed, + _("Show if the host system(3) call is allowed for the target."), + &show_cmdlist); +} diff --git a/gdb/target-fileio.h b/gdb/target-fileio.h new file mode 100644 index 00000000000..46efd90428a --- /dev/null +++ b/gdb/target-fileio.h @@ -0,0 +1,53 @@ +/* Target File-I/O communications + + Copyright (C) 2003, 2007, 2008 Free Software Foundation, Inc. + + 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 3 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, see <http://www.gnu.org/licenses/>. */ + + +#ifndef TARGET_FILEIO_H +#define TARGET_FILEIO_H + +struct cmd_list_element; + +struct file_io_operations { + int (*read_bytes) (CORE_ADDR memaddr, gdb_byte *myaddr, int len); + int (*write_bytes)(CORE_ADDR memaddr, gdb_byte *myaddr, int len); + void (*reply)(int retcode, int error); + void (*set_ctrl_c_signal_handler)(void); +}; + + +#define ioerror() reply(-1, FILEIO_EIO) +#define badfd() reply(-1, FILEIO_EBADF) +#define return_errno(retcode) reply(retcode, ((retcode) < 0) ? target_fileio_errno_to_target (errno) : 0) +#define return_success(retcode) reply(retcode, 0) + + +/* Unified interface to target fileio */ +extern void target_fileio_request (char *buf, struct file_io_operations *operations); + +/* Cleanup any target fileio state. */ +extern void target_fileio_reset (void); + +extern void initialize_target_fileio ( + struct cmd_list_element *set_cmdlist, + struct cmd_list_element *show_cmdlist); + + +extern int target_fio_no_longjmp; + +#endif diff --git a/gdb/target.c b/gdb/target.c index 87ddf249e0a..3887f1774c2 100644 --- a/gdb/target.c +++ b/gdb/target.c @@ -39,6 +39,10 @@ #include "gdbcore.h" #include "exceptions.h" #include "target-descriptions.h" +// begin ARC +#include "observer.h" +#include "cli/cli-decode.h" +// end ARC static void target_info (char *, int); @@ -228,9 +232,28 @@ target_command (char *arg, int from_tty) /* Add a possible target architecture to the list. */ +// begin ARC +static void +pre_open_notify (char *name, int from_tty) +{ + observer_notify_target_pre_connect(¤t_target); +} + +static void +post_open_notify (char *name, int from_tty) +{ + observer_notify_target_post_connect(¤t_target); +} +// end ARC + + void add_target (struct target_ops *t) { +// begin ARC + struct cmd_list_element *cmd; +// end ARC + /* Provide default values for all "must have" methods. */ if (t->to_xfer_partial == NULL) t->to_xfer_partial = default_xfer_partial; @@ -258,7 +281,16 @@ Remaining arguments are interpreted by the target protocol. For more\n\ information on the arguments for a particular protocol, type\n\ `help target ' followed by the protocol name."), &targetlist, "target ", 0, &cmdlist); + +// begin ARC + cmd = +// end ARC add_cmd (t->to_shortname, no_class, t->to_open, t->to_doc, &targetlist); + +// begin ARC + (void) add_cmd ("", no_class, pre_open_notify, "", &cmd->hook_pre); + (void) add_cmd ("", no_class, post_open_notify, "", &cmd->hook_post); +// end ARC } /* Stub functions */ @@ -389,6 +421,7 @@ update_current_target (void) INHERIT (to_shortname, t); INHERIT (to_longname, t); INHERIT (to_doc, t); + INHERIT (to_data, t); // ARC 16/02/2009 gdb bug: 9886 INHERIT (to_open, t); INHERIT (to_close, t); INHERIT (to_attach, t); @@ -645,6 +678,10 @@ update_current_target (void) if (targetdebug) setup_target_debug (); + +// begin ARC + observer_notify_target_updated(¤t_target); +// end ARC } /* Mark OPS as a running target. This reverses the effect @@ -685,7 +722,7 @@ target_mark_exited (struct target_ops *ops) break; if (t == NULL) internal_error (__FILE__, __LINE__, - "Attempted to mark unpushed target \"%s\" as running", + "Attempted to mark unpushed target \"%s\" as non-running", // ARC 17/11/08 correct message gdb bug: 9887 ops->to_shortname); ops->to_has_execution = 0; @@ -1649,6 +1686,9 @@ void target_detach (char *args, int from_tty) { (current_target.to_detach) (args, from_tty); +// begin ARC + observer_notify_target_post_disconnect(¤t_target); +// end ARC } void @@ -1760,7 +1800,13 @@ find_default_attach (char *args, int from_tty) struct target_ops *t; t = find_default_run_target ("attach"); +// begin ARC + observer_notify_target_pre_connect(t); +// end ARC (t->to_attach) (args, from_tty); +// begin ARC + observer_notify_target_post_connect(t); +// end ARC return; } @@ -2060,6 +2106,9 @@ debug_to_close (int quitting) { target_close (&debug_target, quitting); fprintf_unfiltered (gdb_stdlog, "target_close (%d)\n", quitting); +// begin ARC + observer_notify_target_post_disconnect(&debug_target); +// end ARC } void @@ -2069,6 +2118,9 @@ target_close (struct target_ops *targ, int quitting) targ->to_xclose (targ, quitting); else if (targ->to_close != NULL) targ->to_close (quitting); +// begin ARC + observer_notify_target_post_disconnect(targ); +// end ARC } static void @@ -2092,6 +2144,9 @@ static void debug_to_detach (char *args, int from_tty) { debug_target.to_detach (args, from_tty); +// begin ARC + observer_notify_target_post_disconnect(&debug_target); +// end ARC fprintf_unfiltered (gdb_stdlog, "target_detach (%s, %d)\n", args, from_tty); } diff --git a/gdb/testsuite/config/arc-jtag.exp b/gdb/testsuite/config/arc-jtag.exp deleted file mode 100644 index 16891111772..00000000000 --- a/gdb/testsuite/config/arc-jtag.exp +++ /dev/null @@ -1,112 +0,0 @@ -# Test Framework Driver for GDB using the arcjtag target. - -# Copyright 2005 Free Software Foundation, Inc. -# -# 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. - -load_lib gdb.exp - -# -# gdb_target_arcjtag -# Set gdb to target arcjtag. -# -proc gdb_target_arcjtag { } { - global gdb_prompt - global exit_status - - # our arcjtag target doesn't take any options (yet) - #set target_arcjtag_options "[board_info target gdb,target_sim_options]"; - - send_gdb "target arcjtag\n" - - gdb_expect 60 { - -re "A program is being debugged already.*Kill it.*y or n. $" { - send_gdb "y\n" - verbose "\t\tKilling previous program being debugged" - exp_continue - } - -re "Connected to the arcjtag target.*$gdb_prompt $" { - verbose "Set target to arcjtag" - } - -re "$gdb_prompt $" { - verbose "Retrying target arcjtag..." - send_gdb "arc-reset-board\n" - send_gdb "target arcjtag\n" - - gdb_expect 60 { - -re "A program is being debugged already.*Kill it.*y or n. $" { - send_gdb "y\n" - verbose "\t\tKilling previous program being debugged" - exp_continue - } - -re "Connected to the arcjtag target.*$gdb_prompt $" { - verbose "Set target to arcjtag" - } - timeout { - perror "Couldn't set target to arcjtag (timeout)." - return 1 - } - } - } - timeout { - perror "Couldn't set target to arcjtag (timeout)." - return 1 - } - } - - return 0 -} - -# -# gdb_load -- load a file into the debugger. -# return a -1 if anything goes wrong. -# -proc gdb_load { arg } { - global verbose - global loadpath - global loadfile - global GDB - global gdb_prompt - - if { $arg != "" } { - if [gdb_file_cmd $arg] then { return -1 } - } - - if { [gdb_target_arcjtag] != 0 } { - return -1 - } - - # gotta do something about the timeout.... - send_gdb "load\n" - - gdb_expect 180 { - -re ".*$gdb_prompt $" { - if $verbose>1 then { - send_user "Loaded $arg into $GDB\n" - } - return 0 - } - -re "$gdb_prompt $" { - if $verbose>1 then { - perror "GDB couldn't load." - } - } - timeout { - perror "Timed out trying to load $arg." - } - } - - return 1 -} diff --git a/gdb/testsuite/config/remote-gdbserver.exp b/gdb/testsuite/config/remote-gdbserver.exp deleted file mode 100644 index 60d106debc5..00000000000 --- a/gdb/testsuite/config/remote-gdbserver.exp +++ /dev/null @@ -1,570 +0,0 @@ -# Test framework for GDB (remote protocol) using a "gdbserver", -# ie. a debug agent running as a native process on the same or -# a different host. - -# Copyright 2000, 2001, 2003 Free Software Foundation, Inc. - -# 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. - -# Please email any bugs, comments, and/or additions to this file to: -# bug-gdb@prep.ai.mit.edu - -# This file was written by Michael Snyder. (msnyder@redhat.com) - -# -# This module to be used for testing gdb with a "gdbserver" -# built either from libremote or from gdb/gdbserver. -# - -# Load the basic testing library, and the remote stuff. -load_lib ../config/monitor.exp -load_lib telnet.exp -# -# To be addressed or set in your baseboard config file: -# -# set_board_info gdb_protocol "remote" -# Unles you have a gdbserver that uses a different protocol... -# -# set_board_info use_gdb_stub 1 -# This tells the rest of the test suite not to do things -# like "run" which don't work well on remote targets. -# -# set_board_info gdb,do_reload_on_run 1 -# Unles you have a gdbserver that can handle multiple sessions. -# -# set_board_info noargs 1 -# At present there is no provision in the remote protocol -# for passing arguments. This test framework does not -# address the issue, so it's best to set this variable -# in your baseboard configuration file. -# FIXME: there's no reason why the test harness couldn't -# pass commandline args when it spawns gdbserver. -# -# set_board_info gdb,noinferiorio 1 -# Neither the traditional gdbserver nor the one in libremote -# can presently capture stdout and relay it to GDB via the -# 'O' packet. This means that tests involving printf will -# fail unles you set this varibale in your baseboard -# configuration file. -# -# set_board_info gdb,no_hardware_watchpoints 1 -# Unles you have a gdbserver that supports hardware watchpoints. -# FIXME: gdb should detect if the target doesn't support them, -# and fall back to using software watchpoints. -# -# set_board_info gdb_server_prog -# This will be the path to the gdbserver program you want to test. -# Defaults to "gdbserver". -# -# set_board_info sockethost -# The name of the host computer whose socket is being used. -# Defaults to "localhost". Note: old gdbserver requires -# that you define this, but libremote/gdbserver does not. -# -# set_board_info socketport -# Port id to use for socket connection. If not set explicitly, -# it will start at "2345" and increment for each use. -# -# set_board_info rsh_prog -# The program to use to spawn executables on the remote board. -# Default: "rsh" -# -# set_board_info rcp_prog -# The program to use to copy test executables to the remote board. -# Default: "rcp" -# -# set_board_info nfsdir -# If rcp_prog is set to "cp", specify the local directory name that -# is NFS mounted by the board. - -# -# gdb_load -- load a file into the debugger. -# return a -1 if anything goes wrong. -# - -global server_exec; -global portnum; -set portnum "2000"; - -proc gdb_load { args } { - global server_exec; - global portnum; - global verbose; - global gdb_prompt; - - # Port id -- either specified in baseboard file, or managed here. - if [target_info exists gdb,socketport] { - set portnum [target_info gdb,socketport]; - } else { - # Bump the port number to avoid conflicts with hung ports. - incr portnum; - } - - # Extract the local and remote host ids from the target board struct. - - if [target_info exists sockethost] { - set debughost [target_info sockethost]; - } else { - set debughost "localhost:"; - } - # Extract the protocol - if [target_info exists gdb_protocol] { - set protocol [target_info gdb_protocol]; - } else { - set protocol "remote"; - } - - # Extract the name of the gdbserver, if known (default 'gdbserver'). - if [target_info exists gdb_server_prog] { - set gdbserver [target_info gdb_server_prog]; - } else { - set gdbserver "gdbserver"; - } - # Extract the socket hostname - if [target_info exists sockethost] { - set sockethost [target_info sockethost]; - } else { - set sockethost "" - } - - # Get target name - if [target_info exists hostname] { - set target_address [target_info hostname]; - } else { - set target_address "localhost" - } - - # Get the username on the target - if [target_info exists "username"] { - set username [target_info username]; - } else { - set username ""; - } - - # Get download dir - if [target_info exists download_dir] { - set download_dir [target_info download_dir]; - } else { - set download_dir "/tmp" - } - - # Get tests dir - if [target_info exists tests_dir] { - set tests_dir [target_info tests_dir]; - } else { - set tests_dir $download_dir - } - - # Export the host:port pair. - set gdbport $debughost$portnum; - - if { $args == "" || $args == "{}" } { - if [info exists server_exec] { - set args $server_exec; - } else { - send_gdb "info files\n"; - gdb_expect 30 { - -re "Symbols from \"(\[^\"\]+)\"" { - set args $expect_out(1,string); - exp_continue; - } - -re "Local exec file:\[\r\n\]+\[ \t\]*`(\[^'\]+)'," { - set args $expect_out(1,string); - exp_continue; - } - -re "$gdb_prompt $" { } - } - } - } - - # remember new exec file - set server_exec $args; - - # Download the test files into the test_board - gdbserver_download $target_address $username $server_exec \ - $download_dir/a-$portnum.out - - # tell gdb what file we are debugging - if [gdb_file_cmd $args] { - return -1; - } - - if [target_info exists solib_path] { - send_gdb "set solib-absolute-prefix [target_info solib_path]\n" - gdb_expect 30 { - -re "$gdb_prompt $" { - if $verbose>1 then { - send_user "set library path\n" - } - } - default { - perror "Couldn't set library path\n" - return -1 - } - } - } - - for {set i 1} {$i <= 3} {incr i} { - # Fire off the debug agent - set server_spawn_id [gdbserver_spawn $target_address $username \ - "$gdbserver $target_address:$portnum $tests_dir/a-$portnum.out 2>&1"] - - if { $server_spawn_id <= 0 } { return -1 } - - # Wait for the server to produce at least one line and an additional - # character of output. This will wait until any TCP socket has been - # created, so that GDB can connect. - expect { - # expect output from $server_spawn_id - -i $server_spawn_id - -re ".*\n." { } - } - - # We can't just call close, because if gdbserver is local then that means - # that it will get a SIGHUP. Doing it this way could also allow us to - # get at the inferior's input or output if necessary, and means that we - # don't need to redirect output. - expect_background { - -i $server_spawn_id - -re "." { } - eof { - # The spawn ID is already closed now (but not yet waited for). - wait -nowait -i $expect_out(spawn_id) - } - } - - # attach to the "serial port" - if {[gdb_target_cmd $protocol $gdbport] == 0 } { - break - } - verbose -log "Unable to connect to target. Re-trying.." - } - - # do the real load if needed - if [target_info exists gdb_server_do_load] { - send_gdb "load\n" - set timeout 2400 - verbose "Timeout is now $timeout seconds" 2 - gdb_expect { - -re ".*$gdb_prompt $" { - if $verbose>1 then { - send_user "Loaded $arg into $GDB\n" - } - set timeout 30 - verbose "Timeout is now $timeout seconds" 2 - return 1 - } - -re "$gdb_prompt $" { - if $verbose>1 then { - perror "GDB couldn't load." - } - } - timeout { - if $verbose>1 then { - perror "Timed out trying to load $arg." - } - } - } - } - - return 0; -} - - -# Use RSH or telnet depending on the program chosen -# by the board file. -# Return spawn_id -proc gdbserver_spawn { dest username commandline } { - global board_info - if ![target_info exists rsh_prog] { - if { [which remsh] != 0 } { - set RSH remsh - } else { - set RSH rsh - } - } else { - set RSH [target_info rsh_prog]; - } - - if { $RSH == "rsh" } { - return [rsh_gdbserver_spawn $dest $username $commandline] - } else { - - if { $RSH == "telnet" } { - # Spawn the shell - return [telnet_gdbserver_spawn $dest $username $commandline] - - # expect the shell prompt obtained from - # the board description. - # Now spawn gdbserver with its parameters - # and dont expect any output from the gdbserver - # other than the shell prompt - # FIXME ?? Where do I close the telnet - # session ( could use gdb_finish for closing the telnet session) - - - - } else { - verbose "Unknown rsh program " - return -1 - } - } -} - -proc mynewtelnet_open_and_exec { dest port shell_prompt commandline } { - global board_info - - spawn "telnet-exec.exp" $dest $commandline - set board_info($dest,fileid) $spawn_id; - return $spawn_id; -} - - -proc mytelnet_open_and_exec { dest port shell_prompt commandline } { - set tries 0 - set result -1 - set need_respawn 1 - - verbose "Starting a telnet connection to $dest:$port $shell_prompt " 2 - while { $result < 0 && $tries <= 3 } { - if { $need_respawn } { - set need_respawn 0 - spawn "telnet" $dest $port - } - expect { - "Trying " { - exp_continue - } - -re "$shell_prompt.*$" { - verbose "Got prompt $shell_prompt\n" - set result 0 - exp_send $commandline - - } - -re "nt Name:|ogin:" { - if [board_info $connhost exists telnet_username] { - exp_send "[board_info $connhost telnet_username]\n" - exp_continue - } - if [board_info $connhost exists username] { - exp_send "[board_info $connhost username]\n" - exp_continue - } - perror "telnet: need to login" - break - } - "assword:" { - if [board_info $connhost exists telnet_password] { - exp_send "[board_info $connhost telnet_password]\n" - exp_continue - } - if [board_info $connhost exists password] { - exp_send "[board_info $connhost password]\n" - exp_continue - } - perror "telnet: need a password" - break - } - -re "advance.*y/n.*\\?" { - exp_send "n\n" - exp_continue - } - -re {([Aa]dvanced|[Ss]imple) or ([Ss]imple|[Aa]dvanced)} { - exp_send "simple\n" - exp_continue - } - "Connected to" { - exp_continue - } - "unknown host" { - exp_send "\003" - perror "telnet: unknown host" - break - } - "VxWorks Boot" { - exp_send "@\n" - sleep 20 - exp_continue - } - -re "Escape character is.*\\.\[\r\n\]" { - exp_continue - } - "has logged on from" { - exp_continue - } - "You have no Kerberos tickets" { - warning "telnet: no kerberos Tickets, please kinit" - break - } - -re "Connection refused.*$" { - catch "exp_send \"\003\"" foo - sleep 5 - warning "telnet: connection refused." - } - -re "Sorry, this system is engaged.*" { - exp_send "\003" - warning "telnet: already connected." - } - "Connection closed by foreign host.*$" { - warning "telnet: connection closed by foreign host." - break - } - -re "\[\r\n\]+" { - exp_continue - } - timeout { - exp_send "\n" - } - eof { - warning "telnet: got unexpected EOF from telnet." - catch close - catch wait - set need_respawn 1 - sleep 5 - } - } - incr tries - } - - - verbose "spawn id is $spawn_id" - set board_info($dest,fileid) $spawn_id; - return $spawn_id -} - -# Use telnet to spawn a session -proc telnet_gdbserver_spawn { dest username commandline } { - global board_info - set remote $dest - set telnet_prog "telnet" - set prompt [target_info shell_prompt] - set mport 23 - verbose "commandline is $commandline" - return [mynewtelnet_open_and_exec $remote $mport $prompt $commandline] -} - - -# -# Use $RSH to spawn $commandline on remote machine $dest as user $username. -# (Note $username on $dest will have to have appropriate .rhost entries.) -# -proc rsh_gdbserver_spawn { dest username commandline } { - global board_info - - if [target_info exists rsh_prog] { - set RSH [target_info rsh_prog]; - } else { - set RSH rsh - } - - if [board_info $dest exists hostname] { - set remote [board_info $dest hostname]; - } else { - set remote $dest; - } - - if { $username == "" } { - set rsh_useropts "" - } else { - set rsh_useropts "-l" - } - - verbose "spawn $RSH $rsh_useropts $username $remote $commandline"; - spawn $RSH $rsh_useropts $username $remote $commandline; - set board_info($dest,fileid) $spawn_id; - - set timeout 60 - expect { - # expect output from $spawn_id - -i $spawn_id - -re "(.*No route to host)|(poll: protocol failure in circuit setup)|(.*Unknown host)|(.*Connection refused)|(Login incorrect)|(Permission denied)" { - verbose -log "$RSH to $remote failed, output \"$expect_out(buffer)\"" - return -1 - } - -re ".*\r" { } - timeout { - verbose -log "$RSH to $remote timedout (timeout=$timeout)" - return -1 - } - eof { - verbose -log "$RSH to $remote failed" - return -1 - } - } - - return $spawn_id; -} - -# -# Download $srcfile to $destfile on $desthost as user $username using rcp. -# - -proc gdbserver_download {desthost username srcfile destfile} { - if [target_info exists rsh_prog] { - set RSH [target_info rsh_prog]; - } else { - set RSH rsh - } - - if ![target_info exists rcp_prog] { - set RCP rcp - } else { - set RCP [target_info rcp_prog]; - } - - if [board_info $desthost exists name] { - set desthost [board_info $desthost name]; - } - - if [board_info $desthost exists hostname] { - set desthost [board_info $desthost hostname]; - } - - if { $username == "" } { - set rsh_useropts "" - set rcp_dest $desthost - } else { - set rsh_useropts "-l $username" - set rcp_dest "$username@$desthost" - } - - # Delete the output file - # set status [catch "exec $RSH $rsh_useropts $desthost rm -f $destfile |& cat" output] - - if { $RCP != "cp" } { - set status [catch "exec $RCP $srcfile $rcp_dest:$destfile |& cat" output] - } else { - if [target_info exists nfsdir] { - set nfsdir [target_info nfsdir]; - verbose -log "nfsdir is $nfsdir" - set status [catch "exec cp $srcfile $nfsdir/$destfile |& cat" output] - } else { - verbose "\nnfsdir not set\n" - set status 1 - } - } - if { $status == 0 } { - if [target_info exists nfsdir] { - verbose "Copied $srcfile to $nfsdir/$destfile" 2 - return $destfile; - } else { - verbose "Copied $srcfile to $desthost:$destfile" 2 - return $destfile; - } - } else { - verbose "Download to $desthost failed, $output." - return "" - } -} diff --git a/gdb/testsuite/gdb.arch/arc-step-jtag.exp b/gdb/testsuite/gdb.arch/arc-step-jtag.exp deleted file mode 100644 index a51f00e4b54..00000000000 --- a/gdb/testsuite/gdb.arch/arc-step-jtag.exp +++ /dev/null @@ -1,88 +0,0 @@ -if $tracelevel { - strace $tracelevel -} - -# Test single-stepping zero-overhead-loops and delay slots - -if ![istarget "arc-*-*"] then { - verbose "Skipping ARC single-step tests." - return -} - -if ![istarget "*elf32*"] then { - verbose "Skipping ARC JTAG single-step tests." - return -} - -set testfile "arc-step-jtag" -set srcfile ${testfile}.s -set binfile ${objdir}/${subdir}/${testfile} -if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable ""] != "" } { - gdb_suppress_entire_file "Testcase compile failed, so all tests in this file will automatically fail." -} - -gdb_exit -gdb_start -gdb_reinitialize_dir $srcdir/$subdir - -if { [ gdb_load ${binfile} ] != 0 } { - gdb_suppress_entire_file "Load failed, so all tests in this file will automatically fail." -} - -# -# Run to `main' where we begin our tests. -# - -gdb_test "adv _main" ".* in _main .*" "advance to _main" - -gdb_test "stepi" ".* Lmov .*" "step mov instruction" -gdb_test "p \$r0" ".*= 1" "r0 value after mov instruction" - -gdb_test "stepi" ".* Lmov_s .*" "step mov_s instruction" -gdb_test "p \$r0" ".*= 2" "r0 value after mov_s instruction" - -gdb_test "stepi" ".* Lb .*" "step b instruction" - -gdb_test "stepi" ".* Lb_s .*" "step b_s instruction" - -gdb_test "stepi" ".* Lbdotd_dslot .*" "step b.d branch" -gdb_test "stepi" ".* Lbdotd .*" "step b.d delay slot" -gdb_test "p \$r0" ".*= 5" "r0 value after b.d delay slot" - -gdb_test "stepi" ".* Lbl .*" "step bl instruction" - -gdb_test "stepi" ".* Lj_sdotd_dslot .*" "step j_s.d \[blink\] branch" -gdb_test "stepi" ".* Lj_sdotd .*" "step j_s.d \[blink\] delay slot" -gdb_test "p \$r0" ".*= 6" "r0 value after j_s.d \[blink\] delay slot" - -gdb_test "stepi" ".* Lj .*" "step j instruction" - -gdb_test "stepi" ".*" "step mov instruction" -gdb_test "stepi" ".* ZOLstart .*" "step lp instruction" - -gdb_test "p \$lp_count" ".*= 3" "lp_count value" -gdb_test "p \$lp_end - \$lp_start" \ - ".* = 8" "lp_end - lp_start == 8" - -gdb_test "p \$r0" ".* = 6" "r0 value before loop" - -# step thru the loop, checking the value of r0 - -# first iteration -gdb_test "stepi" ".* ZOLmiddle .*" "step add instruction (inside ZOL)" -gdb_test "p \$r0" ".* = 7" "r0 value after 1 iteration" -gdb_test "stepi" ".* ZOLstart .*" "step across end of ZOL" - -# second iteration -gdb_test "stepi" ".* ZOLmiddle .*" "step add instruction (inside ZOL)" -gdb_test "p \$r0" ".* = 8" "r0 value after 2 iterations" -gdb_test "stepi" ".* ZOLstart .*" "step across end of ZOL" - -# last iteration -gdb_test "stepi" ".* ZOLmiddle .*" "step add instruction (inside ZOL)" -gdb_test "p \$r0" ".* = 9" "r0 value after 3 iterations" -gdb_test "stepi" ".* ZOLend .*" "step out of end of ZOL" - -# exit(r0) -gdb_test "continue" ".*Program exited.*011.*" \ - "value of r0 on exit" diff --git a/gdb/testsuite/gdb.arch/arc-step-jtag.s b/gdb/testsuite/gdb.arch/arc-step-jtag.s deleted file mode 100644 index 7ad8e1f9fb2..00000000000 --- a/gdb/testsuite/gdb.arch/arc-step-jtag.s +++ /dev/null @@ -1,46 +0,0 @@ -_main: - .global _main -main: - .global main - - mov r0,1 ; 32 bit instruction -Lmov: - mov_s r0,2 ; 16 bit instruction -Lmov_s: - b Lb ; 32 bit, no delay slot - mov r0,3 - nop -Lb: - b_s Lb_s ; 16 bit, no delay slot - mov r0,4 - nop -Lb_s: - b.d Lbdotd ; 32 bit, delay slot -Lbdotd_dslot: - mov r0,5 - nop -Lbdotd: - - bl Lbl -Lj_sdotd: - j Lj -Lbl: - j_s.d [blink] ; 16 bit, delay slot -Lj_sdotd_dslot: - mov r0,6 -Lj: - mov lp_count,3 ; zero-overhead loop - lp ZOLend -ZOLstart: - add r0,r0,1 -ZOLmiddle: - nop -ZOLend: - ;; r0 should be 9 - - - ;; exit(r0) - flag 1 - nop - nop - nop diff --git a/gdb/testsuite/gdb.arch/arc-step.exp b/gdb/testsuite/gdb.arch/arc-step.exp deleted file mode 100644 index e958b5fbf61..00000000000 --- a/gdb/testsuite/gdb.arch/arc-step.exp +++ /dev/null @@ -1,83 +0,0 @@ -if $tracelevel { - strace $tracelevel -} - -# Test single-stepping zero-overhead-loops and delay slots - -if ![istarget "arc-*-*"] then { - verbose "Skipping ARC single-step tests." - return -} - -if ![istarget "*linux*"] then { - verbose "Skipping ARC linux single-step tests." - return -} - -set testfile "arc-step" -set srcfile ${testfile}.s -set binfile ${objdir}/${subdir}/${testfile} -if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable ""] != "" } { - gdb_suppress_entire_file "Testcase compile failed, so all tests in this file will automatically fail." -} - -gdb_exit -gdb_start -gdb_reinitialize_dir $srcdir/$subdir -gdb_load ${binfile} - -# -# Run to `main' where we begin our tests. -# - -gdb_test "adv _main" ".* in _main .*" "advance to _main" - -gdb_test "stepi" ".* Lmov .*" "step mov instruction" -gdb_test "p \$r0" ".*= 1" "r0 value after mov instruction" - -gdb_test "stepi" ".* Lmov_s .*" "step mov_s instruction" -gdb_test "p \$r0" ".*= 2" "r0 value after mov_s instruction" - -gdb_test "stepi" ".* Lb .*" "step b instruction" - -gdb_test "stepi" ".* Lb_s .*" "step b_s instruction" - -gdb_test "stepi" ".* Lbdotd .*" "step b.d instruction" -gdb_test "p \$r0" ".*= 5" "r0 value after b.d instruction" - -gdb_test "stepi" ".* Lbl .*" "step bl instruction" - -gdb_test "stepi" ".* Lj_sdotd .*" "step j_s.d \[blink\] instruction" -gdb_test "p \$r0" ".*= 6" "r0 value after j_s.d \[blink\] instruction" - -gdb_test "stepi" ".* Lj .*" "step j instruction" - -gdb_test "stepi" ".*" "step mov instruction" -gdb_test "stepi" ".* ZOLstart .*" "step lp instruction" - -gdb_test "p \$lp_count" ".*= 3" "lp_count value" -gdb_test "p \$lp_end - \$lp_start" \ - ".* = 8" "lp_end - lp_start == 8" - -gdb_test "p \$r0" ".* = 6" "r0 value before loop" - -# step thru the loop, checking the value of r0 - -# first iteration -gdb_test "stepi" ".* ZOLmiddle .*" "step add instruction (inside ZOL)" -gdb_test "p \$r0" ".* = 7" "r0 value after 1 iteration" -gdb_test "stepi" ".* ZOLstart .*" "step across end of ZOL" - -# second iteration -gdb_test "stepi" ".* ZOLmiddle .*" "step add instruction (inside ZOL)" -gdb_test "p \$r0" ".* = 8" "r0 value after 1 iteration" -gdb_test "stepi" ".* ZOLstart .*" "step across end of ZOL" - -# last iteration -gdb_test "stepi" ".* ZOLmiddle .*" "step add instruction (inside ZOL)" -gdb_test "p \$r0" ".* = 9" "r0 value after 1 iteration" -gdb_test "stepi" ".* ZOLend .*" "step out of end of ZOL" - -# exit(r0) -gdb_test "continue" ".*Program exited.*011.*" \ - "value of r0 on exit" diff --git a/gdb/testsuite/gdb.arch/arc-step.s b/gdb/testsuite/gdb.arch/arc-step.s deleted file mode 100644 index 8dce39a0e8d..00000000000 --- a/gdb/testsuite/gdb.arch/arc-step.s +++ /dev/null @@ -1,43 +0,0 @@ -_main: - .global _main -main: - .global main - - mov r0,1 ; 32 bit instruction -Lmov: - mov_s r0,2 ; 16 bit instruction -Lmov_s: - b Lb ; 32 bit, no delay slot - mov r0,3 - nop -Lb: - b_s Lb_s ; 16 bit, no delay slot - mov r0,4 - nop -Lb_s: - b.d Lbdotd ; 32 bit, delay slot - mov r0,5 - nop -Lbdotd: - - bl Lbl -Lj_sdotd: - j Lj -Lbl: - j_s.d [blink] ; 16 bit, delay slot - mov r0,6 -Lj: - mov lp_count,3 ; zero-overhead loop - lp ZOLend -ZOLstart: - add r0,r0,1 -ZOLmiddle: - nop -ZOLend: - ;; r0 should be 9 - - - ;; exit(r0) - ;; mov r0,0 - mov r8,1 - trap_s 0 diff --git a/gdb/testsuite/gdb.asm/arc.inc b/gdb/testsuite/gdb.asm/arc.inc deleted file mode 100644 index e22c35c081d..00000000000 --- a/gdb/testsuite/gdb.asm/arc.inc +++ /dev/null @@ -1,55 +0,0 @@ - - comment "subroutine prologue" - .macro gdbasm_enter - st.a blink,[sp,-4] - st.a fp, [sp,-4] - mov fp,sp - .endm - - comment "subroutine epilogue" - .macro gdbasm_leave - ld.ab fp, [sp,4] - ld blink,[sp,0] - j.d [blink] - add sp,sp,4 - .endm - - .macro gdbasm_call subr - bl \subr - .endm - - .macro gdbasm_several_nops - nop - nop - nop - nop - .endm - - comment "exit (0)" - .macro gdbasm_exit0 - mov_s r0,0 - trap_s 0 - .endm - - comment "crt0 startup" - .macro gdbasm_startup - mov fp, 0 - .endm - - comment "Declare a data variable" - .macro gdbasm_datavar name value - .data -\name: - .long \value - .endm - - comment "Declare the start of a subroutine" - .macro gdbasm_declare name - .type \name, @function -\name: - .endm - - comment "End a subroutine" - .macro gdbasm_end name - .size \name, .-name - .endm diff --git a/gdb/testsuite/gdb.asm/asm-source.exp b/gdb/testsuite/gdb.asm/asm-source.exp index a94fd9895af..0f531a96347 100644 --- a/gdb/testsuite/gdb.asm/asm-source.exp +++ b/gdb/testsuite/gdb.asm/asm-source.exp @@ -48,12 +48,6 @@ switch -glob -- [istarget] { "*arm-*-*" { set asm-arch arm } - "arc-*-*" { - set asm-arch arc - set asm-flags "-I${srcdir}/${subdir} -I${objdir}/${subdir}" - set debug-flags "-gdwarf-2" - append link-flags " -marclinux" - } "xscale-*-*" { set asm-arch arm } diff --git a/gdb/testsuite/gdb.base/callfuncs.exp b/gdb/testsuite/gdb.base/callfuncs.exp index 1a4da6e0b69..de1f6479281 100644 --- a/gdb/testsuite/gdb.base/callfuncs.exp +++ b/gdb/testsuite/gdb.base/callfuncs.exp @@ -266,6 +266,15 @@ proc fetch_all_registers {test} { } exp_continue } + -re "^COUNT0\[ \t\]+\[^\r\n\]+\[\r\n\]+" { + if [istarget "arc*"] { + # Filter out COUNT0 which is an instruction counter on the simulator, + # giving spurious differences. + } else { + lappend all_registers_lines $expect_out(0,string) + } + exp_continue + } -re "^\[^ \t\]+\[ \t\]+\[^\r\n\]+\[\r\n\]+" { lappend all_registers_lines $expect_out(0,string) exp_continue diff --git a/gdb/testsuite/gdb.base/float.exp b/gdb/testsuite/gdb.base/float.exp index ca84466cae0..e0f0d9ffabc 100644 --- a/gdb/testsuite/gdb.base/float.exp +++ b/gdb/testsuite/gdb.base/float.exp @@ -97,8 +97,6 @@ if { [istarget "alpha*-*-*"] } then { gdb_test "info float" "fr4.*fr4R.*fr31R.*" "info float" } elseif [istarget "sparc*-*-*"] then { gdb_test "info float" "f0.*f1.*f31.*d0.*d30.*" "info float" -} elseif [istarget "arc*-*-*"] then { - gdb_test "info float" "Software FPU.*" } else { gdb_test "info float" "No floating.point info available for this processor." "info float (unknown target)" } diff --git a/gdb/testsuite/gdb.base/relocate.exp b/gdb/testsuite/gdb.base/relocate.exp index 46f7ab6ae1f..a7beba94f94 100644 --- a/gdb/testsuite/gdb.base/relocate.exp +++ b/gdb/testsuite/gdb.base/relocate.exp @@ -78,7 +78,7 @@ set static_bar_addr [get_var_address static_bar] # Make sure they have different addresses. if { "${static_foo_addr}" == "${static_bar_addr}" } { - fail "static variables have different addresses" + fail "static variables have same address" } else { pass "static variables have different addresses" } @@ -89,7 +89,7 @@ set global_bar_addr [get_var_address global_bar] # Make sure they have different addresses. if { "${global_foo_addr}" == "${global_bar_addr}" } { - fail "global variables have different addresses" + fail "global variables have same address" } else { pass "global variables have different addresses" } @@ -100,7 +100,7 @@ set function_bar_addr [get_var_address function_bar] # Make sure they have different addresses. if { "${function_foo_addr}" == "${function_bar_addr}" } { - fail "functions have different addresses" + fail "functions have same address" } else { pass "functions have different addresses" } @@ -126,7 +126,7 @@ set new_function_foo_addr [get_var_address function_foo] # Make sure they have different addresses. if { "${function_foo_addr}" == "${new_function_foo_addr}" } { - fail "function foo has a different address" + fail "function foo has the same address" } else { pass "function foo has a different address" } diff --git a/gdb/testsuite/lib/arc-gdbserver.exp b/gdb/testsuite/lib/arc-gdbserver.exp deleted file mode 100644 index 6674e9240d0..00000000000 --- a/gdb/testsuite/lib/arc-gdbserver.exp +++ /dev/null @@ -1,98 +0,0 @@ -# Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003 Free Software -# Foundation, Inc. -# -# This file is part of DejaGnu. -# -# DejaGnu 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. -# -# DejaGnu 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 DejaGnu; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -# gdbserver running cross - -#load the config file -load_generic_config "remote-gdbserver" - - -process_multilib_options "" - -# The default compiler for this target. -set_board_info compiler "$env(GDBTEST_CC)" -set_board_info cflags "$env(GDBTEST_CFLAGS)" -set_board_info assembler "$env(GDBTEST_AS) $env(GDBTEST_ASFLAGS)" -set_board_info linker "$env(GDBTEST_LD)" -set_board_info ldflags "$env(GDBTEST_LDFLAGS)" - -# We will be using the standard GDB remote protocol -set_board_info gdb_protocol "remote" - -set_board_info netport "$env(GDBTEST_GDBSERVER_HOST):$env(GDBTEST_GDBSERVER_PORT)" - -# Path to the gdbserver executable, if required. -set_board_info gdb_server_prog $env(GDBTEST_GDBSERVER_PATH) -# "../gdbserver/gdbserver" - -# Name of the computer whose socket will be used, if required. -set_board_info sockethost "$env(GDBTEST_GDBSERVER_HOST):" - -# Port ID to use for socket connection -set_board_info gdb,socketport $env(GDBTEST_GDBSERVER_PORT) - -# Use techniques appropriate to a stub -set_board_info use_gdb_stub 1 - -# This gdbserver can only run a process once per session. -set_board_info gdb,do_reload_on_run 1 - -# There's no support for argument-passing (yet). -set_board_info noargs 1 - -# Can't do FILE IO in current gdbserver -set_board_info gdb,nofileio 1 - -# Can't do input (or output) in the current gdbserver. -set_board_info gdb,noinferiorio 1 - -# Can't do hardware watchpoints, in general -set_board_info gdb,no_hardware_watchpoints 1 - -# Copy the testcases using cp -set_board_info rcp_prog "cp" - - -# Set nfs directory -# On my machine this is how it is mounted. - ramana -# kanika:/home/opt/share on /mnt/nfsmounts type nfs (rw,addr=192.168.100.68) -set_board_info nfsdir $env(GDBTEST_NFSDIR) - -# Set the test directory on the board. Where is this mounted -# on the board. -set_board_info tests_dir $env(GDBTEST_TESTS_DIR) - -# run on target using rsh -set_board_info rsh_prog "rsh" - -# Download directory -set_board_info download_dir $env(GDBTEST_DOWNLOAD_DIR) - -# Hostname -set_board_info hostname $env(GDBTEST_GDBSERVER_HOST) -set_board_info username "root" -set_board_info sockethost "$env(GDBTEST_GDBSERVER_HOST):" - -#Shell prompt -set_board_info shell_prompt "\[arcLinux\]$" - -#set_board_info board,connect "telnet" - -# timeout -#set_board_info gdb,timeout 300 diff --git a/gdb/testsuite/lib/arc-jtag.exp b/gdb/testsuite/lib/arc-jtag.exp deleted file mode 100644 index c613f307a79..00000000000 --- a/gdb/testsuite/lib/arc-jtag.exp +++ /dev/null @@ -1,32 +0,0 @@ -# -# The baseboard file for the arcjtag target -# - -load_generic_config "arc-jtag" - -set_board_info compiler "$env(GDBTEST_CC)" -set_board_info cflags "$env(GDBTEST_CFLAGS)" -set_board_info assembler "$env(GDBTEST_AS) $env(GDBTEST_ASFLAGS)" -set_board_info linker "$env(GDBTEST_LD)" -set_board_info ldflags "$env(GDBTEST_LDFLAGS)" - -#Reload the file before running -set_board_info gdb,do_reload_on_run 1 - -#Arguments cannot be passed -set_board_info noargs 1 - -#File IO not supported -set_board_info gdb,nofileio 1 - -#Inferior is unable to do I/O -set_board_info gdb,noinferiorio 1 - -#Signals not supported -set_board_info gdb,nosignals 1 - -#Skip the huge.exp test -set_board_info gdb,skip_huge_test 1 - -#We use "target arcjtag" to talk to JTAG -set_board_info gdb_protocol "arcjtag" diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp index cbfa253579a..edf52e04698 100644 --- a/gdb/testsuite/lib/gdb.exp +++ b/gdb/testsuite/lib/gdb.exp @@ -191,7 +191,21 @@ proc delete_breakpoints {} { proc gdb_run_cmd {args} { global gdb_prompt - if [target_info exists gdb_init_command] { + + send_gdb "target sim\n" + gdb_expect 120 { + -re "Connected to the simulator.*$gdb_prompt $" { + verbose "Connected to simulator." 2 + } + } + + send_gdb "load\n" + gdb_expect 120 { + -re ".*$gdb_prompt $" { + } + } + + if [target_info exists gdb_init_command] { send_gdb "[target_info gdb_init_command]\n"; gdb_expect 30 { -re "$gdb_prompt $" { } diff --git a/gdb/testsuite/lib/gdbserver-support.exp b/gdb/testsuite/lib/gdbserver-support.exp index 73de76f0883..6fbaa224386 100644 --- a/gdb/testsuite/lib/gdbserver-support.exp +++ b/gdb/testsuite/lib/gdbserver-support.exp @@ -250,10 +250,6 @@ proc gdbserver_start { options arguments } { proc gdbserver_spawn { child_args } { set target_exec [gdbserver_download] - if [target_info exists tests_dir] { - set tests_dir [target_info tests_dir] - set gdbserver_server_exec $tests_dir/$gdbserver_server_exec - } # Fire off the debug agent. This flavour of gdbserver takes as # arguments the port information, the name of the executable file to # be debugged, and any arguments. diff --git a/gdb/testsuite/lib/mi-support.exp b/gdb/testsuite/lib/mi-support.exp index a67b0cc8a8c..99cc80bc64c 100644 --- a/gdb/testsuite/lib/mi-support.exp +++ b/gdb/testsuite/lib/mi-support.exp @@ -773,6 +773,19 @@ proc mi_run_cmd {args} { } global mi_gdb_prompt + send_gdb "target sim\n" + gdb_expect 120 { + -re "Connected to the simulator.*$gdb_prompt $" { + verbose "Connected to simulator." 2 + } + } + + send_gdb "load\n" + gdb_expect 120 { + -re ".*$gdb_prompt $" { + } + } + if [target_info exists gdb_init_command] { send_gdb "[target_info gdb_init_command]\n"; gdb_expect 30 { diff --git a/gdb/testsuite/lib/telnet-exec.exp b/gdb/testsuite/lib/telnet-exec.exp deleted file mode 100644 index 8bbaa8de232..00000000000 --- a/gdb/testsuite/lib/telnet-exec.exp +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/expect -f - -# -# -# - - -set host "192.168.100.222" -set debuggee [lindex $argv 0] - -set timeout 360 -set env(TERM) vt100; # actual value doesn't matter, just has to be set - -spawn telnet $host -sleep 1; # wait for telnet to happen -send "PS1=\\# \r" -expect "\# " -#expect "\[arcLinux\]\$" -#send_user "one\n" - - -send "cd /nfs/gdbserver-tests/ \r" -expect "\# " -#expect "\[arcLinux\]\$" -send_user "starting gdbserver...\n" -send "./gdbserver host:4004 /nfs/gdbserver-tests/ramana-tests/a-4004.out \r" - -expect "xxx" -send "exit"
\ No newline at end of file diff --git a/gdb/version.in b/gdb/version.in index 99b56c20aca..21afad37646 100644 --- a/gdb/version.in +++ b/gdb/version.in @@ -1 +1 @@ -6.8-arc-20070620 +6.8 |