stage0-uefi/amd64/Development/M0.M1

1222 lines
41 KiB
Plaintext

# SPDX-FileCopyrightText: 2022 Andrius Štikonas <andrius@stikonas.eu>
# SPDX-FileCopyrightText: 2017 Jeremiah Orians <jeremiah@pdp10.guru>
#
# SPDX-License-Identifier: GPL-3.0-or-later
DEFINE add_al, 04
DEFINE add_ebx, 83C3
DEFINE add_rax, 4805
DEFINE add_rbx, 4881C3
DEFINE add_rcx, 4881C1
DEFINE add_rdx, 4881C2
DEFINE add_rsp, 4883C4
DEFINE add_rax,rcx 4801C8
DEFINE add_r12,rax 4901C4
DEFINE add_rbx,[rdi+BYTE] 48035F
DEFINE and_rax, 4883E0
DEFINE and_rsp, 4883E4
DEFINE cmp_al, 3C
DEFINE cmp_rax, 483D
DEFINE cmp_rbx, 4881FB
DEFINE cmp_rcx, 4881F9
DEFINE cmp_rdx, 4881FA
DEFINE cmp_rsi, 4881FE
DEFINE cmp_r8, 4983F8
DEFINE cmp_rax,rbx 4839D8
DEFINE cmp_rax,rcx 4839C8
DEFINE cmp_rax,r12 4C39E0
DEFINE cmp_rbx,rdx 4839D3
DEFINE cmp_rcx,rbx 4839D9
DEFINE call E8
DEFINE call_[rcx+BYTE] FF51
DEFINE call_[r14+BYTE] 41FF56
DEFINE call_[r14+DWORD] 41FF96
DEFINE dec_rax 48FFC8
DEFINE imul_rax, 486BC0
DEFINE jbe8 76
DEFINE je8 74
DEFINE jg8 7F
DEFINE jl8 7C
DEFINE jmp8 EB
DEFINE jne8 75
DEFINE je 0F84
DEFINE jl 0F8C
DEFINE jmp E9
DEFINE jne 0F85
DEFINE lea_rbx,[rip+DWORD] 488D1D
DEFINE lea_rdx,[rip+DWORD] 488D15
DEFINE mov_rax, 48C7C0
DEFINE mov_rcx, 48C7C1
DEFINE mov_rdx, 48C7C2
DEFINE mov_rsi, 48C7C6
DEFINE mov_rax,rbx 4889D8
DEFINE mov_rax,rcx 4889C8
DEFINE mov_rax,rdx 4889D0
DEFINE mov_rax,r13 4C89E8
DEFINE mov_rbx,rax 4889C3
DEFINE mov_rbx,rcx 4889CB
DEFINE mov_rbx,r13 4C89EB
DEFINE mov_rbp,rsp 4889E5
DEFINE mov_rcx,rax 4889C1
DEFINE mov_rcx,rbx 4889D9
DEFINE mov_rcx,r8 4C89C1
DEFINE mov_rcx,r9 4C89C9
DEFINE mov_rdi,rax 4889C7
DEFINE mov_rdx,rax 4889C2
DEFINE mov_rdx,rbx 4889DA
DEFINE mov_rdx,rsp 4889E2
DEFINE mov_rsp,rbp 4889EC
DEFINE mov_r8,rsp 4989E0
DEFINE mov_r8,r12 4D89E0
DEFINE mov_r12,rax 4989C4
DEFINE mov_r13,rax 4989C5
DEFINE mov_r13,rdx 4989D5
DEFINE mov_al,[rbx] 8A03
DEFINE mov_al,[rbx+rcx] 8A040B
DEFINE mov_al,[rcx] 8A01
DEFINE mov_al,[rdx] 8A02
DEFINE mov_bl,[rax] 8A18
DEFINE mov_bl,[rdx] 8A1A
DEFINE mov_cl,[rax] 8A08
DEFINE mov_cl,[rbx] 8A0B
DEFINE mov_cl,[rbx+BYTE] 8A4B
DEFINE mov_rax,[rax] 488B00
DEFINE mov_rax,[rcx] 488B01
DEFINE mov_rbx,[rbx] 488B1B
DEFINE mov_rcx,[rbx] 488B0B
DEFINE mov_rcx,[rcx] 488B09
DEFINE mov_rdx,[rdx] 488B12
DEFINE mov_[ebx],al 678803
DEFINE mov_[rbx], C603
DEFINE mov_[rdx],cl 880A
DEFINE mov_[rbx],rax 488903
DEFINE mov_[rcx],rax 488901
DEFINE mov_[rdx],r13 4C892A
DEFINE mov_[rax],BYTE C600
DEFINE mov_rax,[rax+BYTE] 488B40
DEFINE mov_rax,[rbx+BYTE] 488B43
DEFINE mov_rax,[rcx+BYTE] 488B41
DEFINE mov_rax,[rdx+BYTE] 488B42
DEFINE mov_rbx,[rax+BYTE] 488B58
DEFINE mov_rbx,[rcx+BYTE] 488B59
DEFINE mov_rbx,[rdi+BYTE] 488B5F
DEFINE mov_rcx,[rax+BYTE] 488B48
DEFINE mov_rcx,[rdi+BYTE] 488B4F
DEFINE mov_rsp,[rsp+BYTE] 488B6424
DEFINE mov_r14,[rdx+BYTE] 4C8B72
DEFINE mov_rcx,[rip+DWORD] 488B0D
DEFINE mov_r8,[rip+DWORD] 4C8B05
DEFINE mov_r9,[rip+DWORD] 4C8B0D
DEFINE mov_[rax+BYTE],rbx 488958
DEFINE mov_[rcx+BYTE],rax 488941
DEFINE mov_[rcx+BYTE],rbx 488959
DEFINE mov_[rdx+BYTE],rax 488942
DEFINE mov_[rdx+BYTE],rcx 48894A
DEFINE mov_[rip+DWORD],rax 488905
DEFINE mov_[rip+DWORD],rcx 48890D
DEFINE movzx_rax,al 480FB6C0
DEFINE movzx_rbx,bl 480FB6DB
DEFINE movzx_rcx,cl 480FB6C9
DEFINE popf 9D
DEFINE pop_rax 58
DEFINE pop_rbp 5D
DEFINE pop_rbx 5B
DEFINE pop_rcx 59
DEFINE pop_rdi 5F
DEFINE pop_rdx 5A
DEFINE pop_rsi 5E
DEFINE pop_r8 4158
DEFINE pop_r9 4159
DEFINE pop_r12 415C
DEFINE pop_r13 415D
DEFINE pop_r14 415E
DEFINE pop_r15 415F
DEFINE push 6A
DEFINE pushf 9C
DEFINE push_rax 50
DEFINE push_rbp 55
DEFINE push_rbx 53
DEFINE push_rcx 51
DEFINE push_rdi 57
DEFINE push_rdx 52
DEFINE push_rsi 56
DEFINE push_rsp 54
DEFINE push_r12 4154
DEFINE push_r13 4155
DEFINE push_r14 4156
DEFINE push_r15 4157
DEFINE push_[rsp] FF3424
DEFINE ret C3
DEFINE ror_r9 49D1C9
DEFINE shl_rax, 48C1E0
DEFINE shr_rax, 48C1E8
DEFINE sub_rbx, 4883EB
DEFINE sub_rcx, 4883E9
DEFINE sub_rsp, 4883EC
DEFINE xchg_rax,rbx 4893
DEFINE xor_eax,eax 31C0
DEFINE xor_esi,esi 31F6
DEFINE xor_r9,r9 4D31C9
DEFINE xor_r13,r13 4D31ED
# Register usage:
# RAX, RSI, RDI => Temps
# R12 => MALLOC
# R13 => HEAD
# R14 => system->boot
# Struct format: (size 32)
# NEXT => 0
# TYPE => 8
# TEXT => 16
# EXPRESSION => 24
# Types
# None => 0
# MACRO => 1
# STRING => 2
# efi_main(void *image_handle, struct efi_system_table *system)
:_start
# Save non-volatile registers
push_rbp
push_rbx
push_rdi
push_rsi
push_r12
push_r13
push_r14
push_r15
mov_rbp,rsp # save stack pointer
mov_[rip+DWORD],rcx %image_handle # save image_handle
mov_r14,[rdx+BYTE] !96 # system->boot
# Open Loaded Image protocol
mov_r9,[rip+DWORD] %image_handle # arg4 = image_handle
lea_rdx,[rip+DWORD] %LOADED_IMAGE_PROTOCOL # guid = &LOADED_IMAGE_PROTOCOL
mov_rcx,r9 # arg1 = image_handle
call %open_protocol # open protocol
mov_rdi,rax # save image
# Get root file system
mov_r9,[rip+DWORD] %image_handle # arg4 = image_handle
lea_rdx,[rip+DWORD] %SIMPLE_FS_PROTOCOL # guid = &SIMPLE_FS_PROTOCOL
mov_rcx,[rdi+BYTE] !24 # arg1 = root_device = image->device
mov_[rip+DWORD],rcx %root_device # save root_device
call %open_protocol # open protocol
mov_rcx,rax # get rootfs
# Get root directory
lea_rdx,[rip+DWORD] %rootdir # arg2 = &rootdir
sub_rsp, !40 # allocate shadow stack space for UEFI function
call_[rcx+BYTE] !8 # rootfs->open_volume(rootfs, &rootdir)
add_rsp, !40 # deallocate stack
# Push command line arguments onto stack
mov_rbx,[rdi+BYTE] !56 # options = image->load_options
mov_rdx,rbx # save beginning of load_options
add_rbx,[rdi+BYTE] !48 # go to the end of load_options
push !0 # Save end of arguments (NULL) onto stack
:loop_options
cmp_rbx,rdx # Check if we are done
je8 !loop_options_done # We are done
sub_rbx, !2 # --options
mov_al,[rbx] # *options
cmp_al, !0x20 # if *options != ' '
jne8 !loop_options # then continue looping
mov_[rbx], !0 # zero it
add_rbx, %2 # ++options
push_rbx # push another argument onto stack
jmp8 !loop_options # next argument
:loop_options_done
pop_r8 # arg3 = in
pop_r12 # file out
and_rsp, !-16 # align stack to 16 bytes
# Open file for reading
push !1 # Set exit code in case of failure
cmp_r8, !0 # If NULL
je %failed_input # then exit
lea_rdx,[rip+DWORD] %fin # arg2 = &fin
push !1 # arg5 = EFI_FILE_READ_ONLY
push !1 # prepare to set arg4 to EFI_FILE_MODE_READ
pop_r9 # arg4 = EFI_FILE_MODE_READ
mov_rcx,[rip+DWORD] %rootdir # arg1 = rootdir
sub_rsp, !32 # allocate shadow stack space for UEFI function
call_[rcx+BYTE] !8 # rootdir->open()
cmp_rax, %0 # If failed to open
jne %failed_input # then exit
add_rsp, !48 # deallocate stack
# Open file for writing
mov_r8,r12 # arg3 = out
push !1 # Set exit code in case of failure
cmp_r8, !0 # If NULL
je %failed_output # then exit
lea_rdx,[rip+DWORD] %fout # arg2 = &fout
push !0 # arg5 = 0
push !7 # to get 0x8000000000000003 we set the rightmost 3 bits
pop_r9 # and then do right rotation by 1
ror_r9 # arg4 = EFI_FILE_MODE_CREATE| EFI_FILE_MODE_WRITE | EFI_FILE_MODE_READ
mov_rcx,[rip+DWORD] %rootdir # arg1 = rootdir
sub_rsp, !32 # allocate shadow stack space for UEFI function
call_[rcx+BYTE] !8 # rootdir->open()
add_rsp, !48 # deallocate stack
# Allocate ourselves 32 MiB of memory
mov_rdx, %0x2000000 # allocate 32 MiB of memory for malloc pool
call %allocate_pool # allocate memory
mov_r12,rax # save malloc pointer
mov_[rip+DWORD],rax %malloc_pool # save the beginning of malloc pool
# Zero allocated memory buffer
add_rax, %0x2000000 # end of malloc area
:zero_loop
dec_rax # next byte
mov_[rax],BYTE !0 # zero it
cmp_rax,r12 # if we are not done yet
jne8 !zero_loop # then continue looping
xor_r13,r13 # Set HEAD = NULL
call %Tokenize_Line # Process it
mov_rax,r13 # prepare for Reverse_List
call %Reverse_List # Correct order
mov_r13,rax # Update HEAD
call %Identify_Macros # Find the DEFINEs
call %Line_Macro # Apply the DEFINEs
call %Process_String # Handle strings
call %Eval_Immediates # Handle Numbers
call %Preserve_Other # Collect the remaining
call %Print_Hex # Output our results
:Done
xor_eax,eax # Set exit code 0
# Free pool
push_rax # save exit code
mov_rcx,[rip+DWORD] %malloc_pool # arg1 = malloc_pool
call %free_pool # system->boot->free_pool(malloc_pool)
mov_rcx,[rip+DWORD] %fout # get fout
call %close_file # close fout
:failed_output
mov_rcx,[rip+DWORD] %fin # get fin
call %close_file # close fin
:failed_input
mov_rcx,[rip+DWORD] %rootdir # get rootdir
call %close_file # close rootdir
mov_r8,[rip+DWORD] %image_handle # arg3 = image_handle
lea_rdx,[rip+DWORD] %SIMPLE_FS_PROTOCOL # guid = &SIMPLE_FS_PROTOCOL
mov_rcx,[rip+DWORD] %root_device # arg1 = root_device
call %close_protocol # close protocol
mov_r8,[rip+DWORD] %image_handle # arg3 = image_handle
lea_rdx,[rip+DWORD] %LOADED_IMAGE_PROTOCOL # guid = &LOADED_IMAGE_PROTOCOL
mov_rcx,r8 # arg1 = image_handle
call %close_protocol # close protocol
pop_rax # restore exit code
# Restore non-volatile registers
mov_rsp,rbp
pop_r15
pop_r14
pop_r13
pop_r12
pop_rsi
pop_rdi
pop_rbx
pop_rbp
ret # return to UEFI
# Tokenize_Line Function
# Using Head R13
# Creates a linked list of structs
# Uses RBX for in_set strings, RCX for Int C and RDX for Struct Token* p
:Tokenize_Line
push_rbx # Protect RBX
push_rcx # Protect RCX
push_rdx # Protect RDX
:restart
call %fgetc # Read a char
cmp_rax, %-4 # Check for EOF
je8 !done # File is collected
movzx_rax,al # We have to zero extend it to use it
mov_rcx,rax # Protect C
lea_rbx,[rip+DWORD] %comments # Get pointer to "#;"
call %In_Set # Check for comments
cmp_rax, %1 # If comments
je %Purge_LineComment # try again
mov_rax,rcx # put C in place for check
lea_rbx,[rip+DWORD] %terminators # Get pointer to "\n\t "
call %In_Set # Check for terminators
cmp_rax, %1 # If terminator
je8 !restart # try again
mov_rax, %32 # Malloc the struct P
call %malloc # Get pointer to P
mov_rdx,rax # Protect P
mov_[rdx],r13 # P->NEXT = HEAD
mov_r13,rdx # HEAD = P
mov_rax,rcx # put C in place for check
lea_rbx,[rip+DWORD] %string_char # Get pointer to "\"'"
call %In_Set # Check for string chars
cmp_rax, %1 # If string char
je %Store_String # Get string
call %Store_Atom # Get whole token
jmp8 !restart
:done
pop_rdx # Restore RDX
pop_rcx # Restore RCX
pop_rbx # Restore RBX
ret
# fgetc function
# Returns -4 (EOF) or char in RAX
:fgetc
push_rcx # Protect RCX
push_rdx # Protect RDX
mov_rcx,[rip+DWORD] %fin # arg1 = fin
push !1 # size = 1
mov_rdx,rsp # arg2 = &size
xor_esi,esi # zero rsi
push_rsi # allocate stack
mov_r8,rsp # arg3 = &input
push_rsp # align stack to 16 bytes
push_[rsp] # align stack to 16 bytes
and_rsp, !-16 # align stack to 16 bytes
sub_rsp, !32 # allocate shadow stack space for UEFI function
call_[rcx+BYTE] !32 # fin->read()
mov_rsp,[rsp+BYTE] !40 # deallocate stack
pop_rax # save input to rax
pop_rsi # save size to rsi
# If the file ended (0 bytes read) return EOF
cmp_rsi, %0 # if size == 0
jne8 !fgetc_1
mov_rax, %-4 # Put EOF in rax
:fgetc_1
pop_rdx # Restore RDX
pop_rcx # Restore RCX
ret # return
# Malloc isn't actually reserving memory here.
# It just updates the pointer in our already reserved storage pool.
:malloc
push_r12 # Save current malloc pointer
add_r12,rax # Request number of desired bytes
pop_rax # Return pointer
ret
# Purge_LineComment function
# Reads chars until LF and jumps to restart
:Purge_LineComment
call %fgetc # Get a char
cmp_rax, %10 # While not LF
jne8 !Purge_LineComment # Keep reading
jmp %restart
# Store_String Function
# Receives C in RCX, HEAD in RDX and Input file in R15
# Uses RBX for terminator, RCX for C and RDX for string
:Store_String
push_rbx # Protect RBX
push_rcx # Protect RCX
push_rdx # Protect RDX
mov_rax, %2 # Using TYPE STRING
mov_[rdx+BYTE],rax !8 # HEAD->TYPE = STRING
mov_rax, %256 # Malloc the string
call %malloc # Get pointer to P
mov_[rdx+BYTE],rax !16 # HEAD->TEXT = STRING
mov_rbx,rcx # Protect terminator
mov_rdx,rax # Protect string pointer
:Store_String_Loop
mov_[rdx],cl # write byte
call %fgetc # read next char
mov_rcx,rax # Update C
add_rdx, %1 # STRING = STRING + 1
cmp_rcx,rbx # See if we hit terminator
jne8 !Store_String_Loop # Otherwise keep looping
pop_rdx # Restore RDX
pop_rcx # Restore RCX
pop_rbx # Restore RBX
mov_rax,rdx # return HEAD
jmp %restart
# Store_Atom Function
# Receives C in RCX, HEAD in RDX and Input file in R15
# Uses RBX for in_set strings, RCX for C and RDX for string
:Store_Atom
push_rbx # Protect RBX
push_rcx # Protect RCX
push_rdx # Protect RDX
mov_rax, %256 # Malloc the string
call %malloc # Get pointer to P
mov_[rdx+BYTE],rax !16 # HEAD->TEXT = STRING
lea_rbx,[rip+DWORD] %terminators # Get pointer to "\n\t "
mov_rdx,rax # Protect string pointer
:Store_Atom_loop
mov_[rdx],cl # write byte
call %fgetc # read next char
mov_rcx,rax # Update C
add_rdx, %1 # STRING = STRING + 1
call %In_Set # Check for terminators
cmp_rax, %0 # Check for "\n\t "
je8 !Store_Atom_loop # Loop otherwise
pop_rdx # Restore RDX
pop_rcx # Restore RCX
pop_rbx # Restore RBX
mov_rax,rdx # return HEAD
ret
# In_Set function
# Receives Char C in AL and CHAR* in RBX
# Returns 1 if true, zero if false in RAX
:In_Set
push_rbx # Protect RBX
push_rcx # Protect RCX
:In_Set_loop
mov_cl,[rbx] # Read char
movzx_rcx,cl # Zero extend it
cmp_rax,rcx # See if they match
je8 !In_Set_True # return true
cmp_rcx, %0 # Check for NULL
je8 !In_Set_False # return false
add_rbx, %1 # s = s + 1
jmp8 !In_Set_loop # Keep looping
:In_Set_True
mov_rax, %1 # Set True
pop_rcx # Restore RCX
pop_rbx # Restore RBX
ret
:In_Set_False
mov_rax, %0 # Set FALSE
pop_rcx # Restore RCX
pop_rbx # Restore RBX
ret
# Char sets
:terminators
!10 !9 !32 !0 # "\n\t \0"
:comments
!35 !59 !0 # "#;\0"
:string_char
!34 !39 !0 # "\"'\0"
# Reverse_List function
# Receives List in RAX
# Returns the list reversed in RAX
:Reverse_List
push_rbx # Protect RBX
push_rcx # Protect RCX
mov_rbx,rax # Set HEAD
mov_rax, %0 # ROOT = NULL
:Reverse_List_Loop
cmp_rbx, %0 # WHILE HEAD != NULL
je8 !Reverse_List_Done # Stop otherwise
mov_rcx,[rbx] # NEXT = HEAD->NEXT
mov_[rbx],rax # HEAD->NEXT = ROOT
mov_rax,rbx # ROOT = HEAD
mov_rbx,rcx # HEAD = NEXT
jmp8 !Reverse_List_Loop # Keep Going
:Reverse_List_Done
pop_rcx # Restore RCX
pop_rbx # Restore RBX
ret
# Identify_Macros function
# Receives List in RAX
# Updates the list in place; does not modify registers
# Uses RBX for DEFINE, RCX for I
:Identify_Macros
push_rax # Protect RAX
push_rbx # Protect RBX
push_rcx # Protect RCX
push_rdx # Protect RDX
lea_rbx,[rip+DWORD] %DEFINE_str # Setup define string
mov_rcx,rax # I = HEAD
:Identify_Macros_Loop
mov_rax,[rcx+BYTE] !16 # I->TEXT
call %match # IF "DEFINE" == I->TEXT
cmp_rax, %0 # Check if match
jne8 !Identify_Macros_Next # Skip the work
# Deal with MACRO
mov_rax, %1 # Using MACRO
mov_[rcx+BYTE],rax !8 # I->TYPE = MACRO
mov_rax,[rcx] # I->NEXT
mov_rax,[rax+BYTE] !16 # I->NEXT->TEXT
mov_[rcx+BYTE],rax !16 # I->TEXT = I->NEXT->TEXT
mov_rax,[rcx] # I->NEXT
mov_rax,[rax] # I->NEXT->NEXT
mov_rax,[rax+BYTE] !16 # I->NEXT->NEXT->TEXT
mov_[rcx+BYTE],rax !24 # I->EXPRESSION = I->NEXT->NEXT->TEXT
mov_rax,[rcx] # I->NEXT
mov_rax,[rax] # I->NEXT->NEXT
mov_rax,[rax] # I->NEXT->NEXT->NEXT
mov_[rcx],rax # I->NEXT = I->NEXT->NEXT->NEXT
:Identify_Macros_Next
mov_rcx,[rcx] # I = I->NEXT
cmp_rcx, %0 # Check for NULL
jne8 !Identify_Macros_Loop # Keep looping otherwise
pop_rdx # Restore RDX
pop_rcx # Restore RCX
pop_rbx # Restore RBX
pop_rax # Restore RAX
ret
:DEFINE_str
!68 !69 !70 !73 !78 !69 !0 # "DEFINE"
# match function
# Receives CHAR* in RAX and CHAR* in RBX
# Returns 0 (TRUE) or 1 (FALSE) in RAX
:match
push_rbx # Protect RBX
push_rcx # Protect RCX
push_rdx # Protect RDX
mov_rcx,rax # S1 in place
mov_rdx,rbx # S2 in place
:match_Loop
mov_al,[rcx] # S1[0]
movzx_rax,al # Make it useful
mov_bl,[rdx] # S2[0]
movzx_rbx,bl # Make it useful
cmp_rax,rbx # See if they match
jne8 !match_False # If not
add_rcx, %1 # S1 = S1 + 1
add_rdx, %1 # S2 = S2 + 1
cmp_rax, %0 # If reached end of string
je8 !match_Done # Perfect match
jmp8 !match_Loop # Otherwise keep looping
:match_False
mov_rax, %1 # Return false
:match_Done
pop_rdx # Restore RDX
pop_rcx # Restore RCX
pop_rbx # Restore RBX
ret
# Line_Macro function
# Receives List in RAX
# Updates the list in place; does not modify registers
# Uses RAX for I, RBX for I->TEXT, RCX for I->EXPRESSION
:Line_Macro
push_rax # Protect RAX
push_rbx # Protect RBX
push_rcx # Protect RCX
push_rdx # Protect RDX
:Line_Macro_Loop
mov_rbx,[rax+BYTE] !8 # I->TYPE
cmp_rbx, %1 # IF MACRO == I->TYPE
jne8 !Line_Macro_Next # Otherwise move on
# Is a macro apply
mov_rbx,[rax+BYTE] !16 # I->TEXT
mov_rcx,[rax+BYTE] !24 # I->EXPRESSION
mov_rax,[rax] # I->NEXT
call %Set_Expression # Apply it
jmp8 !Line_Macro_Loop # Move on to next
:Line_Macro_Next
mov_rax,[rax] # I->NEXT
cmp_rax, %0 # Check for NULL
jne8 !Line_Macro_Loop # Keep going
pop_rdx # Restore RDX
pop_rcx # Restore RCX
pop_rbx # Restore RBX
pop_rax # Restore RAX
ret
# Set_Expression function
# Receives List in RAX, CHAR* in RBX and CHAR* in RCX
# Updates the list in place; does not modify registers
# Uses RBX for C, RCX for EXP and RDX for I
:Set_Expression
push_rax # Protect RAX
push_rbx # Protect RBX
push_rcx # Protect RCX
push_rdx # Protect RDX
mov_rdx,rax # Set I
:Set_Expression_Loop
mov_rax,[rdx+BYTE] !8 # I->TYPE
cmp_rax, %1 # IF MACRO == I->TYPE
je8 !Set_Expression_Next # Ignore and move on
mov_rax,[rdx+BYTE] !16 # I->TEXT
call %match # Check for match
cmp_rax, %0 # If match
jne8 !Set_Expression_Next # Otherwise next
# We have a non-macro match
mov_[rdx+BYTE],rcx !24 # I->EXPRESSION = EXP
:Set_Expression_Next
mov_rdx,[rdx] # I = I->NEXT
cmp_rdx, %0 # IF NULL == I
jne8 !Set_Expression_Loop # Otherwise keep looping
pop_rdx # Restore RDX
pop_rcx # Restore RCX
pop_rbx # Restore RBX
pop_rax # Restore RAX
ret
# Process_String function
# Receives List in RAX
# Update the list in place; does not modify registers
# Uses RBX for I->TEXT, RCX for I and RDX for S
:Process_String
push_rax # Protect RAX
push_rbx # Protect RBX
push_rcx # Protect RCX
push_rdx # Protect RDX
mov_rcx,rax # I = HEAD
:Process_String_loop
mov_rax,[rcx+BYTE] !8 # I->TYPE
cmp_rax, %2 # IF STRING == I->TYPE
jne8 !Process_String_Next # Skip to next
mov_rbx,[rcx+BYTE] !16 # I->TEXT
mov_al,[rbx] # I->TEXT[0]
movzx_rax,al # make it useful
cmp_rax, %39 # IF '\'' == I->TEXT[0]
jne8 !Process_String_Raw # Deal with '"'
# Deal with '\''
add_rbx, %1 # I->TEXT + 1
mov_[rcx+BYTE],rbx !24 # I->EXPRESSION = I->TEXT + 1
jmp8 !Process_String_Next # Move on to next
:Process_String_Raw
mov_rax,rbx # Get length of I->TEXT
call %string_length # Do it
shr_rax, !2 # LENGTH = LENGTH >> 2
add_rax, %1 # LENGTH = LENGTH + 1
shl_rax, !3 # LENGTH = LENGTH << 3
call %malloc # Get string
mov_rdx,rbx # S = I->TEXT
add_rdx, %1 # S = S + 1
mov_[rcx+BYTE],rax !24 # I->EXPRESSION = hexify
mov_rbx,rax # Put hexify buffer in rbx
:Process_String_Raw_Loop
mov_al,[rdx] # Read 1 chars
movzx_rax,al # Make it useful
add_rdx, %1 # S = S + 1
cmp_al, !0 # Check for NULL
pushf # Protect condition
call %hex8 # write them all
popf # restore condition
jne8 !Process_String_Raw_Loop # Keep looping
:Process_String_Next
mov_rcx,[rcx] # I = I->NEXT
cmp_rcx, %0 # IF NULL == I
jne8 !Process_String_loop # Otherwise keep looping
pop_rdx # Restore RDX
pop_rcx # Restore RCX
pop_rbx # Restore RBX
pop_rax # Restore RAX
ret
# string_length function
# Receives CHAR* in RAX
# Returns INT in RAX
# Uses RAX for CH, RBX for S and RCX for INDEX
:string_length
push_rbx # Protect RBX
push_rcx # Protect RCX
mov_rbx,rax # Set S
mov_rcx, %0 # INDEX = 0
:string_length_loop
mov_al,[rbx+rcx] # S[0]
movzx_rax,al # make it useful
cmp_rax, %0 # IF NULL == S[0]
je8 !string_length_done # Stop
add_rcx, %1 # INDEX = INDEX + 1
jmp8 !string_length_loop # Keep going
:string_length_done
mov_rax,rcx # RETURN INDEX
pop_rcx # Restore RCX
pop_rbx # Restore RBX
ret
# Eval_Immediates function
# Receives List in RAX
# Updates the list in place; does not modify registers
# Uses RBX for I->TEXT[0], RCX for I->TEXT[1] and RDX for I
:Eval_Immediates
push_rax # Protect RAX
push_rbx # Protect RBX
push_rcx # Protect RCX
push_rdx # Protect RDX
mov_rdx,rax # I = HEAD
:Eval_Immediates_Loop
# Check for MACRO
mov_rax,[rdx+BYTE] !8 # I->TYPE
cmp_rax, %1 # IF MACRO == I->TYPE
je8 !Eval_Immediates_Next # Skip to next
# Check for NULL EXPRESSION
mov_rax,[rdx+BYTE] !24 # I->EXPRESSION
cmp_rax, %0 # IF NULL == I->EXPRESSION
jne8 !Eval_Immediates_Next # Skip to next
# Check if number
mov_rax,[rdx+BYTE] !16 # I->TEXT
mov_bl,[rax] # I->TEXT[0]
movzx_rbx,bl # Extend to use
add_rax, %1 # I->TEXT + 1
mov_cl,[rax] # I->TEXT[1]
movzx_rcx,cl # Extend to use
call %numerate_string # Convert string to INT
cmp_rax, %0 # IF 0 == numerate_string(I->TEXT + 1)
jne8 !Eval_Immediates_value # Has a value
# Last chance for Immediate
cmp_rcx, %48 # If '0' == I->TEXT[1]
jne8 !Eval_Immediates_Next # Skip to next
:Eval_Immediates_value
call %express_number # Convert value to hex string
mov_[rdx+BYTE],rax !24 # I->EXPRESSION = express_number(value, I-TEXT[0])
:Eval_Immediates_Next
mov_rdx,[rdx] # I = I->NEXT
cmp_rdx, %0 # IF NULL == I
jne8 !Eval_Immediates_Loop # Otherwise keep looping
pop_rdx # Restore RDX
pop_rcx # Restore RCX
pop_rbx # Restore RBX
pop_rax # Restore RAX
ret
# numerate_string function
# Receives CHAR* in RAX
# Returns value of CHAR* in RAX
# Uses RAX for VALUE, RBX for S, RCX for CH and RSI for NEGATIVE?
:numerate_string
push_rbx # Protect RBX
push_rcx # Protect RCX
push_rdx # Protect RDX
push_rsi # Protect RSI
mov_rbx,rax # put S in correct place
mov_rax, %0 # Initialize to Zero
:numerate_string_loop
mov_cl,[rbx+BYTE] !1 # S[1]
movzx_rcx,cl # make it useful
cmp_rcx, %120 # IF 'x' == S[1]
je8 !numerate_hex # Deal with hex input
# Assume decimal input
mov_rsi, %0 # Assume no negation
mov_cl,[rbx] # S[0]
movzx_rcx,cl # make it useful
cmp_rcx, %45 # IF '-' == S[0]
jne8 !numerate_decimal # Skip negation
mov_rsi, %1 # Set FLAG
add_rbx, %1 # S = S + 1
:numerate_decimal
mov_cl,[rbx] # S[0]
movzx_rcx,cl # make it useful
cmp_rcx, %0 # IF NULL == S[0]
je8 !numerate_decimal_done # We are done
imul_rax, !10 # VALUE = VALUE * 10
sub_rcx, !48 # CH = CH - '0'
cmp_rcx, %9 # Check for illegal
jg8 !numerate_string_fail # If CH > '9'
cmp_rcx, %0 # Check for illegal
jl8 !numerate_string_fail # IF CH < 0
add_rax,rcx # VALUE = VALUE + CH
add_rbx, %1 # S = S + 1
jmp8 !numerate_decimal # Keep looping
:numerate_decimal_done
cmp_rsi, %1 # Check if need to negate
jne8 !numerate_string_done # Nope
imul_rax, !-1 # VALUE = VALUE * -1
jmp8 !numerate_string_done # Done
:numerate_hex
add_rbx, %2 # S = S + 2
:numerate_hex_loop
mov_cl,[rbx] # S[0]
movzx_rcx,cl # make it useful
cmp_rcx, %0 # IF NULL == S[0]
je8 !numerate_string_done # We are done
shl_rax, !4 # VALUE = VALUE << 4
sub_rcx, !48 # CH = CH - '0'
cmp_rcx, %10 # IF 10 >= CH
jl8 !numerate_hex_digit # NO
sub_rcx, !7 # Push A-F into range
:numerate_hex_digit
cmp_rcx, %15 # Check for illegal
jg8 !numerate_string_fail # If CH > 'F'
cmp_rcx, %0 # Check for illegal
jl8 !numerate_string_fail # IF CH < 0
add_rax,rcx # VALUE = VALUE + CH
add_rbx, %1 # S = S + 1
jmp8 !numerate_hex_loop # Keep looping
:numerate_string_fail
mov_rax, %0 # return ZERO
:numerate_string_done
pop_rsi # Restore RSI
pop_rdx # Restore RDX
pop_rcx # Restore RCX
pop_rbx # Restore RBX
ret
# express_number function
# Receives INT in RAX and CHAR in RBX
# Allocates a string and expresses the value in hex
# Returns string in RAX
# Uses RAX for VALUE, RBX for S and RCX for CH
:express_number
push_rbx # Protect RBX
push_rcx # Protect RCX
push_rdx # Protect RDX
mov_rcx,rbx # Put CH in right place
mov_rbx,rax # Protect VALUE
cmp_rcx, %37 # IF '%' == CH
jne8 !express_number2 # Otherwise try @
mov_rax, %9 # We need 9 bytes
call %malloc # Get S pointer
xchg_rax,rbx # Put S and VALUE in place
push_rbx # Protect S
call %hex32l # Store 32bits
jmp8 !express_number_done # done
:express_number2
cmp_rcx, %64 # IF '@' == CH
jne8 !express_number1 # Othrewise try !
mov_rax, %5 # We need 5 bytes
call %malloc # Get S pointer
xchg_rax,rbx # Put S and VALUE in place
push_rbx # Protect S
call %hex16l # Store 16bits
jmp8 !express_number_done # done
:express_number1
mov_rax, %3 # We need 3 bytes
call %malloc # Get S pointer
xchg_rax,rbx # Put S and VALUE in place
push_rbx # Protect S
call %hex8 # Store 8bit
:express_number_done
pop_rax # Restore S
pop_rdx # Restore RDX
pop_rcx # Restore RCX
pop_rbx # Restore RBX
ret
# HEX to ascii routine
# Receives INT in RAX and CHAR* in RBX
# Stores ascii of INT in CHAR*
# Returns only modifying RAX
:hex64l
push_rax # Protect top 32
call %hex32l # Store it
pop_rax # do top 32
shr_rax, !32 # do bottom 32 first
:hex32l
push_rax # Protect top 16
call %hex16l # Store it
pop_rax # do top 16
shr_rax, !16 # do bottom 16 first
:hex16l
push_rax # Protect top byte
call %hex8 # Store it
pop_rax # do high byte
shr_rax, !8 # do bottom byte first
:hex8
push_rax # Protect bottom nibble
shr_rax, !4 # do high nibble first
call %hex4 # Store it
pop_rax # do low nibble
:hex4
and_rax, !0xF # isolate nibble
add_al, !0x30 # convert to ascii (add '0')
cmp_al, !0x39 # valid digit? (compare to '9')
jbe8 !hex1 # yes
add_al, !7 # use alpha range
:hex1
mov_[ebx],al # store result
add_ebx, !1 # next position
ret
# Preserve_Other function
# Receives List in RAX
# Updates the list in place; does not modify registers
# Uses RAX for I, RBX for I->TEXT
:Preserve_Other
push_rax # Protect RAX
push_rbx # Protect RBX
push_rcx # Protect RCX
push_rdx # Protect RDX
:Preserve_Other_Loop
mov_rbx,[rax+BYTE] !24 # I->EXPRESSION
cmp_rbx, %0 # IF NULL == I->EXPRESSION
jne8 !Preserve_Other_Next # Otherwise next
# Needs preserving
mov_rbx,[rax+BYTE] !16 # I->TEXT
mov_[rax+BYTE],rbx !24 # I->EXPRESSION = I->TEXT
:Preserve_Other_Next
mov_rax,[rax] # I = I->NEXT
cmp_rax, %0 # IF NULL == I
jne8 !Preserve_Other_Loop # Otherwise keep looping
pop_rdx # Restore RDX
pop_rcx # Restore RCX
pop_rbx # Restore RBX
pop_rax # Restore RAX
ret
# Print_Hex function
# Receives list in RAX
# walks the list and prints the I->EXPRESSION for all nodes followed by newline
# Uses RBX for I
:Print_Hex
push_rbx # Protect RBX
mov_rbx,r13 # I = Head
:Print_Hex_Loop
mov_rax,[rbx+BYTE] !8 # I->TYPE
cmp_rax, %1 # IF MACRO == I->TYPE
je8 !Print_Hex_Next # Skip
mov_rax,[rbx+BYTE] !24 # Using EXPRESSION
call %File_Print # Print it
mov_rax, %10 # NEWLINE
call %fputc # Append it
:Print_Hex_Next
mov_rbx,[rbx] # Iterate to next Token
cmp_rbx, %0 # Check for NULL
jne8 !Print_Hex_Loop # Otherwise keep looping
pop_rbx # Restore RBX
ret
# File_Print function
# Receives CHAR* in RAX
# calls fputc for every non-null char
:File_Print
push_rbx # Protect RBX
mov_rbx,rax # Protect S
cmp_rax, %0 # Protect against nulls
je8 !File_Print_Done # Simply don't try to print them
:File_Print_Loop
mov_al,[rbx] # Read byte
movzx_rax,al # zero extend
cmp_rax, %0 # Check for NULL
je8 !File_Print_Done # Stop at NULL
call %fputc # write it
add_rbx, %1 # S = S + 1
jmp8 !File_Print_Loop # Keep going
:File_Print_Done
pop_rbx # Restore RBX
ret
# fputc function
# receives CHAR in RAX
# writes char and returns
:fputc
push_rcx # Protect RCX
push_rdx # Protect RDX
push_rsp # align stack to 16 bytes
push_[rsp] # align stack to 16 bytes
and_rsp, !-16 # align stack to 16 bytes
mov_rcx,[rip+DWORD] %fout # arg1 = fout
push !1 # set size
mov_rdx,rsp # arg2 = &size
push_rax # allocate stack
mov_r8,rsp # arg3 = &output
sub_rsp, !32 # allocate shadow stack space for UEFI function
call_[rcx+BYTE] !40 # fout->write()
mov_rsp,[rsp+BYTE] !56 # deallocate stack
pop_rdx # Restore RDX
pop_rcx # Restore RCX
ret # return
# rcx: file handle
:close_file
push_rsp # align stack to 16 bytes
push_[rsp] # align stack to 16 bytes
and_rsp, !-16 # align stack to 16 bytes
sub_rsp, !32 # allocate shadow stack space for UEFI function
call_[rcx+BYTE] !16 # file_handle->close(file_handle)
mov_rsp,[rsp+BYTE] !40 # deallocate stack
ret
# rcx: handle
# rdx: &guid
# r9: agent_handle
# returns interface
:open_protocol
push_rax # allocate stack for interface
mov_r8,rsp # arg3 = &interface
push_rsp # align stack to 16 bytes
push_[rsp] # align stack to 16 bytes
and_rsp, !-16 # align stack to 16 bytes
push !1 # arg6 = EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL
push !0 # arg5 = NULL
sub_rsp, !32 # allocate shadow stack space for UEFI function
call_[r14+DWORD] %280 # system->boot->open_protocol(handle, &guid, &interface, agent_handle, 0, EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL)
mov_rsp,[rsp+BYTE] !56 # deallocate stack
pop_rax # get interface
ret
# rcx: handle
# rdx: &guid
# r8: agent_handle
:close_protocol
push_rsp # align stack to 16 bytes
push_[rsp] # align stack to 16 bytes
and_rsp, !-16 # align stack to 16 bytes
xor_r9,r9 # arg4 = NULL
sub_rsp, !32 # allocate shadow stack space for UEFI function
call_[r14+DWORD] %288 # system->boot->close_protocol(handle, &guid, agent_handle, 0)
mov_rsp,[rsp+BYTE] !40 # deallocate stack
ret
# rdx: number of bytes to allocate
# r14: system->boot
# returns pointer in rax
:allocate_pool
push_rdx # allocate stack for pool pointer
mov_r8,rsp # arg3 = &pool
push !2
pop_rcx # arg1 = EFI_LOADER_DATA
push_rsp # align stack to 16 bytes
push_[rsp] # align stack to 16 bytes
and_rsp, !-16 # align stack to 16 bytes
sub_rsp, !32 # allocate shadow stack space for UEFI function
call_[r14+BYTE] !64 # system->boot->allocate_pool(EFI_LOADER_DATA, 2048, &pool)
mov_rsp,[rsp+BYTE] !40 # deallocate stack
pop_rax # get pool
ret
# rcx: memory pool
# r14: system->boot
:free_pool
push_rsp # align stack to 16 bytes
push_[rsp] # align stack to 16 bytes
and_rsp, !-16 # align stack to 16 bytes
sub_rsp, !32 # allocate shadow stack space for UEFI function
call_[r14+BYTE] !72 # system->boot->free_pool(pool)
mov_rsp,[rsp+BYTE] !40 # deallocate stack
ret
# Protocol GUIDs
:LOADED_IMAGE_PROTOCOL
%0x5B1B31A1
$0x9562
$0x11D2
!0x8E !0x3F !0 !0xA0 !0xC9 !0x69 !0x72 !0x3B
:SIMPLE_FS_PROTOCOL
%0x964E5B22
$0x6459
$0x11D2
!0x8E !0x39 !0 !0xA0 !0xC9 !0x69 !0x72 !0x3B
:malloc_pool
%0 %0
:fin
%0 %0
:fout
%0 %0
:rootdir
%0 %0
:image_handle
%0 %0
:root_device
%0 %0
:ELF_end