uefi64kos.asm
mxlgv_KolibriDev/src/kernel/bootloader/uefi4kos/uefi64kos.asm
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                                                              ;;
;; Copyright (C) KolibriOS team 2020. All rights reserved.      ;;
;; Distributed under terms of the GNU General Public License    ;;
;; Version 2, or (at your option) any later version.            ;;
;;                                                              ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

format pe64 efi
entry main

section '.text' code executable readable

include '../../struct.inc'
include '../../macros.inc'
include '../../const.inc'

purge DQ
include 'uefi64.inc'

MEMORY_MAP_SIZE = 0x10000
GOP_BUFFER_SIZE = 0x100
LIP_BUFFER_SIZE = 0x100
FILE_BUFFER_SIZE = 0x1000

KERNEL_BASE  =  0x10000
RAMDISK_BASE = 0x100000

CODE_32_SELECTOR = 8
DATA_32_SELECTOR = 16
CODE_64_SELECTOR = 24

; linux/arch/x86/include/uapi/asm/e820.h
E820_RAM       = 1
E820_RESERVED  = 2
E820_ACPI      = 3
E820_NVS       = 4
E820_UNUSABLE  = 5
E820_PMEM      = 7

load_file:
virtual at rsp+8
  .root   dq ?
  .name   dq ?
  .buffer dq ?
  .size   dq ?
  .fatal  dq ?
end virtual
        eficall [.root], EFI_FILE_PROTOCOL.Open, [.root], file_handle, \
                [.name], EFI_FILE_MODE_READ, 0
        test    eax, eax
        jz      @f
        xor     eax, eax
        cmp     [.fatal], 1
        jnz     .done
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \
                msg_error_open_file
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \
                [.name]
        jmp     $
@@:

        lea     rax, [.size]
        eficall [file_handle], EFI_FILE_PROTOCOL.Read, [file_handle], rax, \
                [.buffer]
        eficall [file_handle], EFI_FILE_PROTOCOL.Close, [file_handle]
        mov     rax, [.size]
.done:
        push    rax
        call    clearbuf
        mov     rdi, msg
        call    num2dec
        push    rbx
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \
                msg_file_size
        pop     rbx
        push    rbx
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, msg
        pop     rbx
        pop     rax
        ret     8*5

skip_whitespace:
.next_char:
        cmp     byte[rsi], 0
        jz      .done
        cmp     byte[rsi], 0x20 ; ' '
        jz      .whitespace
        cmp     byte[rsi], 9    ; '\t'
        jz      .whitespace
        jmp     .done
.whitespace:
        inc     rsi
        jmp     .next_char
.done:
        ret

skip_until_newline:
.next_char:
        cmp     byte[rsi], 0
        jz      .done
        cmp     byte[rsi], 0xd  ; '\r'
        jz      .done
        cmp     byte[rsi], 0xa  ; '\n'
        jz      .done
        inc     rsi
        jmp     .next_char
.done:
        ret

skip_newline:
.next_char:
        cmp     byte[rsi], 0xd  ; '\r'
        jz      .newline
        cmp     byte[rsi], 0xa  ; '\n'
        jz      .newline
        jmp     .done
.newline:
        inc     rsi
        jmp     .next_char
.done:
        ret

skip_line:
        call    skip_until_newline
        call    skip_newline
        ret

dec2bin:
        mov     edx, 0
.next_char:
        movzx   eax, byte[rsi]
        test    eax, eax
        jz      .done
        sub     eax, '0'
        jb      .done
        cmp     eax, 9
        ja      .done
        inc     rsi
        imul    edx, 10
        add     edx, eax
        jmp     .next_char
.done:
        mov     eax, edx
        ret

parse_option:
        mov     rbx, config_options-3*8
.try_next_option:
        add     rbx, 3*8
        mov     rdi, rsi
        mov     rdx, [rbx]      ; option name
        test    rdx, rdx
        jz      .done
.next_char:
        cmp     byte[rdx], 0
        jnz     @f
        cmp     byte[rdi], '='
        jz      .opt_name_ok
@@:
        cmp     byte[rdi], 0
        jz      .done
        movzx   eax, byte[rdi]
        cmp     [rdx], al
        jnz     .try_next_option
        inc     rdi
        inc     rdx
        jmp     .next_char
.opt_name_ok:
        inc     rdi
        mov     rsi, rdi
        call    qword[rbx+8]
.done:
        ret

parse_line:
.next_line:
        cmp     byte[rsi], 0
        jz      .done
        cmp     byte[rsi], 0xd  ; '\r'
        jz      .skip
        cmp     byte[rsi], 0xa  ; '\n'
        jz      .skip
        cmp     byte[rsi], '#'
        jz      .skip
        call    parse_option
        call    skip_line
        jmp     .next_line
.skip:
        call    skip_line
        jmp     .next_line
.done:
        ret

cfg_opt_func_resolution:
        call    dec2bin
        xor     edx, edx
        mov     [rdx+BOOT_LO.x_res], ax
        cmp     byte[rsi], 'x'
        jz      @f
        cmp     byte[rsi], '*'
        jz      @f
        jmp     .done
@@:
        inc     rsi
        call    dec2bin
        xor     edx, edx
        mov     [rdx+BOOT_LO.y_res], ax
        mov     [cfg_opt_used_resolution], 1
.done:
        ret

cfg_opt_func_acpi:
        call    dec2bin
        mov     [cfg_opt_used_acpi], 1
        mov     [cfg_opt_value_acpi], al
        ret

cfg_opt_func_debug_print:
        call    dec2bin
        mov     [cfg_opt_used_debug_print], 1
        mov     [cfg_opt_value_debug_print], al
        ret

cfg_opt_func_launcher_start:
        call    dec2bin
        mov     [cfg_opt_used_launcher_start], 1
        mov     [cfg_opt_value_launcher_start], al
        ret

cfg_opt_func_mtrr:
        call    dec2bin
        mov     [cfg_opt_used_mtrr], 1
        mov     [cfg_opt_value_mtrr], al
        ret

cfg_opt_func_ask_params:
        call    dec2bin
        mov     [cfg_opt_used_ask_params], 1
        mov     [cfg_opt_value_ask_params], al
        ret

cfg_opt_func_imgfrom:
        call    dec2bin
        mov     [cfg_opt_used_imgfrom], 1
        mov     [cfg_opt_value_imgfrom], al
        ret

cfg_opt_func_syspath:
        mov     rdi, cfg_opt_value_syspath
.next_char:
        movzx   eax, byte[rsi]
        cmp     al, 0xd ; \r
        jz      .done
        cmp     al, 0xa ; \n
        jz      .done
        inc     rsi
        stosb
        jmp     .next_char
.done:
        mov     byte[rdi], 0
        ret

parse_config:
virtual at rsp+8
  .buffer      dq ?
end virtual
;        mov     rsi, [.buffer]
        push    rbx
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \
                msg_parsing_config
        pop     rbx
        mov     rsi, KERNEL_BASE
.next_line:
        call    parse_line
        cmp     byte[rsi], 0
        jnz     .next_line
        ret     1*8

read_options_from_config:
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.BootServices]
        eficall rbx, EFI_BOOT_SERVICES.HandleProtocol, [efi_handle], lipuuid, \
                lip_interface
        test    eax, eax
        jz      @f
        push    rbx
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \
                msg_error_efi_lip_handle
        pop     rbx
        jmp     $
@@:
        mov     rax, [lip_interface]

        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.BootServices]
        eficall rbx, EFI_BOOT_SERVICES.HandleProtocol, \
                [rax+EFI_LOADED_IMAGE_PROTOCOL.DeviceHandle], sfspguid, \
                sfsp_interface
        test    eax, eax
        jz      @f
        push    rbx
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \
                msg_error_lip_dev_sfsp
        pop     rbx
        jmp     $
@@:
        eficall [sfsp_interface], EFI_SIMPLE_FILE_SYSTEM_PROTOCOL.OpenVolume, \
                [sfsp_interface], esp_root
        test    eax, eax
        jz      @f
        push    rbx
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \
                msg_error_sfsp_openvolume
        pop     rbx
        jmp     $
@@:
        push    0 ; not fatal, i.e. it's ok to not find this file
        push    FILE_BUFFER_SIZE
        push    KERNEL_BASE
;        push    file_name
        mov     rax, file_name
        push    rax
        push    [esp_root]
        call    load_file

        test    eax, eax
        jz      @f
        push    KERNEL_BASE
        call    parse_config
@@:

.error:
        ret

print_vmode:
        push    rax rbx rcx rdx rsi rdi
        mov     rbx, rcx
        call    clearbuf
        mov     eax, [rbx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.HorizontalResolution]
        mov     rdi, msg
        call    num2dec
        mov     eax, [rbx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.VerticalResolution]
        mov     rdi, msg+8*2
        call    num2dec

        mov     eax, [rbx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.PixelFormat]
        mov     rdi, msg+16*2
        call    num2dec

        mov     eax, [rbx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.PixelsPerScanLine]
        mov     rdi, msg+24*2
        call    num2dec
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, msg
        pop     rdi rsi rdx rcx rbx rax
        ret

find_vmode_index_by_resolution:
        mov     [cfg_opt_used_resolution], 1
        mov     [cfg_opt_value_vmode], 0
.next_mode:
        movzx   eax, [cfg_opt_value_vmode]
        eficall [gop_interface], EFI_GRAPHICS_OUTPUT_PROTOCOL.QueryMode, \
                [gop_interface], rax, gop_info_size, gop_info
        cmp     rax, EFI_SUCCESS
        jnz     .error
        mov     rcx, [gop_info]
        call    print_vmode
        ; PixelBlueGreenRedReserved8BitPerColor
        cmp     [rcx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.PixelFormat], 1
        jnz     .skip_mode
        xor     edx, edx
        mov     eax, [rcx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.HorizontalResolution]
        cmp     ax, [rdx+BOOT_LO.x_res]
        jnz     .skip_mode
        mov     eax, [rcx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.VerticalResolution]
        cmp     ax, [rdx+BOOT_LO.y_res]
        jnz     .skip_mode
        jmp     .done
.skip_mode:
        inc     [cfg_opt_value_vmode]
        movzx   eax, [cfg_opt_value_vmode]
        mov     rcx, [gop_interface]
        mov     rdx, [rcx+EFI_GRAPHICS_OUTPUT_PROTOCOL.Mode]
        cmp     eax, 8;[rdx+EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE.MaxMode]
        jnz     .next_mode
        mov     [cfg_opt_used_resolution], 0
        mov     [cfg_opt_value_ask_params], 1

        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \
                msg_error_no_such_vmode
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \
                msg_error
        jmp     $
.error:
.done:
        ret

ask_for_params:
        ret
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.BootServices]
        eficall rbx, EFI_BOOT_SERVICES.HandleProtocol, [rax], gopuuid, \
                msg_ask_for_params
        jmp     $

        xor     ebx, ebx
.next_mode:
        call    clearbuf
        mov     eax, ebx
        lea     rdi, [msg]
        call    num2dec

        push    rbx
        eficall [gop_interface], EFI_GRAPHICS_OUTPUT_PROTOCOL.QueryMode, \
                [gop_interface], rbx, gop_info_size, gop_info
        cmp     rax, EFI_SUCCESS
        jnz     .error
        mov     rcx, [gop_info]
        ; PixelBlueGreenRedReserved8BitPerColor
        cmp     [rcx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.PixelFormat], 1
        jnz     .skip
        mov     eax, [rcx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.HorizontalResolution]
        lea     rdi, [msg+4*2]
        call    num2dec
        mov     eax, [rcx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.VerticalResolution]
        lea     rdi, [msg+9*2]
        call    num2dec
;        mov     eax, [rcx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.PixelsPerScanLine]
;        lea     rdi, [msg+14*2]
;        call    num2dec
.skip:
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, msg
        cmp     rax, EFI_SUCCESS
        jnz     .error

        pop     rbx
        inc     rbx
        mov     rcx, [gop_interface]
        mov     rdx, [rcx+EFI_GRAPHICS_OUTPUT_PROTOCOL.Mode]
        cmp     ebx, [rdx+EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE.MaxMode]
        jnz     .next_mode


        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConIn]
        eficall rbx, SIMPLE_INPUT_INTERFACE.Reset, rbx, 1
        cmp     rax, EFI_SUCCESS
        jnz     .error
        xor     ecx, ecx
    @@:
        push    rcx
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConIn]
        eficall rbx, SIMPLE_INPUT_INTERFACE.ReadKeyStroke, rbx, msg
        pop     rcx
        mov     rdx, EFI_DEVICE_ERROR
        cmp     rax, rdx
        jz      .error
        mov     rdx, EFI_NOT_READY
        cmp     rax, rdx
        jz      @b
;        cmp     rax, EFI_SUCCESS
        movzx   eax, word[msg+2]
;jmp .key_done
        cmp     al, 0x0D
        jz      .key_done
        imul    ecx, 10
        sub     eax, '0'
        add     ecx, eax
        jmp     @b
.key_done:
        mov     [cfg_opt_value_vmode], cl
.error:
.done:
        ret

main:
        mov     [efi_handle], rcx
        mov     [efi_table], rdx

        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.Reset, rbx, 1
        test    eax, eax
        jz      @f
        jmp     $       ; what can I do here?
@@:
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \
                msg_u4k_loaded

        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \
                msg_read_options
        call    read_options_from_config

        ; read kernel file
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \
                msg_load_kernel
        push    1
        push    -1
        push    KERNEL_BASE
;        push    kernel_name
        mov     rax, kernel_name
        push    rax
        push    [esp_root]
        call    load_file

        ; read ramdisk image
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \
                msg_load_ramdisk
        push    1
        push    -1
        push    RAMDISK_BASE
;        push    ramdisk_name
        mov     rax, ramdisk_name
        push    rax
        push    [esp_root]
        call    load_file

        ; alloc buffer for devices.dat
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \
                msg_alloc_devicesdat
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.BootServices]
        eficall rbx, EFI_BOOT_SERVICES.AllocatePages, \
                EFI_ALLOCATE_MAX_ADDRESS, EFI_RESERVED_MEMORY_TYPE, 1, \
                devicesdat_data
        cmp     eax, EFI_SUCCESS
        jnz     .error

        ; read devices.dat
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \
                msg_load_devicesdat

        push    0 ; not fatal
        push    [devicesdat_size]
        push    [devicesdat_data]
;        push    devicesdat_name
        mov     rax, devicesdat_name
        push    rax
        push    [esp_root]
        call    load_file
        mov     [devicesdat_size], rax

        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \
                msg_locate_gop_handlers

        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.BootServices]
        eficall rbx, EFI_BOOT_SERVICES.LocateHandle, \
                EFI_LOCATE_SEARCH_TYPE.ByProtocol, gopuuid, 0, \
                gop_buffer_size, gop_buffer
        mov     [status], rax

        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \
                msg_gop_buffer_size
        call    clearbuf
        mov     rax, [gop_buffer_size]
        mov     rdi, msg
        call    num2hex
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, msg

        mov     rax, [status]
        test    eax, eax
        jz      @f
        call    clearbuf
        mov     rdi, msg
        call    num2hex
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \
                msg_error
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, msg
        jmp     $
@@:

        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \
                msg_look_for_gop_handler

        mov     rbx, gop_buffer
.next_gop_handle:
        mov     rax, rbx
        mov     rcx, gop_buffer
        sub     rax, rcx
        cmp     rax, [gop_buffer_size]
        jb      @f
        push    rbx
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \
                msg_error_out_of_handlers
        pop     rbx
        push    rbx
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, msg_error
        pop     rbx
        jmp     $
@@:
        push    rbx
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \
                msg_query_handler
        pop     rbx

        mov     rax, rbx
        push    rbx
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.BootServices]
        eficall rbx, EFI_BOOT_SERVICES.HandleProtocol, [rax], gopuuid, \
                gop_interface
        pop     rbx
;mov rax, 0x8000_0000_0000_0003
        test    eax, eax
        jz      @f
        call    clearbuf
        mov     rdi, msg
        call    num2hex
        push    rbx
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, msg
        pop     rbx

        add     rbx, 8
        jmp     .next_gop_handle
@@:

        call    find_rsdp

        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \
                msg_acpi_tables_done

        cmp     [cfg_opt_used_resolution], 0
        jz      .not_used_resolution
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \
                msg_opt_resolution
        call    clearbuf
        xor     edx, edx
        movzx   eax, [rdx+BOOT_LO.x_res]
        mov     rdi, msg
        call    num2dec
        xor     edx, edx
        movzx   eax, [rdx+BOOT_LO.y_res]
        mov     rdi, msg+8*2
        call    num2dec
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, msg
        call    find_vmode_index_by_resolution
.not_used_resolution:
        cmp     [cfg_opt_used_debug_print], 0
        jz      .not_used_debug_print
        movzx   eax, [cfg_opt_value_debug_print]
        xor     edx, edx
        mov     [rdx+BOOT_LO.debug_print], al
.not_used_debug_print:

        cmp     [cfg_opt_value_ask_params], 0
        jz      @f
        call    ask_for_params
@@:

        movzx   ecx, [cfg_opt_value_vmode]
        eficall [gop_interface], EFI_GRAPHICS_OUTPUT_PROTOCOL.SetMode, \
                [gop_interface], rcx
        test    eax, eax
        jz      @f
        call    clearbuf
        mov     rdi, msg
        call    num2hex
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, msg
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \
                msg_error
        jmp     $
@@:

        mov     rcx, [gop_interface]
        mov     rdx, [rcx+EFI_GRAPHICS_OUTPUT_PROTOCOL.Mode]
        mov     rdi, [rdx+EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE.FrameBufferBase]
        mov     [fb_base], rdi

        mov     ebx, [rdx+EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE.Mode]
        eficall [gop_interface], EFI_GRAPHICS_OUTPUT_PROTOCOL.QueryMode, \
                [gop_interface], rbx, gop_info_size, gop_info
        test    eax, eax
        jz      @f
        jmp     .error
@@:
        mov     rcx, [gop_info]
        mov     eax, [rcx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.HorizontalResolution]
        xor     rdx, rdx
        mov     [rdx+BOOT_LO.x_res], ax
        mov     eax, [rcx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.VerticalResolution]
        mov     [rdx+BOOT_LO.y_res], ax
        mov     eax, [rcx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.PixelsPerScanLine]
        shl     eax, 2
        mov     [rdx+BOOT_LO.pitch], ax

        mov     byte[rdx+BOOT_LO.pci_data+0], 1    ; PCI access mechanism
        mov     byte[rdx+BOOT_LO.pci_data+1], 8    ; last bus, don't know how to count them
        mov     byte[rdx+BOOT_LO.pci_data+2], 0x10 ; PCI version
        mov     byte[rdx+BOOT_LO.pci_data+3], 0x02
        mov     dword[rdx+BOOT_LO.pci_data+4], 0xe3

        ; kernel
;        eficall BootServices, AllocatePages, EFI_RESERVED_MEMORY_TYPE, \
;                450000/0x1000, EFI_ALLOCATE_ADDRESS

        ; ramdisk
;        eficall BootServices, AllocatePages, EFI_RESERVED_MEMORY_TYPE, \
;                2880*512/0x1000, EFI_ALLOCATE_ADDRESS

        call    calc_memmap
;        call    dump_memmap

        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.BootServices]
        eficall rbx, EFI_BOOT_SERVICES.ExitBootServices, [efi_handle], \
                [memory_map_key]
        call    halt_on_error

        cli

        xor     edx, edx
        xor     esi, esi
        mov     [esi+BOOT_LO.bpp], 32
        mov     [esi+BOOT_LO.vesa_mode], dx
        mov     [esi+BOOT_LO.bank_switch], edx
        mov     rdi, [fb_base]
        mov     [esi+BOOT_LO.lfb], edi

        movzx   eax, [cfg_opt_value_mtrr]
        mov     [esi+BOOT_LO.mtrr], al

        movzx   eax, [cfg_opt_value_launcher_start]
        mov     [esi+BOOT_LO.launcher_start], al

        movzx   eax, [cfg_opt_value_debug_print]
        mov     [esi+BOOT_LO.debug_print], al

        mov     [esi+BOOT_LO.dma], dl
;        mov     qword[esi+BOOT_LO.pci_data], 0
        mov     [esi+BOOT_LO.apm_entry], edx
        mov     [esi+BOOT_LO.apm_version], dx
        mov     [esi+BOOT_LO.apm_flags], dx
        mov     [esi+BOOT_LO.apm_code_32], dx
        mov     [esi+BOOT_LO.apm_code_16], dx
        mov     [esi+BOOT_LO.apm_data_16], dx
        mov     [esi+BOOT_LO.bios_hd_cnt], dl

        movzx   eax, [cfg_opt_value_imgfrom]
        mov     [esi+BOOT_LO.rd_load_from], al

        mov     eax, dword[devicesdat_size]
        mov     [rdx+BOOT_LO.devicesdat_size], eax
        mov     eax, dword[devicesdat_data]
        mov     [rdx+BOOT_LO.devicesdat_data], eax

        mov     rsi, cfg_opt_value_syspath
        mov     rdi, BOOT_LO.syspath
        mov     ecx, 0x17
        rep movsb

        lgdt    [cs:GDTR]

        mov     ax, DATA_32_SELECTOR
        mov     ds, ax
        mov     es, ax
        mov     fs, ax
        mov     gs, ax
        mov     ss, ax

        push    CODE_32_SELECTOR
        lea     rax, [.next]
        push    rax
        retf
use32
align 16
.next:
        mov     eax, cr0
        and     eax, not CR0_PG
        mov     cr0, eax

        mov     ecx, MSR_AMD_EFER
        rdmsr
        btr     eax, 8                  ; LME
        wrmsr

        mov     eax, cr4
        and     eax, not CR4_PAE
        mov     cr4, eax

        push    KERNEL_BASE
        retn

use64
.error:
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \
                msg_error
        jmp     $


halt_on_error:
        test    eax, eax
        jz      @f
        call    clearbuf
        mov     rdi, msg
        call    num2hex
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \
                msg_error
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, msg
        jmp     $
@@:
        ret

find_rsdp:
        push    rbx
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \
                msg_look_for_rsdp
        pop     rbx

        mov     rbx, [efi_table]
        mov     rdi, [rbx+EFI_SYSTEM_TABLE.ConfigurationTable]
        mov     rcx, [rbx+EFI_SYSTEM_TABLE.NumberOfTableEntries]
        mov     rax, 0x11d3e4f18868e871
        mov     rdx, 0x81883cc7800022bc
.next_table:
        dec     ecx
        js      .all_tables_done
        cmp     [rdi+0], rax
        jnz     .not_acpi20
        cmp     [rdi+8], rdx
        jnz     .not_acpi20
        mov     rax, [rdi+16]
        mov     rdx, BOOT_LO.acpi_rsdp
        mov     [rdx], eax
        jmp     .all_tables_done
.not_acpi20:
        add     rdi, 24
        jmp     .next_table
.all_tables_done:
        ret

calc_memmap:
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.BootServices]
        eficall rbx, EFI_BOOT_SERVICES.AllocatePages, EFI_ALLOCATE_ANY_PAGES, \
                EFI_RESERVED_MEMORY_TYPE, MEMORY_MAP_SIZE/0x1000, memory_map
        call    halt_on_error

        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.BootServices]
        eficall rbx, EFI_BOOT_SERVICES.GetMemoryMap, memory_map_size, \
                [memory_map], memory_map_key, descriptor_size, descriptor_ver
        call    halt_on_error

        mov     rdi, BOOT_LO.memmap_blocks
        mov     dword[rdi-4], 0 ; memmap_block_cnt
        mov     rsi, [memory_map]
        mov     rbx, rsi
        add     rbx, [memory_map_size]
.next_descr:
        call    add_uefi_memmap
        add     rsi, [descriptor_size]
        cmp     rsi, rbx
        jb      .next_descr
        ret

dump_memmap:
        xor     eax, eax
        mov     rsi, BOOT_LO.memmap_blocks
        mov     ebx, [rax+BOOT_LO.memmap_block_cnt]

        call    clearbuf
        mov     eax, ebx
        mov     rdi, msg
        call    num2dec
        push    rbx
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \
                msg_memmap
        pop     rbx
        push    rbx
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, msg
        pop     rbx
        call    clearbuf
.next_mapping:
        dec     ebx
        js      .done
        mov     rax, rsi
        mov     rcx, BOOT_LO.memmap_blocks
        sub     rax, rcx
        mov     ecx, sizeof.e820entry
        xor     edx, edx
        div     ecx
        mov     rdi, msg
        call    num2dec
        mov     rax, [rsi+e820entry.addr]
        mov     rdi, msg+4*2
        call    num2hex
        mov     rax, [rsi+e820entry.size]
        mov     rdi, msg+24*2
        call    num2hex
        push    rbx
        mov     rbx, [efi_table]
        mov     rbx, [rbx+EFI_SYSTEM_TABLE.ConOut]
        eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, msg
        pop     rbx
        add     rsi, sizeof.e820entry
        jmp     .next_mapping
.done:
        ret


; linux/arch/x86/platform/efi/efi.c
; do_add_efi_memmap
add_uefi_memmap:
        xor     eax, eax
        cmp     [rax+BOOT_LO.memmap_block_cnt], MAX_MEMMAP_BLOCKS
        jz      .done

        mov     rax, [rsi+EFI_MEMORY_DESCRIPTOR.PhysicalStart]
        mov     [rdi+e820entry.addr], rax

        mov     rax, [rsi+EFI_MEMORY_DESCRIPTOR.NumberOfPages]
        shl     rax, 12
        mov     [rdi+e820entry.size], rax

        mov     ecx, [rsi+EFI_MEMORY_DESCRIPTOR.Type]
        cmp     ecx, EFI_LOADER_CODE
        jz      .mem_ram_if_wb
        cmp     ecx, EFI_LOADER_DATA
        jz      .mem_ram_if_wb
        cmp     ecx, EFI_BOOT_SERVICES_CODE
        jz      .mem_ram_if_wb
        cmp     ecx, EFI_BOOT_SERVICES_DATA
        jz      .mem_ram_if_wb
        cmp     ecx, EFI_CONVENTIONAL_MEMORY
        jz      .mem_ram_if_wb
        cmp     ecx, EFI_ACPI_RECLAIM_MEMORY
        mov     eax, E820_ACPI
        jz      .type_done
        cmp     ecx, EFI_ACPI_MEMORY_NVS
        mov     eax, E820_NVS
        jz      .type_done
        cmp     ecx, EFI_UNUSABLE_MEMORY
        mov     eax, E820_UNUSABLE
        jz      .type_done
        cmp     ecx, EFI_PERSISTENT_MEMORY
        mov     eax, E820_PMEM
        jz      .type_done
        jmp     .reserved
.mem_ram_if_wb:
        test    [rsi+EFI_MEMORY_DESCRIPTOR.Attribute], dword EFI_MEMORY_WB
        mov     eax, E820_RAM
        jnz     .type_done
.reserved:
        mov     eax, E820_RESERVED
.type_done:
        mov     [rdi+e820entry.type], eax
        cmp     eax, E820_RAM
        jnz     @f
        xor     eax, eax
        inc     [rax+BOOT_LO.memmap_block_cnt]
        add     rdi, sizeof.e820entry
@@:
.done:
        ret


num2dec:
        push    rax rbx rcx rdx rsi rdi

        xor     ecx, ecx
        mov     ebx, 10
.next_digit:
        xor     edx, edx
        div     ebx
        push    rdx
        inc     ecx
        test    eax, eax
        jnz     .next_digit

.next_char:
        pop     rax
        add     eax, '0'
        stosw
        loop    .next_char

        pop     rdi rsi rdx rcx rbx rax
        ret


num2hex:
        push    rax rbx rcx rdx rsi rdi

        xchg    rdx, rax
        mov     ecx, 16
.next_tetra:
        rol     rdx, 4
        movzx   eax, dl
        and     eax, 0x0f
        movzx   eax, byte[hex+eax]
        stosw
        loop    .next_tetra

        pop     rdi rsi rdx rcx rbx rax
        ret

hex db '0123456789ABCDEF'

clearbuf:
        push    rax rbx rcx rdx rsi rdi
        mov     eax, 0x0020
        mov     ecx, 79
        mov     rdi, msg
        rep stosw
        pop     rdi rsi rdx rcx rbx rax
        ret

section '.data' data readable writeable
efi_handle  dq 0
efi_table   dq 0
uefi_rsptmp dq 0

GDTR:
        dw 4*8-1
        dq GDT
GDT:
        dw 0, 0, 0, 0
        dw 0FFFFh,0,9A00h,0CFh          ; 32-bit code
        dw 0FFFFh,0,9200h,0CFh          ; flat data
        dw 0FFFFh,0,9A00h,0AFh          ; 64-bit code


fb_base         dq 0

gopuuid         db EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID
gop_buffer_size dq GOP_BUFFER_SIZE
gop_handle      dq 0
gop_interface   dq 0
gop_info_size   dq 0
gop_info        dq 0

lipuuid         db EFI_LOADED_IMAGE_PROTOCOL_GUID
lip_buffer_size dq LIP_BUFFER_SIZE
lip_handle      dq 0
lip_interface   dq 0

sfspguid        db EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID
sfsp_interface  dq 0

esp_root        dq ?
file_handle     dq ?
file_name       du '\EFI\KOLIBRIOS\KOLIBRI.INI',0
kernel_name     du '\EFI\KOLIBRIOS\KOLIBRI.KRN',0
ramdisk_name    du '\EFI\KOLIBRIOS\KOLIBRI.IMG',0
devicesdat_name du '\EFI\KOLIBRIOS\DEVICES.DAT',0
file_buffer_size dq FILE_BUFFER_SIZE-1  ; leave the last byte for \0

config_options  dq cfg_opt_name_resolution, cfg_opt_func_resolution, \
                   cfg_opt_cmnt_resolution, \
                   cfg_opt_name_acpi,  cfg_opt_func_acpi, cfg_opt_cmnt_acpi, \
                   cfg_opt_name_debug_print, cfg_opt_func_debug_print, \
                   cfg_opt_cmnt_debug_print, \
                   cfg_opt_name_launcher_start, cfg_opt_func_launcher_start, \
                   cfg_opt_cmnt_launcher_start, \
                   cfg_opt_name_mtrr,  cfg_opt_func_mtrr, cfg_opt_cmnt_mtrr, \
                   cfg_opt_name_ask_params,  cfg_opt_func_ask_params, \
                   cfg_opt_cmnt_ask_params, \
                   cfg_opt_name_imgfrom, cfg_opt_func_imgfrom, \
                   cfg_opt_cmnt_imgfrom, \
                   cfg_opt_name_syspath, cfg_opt_func_syspath, \
                   cfg_opt_cmnt_syspath, \
                   0

cfg_opt_name_resolution     db "resolution",0
cfg_opt_name_acpi           db "acpi",0
cfg_opt_name_debug_print    db "debug_print",0
cfg_opt_name_launcher_start db "launcher_start",0
cfg_opt_name_mtrr           db "mtrr",0
cfg_opt_name_ask_params     db "ask_params",0
cfg_opt_name_imgfrom        db "imgfrom",0
cfg_opt_name_syspath        db "syspath",0

cfg_opt_cmnt_resolution     db "# Graphic mode",0
cfg_opt_cmnt_acpi           db "# ACPI settings",0xa, \
                               "#   0: don't use",0xa, \
                               "#   1: parse ACPI tables",0xa, \
                               "#   2: + call _PIC method",0xa, \
                               "#   3: + get APIC interrupts",0xa,0
cfg_opt_cmnt_debug_print    db "# Duplicate debug output to the screen",0
cfg_opt_cmnt_launcher_start db "# Start LAUNCHER app after kernel is loaded",0
cfg_opt_cmnt_mtrr           db "# Configure MTRR's",0
cfg_opt_cmnt_ask_params     db "# Interrupt booting to ask the user for boot", \
                               " params",0
cfg_opt_cmnt_imgfrom        db "# Where to load ramdisk image from",0
cfg_opt_cmnt_syspath        db "# Path to /sys directory",0

cfg_opt_used_resolution     db 0
cfg_opt_used_acpi           db 0
cfg_opt_used_debug_print    db 0
cfg_opt_used_launcher_start db 0
cfg_opt_used_mtrr           db 0
cfg_opt_used_ask_params     db 0
cfg_opt_used_imgfrom        db 0
cfg_opt_used_syspath        db 0

cfg_opt_value_vmode          db 0
cfg_opt_value_acpi           db 0
cfg_opt_value_debug_print    db 0
cfg_opt_value_launcher_start db 1
cfg_opt_value_mtrr           db 0
cfg_opt_value_ask_params     db 0
cfg_opt_value_imgfrom        db RD_LOAD_FROM_MEMORY
cfg_opt_value_syspath        db "/RD/1",0
                             rb 20

memory_map_key  dq 0
descriptor_size dq 0
descriptor_ver  dq 0
memory_map_size dq MEMORY_MAP_SIZE

msg_u4k_loaded            du "uefi64kos loaded",13,10,0
msg_read_options          du "Read options from config file",13,10,0
msg_file_size             du "File size:",13,10,0
msg_parsing_config        du "Parsing config file",13,10,0
msg_load_kernel           du "Load kernel",13,10,0
msg_load_ramdisk          du "Load ramdisk",13,10,0
msg_load_devicesdat       du "Load DEVICES.DAT",13,10,0
msg_alloc_devicesdat      du "Allocate memory for DEVICES.DAT",13,10,0
msg_locate_gop_handlers   du "Locate GOP handlers",13,10,0
msg_look_for_gop_handler  du "Look for GOP handler",13,10,0
msg_query_handler         du "Query handler",13,10,0
msg_query_vmode           du "Query vmode",13,10,0
msg_vmode_found           du "Video mode found",13,10,0
msg_look_for_rsdp         du "Look for RSDP",13,10,0
msg_rsdp_found            du "RSDP found",13,10,0
msg_acpi_tables_done      du "ACPI tables done",13,10,0
msg_ask_for_params        du "Ask for params",13,10,0
msg_set_graphic_mode      du "Set graphic mode",13,10,0
msg_success               du "Success!",13,10,0
msg_gop_buffer_size       du "GOP buffer size",13,10,0
msg_opt_resolution        du "Option resolution: ",0
msg_memmap                du "Memmap",13,10,0
msg_error                 du "Error!",13,10,0
msg_error_efi_lip_handle  du "efi_handle can't handle LIP",13,10,0
msg_error_lip_dev_sfsp    du "LIP device handle can't handle SFSP",13,10,0
msg_error_sfsp_openvolume du "SFSP OpenVolume failed",13,10,0
msg_error_no_such_vmode   du "No such vmode",13,10,0
msg_error_out_of_handlers du "Out of handlers",13,10,0
msg_error_open_file       du "Error: can't open file ",0
msg_error_exit_boot_services du "Error: Exit boot services",13,10,0
msg                       du 79 dup " ",13,10,0

efi_fs_info_id db EFI_FILE_SYSTEM_INFO_ID
efi_fs_info_size dq sizeof.EFI_FILE_SYSTEM_INFO
efi_fs_info EFI_FILE_SYSTEM_INFO

memory_map      dq ?
gop_buffer      rq GOP_BUFFER_SIZE/8
devicesdat_data dq 0xffffffff
devicesdat_size dq 0x1000
status          dq ?

section '.reloc' fixups data discardable
