1*83d290c5STom Rini/* SPDX-License-Identifier: GPL-2.0 */ 20ca2426bSSimon Glass/* 30ca2426bSSimon Glass * From coreboot x86_asm.S, cleaned up substantially 40ca2426bSSimon Glass * 50ca2426bSSimon Glass * Copyright (C) 2009-2010 coresystems GmbH 60ca2426bSSimon Glass */ 70ca2426bSSimon Glass 80ca2426bSSimon Glass#include <asm/processor.h> 90ca2426bSSimon Glass#include <asm/processor-flags.h> 100ca2426bSSimon Glass#include "bios.h" 110ca2426bSSimon Glass 120ca2426bSSimon Glass#define SEG(segment) $segment * X86_GDT_ENTRY_SIZE 130ca2426bSSimon Glass 140ca2426bSSimon Glass/* 150ca2426bSSimon Glass * This is the interrupt handler stub code. It gets copied to the IDT and 160ca2426bSSimon Glass * to some fixed addresses in the F segment. Before the code can used, 170ca2426bSSimon Glass * it gets patched up by the C function copying it: byte 3 (the $0 in 180ca2426bSSimon Glass * movb $0, %al) is overwritten with the interrupt numbers. 190ca2426bSSimon Glass */ 200ca2426bSSimon Glass 210ca2426bSSimon Glass .code16 220ca2426bSSimon Glass .globl __idt_handler 230ca2426bSSimon Glass__idt_handler: 240ca2426bSSimon Glass pushal 250ca2426bSSimon Glass movb $0, %al /* This instruction gets modified */ 260ca2426bSSimon Glass ljmp $0, $__interrupt_handler_16bit 270ca2426bSSimon Glass .globl __idt_handler_size 280ca2426bSSimon Glass__idt_handler_size: 290ca2426bSSimon Glass .long . - __idt_handler 300ca2426bSSimon Glass 310ca2426bSSimon Glass.macro setup_registers 320ca2426bSSimon Glass /* initial register values */ 330ca2426bSSimon Glass movl 44(%ebp), %eax 340ca2426bSSimon Glass movl %eax, __registers + 0 /* eax */ 350ca2426bSSimon Glass movl 48(%ebp), %eax 360ca2426bSSimon Glass movl %eax, __registers + 4 /* ebx */ 370ca2426bSSimon Glass movl 52(%ebp), %eax 380ca2426bSSimon Glass movl %eax, __registers + 8 /* ecx */ 390ca2426bSSimon Glass movl 56(%ebp), %eax 400ca2426bSSimon Glass movl %eax, __registers + 12 /* edx */ 410ca2426bSSimon Glass movl 60(%ebp), %eax 420ca2426bSSimon Glass movl %eax, __registers + 16 /* esi */ 430ca2426bSSimon Glass movl 64(%ebp), %eax 440ca2426bSSimon Glass movl %eax, __registers + 20 /* edi */ 450ca2426bSSimon Glass.endm 460ca2426bSSimon Glass 470ca2426bSSimon Glass.macro enter_real_mode 480ca2426bSSimon Glass /* Activate the right segment descriptor real mode. */ 490ca2426bSSimon Glass ljmp SEG(X86_GDT_ENTRY_16BIT_CS), $PTR_TO_REAL_MODE(1f) 500ca2426bSSimon Glass1: 510ca2426bSSimon Glass.code16 520ca2426bSSimon Glass /* 530ca2426bSSimon Glass * Load the segment registers with properly configured segment 540ca2426bSSimon Glass * descriptors. They will retain these configurations (limits, 550ca2426bSSimon Glass * writability, etc.) once protected mode is turned off. 560ca2426bSSimon Glass */ 570ca2426bSSimon Glass mov SEG(X86_GDT_ENTRY_16BIT_DS), %ax 580ca2426bSSimon Glass mov %ax, %ds 590ca2426bSSimon Glass mov %ax, %es 600ca2426bSSimon Glass mov %ax, %fs 610ca2426bSSimon Glass mov %ax, %gs 620ca2426bSSimon Glass mov %ax, %ss 630ca2426bSSimon Glass 640ca2426bSSimon Glass /* Turn off protection */ 650ca2426bSSimon Glass movl %cr0, %eax 660ca2426bSSimon Glass andl $~X86_CR0_PE, %eax 670ca2426bSSimon Glass movl %eax, %cr0 680ca2426bSSimon Glass 690ca2426bSSimon Glass /* Now really going into real mode */ 700ca2426bSSimon Glass ljmp $0, $PTR_TO_REAL_MODE(1f) 710ca2426bSSimon Glass1: 720ca2426bSSimon Glass /* 730ca2426bSSimon Glass * Set up a stack: Put the stack at the end of page zero. That way 740ca2426bSSimon Glass * we can easily share it between real and protected, since the 750ca2426bSSimon Glass * 16-bit ESP at segment 0 will work for any case. 760ca2426bSSimon Glass */ 770ca2426bSSimon Glass mov $0x0, %ax 780ca2426bSSimon Glass mov %ax, %ss 790ca2426bSSimon Glass 800ca2426bSSimon Glass /* Load 16 bit IDT */ 810ca2426bSSimon Glass xor %ax, %ax 820ca2426bSSimon Glass mov %ax, %ds 830ca2426bSSimon Glass lidt __realmode_idt 840ca2426bSSimon Glass 850ca2426bSSimon Glass.endm 860ca2426bSSimon Glass 870ca2426bSSimon Glass.macro prepare_for_irom 880ca2426bSSimon Glass movl $0x1000, %eax 890ca2426bSSimon Glass movl %eax, %esp 900ca2426bSSimon Glass 910ca2426bSSimon Glass /* Initialise registers for option rom lcall */ 920ca2426bSSimon Glass movl __registers + 0, %eax 930ca2426bSSimon Glass movl __registers + 4, %ebx 940ca2426bSSimon Glass movl __registers + 8, %ecx 950ca2426bSSimon Glass movl __registers + 12, %edx 960ca2426bSSimon Glass movl __registers + 16, %esi 970ca2426bSSimon Glass movl __registers + 20, %edi 980ca2426bSSimon Glass 990ca2426bSSimon Glass /* Set all segments to 0x0000, ds to 0x0040 */ 1000ca2426bSSimon Glass push %ax 1010ca2426bSSimon Glass xor %ax, %ax 1020ca2426bSSimon Glass mov %ax, %es 1030ca2426bSSimon Glass mov %ax, %fs 1040ca2426bSSimon Glass mov %ax, %gs 1050ca2426bSSimon Glass mov SEG(X86_GDT_ENTRY_16BIT_FLAT_DS), %ax 1060ca2426bSSimon Glass mov %ax, %ds 1070ca2426bSSimon Glass pop %ax 1080ca2426bSSimon Glass 1090ca2426bSSimon Glass.endm 1100ca2426bSSimon Glass 1110ca2426bSSimon Glass.macro enter_protected_mode 1120ca2426bSSimon Glass /* Go back to protected mode */ 1130ca2426bSSimon Glass movl %cr0, %eax 1140ca2426bSSimon Glass orl $X86_CR0_PE, %eax 1150ca2426bSSimon Glass movl %eax, %cr0 1160ca2426bSSimon Glass 1170ca2426bSSimon Glass /* Now that we are in protected mode jump to a 32 bit code segment */ 1180ca2426bSSimon Glass data32 ljmp SEG(X86_GDT_ENTRY_32BIT_CS), $PTR_TO_REAL_MODE(1f) 1190ca2426bSSimon Glass1: 1200ca2426bSSimon Glass .code32 1210ca2426bSSimon Glass mov SEG(X86_GDT_ENTRY_32BIT_DS), %ax 1220ca2426bSSimon Glass mov %ax, %ds 1230ca2426bSSimon Glass mov %ax, %es 1240ca2426bSSimon Glass mov %ax, %gs 1250ca2426bSSimon Glass mov %ax, %ss 1260ca2426bSSimon Glass mov SEG(X86_GDT_ENTRY_32BIT_FS), %ax 1270ca2426bSSimon Glass mov %ax, %fs 1280ca2426bSSimon Glass 1290ca2426bSSimon Glass /* restore proper idt */ 1300ca2426bSSimon Glass lidt idt_ptr 1310ca2426bSSimon Glass.endm 1320ca2426bSSimon Glass 1330ca2426bSSimon Glass/* 1340ca2426bSSimon Glass * In order to be independent of U-Boot's position in RAM we relocate a part 1350ca2426bSSimon Glass * of the code to the first megabyte of RAM, so the CPU can use it in 1360ca2426bSSimon Glass * real-mode. This code lives at asm_realmode_code. 1370ca2426bSSimon Glass */ 1380ca2426bSSimon Glass .globl asm_realmode_code 1390ca2426bSSimon Glassasm_realmode_code: 1400ca2426bSSimon Glass 1410ca2426bSSimon Glass/* Realmode IDT pointer structure. */ 1420ca2426bSSimon Glass__realmode_idt = PTR_TO_REAL_MODE(.) 1430ca2426bSSimon Glass .word 1023 /* 16 bit limit */ 1440ca2426bSSimon Glass .long 0 /* 24 bit base */ 1450ca2426bSSimon Glass .word 0 1460ca2426bSSimon Glass 1470ca2426bSSimon Glass/* Preserve old stack */ 1480ca2426bSSimon Glass__stack = PTR_TO_REAL_MODE(.) 1490ca2426bSSimon Glass .long 0 1500ca2426bSSimon Glass 1510ca2426bSSimon Glass/* Register store for realmode_call and realmode_interrupt */ 1520ca2426bSSimon Glass__registers = PTR_TO_REAL_MODE(.) 1530ca2426bSSimon Glass .long 0 /* 0 - EAX */ 1540ca2426bSSimon Glass .long 0 /* 4 - EBX */ 1550ca2426bSSimon Glass .long 0 /* 8 - ECX */ 1560ca2426bSSimon Glass .long 0 /* 12 - EDX */ 1570ca2426bSSimon Glass .long 0 /* 16 - ESI */ 1580ca2426bSSimon Glass .long 0 /* 20 - EDI */ 1590ca2426bSSimon Glass 1600ca2426bSSimon Glass/* 256 byte buffer, used by int10 */ 1610ca2426bSSimon Glass .globl asm_realmode_buffer 1620ca2426bSSimon Glassasm_realmode_buffer: 1630ca2426bSSimon Glass .skip 256 1640ca2426bSSimon Glass 1650ca2426bSSimon Glass .code32 1660ca2426bSSimon Glass .globl asm_realmode_call 1670ca2426bSSimon Glassasm_realmode_call: 1680ca2426bSSimon Glass /* save all registers to the stack */ 1690ca2426bSSimon Glass pusha 1700ca2426bSSimon Glass pushf 1710ca2426bSSimon Glass movl %esp, __stack 1720ca2426bSSimon Glass movl %esp, %ebp 1730ca2426bSSimon Glass 1740ca2426bSSimon Glass /* 1750ca2426bSSimon Glass * This function is called with regparm=0 and we have to skip the 1760ca2426bSSimon Glass * 36 bytes from pushf+pusha. Hence start at 40. 1770ca2426bSSimon Glass * Set up our call instruction. 1780ca2426bSSimon Glass */ 1790ca2426bSSimon Glass movl 40(%ebp), %eax 1800ca2426bSSimon Glass mov %ax, __lcall_instr + 1 1810ca2426bSSimon Glass andl $0xffff0000, %eax 1820ca2426bSSimon Glass shrl $4, %eax 1830ca2426bSSimon Glass mov %ax, __lcall_instr + 3 1840ca2426bSSimon Glass 1850ca2426bSSimon Glass wbinvd 1860ca2426bSSimon Glass 1870ca2426bSSimon Glass setup_registers 1880ca2426bSSimon Glass enter_real_mode 1890ca2426bSSimon Glass prepare_for_irom 1900ca2426bSSimon Glass 1910ca2426bSSimon Glass__lcall_instr = PTR_TO_REAL_MODE(.) 1920ca2426bSSimon Glass .byte 0x9a 1930ca2426bSSimon Glass .word 0x0000, 0x0000 1940ca2426bSSimon Glass 1950ca2426bSSimon Glass enter_protected_mode 1960ca2426bSSimon Glass 1970ca2426bSSimon Glass /* restore stack pointer, eflags and register values and exit */ 1980ca2426bSSimon Glass movl __stack, %esp 1990ca2426bSSimon Glass popf 2000ca2426bSSimon Glass popa 2010ca2426bSSimon Glass ret 2020ca2426bSSimon Glass 2030ca2426bSSimon Glass .globl __realmode_interrupt 2040ca2426bSSimon Glass__realmode_interrupt: 2050ca2426bSSimon Glass /* save all registers to the stack and store the stack pointer */ 2060ca2426bSSimon Glass pusha 2070ca2426bSSimon Glass pushf 2080ca2426bSSimon Glass movl %esp, __stack 2090ca2426bSSimon Glass movl %esp, %ebp 2100ca2426bSSimon Glass 2110ca2426bSSimon Glass /* 2120ca2426bSSimon Glass * This function is called with regparm=0 and we have to skip the 2130ca2426bSSimon Glass * 36 bytes from pushf+pusha. Hence start at 40. 2140ca2426bSSimon Glass * Prepare interrupt calling code. 2150ca2426bSSimon Glass */ 2160ca2426bSSimon Glass movl 40(%ebp), %eax 2170ca2426bSSimon Glass movb %al, __intXX_instr + 1 /* intno */ 2180ca2426bSSimon Glass 2190ca2426bSSimon Glass setup_registers 2200ca2426bSSimon Glass enter_real_mode 2210ca2426bSSimon Glass prepare_for_irom 2220ca2426bSSimon Glass 2230ca2426bSSimon Glass__intXX_instr = PTR_TO_REAL_MODE(.) 2240ca2426bSSimon Glass .byte 0xcd, 0x00 /* This becomes intXX */ 2250ca2426bSSimon Glass 2260ca2426bSSimon Glass enter_protected_mode 2270ca2426bSSimon Glass 2280ca2426bSSimon Glass /* restore stack pointer, eflags and register values and exit */ 2290ca2426bSSimon Glass movl __stack, %esp 2300ca2426bSSimon Glass popf 2310ca2426bSSimon Glass popa 2320ca2426bSSimon Glass ret 2330ca2426bSSimon Glass 2340ca2426bSSimon Glass/* 2350ca2426bSSimon Glass * This is the 16-bit interrupt entry point called by the IDT stub code. 2360ca2426bSSimon Glass * 2370ca2426bSSimon Glass * Before this code code is called, %eax is pushed to the stack, and the 2380ca2426bSSimon Glass * interrupt number is loaded into %al. On return this function cleans up 2390ca2426bSSimon Glass * for its caller. 2400ca2426bSSimon Glass */ 2410ca2426bSSimon Glass .code16 2420ca2426bSSimon Glass__interrupt_handler_16bit = PTR_TO_REAL_MODE(.) 2430ca2426bSSimon Glass push %ds 2440ca2426bSSimon Glass push %es 2450ca2426bSSimon Glass push %fs 2460ca2426bSSimon Glass push %gs 2470ca2426bSSimon Glass 2487b5c3498SJian Luo /* Save real mode SS */ 2497b5c3498SJian Luo movw %ss, %cs:__realmode_ss 2507b5c3498SJian Luo 2510ca2426bSSimon Glass /* Clear DF to not break ABI assumptions */ 2520ca2426bSSimon Glass cld 2530ca2426bSSimon Glass 2540ca2426bSSimon Glass /* 2550ca2426bSSimon Glass * Clean up the interrupt number. We could do this in the stub, but 2560ca2426bSSimon Glass * it would cost two more bytes per stub entry. 2570ca2426bSSimon Glass */ 2580ca2426bSSimon Glass andl $0xff, %eax 2590ca2426bSSimon Glass pushl %eax /* ... and make it the first parameter */ 2600ca2426bSSimon Glass 2610ca2426bSSimon Glass enter_protected_mode 2620ca2426bSSimon Glass 2637b5c3498SJian Luo /* 2647b5c3498SJian Luo * Now we are in protected mode. We need compute the right ESP based 2657b5c3498SJian Luo * on saved real mode SS otherwise interrupt_handler() won't get 2667b5c3498SJian Luo * correct parameters from the stack. 2677b5c3498SJian Luo */ 2687b5c3498SJian Luo movzwl %cs:__realmode_ss, %ecx 2697b5c3498SJian Luo shll $4, %ecx 2707b5c3498SJian Luo addl %ecx, %esp 2717b5c3498SJian Luo 2720ca2426bSSimon Glass /* Call the C interrupt handler */ 2730ca2426bSSimon Glass movl $interrupt_handler, %eax 2740ca2426bSSimon Glass call *%eax 2750ca2426bSSimon Glass 2767b5c3498SJian Luo /* Restore real mode ESP based on saved SS */ 2777b5c3498SJian Luo movzwl %cs:__realmode_ss, %ecx 2787b5c3498SJian Luo shll $4, %ecx 2797b5c3498SJian Luo subl %ecx, %esp 2807b5c3498SJian Luo 2810ca2426bSSimon Glass enter_real_mode 2820ca2426bSSimon Glass 2837b5c3498SJian Luo /* Restore real mode SS */ 2847b5c3498SJian Luo movw %cs:__realmode_ss, %ss 2857b5c3498SJian Luo 2860ca2426bSSimon Glass /* 2870ca2426bSSimon Glass * Restore all registers, including those manipulated by the C 2880ca2426bSSimon Glass * handler 2890ca2426bSSimon Glass */ 2900ca2426bSSimon Glass popl %eax 2910ca2426bSSimon Glass pop %gs 2920ca2426bSSimon Glass pop %fs 2930ca2426bSSimon Glass pop %es 2940ca2426bSSimon Glass pop %ds 2950ca2426bSSimon Glass popal 2960ca2426bSSimon Glass iret 2970ca2426bSSimon Glass 2987b5c3498SJian Luo__realmode_ss = PTR_TO_REAL_MODE(.) 2997b5c3498SJian Luo .word 0 3007b5c3498SJian Luo 3010ca2426bSSimon Glass .globl asm_realmode_code_size 3020ca2426bSSimon Glassasm_realmode_code_size: 3030ca2426bSSimon Glass .long . - asm_realmode_code 304