diff options
author | Luca Dariz <luca@orpolo.org> | 2023-07-29 19:47:52 +0200 |
---|---|---|
committer | Samuel Thibault <samuel.thibault@ens-lyon.org> | 2023-08-04 23:41:35 +0200 |
commit | 55c6c03da81908fdf7de113e1b3c6efa758e82e7 (patch) | |
tree | 43035e2383974cbc36bf2f887f80db070d184a46 /x86_64 | |
parent | 0440b2aee5586daa986dc9322f4484266d09fb64 (diff) | |
download | gnumach-55c6c03da81908fdf7de113e1b3c6efa758e82e7.tar.gz gnumach-55c6c03da81908fdf7de113e1b3c6efa758e82e7.tar.bz2 gnumach-55c6c03da81908fdf7de113e1b3c6efa758e82e7.zip |
x86_64: refactor segment register handling
The actual values are not saved together with the rest of the thread
state, both because it would be quite espensive (reading MSR, unless
rdfsbase instructions are supported, but that's optional) and not
really needed. The only way the user has to change its value is with a
specific RPC, so we can intercept the change easily. Furthermore,
Leaving the values there exposes them to being corrupted in case of a
double interruption, e.g. an irq is handled just before iretq but
after starting to restore the thread state. This solution was
suggested by Sergey Bugaev.
* i386/i386/db_trace.c: remove fsbase/gsbase from the registers
available
* i386/i386/debug_i386.c: remove fsbase/gsbase from the printed thread
state
* i386/i386/i386asm.sym: remove fsbase/gsbase as it's not needed in
asm anymore
* i386/i386/pcb.c: point fsbase/gsbase to the new location
* i386/i386/thread.h: move fsbase/gsbase to the machine state
* x86_64/locore.S: generalize segment-handling including es/ds/gs/fs
and remove fsbase/gsbase handling. Also, factor out kernel segment
selector setting to a macro.
Message-Id: <20230729174753.1145878-4-luca@orpolo.org>
Diffstat (limited to 'x86_64')
-rw-r--r-- | x86_64/locore.S | 228 |
1 files changed, 85 insertions, 143 deletions
diff --git a/x86_64/locore.S b/x86_64/locore.S index 7127957b..66a9436a 100644 --- a/x86_64/locore.S +++ b/x86_64/locore.S @@ -39,6 +39,10 @@ #include <i386/i386/cpu_number.h> #include <i386/i386/xen.h> + +/* + * Helpers for thread state as saved in the pcb area, during trap or irq handling + */ #define pusha \ pushq %rax ;\ pushq %rcx ;\ @@ -75,45 +79,74 @@ popq %rcx ;\ popq %rax +/* + * Note that we have to load the kernel segment registers even if this + * is a trap from the kernel, because the kernel uses user segment + * registers for copyin/copyout. + * (XXX Would it be smarter just to use fs or gs for that?) + */ #ifdef USER32 -#define PUSH_FSGS \ +#define PUSH_SEGMENTS(reg) \ + movq %ds,reg ;\ + pushq reg ;\ + movq %es,reg ;\ + pushq reg ;\ pushq %fs ;\ - pushq %gs ;\ - subq $16,%rsp + pushq %gs #else -#define PUSH_FSGS \ +#define PUSH_SEGMENTS(reg) \ subq $32,%rsp #endif #ifdef USER32 -#define POP_FSGS \ +#define POP_SEGMENTS(reg) \ popq %gs ;\ popq %fs ;\ - addq $16,%rsp + popq reg ;\ + movq reg,%es ;\ + popq reg ;\ + movq reg,%ds #else -#define POP_FSGS \ +#define POP_SEGMENTS(reg) \ addq $32,%rsp #endif #ifdef USER32 -#define PUSH_FSGS_ISR \ +#define PUSH_SEGMENTS_ISR(reg) \ + movq %ds,reg ;\ + pushq reg ;\ + movq %es,reg ;\ + pushq reg ;\ pushq %fs ;\ pushq %gs #else -#define PUSH_FSGS_ISR \ - subq $16,%rsp +#define PUSH_SEGMENTS_ISR(reg) \ + subq $32,%rsp #endif #ifdef USER32 -#define POP_FSGS_ISR \ +#define POP_SEGMENTS_ISR(reg) \ popq %gs ;\ - popq %fs + popq %fs ;\ + popq reg ;\ + movq reg,%es ;\ + popq reg ;\ + movq reg,%ds #else -#define POP_FSGS_ISR \ - addq $16,%rsp +#define POP_SEGMENTS_ISR(reg) \ + addq $32,%rsp #endif - +#ifdef USER32 +#define SET_KERNEL_SEGMENTS \ + mov %ss,%dx /* switch to kernel segments */ ;\ + mov %dx,%ds ;\ + mov %dx,%es ;\ + mov %dx,%fs ;\ + mov %dx,%gs +#else +#define SET_KERNEL_SEGMENTS +#endif /* * Fault recovery. @@ -350,32 +383,17 @@ ENTRY(start_timer) /* * Trap/interrupt entry points. * - * All traps must create the following save area on the kernel stack: - * - * gs - * fs - * es - * ds - * edi - * esi - * ebp - * cr2 if page fault - otherwise unused - * ebx - * edx - * ecx - * eax - * trap number - * error code - * eip - * cs - * eflags - * user rsp - if from user - * user ss - if from user - * es - if from V86 thread - * ds - if from V86 thread - * fs - if from V86 thread - * gs - if from V86 thread + * All traps must create the i386_saved_state struct on the stack on + * entry. Note that: + * - CR2 is only used if the trap is a page fault + * - user_rsp/user_ss are only used if entering from user space + * - v86_regs are used only from V86 threads + * (TODO check if V86 is still used with USER32) * + * Depending the CPL before entry, the stack might be switched or not; + * if entering from user-space the CPU loads TSS->RSP0 in RSP, + * otherwise RSP is unchanged. After this, the cpu pushes + * SS/RSP/RFLAFS/CS/RIP and optionally ErrorCode and executes the handler. */ /* Try to save/show some information when a double fault happens @@ -426,16 +444,16 @@ trap_check_kernel_exit: /* check for the kernel exit sequence */ cmpq $_kret_iret,16(%rsp) /* on IRET? */ je fault_iret -#if 0 +#ifdef USER32 cmpq $_kret_popl_ds,16(%rsp) /* popping DS? */ je fault_popl_ds cmpq $_kret_popl_es,16(%rsp) /* popping ES? */ je fault_popl_es -#endif cmpq $_kret_popl_fs,16(%rsp) /* popping FS? */ je fault_popl_fs cmpq $_kret_popl_gs,16(%rsp) /* popping GS? */ je fault_popl_gs +#endif take_fault: /* if none of the above: */ jmp EXT(alltraps) /* treat as normal trap. */ @@ -464,6 +482,7 @@ fault_iret: popq %rax /* restore eax */ jmp EXT(alltraps) /* take fault */ +#ifdef USER32 /* * Fault restoring a segment register. The user's registers are still * saved on the stack. The offending segment register has not been @@ -499,6 +518,7 @@ push_gs: 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 */ @@ -562,23 +582,8 @@ ENTRY(t_page_fault) ENTRY(alltraps) pusha /* save the general registers */ trap_push_segs: - movq %ds,%rax /* and the segment registers */ - pushq %rax - movq %es,%rax /* and the segment registers */ - pushq %rax - PUSH_FSGS - - /* Note that we have to load the segment registers - even if this is a trap from the kernel, - because the kernel uses user segment registers for copyin/copyout. - (XXX Would it be smarter just to use fs or gs for that?) */ - 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 + PUSH_SEGMENTS(%rax) + SET_KERNEL_SEGMENTS trap_set_segs: cld /* clear direction flag */ #ifdef USER32 @@ -634,23 +639,20 @@ _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 */ + addq $32,%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 _kret_popl_ds: popq %rax movq %rax,%ds +#endif popa /* restore general registers */ addq $16,%rsp /* discard trap number and error code */ _kret_iret: @@ -777,8 +779,11 @@ INTERRUPT(255) /* XXX handle NMI - at least print a warning like Linux does. */ /* - * All interrupts enter here. - * old %eax on stack; interrupt number in %eax. + * All interrupts enter here. The cpu might have loaded a new RSP, + * depending on the previous CPL, as in alltraps. + * Old %eax on stack, interrupt number in %eax; we need to fill the remaining + * fields of struct i386_interrupt_state, which might be in the pcb or in the + * interrupt stack. */ ENTRY(all_intrs) pushq %rcx /* save registers */ @@ -791,24 +796,15 @@ ENTRY(all_intrs) pushq %r11 cld /* clear direction flag */ - movq %ds,%rdx /* save segment registers */ - pushq %rdx - movq %es,%rdx - pushq %rdx - PUSH_FSGS_ISR + PUSH_SEGMENTS_ISR(%rdx) movq %rsp,%rdx /* on an interrupt stack? */ and $(~(INTSTACK_SIZE-1)),%rdx cmpq %ss:EXT(int_stack_base),%rdx je int_from_intstack /* if not: */ - mov %ss,%dx /* switch to kernel segments */ - mov %dx,%ds - mov %dx,%es -#ifdef USER32 - mov %dx,%fs - mov %dx,%gs -#endif + SET_KERNEL_SEGMENTS + CPU_NUMBER(%edx) movq CX(EXT(int_stack_top),%edx),%rcx @@ -849,11 +845,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_FSGS_ISR - pop %rdx - mov %rdx,%es - pop %rdx - mov %rdx,%ds + POP_SEGMENTS_ISR(%rdx) pop %r11 pop %r10 pop %r9 @@ -871,12 +863,7 @@ int_from_intstack: jb stack_overflowed /* if not: */ call EXT(interrupt) /* call interrupt routine */ _return_to_iret_i: /* ( label for kdb_kintr) */ - POP_FSGS_ISR - pop %rdx - mov %rdx,%es - pop %rdx - mov %rdx,%ds - + POP_SEGMENTS_ISR(%rdx) pop %r11 pop %r10 pop %r9 @@ -909,11 +896,7 @@ stack_overflowed: * ss */ ast_from_interrupt: - POP_FSGS_ISR - pop %rdx - mov %rdx,%es - pop %rdx - mov %rdx,%ds + POP_SEGMENTS_ISR(%rdx) popq %r11 popq %r10 popq %r9 @@ -926,19 +909,8 @@ ast_from_interrupt: pushq $0 /* zero code */ pushq $0 /* zero trap number */ pusha /* save general registers */ - mov %ds,%rdx /* save segment registers */ - push %rdx - mov %es,%rdx - push %rdx - 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 + PUSH_SEGMENTS_ISR(%rdx) + SET_KERNEL_SEGMENTS CPU_NUMBER(%edx) TIME_TRAP_UENTRY @@ -1056,20 +1028,12 @@ kdb_from_iret_i: /* on interrupt stack */ pushq $0 /* zero error code */ pushq $0 /* zero trap number */ pusha /* save general registers */ - mov %ds,%rdx /* save segment registers */ - push %rdx - mov %es,%rdx - push %rdx - PUSH_FSGS + PUSH_SEGMENTS(%rdx) movq %rsp,%rdx /* pass regs, */ movq $0,%rsi /* code, */ movq $-1,%rdi /* type to kdb */ call EXT(kdb_trap) - POP_FSGS - pop %rdx - mov %rdx,%es - pop %rdx - mov %rdx,%ds + POP_SEGMENTS(%rdx) popa /* restore general registers */ addq $16,%rsp @@ -1144,23 +1108,13 @@ ttd_from_iret_i: /* on interrupt stack */ pushq $0 /* zero error code */ pushq $0 /* zero trap number */ pusha /* save general registers */ - mov %ds,%rdx /* save segment registers */ - push %rdx - mov %es,%rdx - push %rdx - push %fs - push %gs + PUSH_SEGMENTS_ISR(%rdx) ud2 // TEST it movq %rsp,%rdx /* pass regs, */ movq $0,%rsi /* code, */ movq $-1,%rdi /* type to kdb */ call _kttd_trap - pop %gs /* restore segment registers */ - pop %fs - pop %rdx - mov %rdx,%es - pop %rdx - mov %rdx,%ds + POP_SEGMENTS_ISR(%rdx) popa /* restore general registers */ addq $16,%rsp @@ -1193,20 +1147,8 @@ syscall_entry_2: pushq $0 /* clear trap number slot */ pusha /* save the general registers */ - movq %ds,%rdx /* and the segment registers */ - pushq %rdx - movq %es,%rdx - pushq %rdx - pushq %fs - pushq %gs - pushq $0 // gsbase - pushq $0 // fsbase - - mov %ss,%dx /* switch to kernel data segment */ - mov %dx,%ds - mov %dx,%es - mov %dx,%fs - mov %dx,%gs + PUSH_SEGMENTS(%rdx) + SET_KERNEL_SEGMENTS /* * Shuffle eflags,eip,cs into proper places |