Nightloader/i386/legacy-boot/src-asm/memory/gdt.asm

299 lines
6.5 KiB
NASM
Executable File

GDT_MEMORY_AREA equ 0x7e00
GDT_ENTRY_COUNT equ 3
; Arguments (note: all arguments are 4-byte padded):
; [Furthest from EBP]
;
; 4. uint32 privilege_level
; - Only 0 to 3; 0 being kernel and 3 being userland.
; Everything above 3 is an error.
;
; 3 uint32 num_pages
; - Only 20 bits are usable. Everything above will
; be cut off to be written to the segment descriptor.
;
; 2. ptr32 base_address
;
; 1. uint32 segment_index
; Note:
; - Zero is NOT usable and will throw an error.
; the highest theoretical value for this is 8191,
; but generally, one should stick to GDT_ENTRY_COUNT.
;
; [Nearest to EBP]
make_regular_data_segment_in_gdt:
.prolog:
push esi
sub esp, 64
mov esi, esp
mov [esi + (64 - 4)], eax
mov [esi + (64 - 8)], ebx
mov [esi + (64 - 12)], ecx
mov [esi + (64 - 16)], edx
mov [esi + (64 - 20)], edi
.get_arguments:
mov eax, [ebp - 4] ; segment_index
mov ebx, [ebp - 8] ; base_address
mov ecx, [ebp - 12] ; num_pages
mov edx, [ebp - 16] ; privilege_level
mov [esi + 16], eax ; segment_index
mov [esi + 20], ebx ; base_address
mov [esi + 24], ecx ; num_pages
mov [esi + 28], edx ; privilege_level
.validate_arguments:
cmp eax, 8191
ja .segment_index_too_tall
cmp eax, 0
je .segment_index_is_zero
cmp edx, 3
ja .privilege_level_too_large
.calculate_work_address:
shl eax, 3 ; Entry offset from table start
add eax, GDT_MEMORY_AREA ; Entry address
mov [esi + 12], eax
.write_segment_limit:
mov [eax], cx
shr ecx, 16
and ecx, 0x0f
mov [eax + 6], cl
.write_base_address:
mov [eax + 2], bx
shr ebx, 16
mov [eax + 4], bl
mov [eax + 7], bh
.set_default_flags:
or [eax + 6], byte (0b1100 << 4)
.set_data_segment_specific_access_byte:
mov al, 0b1001
; OR the privilege level into the access byte
mov dl, [ebp - 16]
shl dl, 1
and dl, 0b0110
or al, dl
shl al, 4 ; Move the 4 bits of general flags to their place
; Data-segment specific part
or al, 0b0010 ; DATA | HEAP | READ_WRITE | NOT_ACCESSED
mov ebx, [esi + 12]
mov [ebx + 5], al ; Write to the final destination
.epilog:
mov edi, [esi + (64 - 20)]
mov edx, [esi + (64 - 16)]
mov ecx, [esi + (64 - 12)]
mov ebx, [esi + (64 - 8)]
mov eax, [esi + (64 - 4)]
add esp, 64
pop esi
ret
.segment_index_too_tall:
.segment_index_is_zero:
.privilege_level_too_large:
; todo(logs): Write debug message.
cli
hlt
; Arguments (note: all arguments are 4-byte padded):
; [Furthest from EBP]
;
; 4. uint32 privilege_level
; - Only 0 to 3; 0 being kernel and 3 being userland.
; Everything above 3 is an error.
;
; 3 uint32 num_pages
; - Only 20 bits are usable. Everything above will
; be cut off to be written to the segment descriptor.
;
; 2. ptr32 base_address
;
; 1. uint32 segment_index
; Note:
; - Zero is NOT usable and will throw an error.
; the highest theoretical value for this is 8191,
; but generally, one should stick to GDT_ENTRY_COUNT.
;
; [Nearest to EBP]
make_code_segment_in_gdt:
.prolog:
push esi
sub esp, 64
mov esi, esp
mov [esi + (64 - 4)], eax
mov [esi + (64 - 8)], ebx
mov [esi + (64 - 12)], ecx
mov [esi + (64 - 16)], edx
mov [esi + (64 - 20)], edi
.get_arguments:
mov eax, [ebp - 4] ; segment_index
mov ebx, [ebp - 8] ; base_address
mov ecx, [ebp - 12] ; num_pages
mov edx, [ebp - 16] ; privilege_level
mov [esi + 16], eax ; segment_index
mov [esi + 20], ebx ; base_address
mov [esi + 24], ecx ; num_pages
mov [esi + 28], edx ; privilege_level
.validate_arguments:
cmp eax, 8191
ja .segment_index_too_tall
cmp eax, 0
je .segment_index_is_zero
cmp edx, 3
ja .privilege_level_too_large
.calculate_work_address:
shl eax, 3 ; Entry offset from table start
add eax, GDT_MEMORY_AREA ; Entry address
mov [esi + 12], eax
.write_segment_limit:
mov [eax], cx
shr ecx, 16
and ecx, 0x0f
mov [eax + 6], cl
.write_base_address:
mov [eax + 2], bx
shr ebx, 16
mov [eax + 4], bl
mov [eax + 7], bh
.set_default_flags:
or [eax + 6], word (0b1100 << 4)
.set_code_segment_specific_access_byte:
mov al, 0b1001
; OR the privilege level into the access byte
mov dl, [ebp - 16]
shl dl, 1
and dl, 0b0110
or al, dl
shl al, 4 ; Move the 4 bits of general flags to their place
; Code-segment specific part
or al, 0b1010 ; CODE | NON-CONFORMING | READABLE | NOT_ACCESSED
mov ebx, [esi + 12]
mov [ebx + 5], al ; Write to the final destination
.epilog:
mov edi, [esi + (64 - 20)]
mov edx, [esi + (64 - 16)]
mov ecx, [esi + (64 - 12)]
mov ebx, [esi + (64 - 8)]
mov eax, [esi + (64 - 4)]
add esp, 64
pop esi
ret
.segment_index_too_tall:
.segment_index_is_zero:
.privilege_level_too_large:
; todo(logs): Write debug message.
cli
hlt
; [Furthest to EBP]
; 1) Return Address
; [Nearest to EBP]
load_flat_gdt:
.prolog:
push esi
sub esp, 64
mov esi, esp
mov [esi + (64 - 4)], eax
mov [esi + (64 - 8)], ebx
mov [esi + (64 - 12)], ecx
mov [esi + (64 - 16)], edx
mov [esi + (64 - 20)], edi
; mov eax, [ebp - 4]
; mov [esi], eax
.set_segments:
push ebp
mov ebp, esp
push dword 2 ; Segment Index
push dword 0 ; Start Offset
push dword 0x000fffff ; Page Count
push dword 0 ; Required privilege level
call make_regular_data_segment_in_gdt
mov esp, ebp
pop ebp
push ebp
mov ebp, esp
push dword 1 ; Segment Index
push dword 0x0000 ; Start Offset
push dword 0x000fffff ; Page Count
push dword 0 ; Required privilege level
call make_code_segment_in_gdt
mov esp, ebp
pop ebp
mov bx, 0
mov ds, bx
mov es, bx
mov fs, bx
mov gs, bx
mov ss, bx
lgdt [.gdtr]
mov ebx, cr0
or ebx, 1
mov cr0, ebx
jmp dword (1 << 3):.epilog
bits 32
.epilog:
mov bx, (2 << 3)
mov ds, bx
mov es, bx
mov fs, bx
mov gs, bx
mov ss, bx
mov edi, [esi + (64 - 20)]
mov edx, [esi + (64 - 16)]
mov ecx, [esi + (64 - 12)]
mov ebx, [esi + (64 - 8)]
mov eax, [esi + (64 - 4)]
jmp dword stage2_protected_entry
align 16
.gdtr:
.gdtr.length:
dw 23
.gdtr.pointer:
dd GDT_MEMORY_AREA
align 1