291 lines
6.4 KiB
ArmAsm
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
|