uefi32kos.asm
mxlgv_KolibriDev/src/kernel/bootloader/uefi4kos/uefi32kos.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 pe efi
entry main

section '.text' code executable readable

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

MEMORY_MAP_SIZE = 0x4000
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

; 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

proc load_file stdcall uses ebx esi edi, _root, _name, _buffer, _size, _fatal
        mov     eax, [_root]
        ccall   [eax+EFI_FILE_PROTOCOL.Open], eax, file_handle, [_name], \
                EFI_FILE_MODE_READ, 0
        test    eax, eax
        jz      @f
        xor     eax, eax
        cmp     [_fatal], 1
        jnz     .done
        mov     ebx, [efi_table]
        mov     ebx, [ebx+EFI_SYSTEM_TABLE.ConOut]
        ccall   [ebx+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], ebx, \
                msg_error_open_file
        ccall   [ebx+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], ebx, \
                [_name]
        jmp     $
@@:
        mov     eax, [file_handle]
        lea     ecx, [_size]
        ccall   [eax+EFI_FILE_PROTOCOL.Read], eax, ecx, [_buffer]
        mov     eax, [file_handle]
        ccall   [eax+EFI_FILE_PROTOCOL.Close], eax
        mov     eax, [_size]
.done:
        ret
endp

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

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

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

proc skip_line
        call    skip_until_newline
        call    skip_newline
        ret
endp

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

proc parse_option
        mov     ebx, config_options-3*4
.try_next_option:
        add     ebx, 3*4
        mov     edi, esi
        mov     edx, [ebx]      ; option name
        test    edx, edx
        jz      .done
.next_char:
        cmp     byte[edx], 0
        jnz     @f
        cmp     byte[edi], '='
        jz      .opt_name_ok
@@:
        cmp     byte[edi], 0
        jz      .done
        movzx   eax, byte[edi]
        cmp     [edx], al
        jnz     .try_next_option
        inc     edi
        inc     edx
        jmp     .next_char
.opt_name_ok:
        inc     edi
        mov     esi, edi
        call    dword[ebx+4]
.done:
        ret
endp

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

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

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

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

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

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

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

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

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

proc parse_config stdcall uses ebx esi edi, _buffer
;        mov     esi, [_buffer]
        mov     esi, KERNEL_BASE
.next_line:
        call    parse_line
        cmp     byte[esi], 0
        jnz     .next_line
        ret
endp

proc read_options_from_config stdcall uses ebx esi edi
        mov     ebx, [efi_table]
        mov     ebx, [ebx+EFI_SYSTEM_TABLE.BootServices]
        ccall   [ebx+EFI_BOOT_SERVICES.HandleProtocol], [efi_handle], \
                lipuuid, lip_interface
        test    eax, eax
        jnz     .error
        mov     eax, [lip_interface]

        mov     ebx, [efi_table]
        mov     ebx, [ebx+EFI_SYSTEM_TABLE.BootServices]
        ccall   [ebx+EFI_BOOT_SERVICES.HandleProtocol], \
                [eax+EFI_LOADED_IMAGE_PROTOCOL.DeviceHandle], sfspguid, \
                sfsp_interface
        test    eax, eax
        jnz     .error

        mov     eax, [sfsp_interface]
        ccall   [eax+EFI_SIMPLE_FILE_SYSTEM_PROTOCOL.OpenVolume], eax, esp_root
        test    eax, eax
        jnz     .error

        stdcall load_file, [esp_root], file_name, KERNEL_BASE, \
                FILE_BUFFER_SIZE, 0
        test    eax, eax
        jz      @f
        stdcall parse_config, KERNEL_BASE
@@:
.error:
        ret
endp

proc print_vmode uses eax ebx ecx esi edi, _gop_if
        mov     ebx, [_gop_if]
        call    clearbuf
        mov     eax, [ebx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.HorizontalResolution]
        mov     edi, msg
        call    num2dec
        mov     eax, [ebx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.VerticalResolution]
        mov     edi, msg+8*2
        call    num2dec

        mov     eax, [ebx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.PixelFormat]
        mov     edi, msg+16*2
        call    num2dec

        mov     eax, [ebx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.PixelsPerScanLine]
        mov     edi, msg+24*2
        call    num2dec
        mov     eax, [esi+EFI_SYSTEM_TABLE.ConOut]
        ccall   [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, msg
        ret
endp

proc find_vmode_index_by_resolution uses ebx esi edi
        mov     [cfg_opt_value_vmode], 0
.next_mode:
;        mov     eax, [esi+EFI_SYSTEM_TABLE.ConOut]
;        ccall   [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \
;                msg_query_vmode

        movzx   ecx, [cfg_opt_value_vmode]
        mov     eax, [gop_interface]
        ccall   [eax+EFI_GRAPHICS_OUTPUT_PROTOCOL.QueryMode], eax, ecx, \
                gop_info_size, gop_info
        test    eax, eax
        jz      @f
        call    clearbuf
        mov     edi, msg
        call    num2hex
        mov     eax, [esi+EFI_SYSTEM_TABLE.ConOut]
        ccall   [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, msg
        jmp     .skip_mode
@@:
        mov     ecx, [gop_info]
        stdcall print_vmode, ecx
        ; PixelBlueGreenRedReserved8BitPerColor
        cmp     [ecx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.PixelFormat], 1
        jnz     .skip_mode
        xor     edx, edx
        mov     eax, [ecx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.HorizontalResolution]
        cmp     ax, [edx+BOOT_LO.x_res]
        jnz     .skip_mode
        mov     eax, [ecx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.VerticalResolution]
        cmp     ax, [edx+BOOT_LO.y_res]
        jnz     .skip_mode
        jmp     .done
.skip_mode:
        inc     [cfg_opt_value_vmode]
        movzx   eax, [cfg_opt_value_vmode]
        mov     ecx, [gop_interface]
        mov     edx, [ecx+EFI_GRAPHICS_OUTPUT_PROTOCOL.Mode]
        cmp     eax, [edx+EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE.MaxMode]
        jnz     .next_mode
        mov     [cfg_opt_used_resolution], 0
        mov     [cfg_opt_value_ask_params], 1

        mov     eax, [esi+EFI_SYSTEM_TABLE.ConOut]
        ccall   [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \
                msg_error_no_such_vmode
        mov     eax, [esi+EFI_SYSTEM_TABLE.ConOut]
        ccall   [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, msg_error
        jmp     $
.error:
.done:
        mov     eax, [esi+EFI_SYSTEM_TABLE.ConOut]
        ccall   [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \
                msg_vmode_found
        ret
endp

proc ask_for_params
        mov     eax, [esi+EFI_SYSTEM_TABLE.ConOut]
        ccall   [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \
                msg_ask_for_params
        jmp     $
.error:
.done:
        ret
endp

main:
        mov     esi, [esp+4]
        mov     [efi_handle], esi
        mov     esi, [esp+8]
        mov     [efi_table], esi

        mov     eax, [esi+EFI_SYSTEM_TABLE.ConOut]
        ccall   [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.Reset], eax, 1
        mov     eax, [esi+EFI_SYSTEM_TABLE.ConOut]
        ccall   [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \
                msg_u4k_loaded

        mov     eax, [esi+EFI_SYSTEM_TABLE.ConOut]
        ccall   [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \
                msg_read_options
        call    read_options_from_config

        mov     eax, [esi+EFI_SYSTEM_TABLE.ConOut]
        ccall   [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \
                msg_load_kernel
        stdcall load_file, [esp_root], kernel_name, KERNEL_BASE, -1, 1

        mov     eax, [esi+EFI_SYSTEM_TABLE.ConOut]
        ccall   [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \
                msg_load_ramdisk
        stdcall load_file, [esp_root], ramdisk_name, RAMDISK_BASE, -1, 1

        mov     eax, [esi+EFI_SYSTEM_TABLE.ConOut]
        ccall   [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \
                msg_alloc_devicesdat

        mov     eax, [esi+EFI_SYSTEM_TABLE.BootServices]
        ccall   [eax+EFI_BOOT_SERVICES.AllocatePages], \
                EFI_ALLOCATE_MAX_ADDRESS, EFI_RESERVED_MEMORY_TYPE, 1, \
                devicesdat_data
        call    halt_on_error

        mov     eax, [esi+EFI_SYSTEM_TABLE.ConOut]
        ccall   [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \
                msg_load_devicesdat

        ccall   load_file, [esp_root], devicesdat_name, [devicesdat_data], \
                [devicesdat_size], 0
        mov     [devicesdat_size], eax

        mov     eax, [esi+EFI_SYSTEM_TABLE.ConOut]
        ccall   [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \
                msg_locate_gop_handlers

        mov     eax, [esi+EFI_SYSTEM_TABLE.BootServices]
        ccall   [eax+EFI_BOOT_SERVICES.LocateHandle], \
                EFI_LOCATE_SEARCH_TYPE.ByProtocol, gopuuid, 0, \
                gop_buffer_size, gop_buffer
        mov     [status], eax

        mov     eax, [esi+EFI_SYSTEM_TABLE.ConOut]
        ccall   [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \
                msg_gop_buffer_size
        call    clearbuf
        mov     eax, [gop_buffer_size]
        mov     edi, msg
        call    num2hex
        mov     eax, [esi+EFI_SYSTEM_TABLE.ConOut]
        ccall   [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, msg

        mov     eax, [status]
        call    halt_on_error

        mov     eax, [esi+EFI_SYSTEM_TABLE.ConOut]
        ccall   [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \
                msg_look_for_gop_handler

        mov     ebx, gop_buffer
.next_gop_handle:
        mov     eax, ebx
        sub     eax, gop_buffer
        cmp     eax, [gop_buffer_size]
        jb      @f
        mov     eax, [esi+EFI_SYSTEM_TABLE.ConOut]
        ccall   [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \
                msg_error_out_of_handlers
        mov     eax, [esi+EFI_SYSTEM_TABLE.ConOut]
        ccall   [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, msg_error
        jmp     $
@@:
        mov     eax, [esi+EFI_SYSTEM_TABLE.ConOut]
        ccall   [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \
                msg_query_handler

        mov     eax, [esi+EFI_SYSTEM_TABLE.BootServices]
        ccall   [eax+EFI_BOOT_SERVICES.HandleProtocol], \
                [ebx], gopuuid, gop_interface
;mov eax, 0x80000003
        test    eax, eax
        jz      @f
        call    clearbuf
        mov     edi, msg
        call    num2hex
        mov     eax, [esi+EFI_SYSTEM_TABLE.ConOut]
        ccall   [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, msg

        add     ebx, 4
        jmp     .next_gop_handle
@@:

        call    find_rsdp

        mov     eax, [esi+EFI_SYSTEM_TABLE.ConOut]
        ccall   [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \
                msg_acpi_tables_done

        cmp     [cfg_opt_used_resolution], 0
        jz      .not_used_resolution
        mov     eax, [esi+EFI_SYSTEM_TABLE.ConOut]
        ccall   [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \
                msg_opt_resolution
        call    clearbuf
        xor     edx, edx
        movzx   eax, [edx+BOOT_LO.x_res]
        mov     edi, msg
        call    num2dec
        xor     edx, edx
        movzx   eax, [edx+BOOT_LO.y_res]
        mov     edi, msg+8*2
        call    num2dec
        mov     eax, [esi+EFI_SYSTEM_TABLE.ConOut]
        ccall   [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, 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     [edx+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]
        mov     eax, [gop_interface]
        ccall   [eax+EFI_GRAPHICS_OUTPUT_PROTOCOL.SetMode], eax, ecx
        call    halt_on_error

        mov     ecx, [gop_interface]
        mov     edx, [ecx+EFI_GRAPHICS_OUTPUT_PROTOCOL.Mode]
        mov     edi, [edx+EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE.FrameBufferBase.lo]
        mov     [fb_base], edi


        mov     ebx, [edx+EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE.Mode]
        mov     eax, [gop_interface]
        ccall   [eax+EFI_GRAPHICS_OUTPUT_PROTOCOL.QueryMode], eax, ebx, \
                gop_info_size, gop_info
        test    eax, eax
        jnz     .error
        mov     ecx, [gop_info]
        mov     eax, [ecx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.HorizontalResolution]
        xor     edx, edx
        mov     [edx+BOOT_LO.x_res], ax
        mov     eax, [ecx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.VerticalResolution]
        mov     [edx+BOOT_LO.y_res], ax
        mov     eax, [ecx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.PixelsPerScanLine]
        shl     eax, 2
        mov     [edx+BOOT_LO.pitch], ax

        mov     byte[edx+BOOT_LO.pci_data+0], 1    ; PCI access mechanism
        mov     byte[edx+BOOT_LO.pci_data+1], 8    ; last bus, don't know how to count them
        mov     byte[edx+BOOT_LO.pci_data+2], 0x10 ; PCI version
        mov     byte[edx+BOOT_LO.pci_data+3], 0x02
        mov     dword[edx+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     eax, [efi_table]
        mov     eax, [eax+EFI_SYSTEM_TABLE.BootServices]
        ccall   [eax+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     edi, [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     [edx+BOOT_LO.devicesdat_size], eax
        mov     eax, dword[devicesdat_data]
        mov     [edx+BOOT_LO.devicesdat_data], eax

        mov     esi, cfg_opt_value_syspath
        mov     edi, 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     eax, [.next]
        push    eax
        retf

.next:
        mov     eax, cr0
        and     eax, not CR0_PG
        mov     cr0, eax

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

        push    KERNEL_BASE
        retn

.error:
        mov     esi, [efi_table]
        mov     eax, [esi+EFI_SYSTEM_TABLE.ConOut]
        ccall   [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \
                msg_error
        jmp     $

halt_on_error:
        test    eax, eax
        jz      @f
        call    clearbuf
        mov     edi, msg
        call    num2hex
        mov     eax, [esi+EFI_SYSTEM_TABLE.ConOut]
        ccall   [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \
                msg_error
        mov     eax, [esi+EFI_SYSTEM_TABLE.ConOut]
        ccall   [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, msg
        jmp     $
@@:
        ret

proc find_rsdp
        mov     eax, [esi+EFI_SYSTEM_TABLE.ConOut]
        ccall   [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \
                msg_look_for_rsdp

        mov     edi, [esi+EFI_SYSTEM_TABLE.ConfigurationTable]
        mov     ecx, [esi+EFI_SYSTEM_TABLE.NumberOfTableEntries]
.next_table:
        dec     ecx
        js      .all_tables_done
        ; EFI_ACPI_TABLE_GUID
        cmp     dword[edi+EFI_CONFIGURATION_TABLE.VendorGUID+0x0], 0x8868e871
        jnz     .not_acpi20
        cmp     dword[edi+EFI_CONFIGURATION_TABLE.VendorGUID+0x4], 0x11d3e4f1
        jnz     .not_acpi20
        cmp     dword[edi+EFI_CONFIGURATION_TABLE.VendorGUID+0x8], 0x800022bc
        jnz     .not_acpi20
        cmp     dword[edi+EFI_CONFIGURATION_TABLE.VendorGUID+0xc], 0x81883cc7
        jnz     .not_acpi20
        mov     eax, [edi+EFI_CONFIGURATION_TABLE.VendorTable]
        mov     edx, BOOT_LO.acpi_rsdp
        mov     [edx], eax
        mov     eax, [esi+EFI_SYSTEM_TABLE.ConOut]
        ccall   [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \
                msg_rsdp_found
        jmp     .all_tables_done
.not_acpi20:
        add     edi, sizeof.EFI_CONFIGURATION_TABLE
        jmp     .next_table
.all_tables_done:
        ret
endp

proc calc_memmap
        mov     eax, [esi+EFI_SYSTEM_TABLE.BootServices]
        ccall   [eax+EFI_BOOT_SERVICES.AllocatePages], EFI_ALLOCATE_ANY_PAGES, \
                EFI_RESERVED_MEMORY_TYPE, MEMORY_MAP_SIZE/0x1000, memory_map
        call    halt_on_error

        mov     eax, [esi+EFI_SYSTEM_TABLE.BootServices]
        ccall   [eax+EFI_BOOT_SERVICES.GetMemoryMap], memory_map_size, \
                [memory_map], memory_map_key, descriptor_size, descriptor_ver
        call    halt_on_error

        push    esi
        mov     edi, BOOT_LO.memmap_blocks
        mov     dword[edi-4], 0 ; memmap_block_cnt
        mov     esi, [memory_map]
        mov     ebx, esi
        add     ebx, [memory_map_size]
.next_descr:
        call    add_uefi_memmap
        add     esi, [descriptor_size]
        cmp     esi, ebx
        jb      .next_descr
        pop     esi
        ret
endp

; linux/arch/x86/platform/efi/efi.c
; do_add_efi_memmap
proc add_uefi_memmap
        cmp     [BOOT_LO.memmap_block_cnt], MAX_MEMMAP_BLOCKS
        jz      .done

        mov     eax, [esi+EFI_MEMORY_DESCRIPTOR.PhysicalStart.lo]
        mov     edx, [esi+EFI_MEMORY_DESCRIPTOR.PhysicalStart.hi]
        mov     [edi+e820entry.addr.lo], eax
        mov     [edi+e820entry.addr.hi], edx

        mov     eax, [esi+EFI_MEMORY_DESCRIPTOR.NumberOfPages.lo]
        mov     edx, [esi+EFI_MEMORY_DESCRIPTOR.NumberOfPages.hi]
        shld    edx, eax, 12
        shl     eax, 12
        mov     [edi+e820entry.size.lo], eax
        mov     [edi+e820entry.size.hi], edx

        mov     ecx, [esi+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    [esi+EFI_MEMORY_DESCRIPTOR.Attribute.lo], EFI_MEMORY_WB
        mov     eax, E820_RAM
        jnz     .type_done
.reserved:
        mov     eax, E820_RESERVED
.type_done:
        mov     [edi+e820entry.type], eax
        cmp     eax, E820_RAM
        jnz     @f
        inc     [BOOT_LO.memmap_block_cnt]
        add     edi, sizeof.e820entry
@@:
.done:
        ret
endp


proc num2dec
        pushad

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

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

        popad
        ret
endp


proc num2hex
        pushad

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

        popad
        ret
endp


hex db '0123456789ABCDEF'

proc clearbuf
        pushad
        mov     eax, 0x0020
        mov     ecx, 79
        mov     edi, msg
        rep stosw
        popad
        ret
endp

section '.data' data readable writeable
efi_handle dd 0
efi_table  dd 0

GDTR:
        dw 3*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


fb_base         dd 0

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

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

sfspguid        db EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID
sfsp_interface  dd 0

esp_root        dd ?
file_handle     dd ?
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 dd FILE_BUFFER_SIZE-1  ; leave the last byte for \0

config_options  dd 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  dd 0
descriptor_size dd 0
descriptor_ver  dd 0
memory_map_size dd MEMORY_MAP_SIZE

msg_u4k_loaded            du "uefi32kos loaded",13,10,0
msg_read_options          du "Read options from 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_error                 du "Error!",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                       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      dd ?
gop_buffer      rd GOP_BUFFER_SIZE/4
devicesdat_data dd 0xffffffff
devicesdat_size dd 0x1000
status dd ?

section '.reloc' fixups data discardable
