1 /* SPDX-License-Identifier: GPL-2.0-only */
3 #define REALMODE_BASE 0x600
4 #define RELOCATED(x) (x - __realmode_code + REALMODE_BASE)
6 #include <arch/ram_segs.h>
11 /* This is the intXX interrupt handler stub code. It gets copied
12 * to the IDT and to some fixed addresses in the F segment. Before
13 * the code can used, it gets patched up by the C function copying
14 * it: byte 3 (the $0 in movb $0, %al) is overwritten with the int#.
21 movb $0, %al /* This instruction gets modified */
22 ljmp $0, $__interrupt_handler_16bit
23 .globl __idt_handler_size
25 .long . - __idt_handler
28 /* In order to be independent of coreboot's position in RAM
29 * we relocate a part of the code to the low megabyte, so the
30 * CPU can use it in real-mode. This code lives at __realmode_code.
32 .globl __realmode_code
35 /* Realmode function return. */
36 __realmode_ret = RELOCATED(.)
39 /* Realmode IDT pointer structure. */
40 __realmode_idt = RELOCATED(.)
41 .word 1023 /* 16 bit limit */
42 .long 0 /* 24 bit base */
45 /* Preserve old stack */
46 __stack = RELOCATED(.)
49 /* Register store for realmode_call and realmode_interrupt */
50 __registers = RELOCATED(.)
54 .long 0 /* 12 - EDX */
55 .long 0 /* 16 - ESI */
56 .long 0 /* 20 - EDI */
58 /* 256 byte buffer, used by int10 */
59 .globl __realmode_buffer
64 .globl __realmode_call
66 /* save all registers to the stack */
70 /* Move the protected mode stack pointer to a safe place */
74 /* This function is called with regparm=0 and we have to
75 * skip the 36 byte from pushf+pusha. Hence start at 40.
80 mov %ax, __lcall_instr + 1
81 andl $0xffff0000, %eax
83 mov %ax, __lcall_instr + 3
85 /* initial register values */
87 movl %eax, __registers + 0 /* eax */
89 movl %eax, __registers + 4 /* ebx */
91 movl %eax, __registers + 8 /* ecx */
93 movl %eax, __registers + 12 /* edx */
95 movl %eax, __registers + 16 /* esi */
97 movl %eax, __registers + 20 /* edi */
99 /* Activate the right segment descriptor real mode. */
100 ljmp $RAM_CODE16_SEG, $RELOCATED(1f)
103 /* 16 bit code from here on... */
105 /* Load the segment registers w/ properly configured
106 * segment descriptors. They will retain these
107 * configurations (limits, writability, etc.) once
108 * protected mode is turned off.
110 mov $RAM_DATA16_SEG, %ax
117 /* Turn off protection */
122 /* Now really going into real mode */
123 ljmp $0, $RELOCATED(1f)
125 /* Setup a stack: Put the stack at the end of page zero.
126 * That way we can easily share it between real and
127 * protected, since the 16 bit ESP at segment 0 will
128 * work for any case. */
134 /* Load 16 bit IDT */
139 /* initialize registers for option ROM lcall */
140 movl __registers + 0, %eax
141 movl __registers + 4, %ebx
142 movl __registers + 8, %ecx
143 movl __registers + 12, %edx
144 movl __registers + 16, %esi
145 movl __registers + 20, %edi
147 /* Set all segments to 0x0000, ds to 0x0040 */
157 /* ************************************ */
158 __lcall_instr = RELOCATED(.)
161 /* ************************************ */
164 * Here is end of real mode call and time to go back to protected mode.
165 * Before that its better to store current eax into some memory address
166 * so that context persist in protected mode too.
168 mov %eax, __realmode_ret
170 /* If we got here, we are just about done.
171 * Need to get back to protected mode.
177 /* Now that we are in protected mode
178 * jump to a 32 bit code segment.
180 ljmpl $RAM_CODE_SEG, $RELOCATED(1f)
183 mov $RAM_DATA_SEG, %ax
190 /* restore proper idt */
193 /* restore stack pointer, eflags and register values */
199 /* return AX from OPROM call */
200 mov __realmode_ret, %eax
203 .globl __realmode_interrupt
204 __realmode_interrupt:
205 /* save all registers to the stack */
209 /* save the stack pointer */
213 /* This function is called with regparm=0 and we have to
214 * skip the 36 byte from pushf+pusha. Hence start at 40.
217 /* prepare interrupt calling code */
219 movb %al, __intXX_instr + 1 /* intno */
221 /* initial register values */
223 movl %eax, __registers + 0 /* eax */
225 movl %eax, __registers + 4 /* ebx */
227 movl %eax, __registers + 8 /* ecx */
229 movl %eax, __registers + 12 /* edx */
231 movl %eax, __registers + 16 /* esi */
233 movl %eax, __registers + 20 /* edi */
235 /* This configures CS properly for real mode. */
236 ljmp $RAM_CODE16_SEG, $RELOCATED(1f)
238 .code16 /* 16 bit code from here on... */
240 /* Load the segment registers w/ properly configured segment
241 * descriptors. They will retain these configurations (limits,
242 * writability, etc.) once protected mode is turned off.
244 mov $RAM_DATA16_SEG, %ax
251 /* Turn off protected mode */
256 /* Now really going into real mode */
257 ljmpl $0, $RELOCATED(1f)
260 /* put the stack at the end of page zero. That way we can easily
261 * share it between real mode and protected mode, because %esp and
262 * %ss:%sp point to the same memory.
270 /* Load 16-bit intXX IDT */
275 /* initialize registers for intXX call */
276 movl __registers + 0, %eax
277 movl __registers + 4, %ebx
278 movl __registers + 8, %ecx
279 movl __registers + 12, %edx
280 movl __registers + 16, %esi
281 movl __registers + 20, %edi
283 /* Set all segments to 0x0000 */
292 __intXX_instr = RELOCATED(.)
293 .byte 0xcd, 0x00 /* This becomes intXX */
296 * Here is end of real mode call and time to go back to protected mode.
297 * Before that its better to store current eax into some memory address
298 * so that context persist in protected mode too.
300 mov %eax, __realmode_ret
302 /* Ok, the job is done, now go back to protected mode coreboot */
307 /* Now that we are in protected mode jump to a 32-bit code segment. */
308 ljmpl $RAM_CODE_SEG, $RELOCATED(1f)
311 mov $RAM_DATA_SEG, %ax
318 /* restore coreboot's 32-bit IDT */
321 /* restore stack pointer, eflags and register values and exit */
325 /* return AX from OPROM call */
326 mov __realmode_ret, %eax
329 /* This is the 16-bit interrupt entry point called by the IDT stub code.
331 * Before this code is called, %eax is pushed to the stack, and the
332 * interrupt number is loaded into %al. On return this function cleans up
336 __interrupt_handler_16bit = RELOCATED(.)
342 /* Clear DF to not break ABI assumptions */
345 /* Clean up the interrupt number. We could have done this in the stub,
346 * but it would have cost 2 more bytes per stub entry.
349 pushl %eax /* ... and make it the first parameter */
351 /* Switch to protected mode */
356 /* ... and jump to a 32 bit code segment. */
357 ljmpl $RAM_CODE_SEG, $RELOCATED(1f)
360 mov $RAM_DATA_SEG, %ax
369 /* Call the C interrupt handler */
370 movl $interrupt_handler, %eax
373 /* Now return to real mode ... */
374 ljmp $RAM_CODE16_SEG, $RELOCATED(1f)
377 /* Load the segment registers with properly configured segment
378 * descriptors. They will retain these configurations (limits,
379 * writability, etc.) once protected mode is turned off.
381 mov $RAM_DATA16_SEG, %ax
388 /* Disable Protected Mode */
393 /* Now really going into real mode */
394 ljmp $0, $RELOCATED(1f)
396 /* Restore real-mode stack segment */
400 /* Restore 16 bit IDT */
405 /* Restore all registers, including those
406 * manipulated by the C handler
416 .globl __realmode_code_size
417 __realmode_code_size:
418 .long . - __realmode_code