diff options
-rw-r--r-- | i386/i386/i386asm.sym | 2 | ||||
-rw-r--r-- | i386/i386/pcb.c | 39 | ||||
-rw-r--r-- | i386/i386/thread.h | 4 | ||||
-rw-r--r-- | kern/thread.c | 3 | ||||
-rw-r--r-- | x86_64/locore.S | 89 |
5 files changed, 116 insertions, 21 deletions
diff --git a/i386/i386/i386asm.sym b/i386/i386/i386asm.sym index 1b9b40bb..fd0be557 100644 --- a/i386/i386/i386asm.sym +++ b/i386/i386/i386asm.sym @@ -108,6 +108,8 @@ offset i386_saved_state r r12 offset i386_saved_state r r13 offset i386_saved_state r r14 offset i386_saved_state r r15 +offset i386_saved_state r fsbase +offset i386_saved_state r gsbase #endif offset i386_interrupt_state i eip diff --git a/i386/i386/pcb.c b/i386/i386/pcb.c index 61125fe8..8a9e3bf4 100644 --- a/i386/i386/pcb.c +++ b/i386/i386/pcb.c @@ -51,6 +51,7 @@ #include "eflags.h" #include "gdt.h" #include "ldt.h" +#include "msr.h" #include "ktss.h" #include "pcb.h" @@ -372,7 +373,10 @@ thread_t switch_context( * Load the rest of the user state for the new thread */ switch_ktss(new->pcb); - +#if defined(__x86_64__) && !defined(USER32) + wrmsr(MSR_REG_FSBASE, new->pcb->iss.fsbase); + wrmsr(MSR_REG_GSBASE, new->pcb->iss.gsbase); +#endif return Switch_context(old, continuation, new); } @@ -667,7 +671,23 @@ kern_return_t thread_setstatus( return ret; break; } - +#if defined(__x86_64__) && !defined(USER32) + case i386_FSGS_BASE_STATE: + { + struct i386_fsgs_base_state *state; + if (count < i386_FSGS_BASE_STATE_COUNT) + return KERN_INVALID_ARGUMENT; + + state = (struct i386_fsgs_base_state *) tstate; + thread->pcb->iss.fsbase = state->fs_base; + thread->pcb->iss.gsbase = state->gs_base; + if (thread == current_thread()) { + wrmsr(MSR_REG_FSBASE, state->fs_base); + wrmsr(MSR_REG_GSBASE, state->gs_base); + } + break; + } +#endif default: return(KERN_INVALID_ARGUMENT); } @@ -843,7 +863,20 @@ kern_return_t thread_getstatus( *count = i386_DEBUG_STATE_COUNT; break; } - +#if defined(__x86_64__) && !defined(USER32) + case i386_FSGS_BASE_STATE: + { + struct i386_fsgs_base_state *state; + if (*count < i386_FSGS_BASE_STATE_COUNT) + return KERN_INVALID_ARGUMENT; + + state = (struct i386_fsgs_base_state *) tstate; + state->fs_base = thread->pcb->iss.fsbase; + state->fs_base = thread->pcb->iss.gsbase; + *count = i386_FSGS_BASE_STATE_COUNT; + break; + } +#endif default: return(KERN_INVALID_ARGUMENT); } diff --git a/i386/i386/thread.h b/i386/i386/thread.h index 933b43d8..b5fc5ffb 100644 --- a/i386/i386/thread.h +++ b/i386/i386/thread.h @@ -51,6 +51,10 @@ */ struct i386_saved_state { +#ifdef __x86_64__ + unsigned long fsbase; + unsigned long gsbase; +#endif unsigned long gs; unsigned long fs; unsigned long es; diff --git a/kern/thread.c b/kern/thread.c index 20c11024..a324eed7 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -1472,6 +1472,9 @@ kern_return_t thread_set_state( if (flavor == i386_DEBUG_STATE && thread == current_thread()) /* This state can be set directly for the curren thread. */ return thread_setstatus(thread, flavor, new_state, new_state_count); + if (flavor == i386_FSGS_BASE_STATE && thread == current_thread()) + /* This state can be set directly for the curren thread. */ + return thread_setstatus(thread, flavor, new_state, new_state_count); #endif if (thread == THREAD_NULL || thread == current_thread()) diff --git a/x86_64/locore.S b/x86_64/locore.S index 0d7cdd0e..366ef292 100644 --- a/x86_64/locore.S +++ b/x86_64/locore.S @@ -34,6 +34,7 @@ #include <i386/i386/trap.h> #include <i386/i386/seg.h> #include <i386/i386/ldt.h> +#include <i386/i386/msr.h> #include <i386/i386/i386asm.h> #include <i386/i386/cpu_number.h> #include <i386/i386/xen.h> @@ -41,6 +42,46 @@ #define pusha pushq %rax ; pushq %rcx ; pushq %rdx ; pushq %rbx ; subq $8,%rsp ; pushq %rbp ; pushq %rsi ; pushq %rdi ; pushq %r8 ; pushq %r9 ; pushq %r10 ; pushq %r11 ; pushq %r12 ; pushq %r13 ; pushq %r14 ; pushq %r15 #define popa popq %r15 ; popq %r14 ; popq %r13 ; popq %r12 ; popq %r11 ; popq %r10 ; popq %r9 ; popq %r8 ; popq %rdi ; popq %rsi ; popq %rbp ; addq $8,%rsp ; popq %rbx ; popq %rdx ; popq %rcx ; popq %rax +#ifdef USER32 +#define PUSH_FSGS \ + pushq %fs ;\ + pushq %gs ;\ + subq $16,%rsp +#else +#define PUSH_FSGS \ + subq $32,%rsp +#endif + +#ifdef USER32 +#define POP_FSGS \ + popq %gs ;\ + popq %fs ;\ + addq $16,%rsp +#else +#define POP_FSGS \ + addq $32,%rsp +#endif + +#ifdef USER32 +#define PUSH_FSGS_ISR \ + pushq %fs ;\ + pushq %gs +#else +#define PUSH_FSGS_ISR \ + subq $16,%rsp +#endif + +#ifdef USER32 +#define POP_FSGS_ISR \ + popq %gs ;\ + popq %fs +#else +#define POP_FSGS_ISR \ + addq $16,%rsp +#endif + + + /* * Fault recovery. */ @@ -405,6 +446,11 @@ push_fs: pushq %fs /* restore fs, */ push_gs: pushq %gs /* restore gs. */ +#ifdef USER32 +push_gsbase: + pushq $0 + pushq $0 +#endif push_segregs: movq %rax,R_TRAPNO(%rsp) /* set trap number */ movq %rdx,R_ERR(%rsp) /* set error code */ @@ -470,8 +516,7 @@ trap_push_segs: pushq %rax movq %es,%rax /* and the segment registers */ pushq %rax - pushq %fs - pushq %gs + PUSH_FSGS /* Note that we have to load the segment registers even if this is a trap from the kernel, @@ -480,9 +525,10 @@ trap_push_segs: mov %ss,%ax /* switch to kernel data segment */ mov %ax,%ds /* (same as kernel stack segment) */ mov %ax,%es +#ifdef USER32 mov %ax,%fs mov %ax,%gs - +#endif trap_set_segs: cld /* clear direction flag */ testl $(EFL_VM),R_EFLAGS(%rsp) /* in V86 mode? */ @@ -536,10 +582,17 @@ _return_to_user: */ _return_from_kernel: + addq $16,%rsp /* skip FS/GS base */ +#ifndef USER32 +_kret_popl_gs: +_kret_popl_fs: + addq $16,%rsp /* skip FS/GS selector */ +#else _kret_popl_gs: popq %gs /* restore segment registers */ _kret_popl_fs: popq %fs +#endif _kret_popl_es: popq %rax movq %rax,%es @@ -695,14 +748,15 @@ ENTRY(all_intrs) pushq %rdx movq %es,%rdx pushq %rdx - pushq %fs - pushq %gs + PUSH_FSGS_ISR + mov %ss,%dx /* switch to kernel segments */ mov %dx,%ds mov %dx,%es +#ifdef USER32 mov %dx,%fs mov %dx,%gs - +#endif CPU_NUMBER(%edx) movq CX(EXT(int_stack_top),%edx),%rcx @@ -741,8 +795,7 @@ LEXT(return_to_iret) /* ( label for kdb_kintr and hardclock) */ cmpq $0,CX(EXT(need_ast),%edx) jnz ast_from_interrupt /* take it if so */ 1: - pop %gs /* restore segment regs */ - pop %fs + POP_FSGS_ISR pop %rdx mov %rdx,%es pop %rdx @@ -796,9 +849,7 @@ stack_overflowed: * ss */ ast_from_interrupt: - pop %gs /* restore all registers ... */ - pop %fs - pop %rdx + POP_FSGS mov %rdx,%es pop %rdx mov %rdx,%ds @@ -818,14 +869,15 @@ ast_from_interrupt: push %rdx mov %es,%rdx push %rdx - push %fs - push %gs + PUSH_FSGS + mov %ss,%dx /* switch to kernel segments */ mov %dx,%ds mov %dx,%es +#ifdef USER32 mov %dx,%fs mov %dx,%gs - +#endif CPU_NUMBER(%edx) TIME_TRAP_UENTRY @@ -947,14 +999,12 @@ kdb_from_iret_i: /* on interrupt stack */ push %rdx mov %es,%rdx push %rdx - push %fs - push %gs + PUSH_FSGS movq %rsp,%rdx /* pass regs, */ movq $0,%rsi /* code, */ movq $-1,%rdi /* type to kdb */ call EXT(kdb_trap) - pop %gs /* restore segment registers */ - pop %fs + POP_FSGS pop %rdx mov %rdx,%es pop %rdx @@ -1039,6 +1089,7 @@ ttd_from_iret_i: /* on interrupt stack */ push %rdx push %fs push %gs + ud2 // TEST it movq %rsp,%rdx /* pass regs, */ movq $0,%rsi /* code, */ movq $-1,%rdi /* type to kdb */ @@ -1087,6 +1138,8 @@ syscall_entry_2: pushq %rdx pushq %fs pushq %gs + pushq $0 // gsbase + pushq $0 // fsbase mov %ss,%dx /* switch to kernel data segment */ mov %dx,%ds |