blob: 9dbf969373611da1a94084a8665b0deb28eea8d3 [file] [log] [blame]
Simon Glass0ca24262014-11-14 20:56:32 -07001/*
2 * From coreboot x86_asm.S, cleaned up substantially
3 *
4 * Copyright (C) 2009-2010 coresystems GmbH
5 *
6 * SPDX-License-Identifier: GPL-2.0
7 */
8
9#include <asm/processor.h>
10#include <asm/processor-flags.h>
11#include "bios.h"
12
13#define SEG(segment) $segment * X86_GDT_ENTRY_SIZE
14
15/*
16 * This is the interrupt handler stub code. It gets copied to the IDT and
17 * to some fixed addresses in the F segment. Before the code can used,
18 * it gets patched up by the C function copying it: byte 3 (the $0 in
19 * movb $0, %al) is overwritten with the interrupt numbers.
20 */
21
22 .code16
23 .globl __idt_handler
24__idt_handler:
25 pushal
26 movb $0, %al /* This instruction gets modified */
27 ljmp $0, $__interrupt_handler_16bit
28 .globl __idt_handler_size
29__idt_handler_size:
30 .long . - __idt_handler
31
32.macro setup_registers
33 /* initial register values */
34 movl 44(%ebp), %eax
35 movl %eax, __registers + 0 /* eax */
36 movl 48(%ebp), %eax
37 movl %eax, __registers + 4 /* ebx */
38 movl 52(%ebp), %eax
39 movl %eax, __registers + 8 /* ecx */
40 movl 56(%ebp), %eax
41 movl %eax, __registers + 12 /* edx */
42 movl 60(%ebp), %eax
43 movl %eax, __registers + 16 /* esi */
44 movl 64(%ebp), %eax
45 movl %eax, __registers + 20 /* edi */
46.endm
47
48.macro enter_real_mode
49 /* Activate the right segment descriptor real mode. */
50 ljmp SEG(X86_GDT_ENTRY_16BIT_CS), $PTR_TO_REAL_MODE(1f)
511:
52.code16
53 /*
54 * Load the segment registers with properly configured segment
55 * descriptors. They will retain these configurations (limits,
56 * writability, etc.) once protected mode is turned off.
57 */
58 mov SEG(X86_GDT_ENTRY_16BIT_DS), %ax
59 mov %ax, %ds
60 mov %ax, %es
61 mov %ax, %fs
62 mov %ax, %gs
63 mov %ax, %ss
64
65 /* Turn off protection */
66 movl %cr0, %eax
67 andl $~X86_CR0_PE, %eax
68 movl %eax, %cr0
69
70 /* Now really going into real mode */
71 ljmp $0, $PTR_TO_REAL_MODE(1f)
721:
73 /*
74 * Set up a stack: Put the stack at the end of page zero. That way
75 * we can easily share it between real and protected, since the
76 * 16-bit ESP at segment 0 will work for any case.
77 */
78 mov $0x0, %ax
79 mov %ax, %ss
80
81 /* Load 16 bit IDT */
82 xor %ax, %ax
83 mov %ax, %ds
84 lidt __realmode_idt
85
86.endm
87
88.macro prepare_for_irom
89 movl $0x1000, %eax
90 movl %eax, %esp
91
92 /* Initialise registers for option rom lcall */
93 movl __registers + 0, %eax
94 movl __registers + 4, %ebx
95 movl __registers + 8, %ecx
96 movl __registers + 12, %edx
97 movl __registers + 16, %esi
98 movl __registers + 20, %edi
99
100 /* Set all segments to 0x0000, ds to 0x0040 */
101 push %ax
102 xor %ax, %ax
103 mov %ax, %es
104 mov %ax, %fs
105 mov %ax, %gs
106 mov SEG(X86_GDT_ENTRY_16BIT_FLAT_DS), %ax
107 mov %ax, %ds
108 pop %ax
109
110.endm
111
112.macro enter_protected_mode
113 /* Go back to protected mode */
114 movl %cr0, %eax
115 orl $X86_CR0_PE, %eax
116 movl %eax, %cr0
117
118 /* Now that we are in protected mode jump to a 32 bit code segment */
119 data32 ljmp SEG(X86_GDT_ENTRY_32BIT_CS), $PTR_TO_REAL_MODE(1f)
1201:
121 .code32
122 mov SEG(X86_GDT_ENTRY_32BIT_DS), %ax
123 mov %ax, %ds
124 mov %ax, %es
125 mov %ax, %gs
126 mov %ax, %ss
127 mov SEG(X86_GDT_ENTRY_32BIT_FS), %ax
128 mov %ax, %fs
129
130 /* restore proper idt */
131 lidt idt_ptr
132.endm
133
134/*
135 * In order to be independent of U-Boot's position in RAM we relocate a part
136 * of the code to the first megabyte of RAM, so the CPU can use it in
137 * real-mode. This code lives at asm_realmode_code.
138 */
139 .globl asm_realmode_code
140asm_realmode_code:
141
142/* Realmode IDT pointer structure. */
143__realmode_idt = PTR_TO_REAL_MODE(.)
144 .word 1023 /* 16 bit limit */
145 .long 0 /* 24 bit base */
146 .word 0
147
148/* Preserve old stack */
149__stack = PTR_TO_REAL_MODE(.)
150 .long 0
151
152/* Register store for realmode_call and realmode_interrupt */
153__registers = PTR_TO_REAL_MODE(.)
154 .long 0 /* 0 - EAX */
155 .long 0 /* 4 - EBX */
156 .long 0 /* 8 - ECX */
157 .long 0 /* 12 - EDX */
158 .long 0 /* 16 - ESI */
159 .long 0 /* 20 - EDI */
160
161/* 256 byte buffer, used by int10 */
162 .globl asm_realmode_buffer
163asm_realmode_buffer:
164 .skip 256
165
166 .code32
167 .globl asm_realmode_call
168asm_realmode_call:
169 /* save all registers to the stack */
170 pusha
171 pushf
172 movl %esp, __stack
173 movl %esp, %ebp
174
175 /*
176 * This function is called with regparm=0 and we have to skip the
177 * 36 bytes from pushf+pusha. Hence start at 40.
178 * Set up our call instruction.
179 */
180 movl 40(%ebp), %eax
181 mov %ax, __lcall_instr + 1
182 andl $0xffff0000, %eax
183 shrl $4, %eax
184 mov %ax, __lcall_instr + 3
185
186 wbinvd
187
188 setup_registers
189 enter_real_mode
190 prepare_for_irom
191
192__lcall_instr = PTR_TO_REAL_MODE(.)
193 .byte 0x9a
194 .word 0x0000, 0x0000
195
196 enter_protected_mode
197
198 /* restore stack pointer, eflags and register values and exit */
199 movl __stack, %esp
200 popf
201 popa
202 ret
203
204 .globl __realmode_interrupt
205__realmode_interrupt:
206 /* save all registers to the stack and store the stack pointer */
207 pusha
208 pushf
209 movl %esp, __stack
210 movl %esp, %ebp
211
212 /*
213 * This function is called with regparm=0 and we have to skip the
214 * 36 bytes from pushf+pusha. Hence start at 40.
215 * Prepare interrupt calling code.
216 */
217 movl 40(%ebp), %eax
218 movb %al, __intXX_instr + 1 /* intno */
219
220 setup_registers
221 enter_real_mode
222 prepare_for_irom
223
224__intXX_instr = PTR_TO_REAL_MODE(.)
225 .byte 0xcd, 0x00 /* This becomes intXX */
226
227 enter_protected_mode
228
229 /* restore stack pointer, eflags and register values and exit */
230 movl __stack, %esp
231 popf
232 popa
233 ret
234
235/*
236 * This is the 16-bit interrupt entry point called by the IDT stub code.
237 *
238 * Before this code code is called, %eax is pushed to the stack, and the
239 * interrupt number is loaded into %al. On return this function cleans up
240 * for its caller.
241 */
242 .code16
243__interrupt_handler_16bit = PTR_TO_REAL_MODE(.)
244 push %ds
245 push %es
246 push %fs
247 push %gs
248
Jian Luo7b5c3492015-07-06 16:42:06 +0800249 /* Save real mode SS */
250 movw %ss, %cs:__realmode_ss
251
Simon Glass0ca24262014-11-14 20:56:32 -0700252 /* Clear DF to not break ABI assumptions */
253 cld
254
255 /*
256 * Clean up the interrupt number. We could do this in the stub, but
257 * it would cost two more bytes per stub entry.
258 */
259 andl $0xff, %eax
260 pushl %eax /* ... and make it the first parameter */
261
262 enter_protected_mode
263
Jian Luo7b5c3492015-07-06 16:42:06 +0800264 /*
265 * Now we are in protected mode. We need compute the right ESP based
266 * on saved real mode SS otherwise interrupt_handler() won't get
267 * correct parameters from the stack.
268 */
269 movzwl %cs:__realmode_ss, %ecx
270 shll $4, %ecx
271 addl %ecx, %esp
272
Simon Glass0ca24262014-11-14 20:56:32 -0700273 /* Call the C interrupt handler */
274 movl $interrupt_handler, %eax
275 call *%eax
276
Jian Luo7b5c3492015-07-06 16:42:06 +0800277 /* Restore real mode ESP based on saved SS */
278 movzwl %cs:__realmode_ss, %ecx
279 shll $4, %ecx
280 subl %ecx, %esp
281
Simon Glass0ca24262014-11-14 20:56:32 -0700282 enter_real_mode
283
Jian Luo7b5c3492015-07-06 16:42:06 +0800284 /* Restore real mode SS */
285 movw %cs:__realmode_ss, %ss
286
Simon Glass0ca24262014-11-14 20:56:32 -0700287 /*
288 * Restore all registers, including those manipulated by the C
289 * handler
290 */
291 popl %eax
292 pop %gs
293 pop %fs
294 pop %es
295 pop %ds
296 popal
297 iret
298
Jian Luo7b5c3492015-07-06 16:42:06 +0800299__realmode_ss = PTR_TO_REAL_MODE(.)
300 .word 0
301
Simon Glass0ca24262014-11-14 20:56:32 -0700302 .globl asm_realmode_code_size
303asm_realmode_code_size:
304 .long . - asm_realmode_code