| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516 |
- 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)
- )
- }
|