diff --git a/.vscode/settings.json b/.vscode/settings.json index abb3706..c6e1ed1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -57,6 +57,8 @@ "internals.h": "c", "stdint.h": "c", "ctype.h": "c", - "gdbstub.h": "c" + "gdbstub.h": "c", + "pthread.h": "c", + "enums.h": "c" } } \ No newline at end of file diff --git a/backup/breakpoint.h b/backup/breakpoint.h deleted file mode 100644 index 95f0482..0000000 --- a/backup/breakpoint.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * QEMU breakpoint & watchpoint definitions - * - * Copyright (c) 2012 SUSE LINUX Products GmbH - * - * SPDX-License-Identifier: GPL-2.0-or-later - */ -#ifndef EXEC_BREAKPOINT_H -#define EXEC_BREAKPOINT_H - -#include "qemu/queue.h" -#include "exec/vaddr.h" -#include "exec/memattrs.h" - -typedef struct CPUBreakpoint { - vaddr pc; - int flags; /* BP_* */ - QTAILQ_ENTRY(CPUBreakpoint) entry; -} CPUBreakpoint; - -typedef struct CPUWatchpoint { - vaddr vaddr; - vaddr len; - vaddr hitaddr; - MemTxAttrs hitattrs; - int flags; /* BP_* */ - QTAILQ_ENTRY(CPUWatchpoint) entry; -} CPUWatchpoint; - -#endif diff --git a/backup/commands.h b/backup/commands.h deleted file mode 100644 index bff3674..0000000 --- a/backup/commands.h +++ /dev/null @@ -1,108 +0,0 @@ -#ifndef GDBSTUB_COMMANDS_H -#define GDBSTUB_COMMANDS_H - -typedef void (*GdbCmdHandler)(GArray *params, void *user_ctx); - -typedef enum GDBThreadIdKind { - GDB_ONE_THREAD = 0, - GDB_ALL_THREADS, /* One process, all threads */ - GDB_ALL_PROCESSES, - GDB_READ_THREAD_ERR -} GDBThreadIdKind; - -typedef union GdbCmdVariant { - const char *data; - uint8_t opcode; - unsigned long val_ul; - unsigned long long val_ull; - struct { - GDBThreadIdKind kind; - uint32_t pid; - uint32_t tid; - } thread_id; -} GdbCmdVariant; - -#define gdb_get_cmd_param(p, i) (&g_array_index(p, GdbCmdVariant, i)) - -/** - * typedef GdbCmdParseEntry - gdb command parser - * - * This structure keeps the information necessary to match a gdb command, - * parse it (extract its parameters), and select the correct handler for it. - * - * @cmd: The command to be matched - * @cmd_startswith: If true, @cmd is compared using startswith - * @schema: Each schema for the command parameter entry consists of 2 chars, - * the first char represents the parameter type handling the second char - * represents the delimiter for the next parameter. - * - * Currently supported schema types: - * 'l' -> unsigned long (stored in .val_ul) - * 'L' -> unsigned long long (stored in .val_ull) - * 's' -> string (stored in .data) - * 'o' -> single char (stored in .opcode) - * 't' -> thread id (stored in .thread_id) - * '?' -> skip according to delimiter - * - * Currently supported delimiters: - * '?' -> Stop at any delimiter (",;:=\0") - * '0' -> Stop at "\0" - * '.' -> Skip 1 char unless reached "\0" - * Any other value is treated as the delimiter value itself - * - * @allow_stop_reply: True iff the gdbstub can respond to this command with a - * "stop reply" packet. The list of commands that accept such response is - * defined at the GDB Remote Serial Protocol documentation. See: - * https://sourceware.org/gdb/onlinedocs/gdb/Stop-Reply-Packets.html#Stop-Reply-Packets. - * - * @need_cpu_context: Pass current CPU context to command handler via user_ctx. - */ -typedef struct GdbCmdParseEntry { - GdbCmdHandler handler; - const char *cmd; - bool cmd_startswith; - const char *schema; - bool allow_stop_reply; - bool need_cpu_context; -} GdbCmdParseEntry; - -/** - * gdb_put_packet() - put string into gdb server's buffer so it is sent - * to the client - */ -int gdb_put_packet(const char *buf); - -/** - * gdb_extend_query_table() - Extend query table. - * @table: GPtrArray of GdbCmdParseEntry entries. - * - * The caller should free @table afterwards - */ -void gdb_extend_query_table(GPtrArray *table); - -/** - * gdb_extend_set_table() - Extend set table. - * @table: GPtrArray of GdbCmdParseEntry entries. - * - * The caller should free @table afterwards - */ -void gdb_extend_set_table(GPtrArray *table); - -/** - * gdb_extend_qsupported_features() - Extend the qSupported features string. - * @qsupported_features: The additional qSupported feature(s) string. The string - * should start with a semicolon and, if there are more than one feature, the - * features should be separate by a semicolon. - * - * The caller should free @qsupported_features afterwards if - * dynamically allocated. - */ -void gdb_extend_qsupported_features(char *qsupported_features); - -/** - * Convert a hex string to bytes. Conversion is done per byte, so 2 hex digits - * are converted to 1 byte. Invalid hex digits are treated as 0 digits. - */ -void gdb_hextomem(GByteArray *mem, const char *buf, int len); - -#endif /* GDBSTUB_COMMANDS_H */ diff --git a/backup/cpu.h b/backup/cpu.h deleted file mode 100644 index fb788ca..0000000 --- a/backup/cpu.h +++ /dev/null @@ -1,1178 +0,0 @@ -/* - * QEMU CPU model - * - * Copyright (c) 2012 SUSE LINUX Products GmbH - * - * 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 - * - */ -#ifndef QEMU_CPU_H -#define QEMU_CPU_H - -#include "hw/qdev-core.h" -#include "disas/dis-asm.h" -#include "exec/breakpoint.h" -#include "exec/hwaddr.h" -#include "exec/vaddr.h" -#include "exec/memattrs.h" -#include "exec/mmu-access-type.h" -#include "exec/tlb-common.h" -#include "qapi/qapi-types-machine.h" -#include "qapi/qapi-types-run-state.h" -#include "qemu/bitmap.h" -#include "qemu/rcu_queue.h" -#include "qemu/queue.h" -#include "qemu/lockcnt.h" -#include "qemu/thread.h" -#include "qom/object.h" - -typedef int (*WriteCoreDumpFunction)(const void *buf, size_t size, - void *opaque); - -/** - * SECTION:cpu - * @section_id: QEMU-cpu - * @title: CPU Class - * @short_description: Base class for all CPUs - */ - -#define TYPE_CPU "cpu" - -/* Since this macro is used a lot in hot code paths and in conjunction with - * FooCPU *foo_env_get_cpu(), we deviate from usual QOM practice by using - * an unchecked cast. - */ -#define CPU(obj) ((CPUState *)(obj)) - -/* - * The class checkers bring in CPU_GET_CLASS() which is potentially - * expensive given the eventual call to - * object_class_dynamic_cast_assert(). Because of this the CPUState - * has a cached value for the class in cs->cc which is set up in - * cpu_exec_realizefn() for use in hot code paths. - */ -typedef struct CPUClass CPUClass; -DECLARE_CLASS_CHECKERS(CPUClass, CPU, - TYPE_CPU) - -/** - * OBJECT_DECLARE_CPU_TYPE: - * @CpuInstanceType: instance struct name - * @CpuClassType: class struct name - * @CPU_MODULE_OBJ_NAME: the CPU name in uppercase with underscore separators - * - * This macro is typically used in "cpu-qom.h" header file, and will: - * - * - create the typedefs for the CPU object and class structs - * - register the type for use with g_autoptr - * - provide three standard type cast functions - * - * The object struct and class struct need to be declared manually. - */ -#define OBJECT_DECLARE_CPU_TYPE(CpuInstanceType, CpuClassType, CPU_MODULE_OBJ_NAME) \ - typedef struct ArchCPU CpuInstanceType; \ - OBJECT_DECLARE_TYPE(ArchCPU, CpuClassType, CPU_MODULE_OBJ_NAME); - -typedef struct CPUWatchpoint CPUWatchpoint; - -/* see physmem.c */ -struct CPUAddressSpace; - -/* see accel/tcg/tb-jmp-cache.h */ -struct CPUJumpCache; - -/* see accel-cpu.h */ -struct AccelCPUClass; - -/* see sysemu-cpu-ops.h */ -struct SysemuCPUOps; - -/** - * CPUClass: - * @class_by_name: Callback to map -cpu command line model name to an - * instantiatable CPU type. - * @list_cpus: list available CPU models and flags. - * @parse_features: Callback to parse command line arguments. - * @reset_dump_flags: #CPUDumpFlags to use for reset logging. - * @memory_rw_debug: Callback for GDB memory access. - * @dump_state: Callback for dumping state. - * @query_cpu_fast: - * Fill in target specific information for the "query-cpus-fast" - * QAPI call. - * @get_arch_id: Callback for getting architecture-dependent CPU ID. - * @set_pc: Callback for setting the Program Counter register. This - * should have the semantics used by the target architecture when - * setting the PC from a source such as an ELF file entry point; - * for example on Arm it will also set the Thumb mode bit based - * on the least significant bit of the new PC value. - * If the target behaviour here is anything other than "set - * the PC register to the value passed in" then the target must - * also implement the synchronize_from_tb hook. - * @get_pc: Callback for getting the Program Counter register. - * As above, with the semantics of the target architecture. - * @gdb_read_register: Callback for letting GDB read a register. - * No more than @gdb_num_core_regs registers can be read. - * @gdb_write_register: Callback for letting GDB write a register. - * No more than @gdb_num_core_regs registers can be written. - * @gdb_adjust_breakpoint: Callback for adjusting the address of a - * breakpoint. Used by AVR to handle a gdb mis-feature with - * its Harvard architecture split code and data. - * @gdb_num_core_regs: Number of core registers accessible to GDB or 0 to infer - * from @gdb_core_xml_file. - * @gdb_core_xml_file: File name for core registers GDB XML description. - * @gdb_get_core_xml_file: Optional callback that returns the file name for - * the core registers GDB XML description. The returned value is expected to - * be a simple constant string: the caller will not g_free() it. If this - * is NULL then @gdb_core_xml_file will be used instead. - * @gdb_stop_before_watchpoint: Indicates whether GDB expects the CPU to stop - * before the insn which triggers a watchpoint rather than after it. - * @gdb_arch_name: Optional callback that returns the architecture name known - * to GDB. The returned value is expected to be a simple constant string: - * the caller will not g_free() it. - * @disas_set_info: Setup architecture specific components of disassembly info - * @adjust_watchpoint_address: Perform a target-specific adjustment to an - * address before attempting to match it against watchpoints. - * @deprecation_note: If this CPUClass is deprecated, this field provides - * related information. - * - * Represents a CPU family or model. - */ -struct CPUClass { - /*< private >*/ - DeviceClass parent_class; - /*< public >*/ - - ObjectClass *(*class_by_name)(const char *cpu_model); - void (*list_cpus)(void); - void (*parse_features)(const char *typename, char *str, Error **errp); - - int (*memory_rw_debug)(CPUState *cpu, vaddr addr, - uint8_t *buf, size_t len, bool is_write); - void (*dump_state)(CPUState *cpu, FILE *, int flags); - void (*query_cpu_fast)(CPUState *cpu, CpuInfoFast *value); - int64_t (*get_arch_id)(CPUState *cpu); - void (*set_pc)(CPUState *cpu, vaddr value); - vaddr (*get_pc)(CPUState *cpu); - int (*gdb_read_register)(CPUState *cpu, GByteArray *buf, int reg); - int (*gdb_write_register)(CPUState *cpu, uint8_t *buf, int reg); - vaddr (*gdb_adjust_breakpoint)(CPUState *cpu, vaddr addr); - - const char *gdb_core_xml_file; - const gchar * (*gdb_arch_name)(CPUState *cpu); - const char * (*gdb_get_core_xml_file)(CPUState *cpu); - - void (*disas_set_info)(CPUState *cpu, disassemble_info *info); - - const char *deprecation_note; - struct AccelCPUClass *accel_cpu; - - /* when system emulation is not available, this pointer is NULL */ - const struct SysemuCPUOps *sysemu_ops; - - /* when TCG is not available, this pointer is NULL */ - const TCGCPUOps *tcg_ops; - - /* - * if not NULL, this is called in order for the CPUClass to initialize - * class data that depends on the accelerator, see accel/accel-common.c. - */ - void (*init_accel_cpu)(struct AccelCPUClass *accel_cpu, CPUClass *cc); - - /* - * Keep non-pointer data at the end to minimize holes. - */ - int reset_dump_flags; - int gdb_num_core_regs; - bool gdb_stop_before_watchpoint; -}; - -/* - * Fix the number of mmu modes to 16, which is also the maximum - * supported by the softmmu tlb api. - */ -#define NB_MMU_MODES 16 - -/* Use a fully associative victim tlb of 8 entries. */ -#define CPU_VTLB_SIZE 8 - -/* - * The full TLB entry, which is not accessed by generated TCG code, - * so the layout is not as critical as that of CPUTLBEntry. This is - * also why we don't want to combine the two structs. - */ -struct CPUTLBEntryFull { - /* - * @xlat_section contains: - * - in the lower TARGET_PAGE_BITS, a physical section number - * - with the lower TARGET_PAGE_BITS masked off, an offset which - * must be added to the virtual address to obtain: - * + the ram_addr_t of the target RAM (if the physical section - * number is PHYS_SECTION_NOTDIRTY or PHYS_SECTION_ROM) - * + the offset within the target MemoryRegion (otherwise) - */ - hwaddr xlat_section; - - /* - * @phys_addr contains the physical address in the address space - * given by cpu_asidx_from_attrs(cpu, @attrs). - */ - hwaddr phys_addr; - - /* @attrs contains the memory transaction attributes for the page. */ - MemTxAttrs attrs; - - /* @prot contains the complete protections for the page. */ - uint8_t prot; - - /* @lg_page_size contains the log2 of the page size. */ - uint8_t lg_page_size; - - /* Additional tlb flags requested by tlb_fill. */ - uint8_t tlb_fill_flags; - - /* - * Additional tlb flags for use by the slow path. If non-zero, - * the corresponding CPUTLBEntry comparator must have TLB_FORCE_SLOW. - */ - uint8_t slow_flags[MMU_ACCESS_COUNT]; - - /* - * Allow target-specific additions to this structure. - * This may be used to cache items from the guest cpu - * page tables for later use by the implementation. - */ - union { - /* - * Cache the attrs and shareability fields from the page table entry. - * - * For ARMMMUIdx_Stage2*, pte_attrs is the S2 descriptor bits [5:2]. - * Otherwise, pte_attrs is the same as the MAIR_EL1 8-bit format. - * For shareability and guarded, as in the SH and GP fields respectively - * of the VMSAv8-64 PTEs. - */ - struct { - uint8_t pte_attrs; - uint8_t shareability; - bool guarded; - } arm; - } extra; -}; - -/* - * Data elements that are per MMU mode, minus the bits accessed by - * the TCG fast path. - */ -typedef struct CPUTLBDesc { - /* - * Describe a region covering all of the large pages allocated - * into the tlb. When any page within this region is flushed, - * we must flush the entire tlb. The region is matched if - * (addr & large_page_mask) == large_page_addr. - */ - vaddr large_page_addr; - vaddr large_page_mask; - /* host time (in ns) at the beginning of the time window */ - int64_t window_begin_ns; - /* maximum number of entries observed in the window */ - size_t window_max_entries; - size_t n_used_entries; - /* The next index to use in the tlb victim table. */ - size_t vindex; - /* The tlb victim table, in two parts. */ - CPUTLBEntry vtable[CPU_VTLB_SIZE]; - CPUTLBEntryFull vfulltlb[CPU_VTLB_SIZE]; - CPUTLBEntryFull *fulltlb; -} CPUTLBDesc; - -/* - * Data elements that are shared between all MMU modes. - */ -typedef struct CPUTLBCommon { - /* Serialize updates to f.table and d.vtable, and others as noted. */ - QemuSpin lock; - /* - * Within dirty, for each bit N, modifications have been made to - * mmu_idx N since the last time that mmu_idx was flushed. - * Protected by tlb_c.lock. - */ - uint16_t dirty; - /* - * Statistics. These are not lock protected, but are read and - * written atomically. This allows the monitor to print a snapshot - * of the stats without interfering with the cpu. - */ - size_t full_flush_count; - size_t part_flush_count; - size_t elide_flush_count; -} CPUTLBCommon; - -/* - * The entire softmmu tlb, for all MMU modes. - * The meaning of each of the MMU modes is defined in the target code. - * Since this is placed within CPUNegativeOffsetState, the smallest - * negative offsets are at the end of the struct. - */ -typedef struct CPUTLB { -#ifdef CONFIG_TCG - CPUTLBCommon c; - CPUTLBDesc d[NB_MMU_MODES]; - CPUTLBDescFast f[NB_MMU_MODES]; -#endif -} CPUTLB; - -/* - * Low 16 bits: number of cycles left, used only in icount mode. - * High 16 bits: Set to -1 to force TCG to stop executing linked TBs - * for this CPU and return to its top level loop (even in non-icount mode). - * This allows a single read-compare-cbranch-write sequence to test - * for both decrementer underflow and exceptions. - */ -typedef union IcountDecr { - uint32_t u32; - struct { -#if HOST_BIG_ENDIAN - uint16_t high; - uint16_t low; -#else - uint16_t low; - uint16_t high; -#endif - } u16; -} IcountDecr; - -/** - * CPUNegativeOffsetState: Elements of CPUState most efficiently accessed - * from CPUArchState, via small negative offsets. - * @can_do_io: True if memory-mapped IO is allowed. - * @plugin_mem_cbs: active plugin memory callbacks - * @plugin_mem_value_low: 64 lower bits of latest accessed mem value. - * @plugin_mem_value_high: 64 higher bits of latest accessed mem value. - */ -typedef struct CPUNegativeOffsetState { - CPUTLB tlb; -#ifdef CONFIG_PLUGIN - /* - * The callback pointer are accessed via TCG (see gen_empty_mem_helper). - */ - GArray *plugin_mem_cbs; - uint64_t plugin_mem_value_low; - uint64_t plugin_mem_value_high; - int32_t plugin_cb_flags; -#endif - IcountDecr icount_decr; - bool can_do_io; -} CPUNegativeOffsetState; - -struct KVMState; -struct kvm_run; - -/* work queue */ - -/* The union type allows passing of 64 bit target pointers on 32 bit - * hosts in a single parameter - */ -typedef union { - int host_int; - unsigned long host_ulong; - void *host_ptr; - vaddr target_ptr; -} run_on_cpu_data; - -#define RUN_ON_CPU_HOST_PTR(p) ((run_on_cpu_data){.host_ptr = (p)}) -#define RUN_ON_CPU_HOST_INT(i) ((run_on_cpu_data){.host_int = (i)}) -#define RUN_ON_CPU_HOST_ULONG(ul) ((run_on_cpu_data){.host_ulong = (ul)}) -#define RUN_ON_CPU_TARGET_PTR(v) ((run_on_cpu_data){.target_ptr = (v)}) -#define RUN_ON_CPU_NULL RUN_ON_CPU_HOST_PTR(NULL) - -typedef void (*run_on_cpu_func)(CPUState *cpu, run_on_cpu_data data); - -struct qemu_work_item; - -#define CPU_UNSET_NUMA_NODE_ID -1 - -/** - * struct CPUState - common state of one CPU core or thread. - * - * @cpu_index: CPU index (informative). - * @cluster_index: Identifies which cluster this CPU is in. - * For boards which don't define clusters or for "loose" CPUs not assigned - * to a cluster this will be UNASSIGNED_CLUSTER_INDEX; otherwise it will - * be the same as the cluster-id property of the CPU object's TYPE_CPU_CLUSTER - * QOM parent. - * Under TCG this value is propagated to @tcg_cflags. - * See TranslationBlock::TCG CF_CLUSTER_MASK. - * @tcg_cflags: Pre-computed cflags for this cpu. - * @nr_threads: Number of threads within this CPU core. - * @thread: Host thread details, only live once @created is #true - * @sem: WIN32 only semaphore used only for qtest - * @thread_id: native thread id of vCPU, only live once @created is #true - * @running: #true if CPU is currently running (lockless). - * @has_waiter: #true if a CPU is currently waiting for the cpu_exec_end; - * valid under cpu_list_lock. - * @created: Indicates whether the CPU thread has been successfully created. - * @halt_cond: condition variable sleeping threads can wait on. - * @exit_request: Another thread requests the CPU to call qemu_process_cpu_events(). - * Should be read only by CPU thread with load-acquire, to synchronize with - * other threads' store-release operation. - * - * In some cases, accelerator-specific code will write exit_request from - * within the same thread, to "bump" the effect of qemu_cpu_kick() to - * the one provided by cpu_exit(), especially when processing interrupt - * flags. In this case, the write and read happen in the same thread - * and the write therefore can use qemu_atomic_set(). - * @interrupt_request: Indicates a pending interrupt request. - * Only used by system emulation. - * @halted: Nonzero if the CPU is in suspended state. - * @stop: Indicates a pending stop request. - * @stopped: Indicates the CPU has been artificially stopped. - * @unplug: Indicates a pending CPU unplug request. - * @crash_occurred: Indicates the OS reported a crash (panic) for this CPU - * @singlestep_enabled: Flags for single-stepping. - * @icount_extra: Instructions until next timer event. - * @cpu_ases: Pointer to array of CPUAddressSpaces (which define the - * AddressSpaces this CPU has) - * @num_ases: number of CPUAddressSpaces in @cpu_ases - * @as: Pointer to the first AddressSpace, for the convenience of targets which - * only have a single AddressSpace - * @gdb_regs: Additional GDB registers. - * @gdb_num_regs: Number of total registers accessible to GDB. - * @gdb_num_g_regs: Number of registers in GDB 'g' packets. - * @node: QTAILQ of CPUs sharing TB cache. - * @opaque: User data. - * @mem_io_pc: Host Program Counter at which the memory was accessed. - * @accel: Pointer to accelerator specific state. - * @vcpu_dirty: Hardware accelerator is not synchronized with QEMU state - * @kvm_fd: vCPU file descriptor for KVM. - * @work_mutex: Lock to prevent multiple access to @work_list. - * @work_list: List of pending asynchronous work. - * @plugin_state: per-CPU plugin state - * @ignore_memory_transaction_failures: Cached copy of the MachineState - * flag of the same name: allows the board to suppress calling of the - * CPU do_transaction_failed hook function. - * @kvm_dirty_gfns: Points to the KVM dirty ring for this CPU when KVM dirty - * ring is enabled. - * @kvm_fetch_index: Keeps the index that we last fetched from the per-vCPU - * dirty ring structure. - * - * @neg_align: The CPUState is the common part of a concrete ArchCPU - * which is allocated when an individual CPU instance is created. As - * such care is taken is ensure there is no gap between between - * CPUState and CPUArchState within ArchCPU. - * - * @neg: The architectural register state ("cpu_env") immediately follows - * CPUState in ArchCPU and is passed to TCG code. The @neg structure holds - * some common TCG CPU variables which are accessed with a negative offset - * from cpu_env. - */ -struct CPUState { - /*< private >*/ - DeviceState parent_obj; - /* cache to avoid expensive CPU_GET_CLASS */ - CPUClass *cc; - /*< public >*/ - - int nr_threads; - - struct QemuThread *thread; -#ifdef _WIN32 - QemuSemaphore sem; -#endif - int thread_id; - bool running, has_waiter; - struct QemuCond *halt_cond; - bool thread_kicked; - bool created; - bool stop; - bool stopped; - - /* Should CPU start in powered-off state? */ - bool start_powered_off; - - bool unplug; - bool crash_occurred; - bool exit_request; - int exclusive_context_count; - uint32_t cflags_next_tb; - uint32_t interrupt_request; - int singlestep_enabled; - int64_t icount_budget; - int64_t icount_extra; - uint64_t random_seed; - sigjmp_buf jmp_env; - - QemuMutex work_mutex; - QSIMPLEQ_HEAD(, qemu_work_item) work_list; - - struct CPUAddressSpace *cpu_ases; - int cpu_ases_count; - int num_ases; - AddressSpace *as; - MemoryRegion *memory; - - struct CPUJumpCache *tb_jmp_cache; - - GArray *gdb_regs; - int gdb_num_regs; - int gdb_num_g_regs; - QTAILQ_ENTRY(CPUState) node; - - /* ice debug support */ - QTAILQ_HEAD(, CPUBreakpoint) breakpoints; - - QTAILQ_HEAD(, CPUWatchpoint) watchpoints; - CPUWatchpoint *watchpoint_hit; - - void *opaque; - - /* In order to avoid passing too many arguments to the MMIO helpers, - * we store some rarely used information in the CPU context. - */ - uintptr_t mem_io_pc; - - /* Only used in KVM */ - int kvm_fd; - struct KVMState *kvm_state; - struct kvm_run *kvm_run; - struct kvm_dirty_gfn *kvm_dirty_gfns; - uint32_t kvm_fetch_index; - uint64_t dirty_pages; - int kvm_vcpu_stats_fd; - - /* Use by accel-block: CPU is executing an ioctl() */ - QemuLockCnt in_ioctl_lock; - -#ifdef CONFIG_PLUGIN - CPUPluginState *plugin_state; -#endif - - /* TODO Move common fields from CPUArchState here. */ - int cpu_index; - int cluster_index; - uint32_t tcg_cflags; - uint32_t halted; - int32_t exception_index; - - bool vcpu_dirty; - AccelCPUState *accel; - - /* Used to keep track of an outstanding cpu throttle thread for migration - * autoconverge - */ - bool throttle_thread_scheduled; - - /* - * Sleep throttle_us_per_full microseconds once dirty ring is full - * if dirty page rate limit is enabled. - */ - int64_t throttle_us_per_full; - - bool ignore_memory_transaction_failures; - - /* Used for user-only emulation of prctl(PR_SET_UNALIGN). */ - bool prctl_unalign_sigbus; - - /* track IOMMUs whose translations we've cached in the TCG TLB */ - GArray *iommu_notifiers; - - /* - * MUST BE LAST in order to minimize the displacement to CPUArchState. - */ - char neg_align[-sizeof(CPUNegativeOffsetState) % 16] QEMU_ALIGNED(16); - CPUNegativeOffsetState neg; -}; - -/* Validate placement of CPUNegativeOffsetState. */ -QEMU_BUILD_BUG_ON(offsetof(CPUState, neg) != - sizeof(CPUState) - sizeof(CPUNegativeOffsetState)); - -static inline CPUArchState *cpu_env(CPUState *cpu) -{ - /* We validate that CPUArchState follows CPUState in cpu-target.c */ - return (CPUArchState *)(cpu + 1); -} - -typedef QTAILQ_HEAD(CPUTailQ, CPUState) CPUTailQ; -extern CPUTailQ cpus_queue; - -#define first_cpu QTAILQ_FIRST_RCU(&cpus_queue) -#define CPU_NEXT(cpu) QTAILQ_NEXT_RCU(cpu, node) -#define CPU_FOREACH(cpu) QTAILQ_FOREACH_RCU(cpu, &cpus_queue, node) -#define CPU_FOREACH_SAFE(cpu, next_cpu) \ - QTAILQ_FOREACH_SAFE_RCU(cpu, &cpus_queue, node, next_cpu) - -extern __thread CPUState *current_cpu; - -/** - * cpu_paging_enabled: - * @cpu: The CPU whose state is to be inspected. - * - * Returns: %true if paging is enabled, %false otherwise. - */ -bool cpu_paging_enabled(const CPUState *cpu); - -/** - * cpu_get_memory_mapping: - * @cpu: The CPU whose memory mappings are to be obtained. - * @list: Where to write the memory mappings to. - * @errp: Pointer for reporting an #Error. - * - * Returns: %true on success, %false otherwise. - */ -bool cpu_get_memory_mapping(CPUState *cpu, MemoryMappingList *list, - Error **errp); - -/** - * cpu_write_elf64_note: - * @f: pointer to a function that writes memory to a file - * @cpu: The CPU whose memory is to be dumped - * @cpuid: ID number of the CPU - * @opaque: pointer to the CPUState struct - */ -int cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cpu, - int cpuid, void *opaque); - -/** - * cpu_write_elf64_qemunote: - * @f: pointer to a function that writes memory to a file - * @cpu: The CPU whose memory is to be dumped - * @cpuid: ID number of the CPU - * @opaque: pointer to the CPUState struct - */ -int cpu_write_elf64_qemunote(WriteCoreDumpFunction f, CPUState *cpu, - void *opaque); - -/** - * cpu_write_elf32_note: - * @f: pointer to a function that writes memory to a file - * @cpu: The CPU whose memory is to be dumped - * @cpuid: ID number of the CPU - * @opaque: pointer to the CPUState struct - */ -int cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cpu, - int cpuid, void *opaque); - -/** - * cpu_write_elf32_qemunote: - * @f: pointer to a function that writes memory to a file - * @cpu: The CPU whose memory is to be dumped - * @cpuid: ID number of the CPU - * @opaque: pointer to the CPUState struct - */ -int cpu_write_elf32_qemunote(WriteCoreDumpFunction f, CPUState *cpu, - void *opaque); - -/** - * cpu_get_crash_info: - * @cpu: The CPU to get crash information for - * - * Gets the previously saved crash information. - * Caller is responsible for freeing the data. - */ -GuestPanicInformation *cpu_get_crash_info(CPUState *cpu); - -/** - * CPUDumpFlags: - * @CPU_DUMP_CODE: - * @CPU_DUMP_FPU: dump FPU register state, not just integer - * @CPU_DUMP_CCOP: dump info about TCG QEMU's condition code optimization state - * @CPU_DUMP_VPU: dump VPU registers - */ -enum CPUDumpFlags { - CPU_DUMP_CODE = 0x00010000, - CPU_DUMP_FPU = 0x00020000, - CPU_DUMP_CCOP = 0x00040000, - CPU_DUMP_VPU = 0x00080000, -}; - -/** - * cpu_dump_state: - * @cpu: The CPU whose state is to be dumped. - * @f: If non-null, dump to this stream, else to current print sink. - * - * Dumps CPU state. - */ -void cpu_dump_state(CPUState *cpu, FILE *f, int flags); - -/** - * cpu_get_phys_page_attrs_debug: - * @cpu: The CPU to obtain the physical page address for. - * @addr: The virtual address. - * @attrs: Updated on return with the memory transaction attributes to use - * for this access. - * - * Obtains the physical page corresponding to a virtual one, together - * with the corresponding memory transaction attributes to use for the access. - * Use it only for debugging because no protection checks are done. - * - * Returns: Corresponding physical page address or -1 if no page found. - */ -hwaddr cpu_get_phys_page_attrs_debug(CPUState *cpu, vaddr addr, - MemTxAttrs *attrs); - -/** - * cpu_get_phys_page_debug: - * @cpu: The CPU to obtain the physical page address for. - * @addr: The virtual address. - * - * Obtains the physical page corresponding to a virtual one. - * Use it only for debugging because no protection checks are done. - * - * Returns: Corresponding physical page address or -1 if no page found. - */ -hwaddr cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); - -/** cpu_asidx_from_attrs: - * @cpu: CPU - * @attrs: memory transaction attributes - * - * Returns the address space index specifying the CPU AddressSpace - * to use for a memory access with the given transaction attributes. - */ -int cpu_asidx_from_attrs(CPUState *cpu, MemTxAttrs attrs); - -/** - * cpu_virtio_is_big_endian: - * @cpu: CPU - - * Returns %true if a CPU which supports runtime configurable endianness - * is currently big-endian. - */ -bool cpu_virtio_is_big_endian(CPUState *cpu); - -/** - * cpu_has_work: - * @cpu: The vCPU to check. - * - * Checks whether the CPU has work to do. - * - * Returns: %true if the CPU has work, %false otherwise. - */ -bool cpu_has_work(CPUState *cpu); - -/** - * cpu_list_add: - * @cpu: The CPU to be added to the list of CPUs. - */ -void cpu_list_add(CPUState *cpu); - -/** - * cpu_list_remove: - * @cpu: The CPU to be removed from the list of CPUs. - */ -void cpu_list_remove(CPUState *cpu); - -/** - * cpu_reset: - * @cpu: The CPU whose state is to be reset. - */ -void cpu_reset(CPUState *cpu); - -/** - * cpu_class_by_name: - * @typename: The CPU base type. - * @cpu_model: The model string without any parameters. - * - * Looks up a concrete CPU #ObjectClass matching name @cpu_model. - * - * Returns: A concrete #CPUClass or %NULL if no matching class is found - * or if the matching class is abstract. - */ -ObjectClass *cpu_class_by_name(const char *typename, const char *cpu_model); - -/** - * cpu_model_from_type: - * @typename: The CPU type name - * - * Extract the CPU model name from the CPU type name. The - * CPU type name is either the combination of the CPU model - * name and suffix, or same to the CPU model name. - * - * Returns: CPU model name or NULL if the CPU class doesn't exist - * The user should g_free() the string once no longer needed. - */ -char *cpu_model_from_type(const char *typename); - -/** - * cpu_create: - * @typename: The CPU type. - * - * Instantiates a CPU and realizes the CPU. - * - * Returns: A #CPUState or %NULL if an error occurred. - */ -CPUState *cpu_create(const char *typename); - -/** - * parse_cpu_option: - * @cpu_option: The -cpu option including optional parameters. - * - * processes optional parameters and registers them as global properties - * - * Returns: type of CPU to create or prints error and terminates process - * if an error occurred. - */ -const char *parse_cpu_option(const char *cpu_option); - -/** - * qemu_cpu_is_self: - * @cpu: The vCPU to check against. - * - * Checks whether the caller is executing on the vCPU thread. - * - * Returns: %true if called from @cpu's thread, %false otherwise. - */ -bool qemu_cpu_is_self(CPUState *cpu); - -/** - * qemu_cpu_kick: - * @cpu: The vCPU to kick. - * - * Kicks @cpu's thread to exit the accelerator. For accelerators that - * can do that, the target vCPU thread will try not to take the BQL. - */ -void qemu_cpu_kick(CPUState *cpu); - -/** - * cpu_is_stopped: - * @cpu: The CPU to check. - * - * Checks whether the CPU is stopped. - * - * Returns: %true if run state is not running or if artificially stopped; - * %false otherwise. - */ -bool cpu_is_stopped(CPUState *cpu); - -/** - * do_run_on_cpu: - * @cpu: The vCPU to run on. - * @func: The function to be executed. - * @data: Data to pass to the function. - * @mutex: Mutex to release while waiting for @func to run. - * - * Used internally in the implementation of run_on_cpu. - */ -void do_run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data, - QemuMutex *mutex); - -/** - * run_on_cpu: - * @cpu: The vCPU to run on. - * @func: The function to be executed. - * @data: Data to pass to the function. - * - * Schedules the function @func for execution on the vCPU @cpu. - */ -void run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data); - -/** - * async_run_on_cpu: - * @cpu: The vCPU to run on. - * @func: The function to be executed. - * @data: Data to pass to the function. - * - * Schedules the function @func for execution on the vCPU @cpu asynchronously. - */ -void async_run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data); - -/** - * async_safe_run_on_cpu: - * @cpu: The vCPU to run on. - * @func: The function to be executed. - * @data: Data to pass to the function. - * - * Schedules the function @func for execution on the vCPU @cpu asynchronously, - * while all other vCPUs are sleeping. - * - * Unlike run_on_cpu and async_run_on_cpu, the function is run outside the - * BQL. - */ -void async_safe_run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data); - -/** - * cpu_in_exclusive_context() - * @cpu: The vCPU to check - * - * Returns true if @cpu is an exclusive context, for example running - * something which has previously been queued via async_safe_run_on_cpu(). - */ -static inline bool cpu_in_exclusive_context(const CPUState *cpu) -{ - return cpu->exclusive_context_count; -} - -/** - * qemu_get_cpu: - * @index: The CPUState@cpu_index value of the CPU to obtain. - * - * Gets a CPU matching @index. - * - * Returns: The CPU or %NULL if there is no matching CPU. - */ -CPUState *qemu_get_cpu(int index); - -/** - * cpu_exists: - * @id: Guest-exposed CPU ID to lookup. - * - * Search for CPU with specified ID. - * - * Returns: %true - CPU is found, %false - CPU isn't found. - */ -bool cpu_exists(int64_t id); - -/** - * cpu_by_arch_id: - * @id: Guest-exposed CPU ID of the CPU to obtain. - * - * Get a CPU with matching @id. - * - * Returns: The CPU or %NULL if there is no matching CPU. - */ -CPUState *cpu_by_arch_id(int64_t id); - -/** - * cpu_interrupt: - * @cpu: The CPU to set an interrupt on. - * @mask: The interrupts to set. - * - * Invokes the interrupt handler. - */ - -void cpu_interrupt(CPUState *cpu, int mask); - -/** - * cpu_test_interrupt: - * @cpu: The CPU to check interrupt(s) on. - * @mask: The interrupts to check. - * - * Checks if any of interrupts in @mask are pending on @cpu. - */ -static inline bool cpu_test_interrupt(CPUState *cpu, int mask) -{ - return qatomic_load_acquire(&cpu->interrupt_request) & mask; -} - -/** - * cpu_set_interrupt: - * @cpu: The CPU to set pending interrupt(s) on. - * @mask: The interrupts to set. - * - * Sets interrupts in @mask as pending on @cpu. Unlike @cpu_interrupt, - * this does not kick the vCPU. - */ -void cpu_set_interrupt(CPUState *cpu, int mask); - -/** - * cpu_set_pc: - * @cpu: The CPU to set the program counter for. - * @addr: Program counter value. - * - * Sets the program counter for a CPU. - */ -static inline void cpu_set_pc(CPUState *cpu, vaddr addr) -{ - cpu->cc->set_pc(cpu, addr); -} - -/** - * cpu_reset_interrupt: - * @cpu: The CPU to clear the interrupt on. - * @mask: The interrupt mask to clear. - * - * Resets interrupts on the vCPU @cpu. - */ -void cpu_reset_interrupt(CPUState *cpu, int mask); - -/** - * cpu_exit: - * @cpu: The CPU to exit. - * - * Requests the CPU @cpu to exit execution. - */ -void cpu_exit(CPUState *cpu); - -/** - * cpu_pause: - * @cpu: The CPU to pause. - * - * Pauses CPU, i.e. puts CPU into stopped state. - */ -void cpu_pause(CPUState *cpu); - -/** - * cpu_resume: - * @cpu: The CPU to resume. - * - * Resumes CPU, i.e. puts CPU into runnable state. - */ -void cpu_resume(CPUState *cpu); - -/** - * cpu_remove_sync: - * @cpu: The CPU to remove. - * - * Requests the CPU to be removed and waits till it is removed. - */ -void cpu_remove_sync(CPUState *cpu); - -/** - * free_queued_cpu_work() - free all items on CPU work queue - * @cpu: The CPU which work queue to free. - */ -void free_queued_cpu_work(CPUState *cpu); - -/** - * process_queued_cpu_work() - process all items on CPU work queue - * @cpu: The CPU which work queue to process. - */ -void process_queued_cpu_work(CPUState *cpu); - -/** - * cpu_exec_start: - * @cpu: The CPU for the current thread. - * - * Record that a CPU has started execution and can be interrupted with - * cpu_exit. - */ -void cpu_exec_start(CPUState *cpu); - -/** - * cpu_exec_end: - * @cpu: The CPU for the current thread. - * - * Record that a CPU has stopped execution and exclusive sections - * can be executed without interrupting it. - */ -void cpu_exec_end(CPUState *cpu); - -/** - * start_exclusive: - * - * Wait for a concurrent exclusive section to end, and then start - * a section of work that is run while other CPUs are not running - * between cpu_exec_start and cpu_exec_end. CPUs that are running - * cpu_exec are exited immediately. CPUs that call cpu_exec_start - * during the exclusive section go to sleep until this CPU calls - * end_exclusive. - */ -void start_exclusive(void); - -/** - * end_exclusive: - * - * Concludes an exclusive execution section started by start_exclusive. - */ -void end_exclusive(void); - -/** - * qemu_init_vcpu: - * @cpu: The vCPU to initialize. - * - * Initializes a vCPU. - */ -void qemu_init_vcpu(CPUState *cpu); - -#define SSTEP_ENABLE 0x1 /* Enable simulated HW single stepping */ -#define SSTEP_NOIRQ 0x2 /* Do not use IRQ while single stepping */ -#define SSTEP_NOTIMER 0x4 /* Do not Timers while single stepping */ - -/** - * cpu_single_step: - * @cpu: CPU to the flags for. - * @enabled: Flags to enable. - * - * Enables or disables single-stepping for @cpu. - */ -void cpu_single_step(CPUState *cpu, int enabled); - -/* Breakpoint/watchpoint flags */ -#define BP_MEM_READ 0x01 -#define BP_MEM_WRITE 0x02 -#define BP_MEM_ACCESS (BP_MEM_READ | BP_MEM_WRITE) -#define BP_STOP_BEFORE_ACCESS 0x04 -/* 0x08 currently unused */ -#define BP_GDB 0x10 -#define BP_CPU 0x20 -#define BP_ANY (BP_GDB | BP_CPU) -#define BP_HIT_SHIFT 6 -#define BP_WATCHPOINT_HIT_READ (BP_MEM_READ << BP_HIT_SHIFT) -#define BP_WATCHPOINT_HIT_WRITE (BP_MEM_WRITE << BP_HIT_SHIFT) -#define BP_WATCHPOINT_HIT (BP_MEM_ACCESS << BP_HIT_SHIFT) - -int cpu_breakpoint_insert(CPUState *cpu, vaddr pc, int flags, - CPUBreakpoint **breakpoint); -int cpu_breakpoint_remove(CPUState *cpu, vaddr pc, int flags); -void cpu_breakpoint_remove_by_ref(CPUState *cpu, CPUBreakpoint *breakpoint); -void cpu_breakpoint_remove_all(CPUState *cpu, int mask); - -/* Return true if PC matches an installed breakpoint. */ -static inline bool cpu_breakpoint_test(CPUState *cpu, vaddr pc, int mask) -{ - CPUBreakpoint *bp; - - if (unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) { - QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) { - if (bp->pc == pc && (bp->flags & mask)) { - return true; - } - } - } - return false; -} - -/** - * cpu_get_address_space: - * @cpu: CPU to get address space from - * @asidx: index identifying which address space to get - * - * Return the requested address space of this CPU. @asidx - * specifies which address space to read. - */ -AddressSpace *cpu_get_address_space(CPUState *cpu, int asidx); - -G_NORETURN void cpu_abort(CPUState *cpu, const char *fmt, ...) - G_GNUC_PRINTF(2, 3); - -/** - * qemu_process_cpu_events: - * @cpu: CPU that left the execution loop - * - * Perform accelerator-independent work after the CPU has left - * the inner execution loop. - */ -void qemu_process_cpu_events(CPUState *cpu); - -/* $(top_srcdir)/cpu.c */ -void cpu_class_init_props(DeviceClass *dc); -void cpu_exec_class_post_init(CPUClass *cc); -void cpu_exec_initfn(CPUState *cpu); -void cpu_vmstate_register(CPUState *cpu); -void cpu_vmstate_unregister(CPUState *cpu); -bool cpu_exec_realizefn(CPUState *cpu, Error **errp); -void cpu_exec_unrealizefn(CPUState *cpu); -void cpu_exec_reset_hold(CPUState *cpu); - -extern const VMStateDescription vmstate_cpu_common; - -#define UNASSIGNED_CPU_INDEX -1 -#define UNASSIGNED_CLUSTER_INDEX -1 - -enum CacheType { - DATA_CACHE, - INSTRUCTION_CACHE, - UNIFIED_CACHE -}; - -#endif diff --git a/backup/enums.h b/backup/enums.h deleted file mode 100644 index c4d54a1..0000000 --- a/backup/enums.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * gdbstub enums - * - * Copyright (c) 2024 Linaro Ltd - * - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#ifndef GDBSTUB_ENUMS_H -#define GDBSTUB_ENUMS_H - -#define DEFAULT_GDBSTUB_PORT "1234" - -/* GDB breakpoint/watchpoint types */ -#define GDB_BREAKPOINT_SW 0 -#define GDB_BREAKPOINT_HW 1 -#define GDB_WATCHPOINT_WRITE 2 -#define GDB_WATCHPOINT_READ 3 -#define GDB_WATCHPOINT_ACCESS 4 - -#endif /* GDBSTUB_ENUMS_H */ diff --git a/backup/gdbstub.c b/backup/gdbstub.c deleted file mode 100644 index dd5fb56..0000000 --- a/backup/gdbstub.c +++ /dev/null @@ -1,2519 +0,0 @@ -/* - * gdb server stub - * - * This implements a subset of the remote protocol as described in: - * - * https://sourceware.org/gdb/onlinedocs/gdb/Remote-Protocol.html - * - * Copyright (c) 2003-2005 Fabrice Bellard - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - * - * SPDX-License-Identifier: LGPL-2.0-or-later - */ - -#include "qemu/osdep.h" -#include "qemu/ctype.h" -#include "qemu/cutils.h" -#include "qemu/module.h" -#include "qemu/error-report.h" -#include "qemu/target-info.h" -#include "trace.h" -#include "exec/gdbstub.h" -#include "gdbstub/commands.h" -#include "gdbstub/syscalls.h" -#ifdef CONFIG_USER_ONLY -#include "accel/tcg/vcpu-state.h" -#include "gdbstub/user.h" -#else -#include "hw/cpu/cluster.h" -#include "hw/boards.h" -#endif -#include "hw/core/cpu.h" - -#include "system/hw_accel.h" -#include "system/runstate.h" -#include "exec/replay-core.h" -#include "exec/hwaddr.h" - -#include "internals.h" - -typedef struct GDBRegisterState { - int base_reg; - gdb_get_reg_cb get_reg; - gdb_set_reg_cb set_reg; - const GDBFeature *feature; -} GDBRegisterState; - -GDBState gdbserver_state; - -void gdb_init_gdbserver_state(void) -{ - g_assert(!gdbserver_state.init); - memset(&gdbserver_state, 0, sizeof(GDBState)); - gdbserver_state.init = true; - gdbserver_state.str_buf = g_string_new(NULL); - gdbserver_state.mem_buf = g_byte_array_sized_new(MAX_PACKET_LENGTH); - gdbserver_state.last_packet = g_byte_array_sized_new(MAX_PACKET_LENGTH + 4); - - /* - * What single-step modes are supported is accelerator dependent. - * By default try to use no IRQs and no timers while single - * stepping so as to make single stepping like a typical ICE HW step. - */ - gdbserver_state.supported_sstep_flags = accel_supported_gdbstub_sstep_flags(); - gdbserver_state.sstep_flags = SSTEP_ENABLE | SSTEP_NOIRQ | SSTEP_NOTIMER; - gdbserver_state.sstep_flags &= gdbserver_state.supported_sstep_flags; -} - -/* writes 2*len+1 bytes in buf */ -void gdb_memtohex(GString *buf, const uint8_t *mem, int len) -{ - int i, c; - for(i = 0; i < len; i++) { - c = mem[i]; - g_string_append_c(buf, tohex(c >> 4)); - g_string_append_c(buf, tohex(c & 0xf)); - } - g_string_append_c(buf, '\0'); -} - -void gdb_hextomem(GByteArray *mem, const char *buf, int len) -{ - int i; - - for(i = 0; i < len; i++) { - guint8 byte = fromhex(buf[0]) << 4 | fromhex(buf[1]); - g_byte_array_append(mem, &byte, 1); - buf += 2; - } -} - -static void hexdump(const char *buf, int len, - void (*trace_fn)(size_t ofs, char const *text)) -{ - char line_buffer[3 * 16 + 4 + 16 + 1]; - - size_t i; - for (i = 0; i < len || (i & 0xF); ++i) { - size_t byte_ofs = i & 15; - - if (byte_ofs == 0) { - memset(line_buffer, ' ', 3 * 16 + 4 + 16); - line_buffer[3 * 16 + 4 + 16] = 0; - } - - size_t col_group = (i >> 2) & 3; - size_t hex_col = byte_ofs * 3 + col_group; - size_t txt_col = 3 * 16 + 4 + byte_ofs; - - if (i < len) { - char value = buf[i]; - - line_buffer[hex_col + 0] = tohex((value >> 4) & 0xF); - line_buffer[hex_col + 1] = tohex((value >> 0) & 0xF); - line_buffer[txt_col + 0] = (value >= ' ' && value < 127) - ? value - : '.'; - } - - if (byte_ofs == 0xF) - trace_fn(i & -16, line_buffer); - } -} - -/* return -1 if error, 0 if OK */ -int gdb_put_packet_binary(const char *buf, int len, bool dump) -{ - int csum, i; - uint8_t footer[3]; - - if (dump && trace_event_get_state_backends(TRACE_GDBSTUB_IO_BINARYREPLY)) { - hexdump(buf, len, trace_gdbstub_io_binaryreply); - } - - for(;;) { - g_byte_array_set_size(gdbserver_state.last_packet, 0); - g_byte_array_append(gdbserver_state.last_packet, - (const uint8_t *) "$", 1); - g_byte_array_append(gdbserver_state.last_packet, - (const uint8_t *) buf, len); - csum = 0; - for(i = 0; i < len; i++) { - csum += buf[i]; - } - footer[0] = '#'; - footer[1] = tohex((csum >> 4) & 0xf); - footer[2] = tohex((csum) & 0xf); - g_byte_array_append(gdbserver_state.last_packet, footer, 3); - - gdb_put_buffer(gdbserver_state.last_packet->data, - gdbserver_state.last_packet->len); - - if (gdb_got_immediate_ack()) { - break; - } - } - return 0; -} - -/* return -1 if error, 0 if OK */ -int gdb_put_packet(const char *buf) -{ - trace_gdbstub_io_reply(buf); - - return gdb_put_packet_binary(buf, strlen(buf), false); -} - -void gdb_put_strbuf(void) -{ - gdb_put_packet(gdbserver_state.str_buf->str); -} - -/* Encode data using the encoding for 'x' packets. */ -void gdb_memtox(GString *buf, const char *mem, int len) -{ - char c; - - while (len--) { - c = *(mem++); - switch (c) { - case '#': case '$': case '*': case '}': - g_string_append_c(buf, '}'); - g_string_append_c(buf, c ^ 0x20); - break; - default: - g_string_append_c(buf, c); - break; - } - } -} - -static uint32_t gdb_get_cpu_pid(CPUState *cpu) -{ -#ifdef CONFIG_USER_ONLY - return getpid(); -#else - if (cpu->cluster_index == UNASSIGNED_CLUSTER_INDEX) { - /* Return the default process' PID */ - int index = gdbserver_state.process_num - 1; - return gdbserver_state.processes[index].pid; - } - return cpu->cluster_index + 1; -#endif -} - -GDBProcess *gdb_get_process(uint32_t pid) -{ - int i; - - if (!pid) { - /* 0 means any process, we take the first one */ - return &gdbserver_state.processes[0]; - } - - for (i = 0; i < gdbserver_state.process_num; i++) { - if (gdbserver_state.processes[i].pid == pid) { - return &gdbserver_state.processes[i]; - } - } - - return NULL; -} - -static GDBProcess *gdb_get_cpu_process(CPUState *cpu) -{ - return gdb_get_process(gdb_get_cpu_pid(cpu)); -} - -static CPUState *find_cpu(uint32_t thread_id) -{ - CPUState *cpu; - - CPU_FOREACH(cpu) { - if (gdb_get_cpu_index(cpu) == thread_id) { - return cpu; - } - } - - return NULL; -} - -CPUState *gdb_get_first_cpu_in_process(GDBProcess *process) -{ - CPUState *cpu; - - CPU_FOREACH(cpu) { - if (gdb_get_cpu_pid(cpu) == process->pid) { - return cpu; - } - } - - return NULL; -} - -static CPUState *gdb_next_cpu_in_process(CPUState *cpu) -{ - uint32_t pid = gdb_get_cpu_pid(cpu); - cpu = CPU_NEXT(cpu); - - while (cpu) { - if (gdb_get_cpu_pid(cpu) == pid) { - break; - } - - cpu = CPU_NEXT(cpu); - } - - return cpu; -} - -/* Return the cpu following @cpu, while ignoring unattached processes. */ -static CPUState *gdb_next_attached_cpu(CPUState *cpu) -{ - cpu = CPU_NEXT(cpu); - - while (cpu) { - if (gdb_get_cpu_process(cpu)->attached) { - break; - } - - cpu = CPU_NEXT(cpu); - } - - return cpu; -} - -/* Return the first attached cpu */ -CPUState *gdb_first_attached_cpu(void) -{ - CPUState *cpu = first_cpu; - GDBProcess *process = gdb_get_cpu_process(cpu); - - if (!process->attached) { - return gdb_next_attached_cpu(cpu); - } - - return cpu; -} - -static CPUState *gdb_get_cpu(uint32_t pid, uint32_t tid) -{ - GDBProcess *process; - CPUState *cpu; - - if (!pid && !tid) { - /* 0 means any process/thread, we take the first attached one */ - return gdb_first_attached_cpu(); - } else if (pid && !tid) { - /* any thread in a specific process */ - process = gdb_get_process(pid); - - if (process == NULL) { - return NULL; - } - - if (!process->attached) { - return NULL; - } - - return gdb_get_first_cpu_in_process(process); - } else { - /* a specific thread */ - cpu = find_cpu(tid); - - if (cpu == NULL) { - return NULL; - } - - process = gdb_get_cpu_process(cpu); - - if (pid && process->pid != pid) { - return NULL; - } - - if (!process->attached) { - return NULL; - } - - return cpu; - } -} - -static const char *get_feature_xml(const char *p, const char **newp, - GDBProcess *process) -{ - CPUState *cpu = gdb_get_first_cpu_in_process(process); - GDBRegisterState *r; - size_t len; - - /* - * qXfer:features:read:ANNEX:OFFSET,LENGTH' - * ^p ^newp - */ - char *term = strchr(p, ':'); - *newp = term + 1; - len = term - p; - - /* Is it the main target xml? */ - if (strncmp(p, "target.xml", len) == 0) { - if (!process->target_xml) { - g_autoptr(GPtrArray) xml = g_ptr_array_new_with_free_func(g_free); - - g_ptr_array_add( - xml, - g_strdup("" - "" - "")); - - if (cpu->cc->gdb_arch_name) { - g_ptr_array_add( - xml, - g_markup_printf_escaped("%s", - cpu->cc->gdb_arch_name(cpu))); - } - for (guint i = 0; i < cpu->gdb_regs->len; i++) { - r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i); - g_ptr_array_add( - xml, - g_markup_printf_escaped("", - r->feature->xmlname)); - } - g_ptr_array_add(xml, g_strdup("")); - g_ptr_array_add(xml, NULL); - - process->target_xml = g_strjoinv(NULL, (void *)xml->pdata); - } - return process->target_xml; - } - /* Is it one of the features? */ - for (guint i = 0; i < cpu->gdb_regs->len; i++) { - r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i); - if (strncmp(p, r->feature->xmlname, len) == 0) { - return r->feature->xml; - } - } - - /* failed */ - return NULL; -} - -void gdb_feature_builder_init(GDBFeatureBuilder *builder, GDBFeature *feature, - const char *name, const char *xmlname, - int base_reg) -{ - char *header = g_markup_printf_escaped( - "" - "" - "", - name); - - builder->feature = feature; - builder->xml = g_ptr_array_new(); - g_ptr_array_add(builder->xml, header); - builder->regs = g_ptr_array_new(); - builder->base_reg = base_reg; - feature->xmlname = xmlname; - feature->name = name; -} - -void gdb_feature_builder_append_tag(const GDBFeatureBuilder *builder, - const char *format, ...) -{ - va_list ap; - va_start(ap, format); - g_ptr_array_add(builder->xml, g_markup_vprintf_escaped(format, ap)); - va_end(ap); -} - -void gdb_feature_builder_append_reg(const GDBFeatureBuilder *builder, - const char *name, - int bitsize, - int regnum, - const char *type, - const char *group) -{ - if (builder->regs->len <= regnum) { - g_ptr_array_set_size(builder->regs, regnum + 1); - } - - builder->regs->pdata[regnum] = (gpointer *)name; - - if (group) { - gdb_feature_builder_append_tag( - builder, - "", - name, bitsize, builder->base_reg + regnum, type, group); - } else { - gdb_feature_builder_append_tag( - builder, - "", - name, bitsize, builder->base_reg + regnum, type); - } -} - -void gdb_feature_builder_end(const GDBFeatureBuilder *builder) -{ - g_ptr_array_add(builder->xml, (void *)""); - g_ptr_array_add(builder->xml, NULL); - - builder->feature->xml = g_strjoinv(NULL, (void *)builder->xml->pdata); - - for (guint i = 0; i < builder->xml->len - 2; i++) { - g_free(g_ptr_array_index(builder->xml, i)); - } - - g_ptr_array_free(builder->xml, TRUE); - - builder->feature->num_regs = builder->regs->len; - builder->feature->regs = (void *)g_ptr_array_free(builder->regs, FALSE); -} - -const GDBFeature *gdb_find_static_feature(const char *xmlname) -{ - const GDBFeature *feature; - - for (feature = gdb_static_features; feature->xmlname; feature++) { - if (!strcmp(feature->xmlname, xmlname)) { - return feature; - } - } - - g_assert_not_reached(); -} - -GArray *gdb_get_register_list(CPUState *cpu) -{ - GArray *results = g_array_new(true, true, sizeof(GDBRegDesc)); - - /* registers are only available once the CPU is initialised */ - if (!cpu->gdb_regs) { - return results; - } - - for (int f = 0; f < cpu->gdb_regs->len; f++) { - GDBRegisterState *r = &g_array_index(cpu->gdb_regs, GDBRegisterState, f); - for (int i = 0; i < r->feature->num_regs; i++) { - const char *name = r->feature->regs[i]; - GDBRegDesc desc = { - r->base_reg + i, - name, - r->feature->name - }; - g_array_append_val(results, desc); - } - } - - return results; -} - -int gdb_read_register(CPUState *cpu, GByteArray *buf, int reg) -{ - GDBRegisterState *r; - - if (reg < cpu->cc->gdb_num_core_regs) { - return cpu->cc->gdb_read_register(cpu, buf, reg); - } - - for (guint i = 0; i < cpu->gdb_regs->len; i++) { - r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i); - if (r->base_reg <= reg && reg < r->base_reg + r->feature->num_regs) { - return r->get_reg(cpu, buf, reg - r->base_reg); - } - } - return 0; -} - -int gdb_write_register(CPUState *cpu, uint8_t *mem_buf, int reg) -{ - GDBRegisterState *r; - - if (reg < cpu->cc->gdb_num_core_regs) { - return cpu->cc->gdb_write_register(cpu, mem_buf, reg); - } - - for (guint i = 0; i < cpu->gdb_regs->len; i++) { - r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i); - if (r->base_reg <= reg && reg < r->base_reg + r->feature->num_regs) { - return r->set_reg(cpu, mem_buf, reg - r->base_reg); - } - } - return 0; -} - -static void gdb_register_feature(CPUState *cpu, int base_reg, - gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg, - const GDBFeature *feature) -{ - GDBRegisterState s = { - .base_reg = base_reg, - .get_reg = get_reg, - .set_reg = set_reg, - .feature = feature - }; - - g_array_append_val(cpu->gdb_regs, s); -} - -static const char *gdb_get_core_xml_file(CPUState *cpu) -{ - CPUClass *cc = cpu->cc; - - /* - * The CPU class can provide the XML filename via a method, - * or as a simple fixed string field. - */ - if (cc->gdb_get_core_xml_file) { - return cc->gdb_get_core_xml_file(cpu); - } - return cc->gdb_core_xml_file; -} - -void gdb_init_cpu(CPUState *cpu) -{ - CPUClass *cc = cpu->cc; - const GDBFeature *feature; - const char *xmlfile = gdb_get_core_xml_file(cpu); - - cpu->gdb_regs = g_array_new(false, false, sizeof(GDBRegisterState)); - - if (xmlfile) { - feature = gdb_find_static_feature(xmlfile); - gdb_register_feature(cpu, 0, - cc->gdb_read_register, cc->gdb_write_register, - feature); - cpu->gdb_num_regs = cpu->gdb_num_g_regs = feature->num_regs; - } - - if (cc->gdb_num_core_regs) { - cpu->gdb_num_regs = cpu->gdb_num_g_regs = cc->gdb_num_core_regs; - } -} - -void gdb_register_coprocessor(CPUState *cpu, - gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg, - const GDBFeature *feature, int g_pos) -{ - GDBRegisterState *s; - guint i; - int base_reg = cpu->gdb_num_regs; - - for (i = 0; i < cpu->gdb_regs->len; i++) { - /* Check for duplicates. */ - s = &g_array_index(cpu->gdb_regs, GDBRegisterState, i); - if (s->feature == feature) { - return; - } - } - - gdb_register_feature(cpu, base_reg, get_reg, set_reg, feature); - - /* Add to end of list. */ - cpu->gdb_num_regs += feature->num_regs; - if (g_pos) { - if (g_pos != base_reg) { - error_report("Error: Bad gdb register numbering for '%s', " - "expected %d got %d", feature->xml, g_pos, base_reg); - } else { - cpu->gdb_num_g_regs = cpu->gdb_num_regs; - } - } -} - -void gdb_unregister_coprocessor_all(CPUState *cpu) -{ - /* - * Safe to nuke everything. GDBRegisterState::xml is static const char so - * it won't be freed - */ - g_array_free(cpu->gdb_regs, true); - - cpu->gdb_regs = NULL; - cpu->gdb_num_regs = 0; - cpu->gdb_num_g_regs = 0; -} - -static void gdb_process_breakpoint_remove_all(GDBProcess *p) -{ - CPUState *cpu = gdb_get_first_cpu_in_process(p); - - while (cpu) { - gdb_breakpoint_remove_all(cpu); - cpu = gdb_next_cpu_in_process(cpu); - } -} - - -static void gdb_set_cpu_pc(vaddr pc) -{ - CPUState *cpu = gdbserver_state.c_cpu; - - cpu_synchronize_state(cpu); - cpu_set_pc(cpu, pc); -} - -void gdb_append_thread_id(CPUState *cpu, GString *buf) -{ - if (gdbserver_state.multiprocess) { - g_string_append_printf(buf, "p%02x.%02x", - gdb_get_cpu_pid(cpu), gdb_get_cpu_index(cpu)); - } else { - g_string_append_printf(buf, "%02x", gdb_get_cpu_index(cpu)); - } -} - -static GDBThreadIdKind read_thread_id(const char *buf, const char **end_buf, - uint32_t *pid, uint32_t *tid) -{ - unsigned long p, t; - int ret; - - if (*buf == 'p') { - buf++; - ret = qemu_strtoul(buf, &buf, 16, &p); - - if (ret) { - return GDB_READ_THREAD_ERR; - } - - /* Skip '.' */ - buf++; - } else { - p = 0; - } - - ret = qemu_strtoul(buf, &buf, 16, &t); - - if (ret) { - return GDB_READ_THREAD_ERR; - } - - *end_buf = buf; - - if (p == -1) { - return GDB_ALL_PROCESSES; - } - - if (pid) { - *pid = p; - } - - if (t == -1) { - return GDB_ALL_THREADS; - } - - if (tid) { - *tid = t; - } - - return GDB_ONE_THREAD; -} - -/** - * gdb_handle_vcont - Parses and handles a vCont packet. - * returns -ENOTSUP if a command is unsupported, -EINVAL or -ERANGE if there is - * a format error, 0 on success. - */ -static int gdb_handle_vcont(const char *p) -{ - int res, signal = 0; - char cur_action; - unsigned long tmp; - uint32_t pid, tid; - GDBProcess *process; - CPUState *cpu; - GDBThreadIdKind kind; - unsigned int max_cpus = gdb_get_max_cpus(); - /* uninitialised CPUs stay 0 */ - g_autofree char *newstates = g_new0(char, max_cpus); - - /* mark valid CPUs with 1 */ - CPU_FOREACH(cpu) { - newstates[cpu->cpu_index] = 1; - } - - /* - * res keeps track of what error we are returning, with -ENOTSUP meaning - * that the command is unknown or unsupported, thus returning an empty - * packet, while -EINVAL and -ERANGE cause an E22 packet, due to invalid, - * or incorrect parameters passed. - */ - res = 0; - - /* - * target_count and last_target keep track of how many CPUs we are going to - * step or resume, and a pointer to the state structure of one of them, - * respectively - */ - int target_count = 0; - CPUState *last_target = NULL; - - while (*p) { - if (*p++ != ';') { - return -ENOTSUP; - } - - cur_action = *p++; - if (cur_action == 'C' || cur_action == 'S') { - cur_action = qemu_tolower(cur_action); - res = qemu_strtoul(p, &p, 16, &tmp); - if (res) { - return res; - } - signal = gdb_signal_to_target(tmp); - } else if (cur_action != 'c' && cur_action != 's') { - /* unknown/invalid/unsupported command */ - return -ENOTSUP; - } - - if (*p == '\0' || *p == ';') { - /* - * No thread specifier, action is on "all threads". The - * specification is unclear regarding the process to act on. We - * choose all processes. - */ - kind = GDB_ALL_PROCESSES; - } else if (*p++ == ':') { - kind = read_thread_id(p, &p, &pid, &tid); - } else { - return -ENOTSUP; - } - - switch (kind) { - case GDB_READ_THREAD_ERR: - return -EINVAL; - - case GDB_ALL_PROCESSES: - cpu = gdb_first_attached_cpu(); - while (cpu) { - if (newstates[cpu->cpu_index] == 1) { - newstates[cpu->cpu_index] = cur_action; - - target_count++; - last_target = cpu; - } - - cpu = gdb_next_attached_cpu(cpu); - } - break; - - case GDB_ALL_THREADS: - process = gdb_get_process(pid); - - if (!process->attached) { - return -EINVAL; - } - - cpu = gdb_get_first_cpu_in_process(process); - while (cpu) { - if (newstates[cpu->cpu_index] == 1) { - newstates[cpu->cpu_index] = cur_action; - - target_count++; - last_target = cpu; - } - - cpu = gdb_next_cpu_in_process(cpu); - } - break; - - case GDB_ONE_THREAD: - cpu = gdb_get_cpu(pid, tid); - - /* invalid CPU/thread specified */ - if (!cpu) { - return -EINVAL; - } - - /* only use if no previous match occourred */ - if (newstates[cpu->cpu_index] == 1) { - newstates[cpu->cpu_index] = cur_action; - - target_count++; - last_target = cpu; - } - break; - } - } - - /* - * if we're about to resume a specific set of CPUs/threads, make it so that - * in case execution gets interrupted, we can send GDB a stop reply with a - * correct value. it doesn't really matter which CPU we tell GDB the signal - * happened in (VM pauses stop all of them anyway), so long as it is one of - * the ones we resumed/single stepped here. - */ - if (target_count > 0) { - gdbserver_state.c_cpu = last_target; - } - - gdbserver_state.signal = signal; - gdb_continue_partial(newstates); - return res; -} - -static const char *cmd_next_param(const char *param, const char delimiter) -{ - static const char all_delimiters[] = ",;:="; - char curr_delimiters[2] = {0}; - const char *delimiters; - - if (delimiter == '?') { - delimiters = all_delimiters; - } else if (delimiter == '0') { - return strchr(param, '\0'); - } else if (delimiter == '.' && *param) { - return param + 1; - } else { - curr_delimiters[0] = delimiter; - delimiters = curr_delimiters; - } - - param += strcspn(param, delimiters); - if (*param) { - param++; - } - return param; -} - -static int cmd_parse_params(const char *data, const char *schema, - GArray *params) -{ - const char *curr_schema, *curr_data; - - g_assert(schema); - g_assert(params->len == 0); - - curr_schema = schema; - curr_data = data; - while (curr_schema[0] && curr_schema[1] && *curr_data) { - GdbCmdVariant this_param; - - switch (curr_schema[0]) { - case 'l': - if (qemu_strtoul(curr_data, &curr_data, 16, - &this_param.val_ul)) { - return -EINVAL; - } - curr_data = cmd_next_param(curr_data, curr_schema[1]); - g_array_append_val(params, this_param); - break; - case 'L': - if (qemu_strtou64(curr_data, &curr_data, 16, - (uint64_t *)&this_param.val_ull)) { - return -EINVAL; - } - curr_data = cmd_next_param(curr_data, curr_schema[1]); - g_array_append_val(params, this_param); - break; - case 's': - this_param.data = curr_data; - curr_data = cmd_next_param(curr_data, curr_schema[1]); - g_array_append_val(params, this_param); - break; - case 'o': - this_param.opcode = *(uint8_t *)curr_data; - curr_data = cmd_next_param(curr_data, curr_schema[1]); - g_array_append_val(params, this_param); - break; - case 't': - this_param.thread_id.kind = - read_thread_id(curr_data, &curr_data, - &this_param.thread_id.pid, - &this_param.thread_id.tid); - curr_data = cmd_next_param(curr_data, curr_schema[1]); - g_array_append_val(params, this_param); - break; - case '?': - curr_data = cmd_next_param(curr_data, curr_schema[1]); - break; - default: - return -EINVAL; - } - curr_schema += 2; - } - - return 0; -} - -static inline int startswith(const char *string, const char *pattern) -{ - return !strncmp(string, pattern, strlen(pattern)); -} - -static bool process_string_cmd(const char *data, - const GdbCmdParseEntry *cmds, int num_cmds) -{ - int i; - g_autoptr(GArray) params = g_array_new(false, true, sizeof(GdbCmdVariant)); - - if (!cmds) { - return false; - } - - for (i = 0; i < num_cmds; i++) { - const GdbCmdParseEntry *cmd = &cmds[i]; - void *user_ctx = NULL; - g_assert(cmd->handler && cmd->cmd); - - if ((cmd->cmd_startswith && !startswith(data, cmd->cmd)) || - (!cmd->cmd_startswith && strcmp(cmd->cmd, data))) { - continue; - } - - if (cmd->schema) { - if (cmd_parse_params(&data[strlen(cmd->cmd)], - cmd->schema, params)) { - return false; - } - } - - if (cmd->need_cpu_context) { - user_ctx = (void *)gdbserver_state.g_cpu; - } - - gdbserver_state.allow_stop_reply = cmd->allow_stop_reply; - cmd->handler(params, user_ctx); - return true; - } - - return false; -} - -static void run_cmd_parser(const char *data, const GdbCmdParseEntry *cmd) -{ - if (!data) { - return; - } - - g_string_set_size(gdbserver_state.str_buf, 0); - g_byte_array_set_size(gdbserver_state.mem_buf, 0); - - /* In case there was an error during the command parsing we must - * send a NULL packet to indicate the command is not supported */ - if (!process_string_cmd(data, cmd, 1)) { - gdb_put_packet(""); - } -} - -static void handle_detach(GArray *params, void *user_ctx) -{ - GDBProcess *process; - uint32_t pid = 1; - - if (gdbserver_state.multiprocess) { - if (!params->len) { - gdb_put_packet("E22"); - return; - } - - pid = gdb_get_cmd_param(params, 0)->val_ul; - } - -#ifdef CONFIG_USER_ONLY - if (gdb_handle_detach_user(pid)) { - return; - } -#endif - - process = gdb_get_process(pid); - gdb_process_breakpoint_remove_all(process); - process->attached = false; - - if (pid == gdb_get_cpu_pid(gdbserver_state.c_cpu)) { - gdbserver_state.c_cpu = gdb_first_attached_cpu(); - } - - if (pid == gdb_get_cpu_pid(gdbserver_state.g_cpu)) { - gdbserver_state.g_cpu = gdb_first_attached_cpu(); - } - - if (!gdbserver_state.c_cpu) { - /* No more process attached */ - gdb_disable_syscalls(); - gdb_continue(); - } - gdb_put_packet("OK"); -} - -static void handle_thread_alive(GArray *params, void *user_ctx) -{ - CPUState *cpu; - - if (!params->len) { - gdb_put_packet("E22"); - return; - } - - if (gdb_get_cmd_param(params, 0)->thread_id.kind == GDB_READ_THREAD_ERR) { - gdb_put_packet("E22"); - return; - } - - cpu = gdb_get_cpu(gdb_get_cmd_param(params, 0)->thread_id.pid, - gdb_get_cmd_param(params, 0)->thread_id.tid); - if (!cpu) { - gdb_put_packet("E22"); - return; - } - - gdb_put_packet("OK"); -} - -static void handle_continue(GArray *params, void *user_ctx) -{ - if (params->len) { - gdb_set_cpu_pc(gdb_get_cmd_param(params, 0)->val_ull); - } - - gdbserver_state.signal = 0; - gdb_continue(); -} - -static void handle_cont_with_sig(GArray *params, void *user_ctx) -{ - unsigned long signal = 0; - - /* - * Note: C sig;[addr] is currently unsupported and we simply - * omit the addr parameter - */ - if (params->len) { - signal = gdb_get_cmd_param(params, 0)->val_ul; - } - - gdbserver_state.signal = gdb_signal_to_target(signal); - if (gdbserver_state.signal == -1) { - gdbserver_state.signal = 0; - } - gdb_continue(); -} - -static void handle_set_thread(GArray *params, void *user_ctx) -{ - uint32_t pid, tid; - CPUState *cpu; - - if (params->len != 2) { - gdb_put_packet("E22"); - return; - } - - if (gdb_get_cmd_param(params, 1)->thread_id.kind == GDB_READ_THREAD_ERR) { - gdb_put_packet("E22"); - return; - } - - if (gdb_get_cmd_param(params, 1)->thread_id.kind != GDB_ONE_THREAD) { - gdb_put_packet("OK"); - return; - } - - pid = gdb_get_cmd_param(params, 1)->thread_id.pid; - tid = gdb_get_cmd_param(params, 1)->thread_id.tid; -#ifdef CONFIG_USER_ONLY - if (gdb_handle_set_thread_user(pid, tid)) { - return; - } -#endif - cpu = gdb_get_cpu(pid, tid); - if (!cpu) { - gdb_put_packet("E22"); - return; - } - - /* - * Note: This command is deprecated and modern gdb's will be using the - * vCont command instead. - */ - switch (gdb_get_cmd_param(params, 0)->opcode) { - case 'c': - gdbserver_state.c_cpu = cpu; - gdb_put_packet("OK"); - break; - case 'g': - gdbserver_state.g_cpu = cpu; - gdb_put_packet("OK"); - break; - default: - gdb_put_packet("E22"); - break; - } -} - -static void handle_insert_bp(GArray *params, void *user_ctx) -{ - int res; - - if (params->len != 3) { - gdb_put_packet("E22"); - return; - } - - res = gdb_breakpoint_insert(gdbserver_state.c_cpu, - gdb_get_cmd_param(params, 0)->val_ul, - gdb_get_cmd_param(params, 1)->val_ull, - gdb_get_cmd_param(params, 2)->val_ull); - if (res >= 0) { - gdb_put_packet("OK"); - return; - } else if (res == -ENOSYS) { - gdb_put_packet(""); - return; - } - - gdb_put_packet("E22"); -} - -static void handle_remove_bp(GArray *params, void *user_ctx) -{ - int res; - - if (params->len != 3) { - gdb_put_packet("E22"); - return; - } - - res = gdb_breakpoint_remove(gdbserver_state.c_cpu, - gdb_get_cmd_param(params, 0)->val_ul, - gdb_get_cmd_param(params, 1)->val_ull, - gdb_get_cmd_param(params, 2)->val_ull); - if (res >= 0) { - gdb_put_packet("OK"); - return; - } else if (res == -ENOSYS) { - gdb_put_packet(""); - return; - } - - gdb_put_packet("E22"); -} - -/* - * handle_set/get_reg - * - * Older gdb are really dumb, and don't use 'G/g' if 'P/p' is available. - * This works, but can be very slow. Anything new enough to understand - * XML also knows how to use this properly. However to use this we - * need to define a local XML file as well as be talking to a - * reasonably modern gdb. Responding with an empty packet will cause - * the remote gdb to fallback to older methods. - */ - -static void handle_set_reg(GArray *params, void *user_ctx) -{ - int reg_size; - - if (params->len != 2) { - gdb_put_packet("E22"); - return; - } - - reg_size = strlen(gdb_get_cmd_param(params, 1)->data) / 2; - gdb_hextomem(gdbserver_state.mem_buf, gdb_get_cmd_param(params, 1)->data, reg_size); - gdb_write_register(gdbserver_state.g_cpu, gdbserver_state.mem_buf->data, - gdb_get_cmd_param(params, 0)->val_ull); - gdb_put_packet("OK"); -} - -static void handle_get_reg(GArray *params, void *user_ctx) -{ - int reg_size; - - if (!params->len) { - gdb_put_packet("E14"); - return; - } - - reg_size = gdb_read_register(gdbserver_state.g_cpu, - gdbserver_state.mem_buf, - gdb_get_cmd_param(params, 0)->val_ull); - if (!reg_size) { - gdb_put_packet("E14"); - return; - } else { - g_byte_array_set_size(gdbserver_state.mem_buf, reg_size); - } - - gdb_memtohex(gdbserver_state.str_buf, - gdbserver_state.mem_buf->data, reg_size); - gdb_put_strbuf(); -} - -static void handle_write_mem(GArray *params, void *user_ctx) -{ - if (params->len != 3) { - gdb_put_packet("E22"); - return; - } - - /* gdb_hextomem() reads 2*len bytes */ - if (gdb_get_cmd_param(params, 1)->val_ull > - strlen(gdb_get_cmd_param(params, 2)->data) / 2) { - gdb_put_packet("E22"); - return; - } - - gdb_hextomem(gdbserver_state.mem_buf, gdb_get_cmd_param(params, 2)->data, - gdb_get_cmd_param(params, 1)->val_ull); - if (gdb_target_memory_rw_debug(gdbserver_state.g_cpu, - gdb_get_cmd_param(params, 0)->val_ull, - gdbserver_state.mem_buf->data, - gdbserver_state.mem_buf->len, true)) { - gdb_put_packet("E14"); - return; - } - - gdb_put_packet("OK"); -} - -static void handle_read_mem(GArray *params, void *user_ctx) -{ - if (params->len != 2) { - gdb_put_packet("E22"); - return; - } - - /* gdb_memtohex() doubles the required space */ - if (gdb_get_cmd_param(params, 1)->val_ull > MAX_PACKET_LENGTH / 2) { - gdb_put_packet("E22"); - return; - } - - g_byte_array_set_size(gdbserver_state.mem_buf, - gdb_get_cmd_param(params, 1)->val_ull); - - if (gdb_target_memory_rw_debug(gdbserver_state.g_cpu, - gdb_get_cmd_param(params, 0)->val_ull, - gdbserver_state.mem_buf->data, - gdbserver_state.mem_buf->len, false)) { - gdb_put_packet("E14"); - return; - } - - gdb_memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, - gdbserver_state.mem_buf->len); - gdb_put_strbuf(); -} - -static void handle_write_all_regs(GArray *params, void *user_ctx) -{ - int reg_id; - size_t len; - uint8_t *registers; - int reg_size; - - if (!params->len) { - return; - } - - cpu_synchronize_state(gdbserver_state.g_cpu); - len = strlen(gdb_get_cmd_param(params, 0)->data) / 2; - gdb_hextomem(gdbserver_state.mem_buf, gdb_get_cmd_param(params, 0)->data, len); - registers = gdbserver_state.mem_buf->data; - for (reg_id = 0; - reg_id < gdbserver_state.g_cpu->gdb_num_g_regs && len > 0; - reg_id++) { - reg_size = gdb_write_register(gdbserver_state.g_cpu, registers, reg_id); - len -= reg_size; - registers += reg_size; - } - gdb_put_packet("OK"); -} - -static void handle_read_all_regs(GArray *params, void *user_ctx) -{ - int reg_id; - size_t len; - - cpu_synchronize_state(gdbserver_state.g_cpu); - g_byte_array_set_size(gdbserver_state.mem_buf, 0); - len = 0; - for (reg_id = 0; reg_id < gdbserver_state.g_cpu->gdb_num_g_regs; reg_id++) { - len += gdb_read_register(gdbserver_state.g_cpu, - gdbserver_state.mem_buf, - reg_id); - g_assert(len == gdbserver_state.mem_buf->len); - } - - gdb_memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, len); - gdb_put_strbuf(); -} - - -static void handle_step(GArray *params, void *user_ctx) -{ - if (params->len) { - gdb_set_cpu_pc(gdb_get_cmd_param(params, 0)->val_ull); - } - - cpu_single_step(gdbserver_state.c_cpu, gdbserver_state.sstep_flags); - gdb_continue(); -} - -static void handle_backward(GArray *params, void *user_ctx) -{ - if (!gdb_can_reverse()) { - gdb_put_packet("E22"); - } - if (params->len == 1) { - switch (gdb_get_cmd_param(params, 0)->opcode) { - case 's': - if (replay_reverse_step()) { - gdb_continue(); - } else { - gdb_put_packet("E14"); - } - return; - case 'c': - if (replay_reverse_continue()) { - gdb_continue(); - } else { - gdb_put_packet("E14"); - } - return; - } - } - - /* Default invalid command */ - gdb_put_packet(""); -} - -static void handle_v_cont_query(GArray *params, void *user_ctx) -{ - gdb_put_packet("vCont;c;C;s;S"); -} - -static void handle_v_cont(GArray *params, void *user_ctx) -{ - int res; - - if (!params->len) { - return; - } - - res = gdb_handle_vcont(gdb_get_cmd_param(params, 0)->data); - if ((res == -EINVAL) || (res == -ERANGE)) { - gdb_put_packet("E22"); - } else if (res) { - gdb_put_packet(""); - } -} - -static void handle_v_attach(GArray *params, void *user_ctx) -{ - GDBProcess *process; - CPUState *cpu; - - g_string_assign(gdbserver_state.str_buf, "E22"); - if (!params->len) { - goto cleanup; - } - - process = gdb_get_process(gdb_get_cmd_param(params, 0)->val_ul); - if (!process) { - goto cleanup; - } - - cpu = gdb_get_first_cpu_in_process(process); - if (!cpu) { - goto cleanup; - } - - process->attached = true; - gdbserver_state.g_cpu = cpu; - gdbserver_state.c_cpu = cpu; - - if (gdbserver_state.allow_stop_reply) { - g_string_printf(gdbserver_state.str_buf, "T%02xthread:", GDB_SIGNAL_TRAP); - gdb_append_thread_id(cpu, gdbserver_state.str_buf); - g_string_append_c(gdbserver_state.str_buf, ';'); - gdbserver_state.allow_stop_reply = false; -cleanup: - gdb_put_strbuf(); - } -} - -static void handle_v_kill(GArray *params, void *user_ctx) -{ - /* Kill the target */ - gdb_put_packet("OK"); - error_report("QEMU: Terminated via GDBstub"); - gdb_exit(0); - gdb_qemu_exit(0); -} - -static const GdbCmdParseEntry gdb_v_commands_table[] = { - /* Order is important if has same prefix */ - { - .handler = handle_v_cont_query, - .cmd = "Cont?", - .cmd_startswith = true - }, - { - .handler = handle_v_cont, - .cmd = "Cont", - .cmd_startswith = true, - .allow_stop_reply = true, - .schema = "s0" - }, - { - .handler = handle_v_attach, - .cmd = "Attach;", - .cmd_startswith = true, - .allow_stop_reply = true, - .schema = "l0" - }, - { - .handler = handle_v_kill, - .cmd = "Kill;", - .cmd_startswith = true - }, -#ifdef CONFIG_USER_ONLY - /* - * Host I/O Packets. See [1] for details. - * [1] https://sourceware.org/gdb/onlinedocs/gdb/Host-I_002fO-Packets.html - */ - { - .handler = gdb_handle_v_file_open, - .cmd = "File:open:", - .cmd_startswith = true, - .schema = "s,L,L0" - }, - { - .handler = gdb_handle_v_file_close, - .cmd = "File:close:", - .cmd_startswith = true, - .schema = "l0" - }, - { - .handler = gdb_handle_v_file_pread, - .cmd = "File:pread:", - .cmd_startswith = true, - .schema = "l,L,L0" - }, - { - .handler = gdb_handle_v_file_readlink, - .cmd = "File:readlink:", - .cmd_startswith = true, - .schema = "s0" - }, -#endif -}; - -static void handle_v_commands(GArray *params, void *user_ctx) -{ - if (!params->len) { - return; - } - - if (!process_string_cmd(gdb_get_cmd_param(params, 0)->data, - gdb_v_commands_table, - ARRAY_SIZE(gdb_v_commands_table))) { - gdb_put_packet(""); - } -} - -static void handle_query_qemu_sstepbits(GArray *params, void *user_ctx) -{ - g_string_printf(gdbserver_state.str_buf, "ENABLE=%x", SSTEP_ENABLE); - - if (gdbserver_state.supported_sstep_flags & SSTEP_NOIRQ) { - g_string_append_printf(gdbserver_state.str_buf, ",NOIRQ=%x", - SSTEP_NOIRQ); - } - - if (gdbserver_state.supported_sstep_flags & SSTEP_NOTIMER) { - g_string_append_printf(gdbserver_state.str_buf, ",NOTIMER=%x", - SSTEP_NOTIMER); - } - - gdb_put_strbuf(); -} - -static void handle_set_qemu_sstep(GArray *params, void *user_ctx) -{ - int new_sstep_flags; - - if (!params->len) { - return; - } - - new_sstep_flags = gdb_get_cmd_param(params, 0)->val_ul; - - if (new_sstep_flags & ~gdbserver_state.supported_sstep_flags) { - gdb_put_packet("E22"); - return; - } - - gdbserver_state.sstep_flags = new_sstep_flags; - gdb_put_packet("OK"); -} - -static void handle_query_qemu_sstep(GArray *params, void *user_ctx) -{ - g_string_printf(gdbserver_state.str_buf, "0x%x", - gdbserver_state.sstep_flags); - gdb_put_strbuf(); -} - -static void handle_query_curr_tid(GArray *params, void *user_ctx) -{ - CPUState *cpu; - GDBProcess *process; - - /* - * "Current thread" remains vague in the spec, so always return - * the first thread of the current process (gdb returns the - * first thread). - */ - process = gdb_get_cpu_process(gdbserver_state.g_cpu); - cpu = gdb_get_first_cpu_in_process(process); - g_string_assign(gdbserver_state.str_buf, "QC"); - gdb_append_thread_id(cpu, gdbserver_state.str_buf); - gdb_put_strbuf(); -} - -static void handle_query_threads(GArray *params, void *user_ctx) -{ - if (!gdbserver_state.query_cpu) { - gdb_put_packet("l"); - return; - } - - g_string_assign(gdbserver_state.str_buf, "m"); - gdb_append_thread_id(gdbserver_state.query_cpu, gdbserver_state.str_buf); - gdb_put_strbuf(); - gdbserver_state.query_cpu = gdb_next_attached_cpu(gdbserver_state.query_cpu); -} - -static void handle_query_gdb_server_version(GArray *params, void *user_ctx) -{ -#if defined(CONFIG_USER_ONLY) - g_string_printf(gdbserver_state.str_buf, "name:qemu-%s;version:%s;", - target_name(), QEMU_VERSION); -#else - g_string_printf(gdbserver_state.str_buf, "name:qemu-system-%s;version:%s;", - target_name(), QEMU_VERSION); -#endif - gdb_put_strbuf(); -} - -static void handle_query_first_threads(GArray *params, void *user_ctx) -{ - gdbserver_state.query_cpu = gdb_first_attached_cpu(); - handle_query_threads(params, user_ctx); -} - -static void handle_query_thread_extra(GArray *params, void *user_ctx) -{ - g_autoptr(GString) rs = g_string_new(NULL); - CPUState *cpu; - - if (!params->len || - gdb_get_cmd_param(params, 0)->thread_id.kind == GDB_READ_THREAD_ERR) { - gdb_put_packet("E22"); - return; - } - - cpu = gdb_get_cpu(gdb_get_cmd_param(params, 0)->thread_id.pid, - gdb_get_cmd_param(params, 0)->thread_id.tid); - if (!cpu) { - return; - } - - cpu_synchronize_state(cpu); - - if (gdbserver_state.multiprocess && (gdbserver_state.process_num > 1)) { - /* Print the CPU model and name in multiprocess mode */ - ObjectClass *oc = object_get_class(OBJECT(cpu)); - const char *cpu_model = object_class_get_name(oc); - const char *cpu_name = - object_get_canonical_path_component(OBJECT(cpu)); - g_string_printf(rs, "%s %s [%s]", cpu_model, cpu_name, - cpu->halted ? "halted " : "running"); - } else { - g_string_printf(rs, "CPU#%d [%s]", cpu->cpu_index, - cpu->halted ? "halted " : "running"); - } - trace_gdbstub_op_extra_info(rs->str); - gdb_memtohex(gdbserver_state.str_buf, (uint8_t *)rs->str, rs->len); - gdb_put_strbuf(); -} - - -static char **extra_query_flags; - -void gdb_extend_qsupported_features(char *qflags) -{ - if (!extra_query_flags) { - extra_query_flags = g_new0(char *, 2); - extra_query_flags[0] = g_strdup(qflags); - } else if (!g_strv_contains((const gchar * const *) extra_query_flags, - qflags)) { - int len = g_strv_length(extra_query_flags); - extra_query_flags = g_realloc_n(extra_query_flags, len + 2, - sizeof(char *)); - extra_query_flags[len] = g_strdup(qflags); - } -} - -static void handle_query_supported(GArray *params, void *user_ctx) -{ - g_string_printf(gdbserver_state.str_buf, "PacketSize=%x", MAX_PACKET_LENGTH); - if (gdb_get_core_xml_file(first_cpu)) { - g_string_append(gdbserver_state.str_buf, ";qXfer:features:read+"); - } - - if (gdb_can_reverse()) { - g_string_append(gdbserver_state.str_buf, - ";ReverseStep+;ReverseContinue+"); - } - -#if defined(CONFIG_USER_ONLY) -#if defined(CONFIG_LINUX) - if (get_task_state(gdbserver_state.c_cpu)) { - g_string_append(gdbserver_state.str_buf, ";qXfer:auxv:read+"); - } - g_string_append(gdbserver_state.str_buf, ";QCatchSyscalls+"); - - g_string_append(gdbserver_state.str_buf, ";qXfer:siginfo:read+"); -#endif - g_string_append(gdbserver_state.str_buf, ";qXfer:exec-file:read+"); -#endif - - if (params->len) { - const char *gdb_supported = gdb_get_cmd_param(params, 0)->data; - - if (strstr(gdb_supported, "multiprocess+")) { - gdbserver_state.multiprocess = true; - } -#if defined(CONFIG_USER_ONLY) - gdb_handle_query_supported_user(gdb_supported); -#endif - } - - g_string_append(gdbserver_state.str_buf, ";vContSupported+;multiprocess+"); - - if (extra_query_flags) { - int extras = g_strv_length(extra_query_flags); - for (int i = 0; i < extras; i++) { - g_string_append(gdbserver_state.str_buf, extra_query_flags[i]); - } - } - - gdb_put_strbuf(); -} - -static void handle_query_xfer_features(GArray *params, void *user_ctx) -{ - GDBProcess *process; - unsigned long len, total_len, addr; - const char *xml; - const char *p; - - if (params->len < 3) { - gdb_put_packet("E22"); - return; - } - - process = gdb_get_cpu_process(gdbserver_state.g_cpu); - if (!gdb_get_core_xml_file(gdbserver_state.g_cpu)) { - gdb_put_packet(""); - return; - } - - p = gdb_get_cmd_param(params, 0)->data; - xml = get_feature_xml(p, &p, process); - if (!xml) { - gdb_put_packet("E00"); - return; - } - - addr = gdb_get_cmd_param(params, 1)->val_ul; - len = gdb_get_cmd_param(params, 2)->val_ul; - total_len = strlen(xml); - if (addr > total_len) { - gdb_put_packet("E00"); - return; - } - - if (len > (MAX_PACKET_LENGTH - 5) / 2) { - len = (MAX_PACKET_LENGTH - 5) / 2; - } - - if (len < total_len - addr) { - g_string_assign(gdbserver_state.str_buf, "m"); - gdb_memtox(gdbserver_state.str_buf, xml + addr, len); - } else { - g_string_assign(gdbserver_state.str_buf, "l"); - gdb_memtox(gdbserver_state.str_buf, xml + addr, total_len - addr); - } - - gdb_put_packet_binary(gdbserver_state.str_buf->str, - gdbserver_state.str_buf->len, true); -} - -static void handle_query_qemu_supported(GArray *params, void *user_ctx) -{ - g_string_printf(gdbserver_state.str_buf, "sstepbits;sstep"); -#ifndef CONFIG_USER_ONLY - g_string_append(gdbserver_state.str_buf, ";PhyMemMode"); -#endif - gdb_put_strbuf(); -} - -static const GdbCmdParseEntry gdb_gen_query_set_common_table[] = { - /* Order is important if has same prefix */ - { - .handler = handle_query_qemu_sstepbits, - .cmd = "qemu.sstepbits", - }, - { - .handler = handle_query_qemu_sstep, - .cmd = "qemu.sstep", - }, - { - .handler = handle_set_qemu_sstep, - .cmd = "qemu.sstep=", - .cmd_startswith = true, - .schema = "l0" - }, -}; - -/** - * extend_table() - extend one of the command tables - * @table: the command table to extend (or NULL) - * @extensions: a list of GdbCmdParseEntry pointers - * - * The entries themselves should be pointers to static const - * GdbCmdParseEntry entries. If the entry is already in the table we - * skip adding it again. - * - * Returns (a potentially freshly allocated) GPtrArray of GdbCmdParseEntry - */ -static GPtrArray *extend_table(GPtrArray *table, GPtrArray *extensions) -{ - if (!table) { - table = g_ptr_array_new(); - } - - for (int i = 0; i < extensions->len; i++) { - gpointer entry = g_ptr_array_index(extensions, i); - if (!g_ptr_array_find(table, entry, NULL)) { - g_ptr_array_add(table, entry); - } - } - - return table; -} - -/** - * process_extended_table() - run through an extended command table - * @table: the command table to check - * @data: parameters - * - * returns true if the command was found and executed - */ -static bool process_extended_table(GPtrArray *table, const char *data) -{ - for (int i = 0; i < table->len; i++) { - const GdbCmdParseEntry *entry = g_ptr_array_index(table, i); - if (process_string_cmd(data, entry, 1)) { - return true; - } - } - return false; -} - - -/* Ptr to GdbCmdParseEntry */ -static GPtrArray *extended_query_table; - -void gdb_extend_query_table(GPtrArray *new_queries) -{ - extended_query_table = extend_table(extended_query_table, new_queries); -} - -static const GdbCmdParseEntry gdb_gen_query_table[] = { - { - .handler = handle_query_curr_tid, - .cmd = "C", - }, - { - .handler = handle_query_threads, - .cmd = "sThreadInfo", - }, - { - .handler = handle_query_gdb_server_version, - .cmd = "GDBServerVersion", - }, - { - .handler = handle_query_first_threads, - .cmd = "fThreadInfo", - }, - { - .handler = handle_query_thread_extra, - .cmd = "ThreadExtraInfo,", - .cmd_startswith = true, - .schema = "t0" - }, -#ifdef CONFIG_USER_ONLY - { - .handler = gdb_handle_query_offsets, - .cmd = "Offsets", - }, -#else - { - .handler = gdb_handle_query_rcmd, - .cmd = "Rcmd,", - .cmd_startswith = true, - .schema = "s0" - }, -#endif - { - .handler = handle_query_supported, - .cmd = "Supported:", - .cmd_startswith = true, - .schema = "s0" - }, - { - .handler = handle_query_supported, - .cmd = "Supported", - .schema = "s0" - }, - { - .handler = handle_query_xfer_features, - .cmd = "Xfer:features:read:", - .cmd_startswith = true, - .schema = "s:l,l0" - }, -#if defined(CONFIG_USER_ONLY) -#if defined(CONFIG_LINUX) - { - .handler = gdb_handle_query_xfer_auxv, - .cmd = "Xfer:auxv:read::", - .cmd_startswith = true, - .schema = "l,l0" - }, - { - .handler = gdb_handle_query_xfer_siginfo, - .cmd = "Xfer:siginfo:read::", - .cmd_startswith = true, - .schema = "l,l0" - }, -#endif - { - .handler = gdb_handle_query_xfer_exec_file, - .cmd = "Xfer:exec-file:read:", - .cmd_startswith = true, - .schema = "l:l,l0" - }, -#endif - { - .handler = gdb_handle_query_attached, - .cmd = "Attached:", - .cmd_startswith = true - }, - { - .handler = gdb_handle_query_attached, - .cmd = "Attached", - }, - { - .handler = handle_query_qemu_supported, - .cmd = "qemu.Supported", - }, -#ifndef CONFIG_USER_ONLY - { - .handler = gdb_handle_query_qemu_phy_mem_mode, - .cmd = "qemu.PhyMemMode", - }, -#endif -}; - -/* Ptr to GdbCmdParseEntry */ -static GPtrArray *extended_set_table; - -void gdb_extend_set_table(GPtrArray *new_set) -{ - extended_set_table = extend_table(extended_set_table, new_set); -} - -static const GdbCmdParseEntry gdb_gen_set_table[] = { - /* Order is important if has same prefix */ - { - .handler = handle_set_qemu_sstep, - .cmd = "qemu.sstep:", - .cmd_startswith = true, - .schema = "l0" - }, -#ifndef CONFIG_USER_ONLY - { - .handler = gdb_handle_set_qemu_phy_mem_mode, - .cmd = "qemu.PhyMemMode:", - .cmd_startswith = true, - .schema = "l0" - }, -#endif -#if defined(CONFIG_USER_ONLY) - { - .handler = gdb_handle_set_catch_syscalls, - .cmd = "CatchSyscalls:", - .cmd_startswith = true, - .schema = "s0", - }, -#endif -}; - -static void handle_gen_query(GArray *params, void *user_ctx) -{ - const char *data; - - if (!params->len) { - return; - } - - data = gdb_get_cmd_param(params, 0)->data; - - if (process_string_cmd(data, - gdb_gen_query_set_common_table, - ARRAY_SIZE(gdb_gen_query_set_common_table))) { - return; - } - - if (process_string_cmd(data, - gdb_gen_query_table, - ARRAY_SIZE(gdb_gen_query_table))) { - return; - } - - if (extended_query_table && - process_extended_table(extended_query_table, data)) { - return; - } - - /* Can't handle query, return Empty response. */ - gdb_put_packet(""); -} - -static void handle_gen_set(GArray *params, void *user_ctx) -{ - const char *data; - - if (!params->len) { - return; - } - - data = gdb_get_cmd_param(params, 0)->data; - - if (process_string_cmd(data, - gdb_gen_query_set_common_table, - ARRAY_SIZE(gdb_gen_query_set_common_table))) { - return; - } - - if (process_string_cmd(data, - gdb_gen_set_table, - ARRAY_SIZE(gdb_gen_set_table))) { - return; - } - - if (extended_set_table && - process_extended_table(extended_set_table, data)) { - return; - } - - /* Can't handle set, return Empty response. */ - gdb_put_packet(""); -} - -static void handle_target_halt(GArray *params, void *user_ctx) -{ - if (gdbserver_state.allow_stop_reply) { - g_string_printf(gdbserver_state.str_buf, "T%02xthread:", GDB_SIGNAL_TRAP); - gdb_append_thread_id(gdbserver_state.c_cpu, gdbserver_state.str_buf); - g_string_append_c(gdbserver_state.str_buf, ';'); - gdb_put_strbuf(); - gdbserver_state.allow_stop_reply = false; - } - /* - * Remove all the breakpoints when this query is issued, - * because gdb is doing an initial connect and the state - * should be cleaned up. - */ - gdb_breakpoint_remove_all(gdbserver_state.c_cpu); -} - -static int gdb_handle_packet(const char *line_buf) -{ - const GdbCmdParseEntry *cmd_parser = NULL; - - trace_gdbstub_io_command(line_buf); - - switch (line_buf[0]) { - case '!': - gdb_put_packet("OK"); - break; - case '?': - { - static const GdbCmdParseEntry target_halted_cmd_desc = { - .handler = handle_target_halt, - .cmd = "?", - .cmd_startswith = true, - .allow_stop_reply = true, - }; - cmd_parser = &target_halted_cmd_desc; - } - break; - case 'c': - { - static const GdbCmdParseEntry continue_cmd_desc = { - .handler = handle_continue, - .cmd = "c", - .cmd_startswith = true, - .allow_stop_reply = true, - .schema = "L0" - }; - cmd_parser = &continue_cmd_desc; - } - break; - case 'C': - { - static const GdbCmdParseEntry cont_with_sig_cmd_desc = { - .handler = handle_cont_with_sig, - .cmd = "C", - .cmd_startswith = true, - .allow_stop_reply = true, - .schema = "l0" - }; - cmd_parser = &cont_with_sig_cmd_desc; - } - break; - case 'v': - { - static const GdbCmdParseEntry v_cmd_desc = { - .handler = handle_v_commands, - .cmd = "v", - .cmd_startswith = true, - .schema = "s0" - }; - cmd_parser = &v_cmd_desc; - } - break; - case 'k': - /* Kill the target */ - error_report("QEMU: Terminated via GDBstub"); - gdb_exit(0); - gdb_qemu_exit(0); - break; - case 'D': - { - static const GdbCmdParseEntry detach_cmd_desc = { - .handler = handle_detach, - .cmd = "D", - .cmd_startswith = true, - .schema = "?.l0" - }; - cmd_parser = &detach_cmd_desc; - } - break; - case 's': - { - static const GdbCmdParseEntry step_cmd_desc = { - .handler = handle_step, - .cmd = "s", - .cmd_startswith = true, - .allow_stop_reply = true, - .schema = "L0" - }; - cmd_parser = &step_cmd_desc; - } - break; - case 'b': - { - static const GdbCmdParseEntry backward_cmd_desc = { - .handler = handle_backward, - .cmd = "b", - .cmd_startswith = true, - .allow_stop_reply = true, - .schema = "o0" - }; - cmd_parser = &backward_cmd_desc; - } - break; - case 'F': - { - static const GdbCmdParseEntry file_io_cmd_desc = { - .handler = gdb_handle_file_io, - .cmd = "F", - .cmd_startswith = true, - .schema = "L,L,o0" - }; - cmd_parser = &file_io_cmd_desc; - } - break; - case 'g': - { - static const GdbCmdParseEntry read_all_regs_cmd_desc = { - .handler = handle_read_all_regs, - .cmd = "g", - .cmd_startswith = true - }; - cmd_parser = &read_all_regs_cmd_desc; - } - break; - case 'G': - { - static const GdbCmdParseEntry write_all_regs_cmd_desc = { - .handler = handle_write_all_regs, - .cmd = "G", - .cmd_startswith = true, - .schema = "s0" - }; - cmd_parser = &write_all_regs_cmd_desc; - } - break; - case 'm': - { - static const GdbCmdParseEntry read_mem_cmd_desc = { - .handler = handle_read_mem, - .cmd = "m", - .cmd_startswith = true, - .schema = "L,L0" - }; - cmd_parser = &read_mem_cmd_desc; - } - break; - case 'M': - { - static const GdbCmdParseEntry write_mem_cmd_desc = { - .handler = handle_write_mem, - .cmd = "M", - .cmd_startswith = true, - .schema = "L,L:s0" - }; - cmd_parser = &write_mem_cmd_desc; - } - break; - case 'p': - { - static const GdbCmdParseEntry get_reg_cmd_desc = { - .handler = handle_get_reg, - .cmd = "p", - .cmd_startswith = true, - .schema = "L0" - }; - cmd_parser = &get_reg_cmd_desc; - } - break; - case 'P': - { - static const GdbCmdParseEntry set_reg_cmd_desc = { - .handler = handle_set_reg, - .cmd = "P", - .cmd_startswith = true, - .schema = "L?s0" - }; - cmd_parser = &set_reg_cmd_desc; - } - break; - case 'Z': - { - static const GdbCmdParseEntry insert_bp_cmd_desc = { - .handler = handle_insert_bp, - .cmd = "Z", - .cmd_startswith = true, - .schema = "l?L?L0" - }; - cmd_parser = &insert_bp_cmd_desc; - } - break; - case 'z': - { - static const GdbCmdParseEntry remove_bp_cmd_desc = { - .handler = handle_remove_bp, - .cmd = "z", - .cmd_startswith = true, - .schema = "l?L?L0" - }; - cmd_parser = &remove_bp_cmd_desc; - } - break; - case 'H': - { - static const GdbCmdParseEntry set_thread_cmd_desc = { - .handler = handle_set_thread, - .cmd = "H", - .cmd_startswith = true, - .schema = "o.t0" - }; - cmd_parser = &set_thread_cmd_desc; - } - break; - case 'T': - { - static const GdbCmdParseEntry thread_alive_cmd_desc = { - .handler = handle_thread_alive, - .cmd = "T", - .cmd_startswith = true, - .schema = "t0" - }; - cmd_parser = &thread_alive_cmd_desc; - } - break; - case 'q': - { - static const GdbCmdParseEntry gen_query_cmd_desc = { - .handler = handle_gen_query, - .cmd = "q", - .cmd_startswith = true, - .schema = "s0" - }; - cmd_parser = &gen_query_cmd_desc; - } - break; - case 'Q': - { - static const GdbCmdParseEntry gen_set_cmd_desc = { - .handler = handle_gen_set, - .cmd = "Q", - .cmd_startswith = true, - .schema = "s0" - }; - cmd_parser = &gen_set_cmd_desc; - } - break; - default: - /* put empty packet */ - gdb_put_packet(""); - break; - } - - if (cmd_parser) { - run_cmd_parser(line_buf, cmd_parser); - } - - return RS_IDLE; -} - -void gdb_set_stop_cpu(CPUState *cpu) -{ - GDBProcess *p = gdb_get_cpu_process(cpu); - - if (!p->attached) { - /* - * Having a stop CPU corresponding to a process that is not attached - * confuses GDB. So we ignore the request. - */ - return; - } - - gdbserver_state.c_cpu = cpu; - gdbserver_state.g_cpu = cpu; -} - -void gdb_read_byte(uint8_t ch) -{ - uint8_t reply; - - gdbserver_state.allow_stop_reply = false; -#ifndef CONFIG_USER_ONLY - if (gdbserver_state.last_packet->len) { - /* Waiting for a response to the last packet. If we see the start - of a new command then abandon the previous response. */ - if (ch == '-') { - trace_gdbstub_err_got_nack(); - gdb_put_buffer(gdbserver_state.last_packet->data, - gdbserver_state.last_packet->len); - } else if (ch == '+') { - trace_gdbstub_io_got_ack(); - } else { - trace_gdbstub_io_got_unexpected(ch); - } - - if (ch == '+' || ch == '$') { - g_byte_array_set_size(gdbserver_state.last_packet, 0); - } - if (ch != '$') - return; - } - if (runstate_is_running()) { - /* - * When the CPU is running, we cannot do anything except stop - * it when receiving a char. This is expected on a Ctrl-C in the - * gdb client. Because we are in all-stop mode, gdb sends a - * 0x03 byte which is not a usual packet, so we handle it specially - * here, but it does expect a stop reply. - */ - if (ch != 0x03) { - trace_gdbstub_err_unexpected_runpkt(ch); - } else { - gdbserver_state.allow_stop_reply = true; - } - vm_stop(RUN_STATE_PAUSED); - } else -#endif - { - switch(gdbserver_state.state) { - case RS_IDLE: - if (ch == '$') { - /* start of command packet */ - gdbserver_state.line_buf_index = 0; - gdbserver_state.line_sum = 0; - gdbserver_state.state = RS_GETLINE; - } else if (ch == '+') { - /* - * do nothing, gdb may preemptively send out ACKs on - * initial connection - */ - } else { - trace_gdbstub_err_garbage(ch); - } - break; - case RS_GETLINE: - if (ch == '}') { - /* start escape sequence */ - gdbserver_state.state = RS_GETLINE_ESC; - gdbserver_state.line_sum += ch; - } else if (ch == '*') { - /* start run length encoding sequence */ - gdbserver_state.state = RS_GETLINE_RLE; - gdbserver_state.line_sum += ch; - } else if (ch == '#') { - /* end of command, start of checksum*/ - gdbserver_state.state = RS_CHKSUM1; - } else if (gdbserver_state.line_buf_index >= sizeof(gdbserver_state.line_buf) - 1) { - trace_gdbstub_err_overrun(); - gdbserver_state.state = RS_IDLE; - } else { - /* unescaped command character */ - gdbserver_state.line_buf[gdbserver_state.line_buf_index++] = ch; - gdbserver_state.line_sum += ch; - } - break; - case RS_GETLINE_ESC: - if (ch == '#') { - /* unexpected end of command in escape sequence */ - gdbserver_state.state = RS_CHKSUM1; - } else if (gdbserver_state.line_buf_index >= sizeof(gdbserver_state.line_buf) - 1) { - /* command buffer overrun */ - trace_gdbstub_err_overrun(); - gdbserver_state.state = RS_IDLE; - } else { - /* parse escaped character and leave escape state */ - gdbserver_state.line_buf[gdbserver_state.line_buf_index++] = ch ^ 0x20; - gdbserver_state.line_sum += ch; - gdbserver_state.state = RS_GETLINE; - } - break; - case RS_GETLINE_RLE: - /* - * Run-length encoding is explained in "Debugging with GDB / - * Appendix E GDB Remote Serial Protocol / Overview". - */ - if (ch < ' ' || ch == '#' || ch == '$' || ch > 126) { - /* invalid RLE count encoding */ - trace_gdbstub_err_invalid_repeat(ch); - gdbserver_state.state = RS_GETLINE; - } else { - /* decode repeat length */ - int repeat = ch - ' ' + 3; - if (gdbserver_state.line_buf_index + repeat >= sizeof(gdbserver_state.line_buf) - 1) { - /* that many repeats would overrun the command buffer */ - trace_gdbstub_err_overrun(); - gdbserver_state.state = RS_IDLE; - } else if (gdbserver_state.line_buf_index < 1) { - /* got a repeat but we have nothing to repeat */ - trace_gdbstub_err_invalid_rle(); - gdbserver_state.state = RS_GETLINE; - } else { - /* repeat the last character */ - memset(gdbserver_state.line_buf + gdbserver_state.line_buf_index, - gdbserver_state.line_buf[gdbserver_state.line_buf_index - 1], repeat); - gdbserver_state.line_buf_index += repeat; - gdbserver_state.line_sum += ch; - gdbserver_state.state = RS_GETLINE; - } - } - break; - case RS_CHKSUM1: - /* get high hex digit of checksum */ - if (!isxdigit(ch)) { - trace_gdbstub_err_checksum_invalid(ch); - gdbserver_state.state = RS_GETLINE; - break; - } - gdbserver_state.line_buf[gdbserver_state.line_buf_index] = '\0'; - gdbserver_state.line_csum = fromhex(ch) << 4; - gdbserver_state.state = RS_CHKSUM2; - break; - case RS_CHKSUM2: - /* get low hex digit of checksum */ - if (!isxdigit(ch)) { - trace_gdbstub_err_checksum_invalid(ch); - gdbserver_state.state = RS_GETLINE; - break; - } - gdbserver_state.line_csum |= fromhex(ch); - - if (gdbserver_state.line_csum != (gdbserver_state.line_sum & 0xff)) { - trace_gdbstub_err_checksum_incorrect(gdbserver_state.line_sum, gdbserver_state.line_csum); - /* send NAK reply */ - reply = '-'; - gdb_put_buffer(&reply, 1); - gdbserver_state.state = RS_IDLE; - } else { - /* send ACK reply */ - reply = '+'; - gdb_put_buffer(&reply, 1); - gdbserver_state.state = gdb_handle_packet(gdbserver_state.line_buf); - } - break; - default: - abort(); - } - } -} - -/* - * Create the process that will contain all the "orphan" CPUs (that are not - * part of a CPU cluster). Note that if this process contains no CPUs, it won't - * be attachable and thus will be invisible to the user. - */ -void gdb_create_default_process(GDBState *s) -{ - GDBProcess *process; - int pid; - -#ifdef CONFIG_USER_ONLY - assert(gdbserver_state.process_num == 0); - pid = getpid(); -#else - if (gdbserver_state.process_num) { - pid = s->processes[s->process_num - 1].pid; - } else { - pid = 0; - } - /* We need an available PID slot for this process */ - assert(pid < UINT32_MAX); - pid++; -#endif - - s->processes = g_renew(GDBProcess, s->processes, ++s->process_num); - process = &s->processes[s->process_num - 1]; - process->pid = pid; - process->attached = false; - process->target_xml = NULL; -} - diff --git a/backup/gdbstub.h b/backup/gdbstub.h deleted file mode 100644 index a16c005..0000000 --- a/backup/gdbstub.h +++ /dev/null @@ -1,164 +0,0 @@ -#ifndef GDBSTUB_H -#define GDBSTUB_H - -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); - -/** - * gdb_init_cpu(): Initialize the CPU for gdbstub. - * @cpu: The CPU to be initialized. - */ -void gdb_init_cpu(CPUState *cpu); - -/** - * gdb_register_coprocessor() - register a supplemental set of registers - * @cpu - the CPU associated with registers - * @get_reg - get function (gdb reading) - * @set_reg - set function (gdb modifying) - * @num_regs - number of registers in set - * @xml - xml name of set - * @gpos - non-zero to append to "general" register set at @gpos - */ -void gdb_register_coprocessor(CPUState *cpu, - gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg, - const GDBFeature *feature, int g_pos); - -/** - * gdb_unregister_coprocessor_all() - unregisters supplemental set of registers - * @cpu - the CPU associated with registers - */ -void gdb_unregister_coprocessor_all(CPUState *cpu); - -/** - * gdbserver_start: start the gdb server - * @port_or_device: connection spec for gdb - * @errp: error handle - * - * For CONFIG_USER this is either a tcp port or a path to a fifo. For - * system emulation you can use a full chardev spec for your gdbserver - * port. - * - * The error handle should be either &error_fatal (for start-up) or - * &error_warn (for QMP/HMP initiated sessions). - * - * Returns true when server successfully started. - */ -bool gdbserver_start(const char *port_or_device, Error **errp); - -/** - * gdb_feature_builder_init() - Initialize GDBFeatureBuilder. - * @builder: The builder to be initialized. - * @feature: The feature to be filled. - * @name: The name of the feature. - * @xmlname: The name of the XML. - * @base_reg: The base number of the register ID. - */ -void gdb_feature_builder_init(GDBFeatureBuilder *builder, GDBFeature *feature, - const char *name, const char *xmlname, - int base_reg); - -/** - * 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); - -/** - * gdb_feature_builder_end() - End building GDBFeature. - * @builder: The builder. - */ -void gdb_feature_builder_end(const GDBFeatureBuilder *builder); - -/** - * gdb_find_static_feature() - Find a static feature. - * @xmlname: The name of the XML. - * - * Return: The static feature. - */ -const GDBFeature *gdb_find_static_feature(const char *xmlname); - -/** - * gdb_read_register() - Read a register associated with a CPU. - * @cpu: The CPU associated with the register. - * @buf: The buffer that the read register will be appended to. - * @reg: The register's number returned by gdb_find_feature_register(). - * - * Return: The number of read bytes. - */ -int gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); - -/** - * gdb_write_register() - Write a register associated with a CPU. - * @cpu: The CPU associated with the register. - * @buf: The buffer that the register contents will be set to. - * @reg: The register's number returned by gdb_find_feature_register(). - * - * The size of @buf must be at least the size of the register being - * written. - * - * Return: The number of written bytes, or 0 if an error occurred (for - * example, an unknown register was provided). - */ -int gdb_write_register(CPUState *cpu, uint8_t *mem_buf, int reg); - -/** - * typedef GDBRegDesc - a register description from gdbstub - */ -typedef struct { - int gdb_reg; - const char *name; - const char *feature_name; -} GDBRegDesc; - -/** - * gdb_get_register_list() - Return list of all registers for CPU - * @cpu: The CPU being searched - * - * Returns a GArray of GDBRegDesc, caller frees array but not the - * const strings. - */ -GArray *gdb_get_register_list(CPUState *cpu); - -void gdb_set_stop_cpu(CPUState *cpu); - -/* in gdbstub-xml.c, generated by scripts/feature_to_c.py */ -extern const GDBFeature gdb_static_features[]; - -#endif /* GDBSTUB_H */ diff --git a/backup/helpers.h b/backup/helpers.h deleted file mode 100644 index b685afa..0000000 --- a/backup/helpers.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * gdbstub helpers - * - * These are all used by the various frontends and have to be host - * aware to ensure things are store in target order. - * - * Copyright (c) 2022 Linaro Ltd - * - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#ifndef _GDBSTUB_HELPERS_H_ -#define _GDBSTUB_HELPERS_H_ - -#ifndef COMPILING_PER_TARGET -#error "gdbstub helpers should only be included by target specific code" -#endif - -#include "qemu/bswap.h" -#include "qemu/target-info.h" -#include "cpu-param.h" - -/* - * The GDB remote protocol transfers values in target byte order. As - * the gdbstub may be batching up several register values we always - * append to the array. - */ - -static inline int gdb_get_reg8(GByteArray *buf, uint8_t val) -{ - g_byte_array_append(buf, &val, 1); - return 1; -} - -static inline int gdb_get_reg16(GByteArray *buf, uint16_t val) -{ - if (target_big_endian()) { - cpu_to_be16s(&val); - } else { - cpu_to_le16s(&val); - } - g_byte_array_append(buf, (uint8_t *) &val, 2); - return 2; -} - -static inline int gdb_get_reg32(GByteArray *buf, uint32_t val) -{ - if (target_big_endian()) { - cpu_to_be32s(&val); - } else { - cpu_to_le32s(&val); - } - g_byte_array_append(buf, (uint8_t *) &val, 4); - return 4; -} - -static inline int gdb_get_reg64(GByteArray *buf, uint64_t val) -{ - if (target_big_endian()) { - cpu_to_be64s(&val); - } else { - cpu_to_le64s(&val); - } - g_byte_array_append(buf, (uint8_t *) &val, 8); - return 8; -} - -static inline int gdb_get_reg128(GByteArray *buf, uint64_t val_hi, - uint64_t val_lo) -{ - uint64_t tmp[2]; - if (target_big_endian()) { - tmp[0] = cpu_to_be64(val_hi); - tmp[1] = cpu_to_be64(val_lo); - } else { - tmp[0] = cpu_to_le64(val_lo); - tmp[1] = cpu_to_le64(val_hi); - } - g_byte_array_append(buf, (uint8_t *)&tmp, 16); - return 16; -} - -static inline int gdb_get_zeroes(GByteArray *array, size_t len) -{ - guint oldlen = array->len; - g_byte_array_set_size(array, oldlen + len); - memset(array->data + oldlen, 0, len); - - return len; -} - -/** - * gdb_get_reg_ptr: get pointer to start of last element - * @len: length of element - * - * This is a helper function to extract the pointer to the last - * element for additional processing. Some front-ends do additional - * dynamic swapping of the elements based on CPU state. - */ -static inline uint8_t *gdb_get_reg_ptr(GByteArray *buf, int len) -{ - return buf->data + buf->len - len; -} - -#if TARGET_LONG_BITS == 64 -#define gdb_get_regl(buf, val) gdb_get_reg64(buf, val) -#define ldtul_p(addr) ldq_p(addr) -#define ldtul_le_p(addr) ldq_le_p(addr) -#define ldtul_be_p(addr) ldq_be_p(addr) -#else -#define gdb_get_regl(buf, val) gdb_get_reg32(buf, val) -#define ldtul_p(addr) ldl_p(addr) -#define ldtul_le_p(addr) ldl_le_p(addr) -#define ldtul_be_p(addr) ldl_be_p(addr) -#endif - -#endif /* _GDBSTUB_HELPERS_H_ */ diff --git a/backup/internals.h b/backup/internals.h deleted file mode 100644 index 92466b2..0000000 --- a/backup/internals.h +++ /dev/null @@ -1,240 +0,0 @@ -/* - * gdbstub internals - * - * Copyright (c) 2022 Linaro Ltd - * - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#ifndef GDBSTUB_INTERNALS_H -#define GDBSTUB_INTERNALS_H - -#include "exec/cpu-common.h" - -/* - * Most "large" transfers (e.g. memory reads, feature XML - * transfer) have mechanisms in the gdb protocol for splitting - * them. However, register values in particular cannot currently - * be split. This packet size must therefore be at least big enough - * for the worst-case register size. Currently that is Arm SME - * ZA storage with a 256x256 byte value. We also must account - * for the conversion from raw data to hex in gdb_memtohex(), - * which writes 2 * size bytes, and for other protocol overhead - * including command, register number and checksum which add - * another 4 bytes of overhead. However, to be consistent with - * the changes made in gdbserver to address this same requirement, - * we add a total of 32 bytes to account for protocol overhead - * (unclear why specifically 32 bytes), bringing the value of - * MAX_PACKET_LENGTH to 2 * 256 * 256 + 32 = 131104. - * - * The commit making this change for gdbserver can be found here: - * https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h= - * b816042e88583f280ad186ff124ab84d31fb592b - */ -#define MAX_PACKET_LENGTH 131104 - -/* - * Shared structures and definitions - */ - -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 GDBState { - bool init; /* have we been initialised? */ - CPUState *c_cpu; /* current CPU for step/continue ops */ - CPUState *g_cpu; /* current CPU for other ops */ - CPUState *query_cpu; /* for q{f|s}ThreadInfo */ - 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; - bool multiprocess; - GDBProcess *processes; - int process_num; - GString *str_buf; - GByteArray *mem_buf; - int sstep_flags; - int supported_sstep_flags; - /* - * 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 gdbserver_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); -int gdb_put_packet_binary(const char *buf, int len, bool dump); -void gdb_memtohex(GString *buf, const uint8_t *mem, int len); -void gdb_memtox(GString *buf, const char *mem, int len); -void gdb_read_byte(uint8_t ch); - -/* - * Packet acknowledgement - we handle this slightly differently - * between user and system mode, mainly to deal with the differences - * between the flexible chardev and the direct fd approaches. - * - * We currently don't support a negotiated QStartNoAckMode - */ - -/** - * gdb_got_immediate_ack() - check ok to continue - * - * Returns true to continue, false to re-transmit for user only, the - * system stub always returns true. - */ -bool gdb_got_immediate_ack(void); -/* utility helpers */ -GDBProcess *gdb_get_process(uint32_t pid); -CPUState *gdb_get_first_cpu_in_process(GDBProcess *process); -CPUState *gdb_first_attached_cpu(void); -void gdb_append_thread_id(CPUState *cpu, GString *buf); -int gdb_get_cpu_index(CPUState *cpu); -unsigned int gdb_get_max_cpus(void); /* both */ -bool gdb_can_reverse(void); /* system emulation, stub for user */ -int gdb_target_sigtrap(void); /* user */ - -void gdb_create_default_process(GDBState *s); - -/* signal mapping, common for system, specialised for user-mode */ -int gdb_signal_to_target(int sig); -int gdb_target_signal_to_gdb(int sig); - -int gdb_get_char(void); /* user only */ - -/** - * gdb_continue() - handle continue in mode specific way. - */ -void gdb_continue(void); - -/** - * gdb_continue_partial() - handle partial continue in mode specific way. - */ -int gdb_continue_partial(char *newstates); - -/* - * Helpers with separate system and user implementations - */ -void gdb_put_buffer(const uint8_t *buf, int len); - -/* - * Command handlers - either specialised or system or user only - */ -void gdb_init_gdbserver_state(void); - -void gdb_handle_query_rcmd(GArray *params, void *ctx); /* system */ -void gdb_handle_query_offsets(GArray *params, void *user_ctx); /* user */ -void gdb_handle_query_xfer_auxv(GArray *params, void *user_ctx); /*user */ -void gdb_handle_query_xfer_siginfo(GArray *params, void *user_ctx); /*user */ -void gdb_handle_v_file_open(GArray *params, void *user_ctx); /* user */ -void gdb_handle_v_file_close(GArray *params, void *user_ctx); /* user */ -void gdb_handle_v_file_pread(GArray *params, void *user_ctx); /* user */ -void gdb_handle_v_file_readlink(GArray *params, void *user_ctx); /* user */ -void gdb_handle_query_xfer_exec_file(GArray *params, void *user_ctx); /* user */ -void gdb_handle_set_catch_syscalls(GArray *params, void *user_ctx); /* user */ -void gdb_handle_query_supported_user(const char *gdb_supported); /* user */ -bool gdb_handle_set_thread_user(uint32_t pid, uint32_t tid); /* user */ -bool gdb_handle_detach_user(uint32_t pid); /* user */ - -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); -bool gdb_handled_syscall(void); -void gdb_disable_syscalls(void); -void gdb_syscall_reset(void); - -/* user/system specific syscall handling */ -void gdb_syscall_handling(const char *syscall_packet); - -/* - * Break/Watch point support - there is an implementation for system - * and user mode. - */ -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_breakpoint_remove_all(CPUState *cs); - -/** - * gdb_target_memory_rw_debug() - handle debug access to memory - * @cs: CPUState - * @addr: nominal address, could be an entire physical address - * @buf: data - * @len: length of access - * @is_write: is it a write operation - * - * This function is specialised depending on the mode we are running - * in. For system guests we can switch the interpretation of the - * address to a physical address. - */ -int gdb_target_memory_rw_debug(CPUState *cs, hwaddr addr, - uint8_t *buf, int len, bool is_write); - -#endif /* GDBSTUB_INTERNALS_H */ diff --git a/backup/meson.build b/backup/meson.build deleted file mode 100644 index 15c666f..0000000 --- a/backup/meson.build +++ /dev/null @@ -1,21 +0,0 @@ -# -# The main gdbstub still relies on per-build definitions of various -# types. The bits pushed to system/user.c try to use guest agnostic -# types such as hwaddr. -# - -# We build two versions of gdbstub, one for each mode -user_ss.add(files( - 'gdbstub.c', - 'syscalls.c', - 'user.c' -)) - -system_ss.add(files( - 'gdbstub.c', - 'syscalls.c', - 'system.c' -)) - -# The user-target is specialised by the guest -specific_ss.add(when: 'CONFIG_USER_ONLY', if_true: files('user-target.c')) diff --git a/backup/syscalls.c b/backup/syscalls.c deleted file mode 100644 index e855df2..0000000 --- a/backup/syscalls.c +++ /dev/null @@ -1,206 +0,0 @@ -/* - * GDB Syscall Handling - * - * GDB can execute syscalls on the guests behalf, currently used by - * the various semihosting extensions. - * - * Copyright (c) 2003-2005 Fabrice Bellard - * Copyright (c) 2023 Linaro Ltd - * - * SPDX-License-Identifier: LGPL-2.0-or-later - */ - -#include "qemu/osdep.h" -#include "qemu/error-report.h" -#include "semihosting/semihost.h" -#include "system/runstate.h" -#include "gdbstub/user.h" -#include "gdbstub/syscalls.h" -#include "gdbstub/commands.h" -#include "trace.h" -#include "internals.h" - -/* Syscall specific state */ -typedef struct { - char syscall_buf[256]; - gdb_syscall_complete_cb current_syscall_cb; -} GDBSyscallState; - -static GDBSyscallState gdbserver_syscall_state; - -/* - * Return true if there is a GDB currently connected to the stub - * and attached to a CPU - */ -static bool gdb_attached(void) -{ - return gdbserver_state.init && gdbserver_state.c_cpu; -} - -static enum { - GDB_SYS_UNKNOWN, - GDB_SYS_ENABLED, - GDB_SYS_DISABLED, -} gdb_syscall_mode; - -/* Decide if either remote gdb syscalls or native file IO should be used. */ -int use_gdb_syscalls(void) -{ - SemihostingTarget target = semihosting_get_target(); - if (target == SEMIHOSTING_TARGET_NATIVE) { - /* -semihosting-config target=native */ - return false; - } else if (target == SEMIHOSTING_TARGET_GDB) { - /* -semihosting-config target=gdb */ - return true; - } - - /* -semihosting-config target=auto */ - /* On the first call check if gdb is connected and remember. */ - if (gdb_syscall_mode == GDB_SYS_UNKNOWN) { - gdb_syscall_mode = gdb_attached() ? GDB_SYS_ENABLED : GDB_SYS_DISABLED; - } - return gdb_syscall_mode == GDB_SYS_ENABLED; -} - -/* called when the stub detaches */ -void gdb_disable_syscalls(void) -{ - gdb_syscall_mode = GDB_SYS_DISABLED; -} - -void gdb_syscall_reset(void) -{ - gdbserver_syscall_state.current_syscall_cb = NULL; -} - -bool gdb_handled_syscall(void) -{ - if (gdbserver_syscall_state.current_syscall_cb) { - gdb_put_packet(gdbserver_syscall_state.syscall_buf); - return true; - } - - return false; -} - -/* - * Send a gdb syscall request. - * This accepts limited printf-style format specifiers, specifically: - * %x - target_ulong argument printed in hex. - * %lx - 64-bit argument printed in hex. - * %s - string pointer (target_ulong) and length (int) pair. - */ -void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...) -{ - char *p, *p_end; - va_list va; - - if (!gdb_attached()) { - return; - } - - gdbserver_syscall_state.current_syscall_cb = cb; - va_start(va, fmt); - - p = gdbserver_syscall_state.syscall_buf; - p_end = p + sizeof(gdbserver_syscall_state.syscall_buf); - *(p++) = 'F'; - while (*fmt) { - if (*fmt == '%') { - uint64_t i64; - uint32_t i32; - - fmt++; - switch (*fmt++) { - case 'x': - i32 = va_arg(va, uint32_t); - p += snprintf(p, p_end - p, "%" PRIx32, i32); - break; - case 'l': - if (*(fmt++) != 'x') { - goto bad_format; - } - i64 = va_arg(va, uint64_t); - p += snprintf(p, p_end - p, "%" PRIx64, i64); - break; - case 's': - i64 = va_arg(va, uint64_t); - i32 = va_arg(va, uint32_t); - p += snprintf(p, p_end - p, "%" PRIx64 "/%x" PRIx32, i64, i32); - break; - default: - bad_format: - error_report("gdbstub: Bad syscall format string '%s'", - fmt - 1); - break; - } - } else { - *(p++) = *(fmt++); - } - } - *p = 0; - - va_end(va); - gdb_syscall_handling(gdbserver_syscall_state.syscall_buf); -} - -/* - * GDB Command Handlers - */ - -void gdb_handle_file_io(GArray *params, void *user_ctx) -{ - if (params->len >= 1 && gdbserver_syscall_state.current_syscall_cb) { - uint64_t ret; - int err; - - ret = gdb_get_cmd_param(params, 0)->val_ull; - if (params->len >= 2) { - err = gdb_get_cmd_param(params, 1)->val_ull; - } else { - err = 0; - } - - /* Convert GDB error numbers back to host error numbers. */ -#define E(X) case GDB_E##X: err = E##X; break - switch (err) { - case 0: - break; - E(PERM); - E(NOENT); - E(INTR); - E(BADF); - E(ACCES); - E(FAULT); - E(BUSY); - E(EXIST); - E(NODEV); - E(NOTDIR); - E(ISDIR); - E(INVAL); - E(NFILE); - E(MFILE); - E(FBIG); - E(NOSPC); - E(SPIPE); - E(ROFS); - E(NAMETOOLONG); - default: - err = EINVAL; - break; - } -#undef E - - gdbserver_syscall_state.current_syscall_cb(gdbserver_state.c_cpu, - ret, err); - gdbserver_syscall_state.current_syscall_cb = NULL; - } - - if (params->len >= 3 && gdb_get_cmd_param(params, 2)->opcode == (uint8_t)'C') { - gdb_put_packet("T02"); - return; - } - - gdb_continue(); -} diff --git a/backup/syscalls.h b/backup/syscalls.h deleted file mode 100644 index d63228e..0000000 --- a/backup/syscalls.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * GDB Syscall support - * - * Copyright (c) 2023 Linaro Ltd - * - * SPDX-License-Identifier: LGPL-2.0-or-later - */ - -#ifndef _SYSCALLS_H_ -#define _SYSCALLS_H_ - -/* For gdb file i/o remote protocol open flags. */ -#define GDB_O_RDONLY 0 -#define GDB_O_WRONLY 1 -#define GDB_O_RDWR 2 -#define GDB_O_APPEND 8 -#define GDB_O_CREAT 0x200 -#define GDB_O_TRUNC 0x400 -#define GDB_O_EXCL 0x800 - -/* For gdb file i/o remote protocol errno values */ -#define GDB_EPERM 1 -#define GDB_ENOENT 2 -#define GDB_EINTR 4 -#define GDB_EBADF 9 -#define GDB_EACCES 13 -#define GDB_EFAULT 14 -#define GDB_EBUSY 16 -#define GDB_EEXIST 17 -#define GDB_ENODEV 19 -#define GDB_ENOTDIR 20 -#define GDB_EISDIR 21 -#define GDB_EINVAL 22 -#define GDB_ENFILE 23 -#define GDB_EMFILE 24 -#define GDB_EFBIG 27 -#define GDB_ENOSPC 28 -#define GDB_ESPIPE 29 -#define GDB_EROFS 30 -#define GDB_ENAMETOOLONG 91 -#define GDB_EUNKNOWN 9999 - -/* For gdb file i/o remote protocol lseek whence. */ -#define GDB_SEEK_SET 0 -#define GDB_SEEK_CUR 1 -#define GDB_SEEK_END 2 - -/* For gdb file i/o stat/fstat. */ -typedef uint32_t gdb_mode_t; -typedef uint32_t gdb_time_t; - -struct gdb_stat { - uint32_t gdb_st_dev; /* device */ - uint32_t gdb_st_ino; /* inode */ - gdb_mode_t gdb_st_mode; /* protection */ - uint32_t gdb_st_nlink; /* number of hard links */ - uint32_t gdb_st_uid; /* user ID of owner */ - uint32_t gdb_st_gid; /* group ID of owner */ - uint32_t gdb_st_rdev; /* device type (if inode device) */ - uint64_t gdb_st_size; /* total size, in bytes */ - uint64_t gdb_st_blksize; /* blocksize for filesystem I/O */ - uint64_t gdb_st_blocks; /* number of blocks allocated */ - gdb_time_t gdb_st_atime; /* time of last access */ - gdb_time_t gdb_st_mtime; /* time of last modification */ - gdb_time_t gdb_st_ctime; /* time of last change */ -} QEMU_PACKED; - -struct gdb_timeval { - gdb_time_t tv_sec; /* second */ - uint64_t tv_usec; /* microsecond */ -} QEMU_PACKED; - -typedef void (*gdb_syscall_complete_cb)(CPUState *cpu, uint64_t ret, int err); - -/** - * gdb_do_syscall: - * @cb: function to call when the system call has completed - * @fmt: gdb syscall format string - * ...: list of arguments to interpolate into @fmt - * - * Send a GDB syscall request. This function will return immediately; - * the callback function will be called later when the remote system - * call has completed. - * - * @fmt should be in the 'call-id,parameter,parameter...' format documented - * for the F request packet in the GDB remote protocol. A limited set of - * printf-style format specifiers is supported: - * %x - target_ulong argument printed in hex - * %lx - 64-bit argument printed in hex - * %s - string pointer (target_ulong) and length (int) pair - */ -void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...); - -/** - * use_gdb_syscalls() - report if GDB should be used for syscalls - * - * This is mostly driven by the semihosting mode the user configures - * but assuming GDB is allowed by that we report true if GDB is - * connected to the stub. - */ -int use_gdb_syscalls(void); - -/** - * gdb_exit: exit gdb session, reporting inferior status - * @code: exit code reported - * - * This closes the session and sends a final packet to GDB reporting - * the exit status of the program. It also cleans up any connections - * detritus before returning. - */ -void gdb_exit(int code); - -/** - * gdb_qemu_exit: ask qemu to exit - * @code: exit code reported - * - * This requests qemu to exit. This function is allowed to return as - * the exit request might be processed asynchronously by qemu backend. - */ -void gdb_qemu_exit(int code); - -#endif /* _SYSCALLS_H_ */ diff --git a/backup/system.c b/backup/system.c deleted file mode 100644 index 5be0d3c..0000000 --- a/backup/system.c +++ /dev/null @@ -1,669 +0,0 @@ -/* - * gdb server stub - system specific bits - * - * Debug integration depends on support from the individual - * accelerators so most of this involves calling the ops helpers. - * - * Copyright (c) 2003-2005 Fabrice Bellard - * Copyright (c) 2022 Linaro Ltd - * - * SPDX-License-Identifier: LGPL-2.0-or-later - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu/error-report.h" -#include "qemu/cutils.h" -#include "exec/gdbstub.h" -#include "gdbstub/syscalls.h" -#include "gdbstub/commands.h" -#include "exec/hwaddr.h" -#include "exec/tb-flush.h" -#include "accel/accel-ops.h" -#include "accel/accel-cpu-ops.h" -#include "system/cpus.h" -#include "system/runstate.h" -#include "system/replay.h" -#include "system/tcg.h" -#include "hw/core/cpu.h" -#include "hw/cpu/cluster.h" -#include "hw/boards.h" -#include "chardev/char.h" -#include "chardev/char-fe.h" -#include "monitor/monitor.h" -#include "trace.h" -#include "internals.h" - -/* System emulation specific state */ -typedef struct { - CharBackend chr; - Chardev *mon_chr; -} GDBSystemState; - -GDBSystemState gdbserver_system_state; - -static void reset_gdbserver_state(void) -{ - g_free(gdbserver_state.processes); - gdbserver_state.processes = NULL; - gdbserver_state.process_num = 0; - gdbserver_state.allow_stop_reply = false; -} - -/* - * Return the GDB index for a given vCPU state. - * - * In system mode GDB numbers CPUs from 1 as 0 is reserved as an "any - * cpu" index. - */ -int gdb_get_cpu_index(CPUState *cpu) -{ - return cpu->cpu_index + 1; -} - -/* - * We check the status of the last message in the chardev receive code - */ -bool gdb_got_immediate_ack(void) -{ - return true; -} - -/* - * GDB Connection management. For system emulation we do all of this - * via our existing Chardev infrastructure which allows us to support - * network and unix sockets. - */ - -void gdb_put_buffer(const uint8_t *buf, int len) -{ - /* - * XXX this blocks entire thread. Rewrite to use - * qemu_chr_fe_write and background I/O callbacks - */ - qemu_chr_fe_write_all(&gdbserver_system_state.chr, buf, len); -} - -static void gdb_chr_event(void *opaque, QEMUChrEvent event) -{ - int i; - GDBState *s = (GDBState *) opaque; - - switch (event) { - case CHR_EVENT_OPENED: - /* Start with first process attached, others detached */ - for (i = 0; i < s->process_num; i++) { - s->processes[i].attached = !i; - } - - s->c_cpu = gdb_first_attached_cpu(); - s->g_cpu = s->c_cpu; - - vm_stop(RUN_STATE_PAUSED); - replay_gdb_attached(); - break; - default: - break; - } -} - -/* - * In system-mode we stop the VM and wait to send the syscall packet - * until notification that the CPU has stopped. This must be done - * because if the packet is sent now the reply from the syscall - * request could be received while the CPU is still in the running - * state, which can cause packets to be dropped and state transition - * 'T' packets to be sent while the syscall is still being processed. - */ -void gdb_syscall_handling(const char *syscall_packet) -{ - vm_stop(RUN_STATE_DEBUG); - qemu_cpu_kick(gdbserver_state.c_cpu); -} - -static void gdb_vm_state_change(void *opaque, bool running, RunState state) -{ - CPUState *cpu = gdbserver_state.c_cpu; - g_autoptr(GString) buf = g_string_new(NULL); - g_autoptr(GString) tid = g_string_new(NULL); - const char *type; - int ret; - - if (running || gdbserver_state.state == RS_INACTIVE) { - return; - } - - /* Is there a GDB syscall waiting to be sent? */ - if (gdb_handled_syscall()) { - return; - } - - if (cpu == NULL) { - /* No process attached */ - return; - } - - if (!gdbserver_state.allow_stop_reply) { - return; - } - - gdb_append_thread_id(cpu, tid); - - switch (state) { - case RUN_STATE_DEBUG: - if (cpu->watchpoint_hit) { - switch (cpu->watchpoint_hit->flags & BP_MEM_ACCESS) { - case BP_MEM_READ: - type = "r"; - break; - case BP_MEM_ACCESS: - type = "a"; - break; - default: - type = ""; - break; - } - trace_gdbstub_hit_watchpoint(type, - gdb_get_cpu_index(cpu), - cpu->watchpoint_hit->vaddr); - g_string_printf(buf, "T%02xthread:%s;%swatch:%" VADDR_PRIx ";", - GDB_SIGNAL_TRAP, tid->str, type, - cpu->watchpoint_hit->vaddr); - cpu->watchpoint_hit = NULL; - goto send_packet; - } else { - trace_gdbstub_hit_break(); - } - if (tcg_enabled()) { - tb_flush(cpu); - } - ret = GDB_SIGNAL_TRAP; - break; - case RUN_STATE_PAUSED: - trace_gdbstub_hit_paused(); - ret = GDB_SIGNAL_INT; - break; - case RUN_STATE_SHUTDOWN: - trace_gdbstub_hit_shutdown(); - ret = GDB_SIGNAL_QUIT; - break; - case RUN_STATE_IO_ERROR: - trace_gdbstub_hit_io_error(); - ret = GDB_SIGNAL_STOP; - break; - case RUN_STATE_WATCHDOG: - trace_gdbstub_hit_watchdog(); - ret = GDB_SIGNAL_ALRM; - break; - case RUN_STATE_INTERNAL_ERROR: - trace_gdbstub_hit_internal_error(); - ret = GDB_SIGNAL_ABRT; - break; - case RUN_STATE_SAVE_VM: - case RUN_STATE_RESTORE_VM: - return; - case RUN_STATE_FINISH_MIGRATE: - ret = GDB_SIGNAL_XCPU; - break; - default: - trace_gdbstub_hit_unknown(state); - ret = GDB_SIGNAL_UNKNOWN; - break; - } - gdb_set_stop_cpu(cpu); - g_string_printf(buf, "T%02xthread:%s;", ret, tid->str); - -send_packet: - gdb_put_packet(buf->str); - gdbserver_state.allow_stop_reply = false; - - /* disable single step if it was enabled */ - cpu_single_step(cpu, 0); -} - -#ifndef _WIN32 -static void gdb_sigterm_handler(int signal) -{ - if (runstate_is_running()) { - vm_stop(RUN_STATE_PAUSED); - } -} -#endif - -static int gdb_monitor_write(Chardev *chr, const uint8_t *buf, int len) -{ - g_autoptr(GString) hex_buf = g_string_new("O"); - gdb_memtohex(hex_buf, buf, len); - gdb_put_packet(hex_buf->str); - return len; -} - -static void gdb_monitor_open(Chardev *chr, ChardevBackend *backend, - bool *be_opened, Error **errp) -{ - *be_opened = false; -} - -static void char_gdb_class_init(ObjectClass *oc, const void *data) -{ - ChardevClass *cc = CHARDEV_CLASS(oc); - - cc->internal = true; - cc->open = gdb_monitor_open; - cc->chr_write = gdb_monitor_write; -} - -#define TYPE_CHARDEV_GDB "chardev-gdb" - -static const TypeInfo char_gdb_type_info = { - .name = TYPE_CHARDEV_GDB, - .parent = TYPE_CHARDEV, - .class_init = char_gdb_class_init, -}; - -static int gdb_chr_can_receive(void *opaque) -{ - /* - * We can handle an arbitrarily large amount of data. - * Pick the maximum packet size, which is as good as anything. - */ - return MAX_PACKET_LENGTH; -} - -static void gdb_chr_receive(void *opaque, const uint8_t *buf, int size) -{ - int i; - - for (i = 0; i < size; i++) { - gdb_read_byte(buf[i]); - } -} - -static int find_cpu_clusters(Object *child, void *opaque) -{ - if (object_dynamic_cast(child, TYPE_CPU_CLUSTER)) { - GDBState *s = (GDBState *) opaque; - CPUClusterState *cluster = CPU_CLUSTER(child); - GDBProcess *process; - - s->processes = g_renew(GDBProcess, s->processes, ++s->process_num); - - process = &s->processes[s->process_num - 1]; - - /* - * GDB process IDs -1 and 0 are reserved. To avoid subtle errors at - * runtime, we enforce here that the machine does not use a cluster ID - * that would lead to PID 0. - */ - assert(cluster->cluster_id != UINT32_MAX); - process->pid = cluster->cluster_id + 1; - process->attached = false; - process->target_xml = NULL; - - return 0; - } - - return object_child_foreach(child, find_cpu_clusters, opaque); -} - -static int pid_order(const void *a, const void *b) -{ - GDBProcess *pa = (GDBProcess *) a; - GDBProcess *pb = (GDBProcess *) b; - - if (pa->pid < pb->pid) { - return -1; - } else if (pa->pid > pb->pid) { - return 1; - } else { - return 0; - } -} - -static void create_processes(GDBState *s) -{ - object_child_foreach(object_get_root(), find_cpu_clusters, s); - - if (gdbserver_state.processes) { - /* Sort by PID */ - qsort(gdbserver_state.processes, - gdbserver_state.process_num, - sizeof(gdbserver_state.processes[0]), - pid_order); - } - - gdb_create_default_process(s); -} - -bool gdbserver_start(const char *device, Error **errp) -{ - Chardev *chr = NULL; - Chardev *mon_chr; - g_autoptr(GString) cs = g_string_new(device); - - if (!first_cpu) { - error_setg(errp, "gdbstub: meaningless to attach gdb to a " - "machine without any CPU."); - return false; - } - - if (!gdb_supports_guest_debug()) { - error_setg(errp, "gdbstub: current accelerator doesn't " - "support guest debugging"); - return false; - } - - if (cs->len == 0) { - error_setg(errp, "gdbstub: missing connection string"); - return false; - } - - trace_gdbstub_op_start(cs->str); - - if (g_strcmp0(cs->str, "none") != 0) { - if (g_str_has_prefix(cs->str, "tcp:")) { - /* enforce required TCP attributes */ - g_string_append_printf(cs, ",wait=off,nodelay=on,server=on"); - } -#ifndef _WIN32 - else if (strcmp(device, "stdio") == 0) { - struct sigaction act; - - memset(&act, 0, sizeof(act)); - act.sa_handler = gdb_sigterm_handler; - sigaction(SIGINT, &act, NULL); - } -#endif - /* - * FIXME: it's a bit weird to allow using a mux chardev here - * and implicitly setup a monitor. We may want to break this. - */ - chr = qemu_chr_new_noreplay("gdb", cs->str, true, NULL); - if (!chr) { - error_setg(errp, "gdbstub: couldn't create chardev"); - return false; - } - } - - if (!gdbserver_state.init) { - gdb_init_gdbserver_state(); - - qemu_add_vm_change_state_handler(gdb_vm_state_change, NULL); - - /* Initialize a monitor terminal for gdb */ - mon_chr = qemu_chardev_new(NULL, TYPE_CHARDEV_GDB, - NULL, NULL, &error_abort); - monitor_init_hmp(mon_chr, false, &error_abort); - } else { - qemu_chr_fe_deinit(&gdbserver_system_state.chr, true); - mon_chr = gdbserver_system_state.mon_chr; - reset_gdbserver_state(); - } - - create_processes(&gdbserver_state); - - if (chr) { - qemu_chr_fe_init(&gdbserver_system_state.chr, chr, &error_abort); - qemu_chr_fe_set_handlers(&gdbserver_system_state.chr, - gdb_chr_can_receive, - gdb_chr_receive, gdb_chr_event, - NULL, &gdbserver_state, NULL, true); - } - gdbserver_state.state = chr ? RS_IDLE : RS_INACTIVE; - gdbserver_system_state.mon_chr = mon_chr; - gdb_syscall_reset(); - - return true; -} - -static void register_types(void) -{ - type_register_static(&char_gdb_type_info); -} - -type_init(register_types); - -/* Tell the remote gdb that the process has exited. */ -void gdb_exit(int code) -{ - char buf[4]; - - if (!gdbserver_state.init) { - return; - } - - trace_gdbstub_op_exiting((uint8_t)code); - - if (gdbserver_state.allow_stop_reply) { - snprintf(buf, sizeof(buf), "W%02x", (uint8_t)code); - gdb_put_packet(buf); - gdbserver_state.allow_stop_reply = false; - } - - qemu_chr_fe_deinit(&gdbserver_system_state.chr, true); -} - -void gdb_qemu_exit(int code) -{ - qemu_system_shutdown_request_with_code(SHUTDOWN_CAUSE_GUEST_SHUTDOWN, - code); -} - -/* - * Memory access - */ -static int phy_memory_mode; - -int gdb_target_memory_rw_debug(CPUState *cpu, hwaddr addr, - uint8_t *buf, int len, bool is_write) -{ - if (phy_memory_mode) { - if (is_write) { - cpu_physical_memory_write(addr, buf, len); - } else { - cpu_physical_memory_read(addr, buf, len); - } - return 0; - } - - if (cpu->cc->memory_rw_debug) { - return cpu->cc->memory_rw_debug(cpu, addr, buf, len, is_write); - } - - return cpu_memory_rw_debug(cpu, addr, buf, len, is_write); -} - -/* - * cpu helpers - */ - -unsigned int gdb_get_max_cpus(void) -{ - MachineState *ms = MACHINE(qdev_get_machine()); - return ms->smp.max_cpus; -} - -bool gdb_can_reverse(void) -{ - return replay_mode == REPLAY_MODE_PLAY; -} - -/* - * Softmmu specific command helpers - */ - -void gdb_handle_query_qemu_phy_mem_mode(GArray *params, - void *ctx) -{ - g_string_printf(gdbserver_state.str_buf, "%d", phy_memory_mode); - gdb_put_strbuf(); -} - -void gdb_handle_set_qemu_phy_mem_mode(GArray *params, void *ctx) -{ - if (!params->len) { - gdb_put_packet("E22"); - return; - } - - if (!gdb_get_cmd_param(params, 0)->val_ul) { - phy_memory_mode = 0; - } else { - phy_memory_mode = 1; - } - gdb_put_packet("OK"); -} - -void gdb_handle_query_rcmd(GArray *params, void *ctx) -{ - const guint8 zero = 0; - int len; - - if (!params->len) { - gdb_put_packet("E22"); - return; - } - - len = strlen(gdb_get_cmd_param(params, 0)->data); - if (len % 2) { - gdb_put_packet("E01"); - return; - } - - g_assert(gdbserver_state.mem_buf->len == 0); - len = len / 2; - gdb_hextomem(gdbserver_state.mem_buf, gdb_get_cmd_param(params, 0)->data, len); - g_byte_array_append(gdbserver_state.mem_buf, &zero, 1); - qemu_chr_be_write(gdbserver_system_state.mon_chr, - gdbserver_state.mem_buf->data, - gdbserver_state.mem_buf->len); - gdb_put_packet("OK"); -} - -/* - * Execution state helpers - */ - -void gdb_handle_query_attached(GArray *params, void *ctx) -{ - gdb_put_packet("1"); -} - -void gdb_continue(void) -{ - if (!runstate_needs_reset()) { - trace_gdbstub_op_continue(); - vm_start(); - } -} - -/* - * Resume execution, per CPU actions. - */ -int gdb_continue_partial(char *newstates) -{ - CPUState *cpu; - int res = 0; - int flag = 0; - - if (!runstate_needs_reset()) { - bool step_requested = false; - CPU_FOREACH(cpu) { - if (newstates[cpu->cpu_index] == 's') { - step_requested = true; - break; - } - } - - if (vm_prepare_start(step_requested)) { - return 0; - } - - CPU_FOREACH(cpu) { - switch (newstates[cpu->cpu_index]) { - case 0: - case 1: - break; /* nothing to do here */ - case 's': - trace_gdbstub_op_stepping(cpu->cpu_index); - cpu_single_step(cpu, gdbserver_state.sstep_flags); - cpu_resume(cpu); - flag = 1; - break; - case 'c': - trace_gdbstub_op_continue_cpu(cpu->cpu_index); - cpu_resume(cpu); - flag = 1; - break; - default: - res = -1; - break; - } - } - } - if (flag) { - qemu_clock_enable(QEMU_CLOCK_VIRTUAL, true); - } - return res; -} - -/* - * Signal Handling - in system mode we only need SIGINT and SIGTRAP; other - * signals are not yet supported. - */ - -enum { - TARGET_SIGINT = 2, - TARGET_SIGTRAP = 5 -}; - -int gdb_signal_to_target(int sig) -{ - switch (sig) { - case 2: - return TARGET_SIGINT; - case 5: - return TARGET_SIGTRAP; - default: - return -1; - } -} - -/* - * Break/Watch point helpers - */ - -bool gdb_supports_guest_debug(void) -{ - const AccelOpsClass *ops = cpus_get_accel(); - if (ops->supports_guest_debug) { - return ops->supports_guest_debug(); - } - return false; -} - -int gdb_breakpoint_insert(CPUState *cs, int type, vaddr addr, vaddr len) -{ - const AccelOpsClass *ops = cpus_get_accel(); - if (ops->insert_breakpoint) { - return ops->insert_breakpoint(cs, type, addr, len); - } - return -ENOSYS; -} - -int gdb_breakpoint_remove(CPUState *cs, int type, vaddr addr, vaddr len) -{ - const AccelOpsClass *ops = cpus_get_accel(); - if (ops->remove_breakpoint) { - return ops->remove_breakpoint(cs, type, addr, len); - } - return -ENOSYS; -} - -void gdb_breakpoint_remove_all(CPUState *cs) -{ - const AccelOpsClass *ops = cpus_get_accel(); - if (ops->remove_all_breakpoints) { - ops->remove_all_breakpoints(cs); - } -} diff --git a/backup/trace-events b/backup/trace-events deleted file mode 100644 index 4fd126a..0000000 --- a/backup/trace-events +++ /dev/null @@ -1,32 +0,0 @@ -# See docs/devel/tracing.rst for syntax documentation. - -# gdbstub.c -gdbstub_op_start(const char *device) "Starting gdbstub using device %s" -gdbstub_op_exiting(uint8_t code) "notifying exit with code=0x%02x" -gdbstub_op_continue(void) "Continuing all CPUs" -gdbstub_op_continue_cpu(int cpu_index) "Continuing CPU %d" -gdbstub_op_stepping(int cpu_index) "Stepping CPU %d" -gdbstub_op_extra_info(const char *info) "Thread extra info: %s" -gdbstub_hit_internal_error(void) "RUN_STATE_INTERNAL_ERROR" -gdbstub_hit_break(void) "RUN_STATE_DEBUG" -gdbstub_hit_paused(void) "RUN_STATE_PAUSED" -gdbstub_hit_shutdown(void) "RUN_STATE_SHUTDOWN" -gdbstub_hit_io_error(void) "RUN_STATE_IO_ERROR" -gdbstub_hit_watchdog(void) "RUN_STATE_WATCHDOG" -gdbstub_hit_unknown(int state) "Unknown run state=0x%x" -gdbstub_io_reply(const char *message) "Sent: %s" -gdbstub_io_binaryreply(size_t ofs, const char *line) "0x%04zx: %s" -gdbstub_io_command(const char *command) "Received: %s" -gdbstub_io_got_ack(void) "Got ACK" -gdbstub_io_got_unexpected(uint8_t ch) "Got 0x%02x when expecting ACK/NACK" -gdbstub_err_got_nack(void) "Got NACK, retransmitting" -gdbstub_err_garbage(uint8_t ch) "received garbage between packets: 0x%02x" -gdbstub_err_overrun(void) "command buffer overrun, dropping command" -gdbstub_err_invalid_repeat(uint8_t ch) "got invalid RLE count: 0x%02x" -gdbstub_err_invalid_rle(void) "got invalid RLE sequence" -gdbstub_err_checksum_invalid(uint8_t ch) "got invalid command checksum digit: 0x%02x" -gdbstub_err_checksum_incorrect(uint8_t expected, uint8_t got) "got command packet with incorrect checksum, expected=0x%02x, received=0x%02x" -gdbstub_err_unexpected_runpkt(uint8_t ch) "unexpected packet (0x%02x) while target running" - -# system.c -gdbstub_hit_watchpoint(const char *type, int cpu_gdb_index, uint64_t vaddr) "Watchpoint hit, type=\"%s\" cpu=%d, vaddr=0x%" PRIx64 "" diff --git a/backup/trace.h b/backup/trace.h deleted file mode 100644 index dee87b1..0000000 --- a/backup/trace.h +++ /dev/null @@ -1 +0,0 @@ -#include "trace/trace-gdbstub.h" diff --git a/backup/user-target.c b/backup/user-target.c deleted file mode 100644 index 43231e6..0000000 --- a/backup/user-target.c +++ /dev/null @@ -1,424 +0,0 @@ -/* - * Target specific user-mode handling - * - * Copyright (c) 2003-2005 Fabrice Bellard - * Copyright (c) 2022 Linaro Ltd - * - * SPDX-License-Identifier: LGPL-2.0-or-later - */ - -#include "qemu/osdep.h" -#include "exec/gdbstub.h" -#include "gdbstub/commands.h" -#include "qemu.h" -#include "internals.h" -#ifdef CONFIG_LINUX -#include "linux-user/loader.h" -#include "linux-user/qemu.h" -#endif - -/* - * Map target signal numbers to GDB protocol signal numbers and vice - * versa. For user emulation's currently supported systems, we can - * assume most signals are defined. - */ - -static int gdb_signal_table[] = { - 0, - TARGET_SIGHUP, - TARGET_SIGINT, - TARGET_SIGQUIT, - TARGET_SIGILL, - TARGET_SIGTRAP, - TARGET_SIGABRT, - -1, /* SIGEMT */ - TARGET_SIGFPE, - TARGET_SIGKILL, - TARGET_SIGBUS, - TARGET_SIGSEGV, - TARGET_SIGSYS, - TARGET_SIGPIPE, - TARGET_SIGALRM, - TARGET_SIGTERM, - TARGET_SIGURG, - TARGET_SIGSTOP, - TARGET_SIGTSTP, - TARGET_SIGCONT, - TARGET_SIGCHLD, - TARGET_SIGTTIN, - TARGET_SIGTTOU, - TARGET_SIGIO, - TARGET_SIGXCPU, - TARGET_SIGXFSZ, - TARGET_SIGVTALRM, - TARGET_SIGPROF, - TARGET_SIGWINCH, - -1, /* SIGLOST */ - TARGET_SIGUSR1, - TARGET_SIGUSR2, -#ifdef TARGET_SIGPWR - TARGET_SIGPWR, -#else - -1, -#endif - -1, /* SIGPOLL */ - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, -#ifdef __SIGRTMIN - __SIGRTMIN + 1, - __SIGRTMIN + 2, - __SIGRTMIN + 3, - __SIGRTMIN + 4, - __SIGRTMIN + 5, - __SIGRTMIN + 6, - __SIGRTMIN + 7, - __SIGRTMIN + 8, - __SIGRTMIN + 9, - __SIGRTMIN + 10, - __SIGRTMIN + 11, - __SIGRTMIN + 12, - __SIGRTMIN + 13, - __SIGRTMIN + 14, - __SIGRTMIN + 15, - __SIGRTMIN + 16, - __SIGRTMIN + 17, - __SIGRTMIN + 18, - __SIGRTMIN + 19, - __SIGRTMIN + 20, - __SIGRTMIN + 21, - __SIGRTMIN + 22, - __SIGRTMIN + 23, - __SIGRTMIN + 24, - __SIGRTMIN + 25, - __SIGRTMIN + 26, - __SIGRTMIN + 27, - __SIGRTMIN + 28, - __SIGRTMIN + 29, - __SIGRTMIN + 30, - __SIGRTMIN + 31, - -1, /* SIGCANCEL */ - __SIGRTMIN, - __SIGRTMIN + 32, - __SIGRTMIN + 33, - __SIGRTMIN + 34, - __SIGRTMIN + 35, - __SIGRTMIN + 36, - __SIGRTMIN + 37, - __SIGRTMIN + 38, - __SIGRTMIN + 39, - __SIGRTMIN + 40, - __SIGRTMIN + 41, - __SIGRTMIN + 42, - __SIGRTMIN + 43, - __SIGRTMIN + 44, - __SIGRTMIN + 45, - __SIGRTMIN + 46, - __SIGRTMIN + 47, - __SIGRTMIN + 48, - __SIGRTMIN + 49, - __SIGRTMIN + 50, - __SIGRTMIN + 51, - __SIGRTMIN + 52, - __SIGRTMIN + 53, - __SIGRTMIN + 54, - __SIGRTMIN + 55, - __SIGRTMIN + 56, - __SIGRTMIN + 57, - __SIGRTMIN + 58, - __SIGRTMIN + 59, - __SIGRTMIN + 60, - __SIGRTMIN + 61, - __SIGRTMIN + 62, - __SIGRTMIN + 63, - __SIGRTMIN + 64, - __SIGRTMIN + 65, - __SIGRTMIN + 66, - __SIGRTMIN + 67, - __SIGRTMIN + 68, - __SIGRTMIN + 69, - __SIGRTMIN + 70, - __SIGRTMIN + 71, - __SIGRTMIN + 72, - __SIGRTMIN + 73, - __SIGRTMIN + 74, - __SIGRTMIN + 75, - __SIGRTMIN + 76, - __SIGRTMIN + 77, - __SIGRTMIN + 78, - __SIGRTMIN + 79, - __SIGRTMIN + 80, - __SIGRTMIN + 81, - __SIGRTMIN + 82, - __SIGRTMIN + 83, - __SIGRTMIN + 84, - __SIGRTMIN + 85, - __SIGRTMIN + 86, - __SIGRTMIN + 87, - __SIGRTMIN + 88, - __SIGRTMIN + 89, - __SIGRTMIN + 90, - __SIGRTMIN + 91, - __SIGRTMIN + 92, - __SIGRTMIN + 93, - __SIGRTMIN + 94, - __SIGRTMIN + 95, - -1, /* SIGINFO */ - -1, /* UNKNOWN */ - -1, /* DEFAULT */ - -1, - -1, - -1, - -1, - -1, - -1 -#endif -}; - -int gdb_signal_to_target(int sig) -{ - if (sig < ARRAY_SIZE(gdb_signal_table)) { - return gdb_signal_table[sig]; - } else { - return -1; - } -} - -int gdb_target_signal_to_gdb(int sig) -{ - int i; - for (i = 0; i < ARRAY_SIZE(gdb_signal_table); i++) { - if (gdb_signal_table[i] == sig) { - return i; - } - } - return GDB_SIGNAL_UNKNOWN; -} - -int gdb_get_cpu_index(CPUState *cpu) -{ - TaskState *ts = get_task_state(cpu); - return ts ? ts->ts_tid : -1; -} - -/* - * User-mode specific command helpers - */ - -void gdb_handle_query_offsets(GArray *params, void *user_ctx) -{ - TaskState *ts; - - ts = get_task_state(gdbserver_state.c_cpu); - g_string_printf(gdbserver_state.str_buf, - "Text=" TARGET_ABI_FMT_lx - ";Data=" TARGET_ABI_FMT_lx - ";Bss=" TARGET_ABI_FMT_lx, - ts->info->code_offset, - ts->info->data_offset, - ts->info->data_offset); - gdb_put_strbuf(); -} - -#if defined(CONFIG_LINUX) -/* Partial user only duplicate of helper in gdbstub.c */ -static inline int target_memory_rw_debug(CPUState *cpu, target_ulong addr, - uint8_t *buf, int len, bool is_write) -{ - if (cpu->cc->memory_rw_debug) { - return cpu->cc->memory_rw_debug(cpu, addr, buf, len, is_write); - } - return cpu_memory_rw_debug(cpu, addr, buf, len, is_write); -} - -void gdb_handle_query_xfer_auxv(GArray *params, void *user_ctx) -{ - TaskState *ts; - unsigned long offset, len, saved_auxv, auxv_len; - - if (params->len < 2) { - gdb_put_packet("E22"); - return; - } - - offset = gdb_get_cmd_param(params, 0)->val_ul; - len = gdb_get_cmd_param(params, 1)->val_ul; - ts = get_task_state(gdbserver_state.c_cpu); - saved_auxv = ts->info->saved_auxv; - auxv_len = ts->info->auxv_len; - - if (offset >= auxv_len) { - gdb_put_packet("E00"); - return; - } - - if (len > (MAX_PACKET_LENGTH - 5) / 2) { - len = (MAX_PACKET_LENGTH - 5) / 2; - } - - if (len < auxv_len - offset) { - g_string_assign(gdbserver_state.str_buf, "m"); - } else { - g_string_assign(gdbserver_state.str_buf, "l"); - len = auxv_len - offset; - } - - g_byte_array_set_size(gdbserver_state.mem_buf, len); - if (target_memory_rw_debug(gdbserver_state.g_cpu, saved_auxv + offset, - gdbserver_state.mem_buf->data, len, false)) { - gdb_put_packet("E14"); - return; - } - - gdb_memtox(gdbserver_state.str_buf, - (const char *)gdbserver_state.mem_buf->data, len); - gdb_put_packet_binary(gdbserver_state.str_buf->str, - gdbserver_state.str_buf->len, true); -} -#endif - -static const char *get_filename_param(GArray *params, int i) -{ - const char *hex_filename = gdb_get_cmd_param(params, i)->data; - gdb_hextomem(gdbserver_state.mem_buf, hex_filename, - strlen(hex_filename) / 2); - g_byte_array_append(gdbserver_state.mem_buf, (const guint8 *)"", 1); - return (const char *)gdbserver_state.mem_buf->data; -} - -static void hostio_reply_with_data(const void *buf, size_t n) -{ - g_string_printf(gdbserver_state.str_buf, "F%zx;", n); - gdb_memtox(gdbserver_state.str_buf, buf, n); - gdb_put_packet_binary(gdbserver_state.str_buf->str, - gdbserver_state.str_buf->len, true); -} - -void gdb_handle_v_file_open(GArray *params, void *user_ctx) -{ - const char *filename = get_filename_param(params, 0); - uint64_t flags = gdb_get_cmd_param(params, 1)->val_ull; - uint64_t mode = gdb_get_cmd_param(params, 2)->val_ull; - -#ifdef CONFIG_LINUX - int fd = do_guest_openat(cpu_env(gdbserver_state.g_cpu), 0, filename, - flags, mode, false); -#else - int fd = open(filename, flags, mode); -#endif - if (fd < 0) { - g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno); - } else { - g_string_printf(gdbserver_state.str_buf, "F%x", fd); - } - gdb_put_strbuf(); -} - -void gdb_handle_v_file_close(GArray *params, void *user_ctx) -{ - int fd = gdb_get_cmd_param(params, 0)->val_ul; - - if (close(fd) == -1) { - g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno); - gdb_put_strbuf(); - return; - } - - gdb_put_packet("F00"); -} - -void gdb_handle_v_file_pread(GArray *params, void *user_ctx) -{ - int fd = gdb_get_cmd_param(params, 0)->val_ul; - size_t count = gdb_get_cmd_param(params, 1)->val_ull; - off_t offset = gdb_get_cmd_param(params, 2)->val_ull; - - size_t bufsiz = MIN(count, BUFSIZ); - g_autofree char *buf = g_try_malloc(bufsiz); - if (buf == NULL) { - gdb_put_packet("E12"); - return; - } - - ssize_t n = pread(fd, buf, bufsiz, offset); - if (n < 0) { - g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno); - gdb_put_strbuf(); - return; - } - hostio_reply_with_data(buf, n); -} - -void gdb_handle_v_file_readlink(GArray *params, void *user_ctx) -{ - const char *filename = get_filename_param(params, 0); - - g_autofree char *buf = g_try_malloc(BUFSIZ); - if (buf == NULL) { - gdb_put_packet("E12"); - return; - } - -#ifdef CONFIG_LINUX - ssize_t n = do_guest_readlink(filename, buf, BUFSIZ); -#else - ssize_t n = readlink(filename, buf, BUFSIZ); -#endif - if (n < 0) { - g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno); - gdb_put_strbuf(); - return; - } - hostio_reply_with_data(buf, n); -} - -void gdb_handle_query_xfer_exec_file(GArray *params, void *user_ctx) -{ - uint32_t pid = gdb_get_cmd_param(params, 0)->val_ul; - uint32_t offset = gdb_get_cmd_param(params, 1)->val_ul; - uint32_t length = gdb_get_cmd_param(params, 2)->val_ul; - - GDBProcess *process = gdb_get_process(pid); - if (!process) { - gdb_put_packet("E00"); - return; - } - - CPUState *cpu = gdb_get_first_cpu_in_process(process); - if (!cpu) { - gdb_put_packet("E00"); - return; - } - - TaskState *ts = get_task_state(cpu); - if (!ts || !ts->bprm || !ts->bprm->filename) { - gdb_put_packet("E00"); - return; - } - - size_t total_length = strlen(ts->bprm->filename); - if (offset > total_length) { - gdb_put_packet("E00"); - return; - } - if (offset + length > total_length) { - length = total_length - offset; - } - - g_string_printf(gdbserver_state.str_buf, "l%.*s", length, - ts->bprm->filename + offset); - gdb_put_strbuf(); -} - -int gdb_target_sigtrap(void) -{ - return TARGET_SIGTRAP; -} diff --git a/backup/user.c b/backup/user.c deleted file mode 100644 index 67403e5..0000000 --- a/backup/user.c +++ /dev/null @@ -1,955 +0,0 @@ -/* - * gdbstub user-mode helper routines. - * - * We know for user-mode we are using TCG so we can call stuff directly. - * - * Copyright (c) 2003-2005 Fabrice Bellard - * Copyright (c) 2022 Linaro Ltd - * - * SPDX-License-Identifier: LGPL-2.0-or-later - */ - -#include "qemu/osdep.h" -#include "qemu/bitops.h" -#include "qemu/cutils.h" -#include "qemu/sockets.h" -#include "qapi/error.h" -#include "exec/hwaddr.h" -#include "exec/tb-flush.h" -#include "exec/gdbstub.h" -#include "gdbstub/commands.h" -#include "gdbstub/syscalls.h" -#include "gdbstub/user.h" -#include "gdbstub/enums.h" -#include "hw/core/cpu.h" -#include "user/signal.h" -#include "trace.h" -#include "internals.h" - -#define GDB_NR_SYSCALLS 1024 -typedef unsigned long GDBSyscallsMask[BITS_TO_LONGS(GDB_NR_SYSCALLS)]; - -/* - * Forked child talks to its parent in order to let GDB enforce the - * follow-fork-mode. This happens inside a start_exclusive() section, so that - * the other threads, which may be forking too, do not interfere. The - * implementation relies on GDB not sending $vCont until it has detached - * either from the parent (follow-fork-mode child) or from the child - * (follow-fork-mode parent). - * - * The parent and the child share the GDB socket; at any given time only one - * of them is allowed to use it, as is reflected in the respective fork_state. - * This is negotiated via the fork_sockets pair as a reaction to $Hg. - * - * Below is a short summary of the possible state transitions: - * - * ENABLED : Terminal state. - * DISABLED : Terminal state. - * ACTIVE : Parent initial state. - * INACTIVE : Child initial state. - * ACTIVE -> DEACTIVATING: On $Hg. - * ACTIVE -> ENABLING : On $D. - * ACTIVE -> DISABLING : On $D. - * ACTIVE -> DISABLED : On communication error. - * DEACTIVATING -> INACTIVE : On gdb_read_byte() return. - * DEACTIVATING -> DISABLED : On communication error. - * INACTIVE -> ACTIVE : On $Hg in the peer. - * INACTIVE -> ENABLE : On $D in the peer. - * INACTIVE -> DISABLE : On $D in the peer. - * INACTIVE -> DISABLED : On communication error. - * ENABLING -> ENABLED : On gdb_read_byte() return. - * ENABLING -> DISABLED : On communication error. - * DISABLING -> DISABLED : On gdb_read_byte() return. - */ -enum GDBForkState { - /* Fully owning the GDB socket. */ - GDB_FORK_ENABLED, - /* Working with the GDB socket; the peer is inactive. */ - GDB_FORK_ACTIVE, - /* Handing off the GDB socket to the peer. */ - GDB_FORK_DEACTIVATING, - /* The peer is working with the GDB socket. */ - GDB_FORK_INACTIVE, - /* Asking the peer to close its GDB socket fd. */ - GDB_FORK_ENABLING, - /* Asking the peer to take over, closing our GDB socket fd. */ - GDB_FORK_DISABLING, - /* The peer has taken over, our GDB socket fd is closed. */ - GDB_FORK_DISABLED, -}; - -enum GDBForkMessage { - GDB_FORK_ACTIVATE = 'a', - GDB_FORK_ENABLE = 'e', - GDB_FORK_DISABLE = 'd', -}; - -/* User-mode specific state */ -typedef struct { - int fd; - char *socket_path; - int running_state; - /* - * Store syscalls mask without memory allocation in order to avoid - * implementing synchronization. - */ - bool catch_all_syscalls; - GDBSyscallsMask catch_syscalls_mask; - bool fork_events; - enum GDBForkState fork_state; - int fork_sockets[2]; - pid_t fork_peer_pid, fork_peer_tid; - uint8_t siginfo[MAX_SIGINFO_LENGTH]; - unsigned long siginfo_len; -} GDBUserState; - -static GDBUserState gdbserver_user_state; - -int gdb_get_char(void) -{ - uint8_t ch; - int ret; - - for (;;) { - ret = recv(gdbserver_user_state.fd, &ch, 1, 0); - if (ret < 0) { - if (errno == ECONNRESET) { - gdbserver_user_state.fd = -1; - } - if (errno != EINTR) { - return -1; - } - } else if (ret == 0) { - close(gdbserver_user_state.fd); - gdbserver_user_state.fd = -1; - return -1; - } else { - break; - } - } - return ch; -} - -bool gdb_got_immediate_ack(void) -{ - int i; - - i = gdb_get_char(); - if (i < 0) { - /* no response, continue anyway */ - return true; - } - - if (i == '+') { - /* received correctly, continue */ - return true; - } - - /* anything else, including '-' then try again */ - return false; -} - -void gdb_put_buffer(const uint8_t *buf, int len) -{ - int ret; - - while (len > 0) { - ret = send(gdbserver_user_state.fd, buf, len, 0); - if (ret < 0) { - if (errno != EINTR) { - return; - } - } else { - buf += ret; - len -= ret; - } - } -} - -/* Tell the remote gdb that the process has exited. */ -void gdb_exit(int code) -{ - char buf[4]; - - if (!gdbserver_state.init) { - return; - } - if (gdbserver_user_state.socket_path) { - unlink(gdbserver_user_state.socket_path); - } - if (gdbserver_user_state.fd < 0) { - return; - } - - trace_gdbstub_op_exiting((uint8_t)code); - - if (gdbserver_state.allow_stop_reply) { - snprintf(buf, sizeof(buf), "W%02x", (uint8_t)code); - gdb_put_packet(buf); - gdbserver_state.allow_stop_reply = false; - } - -} - -void gdb_qemu_exit(int code) -{ - exit(code); -} - -int gdb_handlesig(CPUState *cpu, int sig, const char *reason, void *siginfo, - int siginfo_len) -{ - char buf[256]; - int n; - - if (!gdbserver_state.init || gdbserver_user_state.fd < 0) { - return sig; - } - - if (siginfo) { - /* - * Save target-specific siginfo. - * - * siginfo size, i.e. siginfo_len, is asserted at compile-time to fit in - * gdbserver_user_state.siginfo, usually in the source file calling - * gdb_handlesig. See, for instance, {linux,bsd}-user/signal.c. - */ - memcpy(gdbserver_user_state.siginfo, siginfo, siginfo_len); - gdbserver_user_state.siginfo_len = siginfo_len; - } - - /* disable single step if it was enabled */ - cpu_single_step(cpu, 0); - tb_flush(cpu); - - if (sig != 0) { - gdb_set_stop_cpu(cpu); - if (gdbserver_state.allow_stop_reply) { - g_string_printf(gdbserver_state.str_buf, - "T%02xthread:", gdb_target_signal_to_gdb(sig)); - gdb_append_thread_id(cpu, gdbserver_state.str_buf); - g_string_append_c(gdbserver_state.str_buf, ';'); - if (reason) { - g_string_append(gdbserver_state.str_buf, reason); - } - gdb_put_strbuf(); - gdbserver_state.allow_stop_reply = false; - } - } - /* - * gdb_put_packet() might have detected that the peer terminated the - * connection. - */ - if (gdbserver_user_state.fd < 0) { - return sig; - } - - sig = 0; - gdbserver_state.state = RS_IDLE; - gdbserver_user_state.running_state = 0; - while (gdbserver_user_state.running_state == 0) { - n = read(gdbserver_user_state.fd, buf, 256); - if (n > 0) { - int i; - - for (i = 0; i < n; i++) { - gdb_read_byte(buf[i]); - } - } else { - /* - * XXX: Connection closed. Should probably wait for another - * connection before continuing. - */ - if (n == 0) { - close(gdbserver_user_state.fd); - } - gdbserver_user_state.fd = -1; - return sig; - } - } - sig = gdbserver_state.signal; - gdbserver_state.signal = 0; - return sig; -} - -/* Tell the remote gdb that the process has exited due to SIG. */ -void gdb_signalled(CPUArchState *env, int sig) -{ - char buf[4]; - - if (!gdbserver_state.init || gdbserver_user_state.fd < 0 || - !gdbserver_state.allow_stop_reply) { - return; - } - - snprintf(buf, sizeof(buf), "X%02x", gdb_target_signal_to_gdb(sig)); - gdb_put_packet(buf); - gdbserver_state.allow_stop_reply = false; -} - -static void gdb_accept_init(int fd) -{ - gdb_init_gdbserver_state(); - gdb_create_default_process(&gdbserver_state); - gdbserver_state.processes[0].attached = true; - gdbserver_state.c_cpu = gdb_first_attached_cpu(); - gdbserver_state.g_cpu = gdbserver_state.c_cpu; - gdbserver_user_state.fd = fd; -} - -static bool gdb_accept_socket(int gdb_fd) -{ - int fd; - - for (;;) { - fd = accept(gdb_fd, NULL, NULL); - if (fd < 0 && errno != EINTR) { - perror("accept socket"); - return false; - } else if (fd >= 0) { - qemu_set_cloexec(fd); - break; - } - } - - gdb_accept_init(fd); - return true; -} - -static int gdbserver_open_socket(const char *path, Error **errp) -{ - g_autoptr(GString) buf = g_string_new(""); - char *pid_placeholder; - - pid_placeholder = strstr(path, "%d"); - if (pid_placeholder != NULL) { - g_string_append_len(buf, path, pid_placeholder - path); - g_string_append_printf(buf, "%d", qemu_get_thread_id()); - g_string_append(buf, pid_placeholder + 2); - path = buf->str; - } - - return unix_listen(path, errp); -} - -static bool gdb_accept_tcp(int gdb_fd) -{ - struct sockaddr_in sockaddr = {}; - socklen_t len; - int fd; - - for (;;) { - len = sizeof(sockaddr); - fd = accept(gdb_fd, (struct sockaddr *)&sockaddr, &len); - if (fd < 0 && errno != EINTR) { - perror("accept"); - return false; - } else if (fd >= 0) { - qemu_set_cloexec(fd); - break; - } - } - - /* set short latency */ - if (socket_set_nodelay(fd)) { - perror("setsockopt"); - close(fd); - return false; - } - - gdb_accept_init(fd); - return true; -} - -static int gdbserver_open_port(int port, Error **errp) -{ - struct sockaddr_in sockaddr; - int fd, ret; - - fd = socket(PF_INET, SOCK_STREAM, 0); - if (fd < 0) { - error_setg_errno(errp, errno, "Failed to create socket"); - return -1; - } - qemu_set_cloexec(fd); - - socket_set_fast_reuse(fd); - - sockaddr.sin_family = AF_INET; - sockaddr.sin_port = htons(port); - sockaddr.sin_addr.s_addr = 0; - ret = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)); - if (ret < 0) { - error_setg_errno(errp, errno, "Failed to bind socket"); - close(fd); - return -1; - } - ret = listen(fd, 1); - if (ret < 0) { - error_setg_errno(errp, errno, "Failed to listen to socket"); - close(fd); - return -1; - } - - return fd; -} - -static bool gdbserver_accept(int port, int gdb_fd, const char *path) -{ - bool ret; - - if (port > 0) { - ret = gdb_accept_tcp(gdb_fd); - } else { - ret = gdb_accept_socket(gdb_fd); - if (ret) { - gdbserver_user_state.socket_path = g_strdup(path); - } - } - - if (!ret) { - close(gdb_fd); - } - - return ret; -} - -struct { - int port; - int gdb_fd; - char *path; -} gdbserver_args; - -static void do_gdb_handlesig(CPUState *cs, run_on_cpu_data arg) -{ - int sig; - - sig = target_to_host_signal(gdb_handlesig(cs, 0, NULL, NULL, 0)); - if (sig >= 1 && sig < NSIG) { - qemu_kill_thread(gdb_get_cpu_index(cs), sig); - } -} - -static void *gdbserver_accept_thread(void *arg) -{ - if (gdbserver_accept(gdbserver_args.port, gdbserver_args.gdb_fd, - gdbserver_args.path)) { - CPUState *cs = first_cpu; - - async_safe_run_on_cpu(cs, do_gdb_handlesig, RUN_ON_CPU_NULL); - qemu_kill_thread(gdb_get_cpu_index(cs), host_interrupt_signal); - } - - g_free(gdbserver_args.path); - gdbserver_args.path = NULL; - - return NULL; -} - -#define USAGE "\nUsage: -g {port|path}[,suspend={y|n}]" - -bool gdbserver_start(const char *args, Error **errp) -{ - g_auto(GStrv) argv = g_strsplit(args, ",", 0); - const char *port_or_path = NULL; - bool suspend = true; - int gdb_fd, port; - GStrv arg; - - for (arg = argv; *arg; arg++) { - g_auto(GStrv) tokens = g_strsplit(*arg, "=", 2); - - if (g_strcmp0(tokens[0], "suspend") == 0) { - if (tokens[1] == NULL) { - error_setg(errp, - "gdbstub: missing \"suspend\" option value" USAGE); - return false; - } else if (!qapi_bool_parse(tokens[0], tokens[1], - &suspend, errp)) { - return false; - } - } else { - if (port_or_path) { - error_setg(errp, "gdbstub: unknown option \"%s\"" USAGE, *arg); - return false; - } - port_or_path = *arg; - } - } - if (!port_or_path) { - error_setg(errp, "gdbstub: port or path not specified" USAGE); - return false; - } - - port = g_ascii_strtoull(port_or_path, NULL, 10); - if (port > 0) { - gdb_fd = gdbserver_open_port(port, errp); - } else { - gdb_fd = gdbserver_open_socket(port_or_path, errp); - } - if (gdb_fd < 0) { - return false; - } - - if (suspend) { - if (gdbserver_accept(port, gdb_fd, port_or_path)) { - gdb_handlesig(first_cpu, 0, NULL, NULL, 0); - return true; - } else { - error_setg(errp, "gdbstub: failed to accept connection"); - return false; - } - } else { - QemuThread thread; - - gdbserver_args.port = port; - gdbserver_args.gdb_fd = gdb_fd; - gdbserver_args.path = g_strdup(port_or_path); - qemu_thread_create(&thread, "gdb-accept", - &gdbserver_accept_thread, NULL, - QEMU_THREAD_DETACHED); - return true; - } -} - -void gdbserver_fork_start(void) -{ - if (!gdbserver_state.init || gdbserver_user_state.fd < 0) { - return; - } - if (!gdbserver_user_state.fork_events || - qemu_socketpair(AF_UNIX, SOCK_STREAM, 0, - gdbserver_user_state.fork_sockets) < 0) { - gdbserver_user_state.fork_state = GDB_FORK_DISABLED; - return; - } - gdbserver_user_state.fork_state = GDB_FORK_INACTIVE; - gdbserver_user_state.fork_peer_pid = getpid(); - gdbserver_user_state.fork_peer_tid = qemu_get_thread_id(); -} - -static void disable_gdbstub(CPUState *thread_cpu) -{ - CPUState *cpu; - - close(gdbserver_user_state.fd); - gdbserver_user_state.fd = -1; - CPU_FOREACH(cpu) { - cpu_breakpoint_remove_all(cpu, BP_GDB); - /* no cpu_watchpoint_remove_all for user-mode */ - cpu_single_step(cpu, 0); - } - tb_flush(thread_cpu); -} - -void gdbserver_fork_end(CPUState *cpu, pid_t pid) -{ - char b; - int fd; - - if (!gdbserver_state.init || gdbserver_user_state.fd < 0) { - return; - } - - if (pid == -1) { - if (gdbserver_user_state.fork_state != GDB_FORK_DISABLED) { - g_assert(gdbserver_user_state.fork_state == GDB_FORK_INACTIVE); - close(gdbserver_user_state.fork_sockets[0]); - close(gdbserver_user_state.fork_sockets[1]); - } - return; - } - - if (gdbserver_user_state.fork_state == GDB_FORK_DISABLED) { - if (pid == 0) { - disable_gdbstub(cpu); - } - return; - } - - if (pid == 0) { - close(gdbserver_user_state.fork_sockets[0]); - fd = gdbserver_user_state.fork_sockets[1]; - g_assert(gdbserver_state.process_num == 1); - g_assert(gdbserver_state.processes[0].pid == - gdbserver_user_state.fork_peer_pid); - g_assert(gdbserver_state.processes[0].attached); - gdbserver_state.processes[0].pid = getpid(); - } else { - close(gdbserver_user_state.fork_sockets[1]); - fd = gdbserver_user_state.fork_sockets[0]; - gdbserver_user_state.fork_state = GDB_FORK_ACTIVE; - gdbserver_user_state.fork_peer_pid = pid; - gdbserver_user_state.fork_peer_tid = pid; - - if (!gdbserver_state.allow_stop_reply) { - goto fail; - } - g_string_printf(gdbserver_state.str_buf, - "T%02xfork:p%02x.%02x;thread:p%02x.%02x;", - gdb_target_signal_to_gdb(gdb_target_sigtrap()), - pid, pid, (int)getpid(), qemu_get_thread_id()); - gdb_put_strbuf(); - } - - gdbserver_state.state = RS_IDLE; - gdbserver_state.allow_stop_reply = false; - gdbserver_user_state.running_state = 0; - for (;;) { - switch (gdbserver_user_state.fork_state) { - case GDB_FORK_ENABLED: - if (gdbserver_user_state.running_state) { - close(fd); - return; - } - QEMU_FALLTHROUGH; - case GDB_FORK_ACTIVE: - if (read(gdbserver_user_state.fd, &b, 1) != 1) { - goto fail; - } - gdb_read_byte(b); - break; - case GDB_FORK_DEACTIVATING: - b = GDB_FORK_ACTIVATE; - if (write(fd, &b, 1) != 1) { - goto fail; - } - gdbserver_user_state.fork_state = GDB_FORK_INACTIVE; - break; - case GDB_FORK_INACTIVE: - if (read(fd, &b, 1) != 1) { - goto fail; - } - switch (b) { - case GDB_FORK_ACTIVATE: - gdbserver_user_state.fork_state = GDB_FORK_ACTIVE; - break; - case GDB_FORK_ENABLE: - gdbserver_user_state.fork_state = GDB_FORK_ENABLED; - break; - case GDB_FORK_DISABLE: - gdbserver_user_state.fork_state = GDB_FORK_DISABLED; - break; - default: - g_assert_not_reached(); - } - break; - case GDB_FORK_ENABLING: - b = GDB_FORK_DISABLE; - if (write(fd, &b, 1) != 1) { - goto fail; - } - gdbserver_user_state.fork_state = GDB_FORK_ENABLED; - break; - case GDB_FORK_DISABLING: - b = GDB_FORK_ENABLE; - if (write(fd, &b, 1) != 1) { - goto fail; - } - gdbserver_user_state.fork_state = GDB_FORK_DISABLED; - break; - case GDB_FORK_DISABLED: - close(fd); - disable_gdbstub(cpu); - return; - default: - g_assert_not_reached(); - } - } - -fail: - close(fd); - if (pid == 0) { - disable_gdbstub(cpu); - } -} - -void gdb_handle_query_supported_user(const char *gdb_supported) -{ - if (strstr(gdb_supported, "fork-events+")) { - gdbserver_user_state.fork_events = true; - } - g_string_append(gdbserver_state.str_buf, ";fork-events+"); -} - -bool gdb_handle_set_thread_user(uint32_t pid, uint32_t tid) -{ - if (gdbserver_user_state.fork_state == GDB_FORK_ACTIVE && - pid == gdbserver_user_state.fork_peer_pid && - tid == gdbserver_user_state.fork_peer_tid) { - gdbserver_user_state.fork_state = GDB_FORK_DEACTIVATING; - gdb_put_packet("OK"); - return true; - } - return false; -} - -bool gdb_handle_detach_user(uint32_t pid) -{ - bool enable; - - if (gdbserver_user_state.fork_state == GDB_FORK_ACTIVE) { - enable = pid == gdbserver_user_state.fork_peer_pid; - if (enable || pid == getpid()) { - gdbserver_user_state.fork_state = enable ? GDB_FORK_ENABLING : - GDB_FORK_DISABLING; - gdb_put_packet("OK"); - return true; - } - } - return false; -} - -/* - * Execution state helpers - */ - -void gdb_handle_query_attached(GArray *params, void *user_ctx) -{ - gdb_put_packet("0"); -} - -void gdb_continue(void) -{ - gdbserver_user_state.running_state = 1; - trace_gdbstub_op_continue(); -} - -/* - * Resume execution, for user-mode emulation it's equivalent to - * gdb_continue. - */ -int gdb_continue_partial(char *newstates) -{ - CPUState *cpu; - int res = 0; - /* - * This is not exactly accurate, but it's an improvement compared to the - * previous situation, where only one CPU would be single-stepped. - */ - CPU_FOREACH(cpu) { - if (newstates[cpu->cpu_index] == 's') { - trace_gdbstub_op_stepping(cpu->cpu_index); - cpu_single_step(cpu, gdbserver_state.sstep_flags); - } - } - gdbserver_user_state.running_state = 1; - return res; -} - -/* - * Memory access helpers - */ -int gdb_target_memory_rw_debug(CPUState *cpu, hwaddr addr, - uint8_t *buf, int len, bool is_write) -{ - if (cpu->cc->memory_rw_debug) { - return cpu->cc->memory_rw_debug(cpu, addr, buf, len, is_write); - } - return cpu_memory_rw_debug(cpu, addr, buf, len, is_write); -} - -/* - * cpu helpers - */ - -unsigned int gdb_get_max_cpus(void) -{ - CPUState *cpu; - unsigned int max_cpus = 1; - - CPU_FOREACH(cpu) { - max_cpus = max_cpus <= cpu->cpu_index ? cpu->cpu_index + 1 : max_cpus; - } - - return max_cpus; -} - -/* replay not supported for user-mode */ -bool gdb_can_reverse(void) -{ - return false; -} - -/* - * Break/Watch point helpers - */ - -bool gdb_supports_guest_debug(void) -{ - /* user-mode == TCG == supported */ - return true; -} - -int gdb_breakpoint_insert(CPUState *cs, int type, vaddr addr, vaddr len) -{ - CPUState *cpu; - int err = 0; - - switch (type) { - case GDB_BREAKPOINT_SW: - case GDB_BREAKPOINT_HW: - CPU_FOREACH(cpu) { - err = cpu_breakpoint_insert(cpu, addr, BP_GDB, NULL); - if (err) { - break; - } - } - return err; - default: - /* user-mode doesn't support watchpoints */ - return -ENOSYS; - } -} - -int gdb_breakpoint_remove(CPUState *cs, int type, vaddr addr, vaddr len) -{ - CPUState *cpu; - int err = 0; - - switch (type) { - case GDB_BREAKPOINT_SW: - case GDB_BREAKPOINT_HW: - CPU_FOREACH(cpu) { - err = cpu_breakpoint_remove(cpu, addr, BP_GDB); - if (err) { - break; - } - } - return err; - default: - /* user-mode doesn't support watchpoints */ - return -ENOSYS; - } -} - -void gdb_breakpoint_remove_all(CPUState *cs) -{ - cpu_breakpoint_remove_all(cs, BP_GDB); -} - -/* - * For user-mode syscall support we send the system call immediately - * and then return control to gdb for it to process the syscall request. - * Since the protocol requires that gdb hands control back to us - * using a "here are the results" F packet, we don't need to check - * gdb_handlesig's return value (which is the signal to deliver if - * execution was resumed via a continue packet). - */ -void gdb_syscall_handling(const char *syscall_packet) -{ - gdb_put_packet(syscall_packet); - gdb_handlesig(gdbserver_state.c_cpu, 0, NULL, NULL, 0); -} - -static bool should_catch_syscall(int num) -{ - if (gdbserver_user_state.catch_all_syscalls) { - return true; - } - if (num < 0 || num >= GDB_NR_SYSCALLS) { - return false; - } - return test_bit(num, gdbserver_user_state.catch_syscalls_mask); -} - -void gdb_syscall_entry(CPUState *cs, int num) -{ - if (should_catch_syscall(num)) { - g_autofree char *reason = g_strdup_printf("syscall_entry:%x;", num); - gdb_handlesig(cs, gdb_target_sigtrap(), reason, NULL, 0); - } -} - -void gdb_syscall_return(CPUState *cs, int num) -{ - if (should_catch_syscall(num)) { - g_autofree char *reason = g_strdup_printf("syscall_return:%x;", num); - gdb_handlesig(cs, gdb_target_sigtrap(), reason, NULL, 0); - } -} - -void gdb_handle_set_catch_syscalls(GArray *params, void *user_ctx) -{ - const char *param = gdb_get_cmd_param(params, 0)->data; - GDBSyscallsMask catch_syscalls_mask; - bool catch_all_syscalls; - unsigned int num; - const char *p; - - /* "0" means not catching any syscalls. */ - if (strcmp(param, "0") == 0) { - gdbserver_user_state.catch_all_syscalls = false; - memset(gdbserver_user_state.catch_syscalls_mask, 0, - sizeof(gdbserver_user_state.catch_syscalls_mask)); - gdb_put_packet("OK"); - return; - } - - /* "1" means catching all syscalls. */ - if (strcmp(param, "1") == 0) { - gdbserver_user_state.catch_all_syscalls = true; - gdb_put_packet("OK"); - return; - } - - /* - * "1;..." means catching only the specified syscalls. - * The syscall list must not be empty. - */ - if (param[0] == '1' && param[1] == ';') { - catch_all_syscalls = false; - memset(catch_syscalls_mask, 0, sizeof(catch_syscalls_mask)); - for (p = ¶m[2];; p++) { - if (qemu_strtoui(p, &p, 16, &num) || (*p && *p != ';')) { - goto err; - } - if (num >= GDB_NR_SYSCALLS) { - /* - * Fall back to reporting all syscalls. Reporting extra - * syscalls is inefficient, but the spec explicitly allows it. - * Keep parsing in case there is a syntax error ahead. - */ - catch_all_syscalls = true; - } else { - set_bit(num, catch_syscalls_mask); - } - if (!*p) { - break; - } - } - gdbserver_user_state.catch_all_syscalls = catch_all_syscalls; - if (!catch_all_syscalls) { - memcpy(gdbserver_user_state.catch_syscalls_mask, - catch_syscalls_mask, sizeof(catch_syscalls_mask)); - } - gdb_put_packet("OK"); - return; - } - -err: - gdb_put_packet("E00"); -} - -void gdb_handle_query_xfer_siginfo(GArray *params, void *user_ctx) -{ - unsigned long offset, len; - uint8_t *siginfo_offset; - - offset = gdb_get_cmd_param(params, 0)->val_ul; - len = gdb_get_cmd_param(params, 1)->val_ul; - - if (offset + len > gdbserver_user_state.siginfo_len) { - /* Invalid offset and/or requested length. */ - gdb_put_packet("E01"); - return; - } - - siginfo_offset = (uint8_t *)gdbserver_user_state.siginfo + offset; - - /* Reply */ - g_string_assign(gdbserver_state.str_buf, "l"); - gdb_memtox(gdbserver_state.str_buf, (const char *)siginfo_offset, len); - gdb_put_packet_binary(gdbserver_state.str_buf->str, - gdbserver_state.str_buf->len, true); -} diff --git a/backup/user.h b/backup/user.h deleted file mode 100644 index 654986d..0000000 --- a/backup/user.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * gdbstub user-mode only APIs - * - * Copyright (c) 2022 Linaro Ltd - * - * SPDX-License-Identifier: LGPL-2.0-or-later - */ - -#ifndef GDBSTUB_USER_H -#define GDBSTUB_USER_H - -#define MAX_SIGINFO_LENGTH 128 - -/** - * gdb_handlesig() - yield control to gdb - * @cpu: CPU - * @sig: if non-zero, the signal number which caused us to stop - * @reason: stop reason for stop reply packet or NULL - * @siginfo: target-specific siginfo struct - * @siginfo_len: target-specific siginfo struct length - * - * This function yields control to gdb, when a user-mode-only target - * needs to stop execution. If @sig is non-zero, then we will send a - * stop packet to tell gdb that we have stopped because of this signal. - * - * This function will block (handling protocol requests from gdb) - * until gdb tells us to continue target execution. When it does - * return, the return value is a signal to deliver to the target, - * or 0 if no signal should be delivered, ie the signal that caused - * us to stop should be ignored. - */ -int gdb_handlesig(CPUState *, int, const char *, void *, int); - -/** - * gdb_signalled() - inform remote gdb of sig exit - * @as: current CPUArchState - * @sig: signal number - */ -void gdb_signalled(CPUArchState *as, int sig); - -/** - * gdbserver_fork_start() - inform gdb of the upcoming fork() - */ -void gdbserver_fork_start(void); - -/** - * gdbserver_fork_end() - inform gdb of the completed fork() - * @cs: CPU - * @pid: 0 if in child process, -1 if fork failed, child process pid otherwise - */ -void gdbserver_fork_end(CPUState *cs, pid_t pid); - -/** - * gdb_syscall_entry() - inform gdb of syscall entry and yield control to it - * @cs: CPU - * @num: syscall number - */ -void gdb_syscall_entry(CPUState *cs, int num); - -/** - * gdb_syscall_entry() - inform gdb of syscall return and yield control to it - * @cs: CPU - * @num: syscall number - */ -void gdb_syscall_return(CPUState *cs, int num); - -#endif /* GDBSTUB_USER_H */ diff --git a/gdbstub/commands.h b/gdbstub/commands.h index 4a7eb87..2641c6d 100644 --- a/gdbstub/commands.h +++ b/gdbstub/commands.h @@ -68,12 +68,6 @@ typedef struct GdbCmdParseEntry { bool need_cpu_context; } GdbCmdParseEntry; -/** - * gdb_put_packet() - put string into gdb server's buffer so it is sent - * to the client - */ -int gdb_put_packet(const char *buf); - /** * gdb_extend_query_table() - Extend query table. * @table: GPtrArray of GdbCmdParseEntry entries. diff --git a/gdbstub/cpu.h b/gdbstub/cpu.h index 23ebf5d..7874d0e 100644 --- a/gdbstub/cpu.h +++ b/gdbstub/cpu.h @@ -20,23 +20,6 @@ #ifndef QEMU_CPU_H #define QEMU_CPU_H -// #include "disas/dis-asm.h" -// #include "exec/breakpoint.h" -// #include "exec/hwaddr.h" -// #include "exec/memattrs.h" -// #include "exec/mmu-access-type.h" -// #include "exec/tlb-common.h" -// #include "exec/vaddr.h" -// #include "hw/qdev-core.h" -// #include "qapi/qapi-types-machine.h" -// #include "qapi/qapi-types-run-state.h" -// #include "qemu/bitmap.h" -// #include "qemu/lockcnt.h" -// #include "qemu/queue.h" -// #include "qemu/rcu_queue.h" -// #include "qemu/thread.h" -// #include "qom/object.h" - #include #include "breakpoint.h" @@ -486,7 +469,8 @@ struct CPUState { struct QemuThread *thread; int thread_id; - bool running, has_waiter; + bool running; + bool has_waiter; struct QemuCond *halt_cond; bool thread_kicked; bool created; diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c index 3db3a25..e249e47 100644 --- a/gdbstub/gdbstub.c +++ b/gdbstub/gdbstub.c @@ -30,24 +30,24 @@ #include "enums.h" #include "internals.h" -GDBState gdbserver_state; +GDBState gdb_state; -void gdb_init_gdbserver_state(void) { - g_assert(!gdbserver_state.init); - memset(&gdbserver_state, 0, sizeof(GDBState)); - gdbserver_state.init = true; - gdbserver_state.str_buf = g_string_new(NULL); - gdbserver_state.mem_buf = g_byte_array_sized_new(MAX_PACKET_LENGTH); - gdbserver_state.last_packet = g_byte_array_sized_new(MAX_PACKET_LENGTH + 4); +void gdb_init_gdb_state(void) { + g_assert(!gdb_state.init); + memset(&gdb_state, 0, sizeof(GDBState)); + gdb_state.init = true; + gdb_state.str_buf = g_string_new(NULL); + gdb_state.mem_buf = g_byte_array_sized_new(MAX_PACKET_LENGTH); + gdb_state.last_packet = g_byte_array_sized_new(MAX_PACKET_LENGTH + 4); /* * What single-step modes are supported is accelerator dependent. * By default try to use no IRQs and no timers while single * stepping so as to make single stepping like a typical ICE HW step. */ - gdbserver_state.supported_sstep_flags = 0; - gdbserver_state.sstep_flags = SSTEP_ENABLE | SSTEP_NOIRQ | SSTEP_NOTIMER; - gdbserver_state.sstep_flags &= gdbserver_state.supported_sstep_flags; + gdb_state.supported_sstep_flags = 0; + gdb_state.sstep_flags = SSTEP_ENABLE | SSTEP_NOIRQ | SSTEP_NOTIMER; + gdb_state.sstep_flags &= gdb_state.supported_sstep_flags; } /* writes 2*len+1 bytes in buf */ @@ -76,9 +76,9 @@ int gdb_put_packet_binary(const char *buf, int len, bool dump) { uint8_t footer[3]; for (;;) { - g_byte_array_set_size(gdbserver_state.last_packet, 0); - g_byte_array_append(gdbserver_state.last_packet, (const uint8_t *)"$", 1); - g_byte_array_append(gdbserver_state.last_packet, (const uint8_t *)buf, len); + g_byte_array_set_size(gdb_state.last_packet, 0); + g_byte_array_append(gdb_state.last_packet, (const uint8_t *)"$", 1); + g_byte_array_append(gdb_state.last_packet, (const uint8_t *)buf, len); csum = 0; for (i = 0; i < len; i++) { csum += buf[i]; @@ -86,9 +86,9 @@ int gdb_put_packet_binary(const char *buf, int len, bool dump) { footer[0] = '#'; footer[1] = tohex((csum >> 4) & 0xf); footer[2] = tohex((csum) & 0xf); - g_byte_array_append(gdbserver_state.last_packet, footer, 3); + g_byte_array_append(gdb_state.last_packet, footer, 3); - gdb_put_buffer(gdbserver_state.last_packet->data, gdbserver_state.last_packet->len); + gdb_put_buffer(gdb_state.last_packet->data, gdb_state.last_packet->len); if (gdb_got_immediate_ack()) { break; @@ -99,7 +99,7 @@ int gdb_put_packet_binary(const char *buf, int len, bool dump) { int gdb_put_packet(const char *buf) { return gdb_put_packet_binary(buf, strlen(buf), false); } -void gdb_put_strbuf(void) { gdb_put_packet(gdbserver_state.str_buf->str); } +void gdb_put_strbuf(void) { gdb_put_packet(gdb_state.str_buf->str); } void gdb_memtox(GString *buf, const char *mem, int len) { char c; @@ -227,20 +227,16 @@ int gdb_write_register(CPUState *cpu, uint8_t *mem_buf, int reg) { static void gdb_process_breakpoint_remove_all() { CPUState *cpu = get_cpu(); - while (cpu) { - gdb_breakpoint_remove_all(cpu); - cpu = gdb_next_cpu_in_process(cpu); - } + gdb_breakpoint_remove_all(cpu); } static void gdb_set_cpu_pc(vaddr pc) { - CPUState *cpu = gdbserver_state.c_cpu; - + CPUState *cpu = get_cpu(); cpu_synchronize_state(cpu); cpu_set_pc(cpu, pc); } -void gdb_append_thread_id(CPUState *cpu, GString *buf) { g_string_append_printf(buf, "%02x", gdb_get_cpu_index(cpu)); } +void gdb_append_thread_id(CPUState *cpu, GString *buf) { g_string_append_printf(buf, "%02x", 1); } static GDBThreadIdKind read_thread_id(const char *buf, const char **end_buf, uint32_t *pid, uint32_t *tid) { unsigned long p, t; @@ -354,16 +350,13 @@ static int gdb_handle_vcont(const char *p) { return -EINVAL; case GDB_ALL_PROCESSES: - cpu = gdb_first_attached_cpu(); - while (cpu) { + cpu = get_cpu(); + if (cpu) { if (newstates[cpu->cpu_index] == 1) { newstates[cpu->cpu_index] = cur_action; - target_count++; last_target = cpu; } - - cpu = gdb_next_attached_cpu(cpu); } break; @@ -383,7 +376,7 @@ static int gdb_handle_vcont(const char *p) { break; case GDB_ONE_THREAD: - cpu = gdb_get_cpu(); + cpu = get_cpu(); /* invalid CPU/thread specified */ if (!cpu) { @@ -409,10 +402,10 @@ static int gdb_handle_vcont(const char *p) { * the ones we resumed/single stepped here. */ if (target_count > 0) { - gdbserver_state.c_cpu = last_target; + gdb_state.c_cpu = last_target; } - gdbserver_state.signal = signal; + gdb_state.signal = signal; gdb_continue_partial(newstates); return res; } @@ -522,10 +515,10 @@ static bool process_string_cmd(const char *data, const GdbCmdParseEntry *cmds, i } if (cmd->need_cpu_context) { - user_ctx = (void *)gdbserver_state.g_cpu; + user_ctx = (void *)gdb_state.c_cpu; } - gdbserver_state.allow_stop_reply = cmd->allow_stop_reply; + gdb_state.allow_stop_reply = cmd->allow_stop_reply; cmd->handler(params, user_ctx); return true; } @@ -538,8 +531,8 @@ static void run_cmd_parser(const char *data, const GdbCmdParseEntry *cmd) { return; } - g_string_set_size(gdbserver_state.str_buf, 0); - g_byte_array_set_size(gdbserver_state.mem_buf, 0); + g_string_set_size(gdb_state.str_buf, 0); + g_byte_array_set_size(gdb_state.mem_buf, 0); /* In case there was an error during the command parsing we must * send a NULL packet to indicate the command is not supported */ @@ -550,17 +543,10 @@ static void run_cmd_parser(const char *data, const GdbCmdParseEntry *cmd) { static void handle_detach(GArray *params, void *user_ctx) { GDBProcess *process; - uint32_t pid = 1; process = gdb_get_process(); gdb_process_breakpoint_remove_all(); process->attached = false; - if (pid == gdb_get_cpu_pid(gdbserver_state.c_cpu)) { - gdbserver_state.c_cpu = gdb_first_attached_cpu(); - } - if (pid == gdb_get_cpu_pid(gdbserver_state.g_cpu)) { - gdbserver_state.g_cpu = gdb_first_attached_cpu(); - } - if (!gdbserver_state.c_cpu) { + if (!get_cpu()) { gdb_disable_syscalls(); gdb_continue(); } @@ -577,7 +563,7 @@ static void handle_thread_alive(GArray *params, void *user_ctx) { gdb_put_packet("E22"); return; } - cpu = gdb_get_cpu(); + cpu = get_cpu(); if (!cpu) { gdb_put_packet("E22"); return; @@ -589,7 +575,7 @@ static void handle_continue(GArray *params, void *user_ctx) { if (params->len) { gdb_set_cpu_pc(gdb_get_cmd_param(params, 0)->val_ull); } - gdbserver_state.signal = 0; + gdb_state.signal = 0; gdb_continue(); } @@ -602,15 +588,14 @@ static void handle_cont_with_sig(GArray *params, void *user_ctx) { if (params->len) { signal = gdb_get_cmd_param(params, 0)->val_ul; } - gdbserver_state.signal = gdb_signal_to_target(signal); - if (gdbserver_state.signal == -1) { - gdbserver_state.signal = 0; + gdb_state.signal = gdb_signal_to_target(signal); + if (gdb_state.signal == -1) { + gdb_state.signal = 0; } gdb_continue(); } static void handle_set_thread(GArray *params, void *user_ctx) { - uint32_t pid, tid; CPUState *cpu; if (params->len != 2) { gdb_put_packet("E22"); @@ -624,9 +609,7 @@ static void handle_set_thread(GArray *params, void *user_ctx) { gdb_put_packet("OK"); return; } - pid = gdb_get_cmd_param(params, 1)->thread_id.pid; - tid = gdb_get_cmd_param(params, 1)->thread_id.tid; - cpu = gdb_get_cpu(); + cpu = get_cpu(); if (!cpu) { gdb_put_packet("E22"); return; @@ -637,11 +620,11 @@ static void handle_set_thread(GArray *params, void *user_ctx) { */ switch (gdb_get_cmd_param(params, 0)->opcode) { case 'c': - gdbserver_state.c_cpu = cpu; + gdb_state.c_cpu = cpu; gdb_put_packet("OK"); break; case 'g': - gdbserver_state.g_cpu = cpu; + gdb_state.c_cpu = cpu; gdb_put_packet("OK"); break; default: @@ -658,7 +641,7 @@ static void handle_insert_bp(GArray *params, void *user_ctx) { return; } - res = gdb_breakpoint_insert(gdbserver_state.c_cpu, gdb_get_cmd_param(params, 0)->val_ul, + res = gdb_breakpoint_insert(gdb_state.c_cpu, gdb_get_cmd_param(params, 0)->val_ul, gdb_get_cmd_param(params, 1)->val_ull, gdb_get_cmd_param(params, 2)->val_ull); if (res >= 0) { gdb_put_packet("OK"); @@ -679,7 +662,7 @@ static void handle_remove_bp(GArray *params, void *user_ctx) { return; } - res = gdb_breakpoint_remove(gdbserver_state.c_cpu, gdb_get_cmd_param(params, 0)->val_ul, + res = gdb_breakpoint_remove(gdb_state.c_cpu, gdb_get_cmd_param(params, 0)->val_ul, gdb_get_cmd_param(params, 1)->val_ull, gdb_get_cmd_param(params, 2)->val_ull); if (res >= 0) { gdb_put_packet("OK"); @@ -701,8 +684,8 @@ static void handle_set_reg(GArray *params, void *user_ctx) { } reg_size = strlen(gdb_get_cmd_param(params, 1)->data) / 2; - gdb_hextomem(gdbserver_state.mem_buf, gdb_get_cmd_param(params, 1)->data, reg_size); - gdb_write_register(gdbserver_state.g_cpu, gdbserver_state.mem_buf->data, gdb_get_cmd_param(params, 0)->val_ull); + gdb_hextomem(gdb_state.mem_buf, gdb_get_cmd_param(params, 1)->data, reg_size); + gdb_write_register(gdb_state.c_cpu, gdb_state.mem_buf->data, gdb_get_cmd_param(params, 0)->val_ull); gdb_put_packet("OK"); } @@ -714,15 +697,15 @@ static void handle_get_reg(GArray *params, void *user_ctx) { return; } - reg_size = gdb_read_register(gdbserver_state.g_cpu, gdbserver_state.mem_buf, gdb_get_cmd_param(params, 0)->val_ull); + reg_size = gdb_read_register(gdb_state.c_cpu, gdb_state.mem_buf, gdb_get_cmd_param(params, 0)->val_ull); if (!reg_size) { gdb_put_packet("E14"); return; } else { - g_byte_array_set_size(gdbserver_state.mem_buf, reg_size); + g_byte_array_set_size(gdb_state.mem_buf, reg_size); } - gdb_memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, reg_size); + gdb_memtohex(gdb_state.str_buf, gdb_state.mem_buf->data, reg_size); gdb_put_strbuf(); } @@ -738,9 +721,9 @@ static void handle_write_mem(GArray *params, void *user_ctx) { return; } - gdb_hextomem(gdbserver_state.mem_buf, gdb_get_cmd_param(params, 2)->data, gdb_get_cmd_param(params, 1)->val_ull); - if (gdb_target_memory_rw_debug(gdbserver_state.g_cpu, gdb_get_cmd_param(params, 0)->val_ull, - gdbserver_state.mem_buf->data, gdbserver_state.mem_buf->len, true)) { + gdb_hextomem(gdb_state.mem_buf, gdb_get_cmd_param(params, 2)->data, gdb_get_cmd_param(params, 1)->val_ull); + if (gdb_target_memory_rw_debug(gdb_state.c_cpu, gdb_get_cmd_param(params, 0)->val_ull, gdb_state.mem_buf->data, + gdb_state.mem_buf->len, true)) { gdb_put_packet("E14"); return; } @@ -760,15 +743,15 @@ static void handle_read_mem(GArray *params, void *user_ctx) { return; } - g_byte_array_set_size(gdbserver_state.mem_buf, gdb_get_cmd_param(params, 1)->val_ull); + g_byte_array_set_size(gdb_state.mem_buf, gdb_get_cmd_param(params, 1)->val_ull); - if (gdb_target_memory_rw_debug(gdbserver_state.g_cpu, gdb_get_cmd_param(params, 0)->val_ull, - gdbserver_state.mem_buf->data, gdbserver_state.mem_buf->len, false)) { + if (gdb_target_memory_rw_debug(gdb_state.c_cpu, gdb_get_cmd_param(params, 0)->val_ull, gdb_state.mem_buf->data, + gdb_state.mem_buf->len, false)) { gdb_put_packet("E14"); return; } - gdb_memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, gdbserver_state.mem_buf->len); + gdb_memtohex(gdb_state.str_buf, gdb_state.mem_buf->data, gdb_state.mem_buf->len); gdb_put_strbuf(); } @@ -782,12 +765,12 @@ static void handle_write_all_regs(GArray *params, void *user_ctx) { return; } - cpu_synchronize_state(gdbserver_state.g_cpu); + cpu_synchronize_state(gdb_state.c_cpu); len = strlen(gdb_get_cmd_param(params, 0)->data) / 2; - gdb_hextomem(gdbserver_state.mem_buf, gdb_get_cmd_param(params, 0)->data, len); - registers = gdbserver_state.mem_buf->data; - for (reg_id = 0; reg_id < gdbserver_state.g_cpu->gdb_num_g_regs && len > 0; reg_id++) { - reg_size = gdb_write_register(gdbserver_state.g_cpu, registers, reg_id); + gdb_hextomem(gdb_state.mem_buf, gdb_get_cmd_param(params, 0)->data, len); + registers = gdb_state.mem_buf->data; + for (reg_id = 0; reg_id < gdb_state.c_cpu->gdb_num_g_regs && len > 0; reg_id++) { + reg_size = gdb_write_register(gdb_state.c_cpu, registers, reg_id); len -= reg_size; registers += reg_size; } @@ -798,15 +781,15 @@ static void handle_read_all_regs(GArray *params, void *user_ctx) { int reg_id; size_t len; - cpu_synchronize_state(gdbserver_state.g_cpu); - g_byte_array_set_size(gdbserver_state.mem_buf, 0); + cpu_synchronize_state(gdb_state.c_cpu); + g_byte_array_set_size(gdb_state.mem_buf, 0); len = 0; - for (reg_id = 0; reg_id < gdbserver_state.g_cpu->gdb_num_g_regs; reg_id++) { - len += gdb_read_register(gdbserver_state.g_cpu, gdbserver_state.mem_buf, reg_id); - g_assert(len == gdbserver_state.mem_buf->len); + for (reg_id = 0; reg_id < gdb_state.c_cpu->gdb_num_g_regs; reg_id++) { + len += gdb_read_register(gdb_state.c_cpu, gdb_state.mem_buf, reg_id); + g_assert(len == gdb_state.mem_buf->len); } - gdb_memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, len); + gdb_memtohex(gdb_state.str_buf, gdb_state.mem_buf->data, len); gdb_put_strbuf(); } @@ -814,7 +797,7 @@ static void handle_step(GArray *params, void *user_ctx) { if (params->len) { gdb_set_cpu_pc(gdb_get_cmd_param(params, 0)->val_ull); } - cpu_single_step(gdbserver_state.c_cpu, gdbserver_state.sstep_flags); + cpu_single_step(gdb_state.c_cpu, gdb_state.sstep_flags); gdb_continue(); } @@ -841,7 +824,7 @@ static void handle_v_attach(GArray *params, void *user_ctx) { GDBProcess *process; CPUState *cpu; - g_string_assign(gdbserver_state.str_buf, "E22"); + g_string_assign(gdb_state.str_buf, "E22"); if (!params->len) { goto cleanup; } @@ -857,14 +840,13 @@ static void handle_v_attach(GArray *params, void *user_ctx) { } process->attached = true; - gdbserver_state.g_cpu = cpu; - gdbserver_state.c_cpu = cpu; + gdb_state.c_cpu = cpu; - if (gdbserver_state.allow_stop_reply) { - g_string_printf(gdbserver_state.str_buf, "T%02xthread:", GDB_SIGNAL_TRAP); - gdb_append_thread_id(cpu, gdbserver_state.str_buf); - g_string_append_c(gdbserver_state.str_buf, ';'); - gdbserver_state.allow_stop_reply = false; + if (gdb_state.allow_stop_reply) { + g_string_printf(gdb_state.str_buf, "T%02xthread:", GDB_SIGNAL_TRAP); + gdb_append_thread_id(cpu, gdb_state.str_buf); + g_string_append_c(gdb_state.str_buf, ';'); + gdb_state.allow_stop_reply = false; cleanup: gdb_put_strbuf(); } @@ -897,14 +879,14 @@ static void handle_v_commands(GArray *params, void *user_ctx) { } static void handle_query_qemu_sstepbits(GArray *params, void *user_ctx) { - g_string_printf(gdbserver_state.str_buf, "ENABLE=%x", SSTEP_ENABLE); + g_string_printf(gdb_state.str_buf, "ENABLE=%x", SSTEP_ENABLE); - if (gdbserver_state.supported_sstep_flags & SSTEP_NOIRQ) { - g_string_append_printf(gdbserver_state.str_buf, ",NOIRQ=%x", SSTEP_NOIRQ); + if (gdb_state.supported_sstep_flags & SSTEP_NOIRQ) { + g_string_append_printf(gdb_state.str_buf, ",NOIRQ=%x", SSTEP_NOIRQ); } - if (gdbserver_state.supported_sstep_flags & SSTEP_NOTIMER) { - g_string_append_printf(gdbserver_state.str_buf, ",NOTIMER=%x", SSTEP_NOTIMER); + if (gdb_state.supported_sstep_flags & SSTEP_NOTIMER) { + g_string_append_printf(gdb_state.str_buf, ",NOTIMER=%x", SSTEP_NOTIMER); } gdb_put_strbuf(); @@ -919,17 +901,17 @@ static void handle_set_qemu_sstep(GArray *params, void *user_ctx) { new_sstep_flags = gdb_get_cmd_param(params, 0)->val_ul; - if (new_sstep_flags & ~gdbserver_state.supported_sstep_flags) { + if (new_sstep_flags & ~gdb_state.supported_sstep_flags) { gdb_put_packet("E22"); return; } - gdbserver_state.sstep_flags = new_sstep_flags; + gdb_state.sstep_flags = new_sstep_flags; gdb_put_packet("OK"); } static void handle_query_qemu_sstep(GArray *params, void *user_ctx) { - g_string_printf(gdbserver_state.str_buf, "0x%x", gdbserver_state.sstep_flags); + g_string_printf(gdb_state.str_buf, "0x%x", gdb_state.sstep_flags); gdb_put_strbuf(); } @@ -944,30 +926,28 @@ static void handle_query_curr_tid(GArray *params, void *user_ctx) { */ process = gdb_get_process(); cpu = get_cpu(); - g_string_assign(gdbserver_state.str_buf, "QC"); - gdb_append_thread_id(cpu, gdbserver_state.str_buf); + g_string_assign(gdb_state.str_buf, "QC"); + gdb_append_thread_id(cpu, gdb_state.str_buf); gdb_put_strbuf(); } static void handle_query_threads(GArray *params, void *user_ctx) { - if (!gdbserver_state.query_cpu) { + if (!gdb_state.query_cpu) { gdb_put_packet("l"); - return; } - - g_string_assign(gdbserver_state.str_buf, "m"); - gdb_append_thread_id(gdbserver_state.query_cpu, gdbserver_state.str_buf); + g_string_assign(gdb_state.str_buf, "m"); + gdb_append_thread_id(gdb_state.query_cpu, gdb_state.str_buf); gdb_put_strbuf(); - gdbserver_state.query_cpu = gdb_next_attached_cpu(gdbserver_state.query_cpu); + gdb_state.query_cpu = 0; } static void handle_query_gdb_server_version(GArray *params, void *user_ctx) { - g_string_printf(gdbserver_state.str_buf, "name:qemu-system-riscv;version:1.9;"); + g_string_printf(gdb_state.str_buf, "name:qemu-system-riscv;version:1.9;"); gdb_put_strbuf(); } static void handle_query_first_threads(GArray *params, void *user_ctx) { - gdbserver_state.query_cpu = gdb_first_attached_cpu(); + gdb_state.query_cpu = get_cpu(); handle_query_threads(params, user_ctx); } @@ -978,13 +958,13 @@ static void handle_query_thread_extra(GArray *params, void *user_ctx) { gdb_put_packet("E22"); return; } - cpu = gdb_get_cpu(); + cpu = get_cpu(); if (!cpu) { return; } cpu_synchronize_state(cpu); g_string_printf(rs, "CPU#%d [%s]", cpu->cpu_index, cpu->halted ? "halted " : "running"); - gdb_memtohex(gdbserver_state.str_buf, (uint8_t *)rs->str, rs->len); + gdb_memtohex(gdb_state.str_buf, (uint8_t *)rs->str, rs->len); gdb_put_strbuf(); } @@ -1002,17 +982,17 @@ void gdb_extend_qsupported_features(char *qflags) { } static void handle_query_supported(GArray *params, void *user_ctx) { - g_string_printf(gdbserver_state.str_buf, "PacketSize=%x", MAX_PACKET_LENGTH); + g_string_printf(gdb_state.str_buf, "PacketSize=%x", MAX_PACKET_LENGTH); if (gdb_get_core_xml_file(get_cpu())) { - g_string_append(gdbserver_state.str_buf, ";qXfer:features:read+"); + g_string_append(gdb_state.str_buf, ";qXfer:features:read+"); } + g_string_append(gdb_state.str_buf, ";vContSupported+;multiprocess+"); if (extra_query_flags) { int extras = g_strv_length(extra_query_flags); for (int i = 0; i < extras; i++) { - g_string_append(gdbserver_state.str_buf, extra_query_flags[i]); + g_string_append(gdb_state.str_buf, extra_query_flags[i]); } } - gdb_put_strbuf(); } @@ -1028,7 +1008,7 @@ static void handle_query_xfer_features(GArray *params, void *user_ctx) { } process = gdb_get_process(); - if (!gdb_get_core_xml_file(gdbserver_state.g_cpu)) { + if (!gdb_get_core_xml_file(gdb_state.c_cpu)) { gdb_put_packet(""); return; } @@ -1053,19 +1033,19 @@ static void handle_query_xfer_features(GArray *params, void *user_ctx) { } if (len < total_len - addr) { - g_string_assign(gdbserver_state.str_buf, "m"); - gdb_memtox(gdbserver_state.str_buf, xml + addr, len); + g_string_assign(gdb_state.str_buf, "m"); + gdb_memtox(gdb_state.str_buf, xml + addr, len); } else { - g_string_assign(gdbserver_state.str_buf, "l"); - gdb_memtox(gdbserver_state.str_buf, xml + addr, total_len - addr); + g_string_assign(gdb_state.str_buf, "l"); + gdb_memtox(gdb_state.str_buf, xml + addr, total_len - addr); } - gdb_put_packet_binary(gdbserver_state.str_buf->str, gdbserver_state.str_buf->len, true); + gdb_put_packet_binary(gdb_state.str_buf->str, gdb_state.str_buf->len, true); } static void handle_query_qemu_supported(GArray *params, void *user_ctx) { - g_string_printf(gdbserver_state.str_buf, "sstepbits;sstep"); - g_string_append(gdbserver_state.str_buf, ";PhyMemMode"); + g_string_printf(gdb_state.str_buf, "sstepbits;sstep"); + g_string_append(gdb_state.str_buf, ";PhyMemMode"); gdb_put_strbuf(); } @@ -1211,26 +1191,24 @@ static void handle_gen_set(GArray *params, void *user_ctx) { } static void handle_target_halt(GArray *params, void *user_ctx) { - if (gdbserver_state.allow_stop_reply) { - g_string_printf(gdbserver_state.str_buf, "T%02xthread:", GDB_SIGNAL_TRAP); - gdb_append_thread_id(gdbserver_state.c_cpu, gdbserver_state.str_buf); - g_string_append_c(gdbserver_state.str_buf, ';'); + if (gdb_state.allow_stop_reply) { + g_string_printf(gdb_state.str_buf, "T%02xthread:", GDB_SIGNAL_TRAP); + gdb_append_thread_id(gdb_state.c_cpu, gdb_state.str_buf); + g_string_append_c(gdb_state.str_buf, ';'); gdb_put_strbuf(); - gdbserver_state.allow_stop_reply = false; + gdb_state.allow_stop_reply = false; } /* * Remove all the breakpoints when this query is issued, * because gdb is doing an initial connect and the state * should be cleaned up. */ - gdb_breakpoint_remove_all(gdbserver_state.c_cpu); + gdb_breakpoint_remove_all(gdb_state.c_cpu); } static RSState gdb_handle_packet(const char *line_buf) { const GdbCmdParseEntry *cmd_parser = NULL; - // trace_gdbstub_io_command(line_buf); - switch (line_buf[0]) { case '!': gdb_put_packet("OK"); @@ -1364,13 +1342,13 @@ static RSState gdb_handle_packet(const char *line_buf) { void gdb_read_byte(uint8_t ch) { uint8_t reply; - gdbserver_state.allow_stop_reply = false; - if (gdbserver_state.last_packet->len) { + gdb_state.allow_stop_reply = false; + if (gdb_state.last_packet->len) { /* Waiting for a response to the last packet. If we see the start of a new command then abandon the previous response. */ if (ch == '-') { // trace_gdbstub_err_got_nack(); - gdb_put_buffer(gdbserver_state.last_packet->data, gdbserver_state.last_packet->len); + gdb_put_buffer(gdb_state.last_packet->data, gdb_state.last_packet->len); } else if (ch == '+') { // trace_gdbstub_io_got_ack(); } else { @@ -1378,7 +1356,7 @@ void gdb_read_byte(uint8_t ch) { } if (ch == '+' || ch == '$') { - g_byte_array_set_size(gdbserver_state.last_packet, 0); + g_byte_array_set_size(gdb_state.last_packet, 0); } if (ch != '$') return; } @@ -1393,17 +1371,17 @@ void gdb_read_byte(uint8_t ch) { if (ch != 0x03) { // trace_gdbstub_err_unexpected_runpkt(ch); } else { - gdbserver_state.allow_stop_reply = true; + gdb_state.allow_stop_reply = true; } - vm_stop(RUN_STATE_PAUSED); + vm_stop(); } else { - switch (gdbserver_state.state) { + switch (gdb_state.state) { case RS_IDLE: if (ch == '$') { /* start of command packet */ - gdbserver_state.line_buf_index = 0; - gdbserver_state.line_sum = 0; - gdbserver_state.state = RS_GETLINE; + gdb_state.line_buf_index = 0; + gdb_state.line_sum = 0; + gdb_state.state = RS_GETLINE; } else if (ch == '+') { /* * do nothing, gdb may preemptively send out ACKs on @@ -1416,37 +1394,37 @@ void gdb_read_byte(uint8_t ch) { case RS_GETLINE: if (ch == '}') { /* start escape sequence */ - gdbserver_state.state = RS_GETLINE_ESC; - gdbserver_state.line_sum += ch; + gdb_state.state = RS_GETLINE_ESC; + gdb_state.line_sum += ch; } else if (ch == '*') { /* start run length encoding sequence */ - gdbserver_state.state = RS_GETLINE_RLE; - gdbserver_state.line_sum += ch; + gdb_state.state = RS_GETLINE_RLE; + gdb_state.line_sum += ch; } else if (ch == '#') { /* end of command, start of checksum*/ - gdbserver_state.state = RS_CHKSUM1; - } else if (gdbserver_state.line_buf_index >= sizeof(gdbserver_state.line_buf) - 1) { + gdb_state.state = RS_CHKSUM1; + } else if (gdb_state.line_buf_index >= sizeof(gdb_state.line_buf) - 1) { // trace_gdbstub_err_overrun(); - gdbserver_state.state = RS_IDLE; + gdb_state.state = RS_IDLE; } else { /* unescaped command character */ - gdbserver_state.line_buf[gdbserver_state.line_buf_index++] = ch; - gdbserver_state.line_sum += ch; + gdb_state.line_buf[gdb_state.line_buf_index++] = ch; + gdb_state.line_sum += ch; } break; case RS_GETLINE_ESC: if (ch == '#') { /* unexpected end of command in escape sequence */ - gdbserver_state.state = RS_CHKSUM1; - } else if (gdbserver_state.line_buf_index >= sizeof(gdbserver_state.line_buf) - 1) { + gdb_state.state = RS_CHKSUM1; + } else if (gdb_state.line_buf_index >= sizeof(gdb_state.line_buf) - 1) { /* command buffer overrun */ // trace_gdbstub_err_overrun(); - gdbserver_state.state = RS_IDLE; + gdb_state.state = RS_IDLE; } else { /* parse escaped character and leave escape state */ - gdbserver_state.line_buf[gdbserver_state.line_buf_index++] = ch ^ 0x20; - gdbserver_state.line_sum += ch; - gdbserver_state.state = RS_GETLINE; + gdb_state.line_buf[gdb_state.line_buf_index++] = ch ^ 0x20; + gdb_state.line_sum += ch; + gdb_state.state = RS_GETLINE; } break; case RS_GETLINE_RLE: @@ -1457,25 +1435,25 @@ void gdb_read_byte(uint8_t ch) { if (ch < ' ' || ch == '#' || ch == '$' || ch > 126) { /* invalid RLE count encoding */ // trace_gdbstub_err_invalid_repeat(ch); - gdbserver_state.state = RS_GETLINE; + gdb_state.state = RS_GETLINE; } else { /* decode repeat length */ int repeat = ch - ' ' + 3; - if (gdbserver_state.line_buf_index + repeat >= sizeof(gdbserver_state.line_buf) - 1) { + if (gdb_state.line_buf_index + repeat >= sizeof(gdb_state.line_buf) - 1) { /* that many repeats would overrun the command buffer */ // trace_gdbstub_err_overrun(); - gdbserver_state.state = RS_IDLE; - } else if (gdbserver_state.line_buf_index < 1) { + gdb_state.state = RS_IDLE; + } else if (gdb_state.line_buf_index < 1) { /* got a repeat but we have nothing to repeat */ // trace_gdbstub_err_invalid_rle(); - gdbserver_state.state = RS_GETLINE; + gdb_state.state = RS_GETLINE; } else { /* repeat the last character */ - memset(gdbserver_state.line_buf + gdbserver_state.line_buf_index, - gdbserver_state.line_buf[gdbserver_state.line_buf_index - 1], repeat); - gdbserver_state.line_buf_index += repeat; - gdbserver_state.line_sum += ch; - gdbserver_state.state = RS_GETLINE; + memset(gdb_state.line_buf + gdb_state.line_buf_index, gdb_state.line_buf[gdb_state.line_buf_index - 1], + repeat); + gdb_state.line_buf_index += repeat; + gdb_state.line_sum += ch; + gdb_state.state = RS_GETLINE; } } break; @@ -1483,33 +1461,33 @@ void gdb_read_byte(uint8_t ch) { /* get high hex digit of checksum */ if (!isxdigit(ch)) { // trace_gdbstub_err_checksum_invalid(ch); - gdbserver_state.state = RS_GETLINE; + gdb_state.state = RS_GETLINE; break; } - gdbserver_state.line_buf[gdbserver_state.line_buf_index] = '\0'; - gdbserver_state.line_csum = fromhex(ch) << 4; - gdbserver_state.state = RS_CHKSUM2; + gdb_state.line_buf[gdb_state.line_buf_index] = '\0'; + gdb_state.line_csum = fromhex(ch) << 4; + gdb_state.state = RS_CHKSUM2; break; case RS_CHKSUM2: /* get low hex digit of checksum */ if (!isxdigit(ch)) { // trace_gdbstub_err_checksum_invalid(ch); - gdbserver_state.state = RS_GETLINE; + gdb_state.state = RS_GETLINE; break; } - gdbserver_state.line_csum |= fromhex(ch); + gdb_state.line_csum |= fromhex(ch); - if (gdbserver_state.line_csum != (gdbserver_state.line_sum & 0xff)) { - // trace_gdbstub_err_checksum_incorrect(gdbserver_state.line_sum, gdbserver_state.line_csum); + if (gdb_state.line_csum != (gdb_state.line_sum & 0xff)) { + // trace_gdbstub_err_checksum_incorrect(gdb_state.line_sum, gdb_state.line_csum); /* send NAK reply */ reply = '-'; gdb_put_buffer(&reply, 1); - gdbserver_state.state = RS_IDLE; + gdb_state.state = RS_IDLE; } else { /* send ACK reply */ reply = '+'; gdb_put_buffer(&reply, 1); - gdbserver_state.state = gdb_handle_packet(gdbserver_state.line_buf); + gdb_state.state = gdb_handle_packet(gdb_state.line_buf); } break; default: diff --git a/gdbstub/internals.h b/gdbstub/internals.h index 1a174a0..aa6e9e2 100644 --- a/gdbstub/internals.h +++ b/gdbstub/internals.h @@ -10,6 +10,7 @@ #define GDBSTUB_INTERNALS_H #include "cpu.h" +#include "utils/conn.h" #define MAX_PACKET_LENGTH 131104 @@ -69,11 +70,10 @@ typedef struct GDBRegisterState { } GDBRegisterState; typedef struct GDBState { - bool init; /* have we been initialised? */ - CPUState *c_cpu; /* current CPU for step/continue ops */ - CPUState *g_cpu; /* current CPU for other ops */ - CPUState *query_cpu; /* for q{f|s}ThreadInfo */ - enum RSState state; /* parsing state */ + 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 */ @@ -86,6 +86,7 @@ typedef struct GDBState { 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. @@ -94,7 +95,7 @@ typedef struct GDBState { } GDBState; /* lives in main gdbstub.c */ -extern GDBState gdbserver_state; +extern GDBState gdb_state; /* * Inline utility function, convert from int to hex and back @@ -128,15 +129,13 @@ 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(CPUState *cpu); +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); -bool gdbserver_start(const char *port_or_device, Error **errp); - /** * gdb_feature_builder_append_tag() - Append a tag. * @builder: The builder. @@ -179,10 +178,11 @@ 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); -int gdb_get_cpu_index(CPUState *cpu); unsigned int gdb_get_max_cpus(void); /* both */ void gdb_create_default_process(GDBState *s); @@ -193,7 +193,7 @@ void gdb_continue(void); int gdb_continue_partial(char *newstates); -void gdb_init_gdbserver_state(void); +void gdb_init_gdb_state(void); void gdb_handle_query_rcmd(GArray *params, void *ctx); /* system */ @@ -209,13 +209,13 @@ void gdb_disable_syscalls(void); // TODO bool runstate_is_running(); -void vm_stop(RunState rs); +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(); -CPUState *cpu_next(CPUState *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); @@ -223,33 +223,23 @@ int gdb_breakpoint_remove(CPUState *cs, int type, vaddr addr, vaddr len); void gdb_put_buffer(const uint8_t *buf, int len); -bool gdbserver_start(const char *device, Error **errp); - 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); -void vm_start(); bool vm_prepare_start(bool step_requested); void qemu_clock_enable(); -uint32_t gdb_get_cpu_pid(CPUState *cpu); - -#define CPU_FOREACH(cpu) for (auto c = get_cpu(); false;) - int gdb_target_memory_rw_debug(CPUState *cs, hwaddr addr, uint8_t *buf, int len, bool is_write); -CPUState *find_cpu(uint32_t thread_id); -CPUState *gdb_next_cpu_in_process(CPUState *cpu); -CPUState *gdb_next_attached_cpu(CPUState *cpu); -CPUState *gdb_first_attached_cpu(void); -CPUState *gdb_get_cpu(); const char *get_feature_xml(const char *p, const char **newp, GDBProcess *process); -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); 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 */ diff --git a/gdbstub/stub.cpp b/gdbstub/stub.cpp index ebf8bb9..3877634 100644 --- a/gdbstub/stub.cpp +++ b/gdbstub/stub.cpp @@ -1,17 +1,27 @@ #include -#include "conn.h" #include "internals.h" +#include "utils/conn.h" int main(int argc, char *argv[]) { + cpu_register_gdb_commands(); + gdb_init_gdb_state(); + gdb_init_cpu(); + vm_stop(); + gdb_state.state = RS_IDLE; + conn_t conn; - if (!conn_init(&conn, "127.0.0.1", 1234)) std::cout << "conn_init error" << std::endl; + if (!conn_init(&conn, "127.0.0.1", 1234)) { + std::cout << "conn_init error" << std::endl; + return -1; + } + gdb_state.conn = &conn; while (1) { conn_recv_packet(&conn); packet_t *pkt = conn_pop_packet(&conn); - printf("packet = %s\n", pkt->data); + if (pkt) gdb_chr_receive(pkt->data, pkt->end_pos + 1); } return 0; diff --git a/gdbstub/system.c b/gdbstub/system.c index 8ffb965..acf1300 100644 --- a/gdbstub/system.c +++ b/gdbstub/system.c @@ -17,17 +17,13 @@ #include "enums.h" #include "internals.h" -static void reset_gdbserver_state(void) { - g_free(gdbserver_state.processes); - gdbserver_state.processes = NULL; - gdbserver_state.process_num = 0; - gdbserver_state.allow_stop_reply = false; +static void reset_gdb_state(void) { + g_free(gdb_state.processes); + gdb_state.processes = NULL; + gdb_state.process_num = 0; + gdb_state.allow_stop_reply = false; } -int gdb_get_cpu_index(CPUState *cpu) { return 0; } - -bool gdb_got_immediate_ack(void) { return true; } - void gdb_chr_receive(const uint8_t *buf, int size) { for (int i = 0; i < size; i++) { gdb_read_byte(buf[i]); @@ -46,7 +42,7 @@ int gdb_target_memory_rw_debug(CPUState *cpu, hwaddr addr, uint8_t *buf, int len unsigned int gdb_get_max_cpus(void) { return 1; } void gdb_handle_query_qemu_phy_mem_mode(GArray *params, void *ctx) { - g_string_printf(gdbserver_state.str_buf, "%d", 1); + g_string_printf(gdb_state.str_buf, "%d", 1); gdb_put_strbuf(); } @@ -73,13 +69,13 @@ void gdb_handle_query_rcmd(GArray *params, void *ctx) { return; } - g_assert(gdbserver_state.mem_buf->len == 0); + g_assert(gdb_state.mem_buf->len == 0); len = len / 2; - gdb_hextomem(gdbserver_state.mem_buf, gdb_get_cmd_param(params, 0)->data, len); - g_byte_array_append(gdbserver_state.mem_buf, &zero, 1); + gdb_hextomem(gdb_state.mem_buf, gdb_get_cmd_param(params, 0)->data, len); + g_byte_array_append(gdb_state.mem_buf, &zero, 1); - // qemu_chr_be_write(gdbserver_system_state.mon_chr, gdbserver_state.mem_buf->data, gdbserver_state.mem_buf->len); - gdb_put_buffer(gdbserver_state.mem_buf->data, gdbserver_state.mem_buf->len); + // qemu_chr_be_write(gdbserver_system_state.mon_chr, gdb_state.mem_buf->data, gdb_state.mem_buf->len); + gdb_put_buffer(gdb_state.mem_buf->data, gdb_state.mem_buf->len); gdb_put_packet("OK"); } @@ -114,7 +110,7 @@ int gdb_continue_partial(char *newstates) { break; /* nothing to do here */ case 's': // trace_gdbstub_op_stepping(c->cpu_index); - cpu_single_step(c, gdbserver_state.sstep_flags); + cpu_single_step(c, gdb_state.sstep_flags); cpu_resume(c); flag = 1; break; @@ -147,33 +143,40 @@ int gdb_signal_to_target(int sig) { } } -bool runstate_is_running() { return true; } -void vm_stop(RunState rs) {} +bool runstate_is_running() { + auto c = get_cpu(); + return c->running; +} +void vm_stop() { get_cpu()->running = false; } +void vm_start() { get_cpu()->running = true; } + void cpu_synchronize_state(CPUState *cpu) {} void gdb_exit(int i) {}; void gdb_qemu_exit(int i) {}; -CPUState *get_cpu() { return (CPUState *)1; } -CPUState *cpu_next(CPUState *cpu) { - if (cpu) - return (CPUState *)0; - else - return (CPUState *)1; -} +CPUState *get_cpu() { return gdb_state.c_cpu; } void gdb_breakpoint_remove_all(CPUState *cs) {} bool gdb_supports_guest_debug(void) { return false; } int gdb_breakpoint_insert(CPUState *cs, int type, vaddr addr, vaddr len) { return 0; } int gdb_breakpoint_remove(CPUState *cs, int type, vaddr addr, vaddr len) { return 0; } -void gdb_put_buffer(const uint8_t *buf, int len) {} +void gdb_put_buffer(const uint8_t *buf, int len) { conn_send_str(gdb_state.conn, (char *)buf, len); } -bool gdbserver_start(const char *device, Error **errp) { return false; } +bool gdb_got_immediate_ack(void) { + int i = conn_get_char(gdb_state.conn); + if (i < 0) { + return true; /* no response, continue anyway */ + } + if (i == '+') { + return true; /* received correctly, continue */ + } + return false; /* anything else, including '-' then try again */ +} 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) { return false; } -void vm_start() {} bool vm_prepare_start(bool step_requested) { return true; } void qemu_clock_enable() {} @@ -182,62 +185,6 @@ void gdb_disable_syscalls(void) {} void cpu_single_step(CPUState *cpu, int enabled) {} -uint32_t gdb_get_cpu_pid(CPUState *cpu) { - if (cpu->cluster_index == UNASSIGNED_CLUSTER_INDEX) { - /* Return the default process' PID */ - int index = gdbserver_state.process_num - 1; - return gdbserver_state.processes[index].pid; - } - return cpu->cluster_index + 1; -} - -CPUState *find_cpu(uint32_t thread_id) { - auto cpu = get_cpu(); - if (gdb_get_cpu_index(cpu) == thread_id) { - return cpu; - } - return NULL; -} - -CPUState *gdb_next_cpu_in_process(CPUState *cpu) { - uint32_t pid = gdb_get_cpu_pid(cpu); - cpu = cpu_next(cpu); - - while (cpu) { - if (gdb_get_cpu_pid(cpu) == pid) { - break; - } - - cpu = cpu_next(cpu); - } - - return cpu; -} - -/* Return the cpu following @cpu, while ignoring unattached processes. */ -CPUState *gdb_next_attached_cpu(CPUState *cpu) { - cpu = cpu_next(cpu); - while (cpu) { - if (gdb_get_process()->attached) { - break; - } - cpu = cpu_next(cpu); - } - return cpu; -} - -/* Return the first attached cpu */ -CPUState *gdb_first_attached_cpu(void) { - CPUState *cpu = get_cpu(); - GDBProcess *process = gdb_get_process(); - if (!process->attached) { - return gdb_next_attached_cpu(cpu); - } - return cpu; -} - -CPUState *gdb_get_cpu() { return gdb_first_attached_cpu(); } - const char *get_feature_xml(const char *p, const char **newp, GDBProcess *process) { CPUState *cpu = get_cpu(); GDBRegisterState *r; @@ -280,7 +227,7 @@ const char *get_feature_xml(const char *p, const char **newp, GDBProcess *proces return NULL; } -GDBProcess *gdb_get_process() { return gdbserver_state.processes; } +GDBProcess *gdb_get_process() { return gdb_state.processes; } void gdb_create_default_process(GDBState *s) { GDBProcess *process; @@ -298,22 +245,34 @@ static void gdb_register_feature(CPUState *cpu, int base_reg, gdb_get_reg_cb get g_array_append_val(cpu->gdb_regs, s); } -void gdb_init_cpu(CPUState *cpu) { - CPUClass *cc = cpu->cc; +void gdb_init_cpu() { + // CPUState c; + CPUState *cpu = new CPUState; + memset(cpu, 0, sizeof(CPUState)); + gdb_state.c_cpu = cpu; + + CPUClass *cc = new CPUClass; + memset(cc, 0, sizeof(CPUClass)); + cc->gdb_read_register = cpu_common_gdb_read_register; + cc->gdb_write_register = cpu_common_gdb_write_register; + cc->gdb_num_core_regs = 33; + + cpu->cc = cc; const GDBFeature *feature; const char *xmlfile = gdb_get_core_xml_file(cpu); cpu->gdb_regs = g_array_new(false, false, sizeof(GDBRegisterState)); + cpu->gdb_num_regs = cpu->gdb_num_g_regs = 33; - if (xmlfile) { - feature = gdb_find_static_feature(xmlfile); - gdb_register_feature(cpu, 0, cc->gdb_read_register, cc->gdb_write_register, feature); - cpu->gdb_num_regs = cpu->gdb_num_g_regs = feature->num_regs; - } + // if (xmlfile) { + // feature = gdb_find_static_feature(xmlfile); + // gdb_register_feature(cpu, 0, cc->gdb_read_register, cc->gdb_write_register, feature); + // cpu->gdb_num_regs = cpu->gdb_num_g_regs = feature->num_regs; + // } - if (cc->gdb_num_core_regs) { - cpu->gdb_num_regs = cpu->gdb_num_g_regs = cc->gdb_num_core_regs; - } + // if (cc->gdb_num_core_regs) { + // cpu->gdb_num_regs = cpu->gdb_num_g_regs = cc->gdb_num_core_regs; + // } } void gdb_register_coprocessor(CPUState *cpu, gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg, const GDBFeature *feature, @@ -358,7 +317,6 @@ void gdb_unregister_coprocessor_all(CPUState *cpu) { const char *gdb_get_core_xml_file(CPUState *cpu) { CPUClass *cc = cpu->cc; - /* * The CPU class can provide the XML filename via a method, * or as a simple fixed string field. @@ -373,4 +331,33 @@ void cpu_resume(CPUState *cpu) { cpu->stop = false; cpu->stopped = false; // qemu_cpu_kick(cpu); -} \ No newline at end of file +} + +void cpu_register_gdb_commands() { + g_autoptr(GPtrArray) query_table = g_ptr_array_new(); + g_autoptr(GPtrArray) set_table = g_ptr_array_new(); + g_autoptr(GString) qsupported_features = g_string_new(NULL); + + /* Set arch-specific handlers for 'q' commands. */ + if (query_table->len) { + gdb_extend_query_table(query_table); + } + + /* Set arch-specific handlers for 'Q' commands. */ + if (set_table->len) { + gdb_extend_set_table(set_table); + } + + /* Set arch-specific qSupported feature. */ + if (qsupported_features->len) { + gdb_extend_qsupported_features(qsupported_features->str); + } +} + +int cpu_common_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg) { + uint32_t val = 0; + g_byte_array_append(buf, (uint8_t *)&val, 4); + return 4; +} + +int cpu_common_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg) { return 0; } \ No newline at end of file diff --git a/gdbstub/utils/conn.c b/gdbstub/utils/conn.c index fbfe4ff..3770ea7 100644 --- a/gdbstub/utils/conn.c +++ b/gdbstub/utils/conn.c @@ -1,4 +1,5 @@ #include "conn.h" + #include #include #include @@ -8,170 +9,165 @@ #include #include #include + #include "utils/csum.h" #include "utils/log.h" -static bool socket_poll(int socket_fd, int timeout, int events) -{ - struct pollfd pfd = (struct pollfd){ - .fd = socket_fd, - .events = events, - }; +static bool socket_poll(int socket_fd, int timeout, int events) { + struct pollfd pfd = (struct pollfd){ + .fd = socket_fd, + .events = events, + }; - return (poll(&pfd, 1, timeout) > 0) && (pfd.revents & events); + return (poll(&pfd, 1, timeout) > 0) && (pfd.revents & events); } -static bool socket_readable(int socket_fd, int timeout) -{ - return socket_poll(socket_fd, timeout, POLLIN); -} +static bool socket_readable(int socket_fd, int timeout) { return socket_poll(socket_fd, timeout, POLLIN); } -static bool socket_writable(int socket_fd, int timeout) -{ - return socket_poll(socket_fd, timeout, POLLOUT); -} +static bool socket_writable(int socket_fd, int timeout) { return socket_poll(socket_fd, timeout, POLLOUT); } -bool conn_init(conn_t *conn, char *addr_str, int port) -{ - if (!pktbuf_init(&conn->pktbuf)) - return false; +bool conn_init(conn_t *conn, char *addr_str, int port) { + if (!pktbuf_init(&conn->pktbuf)) return false; - struct in_addr addr_ip; - if (inet_aton(addr_str, &addr_ip) != 0) { - struct sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = addr_ip.s_addr; - addr.sin_port = htons(port); - conn->listen_fd = socket(AF_INET, SOCK_STREAM, 0); - if (conn->listen_fd < 0) - return false; + struct in_addr addr_ip; + if (inet_aton(addr_str, &addr_ip) != 0) { + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = addr_ip.s_addr; + addr.sin_port = htons(port); + conn->listen_fd = socket(AF_INET, SOCK_STREAM, 0); + if (conn->listen_fd < 0) return false; - int optval = 1; - if (setsockopt(conn->listen_fd, SOL_SOCKET, SO_REUSEADDR, &optval, - sizeof(optval)) < 0) { - warn("Set sockopt fail.\n"); - goto fail; - } - - if (bind(conn->listen_fd, (struct sockaddr *) &addr, sizeof(addr)) < - 0) { - warn("Bind fail.\n"); - goto fail; - } - } else { - struct sockaddr_un addr; - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, addr_str, sizeof(addr.sun_path) - 1); - unlink(addr_str); - conn->listen_fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (conn->listen_fd < 0) - return false; - - if (bind(conn->listen_fd, (struct sockaddr *) &addr, sizeof(addr)) < - 0) { - warn("Bind fail.\n"); - goto fail; - } + int optval = 1; + if (setsockopt(conn->listen_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) { + warn("Set sockopt fail.\n"); + goto fail; } - if (listen(conn->listen_fd, 1) < 0) { - warn("Listen fail.\n"); - goto fail; + if (bind(conn->listen_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + warn("Bind fail.\n"); + goto fail; } + } else { + struct sockaddr_un addr; + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, addr_str, sizeof(addr.sun_path) - 1); + unlink(addr_str); + conn->listen_fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (conn->listen_fd < 0) return false; - conn->socket_fd = accept(conn->listen_fd, NULL, NULL); - if (conn->socket_fd < 0) { - warn("Accept fail.\n"); - goto fail; + if (bind(conn->listen_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + warn("Bind fail.\n"); + goto fail; } + } - return true; + if (listen(conn->listen_fd, 1) < 0) { + warn("Listen fail.\n"); + goto fail; + } + + conn->socket_fd = accept(conn->listen_fd, NULL, NULL); + if (conn->socket_fd < 0) { + warn("Accept fail.\n"); + goto fail; + } + + return true; fail: - close(conn->listen_fd); - return false; + close(conn->listen_fd); + return false; } -void conn_recv_packet(conn_t *conn) -{ - while (!pktbuf_is_complete(&conn->pktbuf) && - socket_readable(conn->socket_fd, -1)) { - ssize_t nread = pktbuf_fill_from_file(&conn->pktbuf, conn->socket_fd); - if (nread == -1) - break; - } - - conn_send_str(conn, STR_ACK); +void conn_recv_packet(conn_t *conn) { + while (!pktbuf_is_complete(&conn->pktbuf) && socket_readable(conn->socket_fd, -1)) { + ssize_t nread = pktbuf_fill_from_file(&conn->pktbuf, conn->socket_fd); + if (nread == -1) break; + } + // conn_send_str(conn, STR_ACK); } -packet_t *conn_pop_packet(conn_t *conn) -{ - packet_t *pkt = pktbuf_pop_packet(&conn->pktbuf); - - return pkt; +packet_t *conn_pop_packet(conn_t *conn) { + packet_t *pkt = pktbuf_pop_packet(&conn->pktbuf); + if (pkt) printf("packet = %s\n", pkt->data); + return pkt; } -bool conn_try_recv_intr(conn_t *conn) -{ - char ch; - - if (!socket_readable(conn->socket_fd, 0)) - return false; - - ssize_t nread = read(conn->socket_fd, &ch, 1); - if (nread != 1) - return false; - - /* FIXME: The character must be INTR_CHAR, otherwise the library - * may work incorrectly. However, I'm not sure if this implementation - * can always meet our expectation (concurrent is so hard QAQ). */ - assert(ch == INTR_CHAR); - return true; +int conn_get_char(conn_t *conn) { + if (socket_readable(conn->socket_fd, -1)) { + uint8_t ch; + int ret; + read(conn->socket_fd, &ch, 1); + return ch; + } + return -1; } -void conn_send_str(conn_t *conn, char *str) -{ - size_t len = strlen(str); +bool conn_try_recv_intr(conn_t *conn) { + char ch; - while (len > 0 && socket_writable(conn->socket_fd, -1)) { - ssize_t nwrite = write(conn->socket_fd, str, len); - if (nwrite == -1) - break; - len -= nwrite; - } + if (!socket_readable(conn->socket_fd, 0)) return false; + + ssize_t nread = read(conn->socket_fd, &ch, 1); + if (nread != 1) return false; + + /* FIXME: The character must be INTR_CHAR, otherwise the library + * may work incorrectly. However, I'm not sure if this implementation + * can always meet our expectation (concurrent is so hard QAQ). */ + assert(ch == INTR_CHAR); + return true; } -void conn_send_pktstr(conn_t *conn, char *pktstr) -{ - char packet[MAX_SEND_PACKET_SIZE]; - size_t len = strlen(pktstr); +void conn_send_str(conn_t *conn, char *str) { + size_t len = strlen(str); + while (len > 0 && socket_writable(conn->socket_fd, -1)) { + ssize_t nwrite = write(conn->socket_fd, str, len); + if (nwrite == -1) break; + len -= nwrite; + } +} - /* 2: '$' + '#' - * 2: checksum digits(maximum) - * 1: '\0' */ - assert(len + 2 + CSUM_SIZE + 1 < MAX_SEND_PACKET_SIZE); +void conn_send_str(conn_t *conn, char *str, int len) { + int ori = len; + while (len > 0 && socket_writable(conn->socket_fd, -1)) { + ssize_t nwrite = write(conn->socket_fd, str, len); + if (nwrite == -1) break; + len -= nwrite; + } + str[ori] = 0; + printf("send = %s\n", str); +} - packet[0] = '$'; - memcpy(packet + 1, pktstr, len); - packet[len + 1] = '#'; +void conn_send_pktstr(conn_t *conn, char *pktstr) { + char packet[MAX_SEND_PACKET_SIZE]; + size_t len = strlen(pktstr); - char csum_str[4]; - uint8_t csum = compute_checksum(pktstr, len); - size_t csum_len = snprintf(csum_str, sizeof(csum_str) - 1, "%02x", csum); - assert(csum_len == CSUM_SIZE); - memcpy(packet + len + 2, csum_str, csum_len); - packet[len + 2 + csum_len] = '\0'; + /* 2: '$' + '#' + * 2: checksum digits(maximum) + * 1: '\0' */ + assert(len + 2 + CSUM_SIZE + 1 < MAX_SEND_PACKET_SIZE); + + packet[0] = '$'; + memcpy(packet + 1, pktstr, len); + packet[len + 1] = '#'; + + char csum_str[4]; + uint8_t csum = compute_checksum(pktstr, len); + size_t csum_len = snprintf(csum_str, sizeof(csum_str) - 1, "%02x", csum); + assert(csum_len == CSUM_SIZE); + memcpy(packet + len + 2, csum_str, csum_len); + packet[len + 2 + csum_len] = '\0'; #ifdef DEBUG - printf("send packet = %s,", packet); - printf(" checksum = %d\n", csum); + printf("send packet = %s,", packet); + printf(" checksum = %d\n", csum); #endif - conn_send_str(conn, packet); + conn_send_str(conn, packet); } -void conn_close(conn_t *conn) -{ - close(conn->socket_fd); - close(conn->listen_fd); - pktbuf_destroy(&conn->pktbuf); +void conn_close(conn_t *conn) { + close(conn->socket_fd); + close(conn->listen_fd); + pktbuf_destroy(&conn->pktbuf); } diff --git a/gdbstub/utils/conn.h b/gdbstub/utils/conn.h index 2f12b61..778a125 100644 --- a/gdbstub/utils/conn.h +++ b/gdbstub/utils/conn.h @@ -20,7 +20,9 @@ bool conn_init(conn_t *conn, char *addr_str, int port); void conn_recv_packet(conn_t *conn); packet_t *conn_pop_packet(conn_t *conn); bool conn_try_recv_intr(conn_t *conn); +int conn_get_char(conn_t *conn); void conn_send_str(conn_t *conn, char *str); +void conn_send_str(conn_t *conn, char *str, int len); void conn_send_pktstr(conn_t *conn, char *pktstr); void conn_close(conn_t *conn); #endif