From 8eaf2c0b7b3578da0a834d92d416e755e89784bb Mon Sep 17 00:00:00 2001 From: Colin Date: Mon, 22 Sep 2025 07:28:09 +0000 Subject: [PATCH] Update gdbstub. --- .vscode/settings.json | 3 +- backup/breakpoint.h | 30 + backup/cpu.h | 1178 ++++++++++++++++++++++++++++++++++++++++ gdbstub/CMakeLists.txt | 9 +- gdbstub/breakpoint.h | 30 + gdbstub/cpu.h | 1151 +++++++++++++++++++++++++++++++++++++++ gdbstub/enums.h | 6 + gdbstub/gdbstub.h | 47 +- gdbstub/stub.cpp | 2 +- gdbstub/system.c | 834 +++++++++++++--------------- 10 files changed, 2808 insertions(+), 482 deletions(-) create mode 100644 backup/breakpoint.h create mode 100644 backup/cpu.h create mode 100644 gdbstub/breakpoint.h create mode 100644 gdbstub/cpu.h diff --git a/.vscode/settings.json b/.vscode/settings.json index 51f9f15..1b6e058 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -53,6 +53,7 @@ "ostream": "cpp", "stdexcept": "cpp", "streambuf": "cpp", - "typeinfo": "cpp" + "typeinfo": "cpp", + "internals.h": "c" } } \ No newline at end of file diff --git a/backup/breakpoint.h b/backup/breakpoint.h new file mode 100644 index 0000000..95f0482 --- /dev/null +++ b/backup/breakpoint.h @@ -0,0 +1,30 @@ +/* + * 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/cpu.h b/backup/cpu.h new file mode 100644 index 0000000..fb788ca --- /dev/null +++ b/backup/cpu.h @@ -0,0 +1,1178 @@ +/* + * 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/gdbstub/CMakeLists.txt b/gdbstub/CMakeLists.txt index 773b85d..99bc37a 100644 --- a/gdbstub/CMakeLists.txt +++ b/gdbstub/CMakeLists.txt @@ -46,6 +46,13 @@ set(CMAKE_CXX_EXTENSIONS ON) +find_package(PkgConfig REQUIRED) # 查找 GLib 库 +pkg_check_modules(GLIB REQUIRED glib-2.0) +include_directories(${GLIB_INCLUDE_DIRS}) # 添加头文件路径 +link_directories(${GLIB_LIBRARY_DIRS}) # 添加链接库 + + + file(GLOB UTILS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/utils/*.cpp @@ -55,4 +62,4 @@ set_source_files_properties(${UTILS_SRC} PROPERTIES LANGUAGE CXX) add_executable(stub ${UTILS_SRC} stub.cpp) target_include_directories(stub PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(stub PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/utils) - +target_link_libraries(stub ${GLIB_LIBRARIES}) diff --git a/gdbstub/breakpoint.h b/gdbstub/breakpoint.h new file mode 100644 index 0000000..7334e78 --- /dev/null +++ b/gdbstub/breakpoint.h @@ -0,0 +1,30 @@ +/* + * 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 { + uintptr_t pc; + int flags; /* BP_* */ + struct CPUBreakpoint* entry; +} CPUBreakpoint; + +typedef struct CPUWatchpoint { + uintptr_t vaddr; + uintptr_t len; + uintptr_t hitaddr; + // MemTxAttrs hitattrs; + int flags; /* BP_* */ + struct CPUWatchpoint* entry; +} CPUWatchpoint; + +#endif diff --git a/gdbstub/cpu.h b/gdbstub/cpu.h new file mode 100644 index 0000000..e8a3bd2 --- /dev/null +++ b/gdbstub/cpu.h @@ -0,0 +1,1151 @@ +/* + * 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 "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" +#include "enums.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; + +struct CPUState; + +/** + * 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 *tname, 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 { +// } 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; + + 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 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: +// * @tname: 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 *tname, const char *cpu_model); + +/** + * cpu_model_from_type: + * @tname: 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 *tname); + +/** + * cpu_create: + * @tname: The CPU type. + * + * Instantiates a CPU and realizes the CPU. + * + * Returns: A #CPUState or %NULL if an error occurred. + */ +CPUState *cpu_create(const char *tname); + +/** + * 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/gdbstub/enums.h b/gdbstub/enums.h index c4d54a1..0377e70 100644 --- a/gdbstub/enums.h +++ b/gdbstub/enums.h @@ -11,6 +11,12 @@ #define DEFAULT_GDBSTUB_PORT "1234" + +typedef uintptr_t vaddr; +typedef int Error; + +#define MMU_ACCESS_COUNT 16 + /* GDB breakpoint/watchpoint types */ #define GDB_BREAKPOINT_SW 0 #define GDB_BREAKPOINT_HW 1 diff --git a/gdbstub/gdbstub.h b/gdbstub/gdbstub.h index a16c005..ce79b0b 100644 --- a/gdbstub/gdbstub.h +++ b/gdbstub/gdbstub.h @@ -1,22 +1,23 @@ #ifndef GDBSTUB_H #define GDBSTUB_H +#include "cpu.h" + typedef struct GDBFeature { - const char *xmlname; - const char *xml; - const char *name; - const char * const *regs; - int num_regs; + 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; + 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); @@ -36,9 +37,8 @@ void gdb_init_cpu(CPUState *cpu); * @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); +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 @@ -70,8 +70,7 @@ bool gdbserver_start(const char *port_or_device, Error **errp); * @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, +void gdb_feature_builder_init(GDBFeatureBuilder *builder, GDBFeature *feature, const char *name, const char *xmlname, int base_reg); /** @@ -80,9 +79,7 @@ void gdb_feature_builder_init(GDBFeatureBuilder *builder, GDBFeature *feature, * @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, ...); +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. @@ -93,12 +90,8 @@ gdb_feature_builder_append_tag(const GDBFeatureBuilder *builder, * @type: The type of the register. * @group: The register group to which this register belongs; it can be NULL. */ -void gdb_feature_builder_append_reg(const GDBFeatureBuilder *builder, - const char *name, - int bitsize, - int regnum, - const char *type, - const char *group); +void gdb_feature_builder_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. @@ -142,9 +135,9 @@ 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; + int gdb_reg; + const char *name; + const char *feature_name; } GDBRegDesc; /** diff --git a/gdbstub/stub.cpp b/gdbstub/stub.cpp index 846a16f..f67bc8b 100644 --- a/gdbstub/stub.cpp +++ b/gdbstub/stub.cpp @@ -1,8 +1,8 @@ -// #include "gdbstub.h" #include #include "conn.h" +#include "gdbstub.h" int main(int argc, char *argv[]) { conn_t conn; diff --git a/gdbstub/system.c b/gdbstub/system.c index 5be0d3c..1436e0e 100644 --- a/gdbstub/system.c +++ b/gdbstub/system.c @@ -10,44 +10,43 @@ * 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 "accel/accel-cpu-ops.h" +#include "accel/accel-ops.h" +#include "chardev/char-fe.h" +#include "chardev/char.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 "gdbstub/commands.h" +#include "gdbstub/syscalls.h" +#include "hw/boards.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" +#include "monitor/monitor.h" +#include "qapi/error.h" +#include "qemu/cutils.h" +#include "qemu/error-report.h" +#include "qemu/osdep.h" +#include "system/cpus.h" +#include "system/replay.h" +#include "system/runstate.h" +#include "system/tcg.h" +#include "trace.h" /* System emulation specific state */ typedef struct { - CharBackend chr; - Chardev *mon_chr; + 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; +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; } /* @@ -56,18 +55,12 @@ static void reset_gdbserver_state(void) * 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; -} +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; -} +bool gdb_got_immediate_ack(void) { return true; } /* * GDB Connection management. For system emulation we do all of this @@ -75,36 +68,34 @@ bool gdb_got_immediate_ack(void) * 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); +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; +static void gdb_chr_event(void *opaque, QEMUChrEvent event) { + int i; + GDBState *s = (GDBState *)opaque; - switch (event) { + 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; - } + /* 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; + s->c_cpu = gdb_first_attached_cpu(); + s->g_cpu = s->c_cpu; - vm_stop(RUN_STATE_PAUSED); - replay_gdb_attached(); - break; + vm_stop(RUN_STATE_PAUSED); + replay_gdb_attached(); + break; default: - break; - } + break; + } } /* @@ -115,142 +106,124 @@ static void gdb_chr_event(void *opaque, QEMUChrEvent event) * 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); +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; +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; - } + if (running || gdbserver_state.state == RS_INACTIVE) { + return; + } - /* Is there a GDB syscall waiting to be sent? */ - if (gdb_handled_syscall()) { - return; - } + /* Is there a GDB syscall waiting to be sent? */ + if (gdb_handled_syscall()) { + return; + } - if (cpu == NULL) { - /* No process attached */ - return; - } + if (cpu == NULL) { + /* No process attached */ + return; + } - if (!gdbserver_state.allow_stop_reply) { - return; - } + if (!gdbserver_state.allow_stop_reply) { + return; + } - gdb_append_thread_id(cpu, tid); + gdb_append_thread_id(cpu, tid); - switch (state) { + 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 (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; } - if (tcg_enabled()) { - tb_flush(cpu); - } - ret = GDB_SIGNAL_TRAP; - 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; + trace_gdbstub_hit_paused(); + ret = GDB_SIGNAL_INT; + break; case RUN_STATE_SHUTDOWN: - trace_gdbstub_hit_shutdown(); - ret = GDB_SIGNAL_QUIT; - break; + trace_gdbstub_hit_shutdown(); + ret = GDB_SIGNAL_QUIT; + break; case RUN_STATE_IO_ERROR: - trace_gdbstub_hit_io_error(); - ret = GDB_SIGNAL_STOP; - break; + trace_gdbstub_hit_io_error(); + ret = GDB_SIGNAL_STOP; + break; case RUN_STATE_WATCHDOG: - trace_gdbstub_hit_watchdog(); - ret = GDB_SIGNAL_ALRM; - break; + trace_gdbstub_hit_watchdog(); + ret = GDB_SIGNAL_ALRM; + break; case RUN_STATE_INTERNAL_ERROR: - trace_gdbstub_hit_internal_error(); - ret = GDB_SIGNAL_ABRT; - break; + trace_gdbstub_hit_internal_error(); + ret = GDB_SIGNAL_ABRT; + break; case RUN_STATE_SAVE_VM: case RUN_STATE_RESTORE_VM: - return; + return; case RUN_STATE_FINISH_MIGRATE: - ret = GDB_SIGNAL_XCPU; - break; + 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); + 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; + gdb_put_packet(buf->str); + gdbserver_state.allow_stop_reply = false; - /* disable single step if it was enabled */ - cpu_single_step(cpu, 0); + /* 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 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 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); +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; + cc->internal = true; + cc->open = gdb_monitor_open; + cc->chr_write = gdb_monitor_write; } #define TYPE_CHARDEV_GDB "chardev-gdb" @@ -261,8 +234,7 @@ static const TypeInfo char_gdb_type_info = { .class_init = char_gdb_class_init, }; -static int gdb_chr_can_receive(void *opaque) -{ +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. @@ -270,341 +242,307 @@ static int gdb_chr_can_receive(void *opaque) return MAX_PACKET_LENGTH; } -static void gdb_chr_receive(void *opaque, const uint8_t *buf, int size) -{ - int i; +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]); - } + 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; +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); + s->processes = g_renew(GDBProcess, s->processes, ++s->process_num); - process = &s->processes[s->process_num - 1]; + 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; + /* + * 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 0; + } - return object_child_foreach(child, find_cpu_clusters, opaque); + 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; +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; - } + 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); +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); - } + 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); + 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); +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 (!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"); } - - 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; + else if (strcmp(device, "stdio") == 0) { + struct sigaction act; - memset(&act, 0, sizeof(act)); - act.sa_handler = gdb_sigterm_handler; - sigaction(SIGINT, &act, NULL); - } + 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; - } + /* + * 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(); + if (!gdbserver_state.init) { + gdb_init_gdbserver_state(); - qemu_add_vm_change_state_handler(gdb_vm_state_change, NULL); + 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(); - } + /* 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); + 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(); + 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; + return true; } -static void register_types(void) -{ - type_register_static(&char_gdb_type_info); -} +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]; +void gdb_exit(int code) { + char buf[4]; - if (!gdbserver_state.init) { - return; - } + if (!gdbserver_state.init) { + return; + } - trace_gdbstub_op_exiting((uint8_t)code); + 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; - } + 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); + 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); -} +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; +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); - } + 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); + 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; +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; -} +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_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; - } +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"); + 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; +void gdb_handle_query_rcmd(GArray *params, void *ctx) { + const guint8 zero = 0; + int len; - if (!params->len) { - gdb_put_packet("E22"); - return; - } + 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; - } + 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"); + 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_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(); - } +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; +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 (!runstate_needs_reset()) { + bool step_requested = false; + CPU_FOREACH(cpu) { + if (newstates[cpu->cpu_index] == 's') { + step_requested = true; + break; + } } - if (flag) { - qemu_clock_enable(QEMU_CLOCK_VIRTUAL, true); + + if (vm_prepare_start(step_requested)) { + return 0; } - return res; + + 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; } /* @@ -612,58 +550,50 @@ int gdb_continue_partial(char *newstates) * signals are not yet supported. */ -enum { - TARGET_SIGINT = 2, - TARGET_SIGTRAP = 5 -}; +enum { TARGET_SIGINT = 2, TARGET_SIGTRAP = 5 }; -int gdb_signal_to_target(int sig) -{ - switch (sig) { +int gdb_signal_to_target(int sig) { + switch (sig) { case 2: - return TARGET_SIGINT; + return TARGET_SIGINT; case 5: - return TARGET_SIGTRAP; + return TARGET_SIGTRAP; default: - return -1; - } + 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; +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_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; +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); - } +void gdb_breakpoint_remove_all(CPUState *cs) { + const AccelOpsClass *ops = cpus_get_accel(); + if (ops->remove_all_breakpoints) { + ops->remove_all_breakpoints(cs); + } }