xref: /OK3568_Linux_fs/u-boot/arch/x86/lib/bios_asm.S (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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