xref: /rk3399_rockchip-uboot/arch/x86/lib/bios_asm.S (revision f448c5d3200372fa73f340144d013fdecf4e2f1f)
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