1/*	$MirOS: src/sys/arch/i386/stand/libsa/gidt.S,v 1.31 2009/10/24 15:57:11 tg Exp $ */
2/*	$OpenBSD: gidt.S,v 1.32 2006/12/26 19:30:44 tom Exp $	*/
3
4/*
5 * Copyright (c) 2008, 2009 Thorsten Glaser
6 * Copyright (c) 1997 Michael Shalayeff
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 *
30 */
31
32	.file "gidt.S"
33
34#include <machine/asm.h>
35#define _LOCORE
36#include <machine/trap.h>
37#include <debug_md.h>
38#undef _LOCORE
39#include <assym.h>
40
41#include "gidt.h"
42
43	.globl	_C_LABEL(BIOS_regs)
44
45	.text
46	.code32
47
48	.globl	_C_LABEL(_rtt)
49ENTRY(_rtt)
50#ifdef GIDT_DEBUG
51	movl	$0xb8000, %ebx
52	movl	$0x4f514f51, (%ebx)
53#endif
54	movw	$0x1234, %ax
55	movw	%ax, 0x472	/* warm boot */
56
57	/* Try to use the KBD to reboot system */
58	movb	$0xfe, %al
59	outb	%al, $0x64
60
61	movl	$0x5000, %ecx
621:	inb	$0x84, %al
63	loop	1b
64
65	movb	$0xfe, %al
66	outb	%al, $0x64
67
68#ifdef GIDT_DEBUG
69	movl	$0xb8000, %ebx
70	movl	$0x07310731, (%ebx)
71#endif
72
73	/* Try to cause a triple fault... */
74	lidt    Idtr_reset
75	xorl	%eax, %eax
76	divl	%eax, %eax
77
78	/* Again... */
79	int $0x8
80
81	/* Again... */
82	movl	$0, %esp	/* segment violation */
83	ret
84
85	/*
86	 * set up the protected mode Interrupt Descriptor Table
87	 */
88
89#ifdef __STDC__
90#define IPROC(n)	X##n
91#define IEMU(n)		IPROC(emu##n)
92#define idtb(b)		idte(emu##b)
93#else
94#define IPROC(n)	X/**/n
95#define IEMU(n)		IPROC(emu/**/n)
96#define idtb(b)		idte(emu/**/b)
97#endif
98
99#define idte(e) \
100	.long	IPROC(e), ((S32TEXT) << 16) | ((0x80 | SDT_SYS386TGT) << 8)
101
102	.p2align 4
103	.globl	idt_for_fixup
104idt_for_fixup:
105	idte(de); idte(db); idte(nmi); idte(bp); idte(of); idte(br)
106	idte(ud); idte(nm); idte(df);  idte(fo); idte(ts); idte(np)
107	idte(ss); idte(gp); idte(pf);  idte(xx); idte(mf); idte(ac)
108	idte(mc)
109	idte(xx); idte(xx); idte(xx);  idte(xx); idte(xx); idte(xx)
110	idte(xx); idte(xx); idte(xx);  idte(xx); idte(xx); idte(xx)
111	idte(xx)
112		/* Maskable interrupts (32-255) */
113		/* BIOS entry points (32-63) */
114		/* DOS entry points (64-80) */
115	idtb(0);  idtb(1);  idtb(2);  idtb(3);  idtb(4);  idtb(5)
116	idtb(6);  idtb(7);  idtb(8);  idtb(9);  idtb(10); idtb(11)
117	idtb(12); idtb(13); idtb(14); idtb(15); idtb(16); idtb(17)
118	idtb(18); idtb(19); idtb(20); idtb(21); idtb(22); idtb(23)
119	idtb(24); idtb(25); idtb(26); idtb(27); idtb(28); idtb(29)
120	idtb(30); idtb(31); idtb(32); idtb(33); idtb(34); idtb(35)
121	idtb(36); idtb(37); idtb(38); idtb(39); idtb(40); idtb(41)
122	idtb(42); idtb(43); idtb(44); idtb(45); idtb(46); idtb(47)
123	.globl	idt_fixup_end
124idt_fixup_end:
125
126	.balign 8
127	.globl	Idtr
128Idtr:	.word	idt_fixup_end - idt_for_fixup - 1
129	.long	idt_for_fixup
130	.word	0
131
132	.balign	8
133	.globl	Idtr_real
134Idtr_real:	.word	1023
135		.long	0
136		.word	0
137
138	.balign	8
139Idtr_reset:	.long	0, 0
140
141	.balign	8
142gdt:
143		/* 0x00 : null */
144	.space	8
145		/* 0x08 : flat code */
146	.word	0xFFFF			# lolimit
147	.word	0			# lobase
148	.byte	0			# midbase
149	.byte	SDT_MEMERAC | 0 | 0x80	# RXAC, dpl = 0, present
150	.byte	0xf | 0 | 0x40 | 0x80	# hilimit, xx, 32bit, 4k granularity
151	.byte	0			# hibase
152		/* 0x10 : flat data */
153	.word	0xFFFF			# lolimit
154	.word	0			# lobase
155	.byte	0			# midbase
156	.byte	SDT_MEMRWA | 0 | 0x80	# RWA, dpl = 0, present
157	.byte	0xf | 0 | 0x40 | 0x80	# hilimit, xx, 32bit, 4k granularity
158	.byte	0			# hibase
159		/* 0x18 : 16 bit code */
160	.word	0xFFFF				# lolimit
161	.word	(SA_LINKADDR & 0xffff)		# lobase
162	.byte	(SA_LINKADDR >> 16) & 0xff	# midbase
163	.byte	SDT_MEMERAC | 0 | 0x80		# RXAC, dpl = 0, present
164	.byte	0x0 | 0 | 0 | 0		# hilimit, xx, 16bit, byte granularity
165	.byte	(SA_LINKADDR >> 20) & 0xff	# hibase
166		/* 0x20 : 16 bit data */
167	.word	0xFFFF				# lolimit
168	.word	(SA_LINKADDR & 0xffff)		# lobase
169	.byte	(SA_LINKADDR >> 16) & 0xff	# midbase
170	.byte	SDT_MEMRWA | 0 | 0x80		# RWA, dpl = 0, present
171	.byte	0x0 | 0 | 0 | 0		# hilimit, xx, 16bit, byte granularity
172	.byte	(SA_LINKADDR >> 20) & 0xff	# hibase
173
174.globl Gdtr
175Gdtr:	.word	. - gdt - 1
176	.long	gdt
177	.word	0
178
179#define IENTRY(name,type) \
180IPROC(name): \
181	pushl	$type ; \
182	jmp	1f
183#define IENTRY_ERR(name,err,type) \
184IPROC(name): \
185	pushl	$err ; \
186	pushl	$type ; \
187	jmp	1f
188
189IPROC(xx):
190	pushl	$1
191	pushl	$T_RESERVED
192	jmp	1f
193
194IENTRY_ERR(de,0,T_DIVIDE)	/* #DE divide by zero */
195IENTRY_ERR(db,0,T_TRCTRAP)	/* #DB debug */
196IENTRY_ERR(nmi,0,T_NMI)		/* NMI */
197IENTRY_ERR(bp,0,T_BPTFLT)	/* #BP breakpoint */
198IENTRY_ERR(of,0,T_OFLOW)	/* #OF overflow */
199IENTRY_ERR(br,0,T_BOUND)	/* #BR BOUND range exceeded */
200IENTRY_ERR(ud,0,T_PRIVINFLT)	/* #UD invalid opcode */
201IENTRY_ERR(nm,0,T_DNA)		/* #NM device not available */
202IENTRY(df,T_DOUBLEFLT)		/* #DF double fault */
203IENTRY_ERR(fo,0,T_FPOPFLT)	/* #FO coprocessor segment overrun */
204IENTRY(ts,T_TSSFLT)		/* #TS invalid TSS */
205IENTRY(np,T_SEGNPFLT)		/* #NP segment not present */
206IENTRY(ss,T_STKFLT)		/* #SS stack fault */
207IENTRY(gp,T_PROTFLT)		/* #GP general protection */
208IENTRY(pf,T_PAGEFLT)		/* #PF page fault */
209IENTRY_ERR(mf,0,T_ARITHTRAP)	/* #MF floating point error */
210IENTRY(ac,T_ALIGNFLT)		/* #AC alignment check */
211IENTRY(mc,T_MACHK)		/* #MC machine check */
212
213	.globl	alltraps
2141:	/* save on jumps */
215	jmp	alltraps
216
217#define	IEMUENT(n)	IEMU(n): pushl $n; jmp 1f
218
219IEMUENT(0);  IEMUENT(1);  IEMUENT(2);  IEMUENT(3)
220IEMUENT(4);  IEMUENT(5);  IEMUENT(6);  IEMUENT(7)
221IEMUENT(8);  IEMUENT(9);  IEMUENT(10); IEMUENT(11)
222IEMUENT(12); IEMUENT(13); IEMUENT(14); IEMUENT(15)
223IEMUENT(16); IEMUENT(17); IEMUENT(18); IEMUENT(19)
224IEMUENT(20); IEMUENT(21); IEMUENT(22); IEMUENT(23)
225IEMUENT(24); IEMUENT(25); IEMUENT(26); IEMUENT(27)
226IEMUENT(28); IEMUENT(29); IEMUENT(30); IEMUENT(31)
2271:	jmp	EMUh	/* redirect for short jumps */
228IEMUENT(32); IEMUENT(33); IEMUENT(34); IEMUENT(35)
229IEMUENT(36); IEMUENT(37); IEMUENT(38); IEMUENT(39)
230IEMUENT(40); IEMUENT(41); IEMUENT(42); IEMUENT(43)
231IEMUENT(44); IEMUENT(45); IEMUENT(46); IEMUENT(47)
2321:	jmp	EMUh
233
234/*
235 * entry point for BIOS real-mode interface
236 * all the magic for real-prot mode switching is here
237 *
238 * Call:	%eax, %ecx, %edx, %ebx, %ebp, %esi, %edi, %es, %ds
239 * Return:	%eax, %edx, %ecx, %eflags (as returned from BIOS)
240 *
241 */
242	.globl	EMUh
243	.balign	8, 0x90
244	.intel_syntax noprefix
245EMUh:
246	xchg	[esp],eax
247	mov	[LemuNR],al
248	pop	eax
249
250	push	ebx
251	push	ebp
252	push	esi
253	push	edi
254	push	ds
255	push	es
256	push	fs
257	push	gs
258
259	push	eax
260	mov	eax,[BIOS_regs + BIOSR_DS]
261	push	eax
262	mov	eax,[BIOS_regs + BIOSR_ES]
263	push	eax
264
265	call	prot_to_real
266	.code16
267
268	pop	eax
269	mov	es,ax
270	pop	eax
271	mov	ds,ax
272	pop	eax
273
274	sti
275	int	0
276	LemuNR = . - 1
277	cli
278
279	push	eax
280	lahf
281	push	eax
282	xor	eax,eax
283	mov	ax,es
284	push	eax
285
286	call	real_to_prot
287	.code32
288
289	mov	[BIOS_regs + BIOSR_CX],ecx
290	mov	[BIOS_regs + BIOSR_DX],edx
291	mov	[BIOS_regs + BIOSR_BX],ebx
292	mov	[BIOS_regs + BIOSR_BP],ebp
293	mov	[BIOS_regs + BIOSR_SI],esi
294	mov	[BIOS_regs + BIOSR_DI],edi
295	pop	eax
296	mov	[BIOS_regs + BIOSR_ES],eax
297	pop	ebx
298	pop	eax
299	mov	[BIOS_regs + BIOSR_AX],eax
300
301	pushfd
302	pop	ebp
303	and	ebp,0xFFFFBFFF		/* clear NT flag */
304	push	ebp
305	popfd
306
307	pop	gs
308	pop	fs
309	pop	es
310	pop	ds
311	pop	edi
312	pop	esi
313	pop	ebp
314	mov	[esp+12],bh
315	pop	ebx
316	iret
317
318#ifndef SMALL_BOOT
319/* Return to DOS or SYSLINUX */
320ENTRY(rtdos)
321	call	prot_to_real
322	.code16
323	mov	ax,0x4C00
324	int	0x21
3251:	jmp	1b
326	.code32
327/* Call buffer at 07c0:0000 in real mode to simulate a BIOS boot */
328ENTRY(bootbuf)
329	pop	eax		/* Don't need return address */
330	pop	esi		/* Buffer Source (linear) */
331	pop	ecx		/* Size */
332	pop	ebp		/* Buffer Target (ES:DI) */
333	pop	ebx		/* Device and flags */
334	pop	edi		/* Initial CS:IP */
335
336	xor	edx,edx
337	dec	edx		/* EDX = 0xFFFFFFFF */
338	mov	dl,bl		/* EDX = 0xFFFFFFzz (z = device) */
339
340	call	prot_to_real
341	.code16
342
343	/* set up flags, real mode stack */
344	xor	eax,eax
345	mov	ss,ax
346	mov	esp,0x000006F0
347	push	eax
348	popf
349
350	/*-
351	 * Now the machine status is as follows:
352	 * - CS = DS = ES = 4000h = (SA_LINKSEG)
353	 * - SS:SP = 0000:06F0h (to make room for !PXE ptr)
354	 * - EFLAGS = 0
355	 * - ESI = linear address of source buffer (must be para aligned)
356	 * - ECX = source buffer size (up to 64 KiB)
357	 * - EBP = target address as ES:DI (FAR pointer)
358	 * - EDI = initial CS:IP (FAR pointer)
359	 * - EBX = bootbuf flags, currently unused
360	 * - EDX = drive
361	 * - _C_LABEL(bios_bootpte) = future DS:SI contents (16by)
362	 * - 0000:0600h .. 0000:07FFh are available to put the boot
363	 *   partition table entry in, according to SYSLINUX docs;
364	 *   we also move certain relocation code there, and our
365	 *   stack is also positioned in there
366	 * - we assume ECX > (“ES”<<4)|“DI” [EBP]
367	 * - “DI” is almost certainly 0 and should be
368	 */
369
370	mov	eax,edi
371	shr	eax,16
372	push	ax		/* future CS */
373	push	di		/* future IP */
374	mov	eax,ds:[8]
375	push	eax		/* !PXE structure RM address */
376	mov	eax,ds:[12]
377	push	eax		/* PXENV+ structure RM address */
378	push	edx		/* future DL (drive) */
379	push	ecx		/* source buffer size */
380	shr	esi,4
381	push	si		/* source buffer segment */
382	mov	eax,ebp
383	shr	eax,16
384	push	ax		/* buffer ES */
385	push	bp		/* buffer DI */
386	xor	ecx,ecx
387	mov	es,cx
388	mov	edi,0x0600
389	mov	esi,offset _C_LABEL(bios_bootpte) - SA_LINKADDR
390	mov	cx,4
391	rep	movsd
392	push	es		/* temporary CS */
393	push	di		/* temporary IP */
394	call	Ltcend
395Ltcbeg:	/* temporary relocation code, copied to 0000:0610h */
396	pop	di		/* buffer target (DI) */
397	pop	es		/* buffer target (ES) */
398	pop	ds		/* source buffer segment */
399Ltcbig:	pop	ecx		/* source buffer size */
400	xor	si,si		/* source buffer offset */
401	cmp	ecx,0x00010000
402	jb	Ltcseg
403	/* copy 64 KiB, reduce, retry */
404	sub	ecx,0x00010000
405	push	ecx
406	mov	ecx,0x4000	/* 64 KiB in dwords */
407	push	di
408	rep	movsd
409	pop	di
410	mov	ax,ds
411	add	ax,0x1000
412	mov	ds,ax
413	mov	ax,es
414	add	ax,0x1000
415	mov	es,ax
416	jmp	Ltcbig
417Ltcseg:	rep	movsb
418Ltcrun:	xor	ax,ax
419	mov	ds,ax
420	mov	si,0x0600
421	pop	edx		/* drive */
422	pop	bx		/* PXENV+ offset */
423	pop	es		/* PXENV+ segment */
424	pop	edi		/* !PXE */
425	mov	bp,sp
426	mov	[bp+8],edi
427	lret			/* future CS:IP on stack */
428Ltcend:	pop	si		/* offset Ltcbeg */
429	mov	cx,offset Ltcend - offset Ltcbeg
430	rep	movsb
431	lret			/* temporary CS:IP (0000:0610h) on stack */
432#endif
433
434/*-
435 * Switch from protected mode to real mode.
436 * Registers trashed: EAX
437 * CS = DS = ES = SA_LINKSEG
438 * SS:SP = old flat:ESP
439 */
440	.code32
441	.globl	prot_to_real
442prot_to_real:
443	cli
444	/* switch to 16 bit code size */
445	ljmp	S16TEXT,offset Lp2r32 - SA_LINKADDR
446Lp2r32:	.code16
447	mov	ax,S16DATA
448	mov	ds,ax
449	mov	es,ax
450	mov	fs,ax
451	mov	gs,ax
452	mov	ss,ax
453	/* disable protected mode */
454	mov	eax,cr0
455	and	eax,~CR0_PE
456	mov	cr0,eax
457	/* switch to 16 bit data size */
458	data32 ljmp SA_LINKSEG,offset Lp2r16 - SA_LINKADDR
459Lp2r16:	/* convert ESP to SS:SP */
460	mov	eax,esp
461	shr	eax,4
462	and	ax,0xF000
463	mov	ss,ax
464	and	esp,0x0000FFFF
465	/* load real mode segments */
466	mov	ax,cs
467	mov	ds,ax
468	mov	es,ax
469	mov	fs,ax
470	mov	gs,ax
471	/* load real mode IDT */
472	data32 addr32 lidt (Idtr_real - SA_LINKADDR)
473	/* fix up return address */
474	pop	eax
475	sub	eax,SA_LINKADDR
476	push	ax
477	ret
478
479/*-
480 * Switch from real mode to protected mode.
481 * Registers trashed: EAX
482 * CS = S32TEXT
483 * DS = ES = SS = S32DATA
484 * ESP = flat, old (SS<<4)|SP
485 */
486	.code16
487	.globl	real_to_prot
488real_to_prot:
489	cli
490	/* fix up return address */
491	xor	eax,eax
492	pop	ax
493	add	eax,SA_LINKADDR
494	push	eax
495	/* load GDT via data segment */
496	mov	ax,SA_LINKSEG
497	mov	ds,ax
498	data32 addr32 lgdt (Gdtr - SA_LINKADDR)
499	/* convert SS:SP to ESP */
500	xor	eax,eax
501	mov	ax,ss
502	shl	eax,4
503	and	esp,0x0000FFFF
504	add	esp,eax
505	/* enter protected mode */
506	mov	eax,cr0
507	or	eax,CR0_PE
508	mov	cr0,eax
509	/* switch to 32 bit */
510	data32 ljmp S32TEXT,offset Lr2p32
511Lr2p32:	.code32
512	mov	eax,S32DATA
513	mov	ds,ax
514	mov	es,ax
515	mov	fs,ax
516	mov	gs,ax
517	mov	ss,ax
518	/* load protected mode IDT */
519	lidt	Idtr
520	ret
521