Compare commits

...

3 Commits

Author SHA1 Message Date
Eric-Paul Ickhorn 65db388f00
Fix A20 by copying others' implementation
The implementation of the A20 wraparound check was off in some way,
which is why the new implementation was copied from osdev.org, just
using minor modifications to the label naming.
2024-08-06 13:25:42 +02:00
Eric-Paul Ickhorn 5ba30e5f8b
Fix GDT length being insufficient
The value of the GDTR register was set to be 8 bytes too small, with
the effect that an accurate emulation software would error out because
of trying to read over the buffer size, as was noticed when using KVM.

This was due to the perception that the 0th index wouldn't be taken
into account; for that reason, the length of the GDTR was one index
short of what it should've been.
2024-08-06 13:20:40 +02:00
Eric-Paul Ickhorn fd8b5bd57e Fix bug in GDT when using KVM acceleration
The bug was due to the DS segment being set to the 0th segment entry,
causing a general protection fault.
2024-07-30 08:52:24 +02:00
2 changed files with 59 additions and 225 deletions

View File

@ -1,95 +1,48 @@
; A20 Wraparound check as gotten from osdev.org
check_a20_wraparound: check_a20_wraparound:
.prolog: pushf
push dword esi push ds
sub esp, 64 push es
mov esi, esp push di
push si
; EAX is used for the return value, xor ax, ax ; ax = 0
; so it doesn't have to be preserved. mov es, ax
mov [esi + (64 - 8)], ebx
mov [esi + (64 - 12)], edi
.test_a20: not ax ; ax = 0xFFFF
; Save the values which are going to be overwritten mov ds, ax
mov edi, 0x0800
mov eax, [edi]
mov edi, 0x00100800
mov ebx, [edi]
; Write to two locations which, if A20 wraps around, mov di, 0x0500
; will both be written to by the second statement. mov si, 0x0510
; Write 0 to 0x08000 to avoid the unlikely case that
; those bytes are there coincidentally and A20 is on.
mov edi, 0x0800
mov [edi], dword 0x00000000
mov edi, 0x00100800
mov [edi], dword 0xffaa00dd
; Check the lower address (which should NOT contain) mov al, byte [es:di]
; this value for the value. If it does contain it, the push ax
; memory space wraps around to 0 every 2^19 bytes.
cmp [0x0800], dword 0xffaa00dd
je .return_a20_not_enabled ; If the value at 0x0800
; was written to instead
; (or at the same time) of
; writing to 0x100800,
; the A20 wasn't enabled.
; Reset the values at the addresses to which the test
; values were written znd return that A20 is enabled.
mov edi, 0x0800
mov [edi], eax
mov edi, 0x00100800
mov [edi], ebx
.return_a20_enabled: mov al, byte [ds:si]
push ax
mov eax, 1 mov byte [es:di], 0x00
mov byte [ds:si], 0xFF
; Restore the one used register which cmp byte [es:di], 0xFF
; doesn't contain the return value.
mov ebx, [esi + (64 - 8)] pop ax
mov byte [ds:si], al
pop ax
mov byte [es:di], al
mov ax, 0
je .exit
mov ax, 1
.exit:
pop si
pop di
pop es
pop ds
popf
add esp, 64
pop dword esi
ret
.return_a20_not_enabled:
; It's only necessary to write to one of the addresses
; as both of them are equal either way.
mov edi, 0x0800
mov [edi], eax
mov eax, 0
; Restore the one used register which
; doesn't contain the return value.
mov ebx, [esi + (64 - 12)]
mov edi, [esi + (64 - 8)]
add esp, 64
pop dword esi
ret
enable_a20_through_keyboard_controller:
.prolog:
push dword esi
sub esp, 64
mov esi, esp
mov [esi + (64 - 8)], ebx
mov [esi + (64 - 12)], ecx
mov [esi + (64 - 16)], edx
mov [esi + (64 - 20)], edi
.epilog:
mov ebx, [esi + (64 - 8)]
mov ecx, [esi + (64 - 12)]
mov edx, [esi + (64 - 16)]
mov edi, [esi + (64 - 20)]
add esp, 64
pop dword esi
ret ret

View File

@ -2,6 +2,8 @@
GDT_MEMORY_AREA equ 0x7e00 GDT_MEMORY_AREA equ 0x7e00
GDT_ENTRY_COUNT equ 3 GDT_ENTRY_COUNT equ 3
; Arguments (note: all arguments are 4-byte padded): ; Arguments (note: all arguments are 4-byte padded):
; [Furthest from EBP] ; [Furthest from EBP]
; ;
@ -109,114 +111,6 @@ make_regular_data_segment_in_gdt:
; 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]
; COMMENTED OUT DUE TO BEING BUGGY!
; make_stack_segment_in_gdt:
; .prolog:
; push esi
; add 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 bx, 16
; mov [eax + 4], bl
; mov [eax + 7], bh
; .set_default_flags:
; or [eax + 6], word (0b1100 << 4)
; .set_stack_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
; ; Stack specific part
; or al, 0b0110 ; DATA | STACK | 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)]
; sub 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): ; Arguments (note: all arguments are 4-byte padded):
; [Furthest from EBP] ; [Furthest from EBP]
; ;
@ -323,6 +217,7 @@ make_code_segment_in_gdt:
hlt hlt
; [Furthest to EBP] ; [Furthest to EBP]
; 1) Return Address ; 1) Return Address
; [Nearest to EBP] ; [Nearest to EBP]
@ -344,73 +239,59 @@ load_flat_gdt:
.set_segments: .set_segments:
push ebp push ebp
mov ebp, esp mov ebp, esp
push dword 2 ; Segment Index push dword 2 ; Segment Index
push dword 0 ; Start Offset push dword 0 ; Start Offset
push dword 0xfffff ; Page Count push dword 0x000fffff ; Page Count
push dword 0 ; Required privilege level push dword 0 ; Required privilege level
call make_regular_data_segment_in_gdt call make_regular_data_segment_in_gdt
mov esp, ebp mov esp, ebp
pop ebp pop ebp
; COMMENTED OUT DUE TO BEING BUGGY
; push ebp
; mov ebp, esp
; push dword 3 ; Segment Index
; push dword LOWER_STACK_POINTER ; Start Offset
; push dword 0xfffff ; Page Count
; push dword 0 ; Required privilege level
; call make_stack_segment_in_gdt
; mov esp, ebp
; pop ebp
push ebp push ebp
mov ebp, esp mov ebp, esp
push dword 1 ; Segment Index push dword 1 ; Segment Index
push dword 0x0000 ; Start Offset push dword 0x0000 ; Start Offset
push dword 0xfffff ; Page Count push dword 0x000fffff ; Page Count
push dword 0 ; Required privilege level push dword 0 ; Required privilege level
call make_code_segment_in_gdt call make_code_segment_in_gdt
mov esp, ebp mov esp, ebp
pop ebp pop ebp
mov bx, 0 mov bx, 0
mov ds, bx mov ds, bx
lgdt [.gdtr]
mov bx, (2 << 3)
mov ds, bx
mov es, bx mov es, bx
mov fs, bx mov fs, bx
mov gs, bx mov gs, bx
; mov bx, (3 << 3)
mov ss, bx mov ss, bx
lgdt [.gdtr]
mov ebx, cr0 mov ebx, cr0
or ebx, 1 or ebx, 1
mov cr0, ebx mov cr0, ebx
xor bx, bx jmp dword (1 << 3):.epilog
mov ds, bx
jmp dword 0x08:.epilog
bits 32 bits 32
.epilog: .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 edi, [esi + (64 - 20)]
mov edx, [esi + (64 - 16)] mov edx, [esi + (64 - 16)]
mov ecx, [esi + (64 - 12)] mov ecx, [esi + (64 - 12)]
mov ebx, [esi + (64 - 8)] mov ebx, [esi + (64 - 8)]
mov eax, [esi + (64 - 4)] mov eax, [esi + (64 - 4)]
add esp, 64 jmp dword stage2_true_entry
pop esi
jmp stage2_true_entry
; jmp after_gdt_loaded
align 16 align 16
.gdtr: .gdtr:
.gdtr.length: .gdtr.length:
dw 15 dw 23
.gdtr.pointer: .gdtr.pointer:
dd GDT_MEMORY_AREA dd GDT_MEMORY_AREA