Skip to content

Commit

Permalink
Kernel+Userland: Add x86_64 registers to RegisterState/PtraceRegisters
Browse files Browse the repository at this point in the history
  • Loading branch information
gunnarbeutner authored and awesomekling committed Jun 27, 2021
1 parent 10ca7f1 commit 233ef26
Show file tree
Hide file tree
Showing 13 changed files with 265 additions and 23 deletions.
73 changes: 72 additions & 1 deletion Kernel/Arch/x86/RegisterState.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
namespace Kernel {

struct [[gnu::packed]] RegisterState {
#if ARCH(I386)
FlatPtr ss;
FlatPtr gs;
FlatPtr fs;
Expand All @@ -28,27 +29,55 @@ struct [[gnu::packed]] RegisterState {
FlatPtr edx;
FlatPtr ecx;
FlatPtr eax;
#else
FlatPtr rdi;
FlatPtr rsi;
FlatPtr rbp;
FlatPtr rsp;
FlatPtr rbx;
FlatPtr rdx;
FlatPtr rcx;
FlatPtr rax;
FlatPtr r8;
FlatPtr r9;
FlatPtr r10;
FlatPtr r11;
FlatPtr r12;
FlatPtr r13;
FlatPtr r14;
FlatPtr r15;
#endif
u16 exception_code;
u16 isr_number;
#if ARCH(X86_64)
u32 padding;
#endif
#if ARCH(I386)
FlatPtr eip;
#else
FlatPtr rip;
#endif
FlatPtr cs;
#if ARCH(I386)
FlatPtr eflags;
FlatPtr userspace_esp;
FlatPtr userspace_ss;
#else
FlatPtr rflags;
FlatPtr userspace_rsp;
#endif
};

#if ARCH(I386)
# define REGISTER_STATE_SIZE (19 * 4)
#else
# define REGISTER_STATE_SIZE (19 * 8)
# define REGISTER_STATE_SIZE (21 * 8)
#endif
static_assert(REGISTER_STATE_SIZE == sizeof(RegisterState));

inline void copy_kernel_registers_into_ptrace_registers(PtraceRegisters& ptrace_regs, const RegisterState& kernel_regs)
{
#if ARCH(I386)
ptrace_regs.eax = kernel_regs.eax,
ptrace_regs.ecx = kernel_regs.ecx,
ptrace_regs.edx = kernel_regs.edx,
Expand All @@ -59,6 +88,26 @@ inline void copy_kernel_registers_into_ptrace_registers(PtraceRegisters& ptrace_
ptrace_regs.edi = kernel_regs.edi,
ptrace_regs.eip = kernel_regs.eip,
ptrace_regs.eflags = kernel_regs.eflags,
#else
ptrace_regs.rax = kernel_regs.rax,
ptrace_regs.rcx = kernel_regs.rcx,
ptrace_regs.rdx = kernel_regs.rdx,
ptrace_regs.rbx = kernel_regs.rbx,
ptrace_regs.rsp = kernel_regs.userspace_rsp,
ptrace_regs.rbp = kernel_regs.rbp,
ptrace_regs.rsi = kernel_regs.rsi,
ptrace_regs.rdi = kernel_regs.rdi,
ptrace_regs.rip = kernel_regs.rip,
ptrace_regs.r8 = kernel_regs.r8;
ptrace_regs.r9 = kernel_regs.r9;
ptrace_regs.r10 = kernel_regs.r10;
ptrace_regs.r11 = kernel_regs.r11;
ptrace_regs.r12 = kernel_regs.r12;
ptrace_regs.r13 = kernel_regs.r13;
ptrace_regs.r14 = kernel_regs.r14;
ptrace_regs.r15 = kernel_regs.r15;
ptrace_regs.rflags = kernel_regs.rflags,
#endif
ptrace_regs.cs = 0;
ptrace_regs.ss = 0;
ptrace_regs.ds = 0;
Expand All @@ -69,6 +118,7 @@ inline void copy_kernel_registers_into_ptrace_registers(PtraceRegisters& ptrace_

inline void copy_ptrace_registers_into_kernel_registers(RegisterState& kernel_regs, const PtraceRegisters& ptrace_regs)
{
#if ARCH(I386)
kernel_regs.eax = ptrace_regs.eax;
kernel_regs.ecx = ptrace_regs.ecx;
kernel_regs.edx = ptrace_regs.edx;
Expand All @@ -79,6 +129,27 @@ inline void copy_ptrace_registers_into_kernel_registers(RegisterState& kernel_re
kernel_regs.edi = ptrace_regs.edi;
kernel_regs.eip = ptrace_regs.eip;
kernel_regs.eflags = (kernel_regs.eflags & ~safe_eflags_mask) | (ptrace_regs.eflags & safe_eflags_mask);
#else
kernel_regs.rax = ptrace_regs.rax;
kernel_regs.rcx = ptrace_regs.rcx;
kernel_regs.rdx = ptrace_regs.rdx;
kernel_regs.rbx = ptrace_regs.rbx;
kernel_regs.rsp = ptrace_regs.rsp;
kernel_regs.rbp = ptrace_regs.rbp;
kernel_regs.rsi = ptrace_regs.rsi;
kernel_regs.rdi = ptrace_regs.rdi;
kernel_regs.rip = ptrace_regs.rip;
kernel_regs.r8 = ptrace_regs.r8;
kernel_regs.r9 = ptrace_regs.r9;
kernel_regs.r10 = ptrace_regs.r10;
kernel_regs.r11 = ptrace_regs.r11;
kernel_regs.r12 = ptrace_regs.r12;
kernel_regs.r13 = ptrace_regs.r13;
kernel_regs.r14 = ptrace_regs.r14;
kernel_regs.r15 = ptrace_regs.r15;
// FIXME: do we need a separate safe_rflags_mask here?
kernel_regs.rflags = (kernel_regs.rflags & ~safe_eflags_mask) | (ptrace_regs.rflags & safe_eflags_mask);
#endif
}

struct [[gnu::packed]] DebugRegisterState {
Expand Down
38 changes: 35 additions & 3 deletions Kernel/Arch/x86/common/Interrupts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ asm( \

static void dump(const RegisterState& regs)
{
#if ARCH(I386)
u16 ss;
u32 esp;

Expand All @@ -127,14 +128,33 @@ static void dump(const RegisterState& regs)
ss = regs.userspace_ss;
esp = regs.userspace_esp;
}
#else
u64 rsp;

if (!(regs.cs & 3))
rsp = regs.rsp;
else
rsp = regs.userspace_rsp;
#endif

dbgln("Exception code: {:04x} (isr: {:04x})", regs.exception_code, regs.isr_number);
#if ARCH(I386)
dbgln(" pc={:04x}:{:08x} eflags={:08x}", (u16)regs.cs, regs.eip, regs.eflags);
dbgln(" stack={:04x}:{:08x}", ss, esp);
dbgln(" ds={:04x} es={:04x} fs={:04x} gs={:04x}", (u16)regs.ds, (u16)regs.es, (u16)regs.fs, (u16)regs.gs);
dbgln(" eax={:08x} ebx={:08x} ecx={:08x} edx={:08x}", regs.eax, regs.ebx, regs.ecx, regs.edx);
dbgln(" ebp={:08x} esp={:08x} esi={:08x} edi={:08x}", regs.ebp, regs.esp, regs.esi, regs.edi);
dbgln(" cr0={:08x} cr2={:08x} cr3={:08x} cr4={:08x}", read_cr0(), read_cr2(), read_cr3(), read_cr4());
#else
dbgln(" pc={:04x}:{:16x} rflags={:16x}", (u16)regs.cs, regs.rip, regs.rflags);
dbgln(" stack={:16x}", rsp);
// FIXME: Add fs_base and gs_base here
dbgln(" rax={:16x} rbx={:16x} rcx={:16x} rdx={:16x}", regs.rax, regs.rbx, regs.rcx, regs.rdx);
dbgln(" rbp={:16x} rsp={:16x} rsi={:16x} rdi={:16x}", regs.rbp, regs.rsp, regs.rsi, regs.rdi);
dbgln(" r8={:16x} r9={:16x} r10={:16x} r11={:16x}", regs.r8, regs.r9, regs.r10, regs.r11);
dbgln(" r12={:16x} r13={:16x} r14={:16x} r15={:16x}", regs.r12, regs.r13, regs.r14, regs.r15);
dbgln(" cr0={:16x} cr2={:16x} cr3={:16x} cr4={:16x}", read_cr0(), read_cr2(), read_cr3(), read_cr4());
#endif
}

void handle_crash(RegisterState& regs, const char* description, int signal, bool out_of_memory)
Expand All @@ -155,7 +175,13 @@ void handle_crash(RegisterState& regs, const char* description, int signal, bool
PANIC("Crash in ring 0");
}

process->crash(signal, regs.eip, out_of_memory);
FlatPtr ip;
#if ARCH(I386)
ip = regs.eip;
#else
ip = regs.rip;
#endif
process->crash(signal, ip, out_of_memory);
}

EH_ENTRY_NO_CODE(6, illegal_instruction);
Expand Down Expand Up @@ -237,8 +263,14 @@ void page_fault_handler(TrapFrame* trap)
current_thread->set_handling_page_fault(false);
};

if (!faulted_in_kernel && !MM.validate_user_stack(current_thread->process(), VirtualAddress(regs.userspace_esp))) {
dbgln("Invalid stack pointer: {}", VirtualAddress(regs.userspace_esp));
VirtualAddress userspace_sp;
#if ARCH(I386)
userspace_sp = VirtualAddress { regs.userspace_esp };
#else
userspace_sp = VirtualAddress { regs.userspace_rsp };
#endif
if (!faulted_in_kernel && !MM.validate_user_stack(current_thread->process(), userspace_sp)) {
dbgln("Invalid stack pointer: {}", userspace_sp);
handle_crash(regs, "Bad stack on page fault", SIGSTKFLT);
}

Expand Down
12 changes: 12 additions & 0 deletions Kernel/PerformanceManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,15 @@ class PerformanceManager {
if (current_thread.is_profiling_suppressed())
return;
if (auto* event_buffer = current_thread.process().current_perf_events_buffer()) {
#if ARCH(I386)
[[maybe_unused]] auto rc = event_buffer->append_with_eip_and_ebp(
current_thread.pid(), current_thread.tid(),
regs.eip, regs.ebp, PERF_EVENT_SAMPLE, lost_time, 0, 0, nullptr);
#else
[[maybe_unused]] auto rc = event_buffer->append_with_eip_and_ebp(
current_thread.pid(), current_thread.tid(),
regs.rip, regs.rbp, PERF_EVENT_SAMPLE, lost_time, 0, 0, nullptr);
#endif
}
}

Expand Down Expand Up @@ -113,9 +119,15 @@ class PerformanceManager {
if (thread.is_profiling_suppressed())
return;
if (auto* event_buffer = thread.process().current_perf_events_buffer()) {
#if ARCH(I386)
[[maybe_unused]] auto rc = event_buffer->append_with_eip_and_ebp(
thread.pid(), thread.tid(),
regs.eip, regs.ebp, PERF_EVENT_PAGE_FAULT, 0, 0, 0, nullptr);
#else
[[maybe_unused]] auto rc = event_buffer->append_with_eip_and_ebp(
thread.pid(), thread.tid(),
regs.rip, regs.rbp, PERF_EVENT_PAGE_FAULT, 0, 0, 0, nullptr);
#endif
}
}

Expand Down
3 changes: 2 additions & 1 deletion Kernel/Scheduler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -594,10 +594,11 @@ void dump_thread_list()
#if ARCH(I386)
if (!thread.current_trap())
return thread.tss().eip;
return thread.get_register_dump_from_stack().eip;
#else
PANIC("get_eip() not implemented");
return thread.get_register_dump_from_stack().rip;
#endif
return thread.get_register_dump_from_stack().eip;
};

Thread::for_each([&](Thread& thread) {
Expand Down
56 changes: 48 additions & 8 deletions Kernel/Syscall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,11 @@ KResultOr<FlatPtr> handle(RegisterState& regs, FlatPtr function, FlatPtr arg1, F
// These syscalls need special handling since they never return to the caller.

if (auto* tracer = process.tracer(); tracer && tracer->is_tracing_syscalls()) {
#if ARCH(I386)
regs.eax = 0;
#else
regs.rax = 0;
#endif
tracer->set_trace_syscalls(false);
process.tracer_trap(*current_thread, regs); // this triggers SIGTRAP and stops the thread!
}
Expand Down Expand Up @@ -165,26 +169,46 @@ NEVER_INLINE void syscall_handler(TrapFrame* trap)

static constexpr FlatPtr iopl_mask = 3u << 12;

if ((regs.eflags & (iopl_mask)) != 0) {
FlatPtr flags;
#if ARCH(I386)
flags = regs.eflags;
#else
flags = regs.rflags;
#endif

if ((flags & (iopl_mask)) != 0) {
PANIC("Syscall from process with IOPL != 0");
}

// NOTE: We take the big process lock before inspecting memory regions.
process.big_lock().lock();

if (!MM.validate_user_stack(process, VirtualAddress(regs.userspace_esp))) {
dbgln("Invalid stack pointer: {:p}", regs.userspace_esp);
VirtualAddress userspace_sp;
#if ARCH(I386)
userspace_sp = VirtualAddress { regs.userspace_esp };
#else
userspace_sp = VirtualAddress { regs.userspace_rsp };
#endif
if (!MM.validate_user_stack(process, userspace_sp)) {
dbgln("Invalid stack pointer: {:p}", userspace_sp);
handle_crash(regs, "Bad stack on syscall entry", SIGSTKFLT);
}

auto* calling_region = MM.find_user_region_from_vaddr(process.space(), VirtualAddress(regs.eip));
VirtualAddress ip;
#if ARCH(I386)
ip = VirtualAddress { regs.eip };
#else
ip = VirtualAddress { regs.rip };
#endif

auto* calling_region = MM.find_user_region_from_vaddr(process.space(), ip);
if (!calling_region) {
dbgln("Syscall from {:p} which has no associated region", regs.eip);
dbgln("Syscall from {:p} which has no associated region", ip);
handle_crash(regs, "Syscall from unknown region", SIGSEGV);
}

if (calling_region->is_writable()) {
dbgln("Syscall from writable memory at {:p}", regs.eip);
dbgln("Syscall from writable memory at {:p}", ip);
handle_crash(regs, "Syscall from writable memory", SIGSEGV);
}

Expand All @@ -193,16 +217,32 @@ NEVER_INLINE void syscall_handler(TrapFrame* trap)
handle_crash(regs, "Syscall from non-syscall region", SIGSEGV);
}

#if ARCH(I386)
auto function = regs.eax;
auto arg1 = regs.edx;
auto arg2 = regs.ecx;
auto arg3 = regs.ebx;
#else
auto function = regs.rax;
auto arg1 = regs.rdx;
auto arg2 = regs.rcx;
auto arg3 = regs.rbx;
#endif

auto result = Syscall::handle(regs, function, arg1, arg2, arg3);
if (result.is_error())
if (result.is_error()) {
#if ARCH(I386)
regs.eax = result.error();
else
#else
regs.rax = result.error();
#endif
} else {
#if ARCH(I386)
regs.eax = result.value();
#else
regs.rax = result.value();
#endif
}

process.big_lock().unlock();

Expand Down
8 changes: 7 additions & 1 deletion Kernel/Syscalls/get_stack_bounds.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@ namespace Kernel {

KResultOr<int> Process::sys$get_stack_bounds(Userspace<FlatPtr*> user_stack_base, Userspace<size_t*> user_stack_size)
{
FlatPtr stack_pointer = Thread::current()->get_register_dump_from_stack().userspace_esp;
auto& regs = Thread::current()->get_register_dump_from_stack();
FlatPtr stack_pointer;
#if ARCH(I386)
stack_pointer = regs.userspace_esp;
#else
stack_pointer = regs.userspace_rsp;
#endif
auto* stack_region = space().find_region_containing(Range { VirtualAddress(stack_pointer), 1 });

// The syscall handler should have killed us if we had an invalid stack pointer.
Expand Down
10 changes: 8 additions & 2 deletions Kernel/Thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -812,7 +812,8 @@ DispatchSignalResult Thread::dispatch_signal(u8 signal)

dbgln_if(SIGNAL_DEBUG, "Setting up user stack to return to EIP {:p}, ESP {:p}", ret_eip, old_esp);
#elif ARCH(X86_64)
FlatPtr* stack = &state.userspace_esp;
FlatPtr* stack = &state.userspace_rsp;
TODO();
#endif

#if ARCH(I386)
Expand Down Expand Up @@ -855,7 +856,12 @@ DispatchSignalResult Thread::dispatch_signal(u8 signal)
// valid (fork, exec etc) but the tss will, so we use that instead.
auto& regs = get_register_dump_from_stack();
setup_stack(regs);
regs.eip = process.signal_trampoline().get();
auto signal_trampoline_addr = process.signal_trampoline().get();
#if ARCH(I386)
regs.eip = signal_trampoline_addr;
#else
regs.rip = signal_trampoline_addr;
#endif

#if ARCH(I386)
dbgln_if(SIGNAL_DEBUG, "Thread in state '{}' has been primed with signal handler {:04x}:{:08x} to deliver {}", state_string(), m_tss.cs, m_tss.eip, signal);
Expand Down
Loading

0 comments on commit 233ef26

Please sign in to comment.