mirror of https://gitlab.com/nakst/essence
1095 lines
20 KiB
ArmAsm
1095 lines
20 KiB
ArmAsm
; This file is part of the Essence operating system.
|
|
; It is released under the terms of the MIT license -- see LICENSE.md.
|
|
; Written by: nakst.
|
|
|
|
[bits 16]
|
|
[org 0x1000]
|
|
|
|
; This is missing any file system specific macros.
|
|
%define vesa_info 0x7000
|
|
%define os_installation_identifier 0x7FF0
|
|
%define temporary_load_buffer 0x9000
|
|
%define page_directory 0x40000
|
|
%define page_directory_length 0x20000
|
|
%define memory_map 0x60000
|
|
%define indirect_block_buffer 0x64000
|
|
; %define magic_breakpoint xchg bx,bx
|
|
%define magic_breakpoint
|
|
|
|
start:
|
|
mov esp,0x7C00
|
|
|
|
; Save information passed from stage 1
|
|
mov [drive_number],dl
|
|
mov [partition_entry],si
|
|
mov [use_emu_read],dh
|
|
mov [max_sectors],bx
|
|
mov [max_heads],cx
|
|
|
|
; @FilesystemSpecific
|
|
FilesystemInitialise
|
|
|
|
check_pci:
|
|
; Check the computer has PCI
|
|
mov ax,0xB101
|
|
xor edi,edi
|
|
int 0x1A
|
|
mov si,error_no_pci
|
|
jc error
|
|
or ah,ah
|
|
jnz error
|
|
|
|
check_cpuid:
|
|
; Check the CPU has CPUID
|
|
mov dword [24],.no_cpuid
|
|
mov eax,0
|
|
cpuid
|
|
jmp .has_cpuid
|
|
.no_cpuid:
|
|
mov si,error_no_cpuid
|
|
jmp error
|
|
.has_cpuid:
|
|
|
|
check_msr:
|
|
; Check the CPU has MSRs
|
|
mov dword [24],.no_msr
|
|
mov ecx,0xC0000080
|
|
rdmsr
|
|
jmp .has_msr
|
|
.no_msr:
|
|
mov si,error_no_msr
|
|
jmp error
|
|
.has_msr:
|
|
|
|
enable_a20:
|
|
; Enable the A20 line, if necessary
|
|
cli
|
|
call check_a20
|
|
jc .a20_enabled
|
|
mov ax,0x2401
|
|
int 0x15
|
|
call check_a20
|
|
jc .a20_enabled
|
|
mov si,error_cannot_enable_a20_line
|
|
jmp error
|
|
.a20_enabled:
|
|
sti
|
|
|
|
identity_paging:
|
|
; Map the first 4MB to itself for the bootloader to do work in protected mode
|
|
mov eax,page_directory / 16
|
|
mov es,ax
|
|
|
|
; Clear the page directory
|
|
xor eax,eax
|
|
mov ecx,0x400
|
|
xor di,di
|
|
rep stosd
|
|
|
|
; Recursive map the directory
|
|
mov dword [es:0x3FF * 4],page_directory | 3
|
|
|
|
; Put the first table in the directory
|
|
mov dword [es:0],(page_directory + 0x1000) | 3
|
|
|
|
; Fill the table
|
|
mov edi,0x1000
|
|
mov cx,0x400
|
|
mov eax,3
|
|
.loop:
|
|
mov [es:edi],eax
|
|
add edi,4
|
|
add eax,0x1000
|
|
loop .loop
|
|
|
|
; Set the pointer to the page directory
|
|
mov eax,page_directory
|
|
mov cr3,eax
|
|
|
|
load_gdt:
|
|
; Load the GDT
|
|
lgdt [gdt_data.gdt]
|
|
|
|
inform_bios_mixed_mode:
|
|
mov eax,0xEC00
|
|
mov ebx,3 ; may switch between legacy and long mode
|
|
int 0x15
|
|
|
|
load_memory_map:
|
|
; Load the memory map
|
|
xor ebx,ebx
|
|
|
|
; Set FS to access the memory map
|
|
mov ax,0
|
|
mov es,ax
|
|
mov ax,memory_map / 16
|
|
mov fs,ax
|
|
|
|
; Loop through each memory map entry
|
|
.loop:
|
|
mov di,.entry
|
|
mov edx,0x534D4150
|
|
mov ecx,24
|
|
mov eax,0xE820
|
|
mov byte [.acpi],1
|
|
int 0x15
|
|
jc .finished
|
|
|
|
; Check the BIOS call worked
|
|
cmp eax,0x534D4150
|
|
jne .fail
|
|
|
|
; pusha
|
|
; mov di,.entry
|
|
; call .print_bytes
|
|
; popa
|
|
|
|
; Check if this is usable memory
|
|
cmp dword [.type],1
|
|
jne .try_next
|
|
cmp dword [.size],0
|
|
je .try_next
|
|
cmp dword [.acpi],0
|
|
je .try_next
|
|
|
|
; Check that the region is big enough
|
|
mov eax,[.size]
|
|
and eax,~0x3FFF
|
|
or eax,eax
|
|
jz .try_next
|
|
|
|
; Check that the base is above 1MB
|
|
cmp dword [.base + 4],0
|
|
jne .base_good
|
|
cmp dword [.base],0x100000
|
|
jl .try_next
|
|
.base_good:
|
|
|
|
; Align the base to the nearest page
|
|
mov eax,[.base]
|
|
and eax,0xFFF
|
|
or eax,eax
|
|
jz .base_aligned
|
|
mov eax,[.base]
|
|
and eax,~0xFFF
|
|
add eax,0x1000
|
|
mov [.base],eax
|
|
sub dword [.size],0x1000
|
|
sbb dword [.size + 4],0
|
|
.base_aligned:
|
|
|
|
; Align the size to the nearest page
|
|
mov eax,[.size]
|
|
and eax,~0xFFF
|
|
mov [.size],eax
|
|
|
|
; Convert the size from bytes to 4KB pages
|
|
mov eax,[.size]
|
|
shr eax,12
|
|
push ebx
|
|
mov ebx,[.size + 4]
|
|
shl ebx,20
|
|
add eax,ebx
|
|
pop ebx
|
|
mov [.size],eax
|
|
mov dword [.size + 4],0
|
|
|
|
; Store the entry
|
|
push ebx
|
|
mov ebx,[.pointer]
|
|
mov eax,[.base]
|
|
mov [fs:bx],eax
|
|
mov eax,[.base + 4]
|
|
mov [fs:bx + 4],eax
|
|
mov eax,[.size]
|
|
mov [fs:bx + 8],eax
|
|
add [.total_memory],eax
|
|
mov eax,[.size + 4]
|
|
adc [.total_memory + 4],eax
|
|
mov [fs:bx + 12],eax
|
|
add dword [.pointer],16
|
|
pop ebx
|
|
|
|
; Continue to the next entry
|
|
.try_next:
|
|
or ebx,ebx
|
|
jnz .loop
|
|
|
|
; Make sure that there were enough entries
|
|
.finished:
|
|
mov eax,[.pointer]
|
|
shr eax,4
|
|
or eax,eax
|
|
jz .fail
|
|
|
|
; Clear the base value for the entry after last
|
|
mov ebx,[.pointer]
|
|
mov dword [fs:bx],0
|
|
mov dword [fs:bx + 4],0
|
|
|
|
; Store the total memory
|
|
mov eax,[.total_memory]
|
|
mov dword [fs:bx + 8],eax
|
|
mov eax,[.total_memory + 4]
|
|
mov dword [fs:bx + 12],eax
|
|
|
|
; Load the kernel!
|
|
jmp load_kernel
|
|
|
|
; Display an error message if we could not load the memory map
|
|
.fail:
|
|
mov si,error_could_not_get_memory_map
|
|
jmp error
|
|
|
|
.pointer: dd 0
|
|
.entry:
|
|
.base: dq 0
|
|
.size: dq 0
|
|
.type: dd 0
|
|
.acpi: dd 0
|
|
.total_memory: dq 0
|
|
|
|
; @FilesystemSpecific
|
|
FilesystemGetKernelSize
|
|
|
|
allocate_kernel_buffer:
|
|
; Switch to protected mode
|
|
push ds
|
|
push es
|
|
push ss
|
|
cli
|
|
mov eax,cr0
|
|
or eax,0x80000001
|
|
mov cr0,eax
|
|
jmp 0x8:.pmode
|
|
|
|
; Set the data segment registers
|
|
[bits 32]
|
|
.pmode:
|
|
mov ax,0x10
|
|
mov ds,ax
|
|
mov es,ax
|
|
mov ss,ax
|
|
|
|
; Work out the size of memory we'll allocate
|
|
mov ecx,[kernel_size]
|
|
shr ecx,12
|
|
inc ecx
|
|
mov edx,ecx
|
|
shl edx,12
|
|
|
|
; For every memory region
|
|
xor ebx,ebx
|
|
.memory_region_loop:
|
|
|
|
; Is this the region starting at 1MB?
|
|
mov eax,[ebx + memory_map + 4]
|
|
or eax,eax
|
|
jnz .try_next_memory_region
|
|
mov eax,[ebx + memory_map]
|
|
cmp eax,0x100000
|
|
jne .try_next_memory_region
|
|
|
|
; Check the region has enough pages remaining
|
|
mov eax,[ebx + memory_map + 8]
|
|
cmp eax,ecx
|
|
jl .try_next_memory_region
|
|
|
|
; Remove ECX pages from the region
|
|
mov eax,[ebx + memory_map + 0]
|
|
mov [kernel_buffer],eax
|
|
add eax,edx
|
|
mov [ebx + memory_map + 0],eax
|
|
sub dword [ebx + memory_map + 8],ecx
|
|
sbb dword [ebx + memory_map + 12],0
|
|
|
|
jmp .found_buffer
|
|
|
|
; Go to the next memory region
|
|
.try_next_memory_region:
|
|
add ebx,16
|
|
mov eax,[load_memory_map.pointer]
|
|
cmp ebx,eax
|
|
jne .memory_region_loop
|
|
mov si,error_no_memory
|
|
jmp error_32
|
|
|
|
.found_buffer:
|
|
; Switch to 16-bit mode
|
|
mov eax,cr0
|
|
and eax,0x7FFFFFFF
|
|
mov cr0,eax
|
|
jmp 0x18:.rmode
|
|
|
|
; Switch to real mode
|
|
[bits 16]
|
|
.rmode:
|
|
mov eax,cr0
|
|
and eax,0x7FFFFFFE
|
|
mov cr0,eax
|
|
jmp 0x0:.finish
|
|
|
|
; Go to error
|
|
.finish:
|
|
pop ss
|
|
pop es
|
|
pop ds
|
|
|
|
; @FilesystemSpecific
|
|
FilesystemLoadKernelIntoBuffer
|
|
|
|
enable_video_mode:
|
|
call vbe_init
|
|
jmp enable_video_mode_done
|
|
%include "boot/x86/vbe.s"
|
|
enable_video_mode_done:
|
|
|
|
setup_elf:
|
|
; Switch to protected mode
|
|
cli
|
|
mov eax,cr0
|
|
or eax,0x80000001
|
|
mov cr0,eax
|
|
jmp 0x8:.pmode
|
|
|
|
; Set the data segment registers
|
|
[bits 32]
|
|
.pmode:
|
|
mov ax,0x10
|
|
mov ds,ax
|
|
mov es,ax
|
|
mov ss,ax
|
|
|
|
; Check the ELF data is correct
|
|
mov ebx,[kernel_buffer]
|
|
mov esi,error_bad_kernel
|
|
cmp dword [ebx + 0],0x464C457F
|
|
jne error_32
|
|
cmp byte [ebx + 4],2
|
|
je setup_elf_64
|
|
cmp byte [ebx + 4],1
|
|
jne error_32
|
|
jmp setup_elf_32
|
|
|
|
setup_elf_32:
|
|
mov dword [page_table_allocation_location],0x2000
|
|
|
|
; Check the ELF data is correct
|
|
mov ebx,[kernel_buffer]
|
|
mov esi,error_bad_kernel
|
|
cmp byte [ebx + 5],1
|
|
jne error_32
|
|
cmp byte [ebx + 7],0
|
|
jne error_32
|
|
cmp byte [ebx + 16],2
|
|
jne error_32
|
|
cmp byte [ebx + 18],0x03
|
|
jne error_32
|
|
|
|
; Find the program headers
|
|
; EAX = ELF header, EBX = program headers
|
|
mov eax,ebx
|
|
mov ebx,[eax + 28]
|
|
add ebx,eax
|
|
|
|
; ECX = entries, EDX = size of entry
|
|
movzx ecx,word [eax + 44]
|
|
movzx edx,word [eax + 42]
|
|
|
|
; Loop through each program header
|
|
.loop_program_headers:
|
|
push eax
|
|
push ecx
|
|
push edx
|
|
push ebx
|
|
|
|
; Only deal with load segments
|
|
mov eax,[ebx]
|
|
cmp eax,1
|
|
jne .next_entry
|
|
|
|
; Work out how many physical pages we need to allocate
|
|
mov ecx,[ebx + 20]
|
|
shr ecx,12
|
|
inc ecx
|
|
|
|
; Get the starting virtual address
|
|
mov eax,[ebx + 8]
|
|
mov [.target_page],eax
|
|
|
|
; For every frame in the segment
|
|
.frame_loop:
|
|
xor ebx,ebx
|
|
|
|
; For every memory region
|
|
.memory_region_loop:
|
|
|
|
; Check the region has enough pages remaining
|
|
mov eax,[ebx + memory_map + 8]
|
|
or eax,eax
|
|
jz .try_next_memory_region
|
|
|
|
; Remove one page from the region
|
|
mov eax,[ebx + memory_map + 4]
|
|
or eax,eax
|
|
jnz .try_next_memory_region ; Reject 64-bit pages.
|
|
mov eax,[ebx + memory_map + 0]
|
|
mov [.physical_page],eax
|
|
add eax,0x1000
|
|
mov [ebx + memory_map + 0],eax
|
|
sub dword [ebx + memory_map + 8],1
|
|
|
|
jmp .found_physical_page
|
|
|
|
; Go to the next memory region
|
|
.try_next_memory_region:
|
|
add ebx,16
|
|
mov eax,[load_memory_map.pointer]
|
|
cmp ebx,eax
|
|
jne .memory_region_loop
|
|
mov esi,error_no_memory
|
|
jmp error_32
|
|
|
|
; Map the page into virtual memory
|
|
.found_physical_page:
|
|
|
|
; Make sure we have a page table
|
|
mov eax,[.target_page]
|
|
shr eax,22
|
|
shl eax,2
|
|
add eax,0xFFFFF000
|
|
mov ebx,[eax]
|
|
cmp ebx,0
|
|
jne .has_pt
|
|
mov ebx,[page_table_allocation_location]
|
|
add ebx,page_directory
|
|
or ebx,7
|
|
mov [eax],ebx
|
|
add dword [page_table_allocation_location],0x1000
|
|
mov eax,cr3
|
|
mov cr3,eax
|
|
.has_pt:
|
|
|
|
; Map the page!
|
|
mov eax,[.target_page]
|
|
shr eax,12
|
|
mov ebx,[.physical_page]
|
|
or ebx,0x103
|
|
shl eax,2
|
|
add eax,0xFFC00000
|
|
mov [eax],ebx
|
|
mov ebx,[.target_page]
|
|
invlpg [ebx]
|
|
|
|
; Go to the next frame
|
|
add dword [.target_page],0x1000
|
|
dec ecx
|
|
or ecx,ecx
|
|
jnz .frame_loop
|
|
|
|
; Restore the pointer to the segment
|
|
pop ebx
|
|
push ebx
|
|
|
|
; Clear the memory
|
|
mov ecx,[ebx + 20]
|
|
xor eax,eax
|
|
mov edi,[ebx + 8]
|
|
rep stosb
|
|
|
|
; Copy the memory
|
|
mov ecx,[ebx + 16]
|
|
mov esi,[ebx + 4]
|
|
add esi,[kernel_buffer]
|
|
mov edi,[ebx + 8]
|
|
rep movsb
|
|
|
|
; Go to the next entry
|
|
.next_entry:
|
|
pop ebx
|
|
pop edx
|
|
pop ecx
|
|
pop eax
|
|
|
|
add ebx,edx
|
|
dec ecx
|
|
or ecx,ecx
|
|
jnz .loop_program_headers
|
|
|
|
jmp run_kernel32
|
|
|
|
.target_page: dd 0
|
|
.physical_page: dd 0
|
|
|
|
run_kernel32:
|
|
; Let the kernel use the memory that was used to store the executable
|
|
xor eax,eax
|
|
mov ebx,[load_memory_map.pointer]
|
|
mov [memory_map + ebx + 4],eax
|
|
mov [memory_map + ebx + 12],eax
|
|
mov [memory_map + ebx + 16],eax
|
|
mov [memory_map + ebx + 20],eax
|
|
mov eax,[memory_map + ebx + 8]
|
|
mov [memory_map + ebx + 24],eax
|
|
mov eax,[memory_map + ebx + 12]
|
|
mov [memory_map + ebx + 28],eax
|
|
mov eax,[kernel_buffer]
|
|
mov [memory_map + ebx],eax
|
|
mov eax,[kernel_size]
|
|
shr eax,12
|
|
mov [memory_map + ebx + 8],eax
|
|
|
|
; Execute the kernel's _start function
|
|
mov ebx,[kernel_buffer]
|
|
mov ecx,[ebx + 24]
|
|
xor edi,edi
|
|
mov esi,1
|
|
jmp ecx
|
|
|
|
setup_elf_64:
|
|
; Check that the processor is 64-bit
|
|
mov ecx,0x80000001
|
|
cpuid
|
|
mov esi,error_no_long_mode
|
|
test eax,0x20000000
|
|
jnz error_32
|
|
|
|
; Disable paging
|
|
mov eax,cr0
|
|
and eax,0x7FFFFFFF
|
|
mov cr0,eax
|
|
|
|
; Identity map the first 4MB
|
|
mov dword [page_table_allocation_location],0x5000
|
|
mov ecx,page_directory_length
|
|
xor eax,eax
|
|
mov edi,page_directory
|
|
rep stosb
|
|
mov dword [page_directory + 0x1000 - 16],page_directory | 3
|
|
mov dword [page_directory],(page_directory + 0x1000) | 7
|
|
mov dword [page_directory + 0x1000],(page_directory + 0x2000) | 7
|
|
mov dword [page_directory + 0x2000],(page_directory + 0x3000) | 7
|
|
mov dword [page_directory + 0x2000 + 8],(page_directory + 0x4000) | 7
|
|
mov edi,page_directory + 0x3000
|
|
mov eax,0x000003
|
|
mov ebx,0x200003
|
|
mov ecx,0x400
|
|
.identity_loop:
|
|
mov [edi],eax
|
|
add edi,8
|
|
add eax,0x1000
|
|
loop .identity_loop
|
|
mov eax,page_directory
|
|
mov cr3,eax
|
|
|
|
; Enable long mode
|
|
mov eax,cr4
|
|
or eax,32
|
|
mov cr4,eax
|
|
mov ecx,0xC0000080
|
|
rdmsr
|
|
or eax,256
|
|
wrmsr
|
|
mov eax,cr0
|
|
or eax,0x80000000
|
|
mov cr0,eax
|
|
|
|
; Go to 64-bit mode
|
|
jmp 0x48:.start_64_bit_mode
|
|
[bits 64]
|
|
.start_64_bit_mode:
|
|
|
|
mov rax,0x50
|
|
mov ds,rax
|
|
mov es,rax
|
|
mov ss,rax
|
|
|
|
; Check the ELF data is correct
|
|
mov rbx,[kernel_buffer]
|
|
mov rsi,error_bad_kernel
|
|
cmp byte [rbx + 5],1
|
|
jne error_64
|
|
cmp byte [rbx + 7],0
|
|
jne error_64
|
|
cmp byte [rbx + 16],2
|
|
jne error_64
|
|
cmp byte [rbx + 18],0x3E
|
|
jne error_64
|
|
|
|
; Find the program headers
|
|
; RAX = ELF header, RBX = program headers
|
|
mov rax,rbx
|
|
mov rbx,[rax + 32]
|
|
add rbx,rax
|
|
|
|
; ECX = entries, EDX = size of entry
|
|
movzx rcx,word [rax + 56]
|
|
movzx rdx,word [rax + 54]
|
|
|
|
; Loop through each program header
|
|
.loop_program_headers:
|
|
push rax
|
|
push rcx
|
|
push rdx
|
|
push rbx
|
|
|
|
; Only deal with load segments
|
|
mov eax,[rbx]
|
|
cmp eax,1
|
|
jne .next_entry
|
|
|
|
; Work out how many physical pages we need to allocate
|
|
mov rcx,[rbx + 40]
|
|
shr rcx,12
|
|
inc rcx
|
|
|
|
; Get the starting virtual address
|
|
mov rax,[rbx + 16]
|
|
shl rax,16
|
|
shr rax,16
|
|
mov [.target_page],rax
|
|
|
|
; For every frame in the segment
|
|
.frame_loop:
|
|
xor rbx,rbx
|
|
|
|
; For every memory region
|
|
.memory_region_loop:
|
|
|
|
; Check the region has enough pages remaining
|
|
mov rax,[rbx + memory_map + 8]
|
|
or rax,rax
|
|
jz .try_next_memory_region
|
|
|
|
; Remove one page from the region
|
|
mov rax,[rbx + memory_map + 0]
|
|
mov [.physical_page],rax
|
|
add rax,0x1000
|
|
mov [rbx + memory_map + 0],rax
|
|
sub qword [rbx + memory_map + 8],1
|
|
|
|
jmp .found_physical_page
|
|
|
|
; Go to the next memory region
|
|
.try_next_memory_region:
|
|
add rbx,16
|
|
mov eax,[load_memory_map.pointer]
|
|
cmp ebx,eax
|
|
jne .memory_region_loop
|
|
mov si,error_no_memory
|
|
jmp error_64
|
|
|
|
; Map the page into virtual memory
|
|
.found_physical_page:
|
|
|
|
; Make sure we have a PDP
|
|
mov rax,[.target_page]
|
|
shr rax,39
|
|
mov r8,0xFFFFFF7FBFDFE000
|
|
mov rbx,[r8 + rax * 8]
|
|
cmp rbx,0
|
|
jne .has_pdp
|
|
mov rbx,[page_table_allocation_location]
|
|
add rbx,page_directory
|
|
or rbx,7
|
|
mov [r8 + rax * 8],rbx
|
|
add qword [page_table_allocation_location],0x1000
|
|
mov rax,cr3
|
|
mov cr3,rax
|
|
.has_pdp:
|
|
|
|
; Make sure we have a PD
|
|
mov rax,[.target_page]
|
|
shr rax,30
|
|
mov r8,0xFFFFFF7FBFC00000
|
|
mov rbx,[r8 + rax * 8]
|
|
cmp rbx,0
|
|
jne .has_pd
|
|
mov rbx,[page_table_allocation_location]
|
|
add rbx,page_directory
|
|
or rbx,7
|
|
mov [r8 + rax * 8],rbx
|
|
add qword [page_table_allocation_location],0x1000
|
|
mov rax,cr3
|
|
mov cr3,rax
|
|
.has_pd:
|
|
|
|
; Make sure we have a PT
|
|
mov rax,[.target_page]
|
|
shr rax,21
|
|
mov r8,0xFFFFFF7F80000000
|
|
mov rbx,[r8 + rax * 8]
|
|
cmp rbx,0
|
|
jne .has_pt
|
|
mov rbx,[page_table_allocation_location]
|
|
add rbx,page_directory
|
|
or rbx,7
|
|
mov [r8 + rax * 8],rbx
|
|
add qword [page_table_allocation_location],0x1000
|
|
mov rax,cr3
|
|
mov cr3,rax
|
|
.has_pt:
|
|
|
|
; Map the page!
|
|
mov rax,[.target_page]
|
|
shr rax,12
|
|
mov rbx,[.physical_page]
|
|
or rbx,0x103
|
|
shl rax,3
|
|
xor r8,r8
|
|
mov r8,0xFFFFFF00
|
|
shl r8,32
|
|
add rax,r8
|
|
mov [rax],rbx
|
|
mov rbx,[.target_page]
|
|
invlpg [rbx]
|
|
|
|
; Go to the next frame
|
|
add qword [.target_page],0x1000
|
|
dec rcx
|
|
or rcx,rcx
|
|
jnz .frame_loop
|
|
|
|
; Restore the pointer to the segment
|
|
pop rbx
|
|
push rbx
|
|
|
|
; Clear the memory
|
|
mov rcx,[rbx + 40]
|
|
xor rax,rax
|
|
mov rdi,[rbx + 16]
|
|
rep stosb
|
|
|
|
; Copy the memory
|
|
mov rcx,[rbx + 32]
|
|
mov rsi,[rbx + 8]
|
|
add rsi,[kernel_buffer]
|
|
mov rdi,[ebx + 16]
|
|
rep movsb
|
|
|
|
; Go to the next entry
|
|
.next_entry:
|
|
pop rbx
|
|
pop rdx
|
|
pop rcx
|
|
pop rax
|
|
|
|
add rbx,rdx
|
|
dec rcx
|
|
or rcx,rcx
|
|
jnz .loop_program_headers
|
|
|
|
jmp run_kernel64
|
|
|
|
.target_page: dq 0
|
|
.physical_page: dq 0
|
|
|
|
run_kernel64:
|
|
; Get the start address of the kernel
|
|
mov rbx,[kernel_buffer]
|
|
mov rcx,[rbx + 24]
|
|
|
|
; Let the kernel use the memory that was used to store the executable
|
|
xor eax,eax
|
|
mov ebx,[load_memory_map.pointer]
|
|
mov [memory_map + ebx + 4],eax
|
|
mov [memory_map + ebx + 12],eax
|
|
mov [memory_map + ebx + 16],eax
|
|
mov [memory_map + ebx + 20],eax
|
|
mov eax,[memory_map + ebx + 8]
|
|
mov [memory_map + ebx + 24],eax
|
|
mov eax,[memory_map + ebx + 12]
|
|
mov [memory_map + ebx + 28],eax
|
|
mov eax,[kernel_buffer]
|
|
mov [memory_map + ebx],eax
|
|
mov eax,[kernel_size]
|
|
shr eax,12
|
|
mov [memory_map + ebx + 8],eax
|
|
|
|
; Map the first MB at 0xFFFFFE0000000000 --> 0xFFFFFE0000100000
|
|
mov rdi,0xFFFFFF7FBFDFE000
|
|
mov rax,[rdi]
|
|
mov rdi,0xFFFFFF7FBFDFEFE0
|
|
mov [rdi],rax
|
|
mov rax,cr3
|
|
mov cr3,rax
|
|
|
|
; Use the new linear address of the GDT
|
|
mov rax,0xFFFFFE0000000000
|
|
add qword [gdt_data.gdt2],rax
|
|
lgdt [gdt_data.gdt]
|
|
|
|
; Execute the kernel's _start function
|
|
xor rdi,rdi
|
|
mov rsi,1
|
|
jmp rcx
|
|
|
|
error_64:
|
|
jmp far [.error_32_ind]
|
|
.error_32_ind: dq error_32
|
|
dd 8
|
|
|
|
[bits 32]
|
|
error_32:
|
|
; Switch to 16-bit mode
|
|
mov eax,cr0
|
|
and eax,0x7FFFFFFF
|
|
mov cr0,eax
|
|
jmp 0x18:.rmode
|
|
|
|
; Switch to real mode
|
|
[bits 16]
|
|
.rmode:
|
|
mov eax,cr0
|
|
and eax,0x7FFFFFFE
|
|
mov cr0,eax
|
|
jmp 0x0:.finish
|
|
|
|
; Go to error
|
|
.finish:
|
|
mov ax,0
|
|
mov ds,ax
|
|
mov es,ax
|
|
mov ss,ax
|
|
jmp error
|
|
|
|
check_a20:
|
|
; Set the carry flag if the A20 line is enabled
|
|
mov ax,0
|
|
mov es,ax
|
|
mov ax,0xFFFF
|
|
mov fs,ax
|
|
mov byte [es:0x600],0
|
|
mov byte [fs:0x610],0xFF
|
|
cmp byte [es:0x600],0xFF
|
|
je .enabled
|
|
stc
|
|
ret
|
|
.enabled:
|
|
clc
|
|
ret
|
|
|
|
error:
|
|
; Print an error message
|
|
lodsb
|
|
or al,al
|
|
jz .break
|
|
mov ah,0xE
|
|
int 0x10
|
|
jmp error
|
|
|
|
; Break indefinitely
|
|
.break:
|
|
cli
|
|
hlt
|
|
|
|
gdt_data:
|
|
.null_entry: dq 0
|
|
.code_entry: dd 0xFFFF ; 0x08
|
|
db 0
|
|
dw 0xCF9A
|
|
db 0
|
|
.data_entry: dd 0xFFFF ; 0x10
|
|
db 0
|
|
dw 0xCF92
|
|
db 0
|
|
.code_entry_16: dd 0xFFFF ; 0x18
|
|
db 0
|
|
dw 0x0F9A
|
|
db 0
|
|
.data_entry_16: dd 0xFFFF ; 0x20
|
|
db 0
|
|
dw 0x0F92
|
|
db 0
|
|
.user_code: dd 0xFFFF ; 0x2B
|
|
db 0
|
|
dw 0xCFFA
|
|
db 0
|
|
.user_data: dd 0xFFFF ; 0x33
|
|
db 0
|
|
dw 0xCFF2
|
|
db 0
|
|
.tss: dd 0x68 ; 0x38
|
|
db 0
|
|
dw 0xE9
|
|
db 0
|
|
dq 0
|
|
.code_entry64: dd 0xFFFF ; 0x48
|
|
db 0
|
|
dw 0xAF9A
|
|
db 0
|
|
.data_entry64: dd 0xFFFF ; 0x50
|
|
db 0
|
|
dw 0xAF92
|
|
db 0
|
|
.user_code64: dd 0xFFFF ; 0x5B
|
|
db 0
|
|
dw 0xAFFA
|
|
db 0
|
|
.user_data64: dd 0xFFFF ; 0x63
|
|
db 0
|
|
dw 0xAFF2
|
|
db 0
|
|
.user_code64c: dd 0xFFFF ; 0x6B
|
|
db 0
|
|
dw 0xAFFA
|
|
db 0
|
|
.gdt: dw (gdt_data.gdt - gdt_data - 1)
|
|
.gdt2: dq gdt_data
|
|
|
|
; @FilesystemSpecific
|
|
FilesystemSpecificCode
|
|
|
|
load_sectors:
|
|
; Load CX sectors from sector EAX to EDI
|
|
pushad
|
|
push edi
|
|
|
|
; Add the partition offset to EAX
|
|
mov bx,[partition_entry]
|
|
mov ebx,[bx + 8]
|
|
add eax,ebx
|
|
|
|
; Load 1 sector
|
|
mov [read_structure.lba],eax
|
|
mov ah,0x42
|
|
mov dl,[drive_number]
|
|
mov si,read_structure
|
|
cmp byte [use_emu_read],1
|
|
je .use_emu
|
|
int 0x13
|
|
jmp .done_read
|
|
.use_emu:
|
|
call load_sector_emu
|
|
.done_read:
|
|
|
|
; Check for error
|
|
mov si,error_cannot_read_disk
|
|
jc error
|
|
|
|
; Copy the data to its destination
|
|
pop edi
|
|
call move_sector_to_target
|
|
|
|
; Go to the next sector
|
|
popad
|
|
add edi,0x200
|
|
inc eax
|
|
loop load_sectors
|
|
ret
|
|
|
|
; Move data from the temporary load buffer to EDI
|
|
move_sector_to_target:
|
|
push ss
|
|
push ds
|
|
push es
|
|
|
|
; Switch to protected mode
|
|
cli
|
|
mov eax,cr0
|
|
or eax,0x80000001
|
|
mov cr0,eax
|
|
jmp 0x8:.pmode
|
|
|
|
; Set the data segment registers
|
|
[bits 32]
|
|
.pmode:
|
|
mov ax,0x10
|
|
mov ds,ax
|
|
mov es,ax
|
|
mov ss,ax
|
|
|
|
; Copy the data
|
|
mov ecx,0x200
|
|
mov esi,temporary_load_buffer
|
|
rep movsb
|
|
|
|
; Switch to 16-bit mode
|
|
mov eax,cr0
|
|
and eax,0x7FFFFFFF
|
|
mov cr0,eax
|
|
jmp 0x18:.rmode
|
|
|
|
; Switch to real mode
|
|
[bits 16]
|
|
.rmode:
|
|
mov eax,cr0
|
|
and eax,0x7FFFFFFE
|
|
mov cr0,eax
|
|
jmp 0x0:.finish
|
|
|
|
; Return to load_sectors
|
|
.finish:
|
|
pop es
|
|
pop ds
|
|
pop ss
|
|
sti
|
|
ret
|
|
|
|
load_sector_emu:
|
|
mov di,[read_structure.lba]
|
|
xor ax,ax
|
|
mov es,ax
|
|
mov bx,0x9000
|
|
|
|
; Calculate cylinder and head.
|
|
mov ax,di
|
|
xor dx,dx
|
|
div word [max_sectors]
|
|
xor dx,dx
|
|
div word [max_heads]
|
|
push dx ; remainder - head
|
|
mov ch,al ; quotient - cylinder
|
|
shl ah,6
|
|
mov cl,ah
|
|
|
|
; Calculate sector.
|
|
mov ax,di
|
|
xor dx,dx
|
|
div word [max_sectors]
|
|
inc dx
|
|
or cl,dl
|
|
|
|
; Load the sector.
|
|
pop dx
|
|
mov dh,dl
|
|
mov dl,[drive_number]
|
|
mov ax,0x0201
|
|
int 0x13
|
|
|
|
ret
|
|
|
|
read_structure:
|
|
; Data for the extended read calls
|
|
dw 0x10
|
|
dw 1
|
|
dd temporary_load_buffer
|
|
.lba: dq 0
|
|
|
|
drive_number: db 0
|
|
use_emu_read: db 0
|
|
partition_entry: dw 0
|
|
max_sectors: dw 0
|
|
max_heads: dw 0
|
|
|
|
page_table_allocation_location: dq 0 ; Relative to page_directory.
|
|
|
|
kernel_buffer: dq 0
|
|
kernel_size: dq 0
|
|
|
|
error_cannot_enable_a20_line: db "Error: Cannot enable the A20 line",0
|
|
error_could_not_get_memory_map: db "Error: Could not get the memory map from the BIOS",0
|
|
error_no_pci: db "Error: Could not find the PCI bus",0
|
|
error_no_cpuid: db "Error: CPU does not have the CPUID instruction",0
|
|
error_no_msr: db "Error: CPU does not have the RDMSR instruction",0
|
|
error_cannot_find_file: db "Error: A file necessary for booting could not found",0
|
|
error_cannot_read_disk: db "Error: The disk could not be read.",0
|
|
error_file_too_large: db "Error: The file was too large to be loaded (more than 256KB).",0
|
|
error_kernel_too_large: db "Error: The kernel was too large for the 3MB buffer.",0
|
|
error_bad_kernel: db "Error: Invalid or unsupported kernel ELF format.",0
|
|
error_no_memory: db "Error: Not enough memory to load kernel",0
|
|
error_no_long_mode: db "Error: The kernel is compiled for a 64-bit processor but the current processor is 32-bit only.",0
|
|
error_could_not_set_video_mode: db "Error: Could not set video mode 1024x768x24.",0
|
|
|
|
reached_end: db "Reached end of stage 2 bootloader.",0
|