[haiku-commits] haiku: hrev51941 - src/system/kernel/arch/x86/64

  • From: Jérôme Duval <jerome.duval@xxxxxxxxx>
  • To: haiku-commits@xxxxxxxxxxxxx
  • Date: Tue, 15 May 2018 06:27:21 -0400 (EDT)

hrev51941 adds 1 changeset to branch 'master'
old head: 27b32ee02c3ba1278e17c41fdd2c1768062ee99e
new head: 496080235a81984a00a97f707d5e0215e11bd7d1
overview: 
https://git.haiku-os.org/haiku/log/?qt=range&q=496080235a81+%5E27b32ee02c3b

----------------------------------------------------------------------------

496080235a81: kernel/x86_64: add ia32 syscall entry and commpage syscall code.
  
  * only for Intel sysenter/sysexit.
  * the entry function processes stack arguments as follows:
      we look up the syscall in the table, find the number of arguments.
      reserve place on the stack for the arguments.
      copy arguments on the stack
      pop register arguments
      call the syscall function
      place the return value in ax and dx registers.
  * TODO: we need to store the arguments somewhere for the post-syscall tracing.
  * the thread exit stub is 32-bit, for the time being use hexadecimal 
instructions.
  
  Change-Id: Ie5c502eb596d4fa7613d238de80643566bc19ed8

                                   [ Jérôme Duval <jerome.duval@xxxxxxxxx> ]

----------------------------------------------------------------------------

Revision:    hrev51941
Commit:      496080235a81984a00a97f707d5e0215e11bd7d1
URL:         https://git.haiku-os.org/haiku/commit/?id=496080235a81
Author:      Jérôme Duval <jerome.duval@xxxxxxxxx>
Date:        Wed May  2 17:31:36 2018 UTC

----------------------------------------------------------------------------

3 files changed, 462 insertions(+), 1 deletion(-)
src/system/kernel/arch/x86/64/entry_compat.S | 429 +++++++++++++++++++++++
src/system/kernel/arch/x86/64/interrupts.S   |   2 +-
src/system/kernel/arch/x86/64/syscalls_asm.S |  32 ++

----------------------------------------------------------------------------

diff --git a/src/system/kernel/arch/x86/64/entry_compat.S 
b/src/system/kernel/arch/x86/64/entry_compat.S
new file mode 100644
index 0000000000..855044ae9c
--- /dev/null
+++ b/src/system/kernel/arch/x86/64/entry_compat.S
@@ -0,0 +1,429 @@
+/*
+ * Copyright 2018, Jérôme Duval, jerome.duval@xxxxxxxxx.
+ * Copyright 2012, Alex Smith, alex@xxxxxxxxxxxxxxxx.
+ * Distributed under the terms of the MIT License.
+ */
+
+
+#include <asm_defs.h>
+
+#include <thread_types.h>
+
+#include <arch/x86/descriptors.h>
+#include <arch/x86/arch_altcodepatch.h>
+#include <arch/x86/arch_cpu.h>
+#include <arch/x86/arch_kernel.h>
+#define COMMPAGE_COMPAT
+#include <commpage_defs.h>
+
+#include "asm_offsets.h"
+#include "syscall_numbers.h"
+#include "syscall_table.h"
+
+
+// Push the remainder of the interrupt frame onto the stack.
+#define PUSH_IFRAME_BOTTOM(iframeType) \
+       push    %rax;   /* orig_rax */          \
+       push    %rax;                                           \
+       push    %rbx;                                           \
+       push    %rcx;                                           \
+       push    %rdx;                                           \
+       push    %rdi;                                           \
+       push    %rsi;                                           \
+       push    %rbp;                                           \
+       push    %r8;                                            \
+       push    %r9;                                            \
+       push    %r10;                                           \
+       push    %r11;                                           \
+       push    %r12;                                           \
+       push    %r13;                                           \
+       push    %r14;                                           \
+       push    %r15;                                           \
+       pushq   $0;                                                     \
+       push    $iframeType;
+
+
+// Restore the interrupt frame.
+#define RESTORE_IFRAME()                               \
+       add             $16, %rsp;                                      \
+       pop             %r15;                                           \
+       pop             %r14;                                           \
+       pop             %r13;                                           \
+       pop             %r12;                                           \
+       pop             %r11;                                           \
+       pop             %r10;                                           \
+       pop             %r9;                                            \
+       pop             %r8;                                            \
+       pop             %rbp;                                           \
+       pop             %rsi;                                           \
+       pop             %rdi;                                           \
+       pop             %rdx;                                           \
+       pop             %rcx;                                           \
+       pop             %rbx;                                           \
+       pop             %rax;                                           \
+       addq    $24, %rsp;
+
+
+// The macros below require R12 to contain the current thread pointer. R12 is
+// callee-save so will be preserved through all function calls and only needs
+// to be obtained once. R13 is used to store the system call start time, will
+// also be preserved.
+
+#define LOCK_THREAD_TIME()                                                     
                        \
+       leaq    THREAD_time_lock(%r12), %rdi;                                   
        \
+       call    acquire_spinlock;
+
+#define UNLOCK_THREAD_TIME()                                                   
                \
+       leaq    THREAD_time_lock(%r12), %rdi;                                   
        \
+       call    release_spinlock;                                               
                        \
+
+#define UPDATE_THREAD_USER_TIME()                                              
                \
+       LOCK_THREAD_TIME()                                                      
                                \
+                                                                               
                                                \
+       call    system_time;                                                    
                        \
+                                                                               
                                                \
+       /* Preserve system_time for post syscall debug */                       
\
+       movq    %rax, %r13;                                                     
                                \
+                                                                               
                                                \
+       /* thread->user_time += now - thread->last_time; */                     
\
+       subq    THREAD_last_time(%r12), %rax;                                   
        \
+       addq    %rax, THREAD_user_time(%r12);                                   
        \
+                                                                               
                                                \
+       /* thread->last_time = now; */                                          
                \
+       movq    %r13, THREAD_last_time(%r12);                                   
        \
+                                                                               
                                                \
+       /* thread->in_kernel = true; */                                         
                \
+       movb    $1, THREAD_in_kernel(%r12);                                     
                \
+                                                                               
                                                \
+       UNLOCK_THREAD_TIME()
+
+#define UPDATE_THREAD_KERNEL_TIME()                                            
                \
+       LOCK_THREAD_TIME()                                                      
                                \
+                                                                               
                                                \
+       call    system_time;                                                    
                        \
+       movq    %rax, %r13;                                                     
                                \
+                                                                               
                                                \
+       /* thread->kernel_time += now - thread->last_time; */           \
+       subq    THREAD_last_time(%r12), %rax;                                   
        \
+       addq    %rax, THREAD_kernel_time(%r12);                                 
        \
+                                                                               
                                                \
+       /* thread->last_time = now; */                                          
                \
+       movq    %r13, THREAD_last_time(%r12);                                   
        \
+                                                                               
                                                \
+       /* thread->in_kernel = false; */                                        
                \
+       movb    $0, THREAD_in_kernel(%r12);                                     
                \
+                                                                               
                                                \
+       UNLOCK_THREAD_TIME()
+
+#define STOP_USER_DEBUGGING()                                                  
                \
+       testl   $(THREAD_FLAGS_BREAKPOINTS_INSTALLED                            
\
+                       | THREAD_FLAGS_SINGLE_STEP), THREAD_flags(%r12);        
\
+       jz              1f;                                                     
                                                \
+       call    x86_exit_user_debug_at_kernel_entry;                            
\
+  1:
+
+#define CLEAR_FPU_STATE() \
+       pxor %xmm0, %xmm0; \
+       pxor %xmm1, %xmm1; \
+       pxor %xmm2, %xmm2; \
+       pxor %xmm3, %xmm3; \
+       pxor %xmm4, %xmm4; \
+       pxor %xmm5, %xmm5; \
+       pxor %xmm6, %xmm6; \
+       pxor %xmm7, %xmm7; \
+       pxor %xmm8, %xmm8; \
+       pxor %xmm9, %xmm9; \
+       pxor %xmm10, %xmm10; \
+       pxor %xmm11, %xmm11; \
+       pxor %xmm12, %xmm12; \
+       pxor %xmm13, %xmm13; \
+       pxor %xmm14, %xmm14; \
+       pxor %xmm15, %xmm15
+
+
+// SYSCALL entry point.
+FUNCTION(x86_64_syscall32_entry):
+       // TODO: implement for AMD SYSCALL
+       sysret
+FUNCTION_END(x86_64_syscall32_entry)
+
+
+// SYSENTER entry point.
+//     ecx - user esp
+FUNCTION(x86_64_sysenter32_entry):
+       swapgs
+
+       // Set up an iframe on the stack (ECX = saved ESP).
+       push    $USER_DATA_SELECTOR                     // ss
+       // zero extend %ecx
+       movl    %ecx, %ecx
+       push    %rcx                                            // rsp
+       pushfq                                                          // flags
+       orl             $(1 << 9), (%rsp)               // set the IF 
(interrupts) bit
+       push    $USER32_CODE_SELECTOR           // cs
+
+       movq    %gs:0, %rdx
+       movq    THREAD_team(%rdx), %rdx
+       movq    TEAM_commpage_address(%rdx), %rdx
+       ASM_STAC
+       add             4 * COMMPAGE_ENTRY_X86_SYSCALL(%rdx), %rdx
+       ASM_CLAC
+       add             $4, %rdx                                // sysenter is 
at offset 2, 2 bytes long
+       push    %rdx                                            // ip
+
+       push    $0                                                      // 
error_code
+       push    $99                                                     // 
vector
+       PUSH_IFRAME_BOTTOM(IFRAME_TYPE_SYSCALL)
+
+       cld
+
+       // Frame pointer is the iframe.
+       movq    %rsp, %rbp
+       andq    $~15, %rsp
+
+       // Preserve call number (R14 is callee-save), get thread pointer.
+       movq    %rax, %r14
+       movq    %gs:0, %r12
+
+       STOP_USER_DEBUGGING()
+       UPDATE_THREAD_USER_TIME()
+
+       // No longer need interrupts disabled.
+       sti
+
+       // Check whether the syscall number is valid.
+       cmpq    $SYSCALL_COUNT, %r14
+       jae             .Lsyscall_return
+
+       // Get the system call table entry. Note I'm hardcoding the shift 
because
+       // sizeof(syscall_info) is 16 and scale factors of 16 aren't supported,
+       // so can't just do leaq kSyscallInfos(, %rax, SYSCALL_INFO_sizeof).
+       movq    %r14, %rax
+       shlq    $4, %rax
+       leaq    kSyscallCompatInfos(, %rax, 1), %rax
+
+       // Restore the arguments from the stack.
+       movq    SYSCALL_INFO_parameter_size(%rax), %rcx
+
+       // Get the address to copy from.
+       movq    IFRAME_user_sp(%rbp), %rsi
+       addq    $4, %rsi
+       movabs  $(USER_BASE + USER_SIZE), %rdx
+       cmp             %rdx, %rsi
+       jae             .Lbad_syscall_args
+
+       // Make space on the stack for the double size.
+       shlq    $1, %rcx
+       cmpq    $48, %rcx
+       ja              .Lprepare_stack
+       movq    $48, %rcx
+.Lprepare_stack:
+       subq    %rcx, %rsp
+       andq    $~15, %rsp
+       movq    %rsp, %rdi
+
+       // Get the extended system call table entry.
+       movq    %r14, %r15
+       imulq   $ EXTENDED_SYSCALL_INFO_sizeof, %r15
+       leaq    kExtendedSyscallCompatInfos(, %r15, 1), %r15
+       xor             %rcx, %rcx
+       movl    EXTENDED_SYSCALL_INFO_parameter_count(%r15), %ecx
+       leaq    EXTENDED_SYSCALL_INFO_parameters(%r15), %r15
+
+       // Set a fault handler.
+       movq    $.Lbad_syscall_args, THREAD_fault_handler(%r12)
+
+       ASM_STAC
+
+       jmp     2f
+       // Copy them by doublewords.
+1:
+       // Advance to next parameter
+       addq    $ SYSCALL_PARAMETER_INFO_sizeof, %r15
+       subq    $1, %rcx
+2:
+       cmpq    $0, %rcx
+       je              4f
+       movsd
+       cmpl    $0x8, SYSCALL_PARAMETER_INFO_used_size(%r15)
+       je              3f
+       movl    $0,     (%rdi)
+       addq    $4, %rdi
+       jmp             1b
+3:
+       // Copy the next doubleword
+       movsd
+       jmp             1b
+4:
+       ASM_CLAC
+       movq    $0, THREAD_fault_handler(%r12)
+
+.Lperform_syscall:
+       testl   $THREAD_FLAGS_DEBUGGER_INSTALLED, THREAD_flags(%r12)
+       jnz             .Lpre_syscall_debug
+
+.Lpre_syscall_debug_done:
+       // arguments on the stack, copy in the registers
+       pop             %rdi
+       pop             %rsi
+       pop             %rdx
+       pop             %rcx
+       pop             %r8
+       pop             %r9
+
+       // TODO: pre-syscall tracing
+
+       // Call the function and save its return value.
+       call    *SYSCALL_INFO_function(%rax)
+       movq    %rax, %rdx
+       movq    %rax, IFRAME_ax(%rbp)
+       shrq    $32, %rdx
+       movq    %rdx, IFRAME_dx(%rbp)
+
+       // TODO: post-syscall tracing
+
+.Lsyscall_return:
+       // Restore the original stack pointer and return.
+       movq    %rbp, %rsp
+
+       testl   $(THREAD_FLAGS_DEBUGGER_INSTALLED | 
THREAD_FLAGS_SIGNALS_PENDING \
+                       | THREAD_FLAGS_DEBUG_THREAD | 
THREAD_FLAGS_BREAKPOINTS_DEFINED \
+                       | THREAD_FLAGS_TRAP_FOR_CORE_DUMP \
+                       | THREAD_FLAGS_64_BIT_SYSCALL_RETURN \
+                       | THREAD_FLAGS_RESTART_SYSCALL | 
THREAD_FLAGS_SYSCALL_RESTARTED) \
+                       , THREAD_flags(%r12)
+       jnz             .Lpost_syscall_work
+
+       cli
+
+       UPDATE_THREAD_KERNEL_TIME()
+
+       // If we've just restored a signal frame, use the IRET path.
+       cmpq    $SYSCALL_RESTORE_SIGNAL_FRAME, %r14
+       je              .Lrestore_fpu
+
+       CLEAR_FPU_STATE()
+
+       // Restore the iframe and RCX/RDX for SYSRET.
+       RESTORE_IFRAME()
+       pop             %rdx
+       addq    $8, %rsp
+       andl  $~0x200,(%rsp)
+       popfq
+       pop             %rcx
+
+       // Restore previous GS base and return.
+       swapgs
+       sti
+       sysexit
+
+
+.Lpre_syscall_debug:
+       // preserve registers
+       push    %rdi
+       push    %rsi
+
+       // user_debug_pre_syscall expects a pointer to a block of arguments, 
need
+       // to push the register arguments onto the stack.
+       movq    %r14, %rdi                              // syscall number
+       movq    0x10(%rsp), %rsi
+       push    %rax
+       call    user_debug_pre_syscall
+       pop             %rax
+
+       // restore registers
+       pop             %rsi
+       pop             %rdi
+       jmp             .Lpre_syscall_debug_done
+
+.Lpost_syscall_work:
+       // Clear the restarted flag.
+       testl   $(THREAD_FLAGS_64_BIT_SYSCALL_RETURN \
+                               | THREAD_FLAGS_SYSCALL_RESTARTED), 
THREAD_flags(%r12)
+       jz              2f
+1:
+       movl    THREAD_flags(%r12), %eax
+       movl    %eax, %edx
+       andl    $~(THREAD_FLAGS_64_BIT_SYSCALL_RETURN \
+                               | THREAD_FLAGS_SYSCALL_RESTARTED), %edx
+       lock
+       cmpxchgl        %edx, THREAD_flags(%r12)
+       jnz             1b
+2:
+       testl   $THREAD_FLAGS_DEBUGGER_INSTALLED, THREAD_flags(%r12)
+       jz              1f
+
+       // Post-syscall debugging. Same as above, need a block of arguments.
+       // TODO: restore arguments from the stack
+       push    IFRAME_r9(%rbp)
+       push    IFRAME_r8(%rbp)
+       push    IFRAME_r10(%rbp)
+       push    IFRAME_dx(%rbp)
+       push    IFRAME_si(%rbp)
+       push    IFRAME_di(%rbp)
+       movq    %r14, %rdi                              // syscall number
+       movq    %rsp, %rsi
+       movq    IFRAME_ax(%rbp), %rdx   // return value
+       movq    %r13, %rcx                              // start time, 
preserved earlier
+       call    user_debug_post_syscall
+       addq    $48, %rsp
+1:
+       // Do we need to handle signals?
+       testl   $(THREAD_FLAGS_SIGNALS_PENDING | THREAD_FLAGS_DEBUG_THREAD \
+                       | THREAD_FLAGS_TRAP_FOR_CORE_DUMP) \
+                       , THREAD_flags(%r12)
+       jnz             .Lpost_syscall_handle_signals
+       cli
+       call    thread_at_kernel_exit_no_signals
+
+.Lpost_syscall_work_done:
+       // Handle syscall restarting.
+       testl   $THREAD_FLAGS_RESTART_SYSCALL, THREAD_flags(%r12)
+       jz              1f
+       movq    %rsp, %rdi
+       call    x86_restart_syscall
+1:
+       // Install breakpoints, if defined.
+       testl   $THREAD_FLAGS_BREAKPOINTS_DEFINED, THREAD_flags(%r12)
+       jz              1f
+       movq    %rbp, %rdi
+       call    x86_init_user_debug_at_kernel_exit
+1:
+       // On this return path it is possible that the frame has been modified,
+       // for example to execute a signal handler. In this case it is safer to
+       // return via IRET.
+       CLEAR_FPU_STATE()
+       jmp .Liret
+
+.Lrestore_fpu:
+       movq    IFRAME_fpu(%rbp), %rax
+       fxrstorq        (%rax)
+.Liret:
+       // Restore the saved registers.
+       RESTORE_IFRAME()
+
+       // Restore the previous GS base and return.
+       swapgs
+       iretq
+
+.Lpost_syscall_handle_signals:
+       call    thread_at_kernel_exit
+       jmp             .Lpost_syscall_work_done
+
+.Lbad_syscall_args:
+       movq    $0, THREAD_fault_handler(%r12)
+       movq    %rbp, %rsp
+       jmp             .Lsyscall_return
+FUNCTION_END(x86_64_sysenter32_entry)
+
+
+/* thread exit stub */
+// TODO: build with the x86 compiler
+FUNCTION(x86_sysenter32_userspace_thread_exit):
+       .byte   0x50            // push %eax
+       mov             $SYSCALL_EXIT_THREAD, %eax
+       .byte   0x89,0xe1       // mov %esp, %ecx
+       sysenter
+FUNCTION_END(x86_sysenter32_userspace_thread_exit)
+SYMBOL(x86_sysenter32_userspace_thread_exit_end):
+
diff --git a/src/system/kernel/arch/x86/64/interrupts.S 
b/src/system/kernel/arch/x86/64/interrupts.S
index 43701b1346..47d886934f 100644
--- a/src/system/kernel/arch/x86/64/interrupts.S
+++ b/src/system/kernel/arch/x86/64/interrupts.S
@@ -242,7 +242,7 @@ FUNCTION_END(int_bottom)
 
 // Handler for an interrupt that occurred in user-mode.
 STATIC_FUNCTION(int_bottom_user):
-       // Load the kerrnel GS segment base.
+       // Load the kernel GS segment base.
        swapgs
 
        // Push the rest of the interrupt frame to the stack.
diff --git a/src/system/kernel/arch/x86/64/syscalls_asm.S 
b/src/system/kernel/arch/x86/64/syscalls_asm.S
new file mode 100644
index 0000000000..e8c4eb6290
--- /dev/null
+++ b/src/system/kernel/arch/x86/64/syscalls_asm.S
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2018, Jérôme Duval. All rights reserved.
+ * Distributed under the terms of the MIT License.
+ */
+
+
+#include <asm_defs.h>
+
+
+.text
+
+/* user space half of the syscall mechanism, to be copied into the commpage */
+
+
+// Intel sysenter/sysexit
+FUNCTION(x86_user_syscall_sysenter):
+       // sysexit forces us to trash edx (-> eip) and ecx (-> esp), but they 
are
+       // scratch registers anyway. We use ecx right away to store esp.
+       movl    %esp, %ecx
+       sysenter
+       ret
+FUNCTION_END(x86_user_syscall_sysenter)
+SYMBOL(x86_user_syscall_sysenter_end):
+
+
+// AMD syscall/sysret
+FUNCTION(x86_user_syscall_syscall):
+       syscall
+       ret
+FUNCTION_END(x86_user_syscall_syscall)
+SYMBOL(x86_user_syscall_syscall_end):
+


Other related posts:

  • » [haiku-commits] haiku: hrev51941 - src/system/kernel/arch/x86/64 - Jérôme Duval