bits 64 org 1000h %include "serial.inc" 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 get_rip: mov rax, [rsp] ret ; 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 ; ----------- 32bit code ---------- 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 IVT descriptor cli ; just to make sure lidt [rmidt] ; load 16bit code segment and jump to 16bit code jmp 0x18:start16 ; ---------- 16bit real mode code ---------- 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 call print_ivt detect_vbios: ; make sure we have video bios vector installed in 10h mov ax, [10h * 4 + 2] test ax, ax jnz .notnull call init_vbios jnc vbios_present jmp halt .notnull: ; beyond being non-zero, the video bios should really be at c0000h cmp ax, 0c000h jz vbios_present ; unexpected address, make sure it starts with 0aa55h mov es, ax xor bx, bx mov ax, [es:bx] cmp ax, 0aa55h call init_vbios ; seems like garbage, try init jc halt vbios_present: mov si, msg_vbios_test call ser_putstr ; run 16bit video bios test mov ax, 13h int 10h ; setup palette mov dx, 3c8h xor al, al out dx, al inc dx xor cl, cl .pal: mov al, cl out dx, al xor al, al out dx, al mov al, cl not al out dx, al inc cl jnz .pal ; fill framebuffer with xor pattern mov ax, 0a000h mov es, ax xor di, di xor cx, cx .yloop: xor dx, dx .xloop: mov ax, dx xor ax, cx stosb inc dx cmp dx, 320 jnz .xloop inc cx cmp cx, 200 jnz .yloop ; halt for ever halt: hlt jmp halt init_vbios: mov ax, 0c000h mov es, ax xor bx, bx cmp word [es:bx], 0aa55h jz .foundsig mov si, err_vbios_notfound call ser_putstr mov ax, [es:bx] call printhex16 mov al, 13 call ser_putchar mov al, 10 call ser_putchar stc ret .foundsig: mov si, msg_vbios_init call ser_putstr ; don't bother with CRC, just call it push es push word 4 mov bp, sp call far [bp] add sp, 4 clc ret msg_vbios_test db 'running video BIOS test',13,10,0 msg_vbios_init db 'attempting to initialize video BIOS',13,10,0 err_vbios_notfound db 'failed to initialize video BIOS, sig not found at c0000h: ',0 print_ivt: xor bx, bx .loop: mov ax, bx shr ax, 2 call printhex8 mov al, ' ' call ser_putchar mov ax, [bx + 2] ; segment call printhex16 mov al, ':' call ser_putchar mov ax, [bx] ; offset call printhex16 mov al, 13 call ser_putchar mov al, 10 call ser_putchar add bx, 4 cmp bx, 33 << 2 jnz .loop ret hexdig db '0123456789abcdef' printhex8: push bx rol al, 4 mov bx, ax and bx, 0fh mov cx, ax mov al, [bx + hexdig] call ser_putchar mov ax, cx rol al, 4 mov bx, ax and bx, 0fh mov al, [bx + hexdig] call ser_putchar pop bx ret printhex16: push ax mov al, ah call printhex8 pop ax call printhex8 ret 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, [si] test al, al jz .done call ser_putchar inc si jmp ser_putstr .done: ret ; ---------- data ------------ 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 align 4 ; real mode IDTR pseudo-descriptor pointing to the IVT at addr 0 dw 0 rmidt: dw 3ffh dd 0 align 4 end: ; vi:ft=nasm: