From 97a4c627cba98a0fb496621b7b805cfb7d5ffe55 Mon Sep 17 00:00:00 2001 From: FalseHonesty Date: Thu, 15 Apr 2021 12:29:00 -0400 Subject: [PATCH] Kernel: Add debug register handling This patch adds functions to read/write from the debug registers, and implements storing/loading them across context switches. --- Kernel/Arch/i386/CPU.cpp | 81 ++++++++++++++++++++++++++++++++++------ Kernel/Arch/x86/CPU.h | 23 ++++++++++++ Kernel/Thread.h | 4 ++ 3 files changed, 97 insertions(+), 11 deletions(-) diff --git a/Kernel/Arch/i386/CPU.cpp b/Kernel/Arch/i386/CPU.cpp index 93c22c1988..f47f0da7f2 100644 --- a/Kernel/Arch/i386/CPU.cpp +++ b/Kernel/Arch/i386/CPU.cpp @@ -855,19 +855,70 @@ FlatPtr read_cr4() return cr4; } -FlatPtr read_dr6() +void read_debug_registers_into(DebugRegisterState& state) { - FlatPtr dr6; -#if ARCH(I386) - asm("mov %%dr6, %%eax" - : "=a"(dr6)); -#else - asm("mov %%dr6, %%rax" - : "=a"(dr6)); -#endif - return dr6; + state.dr0 = read_dr0(); + state.dr1 = read_dr1(); + state.dr2 = read_dr2(); + state.dr3 = read_dr3(); + state.dr6 = read_dr6(); + state.dr7 = read_dr7(); } +void write_debug_registers_from(const DebugRegisterState& state) +{ + write_dr0(state.dr0); + write_dr1(state.dr1); + write_dr2(state.dr2); + write_dr3(state.dr3); + write_dr6(state.dr6); + write_dr7(state.dr7); +} + +void clear_debug_registers() +{ + write_dr0(0); + write_dr1(0); + write_dr2(0); + write_dr3(0); + write_dr7(1 << 10); // Bit 10 is reserved and must be set to 1. +} + +#if ARCH(I386) +# define DEFINE_DEBUG_REGISTER(index) \ + FlatPtr read_dr##index() \ + { \ + FlatPtr value; \ + asm("mov %%dr" #index ", %%eax" \ + : "=a"(value)); \ + return value; \ + } \ + void write_dr##index(FlatPtr value) \ + { \ + asm volatile("mov %%eax, %%dr" #index ::"a"(value)); \ + } +#else +# define DEFINE_DEBUG_REGISTER(index) \ + FlatPtr read_dr##index() \ + { \ + FlatPtr value; \ + asm("mov %%dr" #index ", %%rax" \ + : "=a"(value)); \ + return value; \ + } \ + void write_dr##index(FlatPtr value) \ + { \ + asm volatile("mov %%rax, %%dr" #index ::"a"(value)); \ + } +#endif + +DEFINE_DEBUG_REGISTER(0); +DEFINE_DEBUG_REGISTER(1); +DEFINE_DEBUG_REGISTER(2); +DEFINE_DEBUG_REGISTER(3); +DEFINE_DEBUG_REGISTER(6); +DEFINE_DEBUG_REGISTER(7); + #define XCR_XFEATURE_ENABLED_MASK 0 UNMAP_AFTER_INIT u64 read_xcr0() @@ -1391,6 +1442,15 @@ extern "C" void enter_thread_context(Thread* from_thread, Thread* to_thread) set_fs(to_tss.fs); set_gs(to_tss.gs); + if (from_thread->process().is_traced()) + read_debug_registers_into(from_thread->debug_register_state()); + + if (to_thread->process().is_traced()) { + write_debug_registers_from(to_thread->debug_register_state()); + } else { + clear_debug_registers(); + } + auto& processor = Processor::current(); auto& tls_descriptor = processor.get_gdt_entry(GDT_SELECTOR_TLS); tls_descriptor.set_base(to_thread->thread_specific_data()); @@ -1404,7 +1464,6 @@ extern "C" void enter_thread_context(Thread* from_thread, Thread* to_thread) asm volatile("fxrstor %0" ::"m"(to_thread->fpu_state())); - // TODO: debug registers // TODO: ioperm? } diff --git a/Kernel/Arch/x86/CPU.h b/Kernel/Arch/x86/CPU.h index a747b8f307..d4dd8ace1a 100644 --- a/Kernel/Arch/x86/CPU.h +++ b/Kernel/Arch/x86/CPU.h @@ -430,6 +430,15 @@ struct [[gnu::packed]] RegisterState { FlatPtr userspace_ss; }; +struct [[gnu::packed]] DebugRegisterState { + FlatPtr dr0; + FlatPtr dr1; + FlatPtr dr2; + FlatPtr dr3; + FlatPtr dr6; + FlatPtr dr7; +}; + #if ARCH(I386) # define REGISTER_STATE_SIZE (19 * 4) #else @@ -476,7 +485,21 @@ void write_cr3(FlatPtr); void write_cr4(FlatPtr); void write_xcr0(u64); +void read_debug_registers_into(DebugRegisterState&); +void write_debug_registers_from(const DebugRegisterState&); +void clear_debug_registers(); +FlatPtr read_dr0(); +void write_dr0(FlatPtr); +FlatPtr read_dr1(); +void write_dr1(FlatPtr); +FlatPtr read_dr2(); +void write_dr2(FlatPtr); +FlatPtr read_dr3(); +void write_dr3(FlatPtr); FlatPtr read_dr6(); +void write_dr6(FlatPtr); +FlatPtr read_dr7(); +void write_dr7(FlatPtr); static inline bool is_kernel_mode() { diff --git a/Kernel/Thread.h b/Kernel/Thread.h index 9bc1f33662..6a7078c5a6 100644 --- a/Kernel/Thread.h +++ b/Kernel/Thread.h @@ -763,6 +763,9 @@ public: RegisterState& get_register_dump_from_stack(); const RegisterState& get_register_dump_from_stack() const { return const_cast(this)->get_register_dump_from_stack(); } + DebugRegisterState& debug_register_state() { return m_debug_register_state; } + const DebugRegisterState& debug_register_state() const { return m_debug_register_state; } + TSS& tss() { return m_tss; } const TSS& tss() const { return m_tss; } State state() const { return m_state; } @@ -1200,6 +1203,7 @@ private: NonnullRefPtr m_process; ThreadID m_tid { -1 }; TSS m_tss {}; + DebugRegisterState m_debug_register_state {}; TrapFrame* m_current_trap { nullptr }; u32 m_saved_critical { 1 }; IntrusiveListNode m_ready_queue_node;