refactor + wip bios call from protected mode

This commit is contained in:
pantonshire committed 2024-10-16 17:45:52 +01:00
1 parent 121e13165b
commit 766a9c981c
8 files changed
+389 -136

No files matched your search

+169
View File
@@ -0,0 +1,169 @@
%include "defines.s"
extern gdt_flat_slice
extern gdt_flat
extern GDT_FLAT_IDX_CODE_32
extern GDT_FLAT_IDX_CODE_16
extern GDT_FLAT_IDX_DATA
section .text
[bits 32]
; FIXME: use a separate stack for real mode
bios_call:
.PARAMS_START equ 36
.PARAM_BIOS_INTR_NUM equ .PARAMS_START
.PARAM_BIOS_INTR_ARGS_PTR equ .PARAMS_START + 4
pushad
mov ebp, esp
push ds
push es
push fs
push gs
push ss
; Push cr0, the GDT and the IDT to the stack so we can restore them once we're finished with real mode.
mov eax, cr0
push eax
sub esp, 8
sidt [esp]
sub esp, 8
sgdt [esp]
; Load the address of the `int` instruction we want to run, and store it on the stack for later.
mov eax, [ebp + .PARAM_BIOS_INTR_NUM]
lea eax, [int_fns + (3 * eax)]
push eax
; Copy the BIOS call arguments provided by the caller onto the stack, for us to pop later.
sub esp, 28
mov ecx, 28
mov esi, [ebp + .PARAM_BIOS_INTR_ARGS_PTR]
mov edi, esp
rep movsb
; Switch to a 16-bit code segment, but remain in protected mode.
jmp GDT_FLAT_IDX_CODE_16:.protected_mode_16
[bits 16]
.protected_mode_16:
; Disable paging and protected mode.
mov eax, cr0
and eax, 0x7ffffffe
mov cr0, eax
jmp 0:.real_mode
.real_mode:
xor ax, ax
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
; Load the caller-provided register values off the stack.
pop eax
pop ebx
pop ecx
pop edx
pop esi
pop edi
pop ds
pop es
; Call the function which runs the correct `int` instruction.
push bp
mov bp, sp
call [bp + 2]
pop bp
; TODO: return resulting registers to the caller
; Reset the segment registers that were changed to the caller-provided values.
xor ax, ax
mov ds, ax
mov es, ax
; Reload the GDT in case the BIOS call changed it.
mov bx, sp
lgdt [bx]
add sp, 8
mov bx, sp
lidt [bx]
add sp, 8
; Restore cr0, re-enabling protected mode.
pop eax
mov cr0, eax
jmp GDT_FLAT_IDX_CODE_32:.protected_mode_32
[bits 32]
.protected_mode_32:
pop ss
pop gs
pop fs
pop es
pop ds
popad
ret
global bios_call
%macro int_fn 1
int %1
ret
%endmacro
int_fns:
int_fn 0x00
int_fn 0x01
int_fn 0x02
int_fn 0x03
int_fn 0x04
int_fn 0x05
int_fn 0x06
int_fn 0x07
int_fn 0x08
int_fn 0x09
int_fn 0x0a
int_fn 0x0b
int_fn 0x0c
int_fn 0x0d
int_fn 0x0e
int_fn 0x0f
int_fn 0x10
int_fn 0x11
int_fn 0x12
int_fn 0x13
int_fn 0x14
int_fn 0x15
int_fn 0x16
int_fn 0x17
int_fn 0x18
int_fn 0x19
int_fn 0x1a
int_fn 0x1b
int_fn 0x1c
int_fn 0x1d
int_fn 0x1e
int_fn 0x1f
section .rodata
idt_real:
dw 0x3ff
dd 0
+95
View File
@@ -0,0 +1,95 @@
section .rodata
gdt_flat_slice:
dw GDT_FLAT_LEN
dd gdt_flat
global gdt_flat_slice
; Segment descriptor layout
; | Range (bits) | Field |
; |--------------|---------------|
; | 0-16 | limit |
; | 16-32 | base |
; | 32-40 | base cont. |
; | 40-48 | access |
; | 48-52 | limit cont. |
; | 52-56 | flags |
; | 56-64 | base cont. |
;
; Flags
; - 0: reserved
; - 1: long-mode code segment
; - 2: size
; - unset: 16-bit
; - set: 32-bit
; - 3: granularity
; - unset: limit is measured in bytes
; - set: limit is measured in 4KiB pages
;
; Access
; - 0: accessed
; - unset: CPU will set it when the segment is accessed
; - 1: readable / writable
; - data segments: is segment writable (data segments are always readable)
; - code segments: is segment readable (code segments are never writable)
; - 2: direction / conforming
; - data segments: whether segment grows down
; - code segments: whether this can be executed from a lower-privilege ring
; - 3: executable
; - unset: this is a data segment
; - set: this is a code segment
; - 4: descriptor type
; - unset: this is a task state segment
; - set: this is a data or code segment
; - 5-6: privilege level (ring number)
; - 7: present (must be set)
;
align 8
gdt_flat:
; First GDT entry must be 0.
dq 0
; 32-bit code segment.
; Bytes 0x0000 - 0xffff.
.segment_code_32:
db 0xff, 0xff, \
0x00, 0x00, \
0x00, \
10011011b, \
01000000b, \
0x00
; 16-bit code segment, to use if we want to switch back to real mode.
; Bytes 0x0000 - 0xffff.
.segment_code_16:
db 0xff, 0xff, \
0x00, 0x00, \
0x00, \
10011011b, \
00000000b, \
0x00
; Data segment.
; Pages 0x000000 - 0x0fffff, which covers the entire 32-bit address space (start of 0xfffff-th page
; is 0xfffff * 4096 = 0xfffff000, end of page exclusive is 0xfffff000 + 4096 = 0x100000000).
.segment_data:
db 0xff, 0xff, \
0x00, 0x00, \
0x00, \
10010011b, \
11001111b, \
0x00
global gdt_flat
GDT_FLAT_LEN equ ($ - gdt_flat)
GDT_FLAT_IDX_CODE_32 equ (gdt_flat.segment_code_32 - gdt_flat)
global GDT_FLAT_IDX_CODE_32
GDT_FLAT_IDX_CODE_16 equ (gdt_flat.segment_code_16 - gdt_flat)
global GDT_FLAT_IDX_CODE_16
GDT_FLAT_IDX_DATA equ (gdt_flat.segment_data - gdt_flat)
global GDT_FLAT_IDX_DATA
+13 -90
View File
@@ -1,4 +1,5 @@
%include "defines.s"
%include "ps2.s"
[bits 16]
@@ -7,6 +8,12 @@ extern boot1_bin_sectors
extern boot1_magic
extern _start
extern gdt_flat_slice
extern gdt_flat
extern GDT_FLAT_IDX_CODE_32
extern GDT_FLAT_IDX_CODE_16
extern GDT_FLAT_IDX_DATA
section .prelude
%macro copy_stack_var_to_globals 2
@@ -335,22 +342,22 @@ prelude_main:
cli
; Load our flat-address-space GDT.
lgdt [flat_gdt_slice]
lgdt [gdt_flat_slice]
; Set the protected-mode bit in cr0.
mov eax, cr0
or al, 0x01
mov cr0, eax
; Long jump to set the code segment to flat_gdt.segment_code, and to clear the instruction
; Long jump to set the code segment to gdt_flat.segment_code, and to clear the instruction
; pipeline.
jmp (flat_gdt.segment_code_32 - flat_gdt):.protected_mode
jmp GDT_FLAT_IDX_CODE_32:.protected_mode_32
[bits 32]
.protected_mode:
.protected_mode_32:
; Set the data segments to flat_gdt.segment_data.
mov eax, (flat_gdt.segment_data - flat_gdt)
; Set the data segments to gdt_flat.segment_data.
mov eax, GDT_FLAT_IDX_DATA
mov ds, eax
mov es, eax
mov fs, eax
@@ -363,87 +370,3 @@ prelude_main:
jmp _start
jmp panic_simple
section .data
flat_gdt_slice:
dw FLAT_GDT_LEN
dd flat_gdt
; Segment descriptor layout
; | Range (bits) | Field |
; |--------------|---------------|
; | 0-16 | limit |
; | 16-32 | base |
; | 32-40 | base cont. |
; | 40-48 | access |
; | 48-52 | limit cont. |
; | 52-56 | flags |
; | 56-64 | base cont. |
;
; Flags
; - 0: reserved
; - 1: long-mode code segment
; - 2: size
; - unset: 16-bit
; - set: 32-bit
; - 3: granularity
; - unset: limit is measured in bytes
; - set: limit is measured in 4KiB pages
;
; Access
; - 0: accessed
; - unset: CPU will set it when the segment is accessed
; - 1: readable / writable
; - data segments: is segment writable (data segments are always readable)
; - code segments: is segment readable (code segments are never writable)
; - 2: direction / conforming
; - data segments: whether segment grows down
; - code segments: whether this can be executed from a lower-privilege ring
; - 3: executable
; - unset: this is a data segment
; - set: this is a code segment
; - 4: descriptor type
; - unset: this is a task state segment
; - set: this is a data or code segment
; - 5-6: privilege level (ring number)
; - 7: present (must be set)
;
align 8
flat_gdt:
; First GDT entry must be 0.
.segment_null:
dq 0
; 32-bit code segment.
; Bytes 0x0000 - 0xffff.
.segment_code_32:
db 0xff, 0xff, \
0x00, 0x00, \
0x00, \
10011011b, \
01000000b, \
0x00
; 16-bit code segment, to use if we want to switch back to real mode.
; Bytes 0x0000 - 0xffff.
.segment_code_16:
db 0xff, 0xff, \
0x00, 0x00, \
0x00, \
10011011b, \
00000000b, \
0x00
; Data segment.
; Pages 0x000000 - 0x0fffff, which covers the entire 32-bit address space (start of 0xfffff-th page
; is 0xfffff * 4096 = 0xfffff000, end of page exclusive is 0xfffff000 + 4096 = 0x100000000).
.segment_data:
db 0xff, 0xff, \
0x00, 0x00, \
0x00, \
10010011b, \
11001111b, \
0x00
FLAT_GDT_LEN equ ($ - flat_gdt)