stage0-uefi/amd64/Development/kaem-minimal.M1

569 lines
23 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_rax, 4883C0
DEFINE add_rsp, 4883C4
DEFINE add_rbx, 4883C3
DEFINE add_rsi, 4883C6
DEFINE add_rbx,rsi 4801F3
DEFINE add_r15,rbx 4901DF
DEFINE add_r15,rdi 4901FF
DEFINE add_rbx,[rdi+BYTE] 48035F
DEFINE call E8
DEFINE call_[rcx+BYTE] FF51
DEFINE call_[r14+BYTE] 41FF56
DEFINE call_[r14+DWORD] 41FF96
DEFINE cmp_al, 3C
DEFINE cmp_rbx,rdx 4839D3
DEFINE inc_rax 48FFC0
DEFINE je8 74
DEFINE jmp8 EB
DEFINE jmp E9
DEFINE jne8 75
DEFINE jne 0F85
DEFINE lea_rdx,[rip+DWORD] 488D15
DEFINE lea_r8,[rip+DWORD] 4C8D05
DEFINE mov_dh, B6
DEFINE mov_rbp,rsp 4889E5
DEFINE mov_rbx,rax 4889C3
DEFINE mov_rbx,rcx 4889CB
DEFINE mov_rcx,rax 4889C1
DEFINE mov_rcx,rbx 4889D9
DEFINE mov_rcx,rdi 4889F9
DEFINE mov_rcx,r8 4C89C1
DEFINE mov_rcx,r9 4C89C9
DEFINE mov_rcx,r12 4C89E1
DEFINE mov_rcx,r15 4C89F9
DEFINE mov_rdi,rax 4889C7
DEFINE mov_rdx,rbx 4889DA
DEFINE mov_rdx,rsp 4889E2
DEFINE mov_rsp,rbp 4889EC
DEFINE mov_r8,rax 4989C0
DEFINE mov_r8,rbx 4989D8
DEFINE mov_r8,rdi 4989F8
DEFINE mov_r8,rsp 4989E0
DEFINE mov_r8,r15 4D89F8
DEFINE mov_r9,rax 4989C1
DEFINE mov_r9,rbx 4989D9
DEFINE mov_r9,rdi 4989F9
DEFINE mov_r9,r15 4D89F9
DEFINE mov_r15,rsi 4989F7
DEFINE mov_r15,rax 4989C7
DEFINE mov_[rax], C700
DEFINE mov_[rbx], C603
DEFINE mov_[r8], 49C700
DEFINE mov_[rax],BYTE C600
DEFINE mov_[rax],WORD 66C700
DEFINE mov_[rbx],WORD 66C703
DEFINE mov_[r15],WORD 6641C707
DEFINE mov_al,[rbx] 8A03
DEFINE mov_[rax],r15 4C8938
DEFINE mov_[rbx],ax 668903
DEFINE mov_rax,[rdx+BYTE] 488B42
DEFINE mov_rbx,[rdi+BYTE] 488B5F
DEFINE mov_rcx,[rcx+BYTE] 488B49
DEFINE mov_rcx,[rdi+BYTE] 488B4F
DEFINE mov_rdx,[rcx+BYTE] 488B51
DEFINE mov_r12,[rsp+BYTE] 4C8B6424
DEFINE mov_r14,[rdx+BYTE] 4C8B72
DEFINE mov_rcx,[rip+DWORD] 488B0D
DEFINE mov_rdx,[rip+DWORD] 488B15
DEFINE mov_r8,[rip+DWORD] 4C8B05
DEFINE mov_[rax+BYTE],rbx 488958
DEFINE mov_[rax+BYTE],rcx 488948
DEFINE mov_[rax+BYTE],rsi 488970
DEFINE mov_[rip+DWORD],rcx 48890D
DEFINE mov_[rip+DWORD],rax 488905
DEFINE pop_rax 58
DEFINE pop_rcx 59
DEFINE pop_rdi 5F
DEFINE pop_rdx 5A
DEFINE pop_r8 4158
DEFINE pop_r9 4159
DEFINE push 6A
DEFINE push_rax 50
DEFINE push_rbx 53
DEFINE push_rdi 57
DEFINE push_rdx 52
DEFINE push_rsi 56
DEFINE push_rsp 54
DEFINE push_r8 4150
DEFINE ret C3
DEFINE sub_rbx, 4883EB
DEFINE sub_rsp, 4883EC
DEFINE sub_rbx,rsi 4829F3
DEFINE sub_r15,rdi 4929FF
DEFINE test_eax,eax 85C0
DEFINE test_ecx,ecx 85C9
DEFINE test_r8,r8 4D85C0
DEFINE test_r15,r15 4D85FF
DEFINE xor_ecx,ecx 31C9
DEFINE xor_edx,edx 31D2
DEFINE xor_esi,esi 31F6
DEFINE xor_r8,r8 4D31C0
DEFINE xor_r9,r9 4D31C9
DEFINE xor_r15,r15 4D31FF
# efi_main(void *image_handle, struct efi_system_table *system)
:_start
mov_rbp,rsp # save stack pointer
mov_[rip+DWORD],rcx %image_handle # save image_handle
mov_rbx,rcx # save image_handle
mov_rax,[rdx+BYTE] !64 # system->out
mov_[rip+DWORD],rax %system_out # save system->out
mov_r14,[rdx+BYTE] !96 # system->boot
xor_ecx,ecx # timeout = 0
xor_edx,edx # watchdog_code = 0
xor_r8,r8 # data_size = 0
xor_r9,r9 # watchdog_data = 0
sub_rsp, !32 # allocate shadow stack space for UEFI function
call_[r14+DWORD] %240 # system->boot->set_watchdog_timer
# Open Loaded Image protocol
mov_r9,rbx # arg4 = image_handle
lea_rdx,[rip+DWORD] %LOADED_IMAGE_PROTOCOL # guid = &LOADED_IMAGE_PROTOCOL
mov_rcx,rbx # arg1 = image_handle
call %open_protocol # open protocol
mov_rdi,rax # save image
mov_[rip+DWORD],rax %image # save image
# Get root file system
mov_r9,rbx # 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
push_rax # allocate shadow stack space for UEFI function
push_rax # allocate shadow stack space for UEFI function
call_[rcx+BYTE] !8 # rootfs->open_volume(rootfs, &rootdir)
pop_rax # deallocate stack
pop_rax # 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 # get input file
test_r8,r8 # Check if argument is specified
jne8 !arg_done # then use it
# Else use default_file
lea_r8,[rip+DWORD] %default_file # Use "kaem.amd64"
:arg_done
# Open file for reading
push_rdx # allocate stack for fin
mov_rdx,rsp # 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
# arg3 = script
mov_rcx,[rip+DWORD] %rootdir # arg1 = rootdir
sub_rsp, !32 # allocate shadow stack space for UEFI function
call_[rcx+BYTE] !8 # rootdir->open()
test_eax,eax # if status != EFI_SUCCESS
jne %abort # then exit without closing file
mov_r12,[rsp+BYTE] !40 # get fin
# Allocate pool for command
xor_edx,edx # zero RDX
mov_dh, !0x10 # arg2 = 4096 = 0x1000
call %allocate_pool # allocate memory
mov_rbx,rax # get command
:next_command
xor_esi,esi # i = 0
xor_r15,r15 # command_length = 0
:read_command
call %read_byte # read another byte c
cmp_al, !0xa # if c == '\n'
je8 !read_command_done # then we are done with this command
cmp_al, !0x20 # if c == ' '
jne8 !read_command_comments
test_r15,r15 # and command_length == 0
jne8 !read_command_comments
mov_r15,rsi # command_length = i
:read_command_comments
cmp_al, !0x23 # if c == '#' then process comment
jne8 !read_command_store_char # else store char
:read_command_skip_comment
call %read_byte # get another char
cmp_al, !0xa # if c == '\n'
jne8 !read_command_skip_comment # continue reading until newline
jmp8 !next_command # deal with another line
:read_command_store_char
add_rbx,rsi # rbx = &command[i]
mov_[rbx],ax # command[i] = c
sub_rbx,rsi # rbx = &command[0]
add_rsi, !2 # location of the next char
jmp8 !read_command # continue looping
:read_command_done
test_r15,r15 # if command_length == 0
je8 !next_command # deal with another line
add_rbx,rsi # rbx = &command[i]
mov_[rbx],WORD @0 # command[i] = 0
sub_rbx,rsi # rbx = &command[0]
add_rsi, !2 # add 2 to get string length with NULL terminator
lea_rdx,[rip+DWORD] %prefix # get prefix " +> "
call %File_Print # print it
mov_rdx,rbx # get command
call %File_Print # print it
lea_rdx,[rip+DWORD] %suffix # get suffix "\n\r"
call %File_Print # print it
# Remove command line options
add_r15,rbx # go to the space separating command and its options
mov_[r15],WORD @0 # zero it to hide command line options
# Open executable file for reading
push_rdx # allocate stack for fcmd
mov_rdx,rsp # arg2 = &fcmd
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_r8,rbx # arg3 = command
mov_rcx,[rip+DWORD] %rootdir # arg1 = rootdir
sub_rsp, !32 # allocate shadow stack space for UEFI function
call_[rcx+BYTE] !8 # rootdir->open()
test_eax,eax # if status != EFI_SUCCESS
jne %print_error # then exit
add_rsp, !40 # deallocate stack
pop_rdi # get fcmd
# Restore command line arguments
mov_[r15],WORD @0x20 # restore command line options by readding ' '
# Allocate pool for file_info
xor_edx,edx # zero RDX
mov_dh, !0x10 # arg2 = 4096 = 0x1000
call %allocate_pool # allocate memory
mov_r9,rax # get file_info (arg4 for get_info)
# Get file info
push_rax # save file_info
push_rax # allocate stack for file_size
mov_r8,rsp # arg3 = &file_size
mov_[r8], %0x1000 # file_size = 0x1000
lea_rdx,[rip+DWORD] %FILE_INFO_PROTOCOL # arg2 = &EFI_FILE_INFO_PROTOCOL
mov_rcx,rdi # arg1 = fcmd
sub_rsp, !32 # allocate shadow stack space for UEFI function
call_[rcx+BYTE] !64 # fcmd->get_info(fcmd, &guid, &file_size, file_info)
add_rsp, !40 # deallocate stack
pop_rcx # restore file_info
mov_rdx,[rcx+BYTE] !8 # get file_size
# Free file_info pool
push_rdx # save file_size onto stack
push_rax # allocate shadow stack space for UEFI function
call_[r14+BYTE] !72 # system->boot->free_pool(file_info)
pop_rax # deallocate stack
pop_rdx # restore file_size from stack (arg2 for allocate_pool)
# Allocate pool for executable
push_rdx # save file_size onto stack
call %allocate_pool # allocate memory
mov_r15,rax # get executable
pop_rdx # restore file_size
# Load executable into memory
push_rdx # save file_size onto stack
mov_r8,r15 # arg3 = executable
mov_rdx,rsp # arg2 = &file_size
mov_rcx,rdi # arg1 = fcmd
push_rax # allocate shadow stack space for UEFI
push_rax # allocate shadow stack space for UEFI
push_rax # allocate shadow stack space for UEFI
call_[rcx+BYTE] !32 # fcmd->read(fcmd, &file_size, executable)
pop_rax # deallocate stack
pop_rax # deallocate stack
pop_rax # deallocate stack
# Close fcmd
push_rax # allocate stack
mov_rcx,rdi # arg1 = fcmd
call_[rcx+BYTE] !16 # fcmd->close(fcmd)
pop_rax # restore stack
pop_rdi # restore file_size
# Allocate memory for device_path struct
push !28 # 4 + sizeof(struct efi_device_path_protocol)
pop_rdx # arg2 = 28
call %allocate_pool # allocate memory
mov_r8,rax # get device_path
# Initialize struct
mov_[rax],BYTE !1 # device_path->type = HARDWARE_DEVICE_PATH
inc_rax # next member
mov_[rax],BYTE !3 # device_path->subtype = MEMORY_MAPPED
inc_rax # next member
mov_[rax],WORD @24 # device_path->length = 24
add_rax, !2 # next member
mov_[rax], %1 # device_path->memory_type = EFI_LOADER_CODE
add_rax, !4 # next member
mov_[rax],r15 # device_path->start_address = executable
add_rax, !8 # next member
add_r15,rdi # end_address = executable + file_size
mov_[rax],r15 # device_path->end_address = end_address
sub_r15,rdi # restore r15 = executable
add_rax, !8 # next member
mov_[rax],BYTE !0x7f # device_path[1].type = END_HARDWARE_DEVICE_PATH
inc_rax # next member
mov_[rax],BYTE !0xff # device_path[1].subtype = END_ENTIRE_DEVICE_PATH
inc_rax # next member
mov_[rax],WORD @4 # device_path[1].length = 4
# Load image
push_r8 # save device_path
push_rax # allocate stack for child_ih
push_rsp # arg6 = &child_ih
push_rdi # arg5 = file size
mov_r9,r15 # arg4 = executable
# arg3 = device_path
mov_rdx,[rip+DWORD] %image_handle # arg2 = image_handle
xor_ecx,ecx # arg1 = 0
sub_rsp, !32 # allocate shadow stack space for UEFI function
call_[r14+DWORD] %200 # system->boot->load_image()
add_rsp, !48 # deallocate stack
pop_rdi # save child_ih
# Free device_path pool
pop_rcx # arg1 = device_path
push_rax # allocate shadow stack space for UEFI function
call_[r14+BYTE] !72 # system->boot->free_pool(device_path)
pop_rax # deallocate stack
# Free executable pool
mov_rcx,r15 # arg1 = executable
push_rax # allocate shadow stack space for UEFI function
call_[r14+BYTE] !72 # system->boot->free_pool(executable)
pop_rax # deallocate stack
# Open Child Image
mov_r9,rdi # arg4 = child_ih
lea_rdx,[rip+DWORD] %LOADED_IMAGE_PROTOCOL # guid = &LOADED_IMAGE_PROTOCOL
push_rdx # save &LOADED_IMAGE_PROTOCOL
mov_rcx,r9 # arg1 = child_ih
call %open_protocol # open protocol
mov_[rax+BYTE],rbx !56 # child_image->load_options = command
mov_[rax+BYTE],rsi !48 # set child_image->load_options_size
mov_rcx,[rip+DWORD] %image # get image
mov_rcx,[rcx+BYTE] !24 # image->device
mov_[rax+BYTE],rcx !24 # child_image->device = image->device
mov_r8,rdi # arg3 = image_handle
pop_rdx # arg2 = &LOADED_IMAGE_PROTOCOL
mov_rcx,r8 # arg1 = image_handle
call %close_protocol # close protocol
# Run command
xor_r8,r8 # arg3 = 0 (ExitData)
xor_edx,edx # arg2 = 0 (ExitData size)
mov_rcx,rdi # arg1 = child_ih
push_rax # allocate shadow stack space for UEFI
push_rax # allocate shadow stack space for UEFI
push_rax # allocate shadow stack space for UEFI
call_[r14+DWORD] %208 # system->boot->start_image()
pop_rcx # deallocate stack
pop_rcx # deallocate stack
pop_rcx # deallocate stack
test_eax,eax # check if return code is 0
jne8 !print_error # print error and exit
jmp %next_command # process another line from kaem script
:print_error
push_rax # save exit code
lea_rdx,[rip+DWORD] %subprocess_error # get error message
call %File_Print # print it
pop_rax # restore exit code
# Close script file and exit
:terminate
# Free pool
mov_rcx,rbx # arg1 = command
push_rax # save exit code
push_rax # allocate shadow stack space for UEFI function
call_[r14+BYTE] !72 # system->boot->free_pool(commmand)
mov_rcx,r12 # arg1 = fin
call_[rcx+BYTE] !16 # fin->close(fin)
mov_rcx,[rip+DWORD] %rootdir # arg1 = rootdir
call_[rcx+BYTE] !16 # rootdir->close(rootdir)
pop_rax # deallocate stack
pop_rax # restore exit code
# Exit without closing script file
:terminate_2
mov_r8,[rip+DWORD] %image_handle # arg3 = image_handle
push_r8 # save 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
pop_r8 # 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
:abort
mov_rsp,rbp # restore stack
ret # return to UEFI
# File_Print function
# Receives WCHAR* in RDX
:File_Print
mov_rcx,[rip+DWORD] %system_out # get system_out
push_rax # allocate shadow stack space for UEFI function
push_rax # allocate shadow stack space for UEFI function
call_[rcx+BYTE] !8 # system->out->output_string(system->out, WCHAR*)
pop_rax # deallocate stack
pop_rax # deallocate stack
ret
# read_byte function
# reads a single character
:read_byte
mov_rcx,r12 # arg1 = fin
push !1 # size = 1
mov_rdx,rsp # arg2 = &size
push_rsi # allocate stack
mov_r8,rsp # arg3 = &c
push_rax # allocate shadow stack space for UEFI function
push_rax # allocate shadow stack space for UEFI function
push_rax # allocate shadow stack space for UEFI function
call_[rcx+BYTE] !32 # fin->read()
pop_rax # deallocate stack
pop_rax # deallocate stack
pop_rax # deallocate stack
pop_rax # save c to rax
pop_rcx # save size to rcx
# If the file ended (0 bytes read) terminate
test_ecx,ecx # if size = 0
je8 !terminate # then we are done
ret # return
# rcx: handle
# rdx: &guid
# r9: agent_handle
# returns interface
:open_protocol
push_rax # allocate stack for interface
mov_r8,rsp # arg3 = &interface
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)
add_rsp, !48 # deallocate stack
pop_rax # get interface
ret
# rcx: handle
# rdx: &guid
# r8: agent_handle
:close_protocol
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)
add_rsp, !32 # 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
sub_rsp, !24 # allocate shadow stack space for UEFI
call_[r14+BYTE] !64 # system->boot->allocate_pool(EFI_LOADER_DATA, 2048, &pool)
add_rsp, !24 # deallocate stack
pop_rax # get pool
ret
# Protocol GUIDs
:LOADED_IMAGE_PROTOCOL
%0x5b1b31a1
@0x9562
@0x11d2
!0x8e !0x3f !0 !0xa0 !0xc9 !0x69 !0x72 !0x3b
:SIMPLE_FS_PROTOCOL
%0x0964e5b22
@0x6459
@0x11d2
!0x8e !0x39 !0 !0xa0 !0xc9 !0x69 !0x72 !0x3b
:FILE_INFO_PROTOCOL
%0x09576e92
@0x6d3f
@0x11d2
!0x8e !0x39 !0 !0xa0 !0xc9 !0x69 !0x72 !0x3b
:default_file
"k" "a" "e" "m" "." "a" "m" "d" "6" "4" @0
:prefix
" " "+" ">" " " @0
:subprocess_error
"S" "u" "b" "p" "r" "o" "c" "e" "s" "s" " "
"e" "r" "r" "o" "r"
:suffix
!0xa !0 !0xd !0 @0 # L"\n\r"
:image_handle
%0 %0
:image
%0 %0
:rootdir
%0 %0
:root_device
%0 %0
:system_out
%0 %0
:PE32_end