1/* SPDX-License-Identifier: GPL-2.0-only */ 2 3#define REALMODE_BASE 0x600 4#define RELOCATED(x) (x - __realmode_code + REALMODE_BASE) 5 6#include <arch/ram_segs.h> 7 8/* CR0 bits */ 9#define PE (1 << 0) 10 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#. 15 */ 16 17 .code16 18 .globl __idt_handler 19__idt_handler: 20 pushal 21 movb $0, %al /* This instruction gets modified */ 22 ljmp $0, $__interrupt_handler_16bit 23 .globl __idt_handler_size 24__idt_handler_size: 25 .long . - __idt_handler 26 27 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. 31 */ 32 .globl __realmode_code 33__realmode_code: 34 35/* Realmode function return. */ 36__realmode_ret = RELOCATED(.) 37 .long 0 38 39/* Realmode IDT pointer structure. */ 40__realmode_idt = RELOCATED(.) 41 .word 1023 /* 16 bit limit */ 42 .long 0 /* 24 bit base */ 43 .word 0 44 45/* Preserve old stack */ 46__stack = RELOCATED(.) 47 .long 0 48 49/* Register store for realmode_call and realmode_interrupt */ 50__registers = RELOCATED(.) 51 .long 0 /* 0 - EAX */ 52 .long 0 /* 4 - EBX */ 53 .long 0 /* 8 - ECX */ 54 .long 0 /* 12 - EDX */ 55 .long 0 /* 16 - ESI */ 56 .long 0 /* 20 - EDI */ 57 58/* 256 byte buffer, used by int10 */ 59 .globl __realmode_buffer 60__realmode_buffer: 61 .skip 256 62 63 .code32 64 .globl __realmode_call 65__realmode_call: 66 /* save all registers to the stack */ 67 pusha 68 pushf 69 70 /* Move the protected mode stack pointer to a safe place */ 71 movl %esp, __stack 72 movl %esp, %ebp 73 74 /* This function is called with regparm=0 and we have to 75 * skip the 36 byte from pushf+pusha. Hence start at 40. 76 */ 77 78 /* entry point */ 79 movl 40(%ebp), %eax 80 mov %ax, __lcall_instr + 1 81 andl $0xffff0000, %eax 82 shrl $4, %eax 83 mov %ax, __lcall_instr + 3 84 85 /* initial register values */ 86 movl 44(%ebp), %eax 87 movl %eax, __registers + 0 /* eax */ 88 movl 48(%ebp), %eax 89 movl %eax, __registers + 4 /* ebx */ 90 movl 52(%ebp), %eax 91 movl %eax, __registers + 8 /* ecx */ 92 movl 56(%ebp), %eax 93 movl %eax, __registers + 12 /* edx */ 94 movl 60(%ebp), %eax 95 movl %eax, __registers + 16 /* esi */ 96 movl 64(%ebp), %eax 97 movl %eax, __registers + 20 /* edi */ 98 99 /* Activate the right segment descriptor real mode. */ 100 ljmp $RAM_CODE16_SEG, $RELOCATED(1f) 1011: 102.code16 103 /* 16 bit code from here on... */ 104 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. 109 */ 110 mov $RAM_DATA16_SEG, %ax 111 mov %ax, %ds 112 mov %ax, %es 113 mov %ax, %fs 114 mov %ax, %gs 115 mov %ax, %ss 116 117 /* Turn off protection */ 118 movl %cr0, %eax 119 andl $~PE, %eax 120 movl %eax, %cr0 121 122 /* Now really going into real mode */ 123 ljmp $0, $RELOCATED(1f) 1241: 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. */ 129 mov $0x0, %ax 130 mov %ax, %ss 131 movl $0x1000, %eax 132 movl %eax, %esp 133 134 /* Load 16 bit IDT */ 135 xor %ax, %ax 136 mov %ax, %ds 137 lidt __realmode_idt 138 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 146 147 /* Set all segments to 0x0000, ds to 0x0040 */ 148 push %ax 149 xor %ax, %ax 150 mov %ax, %es 151 mov %ax, %fs 152 mov %ax, %gs 153 mov $0x40, %ax 154 mov %ax, %ds 155 pop %ax 156 157 /* ************************************ */ 158__lcall_instr = RELOCATED(.) 159 .byte 0x9a 160 .word 0x0000, 0x0000 161 /* ************************************ */ 162 163 /* 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. 167 */ 168 mov %eax, __realmode_ret 169 170 /* If we got here, we are just about done. 171 * Need to get back to protected mode. 172 */ 173 movl %cr0, %eax 174 orl $PE, %eax 175 movl %eax, %cr0 176 177 /* Now that we are in protected mode 178 * jump to a 32 bit code segment. 179 */ 180 ljmpl $RAM_CODE_SEG, $RELOCATED(1f) 1811: 182 .code32 183 mov $RAM_DATA_SEG, %ax 184 mov %ax, %ds 185 mov %ax, %es 186 mov %ax, %fs 187 mov %ax, %gs 188 mov %ax, %ss 189 190 /* restore proper idt */ 191 lidt idtarg 192 193 /* restore stack pointer, eflags and register values */ 194 movl __stack, %esp 195 popf 196 popa 197 198 /* and exit */ 199 /* return AX from OPROM call */ 200 mov __realmode_ret, %eax 201 ret 202 203 .globl __realmode_interrupt 204__realmode_interrupt: 205 /* save all registers to the stack */ 206 pusha 207 pushf 208 209 /* save the stack pointer */ 210 movl %esp, __stack 211 movl %esp, %ebp 212 213 /* This function is called with regparm=0 and we have to 214 * skip the 36 byte from pushf+pusha. Hence start at 40. 215 */ 216 217 /* prepare interrupt calling code */ 218 movl 40(%ebp), %eax 219 movb %al, __intXX_instr + 1 /* intno */ 220 221 /* initial register values */ 222 movl 44(%ebp), %eax 223 movl %eax, __registers + 0 /* eax */ 224 movl 48(%ebp), %eax 225 movl %eax, __registers + 4 /* ebx */ 226 movl 52(%ebp), %eax 227 movl %eax, __registers + 8 /* ecx */ 228 movl 56(%ebp), %eax 229 movl %eax, __registers + 12 /* edx */ 230 movl 60(%ebp), %eax 231 movl %eax, __registers + 16 /* esi */ 232 movl 64(%ebp), %eax 233 movl %eax, __registers + 20 /* edi */ 234 235 /* This configures CS properly for real mode. */ 236 ljmp $RAM_CODE16_SEG, $RELOCATED(1f) 2371: 238 .code16 /* 16 bit code from here on... */ 239 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. 243 */ 244 mov $RAM_DATA16_SEG, %ax 245 mov %ax, %ds 246 mov %ax, %es 247 mov %ax, %fs 248 mov %ax, %gs 249 mov %ax, %ss 250 251 /* Turn off protected mode */ 252 movl %cr0, %eax 253 andl $~PE, %eax 254 movl %eax, %cr0 255 256 /* Now really going into real mode */ 257 ljmpl $0, $RELOCATED(1f) 2581: 259 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. 263 */ 264 /* setup a stack */ 265 mov $0x0, %ax 266 mov %ax, %ss 267 movl $0x1000, %eax 268 movl %eax, %esp 269 270 /* Load 16-bit intXX IDT */ 271 xor %ax, %ax 272 mov %ax, %ds 273 lidt __realmode_idt 274 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 282 283 /* Set all segments to 0x0000 */ 284 push %ax 285 xor %ax, %ax 286 mov %ax, %ds 287 mov %ax, %es 288 mov %ax, %fs 289 mov %ax, %gs 290 pop %ax 291 292__intXX_instr = RELOCATED(.) 293 .byte 0xcd, 0x00 /* This becomes intXX */ 294 295 /* 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. 299 */ 300 mov %eax, __realmode_ret 301 302 /* Ok, the job is done, now go back to protected mode coreboot */ 303 movl %cr0, %eax 304 orl $PE, %eax 305 movl %eax, %cr0 306 307 /* Now that we are in protected mode jump to a 32-bit code segment. */ 308 ljmpl $RAM_CODE_SEG, $RELOCATED(1f) 3091: 310 .code32 311 mov $RAM_DATA_SEG, %ax 312 mov %ax, %ds 313 mov %ax, %es 314 mov %ax, %fs 315 mov %ax, %gs 316 mov %ax, %ss 317 318 /* restore coreboot's 32-bit IDT */ 319 lidt idtarg 320 321 /* restore stack pointer, eflags and register values and exit */ 322 movl __stack, %esp 323 popf 324 popa 325 /* return AX from OPROM call */ 326 mov __realmode_ret, %eax 327 ret 328 329/* This is the 16-bit interrupt entry point called by the IDT stub code. 330 * 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 333 * for its caller. 334 */ 335 .code16 336__interrupt_handler_16bit = RELOCATED(.) 337 push %ds 338 push %es 339 push %fs 340 push %gs 341 342 /* Clear DF to not break ABI assumptions */ 343 cld 344 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. 347 */ 348 andl $0xff, %eax 349 pushl %eax /* ... and make it the first parameter */ 350 351 /* Switch to protected mode */ 352 movl %cr0, %eax 353 orl $PE, %eax 354 movl %eax, %cr0 355 356 /* ... and jump to a 32 bit code segment. */ 357 ljmpl $RAM_CODE_SEG, $RELOCATED(1f) 3581: 359 .code32 360 mov $RAM_DATA_SEG, %ax 361 mov %ax, %ds 362 mov %ax, %es 363 mov %ax, %fs 364 mov %ax, %gs 365 mov %ax, %ss 366 367 lidt idtarg 368 369 /* Call the C interrupt handler */ 370 movl $interrupt_handler, %eax 371 call *%eax 372 373 /* Now return to real mode ... */ 374 ljmp $RAM_CODE16_SEG, $RELOCATED(1f) 3751: 376 .code16 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. 380 */ 381 mov $RAM_DATA16_SEG, %ax 382 mov %ax, %ds 383 mov %ax, %es 384 mov %ax, %fs 385 mov %ax, %gs 386 mov %ax, %ss 387 388 /* Disable Protected Mode */ 389 movl %cr0, %eax 390 andl $~PE, %eax 391 movl %eax, %cr0 392 393 /* Now really going into real mode */ 394 ljmp $0, $RELOCATED(1f) 3951: 396 /* Restore real-mode stack segment */ 397 mov $0x0, %ax 398 mov %ax, %ss 399 400 /* Restore 16 bit IDT */ 401 xor %ax, %ax 402 mov %ax, %ds 403 lidt __realmode_idt 404 405 /* Restore all registers, including those 406 * manipulated by the C handler 407 */ 408 popl %eax 409 pop %gs 410 pop %fs 411 pop %es 412 pop %ds 413 popal 414 iret 415 416 .globl __realmode_code_size 417__realmode_code_size: 418 .long . - __realmode_code 419 420 .code32 421