1/* 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 249 /* Save real mode SS */ 250 movw %ss, %cs:__realmode_ss 251 252 /* 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 264 /* 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 273 /* Call the C interrupt handler */ 274 movl $interrupt_handler, %eax 275 call *%eax 276 277 /* Restore real mode ESP based on saved SS */ 278 movzwl %cs:__realmode_ss, %ecx 279 shll $4, %ecx 280 subl %ecx, %esp 281 282 enter_real_mode 283 284 /* Restore real mode SS */ 285 movw %cs:__realmode_ss, %ss 286 287 /* 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 299__realmode_ss = PTR_TO_REAL_MODE(.) 300 .word 0 301 302 .globl asm_realmode_code_size 303asm_realmode_code_size: 304 .long . - asm_realmode_code 305