pub(crate) mod init; use super::mm::{E820_MEM_MAP_DATA, PA_G, PA_NXE, PA_P, PA_PS, PA_RW}; use core::arch::{global_asm, naked_asm}; const KERNEL_IMAGE_PADDR: usize = 0x200000; const KERNEL_PML4: usize = 0x1000; const KERNEL_PDPT_PHYS_MAPPING: usize = 0x2000; const KERNEL_PDPT_KERNEL_SPACE: usize = 0x3000; const KERNEL_PD_KIMAGE: usize = 0x4000; const KERNEL_PT_KIMAGE: usize = 0x5000; #[unsafe(link_section = ".low")] static mut EARLY_GDT: [u64; 7] = [0; 7]; #[unsafe(no_mangle)] #[unsafe(link_section = ".low")] static mut EARLY_GDT_DESCRIPTOR: (u16, u32) = (0, 0); #[unsafe(link_section = ".low")] static mut BIOS_IDT_DESCRIPTOR: (u16, u32) = (0, 0); unsafe extern "C" { fn KIMAGE_32K_COUNT(); fn KIMAGE_PAGES(); fn STAGE1_MAGIC(); fn STAGE1_MAGIC_VALUE(); fn start_32bit() -> !; } global_asm!( r#" .pushsection .mbr, "ax", @progbits .code16 .globl move_mbr move_mbr: xor %ax, %ax mov %ax, %ds mov %ax, %es mov %ax, %ss # move the MBR to 0xe00 mov $128, %cx # 512 bytes mov $0x7c00, %si mov $0x0e00, %di rep movsl ljmp $0x00, $2f 2: # read the kernel stage1 mov $.Lread_data_packet, %si mov $0x42, %ah mov $0x80, %dl int $0x13 jc .Lhalt16 # get memory size info and storage it mov $0xe801, %ax int $0x15 jc .Lhalt16 cmp $0x86, %ah # unsupported function je .Lhalt16 cmp $0x80, %ah # invalid command je .Lhalt16 jcxz 2f mov %cx, %ax mov %dx, %bx 2: mov ${e820_data_addr}, %esp movzw %ax, %eax mov %eax, 8(%esp) # 1k blocks movzw %bx, %ebx mov %ebx, 12(%esp) # 64k blocks # save the destination address to es:di mov %sp, %di add $16, %di # buffer is 1024 - 16 bytes # set default entry size movl $20, 4(%esp) # clear %ebx, len xor %ebx, %ebx mov %ebx, (%esp) 2: # set the magic number to edx mov $0x534D4150, %edx # set function number to eax mov $0xe820, %eax # set default entry size mov $24, %ecx int $0x15 incl (%esp) add $24, %edi jc .Lsave_mem_fin cmp $0, %ebx jz .Lsave_mem_fin cmp $24, %ecx cmovnz 4(%esp), %ecx mov %ecx, 4(%esp) jmp 2b .Lsave_mem_fin: mov $0x3ff, %ax mov ${bios_idt_descriptor}, %di mov %ax, (%di) xor %eax, %eax mov %eax, 2(%di) lgdt .Learly_gdt_descriptor cli # IDT descriptor is 6 0's. borrow the null gdt entry lidt .Learly_gdt # enable protection mode mov %cr0, %eax or $1, %eax mov %eax, %cr0 ljmp $0x08, ${start_32bit} .Lhalt16: hlt jmp . .align 16 .Learly_gdt: .8byte 0x0 # null selector .8byte 0x00cf9a000000ffff # 32bit code selector .8byte 0x00cf92000000ffff # 32bit data selector .align 4 .Learly_gdt_descriptor: .word 0x17 # size .long .Learly_gdt # address .align 16 .Lread_data_packet: .long 0x00070010 # .stage1 takes up 3.5K, or 7 sectors .long 0x00006000 # read to 0000:6000 .8byte 1 # read from LBA 1 .popsection "#, start_32bit = sym start_32bit, bios_idt_descriptor = sym BIOS_IDT_DESCRIPTOR, e820_data_addr = sym E820_MEM_MAP_DATA, options(att_syntax), ); global_asm!( r#" .pushsection .stage1, "ax", @progbits .code16 .Lhalt: hlt jmp . # scratch %eax # return address should be of 2 bytes, and will be zero extended to 4 bytes .Lgo_32bit: cli # borrow the null entry from the early gdt lidt {EARLY_GDT} # set PE bit mov %cr0, %eax or $1, %eax mov %eax, %cr0 ljmp $0x18, $.Lgo_32bit0 .Lgo_16bit0: mov $0x30, %ax mov %ax, %ds mov %ax, %es mov %ax, %ss lidt {BIOS_IDT_DESCRIPTOR} mov %cr0, %eax and $0xfffffffe, %eax mov %eax, %cr0 ljmp $0x00, $2f 2: xor %ax, %ax mov %ax, %ds mov %ax, %ss mov %ax, %es sti pop %eax push %ax ret .code32 # scratch %eax # return address should be of 4 bytes, and extra 2 bytes will be popped from the stack .Lgo_16bit: cli ljmp $0x28, $.Lgo_16bit0 .Lgo_32bit0: mov $0x20, %ax mov %ax, %ds mov %ax, %es mov %ax, %ss pop %ax movzw %ax, %eax push %eax ret # build read disk packet on the stack and perform read operation # # read 16k to 0x8000 and then copy to destination # # %edi: lba start # %esi: destination .code32 read_disk: push %ebp mov %esp, %ebp lea -24(%esp), %esp mov $0x00200010, %eax # packet size 0, sector count 64 mov %eax, (%esp) mov $0x08000000, %eax # destination address 0x0800:0x0000 mov %eax, 4(%esp) mov %edi, 8(%esp) # lba low 4bytes xor %eax, %eax mov %eax, 12(%esp) # lba high 2bytes mov %esi, %edi mov %esp, %esi # packet address call .Lgo_16bit .code16 mov $0x42, %ah mov $0x80, %dl int $0x13 jc .Lhalt call .Lgo_32bit .code32 # move data to destination mov $0x8000, %esi mov $4096, %ecx rep movsl mov %ebp, %esp pop %ebp ret .align 8 .Lgdt_data: .8byte 0x00209a0000000000 # 64bit code selector .8byte 0x0000920000000000 # 64bit data selector .8byte 0x00cf9a000000ffff # 32bit code selector .8byte 0x00cf92000000ffff # 32bit data selector .8byte 0x000f9a000000ffff # 16bit code selector .8byte 0x000f92000000ffff # 16bit data selector {start_32bit}: mov $0x10, %ax mov %ax, %ds mov %ax, %es mov %ax, %ss mov ${STAGE1_MAGIC}, %edi mov (%edi), %edi cmp ${STAGE1_MAGIC_VALUE}, %edi jne .Lhalt mov ${EARLY_GDT_DESCRIPTOR}, %edi mov $0x37, %ax mov %ax, (%edi) mov ${EARLY_GDT}, %eax mov %eax, 2(%edi) # fill in early kernel GDT xchg %eax, %edi xor %eax, %eax mov $2, %ecx # null segment rep stosl # other data mov $.Lgdt_data, %esi mov $12, %ecx rep movsl lgdt {EARLY_GDT_DESCRIPTOR} ljmp $0x18, $2f 2: mov $0x20, %ax mov %ax, %ds mov %ax, %es mov %ax, %ss # temporary kernel stack mov $0x1000, %esp # read kimage into memory lea -16(%esp), %esp mov ${KIMAGE_32K_COUNT}, %ecx shl $1, %ecx movl ${KERNEL_IMAGE_PADDR}, 4(%esp) # destination address movl $8, (%esp) # LBA 2: mov (%esp), %edi mov 4(%esp), %esi mov %ecx, %ebx call read_disk mov %ebx, %ecx addl $0x4000, 4(%esp) addl $32, (%esp) loop 2b lea 16(%esp), %esp cld xor %eax, %eax # clear paging structures mov $0x1000, %edi mov $0x5000, %ecx shr $2, %ecx # %ecx /= 4 rep stosl # set P, RW, G mov $({PA_P} | {PA_RW} | {PA_G}), %ebx xor %edx, %edx mov ${KERNEL_PDPT_PHYS_MAPPING}, %esi # PML4E 0x000 # we need the first 1GB identically mapped # so that we won't trigger a triple fault after # enabling paging mov ${KERNEL_PML4}, %edi call fill_pxe # PML4E 0xff0 mov $({PA_NXE} >> 32), %edx lea 0xff0(%edi), %edi call fill_pxe xor %edx, %edx # setup PDPT for physical memory mapping mov ${KERNEL_PDPT_PHYS_MAPPING}, %edi # set PS or ${PA_PS}, %ebx mov $512, %ecx xor %esi, %esi 2: call fill_pxe lea 8(%edi), %edi add $0x40000000, %esi # 1GB adc $0, %edx loop 2b xor %edx, %edx # PML4E 0xff8 mov ${KERNEL_PDPT_KERNEL_SPACE}, %esi mov ${KERNEL_PML4}, %edi lea 0xff8(%edi), %edi # clear PS and $(~{PA_PS}), %ebx call fill_pxe # PDPTE 0xff8 mov ${KERNEL_PDPT_KERNEL_SPACE}, %edi lea 0xff8(%edi), %edi mov ${KERNEL_PD_KIMAGE}, %esi call fill_pxe # PDE 0xff0 mov ${KERNEL_PD_KIMAGE}, %edi lea 0xff0(%edi), %edi mov ${KERNEL_PT_KIMAGE}, %esi # 0x104000 call fill_pxe # fill PT (kernel image) mov ${KERNEL_PT_KIMAGE}, %edi mov ${KERNEL_IMAGE_PADDR}, %esi mov ${KIMAGE_PAGES}, %ecx 2: call fill_pxe lea 8(%edi), %edi lea 0x1000(%esi), %esi loop 2b # set msr mov $0xc0000080, %ecx rdmsr or $0x901, %eax # set LME, NXE, SCE wrmsr # set cr4 mov %cr4, %eax or $0xa0, %eax # set PAE, PGE mov %eax, %cr4 # load new page table mov ${KERNEL_PML4}, %eax mov %eax, %cr3 mov %cr0, %eax // SET PE, WP, PG or $0x80010001, %eax mov %eax, %cr0 ljmp $0x08, $2f # %ebx: attribute low # %edx: attribute high # %esi: page physical address # %edi: page x entry address fill_pxe: lea (%ebx, %esi, 1), %eax mov %eax, (%edi) mov %edx, 4(%edi) ret .code64 2: jmp {start_64bit} .popsection "#, EARLY_GDT = sym EARLY_GDT, EARLY_GDT_DESCRIPTOR = sym EARLY_GDT_DESCRIPTOR, BIOS_IDT_DESCRIPTOR = sym BIOS_IDT_DESCRIPTOR, KIMAGE_32K_COUNT = sym KIMAGE_32K_COUNT, KIMAGE_PAGES = sym KIMAGE_PAGES, STAGE1_MAGIC = sym STAGE1_MAGIC, STAGE1_MAGIC_VALUE = sym STAGE1_MAGIC_VALUE, KERNEL_IMAGE_PADDR = const KERNEL_IMAGE_PADDR, KERNEL_PML4 = const KERNEL_PML4, PA_P = const PA_P, PA_RW = const PA_RW, PA_G = const PA_G, PA_PS = const PA_PS, PA_NXE = const PA_NXE, KERNEL_PDPT_PHYS_MAPPING = const KERNEL_PDPT_PHYS_MAPPING, KERNEL_PDPT_KERNEL_SPACE = const KERNEL_PDPT_KERNEL_SPACE, KERNEL_PD_KIMAGE = const KERNEL_PD_KIMAGE, KERNEL_PT_KIMAGE = const KERNEL_PT_KIMAGE, start_64bit = sym start_64bit, start_32bit = sym start_32bit, options(att_syntax), ); #[unsafe(naked)] pub unsafe extern "C" fn start_64bit() { naked_asm!( "mov $0x10, %ax", "mov %ax, %ds", "mov %ax, %es", "mov %ax, %ss", "", "mov ${kernel_identical_base}, %rax", "mov ${stack_paddr}, %rsp", "add %rax, %rsp", "", "xor %rbp, %rbp", // Clear previous stack frame "push %rbp", // NULL return address "", "mov ${e820_data_addr}, %rdi", "add %rax, %rdi", "", "jmp {kernel_init}", kernel_identical_base = const 0xffffff0000000000u64, stack_paddr = const 0x80000, e820_data_addr = sym E820_MEM_MAP_DATA, kernel_init = sym init::kernel_init, options(att_syntax) ) }