Maintain fsbase and gsbase as per-thread registers.

Note: This is an incompatible ABI change.
This commit is contained in:
Jonas 'Sortie' Termansen 2013-09-14 18:59:15 +02:00
parent 92d7c1807e
commit 1f72c1637c
16 changed files with 169 additions and 29 deletions

View File

@ -50,7 +50,8 @@ extern "C" __attribute__((noreturn))
void BootstrapKernelThread(void* user, ThreadEntry entry); void BootstrapKernelThread(void* user, ThreadEntry entry);
// These functions create a new kernel process but doesn't start it. // These functions create a new kernel process but doesn't start it.
Thread* CreateKernelThread(Process* process, CPU::InterruptRegisters* regs); Thread* CreateKernelThread(Process* process, CPU::InterruptRegisters* regs,
unsigned long fsbase, unsigned long gsbase);
Thread* CreateKernelThread(Process* process, ThreadEntry entry, void* user, Thread* CreateKernelThread(Process* process, ThreadEntry entry, void* user,
size_t stacksize = 0); size_t stacksize = 0);
Thread* CreateKernelThread(ThreadEntry entry, void* user, size_t stacksize = 0); Thread* CreateKernelThread(ThreadEntry entry, void* user, size_t stacksize = 0);
@ -74,7 +75,8 @@ typedef void (*sighandler_t)(int);
class Thread class Thread
{ {
friend Thread* CreateKernelThread(Process* process, friend Thread* CreateKernelThread(Process* process,
CPU::InterruptRegisters* regs); CPU::InterruptRegisters* regs,
unsigned long fsbase, unsigned long gsbase);
friend void KernelInit(unsigned long magic, multiboot_info_t* bootinfo); friend void KernelInit(unsigned long magic, multiboot_info_t* bootinfo);
friend void Thread__OnSigKill(Thread* thread); friend void Thread__OnSigKill(Thread* thread);
@ -111,6 +113,12 @@ public:
size_t kernelstacksize; size_t kernelstacksize;
bool kernelstackmalloced; bool kernelstackmalloced;
#if defined(__i386__) || defined(__x86_64__)
public:
unsigned long fsbase;
unsigned long gsbase;
#endif
private: private:
CPU::InterruptRegisters registers; CPU::InterruptRegisters registers;
Signal::Queue signalqueue; Signal::Queue signalqueue;

View File

@ -1,6 +1,6 @@
/******************************************************************************* /*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2012. Copyright(C) Jonas 'Sortie' Termansen 2012, 2013.
This file is part of Sortix. This file is part of Sortix.
@ -27,6 +27,8 @@
#include <sys/cdefs.h> #include <sys/cdefs.h>
#include <stdint.h>
__BEGIN_DECLS __BEGIN_DECLS
struct tforkregs_x64 struct tforkregs_x64
@ -49,6 +51,8 @@ struct tforkregs_x64
uint64_t r14; uint64_t r14;
uint64_t r15; uint64_t r15;
uint64_t rflags; uint64_t rflags;
uint64_t fsbase;
uint64_t gsbase;
}; };
__END_DECLS __END_DECLS

View File

@ -1,6 +1,6 @@
/******************************************************************************* /*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2012. Copyright(C) Jonas 'Sortie' Termansen 2012, 2013.
This file is part of Sortix. This file is part of Sortix.
@ -27,6 +27,8 @@
#include <sys/cdefs.h> #include <sys/cdefs.h>
#include <stdint.h>
__BEGIN_DECLS __BEGIN_DECLS
struct tforkregs_x86 struct tforkregs_x86
@ -41,6 +43,8 @@ struct tforkregs_x86
uint32_t esp; uint32_t esp;
uint32_t ebp; uint32_t ebp;
uint32_t eflags; uint32_t eflags;
uint32_t fsbase;
uint32_t gsbase;
}; };
__END_DECLS __END_DECLS

View File

@ -27,6 +27,10 @@
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#if defined(__x86_64__)
#include <msr.h>
#endif
#include <sortix/keycodes.h> #include <sortix/keycodes.h>
#include <sortix/kernel/cpu.h> #include <sortix/kernel/cpu.h>
@ -36,6 +40,10 @@
#include <sortix/kernel/keyboard.h> #include <sortix/kernel/keyboard.h>
#include <sortix/kernel/thread.h> #include <sortix/kernel/thread.h>
#if defined(__i386__)
#include "../x86-family/gdt.h"
#endif
#include "ps2.h" #include "ps2.h"
namespace Sortix { namespace Sortix {
@ -97,7 +105,15 @@ void PS2Keyboard::OnInterrupt(CPU::InterruptRegisters* regs)
uint8_t scancode = PopScancode(); uint8_t scancode = PopScancode();
if ( scancode == KBKEY_F10 ) if ( scancode == KBKEY_F10 )
{ {
CurrentThread()->SaveRegisters(regs); Thread* thread = CurrentThread();
#if defined(__i386__)
thread->fsbase = (unsigned long) GDT::GetFSBase();
thread->gsbase = (unsigned long) GDT::GetGSBase();
#elif defined(__x86_64__)
thread->fsbase = (unsigned long) rdmsr(MSRID_FSBASE);
thread->gsbase = (unsigned long) rdmsr(MSRID_GSBASE);
#endif
thread->SaveRegisters(regs);
Debugger::Run(); Debugger::Run();
} }
PS2KeyboardWork work; PS2KeyboardWork work;

View File

@ -995,7 +995,8 @@ static pid_t sys_tfork(int flags, tforkregs_t* user_regs)
// If the thread could not be created, make the process commit suicide // If the thread could not be created, make the process commit suicide
// in a manner such that we don't wait for its zombie. // in a manner such that we don't wait for its zombie.
Thread* thread = CreateKernelThread(clone, &cpuregs); Thread* thread = CreateKernelThread(clone, &cpuregs, regs.fsbase,
regs.gsbase);
if ( !thread ) if ( !thread )
{ {
clone->AbortConstruction(); clone->AbortConstruction();

View File

@ -25,6 +25,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <assert.h> #include <assert.h>
#include <msr.h>
#include <string.h> #include <string.h>
#include <timespec.h> #include <timespec.h>
@ -130,6 +131,18 @@ static void DoActualSwitch(CPU::InterruptRegisters* regs)
assert(stacklower && stacksize && stackhigher); assert(stacklower && stacksize && stackhigher);
GDT::SetKernelStack(stacklower, stacksize, stackhigher); GDT::SetKernelStack(stacklower, stacksize, stackhigher);
#if defined(__x86_64__)
current->fsbase = (unsigned long) rdmsr(MSRID_FSBASE);
current->gsbase = (unsigned long) rdmsr(MSRID_GSBASE);
wrmsr(MSRID_FSBASE, (uint64_t) next->fsbase);
wrmsr(MSRID_GSBASE, (uint64_t) next->gsbase);
#elif defined(__i386__)
current->fsbase = (unsigned long) GDT::GetFSBase();
current->gsbase = (unsigned long) GDT::GetGSBase();
GDT::SetFSBase((uint32_t) next->fsbase);
GDT::SetGSBase((uint32_t) next->gsbase);
#endif
LogEndSwitch(next, regs); LogEndSwitch(next, regs);
} }

View File

@ -51,6 +51,8 @@ Thread::Thread()
schedulerlistnext = NULL; schedulerlistnext = NULL;
state = NONE; state = NONE;
memset(&registers, 0, sizeof(registers)); memset(&registers, 0, sizeof(registers));
fsbase = 0;
gsbase = 0;
kernelstackpos = 0; kernelstackpos = 0;
kernelstacksize = 0; kernelstacksize = 0;
kernelstackmalloced = false; kernelstackmalloced = false;
@ -96,8 +98,16 @@ extern "C" void BootstrapKernelThread(void* user, ThreadEntry entry)
kthread_exit(); kthread_exit();
} }
Thread* CreateKernelThread(Process* process, CPU::InterruptRegisters* regs) Thread* CreateKernelThread(Process* process, CPU::InterruptRegisters* regs,
unsigned long fsbase, unsigned long gsbase)
{ {
#if defined(__x86_64__)
if ( fsbase >> 48 != 0x0000 && fsbase >> 48 != 0xFFFF )
return errno = EINVAL, (Thread*) NULL;
if ( gsbase >> 48 != 0x0000 && gsbase >> 48 != 0xFFFF )
return errno = EINVAL, (Thread*) NULL;
#endif
assert(process && regs && process->addrspace); assert(process && regs && process->addrspace);
Thread* thread = new Thread; Thread* thread = new Thread;
if ( !thread ) if ( !thread )
@ -105,6 +115,8 @@ Thread* CreateKernelThread(Process* process, CPU::InterruptRegisters* regs)
thread->addrspace = process->addrspace; thread->addrspace = process->addrspace;
thread->SaveRegisters(regs); thread->SaveRegisters(regs);
thread->fsbase = fsbase;
thread->gsbase = gsbase;
kthread_mutex_lock(&process->threadlock); kthread_mutex_lock(&process->threadlock);
@ -134,7 +146,7 @@ Thread* CreateKernelThread(Process* process, ThreadEntry entry, void* user,
CPU::InterruptRegisters regs; CPU::InterruptRegisters regs;
SetupKernelThreadRegs(&regs, entry, user, (addr_t) stack, stacksize); SetupKernelThreadRegs(&regs, entry, user, (addr_t) stack, stacksize);
Thread* thread = CreateKernelThread(process, &regs); Thread* thread = CreateKernelThread(process, &regs, 0, 0);
if ( !thread ) { delete[] stack; return NULL; } if ( !thread ) { delete[] stack; return NULL; }
thread->kernelstackpos = (addr_t) stack; thread->kernelstackpos = (addr_t) stack;
@ -156,7 +168,7 @@ void StartKernelThread(Thread* thread)
Thread* RunKernelThread(Process* process, CPU::InterruptRegisters* regs) Thread* RunKernelThread(Process* process, CPU::InterruptRegisters* regs)
{ {
Thread* thread = CreateKernelThread(process, regs); Thread* thread = CreateKernelThread(process, regs, 0, 0);
if ( !thread ) if ( !thread )
return NULL; return NULL;
StartKernelThread(thread); StartKernelThread(thread);

View File

@ -371,8 +371,6 @@ interrupt_handler_prepare:
movw $0x10, %bp movw $0x10, %bp
movl %ebp, %ds movl %ebp, %ds
movl %ebp, %es movl %ebp, %es
movl %ebp, %fs
movl %ebp, %gs
# Push CR2 in case of page faults # Push CR2 in case of page faults
movq %cr2, %rbp movq %cr2, %rbp
@ -406,8 +404,6 @@ load_interrupted_registers:
popq %rbp popq %rbp
movl %ebp, %ds movl %ebp, %ds
movl %ebp, %es movl %ebp, %es
movl %ebp, %fs
movl %ebp, %gs
popq %rdi popq %rdi
popq %rsi popq %rsi

View File

@ -110,7 +110,13 @@ struct tss_entry
} __attribute__((packed)); } __attribute__((packed));
#endif #endif
#if defined(__i386__)
const size_t GDT_NUM_ENTRIES = 9;
const size_t GDT_FS_ENTRY = 7;
const size_t GDT_GS_ENTRY = 8;
#else
const size_t GDT_NUM_ENTRIES = 7; const size_t GDT_NUM_ENTRIES = 7;
#endif
static struct gdt_entry gdt_entries[GDT_NUM_ENTRIES]; static struct gdt_entry gdt_entries[GDT_NUM_ENTRIES];
static struct tss_entry tss_entry; static struct tss_entry tss_entry;
@ -166,6 +172,11 @@ void Init()
WriteTSS(5, 0x10, 0x0); WriteTSS(5, 0x10, 0x0);
#if defined(__i386__)
SetGate(GDT_FS_ENTRY, 0, 0xFFFFFFFF, 0xF2, gran);
SetGate(GDT_GS_ENTRY, 0, 0xFFFFFFFF, 0xF2, gran);
#endif
// Reload the Global Descriptor Table. // Reload the Global Descriptor Table.
volatile struct gdt_ptr gdt_ptr; volatile struct gdt_ptr gdt_ptr;
gdt_ptr.limit = (sizeof(struct gdt_entry) * GDT_NUM_ENTRIES) - 1; gdt_ptr.limit = (sizeof(struct gdt_entry) * GDT_NUM_ENTRIES) - 1;
@ -175,11 +186,17 @@ void Init()
// Switch the current data segment. // Switch the current data segment.
asm volatile ("mov %0, %%ds\n" asm volatile ("mov %0, %%ds\n"
"mov %0, %%es\n" "mov %0, %%es\n"
"mov %0, %%fs\n"
"mov %0, %%gs\n"
"mov %0, %%ss\n" : : "mov %0, %%ss\n" : :
"r"(KDS)); "r"(KDS));
#if defined(__i386__)
asm volatile ("mov %0, %%fs" : : "r"(GDT_FS_ENTRY << 3 | URPL));
asm volatile ("mov %0, %%gs" : : "r"(GDT_GS_ENTRY << 3 | URPL));
#elif defined(__x86_64__)
asm volatile ("mov %0, %%fs" : : "r"(UDS | URPL));
asm volatile ("mov %0, %%gs" : : "r"(UDS | URPL));
#endif
// Switch the current code segment. // Switch the current code segment.
#if defined(__i386__) #if defined(__i386__)
asm volatile ("push %0\n" asm volatile ("push %0\n"
@ -250,5 +267,41 @@ void SetKernelStack(uintptr_t stacklower, size_t stacksize, uintptr_t stackhighe
#endif #endif
} }
#if defined(__i386__)
uint32_t GetFSBase()
{
struct gdt_entry* entry = gdt_entries + GDT_FS_ENTRY;
return (uint32_t) entry->base_low << 0 |
(uint32_t) entry->base_middle << 16 |
(uint32_t) entry->base_high << 24;
}
uint32_t GetGSBase()
{
struct gdt_entry* entry = gdt_entries + GDT_GS_ENTRY;
return (uint32_t) entry->base_low << 0 |
(uint32_t) entry->base_middle << 16 |
(uint32_t) entry->base_high << 24;
}
void SetFSBase(uint32_t fsbase)
{
struct gdt_entry* entry = gdt_entries + GDT_FS_ENTRY;
entry->base_low = fsbase >> 0 & 0xFFFF;
entry->base_middle = fsbase >> 16 & 0xFF;
entry->base_high = fsbase >> 24 & 0xFF;
asm volatile ("mov %0, %%fs" : : "r"(GDT_FS_ENTRY << 3 | URPL));
}
void SetGSBase(uint32_t gsbase)
{
struct gdt_entry* entry = gdt_entries + GDT_GS_ENTRY;
entry->base_low = gsbase >> 0 & 0xFFFF;
entry->base_middle = gsbase >> 16 & 0xFF;
entry->base_high = gsbase >> 24 & 0xFF;
asm volatile ("mov %0, %%gs" : : "r"(GDT_GS_ENTRY << 3 | URPL));
}
#endif
} // namespace GDT } // namespace GDT
} // namespace Sortix } // namespace Sortix

View File

@ -25,12 +25,20 @@
#ifndef SORTIX_X86_FAMILY_GDT_H #ifndef SORTIX_X86_FAMILY_GDT_H
#define SORTIX_X86_FAMILY_GDT_H #define SORTIX_X86_FAMILY_GDT_H
#include <stdint.h>
namespace Sortix { namespace Sortix {
namespace GDT { namespace GDT {
void Init(); void Init();
void WriteTSS(int32_t num, uint16_t ss0, uintptr_t stack0); void WriteTSS(int32_t num, uint16_t ss0, uintptr_t stack0);
void SetKernelStack(uintptr_t stacklower, size_t stacksize, uintptr_t stackhigher); void SetKernelStack(uintptr_t stacklower, size_t stacksize, uintptr_t stackhigher);
#if defined(__i386__)
uint32_t GetFSBase();
uint32_t GetGSBase();
void SetFSBase(uint32_t fsbase);
void SetGSBase(uint32_t gsbase);
#endif
} // namespace GDT } // namespace GDT
} // namespace Sortix } // namespace Sortix

View File

@ -24,6 +24,7 @@
#include <assert.h> #include <assert.h>
#include <errno.h> #include <errno.h>
#include <msr.h>
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
@ -38,6 +39,7 @@
#include <sortix/kernel/syscall.h> #include <sortix/kernel/syscall.h>
#include <sortix/kernel/thread.h> #include <sortix/kernel/thread.h>
#include "gdt.h"
#include "idt.h" #include "idt.h"
#include "pic.h" #include "pic.h"
@ -272,7 +274,15 @@ void CrashCalltrace(const CPU::InterruptRegisters* regs)
__attribute__((noreturn)) __attribute__((noreturn))
void KernelCrashHandler(CPU::InterruptRegisters* regs) void KernelCrashHandler(CPU::InterruptRegisters* regs)
{ {
CurrentThread()->SaveRegisters(regs); Thread* thread = CurrentThread();
#if defined(__i386__)
thread->fsbase = (unsigned long) GDT::GetFSBase();
thread->gsbase = (unsigned long) GDT::GetGSBase();
#elif defined(__x86_64__)
thread->fsbase = (unsigned long) rdmsr(MSRID_FSBASE);
thread->gsbase = (unsigned long) rdmsr(MSRID_GSBASE);
#endif
thread->SaveRegisters(regs);
// Walk and print the stack frames if this is a debug build. // Walk and print the stack frames if this is a debug build.
if ( CALLTRACE_KERNEL ) if ( CALLTRACE_KERNEL )
@ -290,7 +300,15 @@ void KernelCrashHandler(CPU::InterruptRegisters* regs)
void UserCrashHandler(CPU::InterruptRegisters* regs) void UserCrashHandler(CPU::InterruptRegisters* regs)
{ {
CurrentThread()->SaveRegisters(regs); Thread* thread = CurrentThread();
#if defined(__i386__)
thread->fsbase = (unsigned long) GDT::GetFSBase();
thread->gsbase = (unsigned long) GDT::GetGSBase();
#elif defined(__x86_64__)
thread->fsbase = (unsigned long) rdmsr(MSRID_FSBASE);
thread->gsbase = (unsigned long) rdmsr(MSRID_GSBASE);
#endif
thread->SaveRegisters(regs);
// Execute this crash handler with preemption on. // Execute this crash handler with preemption on.
Interrupt::Enable(); Interrupt::Enable();

View File

@ -368,8 +368,6 @@ fixup_relocate_stack_complete:
movw $0x10, %bp movw $0x10, %bp
movl %ebp, %ds movl %ebp, %ds
movl %ebp, %es movl %ebp, %es
movl %ebp, %fs
movl %ebp, %gs
# Push CR2 in case of page faults # Push CR2 in case of page faults
movl %cr2, %ebp movl %cr2, %ebp
@ -404,8 +402,6 @@ load_interrupted_registers:
popl %ebp popl %ebp
movl %ebp, %ds movl %ebp, %ds
movl %ebp, %es movl %ebp, %es
movl %ebp, %fs
movl %ebp, %gs
popl %edi popl %edi
popl %esi popl %esi

View File

@ -37,8 +37,6 @@ syscall_handler:
movw $0x10, %bp movw $0x10, %bp
movl %ebp, %ds movl %ebp, %ds
movl %ebp, %es movl %ebp, %es
movl %ebp, %fs
movl %ebp, %gs
# Make sure the requested system call is valid. # Make sure the requested system call is valid.
cmp SYSCALL_MAX, %eax cmp SYSCALL_MAX, %eax
@ -66,8 +64,6 @@ valid_syscall:
popl %ebp popl %ebp
movl %ebp, %ds movl %ebp, %ds
movl %ebp, %es movl %ebp, %es
movl %ebp, %fs
movl %ebp, %gs
# Return to user-space, system call result in %eax:%edx, errno in %ecx. # Return to user-space, system call result in %eax:%edx, errno in %ecx.
popl %ebp popl %ebp

View File

@ -34,6 +34,9 @@
__BEGIN_DECLS __BEGIN_DECLS
#define MSRID_FSBASE __UINT32_C(0xC0000100)
#define MSRID_GSBASE __UINT32_C(0xC0000101)
__attribute__((unused)) __attribute__((unused))
static __inline uint64_t rdmsr(uint32_t msrid) static __inline uint64_t rdmsr(uint32_t msrid)
{ {

View File

@ -22,6 +22,9 @@
*******************************************************************************/ *******************************************************************************/
#define MSRID_FSBASE 0xC0000100
#define MSRID_GSBASE 0xC0000101
.section .text .section .text
.globl __call_tfork_with_regs .globl __call_tfork_with_regs
@ -30,10 +33,16 @@ __call_tfork_with_regs:
pushq %rbp pushq %rbp
movq %rsp, %rbp movq %rsp, %rbp
# Save the flags parameter so rdmsr won't trash it and align stack.
pushq %rdi
sub $8, %rsp
# The actual system call expects a struct tforkregs_x64 containing the state # The actual system call expects a struct tforkregs_x64 containing the state
# of each register in the child. Since we create an identical copy, we # of each register in the child. Since we create an identical copy, we
# simply set each member of the structure to our own state. Note that since # simply set each member of the structure to our own state. Note that since
# the stack goes downwards, we create it in the reverse order. # the stack goes downwards, we create it in the reverse order.
pushq $0 # gsbase
pushq $0 # fsbase
pushfq pushfq
pushq %r15 pushq %r15
pushq %r14 pushq %r14
@ -53,8 +62,7 @@ __call_tfork_with_regs:
pushq $0 # rax, result of sfork is 0 for the child. pushq $0 # rax, result of sfork is 0 for the child.
pushq $.Lafter_fork # rip, child will start execution from here. pushq $.Lafter_fork # rip, child will start execution from here.
# Call tfork with a nice pointer to our structure. Note that %rdi contains # Call tfork with a nice pointer to our structure.
# the flag parameter that this function accepted.
movq %rsp, %rsi movq %rsp, %rsi
call tfork call tfork

View File

@ -22,6 +22,9 @@
*******************************************************************************/ *******************************************************************************/
#define MSRID_FSBASE 0xC0000100
#define MSRID_GSBASE 0xC0000101
.section .text .section .text
.globl __call_tfork_with_regs .globl __call_tfork_with_regs
@ -30,12 +33,12 @@ __call_tfork_with_regs:
pushl %ebp pushl %ebp
movl %esp, %ebp movl %esp, %ebp
movl 8(%ebp), %edx # flags parameter, edx need not be preserved.
# The actual system call expects a struct tforkregs_x86 containing the state # The actual system call expects a struct tforkregs_x86 containing the state
# of each register in the child. Since we create an identical copy, we # of each register in the child. Since we create an identical copy, we
# simply set each member of the structure to our own state. Note that since # simply set each member of the structure to our own state. Note that since
# the stack goes downwards, we create it in the reverse order. # the stack goes downwards, we create it in the reverse order.
pushl $0 # gsbase
pushl $0 # fsbase
pushfl pushfl
pushl %ebp pushl %ebp
pushl %esp pushl %esp
@ -49,6 +52,7 @@ __call_tfork_with_regs:
# Call tfork with a nice pointer to our structure. Note that %edi contains # Call tfork with a nice pointer to our structure. Note that %edi contains
# the flag parameter that this function accepted. # the flag parameter that this function accepted.
movl 8(%ebp), %edx # flags parameter, edx need not be preserved.
pushl %esp pushl %esp
pushl %edx pushl %edx
call tfork call tfork