xref: /openbsd/sys/arch/amd64/stand/libsa/gidt.S (revision 09467b48)
1/*	$OpenBSD: gidt.S,v 1.12 2019/11/09 17:58:46 deraadt Exp $	*/
2
3/*
4 * Copyright (c) 1997 Michael Shalayeff
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 */
29
30	.file "gidt.S"
31
32#include <machine/asm.h>
33#define _LOCORE
34#include <machine/trap.h>
35#undef _LOCORE
36#include <assym.h>
37
38#include "gidt.h"
39
40#ifdef GIDT_DEBUG
41#define	gidt_debug0		; \
42	mov	$0xb8000, %eax	; \
43	mov	$0x47314730, (%eax)
44#define	gidt_debug1		; \
45	mov	$(0xb8000 - LINKADDR), %eax	; \
46	mov	$0x4f314f30, (%eax)
47#define	gidt_debug2		; \
48	mov	$0xb8004, %eax	; \
49	mov	$0x47334732, (%eax)
50#define	gidt_debug3		; \
51	mov	$0xb8004, %eax	; \
52	mov	$0x4f334f32, (%eax)
53#define gidt_debug4		; \
54	movl	$0xb8008, %eax	; \
55	movl	$0x47344733, (%eax)
56#else
57#define gidt_debug0 /* gidt_debug0 */
58#define gidt_debug1 /* gidt_debug1 */
59#define gidt_debug2 /* gidt_debug2 */
60#define gidt_debug3 /* gidt_debug3 */
61#define gidt_debug4 /* gidt_debug4 */
62#endif
63
64#define prot2real						\
65	gidt_debug0;						\
66								\
67	ljmp	$S16TEXT, $1f - LINKADDR;			\
681:								\
69	.code16;						\
70	movw	$S16DATA, %ax;					\
71	movw	%ax, %ds;					\
72	movw	%ax, %es;					\
73	gidt_debug1;						\
74								\
75	movl	%cr0, %eax;	/* disable pmmm */		\
76	andl 	$~CR0_PE, %eax;					\
77	movl	%eax, %cr0;					\
78								\
79	/* reload real cs:ip */					\
80	data32 ljmp	$(LINKADDR >> 4), $1f - LINKADDR;	\
811:								\
82	movw	%cs, %ax;	/* setup: %ds, %es, %ss = %cs */ \
83	movw	%ax, %ds;					\
84	movw	%ax, %es;					\
85	xorw	%ax, %ax;					\
86	movw	%ax, %ss;					\
87								\
88	gidt_debug2;						\
89								\
90	data32 addr32 lidt (Idtr_real - LINKADDR); /* load idtr for real mode */
91
92#define real2prot						\
93	gidt_debug3;						\
94								\
95	movw	$LINKADDR >> 4, %ax;				\
96	movw	%ax, %ds;					\
97	data32 addr32 lgdt (Gdtr - LINKADDR); 	/* load the gdtr */	\
98								\
99	movl	%cr0, %eax;	/* enable pmmm */		\
100	orl	$CR0_PE, %eax;					\
101	movl	%eax, %cr0;					\
102								\
103	data32 ljmp	$S32TEXT, $1f;   /* reload %cs,flush pipeline */\
1041:								\
105	.code32;						\
106	/* reload 32bit %ds, %ss, %es */			\
107	mov	$S32DATA, %eax;					\
108	mov	%ax, %ds;					\
109	mov	%ax, %ss;					\
110	mov	%ax, %es;					\
111								\
112	gidt_debug4;						\
113								\
114	/* load idtr for debugger and DOS/BIOS iface */		\
115	lidt	Idtr;
116
117
118	.globl	_C_LABEL(BIOS_regs)
119
120	.text
121	.code32
122	.globl	_ASM_LABEL(pmm_init)
123	.globl	_C_LABEL(_rtt)
124
125ENTRY(_rtt)
126#ifdef SOFTRAID
127	call	_C_LABEL(sr_clear_keys)
128#endif
129#ifdef GIDT_DEBUG
130	movl	$0xb8000, %ebx
131	movl	$0x4f514f51, (%ebx)
132#endif
133	movw	$0x1234, %ax
134	movw	%ax, 0x472	/* warm boot */
135
136	/* Try to use the KBD to reboot system */
137	movb	$0xfe, %al
138	outb	%al, $0x64
139
140	movl	$0x5000, %ecx
1411:	inb	$0x84, %al
142	loop	1b
143
144	movb	$0xfe, %al
145	outb	%al, $0x64
146
147#ifdef GIDT_DEBUG
148	movl	$0xb8000, %ebx
149	movl	$0x07310731, (%ebx)
150#endif
151
152	/* Try to cause a triple fault... */
153	lidt	Idtr_reset
154	xorl	%eax, %eax
155	divl	%eax, %eax
156
157	/* Again... */
158	int $0x8
159
160	/* Again... */
161	movl	$0, %esp	/* segment violation */
162	ret
163
164#define IPROC(n)	X##n
165#define IEMU(n)		IPROC(emu##n)
166
167create_idt_entry:
168	movw	%ax, (%ebx)
169	movw	$S32TEXT, 2(%ebx)
170	movw	$((0x80|SDT_SYS386TGT) << 8), 4(%ebx)
171	shr	$16, %eax
172	movw	%ax, 6(%ebx)
173	addl	$8, %ebx
174	ret
175
176	.align	8, 0x90
177pmm_init:
178
179#define idte(e)	\
180	movl $IPROC(e), %eax; call create_idt_entry
181#define idtb(b)	idte(emu##b)
182
183	/* Build interrupt descriptor table. */
184	/* Maskable interrupts (32-255) */
185	movl	$idt, %ebx
186	movl	$Idtr, %eax
187	movw	$(640 - 1), (%eax)
188	movl	%ebx, 2(%eax)
189
190	/* Internal (0-31) */
191	idte(de); idte(db); idte(nmi); idte(bp); idte(of); idte(br)
192	idte(ud); idte(nm); idte(df);  idte(fo); idte(ts); idte(np)
193	idte(ss); idte(gp); idte(pf);  idte(xx); idte(mf); idte(ac)
194	idte(xx)
195	idte(xx); idte(xx); idte(xx);  idte(xx); idte(xx); idte(xx)
196	idte(xx); idte(xx); idte(xx);  idte(xx); idte(xx); idte(xx)
197	idte(xx)
198	/* BIOS entry points (32-63) */
199	idtb(0);  idtb(1);  idtb(2);  idtb(3);  idtb(4);  idtb(5)
200	idtb(6);  idtb(7);  idtb(8);  idtb(9);  idtb(10); idtb(11)
201	idtb(12); idtb(13); idtb(14); idtb(15); idtb(16); idtb(17)
202	idtb(18); idtb(19); idtb(20); idtb(21); idtb(22); idtb(23)
203	idtb(24); idtb(25); idtb(26); idtb(27); idtb(28); idtb(29)
204	idtb(30); idtb(31); idtb(32); idtb(33); idtb(34); idtb(35)
205	idtb(36); idtb(37); idtb(38); idtb(39); idtb(40); idtb(41)
206	idtb(42); idtb(43); idtb(44); idtb(45); idtb(46); idtb(47)
207	/* DOS entry points (64-80) */
208
209#undef idtb
210#undef idte
211
212	/* load idtr for interrupts */
213	lidt	Idtr
214	ret
215
216	.bss
217	.align 8, 0x90
218idt:
219	/* IDT has 80 entries at 8 bytes each. */
220	.space  640
221
222	.globl	Idtr
223Idtr:	.word	0  // 640 - 1
224	.long	0  // idt
225	.word	0
226
227	.text
228	.align	8
229	.globl	Idtr_real
230Idtr_real:
231	.word	1023
232	.long	0
233	.word	0
234
235	.align	8
236Idtr_reset:
237	.long	0, 0
238
239	.align	8
240gdt:
241		/* 0x00 : null */
242	.space	8
243		/* 0x08 : flat code */
244	.word	0xFFFF			# lolimit
245	.word	0			# lobase
246	.byte	0			# midbase
247	.byte	SDT_MEMERAC | 0 | 0x80	# RXAC, dpl = 0, present
248	.byte	0xf | 0 | 0x40 | 0x80	# hilimit, xx, 32bit, 4k granularity
249	.byte	0			# hibase
250		/* 0x10 : flat data */
251	.word	0xFFFF			# lolimit
252	.word	0			# lobase
253	.byte	0			# midbase
254	.byte	SDT_MEMRWA | 0 | 0x80	# RWA, dpl = 0, present
255	.byte	0xf | 0 | 0x40 | 0x80	# hilimit, xx, 32bit, 4k granularity
256	.byte	0			# hibase
257		/* 0x18 : 16 bit code */
258	.word	0xFFFF			# lolimit
259	.word	(LINKADDR & 0xffff)	# lobase
260	.byte	(LINKADDR >> 16) & 0xff	# midbase
261	.byte	SDT_MEMERAC | 0 | 0x80	# RXAC, dpl = 0, present
262	.byte	0x0 | 0 | 0 | 0		# hilimit, xx, 16bit, byte granularity
263	.byte	(LINKADDR >> 20) & 0xff	# hibase
264		/* 0x20 : 16 bit data */
265	.word	0xFFFF			# lolimit
266	.word	(LINKADDR & 0xffff)	# lobase
267	.byte	(LINKADDR >> 16) & 0xff	# midbase
268	.byte	SDT_MEMRWA | 0 | 0x80	# RWA, dpl = 0, present
269	.byte	0x0 | 0 | 0 | 0		# hilimit, xx, 16bit, byte granularity
270	.byte	(LINKADDR >> 20) & 0xff	# hibase
271
272.globl Gdtr
273Gdtr:	.word	. - gdt - 1
274	.long	gdt
275	.word	0
276
277#define IENTRY(name,type) \
278IPROC(name): \
279	pushl	$type ; \
280	jmp	1f
281#define IENTRY_ERR(name,err,type) \
282IPROC(name): \
283	pushl	$err ; \
284	pushl	$type ; \
285	jmp	1f
286
287IPROC(xx):
288	pushl	$1
289	pushl	$T_RESERVED
290	jmp	1f
291
292IENTRY_ERR(de,0,T_DIVIDE)	/* #DE divide by zero */
293IENTRY_ERR(db,0,T_TRCTRAP)	/* #DB debug */
294IENTRY_ERR(nmi,0,T_NMI)		/* NMI */
295IENTRY_ERR(bp,0,T_BPTFLT)	/* #BP breakpoint */
296IENTRY_ERR(of,0,T_OFLOW)	/* #OF overflow */
297IENTRY_ERR(br,0,T_BOUND)	/* #BR BOUND range exceeded */
298IENTRY_ERR(ud,0,T_PRIVINFLT)	/* #UD invalid opcode */
299IENTRY_ERR(nm,0,T_DNA)		/* #NM device not available */
300IENTRY(df,T_DOUBLEFLT)		/* #DF double fault */
301IENTRY_ERR(fo,0,T_FPOPFLT)	/* #FO coprocessor segment overrun */
302IENTRY(ts,T_TSSFLT)		/* #TS invalid TSS */
303IENTRY(np,T_SEGNPFLT)		/* #NP segment not present */
304IENTRY(ss,T_STKFLT)		/* #SS stack fault */
305IENTRY(gp,T_PROTFLT)		/* #GP general protection */
306IENTRY(pf,T_PAGEFLT)		/* #PF page fault */
307IENTRY_ERR(mf,0,T_ARITHTRAP)	/* #MF floating point error */
308IENTRY(ac,T_ALIGNFLT)		/* #AC alignment check */
3091:
310	cli
311	hlt
312
313#define	IEMUENT(n)	IEMU(n): pushl $n; jmp 1f
314
315IEMUENT(0);  IEMUENT(1);  IEMUENT(2);  IEMUENT(3)
316IEMUENT(4);  IEMUENT(5);  IEMUENT(6);  IEMUENT(7)
317IEMUENT(8);  IEMUENT(9);  IEMUENT(10); IEMUENT(11)
318IEMUENT(12); IEMUENT(13); IEMUENT(14); IEMUENT(15)
319IEMUENT(16); IEMUENT(17); IEMUENT(18); IEMUENT(19)
320IEMUENT(20); IEMUENT(21); IEMUENT(22); IEMUENT(23)
321IEMUENT(24); IEMUENT(25); IEMUENT(26); IEMUENT(27)
322IEMUENT(28); IEMUENT(29); IEMUENT(30); IEMUENT(31)
3231:	jmp	EMUh	/* redirect for short jumps */
324IEMUENT(32); IEMUENT(33); IEMUENT(34); IEMUENT(35)
325IEMUENT(36); IEMUENT(37); IEMUENT(38); IEMUENT(39)
326IEMUENT(40); IEMUENT(41); IEMUENT(42); IEMUENT(43)
327IEMUENT(44); IEMUENT(45); IEMUENT(46); IEMUENT(47)
3281:	jmp	EMUh
329
330/*
331 * entry point for BIOS real-mode interface
332 * all the magic for real-prot mode switching is here
333 *
334 * Note: Once in real mode access to .data or .bss should be avoided since it
335 * may not be reachable within the current segment. The following code also
336 * assumes that .text is writeable.
337 *
338 * Call:	%eax, %ecx, %edx, %ebx, %ebp, %esi, %edi, %es, %ds
339 * Return:	%eax, %edx, %ecx, %eflags (as returned from BIOS)
340 *
341 */
342	.globl	EMUh
343	.align	8, 0x90
344EMUh:
345	/* save %eax */
346	mov	%eax, 5f
347	pop	%eax
348
349	pusha
350	push	%ds
351	push	%es
352	push	%fs
353	push	%gs
354
355	/* save BIOS int vector */
356	mov	%al, intno
357
358	/* Load BIOS registers prior to switching to real mode. */
359	movl	_C_LABEL(BIOS_regs)+BIOSR_ES, %eax
360	mov	%eax, 7f
361	movl	_C_LABEL(BIOS_regs)+BIOSR_DS, %eax
362	mov	%eax, 6f
363
364	prot2real
365
366	push	%ds
367
368	# data32 movl $Leax, %eax
369	.byte	0x66, 0xb8
3707:	.long	0x90909090
371	mov	%ax, %es
372
373	# data32 movl $Leax, %eax
374	.byte	0x66, 0xb8
3756:	.long	0x90909090
376	mov	%ax, %ds
377
378	# data32 movl $Leax, %eax
379	.byte	0x66, 0xb8
3805:	.long	0x90909090
381
382	;sti
383	int	$0
384intno	= . - 1
385	;cli
386
387	pop	%ds
388
389	/* Preserve BX and ES for protected mode. */
390	addr32 movl %eax, (2f - LINKADDR)
391	movl	%ebx, %eax
392	addr32 movl %eax, (4f - LINKADDR)
393	movl	%es, %eax
394	addr32 movl %eax, (3f - LINKADDR)
395	addr32 movl (2f - LINKADDR), %eax
396
397	movb	%ah, %bh
398	lahf
399	xchgb	%ah, %bh
400
401	/* Preserve AX for protected mode. */
402	addr32 movl %eax, (2f - LINKADDR)
403
404	real2prot
405
406	# movl $Leax, %eax
407	.byte	0xb8
4084:	.long	0x90909090
409	movl	%eax, _C_LABEL(BIOS_regs)+BIOSR_BX
410
411	# movl $Leax, %eax
412	.byte	0xb8
4133:	.long	0x90909090
414	movl	%eax, _C_LABEL(BIOS_regs)+BIOSR_ES
415
416	# movl $Leax, %eax
417	.byte	0xb8
4182:	.long	0x90909090
419
420	/* pass BIOS return values back to caller */
421	movl	%eax, 0xb*4(%esp)
422	movl	%ecx, 0xa*4(%esp)
423	movl	%edx, 0x9*4(%esp)
424	movb	%bh , 0xe*4(%esp)
425
426	/* save registers into save area */
427	movl	%eax, _C_LABEL(BIOS_regs)+BIOSR_AX
428	movl	%ecx, _C_LABEL(BIOS_regs)+BIOSR_CX
429	movl	%edx, _C_LABEL(BIOS_regs)+BIOSR_DX
430	movl	%ebp, _C_LABEL(BIOS_regs)+BIOSR_BP
431	movl	%esi, _C_LABEL(BIOS_regs)+BIOSR_SI
432	movl	%edi, _C_LABEL(BIOS_regs)+BIOSR_DI
433
434	/* clear NT flag in eflags */
435	pushf
436	pop	%eax
437	and	$0xffffbfff, %eax
438	push	%eax
439	popf
440
441	pop	%gs
442	pop	%fs
443	pop	%es
444	pop	%ds
445	popa
446	iret
447
448/* Call buffer at 07c0:0000 in real mode to simulate a BIOS boot */
449ENTRY(bootbuf)
450	pop	%eax		/* Don't need return address */
451	pop	%esi		/* Buffer */
452	pop	%edx		/* Device */
453	prot2real		/* Switch */
454
455	/* Set up stack */
456	cli
457	xor	%ax, %ax
458	mov	%ax, %ss
459	mov	$0xfffc, %esp
460	sti
461
462	/* Jump to buffer */
463	ljmp	$0x0, $0x7c00
464
465	.end
466