/* * gdbstub internals * * Copyright (c) 2022 Linaro Ltd * * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef GDBSTUB_INTERNALS_H #define GDBSTUB_INTERNALS_H #include "cpu.h" #include "utils/conn.h" #define MAX_PACKET_LENGTH 131104 enum { GDB_SIGNAL_0 = 0, GDB_SIGNAL_INT = 2, GDB_SIGNAL_QUIT = 3, GDB_SIGNAL_TRAP = 5, GDB_SIGNAL_ABRT = 6, GDB_SIGNAL_ALRM = 14, GDB_SIGNAL_STOP = 17, GDB_SIGNAL_IO = 23, GDB_SIGNAL_XCPU = 24, GDB_SIGNAL_UNKNOWN = 143 }; typedef struct GDBProcess { uint32_t pid; bool attached; char *target_xml; } GDBProcess; enum RSState { RS_INACTIVE, RS_IDLE, RS_GETLINE, RS_GETLINE_ESC, RS_GETLINE_RLE, RS_CHKSUM1, RS_CHKSUM2, }; typedef struct GDBFeature { const char *xmlname; const char *xml; const char *name; const char *const *regs; int num_regs; } GDBFeature; typedef struct GDBFeatureBuilder { GDBFeature *feature; GPtrArray *xml; GPtrArray *regs; int base_reg; } GDBFeatureBuilder; /* Get or set a register. Returns the size of the register. */ typedef int (*gdb_get_reg_cb)(CPUState *cpu, GByteArray *buf, int reg); typedef int (*gdb_set_reg_cb)(CPUState *cpu, uint8_t *buf, int reg); typedef struct GDBRegisterState { int base_reg; gdb_get_reg_cb get_reg; gdb_set_reg_cb set_reg; const GDBFeature *feature; } GDBRegisterState; typedef struct GDBState { bool init; /* have we been initialised? */ CPUState *c_cpu; CPUState *query_cpu; enum RSState state; /* parsing state */ char line_buf[MAX_PACKET_LENGTH]; int line_buf_index; int line_sum; /* running checksum */ int line_csum; /* checksum at the end of the packet */ GByteArray *last_packet; int signal; GDBProcess *processes; int process_num; GString *str_buf; GByteArray *mem_buf; int sstep_flags; int supported_sstep_flags; conn_t *conn; /* * Whether we are allowed to send a stop reply packet at this moment. * Must be set off after sending the stop reply itself. */ bool allow_stop_reply; } GDBState; /* lives in main gdbstub.c */ extern GDBState gdb_state; /* * Inline utility function, convert from int to hex and back */ static inline int fromhex(int v) { if (v >= '0' && v <= '9') { return v - '0'; } else if (v >= 'A' && v <= 'F') { return v - 'A' + 10; } else if (v >= 'a' && v <= 'f') { return v - 'a' + 10; } else { return 0; } } static inline int tohex(int v) { if (v < 10) { return v + '0'; } else { return v - 10 + 'a'; } } /* * Connection helpers for both system and user backends */ void gdb_put_strbuf(void); void gdb_hextomem(GByteArray *mem, const char *buf, int len); void gdb_read_byte(uint8_t ch); void gdb_init_cpu(); void gdb_register_coprocessor(CPUState *cpu, gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg, const GDBFeature *feature, int g_pos); void gdb_unregister_coprocessor_all(CPUState *cpu); /** * gdb_feature_builder_append_tag() - Append a tag. * @builder: The builder. * @format: The format of the tag. * @...: The values to be formatted. */ void G_GNUC_PRINTF(2, 3) gdb_feature_builder_append_tag(const GDBFeatureBuilder *builder, const char *format, ...); /** * gdb_feature_builder_append_reg() - Append a register. * @builder: The builder. * @name: The register's name; it must be unique within a CPU. * @bitsize: The register's size, in bits. * @regnum: The offset of the register's number in the feature. * @type: The type of the register. * @group: The register group to which this register belongs; it can be NULL. */ void gdb_feature_builder_append_reg(const GDBFeatureBuilder *builder, const char *name, int bitsize, int regnum, const char *type, const char *group); void gdb_feature_builder_end(const GDBFeatureBuilder *builder); const GDBFeature *gdb_find_static_feature(const char *xmlname); int gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int gdb_write_register(CPUState *cpu, uint8_t *mem_buf, int reg); typedef struct { int gdb_reg; const char *name; const char *feature_name; } GDBRegDesc; GArray *gdb_get_register_list(CPUState *cpu); /* in gdbstub-xml.c, generated by scripts/feature_to_c.py */ extern const GDBFeature gdb_static_features[]; void gdb_chr_receive(const uint8_t *buf, int size); bool gdb_got_immediate_ack(void); int gdb_put_packet(const char *buf); /* utility helpers */ GDBProcess *gdb_get_process(); void gdb_append_thread_id(CPUState *cpu, GString *buf); unsigned int gdb_get_max_cpus(void); /* both */ void gdb_create_default_process(GDBState *s); int gdb_signal_to_target(int sig); void gdb_continue(void); int gdb_continue_partial(char *newstates); void gdb_init_gdb_state(void); void gdb_handle_query_rcmd(GArray *params, void *ctx); /* system */ void gdb_handle_query_attached(GArray *params, void *ctx); /* both */ /* system only */ void gdb_handle_query_qemu_phy_mem_mode(GArray *params, void *ctx); void gdb_handle_set_qemu_phy_mem_mode(GArray *params, void *ctx); /* sycall handling */ void gdb_handle_file_io(GArray *params, void *user_ctx); void gdb_disable_syscalls(void); // TODO bool runstate_is_running(); void vm_stop(); void vm_start(); void cpu_synchronize_state(CPUState *cpu); void gdb_exit(int i); void gdb_qemu_exit(int i); CPUState *get_cpu(); void gdb_breakpoint_remove_all(CPUState *cs); bool gdb_supports_guest_debug(void); int gdb_breakpoint_insert(CPUState *cs, int type, vaddr addr, vaddr len); int gdb_breakpoint_remove(CPUState *cs, int type, vaddr addr, vaddr len); void gdb_put_buffer(const uint8_t *buf, int len); static inline void cpu_physical_memory_write(hwaddr addr, const void *buf, hwaddr len); static inline void cpu_physical_memory_read(hwaddr addr, const void *buf, hwaddr len); bool runstate_needs_reset(void); bool vm_prepare_start(bool step_requested); void qemu_clock_enable(); int gdb_target_memory_rw_debug(CPUState *cs, hwaddr addr, uint8_t *buf, int len, bool is_write); const char *get_feature_xml(const char *p, const char **newp, GDBProcess *process); const char *gdb_get_core_xml_file(CPUState *cpu); void cpu_register_gdb_commands(); int cpu_common_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int cpu_common_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); #endif /* GDBSTUB_INTERNALS_H */