global gdt global idt global kboot global register_isr global register_trap extern con_init extern fs_init extern kmain extern paging_init extern panic extern printk extern syscall_init extern timer_init extern traps_init extern tty_init %define NRTASKS 64 ; Multiboot header section .mboothdr jmp kboot mboot_hdr: align 4 dd 0x1BADB002 dd 0x00 dd - (0x1BADB002 + 0x00) section .text kboot: cli ; put the stack pointer near the end of conventional memory ; WARNING: don't change this without changing the corresponding ; entries in the TSS constructor in sched.c mov esp, 0x80000 ; setup descriptor tables and enable paging call flush_gdt call enable_a20 call paging_init call flush_idt ; initialize the console call con_init ; add this point, we can display *some* output push .msg call printk add esp, 4 ; initialize secondary subsystems call timer_init call fs_init call tty_init ; last minute setup, then transfer to kmain call kmain ; if kmain decides to return (which it shouldn't), ; disable interrupts and halt the system. cli hlt .msg: db "Kernel booting...", 10, 0 gdt: ; null descriptor dq 0 ; kernel code segment dw 0xFFFF ; limit (bits 0-15) dw 0x0000 ; base (bits 0-15) db 0x00 ; base (bits 16-23) db 0x9A ; access byte db 0xCF ; flags / limit (bits 16-19) db 0x00 ; base (bits 24-31) ; kernel data segment dw 0xFFFF dw 0x0000 db 0x00 db 0x92 db 0xCF db 0x00 ; userspace code segment dw 0xFFFF dw 0x0000 db 0x00 db 0xFA db 0xCF db 0x00 ; userspace data segment dw 0xFFFF dw 0x0000 db 0x00 db 0xF2 db 0xCF db 0x00 ; empty TSS slots for our C scheduler times NRTASKS dq 0 ; garbage TSS descriptor dq 0 .end: gdtp: dw (gdt.end-gdt-1) dd gdt garbage_tss: times 104 db 0 flush_gdt: push ebp mov ebp, esp push ebx ; now, load the GDT lgdt [gdtp] mov ax, 0x10 mov ds, ax mov es, ax mov fs, ax mov gs, ax mov ss, ax jmp 0x08:.end .end: ; finally, we load the task register with ; a pointer to our dummy TSS. this is ; necessary since the CPU *must* save the ; previous task state *somewhere* before ; switching tasks. to avoid overwriting ; any sensitive information, we reserve ; 104 bytes for a *garbage* TSS. mov eax, 5 add eax, NRTASKS shl eax, 3 mov ebx, eax add ebx, gdt mov edx, garbage_tss mov dword [ebx], 103 ; limit - 1 mov [ebx+2], dx shr edx, 16 mov [ebx+4], dl mov [ebx+7], dh mov byte [ebx+5], 0x89 mov byte [ebx+6], 0x00 ltr ax pop ebx pop ebp ret idt: times (256*8) db 0 .end: idtp: dw (idt.end-idt-1) dd idt int_handler: iret flush_idt: push ebp mov ebp, esp ; install the exception handlers (vectors 0-31) call traps_init ; install the default handlers (vectors 32-255) mov ecx, 0 lea edx, [idt+(32*8)] .loop: mov eax, int_handler mov [edx], ax shr eax, 16 mov [edx+6], ax mov word [edx+2], 0x08 mov byte [edx+4], 0x00 mov byte [edx+5], 0x8E inc ecx cmp ecx, (256-32) je .end add edx, 8 jmp .loop .end: ; install the system call handler call syscall_init ; load the IDT, initialize the PIC's, and enable interrupts lidt [idtp] call pic_init sti pop ebp ret ; void register_isr(uint8_t n, uint8_t dpl, void *handler); register_isr: push ebp mov ebp, esp mov edx, [ebp+8] shl edx, 3 add edx, idt mov eax, [ebp+16] mov [edx], ax shr eax, 16 mov [edx+6], ax mov eax, [ebp+12] and al, 3 shl al, 5 mov cl, 0x8E or cl, al mov [edx+5], cl pop ebp ret ; void register_trap(uint8_t n, uint8_t dpl, void *handler); register_trap: push ebp mov ebp, esp mov edx, [ebp+8] shl edx, 3 add edx, idt mov eax, [ebp+16] mov [edx], ax shr eax, 16 mov [edx+6], ax mov eax, [ebp+12] and al, 3 shl al, 5 mov cl, 0x8F or cl, al mov [edx+5], cl pop ebp ret enable_a20: push ebp mov ebp, esp ; test if the A20 gate was already enabled call .testgate jne .end ; send read controller output port command call .waitout mov al, 0xD0 out 0x64, al ; read controller output port call .waitin in al, 0x60 xchg ax, cx ; send write controller output port command call .waitout mov al, 0xD1 out 0x64, al ; write controller output port (enabling gate A20) call .waitout xchg ax, cx or al, 0x02 out 0x60, al ; check if the gate is now enabled call .testgate jne .end ; if the gate is still not working, try the fast-gate method in al, 0x92 and al, 0xFE or al, 0x02 out 0x92, al ; make sure the gate is working (panic if not) call .testgate jne .end push .msg call printk add sp, 4 call panic .waitin: in al, 0x64 test al, 0x01 jz .waitin ret .waitout: in al, 0x64 test al, 0x02 jnz .waitout ret .testgate: ; test if the A20 gate is enabled by depositing ; two words 1MB away from each other in memory. mov word [.tmp+0x100000], 0xBEEF mov cx, [.tmp] mov dx, [.tmp+0x100000] cmp cx, dx ret .end: pop ebp ret .tmp: dw 0xDEAD .msg: db "Failed to enable the A20 gate!", 10, 0 pic_init: push ebp mov ebp, esp mov al, 0x11 out 0x20, al out 0xA0, al mov al, 0x20 out 0x21, al out 0xA1, al mov al, 0x04 out 0x21, al mov al, 0x02 out 0xA1, al mov al, 0x01 out 0x21, al out 0xA1, al mov al, 0xFF out 0x21, al out 0xA1, al pop ebp ret