; $Id: bs3-c16-TrapRmV86Generic.asm 93115 2022-01-01 11:31:46Z vboxsync $
;; @file
; BS3Kit - Trap, 16-bit assembly handlers for real mode and v8086.
;

;
; 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"

%ifndef TMPL_16BIT
 %error "16-bit only template"
%endif


;*********************************************************************************************************************************
;*  External Symbols                                                                                                             *
;*********************************************************************************************************************************
BS3_EXTERN_DATA16 g_bBs3CurrentMode
BS3_EXTERN_DATA16 g_uBs3TrapEipHint
BS3_EXTERN_DATA16 g_uBs3CpuDetected
BS3_EXTERN_DATA16 g_apfnBs3TrapHandlers_c16
TMPL_BEGIN_TEXT
BS3_EXTERN_CMN Bs3TrapDefaultHandler
BS3_EXTERN_CMN Bs3RegCtxRestore
TMPL_BEGIN_TEXT


;;
; Generic entry points for IDT handlers, 8 byte spacing.
;
BS3_PROC_BEGIN _Bs3TrapRmV86GenericEntries
BS3_PROC_BEGIN Bs3TrapRmV86GenericEntries
%macro Bs3TrapRmV86GenericEntryNoErr 1
        push    ax                      ; 1 byte:  Reserve space for fake error cd.     (BP(+2) + 4)
        push    ax                      ; 1 byte:  Save AX                              (BP(+2) + 2)
        mov     ax, i | 00000h          ; 2 bytes: AL = trap/interrupt number; AH=indicate no error code
        jmp     %1                      ; 3 bytes: Jump to handler code
        ALIGNCODE(8)
%assign i i+1
%endmacro

%macro Bs3TrapRmV86GenericEntryErrCd 1
        Bs3TrapRmV86GenericEntryNoErr %1    ; No error code pushed in real mode or V86 mode.
%endmacro

%assign i 0                             ; start counter.
        Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt   ; 0
        Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt   ; 1
        Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt   ; 2
        Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt   ; 3
        Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt   ; 4
        Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt   ; 5
        Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt   ; 6
        Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt   ; 7
        Bs3TrapRmV86GenericEntryErrCd bs3TrapRmV86GenericTrapOrInt   ; 8
        Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt   ; 9
        Bs3TrapRmV86GenericEntryErrCd bs3TrapRmV86GenericTrapOrInt   ; a
        Bs3TrapRmV86GenericEntryErrCd bs3TrapRmV86GenericTrapOrInt   ; b
        Bs3TrapRmV86GenericEntryErrCd bs3TrapRmV86GenericTrapOrInt   ; c
        Bs3TrapRmV86GenericEntryErrCd bs3TrapRmV86GenericTrapOrInt   ; d
        Bs3TrapRmV86GenericEntryErrCd bs3TrapRmV86GenericTrapOrInt   ; e
        Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt   ; f  (reserved)
        Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt   ; 10
        Bs3TrapRmV86GenericEntryErrCd bs3TrapRmV86GenericTrapOrInt   ; 11
        Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt   ; 12
        Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt   ; 13
        Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt   ; 14
        Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt   ; 15 (reserved)
        Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt   ; 16 (reserved)
        Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt   ; 17 (reserved)
        Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt   ; 18 (reserved)
        Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt   ; 19 (reserved)
        Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt   ; 1a (reserved)
        Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt   ; 1b (reserved)
        Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt   ; 1c (reserved)
        Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt   ; 1d (reserved)
        Bs3TrapRmV86GenericEntryErrCd bs3TrapRmV86GenericTrapOrInt   ; 1e
        Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt   ; 1f (reserved)
%rep 224
        Bs3TrapRmV86GenericEntryNoErr bs3TrapRmV86GenericTrapOrInt
%endrep
BS3_PROC_END  Bs3TrapRmV86GenericEntries
AssertCompile(Bs3TrapRmV86GenericEntries_EndProc - Bs3TrapRmV86GenericEntries == 8*256)


;;
; Trap or interrupt with error code, faked if necessary.
;
; early 386+ stack (movzx ebp, sp):
;       [bp + 000h]     ebp
;       [bp + 004h]     ax
;       [bp + 006h]     errcd                   [bp'+0] <--- bp at jmp to common code.
;       [bp + 008h]     cs                      [bp'+2]
;       [bp + 00ah]     ip                      [bp'+4]
;       [bp + 00ch]     flags                   [bp'+6]
;      ([bp + 00eh]     post-iret sp value)     [bp'+8]
;
BS3_PROC_BEGIN _bs3TrapRmV86GenericTrapOrInt
BS3_PROC_BEGIN bs3TrapRmV86GenericTrapOrInt
CPU 386
        jmp     near bs3TrapRmV86GenericTrapErrCode8086 ; Bs3TrapRmV86Init adjusts this on 80386+
        push    ebp
        movzx   ebp, sp
        push    ebx                     ; BP - 04h
        pushfd                          ; BP - 08h
        cld
        push    edx                     ; BP - 0ch
        push    ss                      ; BP - 0eh
        push    esp                     ; BP - 12h

        ; Reserve space for the register and trap frame.
        mov     bx, (BS3TRAPFRAME_size + 7) / 8
.more_zeroed_space:
        push    0
        push    0
        push    0
        push    0
        dec     bx
        jnz     .more_zeroed_space
        movzx   ebx, sp


        mov     edx, [bp - 12h]
        mov     [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rsp], edx     ; high bits
        mov     [ss:bx + BS3TRAPFRAME.uHandlerRsp], edx             ; high bits
        mov     dx, [bp - 0eh]
        mov     [ss:bx + BS3TRAPFRAME.uHandlerSs], dx
        mov     edx, [bp - 0ch]
        mov     [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rdx], edx
        mov     edx, [bp - 8]
        mov     [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rflags], edx  ; high bits
        mov     [ss:bx + BS3TRAPFRAME.fHandlerRfl], edx
        mov     edx, [bp - 4]
        mov     [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rbx], edx
        mov     edx, [bp]
        mov     [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rbp], edx
        mov     edx, eax                                            ; high bits
        mov     dx, [bp + 4]
        mov     [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rax], edx

        mov     [ss:bx + BS3TRAPFRAME.bXcpt], al

        test    ah, 0ffh
        jz      .no_error_code
        mov     dx, [bp + 6]
        mov     [ss:bx + BS3TRAPFRAME.uErrCd], dx
.no_error_code:

        add     bp, 6                   ; adjust so it points to the word before the iret frame.
        xor     dx, dx
        jmp     bs3TrapRmV86GenericCommon
BS3_PROC_END   bs3TrapRmV86GenericTrapErrCode

;;
; Trap with error code - 8086/V20/80186/80286 code variant.
;
BS3_PROC_BEGIN bs3TrapRmV86GenericTrapErrCode8086
CPU 8086
        push    bp
        mov     bp, sp
        push    bx                      ; BP - 2
        pushf                           ; BP - 4
        push    ax                      ; BP - 6
        cld

        ; Reserve space for the register and trap frame.
        mov     bx, (BS3TRAPFRAME_size + 7) / 8
        xor     ax, ax
.more_zeroed_space:
        push    ax
        push    ax
        push    ax
        push    ax
        dec     bx
        jnz     .more_zeroed_space
        mov     bx, sp

        mov     [ss:bx + BS3TRAPFRAME.uHandlerSs], ss
        mov     [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rdx], dx
        mov     dx, [bp - 4]
        mov     [ss:bx + BS3TRAPFRAME.fHandlerRfl], dx
        mov     dx, [bp - 2]
        mov     [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rbx], dx
        mov     dx, [bp]
        mov     [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rbp], dx

        mov     dx, [bp + 2]
        mov     [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rax], dx

        mov     ax, [bp - 6]
        mov     [ss:bx + BS3TRAPFRAME.bXcpt], al

        test    ah, 0ffh
        jz      .no_error_code
        mov     dx, [bp + 4]
        mov     [ss:bx + BS3TRAPFRAME.uErrCd], dx
.no_error_code:

        add     bp, 4                   ; adjust so it points to the word before the iret frame.
        mov     dl, 1
        jmp     bs3TrapRmV86GenericCommon
BS3_PROC_END   bs3TrapRmV86GenericTrapErrCode8086


;;
; Common context saving code and dispatching.
;
; @param    ss:bx   Pointer to the trap frame, zero filled.  The following members
;                   have been filled in by the previous code:
;                       - bXcpt
;                       - uErrCd
;                       - fHandlerRFL
;                       - Ctx.eax
;                       - Ctx.edx
;                       - Ctx.ebx
;                       - Ctx.ebp
;                       - Ctx.rflags - high bits only.
;                       - Ctx.esp    - high bits only.
;                       - All other bytes are zeroed.
;
; @param    bp      Pointer to the word before the iret frame, i.e. where bp
;                   would be saved if this was a normal near call.
; @param    dx      One (1) if 286, zero (0) if 386+.
;
BS3_PROC_BEGIN bs3TrapRmV86GenericCommon
CPU 8086
        ;
        ; Fake EBP frame.
        ;
        mov     ax, [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rbp]
        mov     [bp], ax

        ;
        ; Save the remaining GPRs and segment registers.
        ;
        test    dx, dx
        jnz     .save_word_grps
CPU 386
        mov     [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rcx], ecx
        mov     [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rdi], edi
        mov     [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rsi], esi
        mov     [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.fs], fs
        mov     [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.gs], gs
        jmp     .save_segment_registers
.save_word_grps:
CPU 8086
        mov     [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rcx], cx
        mov     [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rdi], di
        mov     [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rsi], si
.save_segment_registers:
        mov     [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.ds], ds
        mov     [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.es], es
        mov     [ss:bx + BS3TRAPFRAME.uHandlerCs], cs

        ;
        ; Load 16-bit BS3KIT_GRPNM_DATA16 into DS and ES so we can access globals.
        ;
        mov     ax, BS3KIT_GRPNM_DATA16
        mov     ds, ax
        mov     es, ax

        ;
        ; Copy the mode now that we've got a flat DS.  We don't need to update
        ; it as it didn't change.
        ;
        mov     al, [BS3_DATA16_WRT(g_bBs3CurrentMode)]
        mov     [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.bMode], al

        ;
        ; Copy iret info.
        ;
        lea     cx, [bp + 2]
        mov     [ss:bx + BS3TRAPFRAME.uHandlerRsp], cx
        mov     cx, [bp + 2]
        mov     [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rip], cx
        mov     cx, [bp + 6]
        mov     [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rflags], cx
        mov     cx, [bp + 4]
        mov     [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.cs], cx
        mov     cx, ss
        mov     [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.ss], cx
        lea     cx, [bp + 8]
        mov     [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rsp], cx
        mov     byte [ss:bx + BS3TRAPFRAME.cbIretFrame], 3*2

        ; The VM flag and CPL.
        test    al, BS3_MODE_CODE_V86
        jz      .dont_set_vm
        or      byte [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.rflags + 2], X86_EFL_VM >> 16
        mov     byte [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.bCpl], 3
.dont_set_vm:


        ;
        ; Control registers.
        ;
        ; Since we're in real or v8086 here, we cannot save TR and LDTR.
        ; But get MSW (CR0) first since that's always accessible and we
        ; need it even on a 386 to check whether we're in v8086 mode or not.
        ;
        cmp     byte [BS3_DATA16_WRT(g_uBs3CpuDetected)], BS3CPU_80286
        jb      .skip_control_registers_because_80186_or_older
CPU 286
        smsw    ax
        mov     [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.cr0], ax

        test    dx, dx
        jnz     .set_flags
.save_386_control_registers:
CPU 386
        ; 386 control registers are not accessible from virtual 8086 mode.
        test    al, X86_CR0_PE
        jnz     .skip_crX_because_v8086
        mov     eax, cr0
        mov     [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.cr0], eax
        mov     eax, cr2
        mov     [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.cr2], eax
        mov     eax, cr3
        mov     [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.cr3], eax

        test    byte [1 + BS3_DATA16_WRT(g_uBs3CpuDetected)], (BS3CPU_F_CPUID >> 8) ; CR4 first appeared in later 486es.
        jz      .skip_cr4_because_not_there
        mov     eax, cr4
        mov     [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.cr4], eax
        jmp     .set_flags

.skip_cr4_because_not_there:
        mov     byte [edi + BS3TRAPFRAME.Ctx + BS3REGCTX.fbFlags], BS3REG_CTX_F_NO_CR4
        jmp     .set_flags

CPU 8086
.skip_control_registers_because_80186_or_older:
.skip_crX_because_v8086:
        or      byte [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.fbFlags], \
                BS3REG_CTX_F_NO_CR0_IS_MSW | BS3REG_CTX_F_NO_CR2_CR3 | BS3REG_CTX_F_NO_CR4
.set_flags:                             ; The double fault code joins us here.
        or      byte [ss:bx + BS3TRAPFRAME.Ctx + BS3REGCTX.fbFlags], BS3REG_CTX_F_NO_AMD64 | BS3REG_CTX_F_NO_TR_LDTR

        ;
        ; Dispatch it to C code.
        ;
.dispatch_to_handler:
        mov     di, bx
        mov     bl, byte [ss:bx + BS3TRAPFRAME.bXcpt]
        mov     bh, 0
        shl     bx, 1
        mov     bx, [bx + BS3_DATA16_WRT(_g_apfnBs3TrapHandlers_c16)]
        or      bx, bx
        jnz     .call_handler
        mov     bx, Bs3TrapDefaultHandler
.call_handler:
        push    ss
        push    di
        call    bx

        ;
        ; Resume execution using trap frame.
        ;
        xor     ax, ax
        push    ax
        push    ss
        add     di, BS3TRAPFRAME.Ctx
        push    di
        call    Bs3RegCtxRestore
.panic:
        hlt
        jmp     .panic
BS3_PROC_END   bs3TrapRmV86GenericCommon

