; vi:ft=nasm: bits 64 org 1000h BOOT_SERVICES equ 96 BOOT_GET_MEMORY_MAP equ 56 BOOT_EXIT_BOOT_SERVICES equ 232 start: mov [efihandle], rcx mov [systab], rdx ; retrieve memory map ; args: RCX, RDX, R8, and R9. lea rcx, [mmap_size] lea rdx, [mmapbuf] lea r8, [mmap_key] lea r9, [mmap_descsz] lea rax, [mmap_descver] push rax sub rsp, 32 mov rax, [systab] mov rbx, [rax + BOOT_SERVICES] call [rbx + BOOT_GET_MEMORY_MAP] add rsp, 40 ; exit boot services mov rcx, [efihandle] mov rdx, [mmap_key] mov rax, [systab] mov rbx, [rax + BOOT_SERVICES] call [rbx + BOOT_EXIT_BOOT_SERVICES] ; move code to absolute 1000h call get_rip .after_call: sub rax, .after_call - start mov rsi, rax ; source address mov rdi, start ; destination mov rcx, end - start shr rcx, 2 ; dwords cld rep movsd ; copy code to make it absolute mov rax, .abs jmp rax .abs: call setup_serial ; switch to 32-bit compatibility long mode lgdt [gdtlim] push word 8 push qword start32 retfq .hang: jmp .hang get_rip: mov rax, [rsp] ret UART_DATA equ 0x3f8 UART_DIVLO equ 0x3f8 UART_DIVHI equ 0x3f9 UART_FIFO equ 0x3fa UART_LCTL equ 0x3fb UART_MCTL equ 0x3fc UART_LSTAT equ 0x3fd DIV_9600 equ 115200 / 9600 LCTL_8N1 equ 0x03 LCTL_DLAB equ 0x80 FIFO_ENABLE_CLEAR equ 0x07 MCTL_DTR_RTS_OUT2 equ 0x0b LST_TREG_EMPTY equ 0x20 ; serial port setup setup_serial: ; set clock divisor mov al, LCTL_DLAB mov dx, UART_LCTL out dx, al mov ax, DIV_9600 mov dx, UART_DIVLO out dx, al mov al, ah mov dx, UART_DIVHI out dx, al ; set format 8n1 mov al, LCTL_8N1 mov dx, UART_LCTL out dx, al ; clear and enable fifo mov al, FIFO_ENABLE_CLEAR mov dx, UART_FIFO out dx, al ; assert RTS and DTR mov al, MCTL_DTR_RTS_OUT2 mov dx, UART_MCTL out dx, al ret align 8 efihandle dq 0 systab dq 0 ; memory map data mmap_size dq 4096 mmap_key dq 0 mmap_descsz dq 0 mmap_descver dq 0 align 4096 mmapbuf: times 4096 db 0 str_hello db 'hello!',13,10,0 align 4 gdtlim dw 31 gdtbase dq gdt align 8 gdt: ; 0: null segment dd 0, 0 ; 1: code - base:0, lim:4g, G:4k, 32bit, avl, pres|app, dpl:0, type:code/non-conf/rd dd 0x0000ffff dd 0x00cf9a00 ; 2: data - base:0, lim:4g, G:4k, 32bit, avl, pres|app, dpl:0, type:data/rw dd 0x0000ffff dd 0x00cf9200 ; 3: code16 dd 0x0000ffff dd 0x00009a00 bits 32 start32: mov ax, 10h mov ds, ax mov ss, ax mov es, ax mov gs, ax mov fs, ax ; disable paging to deactivate long mode mov eax, cr0 and eax, 7fffffffh mov cr0, eax ; disable long mode (EFER.LME = 0) ; TODO: EFER is MSR c0000080, LME is bit 8 mov ecx, 0c0000080h rdmsr and eax, 0fffffeffh wrmsr ; load 16bit code segment and jump to 16bit code jmp 0x18:start16 .hang: hlt jmp .hang ser_putchar: mov ah, al ; wait until transmit register is empty mov dx, UART_LSTAT .wait: in al, dx and al, LST_TREG_EMPTY jz .wait mov dx, UART_DATA mov al, ah out dx, al ret ser_putstr: mov al, [esi] test al, al jz .done call ser_putchar inc esi jmp ser_putstr .done: ret align 4 ; real mode IDTR pseudo-descriptor pointing to the IVT at addr 0 dw 0 rmidt: dw 3ffh dd 0 bits 16 start16: ; disable protection mov eax, cr0 and eax, 0fffffffeh mov cr0, eax ; load cs <- 0 jmp 0:.loadcs0 .loadcs0: ; zero data segments xor ax, ax mov ds, ax mov es, ax mov fs, ax mov gs, ax ; move stack to the top of 640k mov ax, 9000h mov ss, ax xor sp, sp ; run 16bit video bios test mov ax, 13h ;int 10h mov ax, 0a000h mov es, ax xor di, di mov cx, 32000 mov ax, 6767h rep stosw .hang: hlt jmp .hang align 4 end: