.set ALIGN, 1 << 0 .set MEMINFO, 1 << 1 .set VIDMODE, 1 << 2 .set FLAGS, ALIGN | MEMINFO .set MAGIC, 0x1BADB002 .set CHECKSUM, -(MAGIC + FLAGS) /* the amount of memory to allocate for bootstrap page directories. */ #define BOOTSTRAP_PDIR_SIZE 0x1000 .extern start_64 # defined in start_64.S .code32 .section .boot.rodata, "a", @progbits no_long_mode_err: .asciz "This system is not 64-bit. Press any key to reboot." unsupported_cpu_err: .asciz "Your CPU is not supported by Magenta. Press any key to reboot." .section .boot.bss, "aw", @nobits bootstrap_pml4t: # a single PML4T .skip 0x1000 bootstrap_pdpt: # a single PDPT .skip 0x1000 bootstrap_pdir_buf: # a buffer of page directories. each pdir entry can be used to map 2MB of virtual memory. .skip BOOTSTRAP_PDIR_SIZE bootstrap_stack_bottom: .skip 0x1000 bootstrap_stack_top: .section .boot.text, "ax", @progbits .align 4 .long MAGIC .long FLAGS .long CHECKSUM # Print an error message and wait for RETURN if the system's CPU doesn't support long mode. no_long_mode: # print error message mov $no_long_mode_err, %esi mov $0xb8000, %edi 1: movsb movb $12, (%edi) incl %edi cmpb $0, (%esi) jne 1b 2: inb $0x64, %al test $0x01, %al jz 3f inb $0x60, %al jmp 2b 3: inb $0x64, %al test $0x01, %al jz 3b int $0xff cli hlt # Print an error message and wait for RETURN if the system's CPU isn't supported. unsupported_cpu: # print error message mov $unsupported_cpu_err, %esi mov $0xb8000, %edi 1: movsb movb $12, (%edi) incl %edi cmpb $0, (%esi) jne 1b 2: inb $0x64, %al test $0x01, %al jz 3f inb $0x60, %al jmp 2b 3: inb $0x64, %al test $0x01, %al jz 3b int $0xff cli hlt init_page_tables: /* STEP 1: add an entry in the PML4T pointing to the PDPT. NOTE that we actually add two entries in the PML4T that both point to the same PDPT. This allows us to map the kernel at both vaddr 0x0 AND vaddr 0xffffffff80000000 using the same PDPT */ mov $bootstrap_pml4t, %edi mov $bootstrap_pdpt, %esi # in %ESI, build a PML4T entry containing the base address of the PDPT xor $0xFFF, %esi or $0x3, %esi # add the PML4T entries at PML4T[0] and PML4T[511] mov %esi, (%edi) add $0xFF8, %edi mov %esi, (%edi) /* STEP 2: initialise the PDPT. We need to create an entry in the PDPT for every page directory we intend to use. */ # BOOTSTRAP_PDIR_SIZE contains the total size of the page directory buffer. # use it to calculate the number of page directories we're using. mov $0, %edx mov $BOOTSTRAP_PDIR_SIZE, %eax mov $0x1000, %ebx idiv %ebx mov $bootstrap_pdpt, %edi mov $bootstrap_pdir_buf, %esi /* %ESI is a pointer into the pagedir buffer. %EDI is a pointer into the PDPT. %EAX is the total number of page directories. %EBX is where we build the PDPT entry. */ 1: mov %esi, %ebx xor $0xFFF, %ebx or $0x3, %ebx # skip the first 4 bytes of the 8-byte entry, leaving them zero. add $0x4, %edi mov %ebx, (%edi) add $0x1000, %esi add $0x4, %edi sub $0x1, %eax cmp $0x0, %eax jne 1b /* STEP 3: initialise the page directories. Each page directory can be used to map 2MiB of virtual memory. The exact number of page directories in use is determined by BOOTSTRAP_PDIR_SIZE */ /* the total number of page directory entries is stored in %EAX */ mov $0, %edx mov $BOOTSTRAP_PDIR_SIZE, %eax mov $0x8, %ebx idiv %ebx mov $0x0, %esi mov $bootstrap_pdir_buf, %edi /* %EDI is a pointer into the pagedir buffer. %ESI is the physical address being mapped. must be a multiple of 2MiB. %EAX is the total number of page directories. %EBX is where we build the pagedir entry. */ 2: mov %esi, %ebx xor $0xFFF, %ebx or $0x3, %ebx # skip the first 4 bytes of the 8-byte entry, leaving them zero. add $0x4, %edi mov %ebx, (%edi) add $0x200000, %esi add $0x4, %edi dec %eax jnz 2b ret long_mode_switch: ret .global _start .type _start, @function _start: mov $bootstrap_stack_top, %esp push %ebx # store the pointer to the multiboot info block. # check if long mode is supported movl $0x80000000, %eax # Extended-function 80000000h. cpuid # Is largest extended function cmpl $0x80000000, %eax # any function > 80000000h? jbe no_long_mode # If not, no long mode. movl $0x80000001, %eax # Extended-function 80000001h. cpuid # Now EDX = extended-features flags. btl $29, %edx # Test if long mode is supported. jnc no_long_mode # Exit if not supported. movl $1, %eax cpuid btl $3, %edx mov $0xb8000, %eax movb $76, (%eax) inc %eax movb $0xF, (%eax) call init_page_tables call long_mode_switch # calls start_64, does not return cli hlt