Re: [PATCH] x86: Drop KGDB support

From: H. Peter Anvin

Date: Wed Oct 28 2026 - 10:21:23 EST


On October 28, 2026 5:21:24 AM PDT, Peter Zijlstra <peterz@xxxxxxxxxxxxx> wrote:
>
>KGDB does some horrible things, and with everybody having access to
>virtualization these days, there is really no reason to use it over
>Qemu's gdb-server.
>
>Signed-off-by: Peter Zijlstra (Intel) <peterz@xxxxxxxxxxxxx>
>---
> arch/x86/Kconfig | 2 -
> arch/x86/include/asm/kgdb.h | 92 ----
> arch/x86/include/asm/text-patching.h | 1 -
> arch/x86/kernel/Makefile | 1 -
> arch/x86/kernel/alternative.c | 50 +--
> arch/x86/kernel/cpu/common.c | 16 -
> arch/x86/kernel/early_printk.c | 8 -
> arch/x86/kernel/kgdb.c | 785 -----------------------------------
> arch/x86/kernel/kprobes/core.c | 10 -
> arch/x86/kernel/kprobes/opt.c | 11 +-
> arch/x86/kernel/traps.c | 10 +-
> arch/x86/platform/uv/uv_nmi.c | 76 ----
> 12 files changed, 13 insertions(+), 1049 deletions(-)
>
>diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
>index a5e29ffd5ddc..627553e8496f 100644
>--- a/arch/x86/Kconfig
>+++ b/arch/x86/Kconfig
>@@ -82,7 +82,6 @@ config X86
> select ARCH_HAS_DELAY_TIMER
> select ARCH_HAS_DEVMEM_IS_ALLOWED
> select ARCH_HAS_DMA_OPS if GART_IOMMU || XEN
>- select ARCH_HAS_EARLY_DEBUG if KGDB
> select ARCH_HAS_ELF_RANDOMIZE
> select ARCH_HAS_EXECMEM_ROX if X86_64 && STRICT_MODULE_RWX
> select ARCH_HAS_FAST_MULTIPLIER
>@@ -198,7 +197,6 @@ config X86
> select HAVE_ARCH_KASAN_VMALLOC if X86_64
> select HAVE_ARCH_KFENCE
> select HAVE_ARCH_KMSAN if X86_64
>- select HAVE_ARCH_KGDB
> select HAVE_ARCH_KSTACK_ERASE
> select HAVE_ARCH_MMAP_RND_BITS if MMU
> select HAVE_ARCH_MMAP_RND_COMPAT_BITS if MMU && COMPAT
>diff --git a/arch/x86/include/asm/kgdb.h b/arch/x86/include/asm/kgdb.h
>deleted file mode 100644
>index aacaf2502bd2..000000000000
>--- a/arch/x86/include/asm/kgdb.h
>+++ /dev/null
>@@ -1,92 +0,0 @@
>-/* SPDX-License-Identifier: GPL-2.0 */
>-#ifndef _ASM_X86_KGDB_H
>-#define _ASM_X86_KGDB_H
>-
>-/*
>- * Copyright (C) 2001-2004 Amit S. Kale
>- * Copyright (C) 2008 Wind River Systems, Inc.
>- */
>-
>-#include <asm/ptrace.h>
>-
>-/*
>- * BUFMAX defines the maximum number of characters in inbound/outbound
>- * buffers at least NUMREGBYTES*2 are needed for register packets
>- * Longer buffer is needed to list all threads
>- */
>-#define BUFMAX 1024
>-
>-/*
>- * Note that this register image is in a different order than
>- * the register image that Linux produces at interrupt time.
>- *
>- * Linux's register image is defined by struct pt_regs in ptrace.h.
>- * Just why GDB uses a different order is a historical mystery.
>- */
>-#ifdef CONFIG_X86_32
>-enum regnames {
>- GDB_AX, /* 0 */
>- GDB_CX, /* 1 */
>- GDB_DX, /* 2 */
>- GDB_BX, /* 3 */
>- GDB_SP, /* 4 */
>- GDB_BP, /* 5 */
>- GDB_SI, /* 6 */
>- GDB_DI, /* 7 */
>- GDB_PC, /* 8 also known as eip */
>- GDB_PS, /* 9 also known as eflags */
>- GDB_CS, /* 10 */
>- GDB_SS, /* 11 */
>- GDB_DS, /* 12 */
>- GDB_ES, /* 13 */
>- GDB_FS, /* 14 */
>- GDB_GS, /* 15 */
>-};
>-#define GDB_ORIG_AX 41
>-#define DBG_MAX_REG_NUM 16
>-#define NUMREGBYTES ((GDB_GS+1)*4)
>-#else /* ! CONFIG_X86_32 */
>-enum regnames {
>- GDB_AX, /* 0 */
>- GDB_BX, /* 1 */
>- GDB_CX, /* 2 */
>- GDB_DX, /* 3 */
>- GDB_SI, /* 4 */
>- GDB_DI, /* 5 */
>- GDB_BP, /* 6 */
>- GDB_SP, /* 7 */
>- GDB_R8, /* 8 */
>- GDB_R9, /* 9 */
>- GDB_R10, /* 10 */
>- GDB_R11, /* 11 */
>- GDB_R12, /* 12 */
>- GDB_R13, /* 13 */
>- GDB_R14, /* 14 */
>- GDB_R15, /* 15 */
>- GDB_PC, /* 16 */
>- GDB_PS, /* 17 */
>- GDB_CS, /* 18 */
>- GDB_SS, /* 19 */
>- GDB_DS, /* 20 */
>- GDB_ES, /* 21 */
>- GDB_FS, /* 22 */
>- GDB_GS, /* 23 */
>-};
>-#define GDB_ORIG_AX 57
>-#define DBG_MAX_REG_NUM 24
>-/* 17 64 bit regs and 5 32 bit regs */
>-#define NUMREGBYTES ((17 * 8) + (5 * 4))
>-#endif /* ! CONFIG_X86_32 */
>-
>-static inline void arch_kgdb_breakpoint(void)
>-{
>- asm(" int $3");
>-}
>-#define BREAK_INSTR_SIZE 1
>-#define CACHE_FLUSH_IS_SAFE 1
>-#define GDB_ADJUSTS_BREAK_OFFSET
>-
>-extern int kgdb_ll_trap(int cmd, const char *str,
>- struct pt_regs *regs, long err, int trap, int sig);
>-
>-#endif /* _ASM_X86_KGDB_H */
>diff --git a/arch/x86/include/asm/text-patching.h b/arch/x86/include/asm/text-patching.h
>index ea09381070e8..a1d284560ef8 100644
>--- a/arch/x86/include/asm/text-patching.h
>+++ b/arch/x86/include/asm/text-patching.h
>@@ -33,7 +33,6 @@ extern void text_poke_apply_relocation(u8 *buf, const u8 * const instr, size_t i
> */
> extern void *text_poke(void *addr, const void *opcode, size_t len);
> extern void smp_text_poke_sync_each_cpu(void);
>-extern void *text_poke_kgdb(void *addr, const void *opcode, size_t len);
> extern void *text_poke_copy(void *addr, const void *opcode, size_t len);
> #define text_poke_copy text_poke_copy
> extern void *text_poke_copy_locked(void *addr, const void *opcode, size_t len, bool core_ok);
>diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
>index 31f46fd00527..1a16af34dee3 100644
>--- a/arch/x86/kernel/Makefile
>+++ b/arch/x86/kernel/Makefile
>@@ -128,7 +128,6 @@ obj-$(CONFIG_CRASH_DUMP) += crash_dump_$(BITS).o crash.o
> obj-y += kprobes/
> obj-$(CONFIG_MODULES) += module.o
> obj-$(CONFIG_X86_32) += doublefault_32.o
>-obj-$(CONFIG_KGDB) += kgdb.o
> obj-$(CONFIG_VM86) += vm86_32.o
> obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
>
>diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c
>index efbc11f8c336..d8add52ff923 100644
>--- a/arch/x86/kernel/alternative.c
>+++ b/arch/x86/kernel/alternative.c
>@@ -7,7 +7,6 @@
> #include <linux/memory.h>
> #include <linux/execmem.h>
> #include <linux/cleanup.h>
>-#include <linux/kgdb.h>
> #include <linux/mmap_lock.h>
>
> #include <asm/text-patching.h>
>@@ -2393,25 +2392,17 @@ static void __poke_vmalloc_pages(struct page **pages, void *addr,
> static void poke_vmalloc_pages(struct page **pages, void *addr,
> bool cross_page_boundary)
> {
>- if (in_dbg_master()) {
>- /*
>- * If called from kgdb cannot sleep, but all other CPUs stopped
>- * anyway so safe to proceed without locks
>- */
>- __poke_vmalloc_pages(pages, addr, cross_page_boundary);
>- } else {
>- /*
>- * execmem ROX ranges are shared between modules and can be
>- * collapsed to huge PMD entries, and this collapse can happen
>- * concurrently with a racing set_memory_rox().
>- *
>- * Prevent vmalloc_to_page() from racing by acquiring an
>- * init_mm read lock which pairs with the init_mm write lock in
>- * cpa_collapse_large_pages().
>- */
>- guard(mmap_read_lock)(&init_mm);
>- __poke_vmalloc_pages(pages, addr, cross_page_boundary);
>- }
>+ /*
>+ * execmem ROX ranges are shared between modules and can be
>+ * collapsed to huge PMD entries, and this collapse can happen
>+ * concurrently with a racing set_memory_rox().
>+ *
>+ * Prevent vmalloc_to_page() from racing by acquiring an
>+ * init_mm read lock which pairs with the init_mm write lock in
>+ * cpa_collapse_large_pages().
>+ */
>+ guard(mmap_read_lock)(&init_mm);
>+ __poke_vmalloc_pages(pages, addr, cross_page_boundary);
> }
>
> static void *__text_poke(text_poke_f func, void *addr, const void *src, size_t len)
>@@ -2541,25 +2532,6 @@ void *text_poke(void *addr, const void *opcode, size_t len)
> return __text_poke(text_poke_memcpy, addr, opcode, len);
> }
>
>-/**
>- * text_poke_kgdb - Update instructions on a live kernel by kgdb
>- * @addr: address to modify
>- * @opcode: source of the copy
>- * @len: length to copy
>- *
>- * Only atomic text poke/set should be allowed when not doing early patching.
>- * It means the size must be writable atomically and the address must be aligned
>- * in a way that permits an atomic write. It also makes sure we fit on a single
>- * page.
>- *
>- * Context: should only be used by kgdb, which ensures no other core is running,
>- * despite the fact it does not hold the text_mutex.
>- */
>-void *text_poke_kgdb(void *addr, const void *opcode, size_t len)
>-{
>- return __text_poke(text_poke_memcpy, addr, opcode, len);
>-}
>-
> void *text_poke_copy_locked(void *addr, const void *opcode, size_t len,
> bool core_ok)
> {
>diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
>index 9bd50c68cd7e..8a4aabcd5d60 100644
>--- a/arch/x86/kernel/cpu/common.c
>+++ b/arch/x86/kernel/cpu/common.c
>@@ -18,7 +18,6 @@
> #include <linux/sched/smt.h>
> #include <linux/init.h>
> #include <linux/kprobes.h>
>-#include <linux/kgdb.h>
> #include <linux/mem_encrypt.h>
> #include <linux/smp.h>
> #include <linux/cpu.h>
>@@ -2340,20 +2339,6 @@ static void initialize_debug_regs(void)
> set_debugreg(0, 0);
> }
>
>-#ifdef CONFIG_KGDB
>-/*
>- * Restore debug regs if using kgdbwait and you have a kernel debugger
>- * connection established.
>- */
>-static void dbg_restore_debug_regs(void)
>-{
>- if (unlikely(kgdb_connected && arch_kgdb_ops.correct_hw_break))
>- arch_kgdb_ops.correct_hw_break();
>-}
>-#else /* ! CONFIG_KGDB */
>-#define dbg_restore_debug_regs()
>-#endif /* ! CONFIG_KGDB */
>-
> static inline void setup_getcpu(int cpu)
> {
> unsigned long cpudata = vdso_encode_cpunode(cpu, early_cpu_to_node(cpu));
>@@ -2511,7 +2496,6 @@ void cpu_init(void)
> load_mm_ldt(&init_mm);
>
> initialize_debug_regs();
>- dbg_restore_debug_regs();
>
> doublefault_init_cpu_tss();
>
>diff --git a/arch/x86/kernel/early_printk.c b/arch/x86/kernel/early_printk.c
>index cba75306e5b6..ad7534771bcf 100644
>--- a/arch/x86/kernel/early_printk.c
>+++ b/arch/x86/kernel/early_printk.c
>@@ -46,14 +46,6 @@ static void early_vga_write(struct console *con, const char *str, unsigned n)
> writew(0x720, VGABASE + 2*(max_xpos*j + i));
> current_ypos = max_ypos-1;
> }
>-#ifdef CONFIG_KGDB_KDB
>- if (c == '\b') {
>- if (current_xpos > 0)
>- current_xpos--;
>- } else if (c == '\r') {
>- current_xpos = 0;
>- } else
>-#endif
> if (c == '\n') {
> current_xpos = 0;
> current_ypos++;
>diff --git a/arch/x86/kernel/kgdb.c b/arch/x86/kernel/kgdb.c
>deleted file mode 100644
>index 96af1242454e..000000000000
>--- a/arch/x86/kernel/kgdb.c
>+++ /dev/null
>@@ -1,785 +0,0 @@
>-// SPDX-License-Identifier: GPL-2.0-or-later
>-/*
>- */
>-
>-/*
>- * Copyright (C) 2004 Amit S. Kale <amitkale@xxxxxxxxxxxxxx>
>- * Copyright (C) 2000-2001 VERITAS Software Corporation.
>- * Copyright (C) 2002 Andi Kleen, SuSE Labs
>- * Copyright (C) 2004 LinSysSoft Technologies Pvt. Ltd.
>- * Copyright (C) 2007 MontaVista Software, Inc.
>- * Copyright (C) 2007-2008 Jason Wessel, Wind River Systems, Inc.
>- */
>-/****************************************************************************
>- * Contributor: Lake Stevens Instrument Division$
>- * Written by: Glenn Engel $
>- * Updated by: Amit Kale<akale@xxxxxxxxxxx>
>- * Updated by: Tom Rini <trini@xxxxxxxxxxxxxxxxxxx>
>- * Updated by: Jason Wessel <jason.wessel@xxxxxxxxxxxxx>
>- * Modified for 386 by Jim Kingdon, Cygnus Support.
>- * Original kgdb, compatibility with 2.1.xx kernel by
>- * David Grothe <dave@xxxxxxxx>
>- * Integrated into 2.2.5 kernel by Tigran Aivazian <tigran@xxxxxxx>
>- * X86_64 changes from Andi Kleen's patch merged by Jim Houston
>- */
>-#include <linux/spinlock.h>
>-#include <linux/kdebug.h>
>-#include <linux/string.h>
>-#include <linux/kernel.h>
>-#include <linux/ptrace.h>
>-#include <linux/sched.h>
>-#include <linux/delay.h>
>-#include <linux/kgdb.h>
>-#include <linux/smp.h>
>-#include <linux/nmi.h>
>-#include <linux/hw_breakpoint.h>
>-#include <linux/uaccess.h>
>-#include <linux/memory.h>
>-
>-#include <asm/text-patching.h>
>-#include <asm/debugreg.h>
>-#include <asm/apicdef.h>
>-#include <asm/apic.h>
>-#include <asm/nmi.h>
>-#include <asm/switch_to.h>
>-
>-struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] =
>-{
>-#ifdef CONFIG_X86_32
>- { "ax", 4, offsetof(struct pt_regs, ax) },
>- { "cx", 4, offsetof(struct pt_regs, cx) },
>- { "dx", 4, offsetof(struct pt_regs, dx) },
>- { "bx", 4, offsetof(struct pt_regs, bx) },
>- { "sp", 4, offsetof(struct pt_regs, sp) },
>- { "bp", 4, offsetof(struct pt_regs, bp) },
>- { "si", 4, offsetof(struct pt_regs, si) },
>- { "di", 4, offsetof(struct pt_regs, di) },
>- { "ip", 4, offsetof(struct pt_regs, ip) },
>- { "flags", 4, offsetof(struct pt_regs, flags) },
>- { "cs", 4, offsetof(struct pt_regs, cs) },
>- { "ss", 4, offsetof(struct pt_regs, ss) },
>- { "ds", 4, offsetof(struct pt_regs, ds) },
>- { "es", 4, offsetof(struct pt_regs, es) },
>-#else
>- { "ax", 8, offsetof(struct pt_regs, ax) },
>- { "bx", 8, offsetof(struct pt_regs, bx) },
>- { "cx", 8, offsetof(struct pt_regs, cx) },
>- { "dx", 8, offsetof(struct pt_regs, dx) },
>- { "si", 8, offsetof(struct pt_regs, si) },
>- { "di", 8, offsetof(struct pt_regs, di) },
>- { "bp", 8, offsetof(struct pt_regs, bp) },
>- { "sp", 8, offsetof(struct pt_regs, sp) },
>- { "r8", 8, offsetof(struct pt_regs, r8) },
>- { "r9", 8, offsetof(struct pt_regs, r9) },
>- { "r10", 8, offsetof(struct pt_regs, r10) },
>- { "r11", 8, offsetof(struct pt_regs, r11) },
>- { "r12", 8, offsetof(struct pt_regs, r12) },
>- { "r13", 8, offsetof(struct pt_regs, r13) },
>- { "r14", 8, offsetof(struct pt_regs, r14) },
>- { "r15", 8, offsetof(struct pt_regs, r15) },
>- { "ip", 8, offsetof(struct pt_regs, ip) },
>- { "flags", 4, offsetof(struct pt_regs, flags) },
>- { "cs", 4, offsetof(struct pt_regs, cs) },
>- { "ss", 4, offsetof(struct pt_regs, ss) },
>- { "ds", 4, -1 },
>- { "es", 4, -1 },
>-#endif
>- { "fs", 4, -1 },
>- { "gs", 4, -1 },
>-};
>-
>-int dbg_set_reg(int regno, void *mem, struct pt_regs *regs)
>-{
>- if (
>-#ifdef CONFIG_X86_32
>- regno == GDB_SS || regno == GDB_FS || regno == GDB_GS ||
>-#endif
>- regno == GDB_SP || regno == GDB_ORIG_AX)
>- return 0;
>-
>- if (dbg_reg_def[regno].offset != -1)
>- memcpy((void *)regs + dbg_reg_def[regno].offset, mem,
>- dbg_reg_def[regno].size);
>- return 0;
>-}
>-
>-char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs)
>-{
>- if (regno == GDB_ORIG_AX) {
>- memcpy(mem, &regs->orig_ax, sizeof(regs->orig_ax));
>- return "orig_ax";
>- }
>- if (regno >= DBG_MAX_REG_NUM || regno < 0)
>- return NULL;
>-
>- if (dbg_reg_def[regno].offset != -1)
>- memcpy(mem, (void *)regs + dbg_reg_def[regno].offset,
>- dbg_reg_def[regno].size);
>-
>-#ifdef CONFIG_X86_32
>- switch (regno) {
>- case GDB_GS:
>- case GDB_FS:
>- *(unsigned long *)mem = 0xFFFF;
>- break;
>- }
>-#endif
>- return dbg_reg_def[regno].name;
>-}
>-
>-/**
>- * sleeping_thread_to_gdb_regs - Convert ptrace regs to GDB regs
>- * @gdb_regs: A pointer to hold the registers in the order GDB wants.
>- * @p: The &struct task_struct of the desired process.
>- *
>- * Convert the register values of the sleeping process in @p to
>- * the format that GDB expects.
>- * This function is called when kgdb does not have access to the
>- * &struct pt_regs and therefore it should fill the gdb registers
>- * @gdb_regs with what has been saved in &struct thread_struct
>- * thread field during switch_to.
>- */
>-void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
>-{
>-#ifndef CONFIG_X86_32
>- u32 *gdb_regs32 = (u32 *)gdb_regs;
>-#endif
>- gdb_regs[GDB_AX] = 0;
>- gdb_regs[GDB_BX] = 0;
>- gdb_regs[GDB_CX] = 0;
>- gdb_regs[GDB_DX] = 0;
>- gdb_regs[GDB_SI] = 0;
>- gdb_regs[GDB_DI] = 0;
>- gdb_regs[GDB_BP] = ((struct inactive_task_frame *)p->thread.sp)->bp;
>-#ifdef CONFIG_X86_32
>- gdb_regs[GDB_DS] = __KERNEL_DS;
>- gdb_regs[GDB_ES] = __KERNEL_DS;
>- gdb_regs[GDB_PS] = 0;
>- gdb_regs[GDB_CS] = __KERNEL_CS;
>- gdb_regs[GDB_SS] = __KERNEL_DS;
>- gdb_regs[GDB_FS] = 0xFFFF;
>- gdb_regs[GDB_GS] = 0xFFFF;
>-#else
>- gdb_regs32[GDB_PS] = 0;
>- gdb_regs32[GDB_CS] = __KERNEL_CS;
>- gdb_regs32[GDB_SS] = __KERNEL_DS;
>- gdb_regs[GDB_R8] = 0;
>- gdb_regs[GDB_R9] = 0;
>- gdb_regs[GDB_R10] = 0;
>- gdb_regs[GDB_R11] = 0;
>- gdb_regs[GDB_R12] = 0;
>- gdb_regs[GDB_R13] = 0;
>- gdb_regs[GDB_R14] = 0;
>- gdb_regs[GDB_R15] = 0;
>-#endif
>- gdb_regs[GDB_PC] = 0;
>- gdb_regs[GDB_SP] = p->thread.sp;
>-}
>-
>-static struct hw_breakpoint {
>- unsigned enabled;
>- unsigned long addr;
>- int len;
>- int type;
>- struct perf_event * __percpu *pev;
>-} breakinfo[HBP_NUM];
>-
>-static unsigned long early_dr7;
>-
>-static void kgdb_correct_hw_break(void)
>-{
>- int breakno;
>-
>- for (breakno = 0; breakno < HBP_NUM; breakno++) {
>- struct perf_event *bp;
>- struct arch_hw_breakpoint *info;
>- int val;
>- int cpu = raw_smp_processor_id();
>- if (!breakinfo[breakno].enabled)
>- continue;
>- if (dbg_is_early) {
>- set_debugreg(breakinfo[breakno].addr, breakno);
>- early_dr7 |= encode_dr7(breakno,
>- breakinfo[breakno].len,
>- breakinfo[breakno].type);
>- set_debugreg(early_dr7, 7);
>- continue;
>- }
>- bp = *per_cpu_ptr(breakinfo[breakno].pev, cpu);
>- info = counter_arch_bp(bp);
>- if (bp->attr.disabled != 1)
>- continue;
>- bp->attr.bp_addr = breakinfo[breakno].addr;
>- bp->attr.bp_len = breakinfo[breakno].len;
>- bp->attr.bp_type = breakinfo[breakno].type;
>- info->address = breakinfo[breakno].addr;
>- info->len = breakinfo[breakno].len;
>- info->type = breakinfo[breakno].type;
>- val = arch_install_hw_breakpoint(bp);
>- if (!val)
>- bp->attr.disabled = 0;
>- }
>- if (!dbg_is_early)
>- hw_breakpoint_restore();
>-}
>-
>-static int hw_break_reserve_slot(int breakno)
>-{
>- int cpu;
>- int cnt = 0;
>- struct perf_event **pevent;
>-
>- if (dbg_is_early)
>- return 0;
>-
>- for_each_online_cpu(cpu) {
>- cnt++;
>- pevent = per_cpu_ptr(breakinfo[breakno].pev, cpu);
>- if (dbg_reserve_bp_slot(*pevent))
>- goto fail;
>- }
>-
>- return 0;
>-
>-fail:
>- for_each_online_cpu(cpu) {
>- cnt--;
>- if (!cnt)
>- break;
>- pevent = per_cpu_ptr(breakinfo[breakno].pev, cpu);
>- dbg_release_bp_slot(*pevent);
>- }
>- return -1;
>-}
>-
>-static int hw_break_release_slot(int breakno)
>-{
>- struct perf_event **pevent;
>- int cpu;
>-
>- if (dbg_is_early)
>- return 0;
>-
>- for_each_online_cpu(cpu) {
>- pevent = per_cpu_ptr(breakinfo[breakno].pev, cpu);
>- if (dbg_release_bp_slot(*pevent))
>- /*
>- * The debugger is responsible for handing the retry on
>- * remove failure.
>- */
>- return -1;
>- }
>- return 0;
>-}
>-
>-static int
>-kgdb_remove_hw_break(unsigned long addr, int len, enum kgdb_bptype bptype)
>-{
>- int i;
>-
>- for (i = 0; i < HBP_NUM; i++)
>- if (breakinfo[i].addr == addr && breakinfo[i].enabled)
>- break;
>- if (i == HBP_NUM)
>- return -1;
>-
>- if (hw_break_release_slot(i)) {
>- printk(KERN_ERR "Cannot remove hw breakpoint at %lx\n", addr);
>- return -1;
>- }
>- breakinfo[i].enabled = 0;
>-
>- return 0;
>-}
>-
>-static void kgdb_remove_all_hw_break(void)
>-{
>- int i;
>- int cpu = raw_smp_processor_id();
>- struct perf_event *bp;
>-
>- for (i = 0; i < HBP_NUM; i++) {
>- if (!breakinfo[i].enabled)
>- continue;
>- bp = *per_cpu_ptr(breakinfo[i].pev, cpu);
>- if (!bp->attr.disabled) {
>- arch_uninstall_hw_breakpoint(bp);
>- bp->attr.disabled = 1;
>- continue;
>- }
>- if (dbg_is_early)
>- early_dr7 &= ~encode_dr7(i, breakinfo[i].len,
>- breakinfo[i].type);
>- else if (hw_break_release_slot(i))
>- printk(KERN_ERR "KGDB: hw bpt remove failed %lx\n",
>- breakinfo[i].addr);
>- breakinfo[i].enabled = 0;
>- }
>-}
>-
>-static int
>-kgdb_set_hw_break(unsigned long addr, int len, enum kgdb_bptype bptype)
>-{
>- int i;
>-
>- for (i = 0; i < HBP_NUM; i++)
>- if (!breakinfo[i].enabled)
>- break;
>- if (i == HBP_NUM)
>- return -1;
>-
>- switch (bptype) {
>- case BP_HARDWARE_BREAKPOINT:
>- len = 1;
>- breakinfo[i].type = X86_BREAKPOINT_EXECUTE;
>- break;
>- case BP_WRITE_WATCHPOINT:
>- breakinfo[i].type = X86_BREAKPOINT_WRITE;
>- break;
>- case BP_ACCESS_WATCHPOINT:
>- breakinfo[i].type = X86_BREAKPOINT_RW;
>- break;
>- default:
>- return -1;
>- }
>- switch (len) {
>- case 1:
>- breakinfo[i].len = X86_BREAKPOINT_LEN_1;
>- break;
>- case 2:
>- breakinfo[i].len = X86_BREAKPOINT_LEN_2;
>- break;
>- case 4:
>- breakinfo[i].len = X86_BREAKPOINT_LEN_4;
>- break;
>-#ifdef CONFIG_X86_64
>- case 8:
>- breakinfo[i].len = X86_BREAKPOINT_LEN_8;
>- break;
>-#endif
>- default:
>- return -1;
>- }
>- breakinfo[i].addr = addr;
>- if (hw_break_reserve_slot(i)) {
>- breakinfo[i].addr = 0;
>- return -1;
>- }
>- breakinfo[i].enabled = 1;
>-
>- return 0;
>-}
>-
>-/**
>- * kgdb_disable_hw_debug - Disable hardware debugging while we in kgdb.
>- * @regs: Current &struct pt_regs.
>- *
>- * This function will be called if the particular architecture must
>- * disable hardware debugging while it is processing gdb packets or
>- * handling exception.
>- */
>-static void kgdb_disable_hw_debug(struct pt_regs *regs)
>-{
>- int i;
>- int cpu = raw_smp_processor_id();
>- struct perf_event *bp;
>-
>- /* Disable hardware debugging while we are in kgdb: */
>- set_debugreg(DR7_FIXED_1, 7);
>- for (i = 0; i < HBP_NUM; i++) {
>- if (!breakinfo[i].enabled)
>- continue;
>- if (dbg_is_early) {
>- early_dr7 &= ~encode_dr7(i, breakinfo[i].len,
>- breakinfo[i].type);
>- continue;
>- }
>- bp = *per_cpu_ptr(breakinfo[i].pev, cpu);
>- if (bp->attr.disabled == 1)
>- continue;
>- arch_uninstall_hw_breakpoint(bp);
>- bp->attr.disabled = 1;
>- }
>-}
>-
>-#ifdef CONFIG_SMP
>-/**
>- * kgdb_roundup_cpus - Get other CPUs into a holding pattern
>- *
>- * On SMP systems, we need to get the attention of the other CPUs
>- * and get them into a known state. This should do what is needed
>- * to get the other CPUs to call kgdb_handle_exception(). Note that
>- * on some arches, the NMI approach is not used for rounding up all
>- * the CPUs. For example, in case of MIPS, smp_call_function() is
>- * used to roundup CPUs.
>- *
>- * On non-SMP systems, this is not called.
>- */
>-void kgdb_roundup_cpus(void)
>-{
>- apic_send_IPI_allbutself(NMI_VECTOR);
>-}
>-#endif
>-
>-/**
>- * kgdb_arch_handle_exception - Handle architecture specific GDB packets.
>- * @e_vector: The error vector of the exception that happened.
>- * @signo: The signal number of the exception that happened.
>- * @err_code: The error code of the exception that happened.
>- * @remcomInBuffer: The buffer of the packet we have read.
>- * @remcomOutBuffer: The buffer of %BUFMAX bytes to write a packet into.
>- * @linux_regs: The &struct pt_regs of the current process.
>- *
>- * This function MUST handle the 'c' and 's' command packets,
>- * as well packets to set / remove a hardware breakpoint, if used.
>- * If there are additional packets which the hardware needs to handle,
>- * they are handled here. The code should return -1 if it wants to
>- * process more packets, and a %0 or %1 if it wants to exit from the
>- * kgdb callback.
>- */
>-int kgdb_arch_handle_exception(int e_vector, int signo, int err_code,
>- char *remcomInBuffer, char *remcomOutBuffer,
>- struct pt_regs *linux_regs)
>-{
>- unsigned long addr;
>- char *ptr;
>-
>- switch (remcomInBuffer[0]) {
>- case 'c':
>- case 's':
>- /* try to read optional parameter, pc unchanged if no parm */
>- ptr = &remcomInBuffer[1];
>- if (kgdb_hex2long(&ptr, &addr))
>- linux_regs->ip = addr;
>- fallthrough;
>- case 'D':
>- case 'k':
>- /* clear the trace bit */
>- linux_regs->flags &= ~X86_EFLAGS_TF;
>- atomic_set(&kgdb_cpu_doing_single_step, -1);
>-
>- /* set the trace bit if we're stepping */
>- if (remcomInBuffer[0] == 's') {
>- linux_regs->flags |= X86_EFLAGS_TF;
>- atomic_set(&kgdb_cpu_doing_single_step,
>- raw_smp_processor_id());
>- }
>-
>- return 0;
>- }
>-
>- /* this means that we do not want to exit from the handler: */
>- return -1;
>-}
>-
>-static inline int
>-single_step_cont(struct pt_regs *regs, struct die_args *args)
>-{
>- /*
>- * Single step exception from kernel space to user space so
>- * eat the exception and continue the process:
>- */
>- printk(KERN_ERR "KGDB: trap/step from kernel to user space, "
>- "resuming...\n");
>- kgdb_arch_handle_exception(args->trapnr, args->signr,
>- args->err, "c", "", regs);
>- /*
>- * Reset the BS bit in dr6 (pointed by args->err) to
>- * denote completion of processing
>- */
>- (*(unsigned long *)ERR_PTR(args->err)) &= ~DR_STEP;
>-
>- return NOTIFY_STOP;
>-}
>-
>-static DECLARE_BITMAP(was_in_debug_nmi, NR_CPUS);
>-
>-static int kgdb_nmi_handler(unsigned int cmd, struct pt_regs *regs)
>-{
>- int cpu;
>-
>- switch (cmd) {
>- case NMI_LOCAL:
>- if (atomic_read(&kgdb_active) != -1) {
>- /* KGDB CPU roundup */
>- cpu = raw_smp_processor_id();
>- kgdb_nmicallback(cpu, regs);
>- set_bit(cpu, was_in_debug_nmi);
>- touch_nmi_watchdog();
>-
>- return NMI_HANDLED;
>- }
>- break;
>-
>- case NMI_UNKNOWN:
>- cpu = raw_smp_processor_id();
>-
>- if (__test_and_clear_bit(cpu, was_in_debug_nmi))
>- return NMI_HANDLED;
>-
>- break;
>- default:
>- /* do nothing */
>- break;
>- }
>- return NMI_DONE;
>-}
>-
>-static int __kgdb_notify(struct die_args *args, unsigned long cmd)
>-{
>- struct pt_regs *regs = args->regs;
>-
>- switch (cmd) {
>- case DIE_DEBUG:
>- if (atomic_read(&kgdb_cpu_doing_single_step) != -1) {
>- if (user_mode(regs))
>- return single_step_cont(regs, args);
>- break;
>- } else if (test_thread_flag(TIF_SINGLESTEP))
>- /* This means a user thread is single stepping
>- * a system call which should be ignored
>- */
>- return NOTIFY_DONE;
>- fallthrough;
>- default:
>- if (user_mode(regs))
>- return NOTIFY_DONE;
>- }
>-
>- if (kgdb_handle_exception(args->trapnr, args->signr, cmd, regs))
>- return NOTIFY_DONE;
>-
>- /* Must touch watchdog before return to normal operation */
>- touch_nmi_watchdog();
>- return NOTIFY_STOP;
>-}
>-
>-int kgdb_ll_trap(int cmd, const char *str,
>- struct pt_regs *regs, long err, int trap, int sig)
>-{
>- struct die_args args = {
>- .regs = regs,
>- .str = str,
>- .err = err,
>- .trapnr = trap,
>- .signr = sig,
>-
>- };
>-
>- if (!kgdb_io_module_registered)
>- return NOTIFY_DONE;
>-
>- return __kgdb_notify(&args, cmd);
>-}
>-
>-static int
>-kgdb_notify(struct notifier_block *self, unsigned long cmd, void *ptr)
>-{
>- unsigned long flags;
>- int ret;
>-
>- local_irq_save(flags);
>- ret = __kgdb_notify(ptr, cmd);
>- local_irq_restore(flags);
>-
>- return ret;
>-}
>-
>-static struct notifier_block kgdb_notifier = {
>- .notifier_call = kgdb_notify,
>-};
>-
>-/**
>- * kgdb_arch_init - Perform any architecture specific initialization.
>- *
>- * This function will handle the initialization of any architecture
>- * specific callbacks.
>- */
>-int kgdb_arch_init(void)
>-{
>- int retval;
>-
>- retval = register_die_notifier(&kgdb_notifier);
>- if (retval)
>- goto out;
>-
>- retval = register_nmi_handler(NMI_LOCAL, kgdb_nmi_handler,
>- 0, "kgdb");
>- if (retval)
>- goto out1;
>-
>- retval = register_nmi_handler(NMI_UNKNOWN, kgdb_nmi_handler,
>- 0, "kgdb");
>-
>- if (retval)
>- goto out2;
>-
>- return retval;
>-
>-out2:
>- unregister_nmi_handler(NMI_LOCAL, "kgdb");
>-out1:
>- unregister_die_notifier(&kgdb_notifier);
>-out:
>- return retval;
>-}
>-
>-static void kgdb_hw_overflow_handler(struct perf_event *event,
>- struct perf_sample_data *data, struct pt_regs *regs)
>-{
>- struct task_struct *tsk = current;
>- int i;
>-
>- for (i = 0; i < 4; i++) {
>- if (breakinfo[i].enabled)
>- tsk->thread.virtual_dr6 |= (DR_TRAP0 << i);
>- }
>-}
>-
>-void kgdb_arch_late(void)
>-{
>- int i, cpu;
>- struct perf_event_attr attr;
>- struct perf_event **pevent;
>-
>- /*
>- * Pre-allocate the hw breakpoint instructions in the non-atomic
>- * portion of kgdb because this operation requires mutexs to
>- * complete.
>- */
>- hw_breakpoint_init(&attr);
>- attr.bp_addr = (unsigned long)kgdb_arch_init;
>- attr.bp_len = HW_BREAKPOINT_LEN_1;
>- attr.bp_type = HW_BREAKPOINT_W;
>- attr.disabled = 1;
>- for (i = 0; i < HBP_NUM; i++) {
>- if (breakinfo[i].pev)
>- continue;
>- breakinfo[i].pev = register_wide_hw_breakpoint(&attr, NULL, NULL);
>- if (IS_ERR_PCPU(breakinfo[i].pev)) {
>- printk(KERN_ERR "kgdb: Could not allocate hw"
>- "breakpoints\nDisabling the kernel debugger\n");
>- breakinfo[i].pev = NULL;
>- kgdb_arch_exit();
>- return;
>- }
>- for_each_online_cpu(cpu) {
>- pevent = per_cpu_ptr(breakinfo[i].pev, cpu);
>- pevent[0]->hw.sample_period = 1;
>- pevent[0]->overflow_handler = kgdb_hw_overflow_handler;
>- if (pevent[0]->destroy != NULL) {
>- pevent[0]->destroy = NULL;
>- release_bp_slot(*pevent);
>- }
>- }
>- }
>-}
>-
>-/**
>- * kgdb_arch_exit - Perform any architecture specific uninitalization.
>- *
>- * This function will handle the uninitalization of any architecture
>- * specific callbacks, for dynamic registration and unregistration.
>- */
>-void kgdb_arch_exit(void)
>-{
>- int i;
>- for (i = 0; i < 4; i++) {
>- if (breakinfo[i].pev) {
>- unregister_wide_hw_breakpoint(breakinfo[i].pev);
>- breakinfo[i].pev = NULL;
>- }
>- }
>- unregister_nmi_handler(NMI_UNKNOWN, "kgdb");
>- unregister_nmi_handler(NMI_LOCAL, "kgdb");
>- unregister_die_notifier(&kgdb_notifier);
>-}
>-
>-/**
>- * kgdb_skipexception - Bail out of KGDB when we've been triggered.
>- * @exception: Exception vector number
>- * @regs: Current &struct pt_regs.
>- *
>- * On some architectures we need to skip a breakpoint exception when
>- * it occurs after a breakpoint has been removed.
>- *
>- * Skip an int3 exception when it occurs after a breakpoint has been
>- * removed. Backtrack eip by 1 since the int3 would have caused it to
>- * increment by 1.
>- */
>-int kgdb_skipexception(int exception, struct pt_regs *regs)
>-{
>- if (exception == 3 && kgdb_isremovedbreak(regs->ip - 1)) {
>- regs->ip -= 1;
>- return 1;
>- }
>- return 0;
>-}
>-
>-unsigned long kgdb_arch_pc(int exception, struct pt_regs *regs)
>-{
>- if (exception == 3)
>- return instruction_pointer(regs) - 1;
>- return instruction_pointer(regs);
>-}
>-
>-void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long ip)
>-{
>- regs->ip = ip;
>-}
>-
>-int kgdb_arch_set_breakpoint(struct kgdb_bkpt *bpt)
>-{
>- int err;
>-
>- bpt->type = BP_BREAKPOINT;
>- err = copy_from_kernel_nofault(bpt->saved_instr, (char *)bpt->bpt_addr,
>- BREAK_INSTR_SIZE);
>- if (err)
>- return err;
>- err = copy_to_kernel_nofault((char *)bpt->bpt_addr,
>- arch_kgdb_ops.gdb_bpt_instr, BREAK_INSTR_SIZE);
>- if (!err)
>- return err;
>- /*
>- * It is safe to call text_poke_kgdb() because normal kernel execution
>- * is stopped on all cores, so long as the text_mutex is not locked.
>- */
>- if (mutex_is_locked(&text_mutex))
>- return -EBUSY;
>- text_poke_kgdb((void *)bpt->bpt_addr, arch_kgdb_ops.gdb_bpt_instr,
>- BREAK_INSTR_SIZE);
>- bpt->type = BP_POKE_BREAKPOINT;
>-
>- return 0;
>-}
>-
>-int kgdb_arch_remove_breakpoint(struct kgdb_bkpt *bpt)
>-{
>- if (bpt->type != BP_POKE_BREAKPOINT)
>- goto knl_write;
>- /*
>- * It is safe to call text_poke_kgdb() because normal kernel execution
>- * is stopped on all cores, so long as the text_mutex is not locked.
>- */
>- if (mutex_is_locked(&text_mutex))
>- goto knl_write;
>- text_poke_kgdb((void *)bpt->bpt_addr, bpt->saved_instr,
>- BREAK_INSTR_SIZE);
>- return 0;
>-
>-knl_write:
>- return copy_to_kernel_nofault((char *)bpt->bpt_addr,
>- (char *)bpt->saved_instr, BREAK_INSTR_SIZE);
>-}
>-
>-const struct kgdb_arch arch_kgdb_ops = {
>- /* Breakpoint instruction: */
>- .gdb_bpt_instr = { 0xcc },
>- .flags = KGDB_HW_BREAKPOINT,
>- .set_hw_breakpoint = kgdb_set_hw_break,
>- .remove_hw_breakpoint = kgdb_remove_hw_break,
>- .disable_hw_break = kgdb_disable_hw_debug,
>- .remove_all_hw_break = kgdb_remove_all_hw_break,
>- .correct_hw_break = kgdb_correct_hw_break,
>-};
>diff --git a/arch/x86/kernel/kprobes/core.c b/arch/x86/kernel/kprobes/core.c
>index 133ff20caccd..65707e4cffae 100644
>--- a/arch/x86/kernel/kprobes/core.c
>+++ b/arch/x86/kernel/kprobes/core.c
>@@ -37,7 +37,6 @@
> #include <linux/extable.h>
> #include <linux/kdebug.h>
> #include <linux/kallsyms.h>
>-#include <linux/kgdb.h>
> #include <linux/ftrace.h>
> #include <linux/kasan.h>
> #include <linux/objtool.h>
>@@ -311,15 +310,6 @@ static bool can_probe(unsigned long paddr)
> if (insn_decode_kernel(&insn, (void *)__addr) < 0)
> return false;
>
>-#ifdef CONFIG_KGDB
>- /*
>- * If there is a dynamically installed kgdb sw breakpoint,
>- * this function should not be probed.
>- */
>- if (insn.opcode.bytes[0] == INT3_INSN_OPCODE &&
>- kgdb_has_hit_break(addr))
>- return false;
>-#endif
> addr += insn.length;
> }
>
>diff --git a/arch/x86/kernel/kprobes/opt.c b/arch/x86/kernel/kprobes/opt.c
>index 3f8fea52619f..821a416faf67 100644
>--- a/arch/x86/kernel/kprobes/opt.c
>+++ b/arch/x86/kernel/kprobes/opt.c
>@@ -15,7 +15,6 @@
> #include <linux/extable.h>
> #include <linux/kdebug.h>
> #include <linux
Is that really true? What if the debugging depends on specific hardware?