Files
2026-03-26 13:58:48 -07:00

709 lines
19 KiB
NASM

;TITLE cpu.asm: Assembly code for the x64 resources
global DisableInterrupts
global WritePortDword
global WritePortWord
global WritePortByte
global ReadPortDword
global ReadPortWord
global ReadPortByte
global WriteHighCMOSByte
global WriteLowCMOSByte
global SendAPMSMI
global WritePCIByte
global WritePCIWord
global WritePCIDword
global ReadPCIByte
global ReadPCIWord
global ReadPCIDword
global _rdmsr
global _wrmsr
global _load_gdt
global _store_idtr
global _store_gdtr
global _store_ldtr
global _rflags
global ReadCR0
global ReadCR2
global ReadCR3
global ReadCR4
global ReadCR8
global WriteCR0
global WriteCR2
global WriteCR3
global WriteCR4
global WriteCR8
global hypercall
global hypercall_page
global __cpuid__
global __swsmi__
global __swsmi_timed__
extern __x86_return_thunk
extern __x86_indirect_thunk_r11
section .text
%macro RET 0
jmp __x86_return_thunk
%endmacro
%macro SETCTX_CPUID 0
xchg rax, [rdi]
xchg rbx, [rdi+0x8]
xchg rcx, [rdi+0x10]
xchg rdx, [rdi+0x18]
%endmacro
;This function has one argument: CPUID_CTX structure which contain 4 regs: rax, rbx, rcx, rdx:
;------------------------------------------------------------------------------
; void
; __cpuid__ (
; CPUID_CTX ctx
; )
;------------------------------------------------------------------------------
__cpuid__:
SETCTX_CPUID
cpuid
SETCTX_CPUID
RET
;------------------------------------------------------------------------------
; UINT64 _rflags()
;------------------------------------------------------------------------------
_rflags:
pushfq
pop rax
RET
;------------------------------------------------------------------------------
; void _store_idtr(
; unsigned char *address // rdi
; )
;------------------------------------------------------------------------------
_store_idtr:
sidt [rdi]
RET
;------------------------------------------------------------------------------
; void _load_idtr(
; unsigned char *address // rdi
; )
;------------------------------------------------------------------------------
_load_idtr:
lidt [rdi]
RET
;------------------------------------------------------------------------------
; void _store_gdtr(
; unsigned char *address // rdi
; )
;------------------------------------------------------------------------------
_store_gdtr:
sgdt [rdi]
RET
;------------------------------------------------------------------------------
; void _load_gdtr(
; unsigned char *address // rdi
; )
;------------------------------------------------------------------------------
_load_gdtr:
lgdt [rdi]
RET
;------------------------------------------------------------------------------
; void _store_ldtr(
; unsigned char *address // rdi
; )
;------------------------------------------------------------------------------
_store_ldtr:
;sldt fword ptr [rdi]
sldt [rdi]
RET
;------------------------------------------------------------------------------
; void _load_ldtr(
; unsigned char *address // rdi
; )
;------------------------------------------------------------------------------
_load_ldtr:
;lldt fword ptr [rdi]
RET
;------------------------------------------------------------------------------
; void _load_gdt(
; unsigned char *value // rdi
; )
;------------------------------------------------------------------------------
_load_gdt:
sgdt [rdi]
lgdt [rdi]
RET
;------------------------------------------------------------------------------
; void _rdmsr(
; unsigned int msr_num, // rdi
; unsigned int* msr_lo, // rsi
; unsigned int* msr_hi // rdx
; )
;------------------------------------------------------------------------------
_rdmsr:
push r10
push r11
push rax
push rdx
mov rcx, rdi
mov r10, rsi ; msr_lo
mov r11, rdx ; msr_hi
; rcx has msr_num
rdmsr
; Write MSR results in edx:eax
mov [r10], eax
mov [r11], edx
pop rdx
pop rax
pop r11
pop r10
RET
;------------------------------------------------------------------------------
; void _wrmsr(
; unsigned int msr_num, // rdi
; unsigned int msr_hi, // rsi
; unsigned int msr_lo // rdx
; )
;------------------------------------------------------------------------------
_wrmsr:
push rax
push rcx
; move msr_num to rcx
mov rcx, rdi
; move msr_lo from rdx to rax
mov rax, rsi
; move msr_hi to rdx
wrmsr
pop rcx
pop rax
RET
;------------------------------------------------------------------------------
; void
; DisableInterrupts (
; )
;------------------------------------------------------------------------------
DisableInterrupts:
cli
RET
;------------------------------------------------------------------------------
; void
; WritePortDword (
; unsigned int out_value // rdi
; unsigned short port_num // rsi
; )
;------------------------------------------------------------------------------
WritePortDword:
push rax
push rdx
mov rax, rdi
mov rdx, rsi
out dx, eax
pop rdx
pop rax
RET
;------------------------------------------------------------------------------
; void
; WritePortWord (
; unsigned short out_value // rdi
; unsigned short port_num // rsi
; )
;------------------------------------------------------------------------------
WritePortWord:
push rax
push rdx
mov rax, rdi
mov rdx, rsi
out dx, ax
pop rdx
pop rax
RET
;------------------------------------------------------------------------------
; void
; WritePortByte (
; unsigned char out_value // rdi
; unsigned short port_num // rsi
; )
;------------------------------------------------------------------------------
WritePortByte:
push rax
push rdx
mov rax, rdi
mov rdx, rsi
out dx, al
pop rdx
pop rax
RET
;------------------------------------------------------------------------------
; unsigned int
; ReadPortDword (
; unsigned short port_num // rdi
; )
;------------------------------------------------------------------------------
ReadPortDword:
push rdx
xor rax, rax
mov rdx, rdi
in eax, dx
pop rdx
RET
;------------------------------------------------------------------------------
; unsigned short
; ReadPortWord (
; unsigned short port_num // rdi
; )
;------------------------------------------------------------------------------
ReadPortWord:
push rdx
xor rax, rax
mov rdx, rdi
in ax, dx
pop rdx
RET
;------------------------------------------------------------------------------
; unsigned char
; ReadPortByte (
; unsigned short port_num // rdi
; )
;------------------------------------------------------------------------------
ReadPortByte:
push rdx
xor rax, rax
mov rdx, rdi
in al, dx
pop rdx
RET
;------------------------------------------------------------------------------
; void
; WriteHighCMOSByte (
; unsigned char cmos_off // rdi
; unsigned char val // rsi
; )
;------------------------------------------------------------------------------
WriteHighCMOSByte:
push rax
mov rax, rdi
out 72h, al
mov rax, rsi
out 73h, al
pop rax
RET
;------------------------------------------------------------------------------
; void
; WriteLowCMOSByte (
; unsigned char cmos_off // rdi
; unsigned char val // rsi
; )
;------------------------------------------------------------------------------
WriteLowCMOSByte:
push rax
mov rax, rdi
or al, 80h
out 70h, al
mov rax, rsi
out 71h, al
pop rax
RET
; @TODO: looks incorrect
;------------------------------------------------------------------------------
; void
; SendAPMSMI (
; unsigned int apm_port_value // rdi
; IN UINT64 rax_value // rsi
; )
;------------------------------------------------------------------------------
SendAPMSMI:
push rax
push rdx
mov rax, rdi
mov rdx, rsi
mov dx, 0B2h
out dx, eax
pop rdx
pop rax
RET
;------------------------------------------------------------------------------
;This function has one argument: SMI_CTX structure pointer which contain SMI code and data values and 6 regs: rax, rbx, rcx, rdx, rsi, rdi:
; IN UINT64 smi_code_data (only lower 16 bit used)
; IN/OUT UINT64 rax_value
; IN/OUT UINT64 rbx_value
; IN/OUT UINT64 rcx_value
; IN/OUT UINT64 rdx_value
; IN/OUT UINT64 rsi_value
; IN/OUT UINT64 rdi_value
;------------------------------------------------------------------------------
; void
; __swsmi__ (
; SMI_CTX ctx
; )
;------------------------------------------------------------------------------
__swsmi__:
mov r10, rdi
; setting up GPR (arguments) to SMI handler call
; notes:
; RAX will get partially overwritten (AX) by smi_code_data
xchg rax, [r10+08h] ; rax_value (partially overwritten with smi_code_data)
mov ax, [r10+0h] ; smi_code_data
xchg rbx, [r10+010h] ; rbx_value
xchg rcx, [r10+018h] ; rcx_value
xchg rdx, [r10+020h] ; rdx_value
xchg rsi, [r10+028h] ; rsi_value
xchg rdi, [r10+030h] ; rdi_value
; Output smi_code_data split up to their designated ports, SW SMI data
; (0xB3) and SW SMI control (0xB2), respectively.
;
; Resist from outputting both at once as a single word, as some systems
; reject the request if the i/o spans more than a byte, e.g.:
; https://github.com/tianocore/edk2-platforms/blob/aa3f6fd542e99dde4206537b095f1a2201275e75/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmSw.c#L314
ror ax, 8
out 0B3h, al
ror ax, 8
out 0B2h, al
; some SMI handlers return data/errorcode in GPRs, need to return this to the caller
xchg rax, [r10+08h] ; rax_value
xchg rbx, [r10+010h] ; rbx_value
xchg rcx, [r10+018h] ; rcx_value
xchg rdx, [r10+020h] ; rdx_value
xchg rsi, [r10+028h] ; rsi_value
xchg rdi, [r10+030h] ; rdi_value
RET
;------------------------------------------------------------------------------
;This function has two arguments: SMI_CTX structure pointer which contain SMI code and data values and 6 regs: rax, rbx, rcx, rdx, rsi, rdi:
; IN UINT64 smi_code_data (only lower 16 bit used)
; IN/OUT UINT64 rax_value
; IN/OUT UINT64 rbx_value
; IN/OUT UINT64 rcx_value
; IN/OUT UINT64 rdx_value
; IN/OUT UINT64 rsi_value
; IN/OUT UINT64 rdi_value
; The second argument is a pointer to an unsigned long to return the measured execution time
;------------------------------------------------------------------------------
; void
; __swsmi_timed__ (
; SMI_CTX ctx // rdi
; unsigned long *time // rsi
; )
;------------------------------------------------------------------------------
__swsmi_timed__:
mov r10, rdi
push r12 ; callee-saved register
mov r12, rsi
; setting up GPR (arguments) to SMI handler call
; notes:
; RAX will get partially overwritten (AX) by smi_code_data
xchg rax, [r10+08h] ; rax_value (partially overwritten with smi_code_data)
mov ax, [r10+0h] ; smi_code_data
xchg rbx, [r10+010h] ; rbx_value
xchg rcx, [r10+018h] ; rcx_value
xchg rdx, [r10+020h] ; rdx_value
xchg rsi, [r10+028h] ; rsi_value
xchg rdi, [r10+030h] ; rdi_value
; Output smi_code_data split up to their designated ports, SW SMI data
; (0xB3) and SW SMI control (0xB2), respectively.
;
; Resist from outputting both at once as a single word, as some systems
; reject the request if the i/o spans more than a byte, e.g.:
; https://github.com/tianocore/edk2-platforms/blob/aa3f6fd542e99dde4206537b095f1a2201275e75/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmSw.c#L314
ror ax, 8
out 0B3h, al
wbinvd
mfence
; read time-stamp counter
mov r9, rax
mov r11, rdx
rdtsc
shl rdx, 32
or rax, rdx
mov r8, rax
mov rax, r9
mov rdx, r11
; trigger SMI
ror ax, 8
out 0B2h, al
; measure SMI execution time
mov r9, rax
mov r11, rdx
rdtsc
shl rdx, 32
or rax, rdx
sub rax, r8
mov [r12], rax
mov rax, r9
mov rdx, r11
; some SMI handlers return data/errorcode in GPRs, need to return this to the caller
xchg rax, [r10+08h] ; rax_value
xchg rbx, [r10+010h] ; rbx_value
xchg rcx, [r10+018h] ; rcx_value
xchg rdx, [r10+020h] ; rdx_value
xchg rsi, [r10+028h] ; rsi_value
xchg rdi, [r10+030h] ; rdi_value
pop r12
RET
;------------------------------------------------------------------------------
; void
; WritePCIByte (
; unsigned int pci_reg // rdi
; unsigned short cfg_data_port // rsi
; unsigned char byte_value // rdx
; )
;------------------------------------------------------------------------------
WritePCIByte:
push rax
push rsi
push rdx
cli
mov rax, rdi ; pci_reg
mov dx, 0CF8h
out dx, eax
pop rax ; byte_value
pop rdx ; cfg_data_port
out dx, al
sti
pop rax
RET
;------------------------------------------------------------------------------
; void
; WritePCIWord (
; unsigned int pci_reg // rdi
; unsigned short cfg_data_port // rsi
; unsigned short word_value // rdx
; )
;------------------------------------------------------------------------------
WritePCIWord:
push rax
push rsi
push rdx
cli
mov rax, rdi ; pci_reg
mov dx, 0CF8h
out dx, eax
pop rax ; byte_value
pop rdx ; cfg_data_port
out dx, ax
sti
pop rax
RET
;------------------------------------------------------------------------------
; void
; WritePCIDword (
; unsigned int pci_reg // rdi
; unsigned short cfg_data_port // rsi
; unsigned int dword_value // rdx
; )
;------------------------------------------------------------------------------
WritePCIDword:
push rax
push rsi
push rdx
cli
mov rax, rdi ; pci_reg
mov dx, 0CF8h
out dx, eax
pop rax ; byte_value
pop rdx ; cfg_data_port
out dx, eax
sti
pop rax
RET
;------------------------------------------------------------------------------
; unsigned char
; ReadPCIByte (
; unsigned int pci_reg // rdi
; unsigned short cfg_data_port // rsi
; )
;------------------------------------------------------------------------------
ReadPCIByte:
cli
mov rax, rdi ; pci_reg
mov dx, 0CF8h
out dx, eax
xor rax, rax
mov rdx, rsi ; cfg_data_port
in al, dx
sti
RET
;------------------------------------------------------------------------------
; unsigned short
; ReadPCIWord (
; unsigned int pci_reg // rdi
; unsigned short cfg_data_port // rsi
; )
;------------------------------------------------------------------------------
ReadPCIWord:
cli
mov rax, rdi ; pci_reg
mov dx, 0CF8h
out dx, eax
xor rax, rax
mov rdx, rsi ; cfg_data_port
in ax, dx
sti
RET
;------------------------------------------------------------------------------
; unsigned int
; ReadPCIDword (
; unsigned int pci_reg // rdi
; unsigned short cfg_data_port // rsi
; )
;------------------------------------------------------------------------------
ReadPCIDword:
cli
mov rax, rdi ; pci_reg
mov dx, 0CF8h
out dx, eax
xor rax, rax
mov rdx, rsi ; cfg_data_port
in eax, dx
sti
RET
ReadCR0:
xor rax, rax
mov rax, cr0
RET
ReadCR2:
xor rax, rax
mov rax, cr2
RET
ReadCR3:
xor rax, rax
mov rax, cr3
RET
ReadCR4:
xor rax, rax
mov rax, cr4
RET
ReadCR8:
xor rax, rax
mov rax, cr8
RET
WriteCR0:
mov cr0, rdi
RET
WriteCR2:
mov cr2, rdi
RET
WriteCR3:
mov cr3, rdi
RET
WriteCR4:
mov cr4, rdi
RET
WriteCR8:
mov cr8, rdi
RET
;------------------------------------------------------------------------------
; UINT64
; hypercall(
; UINT64 rcx_val, // rdi
; UINT64 rdx_val, // rsi
; UINT64 r8_val, // rdx
; UINT64 r9_val, // rcx
; UINT64 r10_val, // r8
; UINT64 r11_val, // r9
; UINT64 rax_val, // on stack +10h
; UINT64 rbx_val, // on stack +18h
; UINT64 rdi_val, // on stack +20h
; UINT64 rsi_val, // on stack +28h
; UINT64 xmm_buffer, // on stack +30h
; UINT64 hypercall_page // on stack +38h
; )
;------------------------------------------------------------------------------
hypercall:
push rbp
mov rbp, rsp
push rbx
mov rax, qword [rbp + 30h]
test rax, rax
jz hypercall_skip_xmm
pinsrq xmm0, qword [rax + 000h], 00h
pinsrq xmm0, qword [rax + 008h], 01h
pinsrq xmm1, qword [rax + 010h], 00h
pinsrq xmm1, qword [rax + 018h], 01h
pinsrq xmm2, qword [rax + 020h], 00h
pinsrq xmm2, qword [rax + 028h], 01h
pinsrq xmm3, qword [rax + 030h], 00h
pinsrq xmm3, qword [rax + 038h], 01h
pinsrq xmm4, qword [rax + 040h], 00h
pinsrq xmm4, qword [rax + 048h], 01h
pinsrq xmm5, qword [rax + 050h], 00h
pinsrq xmm5, qword [rax + 058h], 01h
hypercall_skip_xmm:
mov r11, r9
mov r10, r8
mov r9, rcx
mov r8, rdx
mov rdx, rsi
mov rcx, rdi
mov rdi, qword [rbp + 20h]
mov rsi, qword [rbp + 28h]
mov rax, qword [rbp + 10h]
mov rbx, qword [rbp + 18h]
mov r11, qword [rbp + 38h]
call __x86_indirect_thunk_r11
pop rbx
pop rbp
RET
;------------------------------------------------------------------------------
; UINT64 hypercall_page ( )
;------------------------------------------------------------------------------
hypercall_page:
vmcall
RET