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