xref: /openbmc/u-boot/arch/x86/lib/bios_asm.S (revision 83d290c56fab2d38cd1ab4c4cc7099559c1d5046)
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