CloverBootloader/MemoryFix/AptioMemoryFix/X64/AsmRtShims.nasm

301 lines
9.7 KiB
NASM

;------------------------------------------------------------------------------
;
; Functions to wrap RuntimeServices code that needs to be written to
;
; by Download-Fritz & vit9696
; refactored by Zenith432
;------------------------------------------------------------------------------
BITS 64
DEFAULT REL
; Constructs a shim with write protection patch avoiding
; a conflict with XNU W^X mapping.
; The argument marks a number of args the func takes (1~5).
%macro ConstructShim 1
%if %1 > 5
%error "At Most 5 Args Supported."
%endif
cmp qword [ASM_PFX(gRequiresWriteUnprotect)], 0
jz .SKIP_WRITE_UNPROTECT
push rsi
push rbx
sub rsp, 0x28
pushfq
cli
pop rsi
push rax
mov rbx, cr0
mov rax, rbx
and rax, 0xfffffffffffeffff
mov cr0, rax
%if %1 > 4
mov rax, qword [rsp+0x68]
mov qword [rsp+0x28], rax
%endif
pop rax
call rax
add rsp, 0x28
test ebx, 0x10000
je .SKIP_RESTORE_WP
mov cr0, rbx
.SKIP_RESTORE_WP:
pop rbx
test si, 0x200
pop rsi
je .SKIP_RESTORE_INTR
sti
.SKIP_RESTORE_INTR:
ret
.SKIP_WRITE_UNPROTECT:
jmp rax
%endmacro
; Redirects Boot prefixed variables from gBootVariableGuid
; to gRedirectVariableGuid.
; Variable name is assumed to be in %rcx.
; Guid is assumed to be in %rdx.
; Temporary registers: %rax.
%macro PerformBootVariableRedirect 0
; Check if we have variable redirection enabled.
mov rax, qword [ASM_PFX(gBootVariableRedirect)]
test rax, rax
jz .SKIP_BOOT_VARIABLE_REDIRECT
; Compare whether GUID matches gBootVariableGuid
mov rax, qword [rdx]
cmp qword [ASM_PFX(gBootVariableGuid)], rax
jnz .SKIP_BOOT_VARIABLE_REDIRECT
mov rax, qword [rdx+8]
cmp qword [ASM_PFX(gBootVariableGuid)+8], rax
jnz .SKIP_BOOT_VARIABLE_REDIRECT
; Compare whether variable prefix matches Boot
mov ax, word [rcx]
cmp ax, 'B'
jnz .SKIP_BOOT_VARIABLE_REDIRECT
mov ax, word [rcx+2]
cmp ax, 'o'
jnz .SKIP_BOOT_VARIABLE_REDIRECT
mov ax, word [rcx+4]
cmp ax, 'o'
jnz .SKIP_BOOT_VARIABLE_REDIRECT
mov ax, word [rcx+6]
cmp ax, 't'
jnz .SKIP_BOOT_VARIABLE_REDIRECT
; This is a Boot prefixed variable from gBootVariableGuid.
; Redirect it to gRedirectVariableGuid.
lea rdx, [ASM_PFX(gRedirectVariableGuid)]
.SKIP_BOOT_VARIABLE_REDIRECT:
%endmacro
SECTION .text
ALIGN 8 ; to align the dqs
global ASM_PFX(gRtShimsDataStart)
ASM_PFX(gRtShimsDataStart):
global ASM_PFX(RtShimsReturnInvalidParameter)
ASM_PFX(RtShimsReturnInvalidParameter):
mov rax, 0x8000000000000002
ret
global ASM_PFX(RtShimsReturnSecurityViolation)
ASM_PFX(RtShimsReturnSecurityViolation):
mov rax, 0x800000000000001A
ret
global ASM_PFX(RtShimSetVariable)
ASM_PFX(RtShimSetVariable):
; For performance and simplicity do initial validation ourselves.
test rcx, rcx
jz ASM_PFX(RtShimsReturnInvalidParameter) ; VariableName is NULL
test rdx, rdx
jz ASM_PFX(RtShimsReturnInvalidParameter) ; VendorGuid is NULL
.INITIAL_VALIDATION_OVER:
PerformBootVariableRedirect
; Once boot.efi virtualizes the pointers we should protect read-only
; variables from writing.
mov rax, qword [ASM_PFX(gGetVariableOverride)]
test rax, rax
jnz .SKIP_ACCESS_CHECK
; We have a virtualized pointer, so we also need to protect write-only
; variables from reading. Compare VendorGuid against gReadOnlyVariableGuid
; and return EFI_SECURITY_VIOLATION on equals.
mov rax, qword [rdx]
cmp qword [ASM_PFX(gReadOnlyVariableGuid)], rax
jnz .SKIP_ACCESS_CHECK
mov rax, qword [rdx+8]
cmp qword [ASM_PFX(gReadOnlyVariableGuid)+8], rax
jz ASM_PFX(RtShimsReturnSecurityViolation)
.SKIP_ACCESS_CHECK:
mov rax, qword [ASM_PFX(gSetVariable)]
jmp FiveArgsShim
global ASM_PFX(RtShimGetVariable)
ASM_PFX(RtShimGetVariable):
; For performance and simplicity do initial validation ourselves.
test rcx, rcx
jz ASM_PFX(RtShimsReturnInvalidParameter) ; VariableName is NULL
test rdx, rdx
jz ASM_PFX(RtShimsReturnInvalidParameter) ; VendorGuid is NULL
test r9, r9
jz ASM_PFX(RtShimsReturnInvalidParameter) ; DataSize is NULL
cmp qword [rsp+0x28], 0
jnz .INITIAL_VALIDATION_OVER ; Data is not NULL
mov rax, qword [r9]
test rax, rax
jnz ASM_PFX(RtShimsReturnInvalidParameter) ; Data is NULL and *DataSize is not 0
.INITIAL_VALIDATION_OVER:
PerformBootVariableRedirect
; Once boot.efi virtualizes the pointers we should protect write-only
; variables from reading. Prior to that a custom wrapper is used.
mov rax, qword [ASM_PFX(gGetVariableOverride)]
test rax, rax
jnz .SKIP_ACCESS_CHECK_WRAPPER
; We have a virtualized pointer, so we also need to protect write-only
; variables from reading. Compare VendorGuid against gWriteOnlyVariableGuid
; and return EFI_SECURITY_VIOLATION on equals.
mov rax, qword [rdx]
cmp qword [ASM_PFX(gWriteOnlyVariableGuid)], rax
jnz .SKIP_ACCESS_CHECK_INTERNAL
mov rax, qword [rdx+8]
cmp qword [ASM_PFX(gWriteOnlyVariableGuid)+8], rax
jz ASM_PFX(RtShimsReturnSecurityViolation)
.SKIP_ACCESS_CHECK_INTERNAL:
mov rax, qword [ASM_PFX(gGetVariable)]
.SKIP_ACCESS_CHECK_WRAPPER:
;jmp short FiveArgsShim
; fall through to FiveArgsShim
FiveArgsShim:
ConstructShim 5
global ASM_PFX(RtShimGetNextVariableName)
ASM_PFX(RtShimGetNextVariableName):
; TODO: I am not sure whether we need GetNextVariableName support
; for boot variable routing... Probably good enough without it.
mov rax, qword [ASM_PFX(gGetNextVariableName)]
jmp short FourArgsShim
global ASM_PFX(RtShimGetTime)
ASM_PFX(RtShimGetTime):
; On old AMI firmwares (like the one found in GA-Z87X-UD4H) there is a chance
; of getting 2047 (EFI_UNSPECIFIED_TIMEZONE) from GetTime. This is valid,
; yet is disliked by some software including but not limited to UEFI Shell.
; See the patch: https://lists.01.org/pipermail/edk2-devel/2018-May/024534.html
; As a workaround we make sure this does not happen at all.
push rsi
push rbx
push rcx ; Save the original EFI_TIME pointer
sub rsp, 0x20
pushfq
cli
pop rsi
mov rbx, cr0
mov rax, rbx
and rax, 0xfffffffffffeffff
mov cr0, rax
mov rax, qword [ASM_PFX(gGetTime)]
call rax
add rsp, 0x20
test ebx, 0x10000
je .SKIP_RESTORE_WP
mov cr0, rbx
.SKIP_RESTORE_WP:
pop rbx ; load saved EFI_TIME pointer
test rax, rax ; check for EFI_ERROR
js .SKIP_CORRECT_TIMEZONE
cmp word [rbx + 12], 2047 ; offsetof(EFI_TIME, TimeZone)
jnz .SKIP_CORRECT_TIMEZONE
mov word [rbx + 12], 0 ; default to UTC
.SKIP_CORRECT_TIMEZONE:
pop rbx
test si, 0x200
pop rsi
je .SKIP_RESTORE_INTR
sti
.SKIP_RESTORE_INTR:
ret
global ASM_PFX(RtShimSetTime)
ASM_PFX(RtShimSetTime):
mov rax, qword [ASM_PFX(gSetTime)]
jmp short FourArgsShim
global ASM_PFX(RtShimGetWakeupTime)
ASM_PFX(RtShimGetWakeupTime):
mov rax, qword [ASM_PFX(gGetWakeupTime)]
jmp short FourArgsShim
global ASM_PFX(RtShimSetWakeupTime)
ASM_PFX(RtShimSetWakeupTime):
mov rax, qword [ASM_PFX(gSetWakeupTime)]
jmp short FourArgsShim
global ASM_PFX(RtShimGetNextHighMonoCount)
ASM_PFX(RtShimGetNextHighMonoCount):
mov rax, qword [ASM_PFX(gGetNextHighMonoCount)]
jmp short FourArgsShim
global ASM_PFX(RtShimResetSystem)
ASM_PFX(RtShimResetSystem):
mov rax, qword [ASM_PFX(gResetSystem)] ; Note - doesn't return!
;jmp short FourArgsShim
; fall through to FourArgsShim
FourArgsShim:
ConstructShim 4
ALIGN 8
global ASM_PFX(gRequiresWriteUnprotect)
ASM_PFX(gRequiresWriteUnprotect): dq 0
global ASM_PFX(gBootVariableRedirect)
ASM_PFX(gBootVariableRedirect): dq 0
global ASM_PFX(gGetNextVariableName)
ASM_PFX(gGetNextVariableName): dq 0
global ASM_PFX(gGetVariable)
ASM_PFX(gGetVariable): dq 0
global ASM_PFX(gSetVariable)
ASM_PFX(gSetVariable): dq 0
global ASM_PFX(gGetTime)
ASM_PFX(gGetTime): dq 0
global ASM_PFX(gSetTime)
ASM_PFX(gSetTime): dq 0
global ASM_PFX(gGetWakeupTime)
ASM_PFX(gGetWakeupTime): dq 0
global ASM_PFX(gSetWakeupTime)
ASM_PFX(gSetWakeupTime): dq 0
global ASM_PFX(gGetNextHighMonoCount)
ASM_PFX(gGetNextHighMonoCount): dq 0
global ASM_PFX(gResetSystem)
ASM_PFX(gResetSystem): dq 0
global ASM_PFX(gGetVariableOverride)
ASM_PFX(gGetVariableOverride): dq 0
global ASM_PFX(gBootVariableGuid)
ASM_PFX(gBootVariableGuid): times 2 dq 0
global ASM_PFX(gRedirectVariableGuid)
ASM_PFX(gRedirectVariableGuid): times 2 dq 0
global ASM_PFX(gReadOnlyVariableGuid)
ASM_PFX(gReadOnlyVariableGuid): times 2 dq 0
global ASM_PFX(gWriteOnlyVariableGuid)
ASM_PFX(gWriteOnlyVariableGuid): times 2 dq 0
global ASM_PFX(gRtShimsDataEnd)
ASM_PFX(gRtShimsDataEnd):