summaryrefslogtreecommitdiff
path: root/rt/+x86_64/realcall.S
diff options
context:
space:
mode:
authorAlejandro Sior <aho@sior.be>2022-05-13 20:50:39 +0200
committerAlejandro Sior <aho@sior.be>2022-05-13 20:50:39 +0200
commite50cbe8eb763cf63fb44b047d5164f6fbf07eb39 (patch)
tree8acb610a4f33462c843f54016861780ffadc010d /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.S161
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