-; vi:ft=nasm:
bits 64
- org 100000h
- default rel
-
- ; EFI_SYSTEM_TABLE offsets
-SIMPLE_TEXT_OUTPUT equ 64
-BOOT_SERVICES equ 80
+ org 1000h
- ; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL function offsets
-TEXT_OUT_STRING equ 8
-TEXT_SET_ATTR equ 40
-TEXT_CLEAR_SCREEN equ 48
+%include "serial.inc"
- ; EFI_BOOT_SERVICES function offsets
+BOOT_SERVICES equ 96
BOOT_GET_MEMORY_MAP equ 56
BOOT_EXIT_BOOT_SERVICES equ 232
-
start:
mov [efihandle], rcx
mov [systab], rdx
- mov rax, [rdx + BOOT_SERVICES]
- mov [bootsrv], rax
- sub rsp, 32 ; leave space for shadow area
- and rsp, 0xfffffffffffffff0 ; make sure sp is 16-byte aligned
-
- mov rcx, [rdx + SIMPLE_TEXT_OUTPUT]
- mov rdx, 0x0c
- call [rcx + TEXT_SET_ATTR]
-
- mov rax, [systab]
- mov rcx, [rax + SIMPLE_TEXT_OUTPUT]
- call [rcx + TEXT_CLEAR_SCREEN]
-
- mov rax, [systab]
- mov rcx, [rax + SIMPLE_TEXT_OUTPUT]
- lea rdx, [str_hello]
- call [rcx + TEXT_OUT_STRING]
-
-
- mov rax, [systab]
- mov rcx, [rax + SIMPLE_TEXT_OUTPUT]
- mov rdx, 0x07
- call [rcx + TEXT_SET_ATTR]
-
- call get_rip
- mov rdi, rax
- call printhex64
- call newline
-
; retrieve memory map
; args: RCX, RDX, R8, and R9.
lea rcx, [mmap_size]
lea r9, [mmap_descsz]
lea rax, [mmap_descver]
push rax
- push rax
+ sub rsp, 32
mov rax, [systab]
mov rbx, [rax + BOOT_SERVICES]
call [rbx + BOOT_GET_MEMORY_MAP]
- add rsp, 16
-
+ 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]
-.hang: jmp .hang
+ ; 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
- ; expects number in rdi
-printhex64:
- mov rcx, 16
- lea rbx, [hexbuf]
-.loop: rol rdi, 4
- mov rax, rdi
- and rax, 0xf
- lea rdx, [hexdig]
- shl rax, 1
- add rdx, rax
- mov ax, [rdx]
- mov [rbx], ax
- add rbx, 2
- dec rcx
+ ; 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
- mov word [rbx], 0
- lea rdi, [hexbuf]
-
- ; expects string in rdi
-printstr:
- mov rcx, [systab]
- mov rcx, [rcx + SIMPLE_TEXT_OUTPUT]
- mov rdx, rdi
- sub rsp, 32
- call [rcx + TEXT_OUT_STRING]
- add rsp, 32
ret
-newline:
- lea rdi, [str_hello + 18]
- jmp printstr
+hexdig db '0123456789abcdef'
-hexdig dw __utf16__ "0123456789abcdef"
-hexbuf: times 40 db 0
+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
-bootsrv dq 0
-str_hello dw __utf16__(`qurashee!\r\n`),0
-
- align 8
; memory map data
-mmap_size dq 0
+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: