;boot.asm ;Author: Josh Holtrop ;Date: 07/08/04 ;Modified: 07/10/04 %define MULTIBOOT_MAGIC 0x1BADB002 %define MULTIBOOT_FLAGS 0x00010003 %define VIRT_OFFSET 0xC0000000 ;3gb virtual address %define PHYS_START 0x00100000 ;1mb physical address %define VIRT_STACK_TOP 0xCFFFFFFC ;3gb+256mb virtual address %define GDT_P PHYS_START ;1mb physical - Global Descriptor Table space %define GDT_V GDT_P+VIRT_OFFSET %define IDT_P PHYS_START+0x2000 ;1mb+8kb - Interrupt Descriptor Table space %define IDT_V IDT_P+VIRT_OFFSET %define PDBR_P PHYS_START+0x4000 ;1mb+16kb - Page Directory Base Register (first PD) %define PDBR_V PDBR_P+VIRT_OFFSET %define LOPT_P PHYS_START+0x5000 ;1mb+20kb - LOw Page Table for mapping first 4mb %define LOPT_V LOPT_P+VIRT_OFFSET %define PT_STACK_P PHYS_START+0x6000 ;1mb+24kb - page table for initial kernel stack pages (under 0xD0000000) %define PT_STACK_V PT_STACK_P+VIRT_OFFSET %define STACK_P PHYS_START+0x7000 ;1mb+28kb - initial 4kb kernel stack page %define STACK_V STACK_P+VIRT_OFFSET %define KERNEL_P PHYS_START+0x8000 ;1mb+32kb - the kernel's physical address %define KERNEL_V KERNEL_P+VIRT_OFFSET ;3gb+1mb+32kb, the virtual address of the kernel extern _k_init, _isr, _k_mbsave, _end, _rm_params, _initrd [bits 32] [global _start] _start: multiboot_header: dd MULTIBOOT_MAGIC ;magic dd MULTIBOOT_FLAGS ;flags dd -(MULTIBOOT_MAGIC + MULTIBOOT_FLAGS) ;checksum dd multiboot_header-VIRT_OFFSET ;header_addr dd _start-VIRT_OFFSET ;load_addr dd 0 ;load_end_addr dd _end-VIRT_OFFSET ;bss_end_addr dd multiboot_entry-VIRT_OFFSET ;entry_addr ; dd 1 ;mode_type ; dd 80 ;width ; dd 25 ;height ; dd 0 ;depth [global multiboot_entry] multiboot_entry: ;This is where the kernel begins execution from the bootloader. ;At this point, a temporary gdt is set up to "map" 0xC000_0000 to 0x0. cli ;should already be off... lgdt [gdtrbs32-VIRT_OFFSET] jmp KERNEL_CODE_BS32:segmented_start segmented_start: mov cx, KERNEL_DATA_BS32 mov ss, cx mov ds, cx mov es, cx mov gs, cx mov fs, cx mov esp, STACK_V+0x1000 ;ok, now we can access our data ;Then the multiboot info structures are saved to local data variables. add ebx, VIRT_OFFSET push eax push ebx ;pointer to multiboot info structure call _k_mbsave ;save multiboot info structures add esp, 8 cmp eax, 0 ; eax = pointer to mb_module_t struct for rmmod jz pm_return ;go back to real mode to initialize video mode mov ebx, eax ; pointer to mb_module_t mov ecx, [ebx+4] ; end of module mov eax, [ebx] ; start of module mov esi, eax ; start of module sub ecx, eax ; ecx = length of rmmod shr ecx, 2 ; ecx = length of rmmod in dwords mov edi, 0xC0005000 ; where to copy rmmod to (0x5000 physical) rep movsd ; copy rmmod to first 1mb mov ebx, pm_return ; return address mov ecx, _rm_params ; put real mode params here (video mode etc...) sub ecx, VIRT_OFFSET mov eax, cr0 and eax, 0x7FFFFFFE ; leave PM mov cr0, eax jmp 0x0:0x5010 ; jump to rmmod ;Next we enable paging with the first 4mb mapped 1:1 virtual:physical ; and with the 4mb starting at 0xC000_0000 mapped to the first 4mb physical. pm_return: mov esp, STACK_V+0x1000 ;ok, now we can access our data again xor eax, eax mov edi, PDBR_V mov ecx, 1024 ;clear the PDBR rep stosd mov edi, PT_STACK_V mov ecx, 1024 ;clear the PT_STACK rep stosd mov [PDBR_V], dword LOPT_P|0x03 ;store the physical address of the LOw Page Table | (read/write, present) mov [PDBR_V+0xC00], dword LOPT_P|0x03 ;store the physical address of the LOw Page Table | (read/write, present) mov [PDBR_V+0xCFC], dword PT_STACK_P|0x03 ;store the physical address of the initial stack page page table | (read/write, present) mov [PDBR_V+0xFFC], dword PDBR_P|0x03 ;store the physical address of the page directory (identity map) | (read/write, present) mov [PT_STACK_V+0xFFC], dword STACK_P|0x03 ;store the physical address of the initial stack page | (read/write, present) mov edi, LOPT_V mov ecx, 1024 mov eax, 0x03 ;starting physical address = 0x0 | (read/write, present flags) fill_lopt_loop: ;fill the page table stosd add eax, 4096 ;increment next phsyical address by 4kb loop fill_lopt_loop mov eax, PDBR_P mov cr3, eax ;store the Page Directory Base Address mov eax, cr0 or eax, 0x80000000 ;set Page Enable bit mov cr0, eax ;now paging is active! mov edi, GDT_V mov esi, gdt mov ecx, gdt_end-gdt rep movsb mov edi, IDT_V ;destination mov esi, isr_0 ;address of isr0 mov edx, isr_1-isr_0 ;distance between isr labels mov ecx, 50 ;number of isrlabels fill_idt: mov ebx, esi mov ax, si stosw ;0 offset 15:0 mov ax, KERNEL_CODE stosw ;2 selector 15:0 mov ax, 0x8E00 stosw ;4 [P][DPL][0][TYPE][0][0][0][0][0][0][0][0] shr esi, 16 mov ax, si stosw ;6 offset 31:16 mov esi, ebx add esi, edx loop fill_idt mov word [IDT_V+0x30*8+4], 0xEE00 ;interrupt 0x30 has user priviledges ;Then we can start using our "real" gdt, then unmap the lower 4mb. lgdt [gdtr] ;load gdt jmp KERNEL_CODE:newgdtcontinue newgdtcontinue: mov ax, KERNEL_DATA mov es, ax mov ds, ax mov gs, ax mov fs, ax mov ss, ax mov esp, VIRT_STACK_TOP ;stack just under 3gb+256mb, moves downward lidt [idtr] ;load idt mov [PDBR_V], dword 0 ;unmap 0x0, we are running completely paged at 0xC000_0000 call _k_init ;C kernel initialization idle_loop: ; system idle loop sti hlt jmp idle_loop ;------------------------------------------------------- gdtrbs32: dw gdt_endbs32-gdtbs32-1 dd gdtbs32-VIRT_OFFSET gdtbs32: ;null descriptor dd 0 dd 0 ;a base of 0x4000_0000, when added to 0xC000_0000 will produce 0x0000_0000 physical before paging in effect KERNEL_CODE_BS32 equ $-gdtbs32 db 0xff ;limit 7:0 db 0xff ;limit 15:8 db 0x00 ;base 7:0 db 0x00 ;base 15:8 db 0x00 ;base 23:16 db 0x9a ;access db 0xcf ;flags / limit 19:16 db 0x40 ;base 31:24 KERNEL_DATA_BS32 equ $-gdtbs32 db 0xff ;limit 7:0 db 0xff ;limit 15:8 db 0x00 ;base 7:0 db 0x00 ;base 15:8 db 0x00 ;base 23:16 db 0x92 ;access db 0xcf ;flags / limit 19:16 db 0x40 ;base 31:24 gdt_endbs32: %include "gdt.inc" %include "idt.inc"