xref: /titanic_50/usr/src/uts/i86pc/ml/bios_call_src.s (revision d2ec54f7875f7e05edd56195adbeb593c947763f)
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29#if defined(__lint)
30
31int silence_lint = 0;
32
33#else
34
35#include <sys/segments.h>
36#include <sys/controlregs.h>
37
38/*
39 * Do a call into BIOS.  This goes down to 16 bit real mode and back again.
40 */
41
42/*
43 * instruction prefix to change operand size in instruction
44 */
45#define DATASZ	.byte 0x66;
46
47#if defined(__amd64)
48#define	MOVCR(x, y)	movq  x,%rax; movq  %rax, y
49#define LOAD_XAX(sym)	leaq	sym, %rax
50#elif defined(__i386)
51#define	MOVCR(x, y)	movl  x,%eax; movl  %eax, y
52#define LOAD_XAX(sym)	leal	sym, %eax
53#endif
54
55	.globl	_start
56_start:
57
58#if defined(__i386)
59
60	/*
61	 * Save caller registers
62	 */
63	movl	%ebp, save_ebp
64	movl	%esp, save_esp
65	movl	%ebx, save_ebx
66	movl	%esi, save_esi
67	movl	%edi, save_edi
68
69	/* get registers argument into esi */
70	movl	8(%esp), %esi
71
72	/* put interrupt number in %bl */
73	movl	4(%esp), %ebx
74
75	/* Switch to a low memory stack */
76	movl	$_start, %esp
77
78	/* allocate space for args on stack */
79	subl	$18, %esp
80	movl	%esp, %edi
81
82#elif defined(__amd64)
83
84	/*
85	 * Save caller registers
86	 */
87	movq	%rbp, save_rbp
88	movq	%rsp, save_rsp
89	movq	%rbx, save_rbx
90	movq	%rsi, save_rsi
91	movq	%r12, save_r12
92	movq	%r13, save_r13
93	movq	%r14, save_r14
94	movq	%r15, save_r15
95
96	/* Switch to a low memory stack */
97	movq	$_start, %rsp
98
99	/* put interrupt number in %bl */
100	movq	%rdi, %rbx
101
102	/* allocate space for args on stack */
103	subq	$18, %rsp
104	movq	%rsp, %rdi
105
106#endif
107
108	/* copy args from high memory to stack in low memory */
109	cld
110	movl	$18, %ecx
111	rep
112	movsb
113
114	/*
115	 * Save system registers
116	 */
117	sidt	save_idt
118	sgdt	save_gdt
119	str	save_tr
120	movw	%cs, save_cs
121	movw	%ds, save_ds
122	movw	%ss, save_ss
123	movw	%es, save_es
124	movw	%fs, save_fs
125	movw	%gs, save_gs
126	MOVCR(	%cr4, save_cr4)
127	MOVCR(	%cr3, save_cr3)
128	MOVCR(	%cr0, save_cr0)
129
130#if defined(__amd64)
131	/*
132	 * save/clear the extension parts of the fs/gs base registers and cr8
133	 */
134	movl	$MSR_AMD_FSBASE, %ecx
135	rdmsr
136	movl	%eax, save_fsbase
137	movl	%edx, save_fsbase + 4
138	xorl	%eax, %eax
139	xorl	%edx, %edx
140	wrmsr
141
142	movl	$MSR_AMD_GSBASE, %ecx
143	rdmsr
144	movl	%eax, save_gsbase
145	movl	%edx, save_gsbase + 4
146	xorl	%eax, %eax
147	xorl	%edx, %edx
148	wrmsr
149
150	movl	$MSR_AMD_KGSBASE, %ecx
151	rdmsr
152	movl	%eax, save_kgsbase
153	movl	%edx, save_kgsbase + 4
154	xorl	%eax, %eax
155	xorl	%edx, %edx
156	wrmsr
157
158	movq	%cr8, %rax
159	movq	%rax, save_cr8
160#endif
161
162	/*
163	 * set offsets in 16 bit ljmp instructions below
164	 */
165	LOAD_XAX(enter_real)
166	movw	%ax, enter_real_ljmp
167
168	LOAD_XAX(enter_protected)
169	movw	%ax, enter_protected_ljmp
170
171	LOAD_XAX(gdt_info)
172	movw	%ax, gdt_info_load
173
174	/*
175	 * insert BIOS interrupt number into later instruction
176	 */
177	movb    %bl, int_instr+1
178	jmp     1f
1791:
180
181	/*
182	 * zero out all the registers to make sure they're 16 bit clean
183	 */
184#if defined(__amd64)
185	xorq	%r8, %r8
186	xorq	%r9, %r9
187	xorq	%r10, %r10
188	xorq	%r11, %r11
189	xorq	%r12, %r12
190	xorq	%r13, %r13
191	xorq	%r14, %r14
192	xorq	%r15, %r15
193#endif
194	xorl	%eax, %eax
195	xorl	%ebx, %ebx
196	xorl	%ecx, %ecx
197	xorl	%edx, %edx
198	xorl	%ebp, %ebp
199	xorl	%esi, %esi
200	xorl	%edi, %edi
201
202	/*
203	 * Load our own GDT/IDT
204	 */
205	lgdt	gdt_info
206	lidt	idt_info
207
208#if defined(__amd64)
209	/*
210	 * Shut down 64 bit mode. First get into compatiblity mode.
211	 */
212	movq	%rsp, %rax
213	pushq	$B32DATA_SEL
214	pushq	%rax
215	pushf
216	pushq	$B32CODE_SEL
217	pushq	$1f
218	iretq
2191:
220	.code32
221
222	/*
223	 * disable long mode by:
224	 * - shutting down paging (bit 31 of cr0)
225	 * - flushing the TLB
226	 * - disabling LME (long made enable) in EFER (extended feature reg)
227	 */
228	movl	%cr0, %eax
229	btcl	$31, %eax		/* disable paging */
230	movl	%eax, %cr0
231	ljmp	$B32CODE_SEL, $1f
2321:
233
234	xorl	%eax, %eax
235	movl	%eax, %cr3		/* flushes TLB */
236
237	movl	$MSR_AMD_EFER, %ecx	/* Extended Feature Enable */
238	rdmsr
239	btcl	$8, %eax		/* bit 8 Long Mode Enable bit */
240	wrmsr
241#endif
242
243	/*
244	 * ok.. now enter 16 bit mode, so we can shut down protected mode
245	 *
246	 * We'll have to act like we're still in a 32 bit section.
247	 * So the code from this point has DATASZ in front of it to get 32 bit
248	 * operands. If DATASZ is missing the operands will be 16 bit.
249	 *
250	 * Now shut down paging and protected (ie. segmentation) modes.
251	 */
252	ljmp	$B16CODE_SEL, $enter_16_bit
253enter_16_bit:
254
255	/*
256	 * Make sure hidden parts of segment registers are 16 bit clean
257	 */
258	DATASZ	movl	$B16DATA_SEL, %eax
259		movw    %ax, %ss
260		movw    %ax, %ds
261		movw    %ax, %es
262		movw    %ax, %fs
263		movw    %ax, %gs
264
265
266	DATASZ	movl	$0x0, %eax	/* put us in real mode */
267	DATASZ	movl	%eax, %cr0
268	.byte	0xea			/* ljmp */
269enter_real_ljmp:
270	.value	0			/* addr (16 bit) */
271	.value	0x0			/* value for %cs */
272enter_real:
273
274	/*
275	 * zero out the remaining segment registers
276	 */
277	DATASZ	xorl	%eax, %eax
278		movw    %ax, %ss
279		movw    %ax, %ds
280		movw    %ax, %es
281		movw    %ax, %fs
282		movw    %ax, %gs
283
284	/*
285	 * load the arguments to the BIOS call from the stack
286	 */
287	popl	%eax	/* really executes a 16 bit pop */
288	popl	%ebx
289	popl	%ecx
290	popl	%edx
291	popl	%esi
292	popl	%edi
293	popl	%ebp
294	pop	%es
295	pop	%ds
296
297	/*
298	 * do the actual BIOS call
299	 */
300	sti
301int_instr:
302	int	$0x10		/* this int number is overwritten */
303	cli			/* ensure interrupts remain disabled */
304
305	/*
306	 * save results of the BIOS call
307	 */
308	pushf
309	push	%ds
310	push	%es
311	pushl	%ebp		/* still executes as 16 bit */
312	pushl	%edi
313	pushl	%esi
314	pushl	%edx
315	pushl	%ecx
316	pushl	%ebx
317	pushl	%eax
318
319	/*
320	 * Restore protected mode and 32 bit execution
321	 */
322	push	$0			/* make sure %ds is zero before lgdt */
323	pop	%ds
324	.byte	0x0f, 0x01, 0x16	/* lgdt */
325gdt_info_load:
326	.value	0	/* temp GDT in currently addressible mem */
327
328	DATASZ	movl	$0x1, %eax
329	DATASZ	movl	%eax, %cr0
330
331	.byte	0xea			/* ljmp */
332enter_protected_ljmp:
333	.value	0			/* addr (still in 16 bit) */
334	.value	B32CODE_SEL		/* %cs value */
335enter_protected:
336
337	/*
338	 * We are now back in a 32 bit code section, fix data/stack segments
339	 */
340	.code32
341	movw	$B32DATA_SEL, %ax
342	movw	%ax, %ds
343	movw	%ax, %ss
344
345	/*
346	 * Re-enable paging. Note we only use 32 bit mov's to restore these
347	 * control registers. That's OK as the upper 32 bits are always zero.
348	 */
349	movl	save_cr4, %eax
350	movl	%eax, %cr4
351	movl	save_cr3, %eax
352	movl	%eax, %cr3
353
354#if defined(__amd64)
355	/*
356	 * re-enable long mode
357	 */
358	movl	$MSR_AMD_EFER, %ecx
359	rdmsr
360	btsl	$8, %eax
361	wrmsr
362#endif
363
364	movl	save_cr0, %eax
365	movl	%eax, %cr0
366	jmp	enter_paging
367enter_paging:
368
369
370#if defined(__amd64)
371	/*
372	 * transition back to 64 bit mode
373	 */
374	pushl	$B64CODE_SEL
375	pushl	$longmode
376	lret
377longmode:
378	.code64
379#endif
380	/*
381	 * restore caller frame pointer and segment registers
382	 */
383	lgdt	save_gdt
384	lidt	save_idt
385
386	/*
387	 * Before loading the task register we need to reset the busy bit
388	 * in its corresponding GDT selector. The busy bit is the 2nd bit in
389	 * the 5th byte of the selector.
390	 */
391#if defined(__i386)
392	movzwl	save_tr, %eax
393	addl	save_gdt+2, %eax
394	btcl	$1, 5(%eax)
395#elif defined(__amd64)
396	movzwq	save_tr, %rax
397	addq	save_gdt+2, %rax
398	btcl	$1, 5(%rax)
399#endif
400	ltr	save_tr
401	movw	save_ds, %ds
402	movw	save_ss, %ss
403	movw	save_es, %es
404	movw	save_fs, %fs
405	movw	save_gs, %gs
406
407#if defined(__i386)
408	pushl	save_cs
409	pushl	$.newcs
410	lret
411#elif defined(__amd64)
412	pushq	save_cs
413	pushq	$.newcs
414	lretq
415#endif
416.newcs:
417
418#if defined(__amd64)
419	/*
420	 * restore the hidden kernel segment base register values
421	 */
422	movl	save_fsbase, %eax
423	movl	save_fsbase + 4, %edx
424	movl	$MSR_AMD_FSBASE, %ecx
425	wrmsr
426
427	movl	save_gsbase, %eax
428	movl	save_gsbase + 4, %edx
429	movl	$MSR_AMD_GSBASE, %ecx
430	wrmsr
431
432	movl	save_kgsbase, %eax
433	movl	save_kgsbase + 4, %edx
434	movl	$MSR_AMD_KGSBASE, %ecx
435	wrmsr
436
437	movq	save_cr8, %rax
438	cmpq	$0, %rax
439	je	1f
440	movq	%rax, %cr8
4411:
442#endif
443
444	/*
445	 * copy results to caller's location, then restore remaining registers
446	 */
447#if defined(__i386)
448	movl    save_esp, %edi
449	movl	8(%edi), %edi
450	movl	%esp, %esi
451	movl	$18, %ecx
452	rep
453	movsb
454	movw	18(%esp), %ax
455	andl	$0xffff, %eax
456	movl    save_ebx, %ebx
457	movl    save_esi, %esi
458	movl    save_edi, %edi
459	movl    save_esp, %esp
460	movl    save_ebp, %ebp
461	movl    save_esp, %esp
462	ret
463
464#elif defined(__amd64)
465	movq    save_rsi, %rdi
466	movq	%rsp, %rsi
467	movq	$18, %rcx
468	rep
469	movsb
470	movw	18(%rsp), %ax
471	andq	$0xffff, %rax
472	movq    save_r12, %r12
473	movq    save_r13, %r13
474	movq    save_r14, %r14
475	movq    save_r15, %r15
476	movq    save_rbx, %rbx
477	movq    save_rbp, %rbp
478	movq    save_rsp, %rsp
479	ret
480
481#endif
482
483
484/*
485 * Caller's registers to restore
486 */
487	.align 4
488save_esi:
489	.long	0
490save_edi:
491	.long	0
492save_ebx:
493	.long	0
494save_ebp:
495	.long	0
496save_esp:
497	.long	0
498
499	.align 8
500#if defined(__amd64)
501save_rsi:
502	.quad	0
503save_rbx:
504	.quad	0
505save_rbp:
506	.quad	0
507save_rsp:
508	.quad	0
509save_r12:
510	.quad	0
511save_r13:
512	.quad	0
513save_r14:
514	.quad	0
515save_r15:
516	.quad	0
517save_kgsbase:
518	.quad	0
519save_gsbase:
520	.quad	0
521save_fsbase:
522	.quad	0
523save_cr8:
524	.quad	0
525#endif	/* __amd64 */
526
527save_idt:
528	.quad	0
529	.quad	0
530
531save_gdt:
532	.quad	0
533	.quad	0
534
535save_cr0:
536	.quad	0
537save_cr3:
538	.quad	0
539save_cr4:
540	.quad	0
541save_cs:
542	.quad	0
543save_ss:
544	.value	0
545save_ds:
546	.value	0
547save_es:
548	.value	0
549save_fs:
550	.value	0
551save_gs:
552	.value	0
553save_tr:
554	.value	0
555
556idt_info:
557	.value 0x3ff
558	.quad 0
559
560
561/*
562 * We need to trampoline thru a gdt we have in low memory.
563 */
564#include "../boot/boot_gdt.s"
565#endif /* __lint */
566