; $Id: bs3-mode-TrapSystemCallHandler.asm 93115 2022-01-01 11:31:46Z vboxsync $
;; @file
; BS3Kit - System call trap handler.
;

;
; Copyright (C) 2007-2022 Oracle Corporation
;
; This file is part of VirtualBox Open Source Edition (OSE), as
; available from http://www.virtualbox.org. This file is free software;
; you can redistribute it and/or modify it under the terms of the GNU
; General Public License (GPL) as published by the Free Software
; Foundation, in version 2 as it comes in the "COPYING" file of the
; VirtualBox OSE distribution. VirtualBox OSE is distributed in the
; hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
;
; The contents of this file may alternatively be used under the terms
; of the Common Development and Distribution License Version 1.0
; (CDDL) only, as it comes in the "COPYING.CDDL" file of the
; VirtualBox OSE distribution, in which case the provisions of the
; CDDL are applicable instead of those of the GPL.
;
; You may elect to license modified versions of this file under the
; terms and conditions of either the GPL or the CDDL or both.
;

;*********************************************************************************************************************************
;*  Header Files                                                                                                                 *
;*********************************************************************************************************************************
%include "bs3kit-template-header.mac"


;*********************************************************************************************************************************
;*  External Symbols                                                                                                             *
;*********************************************************************************************************************************
BS3_EXTERN_DATA16 g_bBs3CurrentMode
%if TMPL_BITS != 64
BS3_EXTERN_DATA16 g_uBs3CpuDetected
%endif
%if TMPL_BITS == 16
BS3_EXTERN_DATA16 g_uBs3TrapEipHint
%endif
TMPL_BEGIN_TEXT

BS3_EXTERN_CMN Bs3SelProtFar32ToFlat32
BS3_EXTERN_CMN Bs3RegCtxConvertToRingX
BS3_EXTERN_CMN Bs3RegCtxRestore
BS3_EXTERN_CMN Bs3Panic

BS3_BEGIN_TEXT16
extern Bs3PrintStrN_c16_CX_Bytes_At_DS_SI
TMPL_BEGIN_TEXT


;;
; System call handler.
;
; This is an assembly trap handler that is called in response to a system call
; request from 'user' code.  The only fixed parameter is [ER]AX which contains
; the system call number.  Other registers are assigned on a per system call
; basis, ditto for which registers are preserved and which are used to return
; stuff.  Generally, though, we preserve all registers not used as return
; values or otherwise implicitly transformed by the call.
;
; Note! The 16-bit versions of this code must be careful with using extended
;       registers as we wish this code to work on real 80286 (maybe even 8086)
;       CPUs too!
;
BS3_PROC_BEGIN_MODE Bs3TrapSystemCallHandler, BS3_PBC_NEAR ; Near because we'll probably only ever need this from CGROUP16.
        ;
        ; This prologue is kind of complicated because of 80286 and older CPUs
        ; as well as different requirements for 64-bit and the other modes.
        ;
%define VAR_CALLER_BP      [xBP]
%if TMPL_BITS != 64
 %define VAR_CALLER_DS     [xBP         - xCB]
%endif
%define VAR_CALLER_BX      [xBP - sCB*1 - xCB] ; Note! the upper word is not clean on pre-386 (16-bit mode).
%define VAR_CALLER_AX      [xBP - sCB*2 - xCB]
%define VAR_CALLER_CX      [xBP - sCB*3 - xCB]
%define VAR_CALLER_DX      [xBP - sCB*4 - xCB]
%define VAR_CALLER_SI      [xBP - sCB*5 - xCB]
%define VAR_CALLER_SI_HI   [xBP - sCB*5 - xCB + 2]
%define VAR_CALLER_DI      [xBP - sCB*6 - xCB]
%define VAR_CALLER_DI_HI   [xBP - sCB*6 - xCB + 2]
%if TMPL_BITS == 16
 %define VAR_CALLER_EBP    [xBP - sCB*7 - xCB]
 %define VAR_CALLER_ESP    [xBP - sCB*8 - xCB]
 %define VAR_CALLER_EFLAGS [xBP - sCB*9 - xCB]
 %define VAR_CALLER_MODE   [xBP - sCB*9 - xCB*2]
 %define BP_TOP_STACK_EXPR xBP - sCB*9 - xCB*2
%else
 %define VAR_CALLER_MODE   [xBP - sCB*6 - xCB*2]
 %define BP_TOP_STACK_EXPR xBP - sCB*6 - xCB*2
%endif
        push    xBP
        mov     xBP, xSP
%if TMPL_BITS == 64
        push    0
        mov     [rsp+2], es
        mov     [rsp], ds
%else
        push    ds
 %ifdef TMPL_CMN_R86
        push    BS3_SEL_DATA16
 %else
        push    RT_CONCAT(BS3_SEL_R0_DS,TMPL_BITS)
 %endif
        pop     ds                      ; DS = BS3KIT_GRPNM_DATA16 or FLAT and we can safely access data
 %if TMPL_BITS == 16 && (TMPL_MODE == BS3_MODE_RM || TMPL_MODE == BS3_MODE_PE16)
        cmp     byte [BS3_DATA16_WRT(g_uBs3CpuDetected)], BS3CPU_80286
        jbe     .prologue_pre_80386
 %endif
%endif
        push    sBX
        push    sAX
        push    sCX
        push    sDX
        push    sSI
        push    sDI
%if TMPL_BITS == 16
        push    ebp
        push    esp
        pushfd
 %if TMPL_MODE == BS3_MODE_RM || TMPL_MODE == BS3_MODE_PE16
        jmp     .prologue_end

.prologue_pre_80386:
        push    bx                      ; dummy
        push    bx
        xor     bx, bx
        push    bx                      ; dummy
        push    ax
        push    bx                      ; dummy
        push    cx
        push    bx                      ; dummy
        push    dx
        push    bx                      ; dummy
        push    si
        push    bx                      ; dummy
        push    di
        sub     sp, 0ch                 ; dummy
 %endif
%endif
.prologue_end:

        ;
        ; VAR_CALLER_MODE: Save the current mode (important for v8086 with 16-bit kernel).
        ;
        xor     xBX, xBX
        mov     bl, [BS3_DATA16_WRT(g_bBs3CurrentMode)]
        push    xBX

        ;
        ; Dispatch the system call.
        ;
        cmp     ax, BS3_SYSCALL_LAST
        ja      .invalid_syscall
%if TMPL_BITS == 16
        mov     bx, ax
        shl     bx, 1
        jmp     word [cs:.aoffSyscallHandlers + bx]
%else
        movzx   ebx, ax
        mov     ebx, [.aoffSyscallHandlers + ebx * 4]
        jmp     xBX
%endif
.aoffSyscallHandlers:
%ifdef TMPL_16BIT
        dw      .invalid_syscall wrt CGROUP16
        dw      .print_chr       wrt CGROUP16
        dw      .print_str       wrt CGROUP16
        dw      .to_ringX        wrt CGROUP16
        dw      .to_ringX        wrt CGROUP16
        dw      .to_ringX        wrt CGROUP16
        dw      .to_ringX        wrt CGROUP16
        dw      .restore_ctx     wrt CGROUP16
%else
        dd      .invalid_syscall wrt FLAT
        dd      .print_chr       wrt FLAT
        dd      .print_str       wrt FLAT
        dd      .to_ringX        wrt FLAT
        dd      .to_ringX        wrt FLAT
        dd      .to_ringX        wrt FLAT
        dd      .to_ringX        wrt FLAT
        dd      .restore_ctx     wrt FLAT
%endif

        ;
        ; Invalid system call.
        ;
.invalid_syscall:
        int3
        jmp     .return

        ;
        ; Print char in the CL register.
        ;
        ; We use the vga bios teletype interrupt to do the writing, so we must
        ; be in some kind of real mode for this to work.  16-bit code segment
        ; requried for the mode switching code.
        ;
BS3_BEGIN_TEXT16
        BS3_SET_BITS TMPL_BITS
.print_chr:
%if TMPL_BITS != 64
        push    es
        mov     di, ss                  ; Must save and restore SS for supporting 16/32 and 32/16 caller/kernel ring-0 combinations.
%endif
%ifndef TMPL_CMN_R86
        ; Switch to real mode (20h param scratch area not required).
        extern  TMPL_NM(Bs3SwitchToRM)
        call    TMPL_NM(Bs3SwitchToRM)
        BS3_SET_BITS 16
%endif

        ; Print the character, turning '\n' into '\r\n'.
        cmp     cl, 0ah                 ; \n
        je      .print_chr_newline
        mov     ah, 0eh
        mov     al, cl
        mov     bx, 0ff00h
        int     10h
        jmp     .print_chr_done

.print_chr_newline:
        mov     ax, 0e0dh               ; cmd + \r
        mov     bx, 0ff00h
        int     10h
        mov     ax, 0e0ah               ; cmd + \n
        mov     bx, 0ff00h
        int     10h

.print_chr_done:
%ifndef TMPL_CMN_R86
        ; Switch back (20h param scratch area not required).
        extern  RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_rm)
        call    RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_rm)
        BS3_SET_BITS TMPL_BITS
%endif
%if TMPL_BITS != 64
        mov     ss, di
        pop     es
%endif
        jmp     .return
TMPL_BEGIN_TEXT


        ;
        ; Prints DX chars from the string pointed to by CX:xSI to the screen.
        ;
        ; We use the vga bios teletype interrupt to do the writing, so we must
        ; be in some kind of real mode for this to work.  The string must be
        ; accessible from real mode too.
        ;
.print_str:
%if TMPL_BITS != 64
        push    es
%endif
        ; Convert the incoming pointer to real mode (assuming caller checked
        ; that real mode can access it).
        call    .convert_ptr_arg_to_real_mode_ax_si
        mov     cx, VAR_CALLER_DX

        ; Switch to real mode (no 20h scratch required)
%ifndef TMPL_CMN_R86
 %if TMPL_BITS != 16
        jmp     .print_str_to_16bit
BS3_BEGIN_TEXT16
.print_str_to_16bit:
        BS3_SET_BITS TMPL_BITS
 %endif
        extern  TMPL_NM(Bs3SwitchToRM)
        call    TMPL_NM(Bs3SwitchToRM)
        BS3_SET_BITS 16
%endif
        ; Call code in Bs3PrintStrN to do the work.
        mov     ds, ax
        call    Bs3PrintStrN_c16_CX_Bytes_At_DS_SI

        ; Switch back (20h param scratch area not required).
%ifndef TMPL_CMN_R86
        extern  RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_rm)
        call    RT_CONCAT3(_Bs3SwitchTo,TMPL_MODE_UNAME,_rm)
 %if TMPL_BITS != 16
        BS3_SET_BITS TMPL_BITS
        jmp     .print_str_end
TMPL_BEGIN_TEXT
 %endif
.print_str_end:
%endif
%if TMPL_BITS != 64
        pop     es
%endif
        jmp     .return


        ;
        ; Switch the caller to ring-0, ring-1, ring-2 or ring-3.
        ;
        ; This implement this by saving the entire register context, calling
        ; a transformation function (C) and restoring the modified register
        ; context using a generic worker.
        ;
.to_ringX:
        sub     xSP, BS3REGCTX_size
        mov     xBX, xSP                ; xBP = BS3REGCTX pointer.
        call    .save_context

%if TMPL_BITS == 32
        ; Convert xBP to flat pointer in 32-bit
        push    ss
        push    xBX
        call    Bs3SelProtFar32ToFlat32
        add     sSP, 8
        mov     xBX, xAX
%endif
        push    xBX                     ; Save pointer for the final restore call.

        ; Convert the register context from whatever it is to ring-0.
BONLY64 sub     rsp, 10h
        mov     ax, VAR_CALLER_AX
        sub     ax, BS3_SYSCALL_TO_RING0
        push    xAX
BONLY16 push    ss
        push    xBX
        BS3_CALL Bs3RegCtxConvertToRingX, 2
        add     xSP, sCB + xCB BS3_ONLY_64BIT(+ 10h)

        ; Restore the register context (does not return).
        pop     xBX                     ; restore saved pointer.
BONLY64 sub     rsp, 18h
BONLY16 push    ss
        push    xBX
        BS3_CALL Bs3RegCtxRestore, 1
        jmp     Bs3Panic


        ;
        ; Restore context pointed to by cx:xSI.
        ;
.restore_ctx:
        call    .convert_ptr_arg_to_cx_xSI
BONLY64 sub     rsp, 10h
        mov     xDX, VAR_CALLER_DX
        push    xDX
BONLY16 push    cx
        push    xSI
        BS3_CALL Bs3RegCtxRestore, 2
        jmp     Bs3Panic

        ;
        ; Return.
        ;
.return:
        pop     xBX                     ; saved mode
        mov     [BS3_DATA16_WRT(g_bBs3CurrentMode)], bl
%if TMPL_BITS == 16
        and     bl, BS3_MODE_CODE_MASK
        cmp     bl, BS3_MODE_CODE_V86
        je      .return_to_v8086_from_16bit_krnl
        cmp     bl, BS3_MODE_CODE_32
        je      .return_to_32bit_from_16bit_krnl
 %if TMPL_MODE == BS3_MODE_RM || TMPL_MODE == BS3_MODE_PE16
        cmp     byte [BS3_DATA16_WRT(g_uBs3CpuDetected)], BS3CPU_80286
        jbe     .return_pre_80386
 %endif

        popfd
        pop     esp
        pop     ebp
%endif
        pop     sDI
        pop     sSI
        pop     sDX
        pop     sCX
        pop     sAX
        pop     sBX
%if TMPL_BITS != 64
        pop     ds
        leave
        iret
%else
        mov     es, [rsp+2]
        mov     ds, [rsp]
        leave                           ; skips ds
        iretq
%endif

%if TMPL_BITS == 16
 %if TMPL_MODE == BS3_MODE_RM || TMPL_MODE == BS3_MODE_PE16
        ; Variant of the above for 80286 and older.
.return_pre_80386:
        add     sp, 0ch
        pop     di
        pop     bx                      ; dummy
        pop     si
        pop     bx                      ; dummy
        pop     dx
        pop     bx                      ; dummy
        pop     cx
        pop     bx                      ; dummy
        pop     ax
        pop     bx                      ; dummy
        pop     bx                      ; pushed twice
        pop     bx
        pop     ds
        pop     bp
        iret
 %endif

.return_to_v8086_from_16bit_krnl:
        int3
        jmp     .return_to_v8086_from_16bit_krnl

        ;
        ; Returning to 32-bit code may require us to expand and seed the eip
        ; and esp addresses in the iret frame since these are truncated when
        ; using a 16-bit interrupt handler.
        ;
        ; Incoming stack:        New stack diff cpl:
        ;   bp + 0ah: [ss]
        ;   bp + 08h: [sp]         bx + 38h: [ss]       New stack same cpl:
        ;   bp + 06h: flags
        ;   bp + 04h: cs           bx + 34h: [esp]        bx + 30h: eflags
        ;   bp + 02h: ip
        ;   --------------         bx + 30h: eflags       bx + 2ch: cs
        ;   bp + 00h: bp
        ;   bp - 02h: ds           bx + 2ch: cs           bx + 28h: eip
        ;                                                 -------------
        ;   bp - 06h: ebx          bx + 28h: eip          bx + 26h: bp
        ;                         --------------          bx + 24h: ds
        ;   bp - 0ah: eax          bx + 26h: bp
        ;                          bx + 24h: ds           bx + 20h: ebx
        ;   bp - 0eh: ecx
        ;                          bx + 20h: ebx          bx + 1ch: eax
        ;   bp - 12h: edx
        ;                          bx + 1ch: eax          bx + 18h: ecx
        ;   bp - 16h: esi
        ;                          bx + 18h: ecx          bx + 14h: edx
        ;   bp - 1ah: edi
        ;                          bx + 14h: edx          bx + 10h: esi
        ;   bp - 1eh: esp
        ;                          bx + 10h: esi          bx + 0ch: edi
        ;   bp - 22h: ebp
        ;                          bx + 0ch: edi          bx + 08h: esp
        ;   bp - 26h: eflags
        ;                          bx + 08h: esp          bx + 04h: ebp
        ;
        ;                          bx + 04h: ebp          bx + 00h: eflags
        ;
        ;                          bx + 00h: eflags
        ;
        ;
        ; If we're returning to the same CPL, we're still using the stack of
        ; the 32-bit caller.  The high ESP word does not need restoring.
        ;
        ; If we're returning to a lower CPL, there on a 16-bit ring-0 stack,
        ; however, the high ESP word is still that of the caller.
        ;
.return_to_32bit_from_16bit_krnl:
        mov     ax, cs
        and     al, 3
        mov     ah, 3
        and     ah, [xBP + xCB*2]
        ; The iret frame doubles in size, so allocate more stack.
        cmp     al, ah
        je      .return_to_32bit_from_16bit_krnl_same_cpl_sub_sp
        sub     sp, 2*2
.return_to_32bit_from_16bit_krnl_same_cpl_sub_sp:
        sub     sp, 3*2
        mov     bx, sp
        ; Copy the saved registers.
        xor     di, di
.return_to_32bit_from_16bit_krnl_copy_loop:
        mov     ecx, [bp + di - 26h]
        mov     [ss:bx + di], ecx
        add     di, 4
        cmp     di, 28h
        jb      .return_to_32bit_from_16bit_krnl_copy_loop
        ; Convert the 16-bit iret frame to a 32-bit iret frame.
        mov     ecx, [BS3_DATA16_WRT(g_uBs3TrapEipHint)]
        mov     cx,  [bp + 02h]         ; ip
        mov     [ss:bx + 28h], ecx
        mov     ecx, 0f00d0000h
        mov     cx,  [bp + 04h]         ; cs
        mov     [ss:bx + 2ch], ecx
        mov     ecx, [ss:bx]            ; caller eflags
        mov     cx,  [bp + 06h]         ; flags
        mov     [ss:bx + 30h], ecx
        cmp     al, ah
        jz      .return_to_32bit_from_16bit_krnl_do_return
        mov     ecx, [ss:bx + 08h]      ; caller esp
        mov     cx,  [bp + 08h]         ; sp
        mov     [ss:bx + 34h], ecx
        mov     ecx, 0f00d0000h
        mov     cx,  [bp + 0ah]         ; ss
        mov     [ss:bx + 38h], ecx
.return_to_32bit_from_16bit_krnl_do_return:
        popfd
        pop     ecx                     ; esp - only the high bits!
        mov     cx, sp
        mov     esp, ecx
        pop     ebp
        lea     bp, [bx + 26h]
        pop     edi
        pop     esi
        pop     edx
        pop     ecx
        pop     eax
        pop     ebx
        pop     ds
        leave
        iretd

%endif ; 16-bit


        ;
        ; Internal function. ss:xBX = Pointer to register frame (BS3REGCTX).
        ; @uses xAX
        ;
.save_context:
%if TMPL_BITS == 16
        cmp     byte [BS3_DATA16_WRT(g_uBs3CpuDetected)], BS3CPU_80386
        jae     .save_context_full

        ;
        ; 80286 or earlier.
        ;

        ; Clear the state area first.
        push    di
        xor     di, di
.save_context_16_clear_loop:
        mov     word [ss:bx + di], 0
        mov     word [ss:bx + di + 2], 0
        mov     word [ss:bx + di + 4], 0
        mov     word [ss:bx + di + 6], 0
        add     di, 8
        cmp     di, BS3REGCTX_size
        jb      .save_context_16_clear_loop
        pop     di

        ; Do the 8086/80186/80286 state saving.
        mov     ax, VAR_CALLER_AX
        mov     [ss:bx + BS3REGCTX.rax], ax
        mov     cx, VAR_CALLER_CX
        mov     [ss:bx + BS3REGCTX.rcx], ax
        mov     ax, VAR_CALLER_DX
        mov     [ss:bx + BS3REGCTX.rdx], ax
        mov     ax, VAR_CALLER_BX
        mov     [ss:bx + BS3REGCTX.rbx], ax
        mov     [ss:bx + BS3REGCTX.rsi], si
        mov     [ss:bx + BS3REGCTX.rdi], di
        mov     ax, VAR_CALLER_BP
        mov     [ss:bx + BS3REGCTX.rbp], ax
        mov     ax, VAR_CALLER_DS
        mov     [ss:bx + BS3REGCTX.ds], ax
        mov     [ss:bx + BS3REGCTX.es], es
        mov     ax, [xBP + xCB]
        mov     [ss:bx + BS3REGCTX.rip], ax
        mov     ax, [xBP + xCB*2]
        mov     [ss:bx + BS3REGCTX.cs], ax
        and     al, X86_SEL_RPL
        mov     [ss:bx + BS3REGCTX.bCpl], al
        cmp     al, 0
        je      .save_context_16_same
        mov     ax, [xBP + xCB*4]
        mov     [ss:bx + BS3REGCTX.rsp], ax
        mov     ax, [xBP + xCB*5]
        mov     [ss:bx + BS3REGCTX.ss], ax
        jmp     .save_context_16_done_stack
.save_context_16_same:
        mov     ax, bp
        add     ax, xCB * (1 + 3)
        mov     [ss:bx + BS3REGCTX.rsp], ax
        mov     ax, ss
        mov     [ss:bx + BS3REGCTX.ss], ax
.save_context_16_done_stack:
        mov     ax, [xBP + xCB*3]
        mov     [ss:bx + BS3REGCTX.rflags], ax
        mov     al, VAR_CALLER_MODE
        mov     [ss:bx + BS3REGCTX.bMode], al
        cmp     byte [BS3_DATA16_WRT(g_uBs3CpuDetected)], BS3CPU_80286
        jne     .save_context_16_return
        smsw    [ss:bx + BS3REGCTX.cr0]
        str     [ss:bx + BS3REGCTX.tr]
        sldt    [ss:bx + BS3REGCTX.ldtr]
.save_context_16_return:
        or      byte [ss:bx + BS3REGCTX.fbFlags], BS3REG_CTX_F_NO_AMD64 | BS3REG_CTX_F_NO_CR4
        ret
%endif ; TMPL_BITS == 16

        ;
        ; 80386 or later.
        ;
.save_context_full:

        ; Clear the state area.
        push    xDI
        xor     xDI, xDI
        AssertCompileSizeAlignment(BS3REGCTX, 16)
.save_context_full_clear_loop:
%if TMPL_BITS != 64
        mov     dword [ss:xBX + xDI], 0
        mov     dword [ss:xBX + xDI + 4], 0
        add     xDI, 8
%else
        mov     qword [xBX + xDI], 0
        mov     qword [xBX + xDI + 8], 0
        add     xDI, 10h
%endif
        cmp     xDI, BS3REGCTX_size
        jb      .save_context_full_clear_loop
        pop     xDI

        ; Do the 386+ state saving.
%if TMPL_BITS == 16                     ; save the high word of registered pushed on the stack.
        mov     ecx, VAR_CALLER_AX
        mov     [ss:bx + BS3REGCTX.rax], ecx
        mov     ecx, VAR_CALLER_CX
        mov     [ss:bx + BS3REGCTX.rcx], ecx
        mov     ecx, VAR_CALLER_DX
        mov     [ss:bx + BS3REGCTX.rdx], ecx
        mov     ecx, VAR_CALLER_BX
        mov     [ss:bx + BS3REGCTX.rbx], ecx
        mov     ecx, VAR_CALLER_EBP
        mov     [ss:bx + BS3REGCTX.rbp], ecx
        mov     ecx, VAR_CALLER_ESP
        mov     [ss:bx + BS3REGCTX.rsp], ecx
        mov     ecx, VAR_CALLER_SI
        mov     [ss:bx + BS3REGCTX.rsi], ecx
        mov     ecx, VAR_CALLER_DI
        mov     [ss:bx + BS3REGCTX.rdi], ecx
        mov     ecx, VAR_CALLER_EFLAGS
        mov     [ss:bx + BS3REGCTX.rflags], ecx

        ; Seed high EIP word if 32-bit CS.
        lar     ecx, [bp + 4]
        jnz     .save_context_full_done_16bit_high_word
        test    ecx, X86LAR_F_D
        jz      .save_context_full_done_16bit_high_word
        mov     ecx, [BS3_DATA16_WRT(g_uBs3TrapEipHint)]
        mov     [ss:bx + BS3REGCTX.rip], ecx
.save_context_full_done_16bit_high_word:
%endif ; 16-bit
        mov     xAX, VAR_CALLER_AX
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.rax], xAX
        mov     xCX, VAR_CALLER_CX
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.rcx], xCX
        mov     xAX, VAR_CALLER_DX
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.rdx], xAX
        mov     xAX, VAR_CALLER_BX
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.rbx], xAX
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.rsi], sSI
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.rdi], sDI
        mov     xAX, VAR_CALLER_BP
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.rbp], xAX
%if TMPL_BITS != 64
        mov     ax, VAR_CALLER_DS
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.ds], ax
%else
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.ds], ds
%endif
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.es], es
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.fs], fs
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.gs], gs
        mov     xAX, [xBP + xCB]
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.rip], xAX
        mov     ax, [xBP + xCB*2]
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.cs], ax
%if TMPL_MODE != BS3_MODE_RM
        and     al, X86_SEL_RPL
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.bCpl], al
        cmp     al, 0
        je      .save_context_full_same
        mov     xAX, [xBP + xCB*4]
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.rsp], xAX
        mov     ax, [xBP + xCB*5]
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.ss], ax
        jmp     .save_context_full_done_stack
%else
        mov     byte [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.bCpl], 0
%endif
.save_context_full_same:
        mov     xAX, xBP
        add     xAX, xCB * (1 + 3)
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.rsp], xAX
        mov     ax, ss
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.ss], ax
.save_context_full_done_stack:
        mov     xAX, [xBP + xCB*3]
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.rflags], xAX

        mov     al, VAR_CALLER_MODE
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.bMode], al
%if TMPL_BITS == 64
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.r8], r8
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.r9], r9
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.r10], r10
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.r11], r11
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.r12], r12
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.r13], r13
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.r14], r14
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.r15], r15
%endif
        str     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.tr]
        sldt    [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.ldtr]
        mov     sAX, cr0
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.cr0], sAX
        mov     sAX, cr2
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.cr2], sAX
        mov     sAX, cr3
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.cr3], sAX
%if TMPL_BITS != 64
        test    byte [BS3_DATA16_WRT(g_uBs3CpuDetected)], (BS3CPU_F_CPUID >> 8)
        jnz     .have_cr4
        or      byte [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.fbFlags], BS3REG_CTX_F_NO_CR4
        jmp     .done_cr4
.have_cr4:
%endif
        mov     sAX, cr4
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.cr4], sAX
%if TMPL_BITS != 64
.done_cr4:
        or      byte [ss:xBX + BS3REGCTX.fbFlags], BS3REG_CTX_F_NO_AMD64

        ; Deal with extended v8086 frame.
 %if TMPL_BITS == 32
        test    dword [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.rflags], X86_EFL_VM
        jz      .save_context_full_return
 %else
        test    byte VAR_CALLER_MODE, BS3_MODE_CODE_V86
        jz      .save_context_full_return
        mov     dword [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.rflags], X86_EFL_VM
 %endif
        mov     xAX, [xBP + xCB*4]
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.rsp], xAX
        mov     ax, [xBP + xCB*5]
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.ss], ax
        mov     ax, [xBP + xCB*6]
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.es], ax
        mov     ax, [xBP + xCB*7]
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.ds], ax
        mov     ax, [xBP + xCB*8]
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.fs], ax
        mov     ax, [xBP + xCB*9]
        mov     [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.gs], ax
        mov     byte [BS3_NOT_64BIT(ss:) xBX + BS3REGCTX.bCpl], 3
        jmp     .save_context_full_return

%endif  ; !64-bit

.save_context_full_return:
        ret

%if TMPL_BITS == 16
        CPU 286
%endif

        ;
        ; Internal function for converting a syscall pointer parameter (cx:xSI)
        ; to a pointer we can use here in this context.
        ;
        ; Returns the result in cx:xSI.
        ; @uses xAX, xCX, xDX
        ;
.convert_ptr_arg_to_cx_xSI:
        call    .convert_ptr_arg_to_flat
%if TMPL_BITS == 16
        ; Convert to tiled address.
        mov     si, ax                  ; offset.
        shl     dx, X86_SEL_SHIFT
        add     dx, BS3_SEL_TILED
        mov     cx, dx
%else
        ; Just supply a flat selector.
        mov     xSI, xAX
        mov     cx, ds
%endif
        ret

        ;
        ; Internal function for converting a syscall pointer parameter (caller CX:xSI)
        ; to a real mode pointer.
        ;
        ; Returns the result in AX:SI.
        ; @uses xAX, xCX, xDX
        ;
.convert_ptr_arg_to_real_mode_ax_si:
        call    .convert_ptr_arg_to_flat
        mov     si, ax
%if TMPL_BITS == 16
        mov     ax, dx
%else
        shr     eax, 16
%endif
        shl     ax, 12
        ret

        ;
        ; Internal function for the above that wraps the Bs3SelProtFar32ToFlat32 call.
        ;
        ; @returns  eax (32-bit, 64-bit), dx+ax (16-bit).
        ; @uses     eax, ecx, edx
        ;
.convert_ptr_arg_to_flat:
%if TMPL_BITS == 16
        ; Convert to (32-bit) flat address first.
        test    byte VAR_CALLER_MODE, BS3_MODE_CODE_V86
        jz      .convert_ptr_arg_to_flat_prot_16

        mov     ax, VAR_CALLER_CX
        mov     dx, ax
        shl     ax, 4
        shr     dx, 12
        add     ax, VAR_CALLER_SI
        adc     dx, 0
        ret

.convert_ptr_arg_to_flat_prot_16:
        push    es
        push    bx
        push    word VAR_CALLER_CX      ; selector
        xor     ax, ax
        test    byte VAR_CALLER_MODE, BS3_MODE_CODE_16
        jnz     .caller_is_16_bit
        mov     ax, VAR_CALLER_SI_HI
.caller_is_16_bit:
        push    ax                      ; offset high
        push    word VAR_CALLER_SI      ; offset low
        call    Bs3SelProtFar32ToFlat32
        add     sp, 2*3
        pop     bx
        pop     es
        ret

%else ; 32 or 64 bit
        test    byte VAR_CALLER_MODE, BS3_MODE_CODE_V86
        jz      .convert_ptr_arg_to_cx_xSI_prot

        ; Convert real mode address to flat address and return it.
        movzx   eax, word VAR_CALLER_CX
        shl     eax, 4
        movzx   edx, word VAR_CALLER_SI
        add     eax, edx
        ret

        ; Convert to (32-bit) flat address.
.convert_ptr_arg_to_cx_xSI_prot:
 %if TMPL_BITS == 64
        push    r11
        push    r10
        push    r9
        push    r8
        sub     rsp, 10h
 %endif
        movzx   ecx, word VAR_CALLER_CX
        push    xCX
        mov     eax, VAR_CALLER_SI
        test    byte VAR_CALLER_MODE, BS3_MODE_CODE_16
        jz      .no_masking_offset
        and     eax, 0ffffh
.no_masking_offset:
        push    xAX
        BS3_CALL Bs3SelProtFar32ToFlat32,2
        add     xSP, xCB*2 BS3_ONLY_64BIT(+ 10h)
 %if TMPL_BITS == 64
        pop     r8
        pop     r9
        pop     r10
        pop     r11
 %endif
%endif
        ret

BS3_PROC_END_MODE   Bs3TrapSystemCallHandler

