1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
|
.code64
.globl bios.regs
bios.regs:
reax:
.int 0x0
rebx:
.int 0x0
recx:
.int 0x0
redx:
.int 0x0
redi:
.int 0x0
resi:
.int 0x0
res:
.short 0x0
prev_idt:
.quad 0x0
prev_gdt:
.quad 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 bios.call
bios.call:
xchg %bx, %bx
push %rbx
push %r12
push %r13
push %r14
push %r15
pushf
# :Save code segment (and second push to prepare for far return)
mov %cs, %ax
push %ax
push %ax
# :Save data segment
mov %ds, %ax
push %ax
# :Save the interrupt number
push %di
cli
sidt (prev_idt)
sgdt (prev_gdt)
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
lidt bios_idtr
# :Self modifying code to call arbitrary interrupts
pop %ax
mov $real_call_int, %bx
mov %al, 1(%bx)
# :Load registers
mov (reax), %eax
mov (rebx), %ebx
mov (recx), %ecx
mov (redx), %edx
mov (redi), %edi
mov (resi), %esi
mov (res), %es
sti
real_call_int:
int $0x0
cli
mov %es, (res)
mov %eax, (reax)
mov %ebx, (rebx)
mov %ecx, (recx)
mov %edx, (redx)
mov %edi, (redi)
mov %esi, (resi)
# :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 (prev_gdt)
# :At this point %ds is latest in stack
pop %ax
# :At this point %cs is latest in stack
# Do a long jump
push $real_call_to_longmode_up
retf
.code64
real_call_to_longmode_up:
mov %ax, %fs
mov %ax, %gs
mov %ax, %ss
mov %ax, %es
mov %ax, %ds
# :Avoid doing this until the bootloader loads a 64 bits IDT
# XXX
#idt (prev_idt)
real_call_end:
popf
pop %r15
pop %r14
pop %r13
pop %r12
pop %rbx
ret
|