Files
mango/arch/x86_64/start_32.S

291 lines
6.4 KiB
ArmAsm

.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.
with buffer size 0x2000 (enough for exactly two page directories)
we can map 2 GiB of virtual memory.
we use this to map the virtual address range 0xffffffff'80000000 -> 0xffffffff'fffff000
to the physical address range 0x00000000 -> 0x80000000,
to map the kernel's code and data, and give the kernel plenty of space to initialise itself.
*/
#define BOOTSTRAP_PDIR_SIZE 0x2000
.extern start_64 # defined in start_64.S
.code32
.section .boot.rodata, "a", @progbits
/*******
ERROR MESSAGE STRINGS
*******/
no_long_mode_err:
.asciz "This system is not 64-bit. Press any key to reboot."
/*******
LONG MDOE BOOSTRAP GDT
*******/
bootstrap_gdt:
/* 0x00: null descriptor */
.long 0x0
.long 0x0
/* 0x08: kernel code segment */
.long 0x0000ffff
.long 0x002f9a00
/* 0x10: kernel data segment */
.long 0x0000ffff
.long 0x002f9200
bootstrap_gdt_ptr:
.word 24 /* 8 bytes for each entry in bootstrap_gdt */
.long bootstrap_gdt
/*******
LONG MODE BOOTSTRAP PAGING STRUCTURES
*******/
.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 0x4000
bootstrap_stack_top:
/*******
MULTIBOOT HEADER
*******/
.section .boot.text, "ax", @progbits
.align 4
.long MAGIC
.long FLAGS
.long CHECKSUM
# Print an error message, wait for RETURN, and
# reboot 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
.global init_page_tables
.type init_page_tables, @function
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
and $0xFFFFF000, %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: write the pgdir entries to the BEGINNING of the PDPT */
# 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
and $0xFFFFF000, %ebx
or $0x3, %ebx
mov %ebx, (%edi)
add $0x1000, %esi
add $0x8, %edi
sub $0x1, %eax
cmp $0x0, %eax
jne 1b
/* STEP 3: write the pgdir entries to the END of the PDPT */
# 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
/* with the number of page directories in %EAX, calculate the starting index into the PDPT.
we need to duplicate the pgdir entries here to map the kernel at vaddr 0xffffffff80000000 and up */
mov %eax, %ecx
mov $0x8, %edx
mul %edx
xchg %eax, %ecx
add $0x1000, %edi
sub %ecx, %edi
#sub $0x08, %edi # subtract from the index here to adjust where the mapping starts
# NOTE that you must subtract in multiples of 0x8
/* %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. */
2:
mov %esi, %ebx
and $0xFFFFF000, %ebx
or $0x3, %ebx
mov %ebx, (%edi)
add $0x1000, %esi
add $0x8, %edi
sub $0x1, %eax
cmp $0x0, %eax
jne 2b
/* STEP 4: initialise the page directories.
Each page directory entry 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. */
3:
mov %esi, %ebx
and $0xFFFFF000, %ebx
or $0x83, %ebx
mov %ebx, (%edi)
add $0x200000, %esi
add $0x8, %edi
sub $0x1, %eax
cmp $0x0, %eax
jne 3b
ret
.global long_mode_switch
.type long_mode_switch, @function
long_mode_switch:
/* load %CR3 with the physical address of the PML4T */
mov $bootstrap_pml4t, %eax
mov %eax, %cr3
/* enable long mode and the syscall/sysret instructions */
movl $0xC0000080, %ecx
rdmsr
orl $0x00000101, %eax
wrmsr
lgdt (bootstrap_gdt_ptr)
mov %cr4, %ecx
orl $0x00000020, %ecx
mov %ecx, %cr4
mov %cr0, %ecx
orl $0x8000002a, %ecx
mov %ecx, %cr0
mov $0x10, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
ljmpl $0x08, $start_64
cli
hlt
.global _start
.type _start, @function
_start:
mov $bootstrap_stack_top, %esp
push %ebx # store the pointer to the multiboot info block as a 64-bit value.
push $0x00 # store the pointer to the multiboot info block as a 64-bit value.
# 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
call init_page_tables
call long_mode_switch # calls start_64, does not return
cli
hlt