root/sys/arch/i386/include/frameasm.h
/*      $NetBSD: frameasm.h,v 1.36 2026/01/17 10:59:10 bouyer Exp $     */

#ifndef _I386_FRAMEASM_H_
#define _I386_FRAMEASM_H_

#ifdef _KERNEL_OPT
#include "opt_multiprocessor.h"
#include "opt_xen.h"
#endif


#ifdef XEN
/* XXX assym.h */
#define TRAP_INSTR      int     $0x82
#endif /* XEN */

#if defined(XENPV)
/*
 * acessing EVTCHN_UPCALL_MASK is safe only if preemption is disabled, i.e.:
 * l_nopreempt is not 0, or
 * ci_ilevel is not 0, or
 * EVTCHN_UPCALL_MASK is not 0
 * ci_idepth is not negative
 */
#ifdef DIAGNOSTIC
#define CLI(reg)        \
                        movl    CPUVAR(CURLWP),reg ;  \
                        cmpl    $0, L_NOPREEMPT(reg); \
                        jne     199f; \
                        cmpb    $0, CPUVAR(ILEVEL); \
                        jne     199f; \
                        movl    CPUVAR(IDEPTH), reg; \
                        test    reg, reg; \
                        jns     199f; \
                        movl    $panicstr,reg ; \
                        cmpl    $0, 0(reg); \
                        jne     199f; \
                        pushl   $199f; \
                        movl    $_C_LABEL(cli_panic), reg; \
                        pushl   0(reg); \
                        call    _C_LABEL(panic); \
                        addl    $8,%esp; \
199:                    movl    CPUVAR(VCPU),reg ;  \
                        movb    $1,EVTCHN_UPCALL_MASK(reg)

#define STI(reg) \
                        movl    CPUVAR(VCPU),reg ;  \
                        cmpb    $0, EVTCHN_UPCALL_MASK(reg) ; \
                        jne     198f ; \
                        movl    $panicstr,reg ; \
                        cmpl    $0, 0(reg); \
                        jne     198f; \
                        pushl   $198f; \
                        movl    $_C_LABEL(sti_panic), reg; \
                        pushl   0(reg); \
                        call    _C_LABEL(panic); \
                        addl    $8,%esp; \
198:                    movb    $0,EVTCHN_UPCALL_MASK(reg)


/*
 * Here we have a window where we could be migrated between enabling
 * interrupts and testing * EVTCHN_UPCALL_PENDING. But it's not a big issue,
 * at worst we'll call stipending() on the new CPU which have no pending
 * interrupts, and the pending interrupts on the old CPU have already
 * been processed.
 */
#define STIC(reg) \
                        movl    CPUVAR(VCPU),reg ;  \
                        cmpb    $0, EVTCHN_UPCALL_MASK(reg) ; \
                        jne     197f ; \
                        movl    $panicstr,reg ; \
                        cmpl    $0, 0(reg); \
                        jne     197f; \
                        pushl   $197f; \
                        movl    $_C_LABEL(sti_panic), reg; \
                        pushl   0(reg); \
                        call    _C_LABEL(panic); \
                        addl    $8,%esp; \
197:                    movl    CPUVAR(VCPU),reg ;  \
                        movb    $0,EVTCHN_UPCALL_MASK(reg); \
                        testb   $0xff,EVTCHN_UPCALL_PENDING(reg)

#else
#define CLI(reg)        \
                        movl    CPUVAR(VCPU),reg ;  \
                        movb    $1,EVTCHN_UPCALL_MASK(reg)

#define STI(reg) \
                        movl    CPUVAR(VCPU),reg ;  \
                        movb    $0,EVTCHN_UPCALL_MASK(reg)

#define STIC(reg) \
                        movl    CPUVAR(VCPU),reg ;  \
                        movb    $0,EVTCHN_UPCALL_MASK(reg); \
                        testb   $0xff,EVTCHN_UPCALL_PENDING(reg)

#endif /* DIAGNOSTIC */
#define CLI2(reg, reg2) \
                        movl    CPUVAR(CURLWP),reg; \
                        incl    L_NOPREEMPT(reg); \
                        movl    CPUVAR(VCPU),reg2 ;  \
                        movb    $1,EVTCHN_UPCALL_MASK(reg2); \
                        decl    L_NOPREEMPT(reg);

#define PUSHFCLI(reg, reg2) \
                        movl    CPUVAR(CURLWP),reg; \
                        incl    L_NOPREEMPT(reg); \
                        movl    CPUVAR(VCPU),reg2 ;  \
                        movzbl  EVTCHN_UPCALL_MASK(reg2), reg2; \
                        pushl   reg2 ; \
                        movl    CPUVAR(VCPU),reg2 ;  \
                        movb    $1,EVTCHN_UPCALL_MASK(reg2); \
                        decl    L_NOPREEMPT(reg);

#define POPF(reg)       call _C_LABEL(xen_write_psl); \
                        addl    $4,%esp
#else
#define CLI(reg)        cli
#define CLI2(reg, reg2) cli
#define STI(reg)        sti
#define PUSHFCLI(reg, reg2) pushf ; cli
#define POPF(reg)       popf

#endif /* XENPV */

#define HP_NAME_CLAC            1
#define HP_NAME_STAC            2
#define HP_NAME_NOLOCK          3
#define HP_NAME_RETFENCE        4
#define HP_NAME_CAS_64          5
#define HP_NAME_SPLLOWER        6
#define HP_NAME_MUTEX_EXIT      7

#define HOTPATCH(name, size) \
123:                                            ; \
        .pushsection    .rodata.hotpatch, "a"   ; \
        .byte           name                    ; \
        .byte           size                    ; \
        .long           123b                    ; \
        .popsection

#define SMAP_ENABLE \
        HOTPATCH(HP_NAME_CLAC, 3)               ; \
        .byte 0x90, 0x90, 0x90

#define SMAP_DISABLE \
        HOTPATCH(HP_NAME_STAC, 3)               ; \
        .byte 0x90, 0x90, 0x90

/*
 * These are used on interrupt or trap entry or exit.
 */
#define INTRENTRY \
        SMAP_ENABLE                     ; \
        subl    $TF_PUSHSIZE,%esp       ; \
        movw    %gs,TF_GS(%esp)         ; \
        movw    %fs,TF_FS(%esp)         ; \
        movl    %eax,TF_EAX(%esp)       ; \
        movw    %es,TF_ES(%esp)         ; \
        movw    %ds,TF_DS(%esp)         ; \
        movl    $GSEL(GDATA_SEL, SEL_KPL),%eax  ; \
        movl    %edi,TF_EDI(%esp)       ; \
        movl    %esi,TF_ESI(%esp)       ; \
        movw    %ax,%ds                 ; \
        movl    %ebp,TF_EBP(%esp)       ; \
        movw    %ax,%es                 ; \
        movl    %ebx,TF_EBX(%esp)       ; \
        movw    %ax,%gs                 ; \
        movl    %edx,TF_EDX(%esp)       ; \
        movl    $GSEL(GCPU_SEL, SEL_KPL),%eax   ; \
        movl    %ecx,TF_ECX(%esp)       ; \
        movl    %eax,%fs                ; \
        cld

#define INTRFASTEXIT \
        jmp     intrfastexit

#define INTR_RECURSE_HWFRAME \
        pushfl                          ; \
        pushl   %cs                     ; \
        pushl   %esi                    ;

#define CHECK_DEFERRED_SWITCH \
        cmpl    $0, CPUVAR(WANT_PMAPLOAD)

#define CHECK_ASTPENDING(reg)   movl    CPUVAR(CURLWP),reg      ; \
                                cmpl    $0, L_MD_ASTPENDING(reg)
#define CLEAR_ASTPENDING(reg)   movl    $0, L_MD_ASTPENDING(reg)

/*
 * If the FPU state is not in the CPU, restore it. Executed with interrupts
 * disabled.
 *
 *     %ebx must not be modified
 */
#define HANDLE_DEFERRED_FPU     \
        movl    CPUVAR(CURLWP),%eax                     ; \
        testl   $MDL_FPU_IN_CPU,L_MD_FLAGS(%eax)        ; \
        jnz     1f                                      ; \
        pushl   %eax                                    ; \
        call    _C_LABEL(fpu_handle_deferred)           ; \
        popl    %eax                                    ; \
        orl     $MDL_FPU_IN_CPU,L_MD_FLAGS(%eax)        ; \
1:

/*
 * IDEPTH_INCR:
 * increase ci_idepth and switch to the interrupt stack if necessary.
 * note that the initial value of ci_idepth is -1.
 *
 * => should be called with interrupt disabled.
 * => save the old value of %esp in %eax.
 */

#define IDEPTH_INCR \
        incl    CPUVAR(IDEPTH); \
        movl    %esp, %eax; \
        jne     999f; \
        movl    CPUVAR(INTRSTACK), %esp; \
999:    pushl   %eax; /* eax == pointer to intrframe */ \

/*
 * IDEPTH_DECR:
 * decrement ci_idepth and switch back to
 * the original stack saved by IDEPTH_INCR.
 *
 * => should be called with interrupt disabled.
 */

#define IDEPTH_DECR \
        popl    %esp; \
        decl    CPUVAR(IDEPTH)

#endif /* _I386_FRAMEASM_H_ */