;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; FoolOS Boot Loader ; ; Copyright 2014 M.Idziorek ; ; we have just been loaded by the BIOS and are in 16-bits real mode! ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;we want 16-bit instructions, before we switch to 32-bit protected mode. [bits 16] ;define origin of boot record in memory: 0x7c00 ;this is where the BIOS per definition will put the first ;512 bytes of data from the boot device ;The Boot record is identified by the last 2 magic bytes: 0xaa55 (?) [org 0x7c00] ;define where we will load our kernel into memory KERNEL_OFFSET equ 0x1000 jmp boot_16 ;start boot process ;Global Data INT_COUNT: db 0 STR_VERSION: db "_<-Fool-Loader~0.0.8~",0 STR_PROT: db "Entered 32-bit Protected Mode.",0 STR_LOADED: db "FoolOS Kernel Loaded.",0 BOOT_DRIVE: db 0 [bits 32] ; default interrupt handler interrupt: mov al, 0xA0 ; set bit 4 of OCW 2 out 0xA0, al ; write to primary PIC command register mov al, 0x20 ; set bit 4 of OCW 2 out 0x20, al ; write to primary PIC command register iret ;handle keyboard interrupt interrupt2: ; mov eax,[INT_COUNT] ; inc eax ; mov [INT_COUNT],eax in al,0x60 mov [INT_COUNT],al ; call KERNEL_OFFSET+(idt_end-idt_start)+1 ;jump into our Kernel it ; will follow our ; interrupt table ; out 0x60,al mov al, 0x20 ; set bit 4 of OCW 2 out 0x20, al ; write to primary PIC command register iret ;lets put our temporary GDT (Global Descriptor Table) here %include "boot/GDT.asm" ;include 16-bit real mode routines (print_string, print_hex, disk_load) %include "boot/common.asm" ;include 32-bit Protected Mode routines (print_string_pm,print_hex_pm) %include "boot/common_pm.asm" ;include our routines for switching to 32-bit protected mode %include "boot/pm.asm" ;;;;;;;; BOOT 16-bit real ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;lets start [bits 16] boot_16: ;setup the stack mov bp,0x8000 mov sp,bp ;remember BOOT_DRIVE (as was set by BIOS) mov [BOOT_DRIVE],dl ;print FoolOS version info mov bx, STR_VERSION call print_string ;Load the KERNEL mov bx,KERNEL_OFFSET mov dh, 15 mov dl, [BOOT_DRIVE] call disk_load mov bx, STR_LOADED call print_string ;lets enter Protected mode! call switch_to_pm ;;;;;;;; BOOT 32-bit protected mode;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; [bits 32] boot_32_pm: mov ecx,160 mov ebx,STR_PROT call print_string_pm ;enable A20 ;http://www.brokenthorn.com/Resources/OSDev9.html ;Method 3.1: Enables A20 through keyboard controller mov al, 0xdd ; command 0xdd: enable a20 out 0x64, al ; send command to controller lidt [idt_descriptor] ;load descriptor table! ;pollkb example ; pollkb: ; mov cx,320 ; mov ax,0 ; in al,0x60 ; mov dx,ax ; call print_hex_pm ; jmp pollkb ; ;mov al,11111101b ;out 0x61, al sti ;enable interrupts ;************************************************************************ ; Map the 8259A PIC to use interrupts 32-47 within our interrupt table ;************************************************************************ %define ICW_1 0x11 ; 00010001 binary. Enables initialization mode and we are sending ICW 4 %define PIC_1_CTRL 0x20 ; Primary PIC control register %define PIC_2_CTRL 0xA0 ; Secondary PIC control register %define PIC_1_DATA 0x21 ; Primary PIC data register %define PIC_2_DATA 0xA1 ; Secondary PIC data register %define IRQ_0 0x20 ; IRQs 0-7 mapped to use interrupts 0x20-0x27 %define IRQ_8 0x28 ; IRQs 8-15 mapped to use interrupts 0x28-0x36 MapPIC: ; Send ICW 1 - Begin initialization ------------------------- ; Setup to initialize the primary PIC. Send ICW 1 mov al, ICW_1 out PIC_1_CTRL, al ; Send ICW 2 - Map IRQ base interrupt numbers --------------- ; Remember that we have 2 PICs. Because we are cascading with this second PIC, send ICW 1 to second PIC command register out PIC_2_CTRL, al ; send ICW 2 to primary PIC mov al, IRQ_0 out PIC_1_DATA, al ; send ICW 2 to secondary controller mov al, IRQ_8 out PIC_2_DATA, al ; Send ICW 3 - Set the IR line to connect both PICs --------- ; Send ICW 3 to primary PIC mov al, 0x4 ; 0x04 => 0100, second bit (IR line 2) out PIC_1_DATA, al ; write to data register of primary PIC ; Send ICW 3 to secondary PIC mov al, 0x2 ; 010=> IR line 2 out PIC_2_DATA, al ; write to data register of secondary PIC ; Send ICW 4 - Set x86 mode -------------------------------- mov al, 1 ; bit 0 enables 80x86 mode ; send ICW 4 to both primary and secondary PICs out PIC_1_DATA, al out PIC_2_DATA, al ; All done. Null out the data registers mov al, 0 out PIC_1_DATA, al out PIC_2_DATA, al ;mask in al, 0x21 ; read in the primary PIC Interrupt Mask Register (IMR) and al, 0x00 ; 0xEF => 11101111b. This sets the IRQ4 bit (Bit 5) in AL out 0x21, al ; write the value back into IMR in al, 0xA1 ; read in the primary PIC Interrupt Mask Register (IMR) and al, 0x00 ; 0xEF => 11101111b. This sets the IRQ4 bit (Bit 5) in AL out 0xA1, al ; write the value back into IMR call KERNEL_OFFSET+(idt_end-idt_start) ;jump into our Kernel it ; will follow our ; interrupt table idt_descriptor: dw idt_end-idt_start-1 dd KERNEL_OFFSET ;;;; DEBUGGING STUFF times 8 db '@' dw interrupt db '@' dw interrupt2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;so we get identified as MBR times 510-($-$$) db 0 dw 0xaa55 ;interrupt descriptor table (hardcoded address of interrupts:) idt_start: times 33 db 0x50,0x7c,0x08,0x00,0x00,10001110b,0x0,0x0 db 0x59,0x7c,0x08,0x00,0x00,10001110b,0x0,0x0 times 253 db 0x50,0x7c,0x08,0x00,0x00,10001110b,0x0,0x0 idt_end: