diff options
| author | Alejandro Sior <aho@sior.be> | 2022-05-13 20:50:39 +0200 |
|---|---|---|
| committer | Alejandro Sior <aho@sior.be> | 2022-05-13 20:50:39 +0200 |
| commit | e50cbe8eb763cf63fb44b047d5164f6fbf07eb39 (patch) | |
| tree | 8acb610a4f33462c843f54016861780ffadc010d /rt/+x86_64/realcall.S | |
boot: longmode to realmode stub
This is the initial commit to the repo. It adds all the code.
The last proper thing that I got working before committing is
calling real mode functions from long mode using a stub.
Next up, I would like to implement a disk reader abstraction over
the BIOS in order to read form partitions (most notably FAT32).
Diffstat (limited to 'rt/+x86_64/realcall.S')
| -rw-r--r-- | rt/+x86_64/realcall.S | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/rt/+x86_64/realcall.S b/rt/+x86_64/realcall.S new file mode 100644 index 0000000..46745f8 --- /dev/null +++ b/rt/+x86_64/realcall.S @@ -0,0 +1,161 @@ +.code64 + +.globl real.regs +real.regs: +reax: + .int 0x0 +rebx: + .int 0x0 +recx: + .int 0x0 +redx: + .int 0x0 +redi: + .int 0x0 +resi: + .int 0x0 + + +# :real_call +# This function is intended to be called from long mode +# and calls another function in real mode. Note that precautions have +# to be made. Recall that real mode can only access below 0xFFFFF. +# So the target function, and objects of arguments passed to it cannot +# be located above that. A reasonable scheme is to put the result +# in a buffer and then, once back in long mode, copy its content to higher +# above. +# The procedure to go from Long Mode back to Real Mode is explained in much details +# in the AMD64 Programmer's Manual Vol. 2, in particular, the figure 1-6. in section 1.3. +.globl real.call +real.call: + # :Push all the segments registers + push %rbx + push %r12 + push %r13 + push %r14 + push %r15 + push %di + + cli + + + lgdt gdtr32 + pushq $(gdt32_code - gdt32) + pushq $real_call_to_pmode_down + retfq +.code32 +real_call_to_pmode_down: + # :Here, we are in compatibility mode (32 bits) + + # :Disable paging + mov %cr0, %eax + and $~(1 << 31), %eax + mov %eax, %cr0 + + # :Disabling long mode in msr + mov $0xc0000080, %ecx + rdmsr + and $~(1 << 8), %eax + wrmsr + + # :Here we are in true protected mode (32 bits) + # Let's continue our descent to real mode + # by switching our gdt to 16 bits + lgdt gdtr16 + ljmp $(gdt16_code - gdt16), $real_call_to_16bits_pmode_down +.code16 +real_call_to_16bits_pmode_down: + + # :Disable protected mode + mov %cr0, %eax + and $~1, %eax + mov %eax, %cr0 + + ljmp $0, $real_call_to_16bits_rmode_down +real_call_to_16bits_rmode_down: + mov $0, %ax + mov %ax, %ds + mov %ax, %es + mov %ax, %ss + mov %ax, %gs + mov %ax, %fs + + # :real mode, yay + + lidt bios_idtr + + sti + + mov rebx, %ebx + mov recx, %ecx + mov redx, %edx + mov redi, %edi + mov resi, %esi + + pop %ax + call *%ax + + mov %eax, reax + mov %ebx, rebx + mov %ecx, recx + mov %edx, redx + mov %edi, redi + mov %esi, resi + + cli + + # :Restore protected mode + mov %cr0, %eax + or $1, %eax + mov %eax, %cr0 + + lgdt gdtr32 + ljmp $(gdt32_code - gdt32), $real_call_to_pmode_up +.code32 +real_call_to_pmode_up: + # :Restore PAE (probably unneeded) + mov %cr4, %eax + or $(1 << 5), %eax + mov %eax, %cr4 + + # :Restore long mode + mov $0xc0000080, %ecx + rdmsr + or $(1 << 8), %eax + wrmsr + + # :Restore paging + mov %cr0, %eax + or $1 << 31, %eax + mov %eax, %cr0 + + lgdt gdtr64 + ljmp $(gdt64_code - gdt64), $real_call_to_longmode_up +.code64 +real_call_to_longmode_up: + mov $(gdt64_data - gdt64), %ax + mov %ax, %fs + mov %ax, %gs + mov %ax, %ss + mov %ax, %es + mov %ax, %ds + +real_call_end: + pop %r15 + pop %r14 + pop %r13 + pop %r12 + pop %rbx + ret + +.globl testt +testt: + mov $1, %al + mov $0x43, %bh + mov $0x6, %ah + mov $0, %ch + mov $0, %cl + mov $5, %dh + mov $5, %dl + int $0x10 + ret |
