;start.s ;Entrypoint for kernel, hello world! bits 32 ;Some symbols we'll need from other files... extern kern_main extern gdtr extern _kernel_start extern _kernel_end ;This will be our entrypoint function name - gotta initialize it now as global so the linker knows later. global start ; Set up for C code. Practically the only requirement for C-generated assembly to work properly is alignment and the presence of a stack. section .bss align=16 stack_bottom: resb 4096 ; 4096 bytes (4kb) large stack. by skipping some amount of data (and eventually filling it with zeroes?), we've essentially just reserved space for our stack. ;Remember, stack grows DOWNWARD! So the last thing in the section -> the highest memory address -> the very first thing on the stack! ;Therefore, we put a label here to represent the top of our stack for later. stack_top: ;We're gonna throw the TSS here too. section .tss align=16 nobits tss: resb 104 section .bootstrap_tables nobits align=4096 bootstrap_PD: resb 4096 bootstrap_PT: resb 4096 ;Actual code. Entry point goes here! section .lower_text exec ;Here it is! start: ;We made a GDT! Let's use it! lgdt [gdtr - 0xC0000000] ;Now we start by setting the code segment (CS) with a far jump... jmp 0x08:gdt_load gdt_load: ;Now we go ahead and dump our kernel mode data segment (0x10) into the rest of our segments. ;Also preserving eax because it has the bootloader's multiboot magic in it and I want to check it in main. mov cx, 0x10 mov ds, cx mov es, cx mov ss, cx mov fs, cx mov gs, cx ;So, we want to set up a page table and page directory. Let's take inventory. ;eax - preserve, mb_magic ;ebx - preserve, mb_info ;ecx,edx,esi - can use ;Can technically use esp as the stack is set to stack_top later. ;ecx = our current indexing for pages, this is a 4K-aligned address mov ecx, 0x0 ;edi = our pointer to the current page table entry. Array of 4byte entries. It's at, of course, where we put it earlier. ;Worth noting that it's actually physically at ~1M with the kernel. The pointer, however, is virtually inclined. So we need to adjust. mov edi, bootstrap_PT - 0xC0000000 page_alloc_loop: ;If we're lower than our kernel, skip this page. If we've passed our kernel, we're done mapping. cmp ecx, _kernel_start jl next_page cmp ecx, _kernel_end - 0xC0000000 jge page_alloc_done ;Okay, we're in kernel zone. Let's map this one. ;Let's make a page table entry in edx. ;The top bits for the page table entry are just the upper bits of the address, so we dont need to touch it. mov edx, ecx ;Set last two bits, "present" and "writeable". Later on we'll make a proper page table with proper permissions, but we're bootstrapping here. or edx, 0x003 ;edi holds a pointer to the page table entry, so dereference it and throw our new entry (in edx) to it. Done! mov [edi], edx next_page: ;address page + 1 add ecx, 0x1000 ;page table entry + 1 add edi, 4 jmp page_alloc_loop page_alloc_done: ;By now we've mapped the kernel in a page table! Let's map VGA too, we'll need that address later. mov dword [bootstrap_PT - 0xC0000000 + 1023 * 4], 0x000B8000 | 0x27 ;We have our page tables mapped to the phys addr of kernel + VGA. Time to add them to our page directory! ;First identity maps, second maps to higher half. mov dword [bootstrap_PD - 0xC0000000], bootstrap_PT - 0xC0000000 + 0x003 mov dword [bootstrap_PD - 0xC0000000 + 4 * (0xC0000000 >> 22)], bootstrap_PT - 0xC0000000 + 0x003 ;Let's set it! You do this with cr3. mov ecx, bootstrap_PD - 0xC0000000 mov cr3, ecx ;Enable paging in cr0 mov ecx, cr0 or ecx, 0x80010000 mov cr0, ecx ;Done! Leave to the higher half! lea ecx, setup_c_env jmp ecx section .text setup_c_env: ;set up the stack. Stack grows downward on x86. We did the work earlier of defining where the top of the stack is, so just tell esp. mov esp, stack_top ;set the stack pointer push ebx push eax ;To C-land! call kern_main ;You should never get here, but in case you do, we will just hang. hang: cli ;Interrupts: off hlt ;Halt! jmp hang ;just in case...