[PATCH v4 09/44] gdb, intelgt: add the target-dependent definitions for the Intel GT architecture
Markus Metzger <[email protected]>
| Newsgroups | gmane.comp.gdb.patches |
|---|---|
| Message-ID | <[email protected]> |
From: Tankut Baris Aktemur <[email protected]> Introduce gdb/intelgt-tdep.c for the Intel GT target. The target is defined in a future patch as a gdbserver low target implementation. Unlike, for example, X86-64, IntelGT does not have a dedicated breakpoint instruction. Instead, it has a breakpoint bit in each instruction. Hence, any instruction can be used as a breakpoint instruction. It further supports ignoring breakpoints for stepping over or resuming from a breakpoint. This only works, of course, if we use breakpoint bits inside the original instruction rather than replacing it with a fixed breakpoint instruction. Add gdbarch methods for inserting and removing memory breakpoints by setting and clearing those breakpoint bits. We define one pseudo-register, $framedesc, that provides a type alias for the frame descriptor register in GRF, representing it as a struct. The value of the $pc register is the $ip register, which is provided in $cr0.2, offset by $isabase. A translation function to map the device_id to a gen_version is provided. This will be useful in various places, in particular to generate the correct disassembly. Co-authored-by: Markus Metzger <[email protected]> Co-authored-by: Natalia Saiapova <[email protected]> Co-authored-by: Mihails Strasuns <[email protected]> Co-authored-by: Mohamed Bouhaouel <[email protected]> Reviewed-By: Simon Marchi <[email protected]> --- gdb/Makefile.in | 1 + gdb/configure.tgt | 5 + gdb/intelgt-tdep.c | 889 ++++++++++++++++++++++++++++++++++++++++++++ gdb/selftest-arch.c | 3 +- 4 files changed, 897 insertions(+), 1 deletion(-) create mode 100644 gdb/intelgt-tdep.c diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 4794e01246e..2993e6b55c8 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -755,6 +755,7 @@ ALL_64_TARGET_OBS = \ ia64-linux-tdep.o \ ia64-tdep.o \ ia64-vms-tdep.o \ + intelgt-tdep.o \ loongarch-linux-tdep.o \ loongarch-tdep.o \ mips-fbsd-tdep.o \ diff --git a/gdb/configure.tgt b/gdb/configure.tgt index 7313b31e42a..5fb492b7acd 100644 --- a/gdb/configure.tgt +++ b/gdb/configure.tgt @@ -362,6 +362,11 @@ ia64-*-*vms*) gdb_target_obs="ia64-vms-tdep.o" ;; +intelgt-*-elf) + # Target: Intel(R) Graphics Technology graphics processor + gdb_target_obs="intelgt-tdep.o arch/intelgt.o" + ;; + iq2000-*-*) gdb_target_obs="iq2000-tdep.o" ;; diff --git a/gdb/intelgt-tdep.c b/gdb/intelgt-tdep.c new file mode 100644 index 00000000000..b4c4f3fa115 --- /dev/null +++ b/gdb/intelgt-tdep.c @@ -0,0 +1,889 @@ +/* Target-dependent code for the Intel(R) Graphics Technology architecture. + + Copyright (C) 2019-2026 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 "arch-utils.h" +#include "arch/intelgt.h" +#include "cli/cli-cmds.h" +#include "dwarf2/frame.h" +#include "extract-store-integer.h" +#include "frame-unwind.h" +#include "gdbtypes.h" +#include "target.h" +#include "target-descriptions.h" +#include "value.h" +#include "gdbthread.h" +#include "inferior.h" +#include "user-regs.h" +#include <algorithm> + +/* Global debug flag. */ +static bool intelgt_debug = false; + +/* Print an "intelgt" debug statement. */ + +#define intelgt_debug_printf(fmt, ...) \ + debug_prefixed_printf_cond (intelgt_debug, "intelgt", fmt, ##__VA_ARGS__) + +/* Regnum pair describing the assigned regnum range for a single + regset. START is inclusive, END is exclusive. '-1' means + not-yet-set. */ + +struct regnum_range +{ + int start = -1; + int end = -1; +}; + +/* Data specific for this architecture. */ + +struct intelgt_gdbarch_tdep : gdbarch_tdep_base +{ + intelgt_gdbarch_tdep () = default; + + /* Return regnum where frame descriptors are stored. */ + int framedesc_base_regnum () + { + /* For EM_INTELGT frame descriptors are stored at MAX_GRF - 1. */ + gdb_assert (regset_ranges[intelgt::REGSET_GRF].end > 1); + return regset_ranges[intelgt::REGSET_GRF].end - 1; + } + + /* $r0 GRF register number. */ + int r0_regnum = -1; + + /* $ce register number in the regcache. */ + int ce_regnum = -1; + + /* Register number for the GRF containing function return value. */ + int retval_regnum = -1; + + /* Register number for the control register. */ + int cr0_regnum = -1; + + /* Register number for the state register. */ + int sr0_regnum = -1; + + /* Register number for the instruction base virtual register. */ + int isabase_regnum = -1; + + /* Register number for the general state base SBA register. */ + int genstbase_regnum = -1; + + /* Register number for the DBG0 register. */ + int dbg0_regnum = -1; + + /* Assigned regnum ranges for DWARF regsets. */ + regnum_range regset_ranges[intelgt::REGSET_COUNT]; + + /* Enabled pseudo-registers for the current target description. */ + std::vector<std::string> enabled_pseudo_regs; + + /* The framedesc user register's type. */ + type *framedesc_type = nullptr; +}; + +/* Per-inferior cached data for the Intelgt target. */ + +struct intelgt_inferior_data +{ + /* Device target id. */ + uint32_t device_id = 0u; +}; + +static const registry<inferior>::key<intelgt_inferior_data> + intelgt_inferior_data_handle; + +/* Fetch the per-inferior data. */ + +static intelgt_inferior_data * +get_intelgt_inferior_data (inferior *inf) +{ + return &intelgt_inferior_data_handle.try_emplace (inf); +} + +/* Helper function to return the device id using GDBARCH. */ + +static uint32_t +get_device_id (gdbarch *gdbarch) +{ + const target_desc *tdesc = gdbarch_target_desc (gdbarch); + const tdesc_device *device_info = tdesc_device_info (tdesc); + if (!device_info->target_id.has_value ()) + error (_("A target id for the device is required.")); + + return *device_info->target_id; +} + +/* Helper function to return the device id using the inferior. */ + +static uint32_t +get_device_id (inferior *inferior) +{ + intelgt_inferior_data *inf_data = get_intelgt_inferior_data (inferior); + if (inf_data->device_id == 0u) + inf_data->device_id = get_device_id (inferior->arch ()); + + return inf_data->device_id; +} + +/* Read part of REGNUM at OFFSET into BUFFER. The length of data to + read is SIZE. Consider using this helper function when reading + subregisters of CR0, SR0, and R0. */ + +static void +intelgt_read_register_part (readable_regcache *regcache, int regnum, + size_t offset, + gdb::array_view<gdb_byte> buffer, + const char *error_message) +{ + if (regnum == -1) + error (_("%s Unexpected reg num '-1'."), error_message); + + gdbarch *arch = regcache->arch (); + const char *regname = gdbarch_register_name (arch, regnum); + int regsize = register_size (arch, regnum); + + if (offset + buffer.size () > regsize) + error (_("%s %s[%zu:%zu] is outside the range of %s[%d:0]."), + error_message, regname, (offset + buffer.size () - 1), offset, + regname, (regsize - 1)); + + gdb_byte *bytes = (gdb_byte *) alloca (buffer.size ()); + gdb::array_view<gdb_byte> view (bytes, buffer.size ()); + + register_status reg_status + = regcache->cooked_read_part (regnum, offset, view); + + if (reg_status == REG_UNAVAILABLE) + throw_error (NOT_AVAILABLE_ERROR, + _("%s Register %s (%d) is not available."), + error_message, regname, regnum); + + if (reg_status == REG_UNKNOWN) + error (_("%s Register %s (%d) is unknown."), error_message, + regname, regnum); + + extract_integer (buffer, view, gdbarch_byte_order (arch), false); +} + +/* Write BUFFER into part of REGNUM at OFFSET. The length of data to + read is SIZE. */ + +static void +intelgt_write_register_part (regcache *regcache, int regnum, + size_t offset, + gdb::array_view<const gdb_byte> buffer, + const char *error_message) +{ + if (regnum == -1) + error (_("%s Unexpected reg num '-1'."), error_message); + + gdbarch *arch = regcache->arch (); + const char *regname = gdbarch_register_name (arch, regnum); + int regsize = register_size (arch, regnum); + + if (offset + buffer.size () > regsize) + error (_("%s[%ld:%ld] is outside the range of %s[%d:0]."), + regname, (offset + buffer.size () - 1), offset, regname, + (regsize - 1)); + + gdb_byte *bytes = (gdb_byte *) alloca (buffer.size ()); + gdb::array_view<gdb_byte> view (bytes, buffer.size ()); + + extract_integer (view, buffer, gdbarch_byte_order (arch), false); + regcache->cooked_write_part (regnum, offset, view); +} + +/* Utility function to look up the pseudo-register number by name. Exact + amount of pseudo-registers may differ and thus fixed constants can't be + used for this. */ + +static int +intelgt_pseudo_register_num (gdbarch *arch, const char *name) +{ + intelgt_gdbarch_tdep *data = gdbarch_tdep<intelgt_gdbarch_tdep> (arch); + auto iter = std::find (data->enabled_pseudo_regs.begin (), + data->enabled_pseudo_regs.end (), name); + gdb_assert (iter != data->enabled_pseudo_regs.end ()); + return gdbarch_num_regs (arch) + (iter - data->enabled_pseudo_regs.begin ()); +} + +/* Convert a DWARF register number to a GDB register number. This + function requires the register listing in the target description to + be in the same order in each regset as the intended DWARF numbering + order. Currently this always holds true when gdbserver generates + the target description. */ + +static int +intelgt_dwarf_reg_to_regnum (gdbarch *gdbarch, int num) +{ + constexpr int ip = 0; + constexpr int ce = 1; + + /* Register sets follow this format: [START, END), where START is + inclusive and END is exclusive. */ + constexpr regnum_range dwarf_nums[intelgt::REGSET_COUNT] = { + [intelgt::REGSET_SBA] = { 5, 12 }, + [intelgt::REGSET_GRF] = { 16, 272 }, + [intelgt::REGSET_ADDR] = { 272, 288 }, + [intelgt::REGSET_FLAG] = { 288, 304 }, + [intelgt::REGSET_ACC] = { 304, 320 }, + [intelgt::REGSET_MME] = { 320, 336 }, + }; + + /* Number of SBA registers. */ + constexpr size_t sba_dwarf_len = dwarf_nums[intelgt::REGSET_SBA].end + - dwarf_nums[intelgt::REGSET_SBA].start; + + /* Map the DWARF register numbers of SBA registers to their names. + Base number is dwarf_nums[intelgt::REGSET_SBA].start. */ + constexpr const char *sba_dwarf_reg_order[sba_dwarf_len] { + "btbase", + "scrbase0", + "genstbase", + "sustbase", + "blsustbase", + "blsastbase", + "scrbase1" + }; + + intelgt_gdbarch_tdep *data + = gdbarch_tdep<intelgt_gdbarch_tdep> (gdbarch); + + if (num == ip) + return intelgt_pseudo_register_num (gdbarch, "ip"); + if (num == ce) + return data->ce_regnum; + + for (int regset = 0; regset < intelgt::REGSET_COUNT; ++regset) + if (num >= dwarf_nums[regset].start && num < dwarf_nums[regset].end) + { + if (regset == intelgt::REGSET_SBA) + { + /* For SBA registers we first find out the name of the + register out of DWARF register number and then find the + register number corresponding to the name. */ + int sba_num = num - dwarf_nums[intelgt::REGSET_SBA].start; + std::string name (sba_dwarf_reg_order[sba_num]); + + return user_reg_map_name_to_regnum (gdbarch, name); + } + else + { + int candidate = (data->regset_ranges[regset].start + num + - dwarf_nums[regset].start); + + if (candidate < data->regset_ranges[regset].end) + return candidate; + } + } + + return -1; +} + +/* Return the PC of the first real instruction. */ + +static CORE_ADDR +intelgt_skip_prologue (gdbarch *gdbarch, CORE_ADDR start_pc) +{ + intelgt_debug_printf ("start_pc: %s", paddress (gdbarch, start_pc)); + CORE_ADDR func_addr; + + if (find_pc_partial_function (start_pc, nullptr, &func_addr, nullptr)) + { + CORE_ADDR post_prologue_pc + = skip_prologue_using_sal (gdbarch, func_addr); + + intelgt_debug_printf ("post prologue pc: %s", + paddress (gdbarch, post_prologue_pc)); + + if (post_prologue_pc != 0) + return post_prologue_pc; + } + + /* Could not find the end of prologue using SAL. */ + return start_pc; +} + +/* Implementation of gdbarch's return_value method. */ + +static enum return_value_convention +intelgt_return_value_as_value (gdbarch *gdbarch, value *function, + type *valtype, regcache *regcache, + value **read_value, const gdb_byte *writebuf) +{ + gdb_assert_not_reached ("intelgt_return_value_as_value is to be " + "implemented later."); +} + +/* Architecture-specific register state initialization. */ + +static void +intelgt_init_reg (gdbarch *gdbarch, int regnum, dwarf2_frame_state_reg *reg, + const frame_info_ptr &this_frame) +{ + int ip_regnum = intelgt_pseudo_register_num (gdbarch, "ip"); + + if (regnum == ip_regnum) + reg->how = DWARF2_FRAME_REG_RA; + else if (regnum == gdbarch_sp_regnum (gdbarch)) + reg->how = DWARF2_FRAME_REG_CFA; +} + +/* A helper function that returns the value of the ISABASE register. */ + +static CORE_ADDR +intelgt_get_isabase (readable_regcache *regcache) +{ + gdbarch *gdbarch = regcache->arch (); + intelgt_gdbarch_tdep *data + = gdbarch_tdep<intelgt_gdbarch_tdep> (gdbarch); + gdb_assert (data->isabase_regnum != -1); + + uint64_t isabase = 0; + if (regcache->cooked_read (data->isabase_regnum, &isabase) != REG_VALID) + throw_error (NOT_AVAILABLE_ERROR, + _("Register %d (isabase) is not available"), + data->isabase_regnum); + return isabase; +} + +/* The 'unwind_pc' gdbarch method. */ + +static CORE_ADDR +intelgt_unwind_pc (gdbarch *gdbarch, const frame_info_ptr &next_frame) +{ + /* Use ip register here, as IGC uses 32bit values (pc is 64bit). */ + int ip_regnum = intelgt_pseudo_register_num (gdbarch, "ip"); + CORE_ADDR prev_ip = frame_unwind_register_unsigned (next_frame, + ip_regnum); + intelgt_debug_printf ("prev_ip: %s", paddress (gdbarch, prev_ip)); + + /* Program counter is $ip + $isabase. */ + intelgt_gdbarch_tdep *data + = gdbarch_tdep<intelgt_gdbarch_tdep> (gdbarch); + gdb_assert (data->isabase_regnum != -1); + CORE_ADDR isabase + = frame_unwind_register_unsigned (next_frame, + data->isabase_regnum); + + return isabase + prev_ip; +} + +/* Frame unwinding. */ + +static void +intelgt_frame_this_id (const frame_info_ptr &this_frame, + void **this_prologue_cache, frame_id *this_id) +{ + /* FIXME: Assembly-level unwinding for intelgt is not available at + the moment. Stop at the first frame. */ + *this_id = outer_frame_id; +} + +/* This is a "bare minimum" unwinder that avoids crashing GDB + if debugging a program without debug info. */ + +static const frame_unwind_legacy intelgt_frame_unwind ( + "intelgt prologue", + NORMAL_FRAME, /* type */ + FRAME_UNWIND_ARCH, /* class */ + default_frame_unwind_stop_reason, /* stop_reason */ + intelgt_frame_this_id, /* this_id */ + nullptr, /* prev_register */ + nullptr, /* unwind_data */ + default_frame_sniffer /* sniffer */ +); + +/* The memory_insert_breakpoint gdbarch method. */ + +static int +intelgt_memory_insert_breakpoint (gdbarch *gdbarch, bp_target_info *bp) +{ + intelgt_debug_printf ("req ip: %s", paddress (gdbarch, + bp->reqstd_address)); + + /* Ensure that we have enough space in the breakpoint. */ + static_assert (intelgt::MAX_INST_LENGTH <= BREAKPOINT_MAX); + + gdb_byte inst[intelgt::MAX_INST_LENGTH]; + int err = target_read_memory (bp->reqstd_address, inst, + intelgt::MAX_INST_LENGTH); + if (err != 0) + { + /* We could fall back to reading a full and then a compacted + instruction but we should rather allow short reads than + having the caller try smaller and smaller sizes. */ + intelgt_debug_printf ("Failed to read memory at %s (%s).", + paddress (gdbarch, bp->reqstd_address), + strerror (err)); + return err; + } + + bp->placed_address = bp->reqstd_address; + uint32_t device_id = get_device_id (current_inferior ()); + bp->shadow_len = intelgt::inst_length (inst, device_id); + + /* Make a copy before we set the breakpoint so we can restore the + original instruction when removing the breakpoint again. + + This isn't strictly necessary but it saves one target access. */ + memcpy (bp->shadow_contents, inst, bp->shadow_len); + + const bool already = intelgt::set_breakpoint (inst, device_id); + if (already) + { + /* Warn if the breakpoint bit is already set. + + There is still a breakpoint, probably hard-coded, and it should + still trigger and we're still able to step over it. It's just + not our breakpoint. */ + warning (_("Using permanent breakpoint at %s."), + paddress (gdbarch, bp->placed_address)); + + /* There's no need to write the unmodified instruction back. */ + return 0; + } + + err = target_write_raw_memory (bp->placed_address, inst, bp->shadow_len); + if (err != 0) + intelgt_debug_printf ("Failed to insert breakpoint at %s (%s).", + paddress (gdbarch, bp->placed_address), + strerror (err)); + + return err; +} + +/* The memory_remove_breakpoint gdbarch method. */ + +static int +intelgt_memory_remove_breakpoint (gdbarch *gdbarch, struct bp_target_info *bp) +{ + intelgt_debug_printf ("req ip: %s, placed ip: %s", + paddress (gdbarch, bp->reqstd_address), + paddress (gdbarch, bp->placed_address)); + + uint32_t device_id = get_device_id (current_inferior ()); + if (intelgt::has_breakpoint (bp->shadow_contents, device_id)) + warning (_("Re-inserting permanent breakpoint at %s."), + paddress (gdbarch, bp->placed_address)); + + int err = target_write_raw_memory (bp->placed_address, bp->shadow_contents, + bp->shadow_len); + if (err != 0) + intelgt_debug_printf ("Failed to remove breakpoint at %s (%s).", + paddress (gdbarch, bp->placed_address), + strerror (err)); + + return err; +} + +/* The program_breakpoint_here_p gdbarch method. */ + +static bool +intelgt_program_breakpoint_here_p (gdbarch *gdbarch, CORE_ADDR pc) +{ + intelgt_debug_printf ("pc: %s", paddress (gdbarch, pc)); + + gdb_byte inst[intelgt::MAX_INST_LENGTH]; + int err = target_read_memory (pc, inst, intelgt::MAX_INST_LENGTH); + if (err != 0) + { + /* We could fall back to reading a full and then a compacted + instruction but we should rather allow short reads than + having the caller try smaller and smaller sizes. */ + intelgt_debug_printf ("Failed to read memory at %s (%s).", + paddress (gdbarch, pc), strerror (err)); + return err; + } + + uint32_t device_id = get_device_id (current_inferior ()); + const bool is_bkpt = intelgt::has_breakpoint (inst, device_id); + + intelgt_debug_printf ("%sbreakpoint found.", is_bkpt ? "" : "no "); + + return is_bkpt; +} + +/* The 'breakpoint_kind_from_pc' gdbarch method. + This is a required gdbarch function. */ + +static int +intelgt_breakpoint_kind_from_pc (gdbarch *gdbarch, CORE_ADDR *pcptr) +{ + intelgt_debug_printf ("*pcptr: %s", paddress (gdbarch, *pcptr)); + + return intelgt::BP_INSTRUCTION; +} + +/* The 'sw_breakpoint_from_kind' gdbarch method. */ + +static const gdb_byte * +intelgt_sw_breakpoint_from_kind (gdbarch *gdbarch, int kind, int *size) +{ + intelgt_debug_printf ("kind: %d", kind); + + /* We do not support breakpoint instructions. + + We use breakpoint bits in instructions, instead. See + intelgt_memory_insert_breakpoint. */ + *size = 0; + return nullptr; +} + +/* Print one instruction from MEMADDR on INFO->STREAM. */ + +static int +intelgt_print_insn (bfd_vma memaddr, struct disassemble_info *info) +{ + /* Disassembler is to be added in a later patch. */ + return -1; +} + +/* The "read_pc" gdbarch method. */ + +static CORE_ADDR +intelgt_read_pc (readable_regcache *regcache) +{ + gdbarch *arch = regcache->arch (); + intelgt_gdbarch_tdep *data = gdbarch_tdep<intelgt_gdbarch_tdep> (arch); + + /* Instruction pointer is stored in CR0.2. */ + uint32_t ip; + intelgt_read_register_part (regcache, data->cr0_regnum, + sizeof (uint32_t) * 2, + gdb::make_array_view ((gdb_byte *) &ip, + sizeof (uint32_t)), + _("Cannot compute PC.")); + + /* Program counter is $ip + $isabase. */ + CORE_ADDR isabase = intelgt_get_isabase (regcache); + return isabase + ip; +} + +/* The "write_pc" gdbarch method. */ + +static void +intelgt_write_pc (struct regcache *regcache, CORE_ADDR pc) +{ + gdbarch *arch = regcache->arch (); + /* Program counter is $ip + $isabase, can only modify $ip. Need + to ensure that the new value fits within $ip modification range + and propagate the write accordingly. */ + CORE_ADDR isabase = intelgt_get_isabase (regcache); + if (pc < isabase || pc > isabase + UINT32_MAX) + error (_("Can't update $pc to value %s, out of range"), + paddress (arch, pc)); + + intelgt_gdbarch_tdep *data = gdbarch_tdep<intelgt_gdbarch_tdep> (arch); + + /* Instruction pointer is stored in CR0.2. */ + uint32_t ip = pc - isabase; + intelgt_write_register_part (regcache, data->cr0_regnum, + sizeof (uint32_t) * 2, + gdb::make_array_view ((gdb_byte *) &ip, + sizeof (uint32_t)), + _("Cannot write IP into CR0.")); +} + +/* Return the name of pseudo-register REGNUM. */ + +static const char * +intelgt_pseudo_register_name (gdbarch *arch, int regnum) +{ + intelgt_gdbarch_tdep *data = gdbarch_tdep<intelgt_gdbarch_tdep> (arch); + int base_num = gdbarch_num_regs (arch); + if (regnum < base_num + || regnum >= base_num + data->enabled_pseudo_regs.size ()) + error (_("Invalid pseudo-register regnum %d"), regnum); + return data->enabled_pseudo_regs[regnum - base_num].c_str (); +} + +/* Return the GDB type object for the "standard" data type of data in + pseudo-register REGNUM. */ + +static type * +intelgt_pseudo_register_type (gdbarch *arch, int regnum) +{ + const char *name = intelgt_pseudo_register_name (arch, regnum); + const struct builtin_type *bt = builtin_type (arch); + + if (strcmp (name, "ip") == 0) + return bt->builtin_uint32; + + return nullptr; +} + +/* Read the value of a pseudo-register REGNUM. */ + +static struct value * +intelgt_pseudo_register_read_value (gdbarch *arch, + const frame_info_ptr &next_frame, + int pseudo_regnum) +{ + const char *name = intelgt_pseudo_register_name (arch, pseudo_regnum); + intelgt_gdbarch_tdep *data = gdbarch_tdep<intelgt_gdbarch_tdep> (arch); + + if (strcmp (name, "ip") == 0) + { + int regsize = register_size (arch, pseudo_regnum); + /* Instruction pointer is stored in CR0.2. */ + gdb_assert (data->cr0_regnum != -1); + /* CR0 elements are 4 byte wide. */ + gdb_assert (regsize + 8 <= register_size (arch, data->cr0_regnum)); + + return pseudo_from_raw_part (next_frame, pseudo_regnum, + data->cr0_regnum, 8); + } + + return nullptr; +} + +/* Write the value of a pseudo-register REGNUM. */ + +static void +intelgt_pseudo_register_write (gdbarch *arch, + const frame_info_ptr &next_frame, + int pseudo_regnum, + gdb::array_view<const gdb_byte> buf) +{ + const char *name = intelgt_pseudo_register_name (arch, pseudo_regnum); + intelgt_gdbarch_tdep *data = gdbarch_tdep<intelgt_gdbarch_tdep> (arch); + + if (strcmp (name, "ip") == 0) + { + /* Instruction pointer is stored in CR0.2. */ + gdb_assert (data->cr0_regnum != -1); + int cr0_size = register_size (arch, data->cr0_regnum); + + /* CR0 elements are 4 byte wide. */ + int reg_size = register_size (arch, pseudo_regnum); + gdb_assert (reg_size + 8 <= cr0_size); + pseudo_to_raw_part (next_frame, buf, data->cr0_regnum, 8); + } + else + error ("Pseudo-register %s is read-only", name); +} + +/* Called by tdesc_use_registers each time a new regnum + is assigned. Used to track down assigned numbers for + any important regnums. */ + +static int +intelgt_unknown_register_cb (gdbarch *arch, tdesc_feature *feature, + const char *reg_name, int possible_regnum) +{ + intelgt_gdbarch_tdep *data = gdbarch_tdep<intelgt_gdbarch_tdep> (arch); + + /* First, check if this a beginning of a not yet tracked regset + assignment. */ + + for (int regset = 0; regset < intelgt::REGSET_COUNT; ++regset) + if (data->regset_ranges[regset].start == -1 + && feature->name == intelgt::DWARF_REGSET_FEATURES[regset]) + { + data->regset_ranges[regset].start = possible_regnum; + data->regset_ranges[regset].end + = feature->registers.size () + possible_regnum; + break; + } + + /* Second, check if it is any specific individual register that + needs to be tracked. */ + + if (strcmp ("r0", reg_name) == 0) + data->r0_regnum = possible_regnum; + else if (strcmp ("r26", reg_name) == 0) + data->retval_regnum = possible_regnum; + else if (strcmp ("cr0", reg_name) == 0) + data->cr0_regnum = possible_regnum; + else if (strcmp ("sr0", reg_name) == 0) + data->sr0_regnum = possible_regnum; + else if (strcmp ("isabase", reg_name) == 0) + data->isabase_regnum = possible_regnum; + else if (strcmp ("ce", reg_name) == 0) + data->ce_regnum = possible_regnum; + else if (strcmp ("genstbase", reg_name) == 0) + data->genstbase_regnum = possible_regnum; + else if (strcmp ("dbg0", reg_name) == 0) + data->dbg0_regnum = possible_regnum; + + return possible_regnum; +} + +/* Read the 'framedesc' user register, a structured alias + of the actual GRF. */ + +static value * +intelgt_value_of_framedesc_user_reg (const frame_info_ptr &frame, + const void *baton) +{ + gdbarch *arch = get_frame_arch (frame); + intelgt_gdbarch_tdep *tdep = gdbarch_tdep<intelgt_gdbarch_tdep> (arch); + type *framedesc_type = tdep->framedesc_type; + int framedesc_size = framedesc_type->length (); + int grf_num = tdep->framedesc_base_regnum (); + int grf_size = register_size (arch, grf_num); + gdb_assert (framedesc_size <= grf_size); + + value *grf + = value_of_register (grf_num, get_next_frame_sentinel_okay (frame)); + value *result = value::allocate (framedesc_type); + grf->contents_copy (result, 0, 0, framedesc_size); + return result; +} + +/* Architecture initialization. */ + +static gdbarch * +intelgt_gdbarch_init (gdbarch_info info, gdbarch_list *arches) +{ + /* If there is already a candidate, use it. */ + arches = gdbarch_list_lookup_by_info (arches, &info); + if (arches != nullptr) + return arches->gdbarch; + + const target_desc *tdesc = info.target_desc; + gdbarch_up gdbarch_u + (gdbarch_alloc (&info, + gdbarch_tdep_up (new intelgt_gdbarch_tdep))); + gdbarch *gdbarch = gdbarch_u.get (); + intelgt_gdbarch_tdep *data + = gdbarch_tdep<intelgt_gdbarch_tdep> (gdbarch); + + set_gdbarch_ptr_bit (gdbarch, 64); + set_gdbarch_addr_bit (gdbarch, 64); + set_gdbarch_long_bit (gdbarch, 64); + + /* Initialize register info. */ + set_gdbarch_num_regs (gdbarch, 0); + set_gdbarch_register_name (gdbarch, tdesc_register_name); + + if (tdesc_has_registers (tdesc)) + { + /* First assign register numbers to all registers. The + callback function will record any relevant metadata + about it in the intelgt_gdbarch_tdep instance to be + inspected after. */ + + tdesc_use_registers (gdbarch, tdesc, tdesc_data_alloc (), + intelgt_unknown_register_cb); + + /* Now check the collected metadata to ensure that all + mandatory pieces are in place. */ + + if (data->ce_regnum == -1) + error (_("Debugging requires $ce to be provided by the target")); + if (data->retval_regnum == -1) + error (_("Debugging requires return value register to be provided " + "by the target")); + if (data->cr0_regnum == -1) + error (_("Debugging requires control register to be provided by " + "the target")); + if (data->sr0_regnum == -1) + error (_("Debugging requires state register to be provided by " + "the target")); + + /* Add the 'framedesc' register as a structured user register. */ + user_reg_add (gdbarch, "framedesc", + intelgt_value_of_framedesc_user_reg, nullptr); + + /* Create the framedesc user register's type. */ + const struct builtin_type *bt = builtin_type (gdbarch); + type *framedesc = arch_composite_type (gdbarch, "framedesc", + TYPE_CODE_STRUCT); + append_composite_type_field (framedesc, "return_ip", bt->builtin_int32); + append_composite_type_field (framedesc, "return_callmask", + bt->builtin_int32); + append_composite_type_field (framedesc, "be_sp", bt->builtin_int32); + append_composite_type_field (framedesc, "be_fp", bt->builtin_int32); + append_composite_type_field (framedesc, "fe_fp", bt->builtin_int64); + append_composite_type_field (framedesc, "fe_sp", bt->builtin_int64); + data->framedesc_type = framedesc; + + /* Unconditionally enabled pseudo-registers: */ + data->enabled_pseudo_regs.push_back ("ip"); + + set_gdbarch_num_pseudo_regs (gdbarch, data->enabled_pseudo_regs.size ()); + set_gdbarch_pseudo_register_read_value + (gdbarch, intelgt_pseudo_register_read_value); + set_gdbarch_pseudo_register_write (gdbarch, + intelgt_pseudo_register_write); + set_tdesc_pseudo_register_type (gdbarch, intelgt_pseudo_register_type); + set_tdesc_pseudo_register_name (gdbarch, intelgt_pseudo_register_name); + set_gdbarch_read_pc (gdbarch, intelgt_read_pc); + set_gdbarch_write_pc (gdbarch, intelgt_write_pc); + } + + /* Populate gdbarch fields. */ + set_gdbarch_register_type (gdbarch, tdesc_register_type); + set_gdbarch_dwarf2_reg_to_regnum (gdbarch, intelgt_dwarf_reg_to_regnum); + + set_gdbarch_skip_prologue (gdbarch, intelgt_skip_prologue); + set_gdbarch_inner_than (gdbarch, core_addr_greaterthan); + set_gdbarch_unwind_pc (gdbarch, intelgt_unwind_pc); + dwarf2_append_unwinders (gdbarch); + frame_unwind_append_unwinder (gdbarch, &intelgt_frame_unwind); + + set_gdbarch_return_value_as_value (gdbarch, intelgt_return_value_as_value); + + /* We don't really have global breakpoints, but we have inferiors + without threads, so GDB thinks that nothing is executing and would + remove breakpoints, while we may get new threads at any time. */ + set_gdbarch_has_global_breakpoints (gdbarch, 1); + set_gdbarch_memory_insert_breakpoint (gdbarch, + intelgt_memory_insert_breakpoint); + set_gdbarch_memory_remove_breakpoint (gdbarch, + intelgt_memory_remove_breakpoint); + set_gdbarch_program_breakpoint_here_p (gdbarch, + intelgt_program_breakpoint_here_p); + set_gdbarch_breakpoint_kind_from_pc (gdbarch, + intelgt_breakpoint_kind_from_pc); + set_gdbarch_sw_breakpoint_from_kind (gdbarch, + intelgt_sw_breakpoint_from_kind); + dwarf2_frame_set_init_reg (gdbarch, intelgt_init_reg); + + /* Disassembly. */ + set_gdbarch_print_insn (gdbarch, intelgt_print_insn); + + return gdbarch_u.release (); +} + +static void +show_intelgt_debug (ui_file *file, int from_tty, + cmd_list_element *c, const char *value) +{ + gdb_printf (file, _("Intel(R) Graphics Technology debugging is " + "%s.\n"), value); +} + +INIT_GDB_FILE (intelgt_tdep) +{ + gdbarch_register (bfd_arch_intelgt, intelgt_gdbarch_init); + + /* Debugging flag. */ + add_setshow_boolean_cmd ("intelgt", class_maintenance, &intelgt_debug, + _("Set Intel(R) Graphics Technology debugging."), + _("Show Intel(R) Graphics Technology debugging."), + _("When on, Intel(R) Graphics Technology " + "debugging is enabled."), + nullptr, + show_intelgt_debug, + &setdebuglist, &showdebuglist); +} diff --git a/gdb/selftest-arch.c b/gdb/selftest-arch.c index f8e28f89eca..6c21bab967b 100644 --- a/gdb/selftest-arch.c +++ b/gdb/selftest-arch.c @@ -124,7 +124,8 @@ selftest_skip_warning_arch (struct gdbarch *gdbarch) that seems more trouble than it's worth. */ return (streq (name, "m68hc11") || streq (name, "m68hc12") - || streq (name, "m68hc12:HCS12")); + || streq (name, "m68hc12:HCS12") + || streq (name, "intelgt")); } } /* namespace selftests */ -- 2.43.0 ________________________________________ Intel Deutschland GmbH Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany Tel: +49 (89) 99143-0 www.intel.de Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman Chairperson of the Supervisory Board: Sonja Pierer Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928 This e-mail and any attachments may contain confidential material for the sole use of the intended recipient(s). Any review or distribution by others is strictly prohibited. If you are not the intended recipient, please contact the sender and delete all copies.