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