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