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