xref: /minix3/sys/arch/i386/stand/lib/realprot.S (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1*0a6a1f1dSLionel Sambuc/*	$NetBSD: realprot.S,v 1.11 2013/12/24 19:00:56 jakllsch Exp $	*/
258a2b000SEvgeniy Ivanov
358a2b000SEvgeniy Ivanov/*-
458a2b000SEvgeniy Ivanov * Copyright (c) 2003 The NetBSD Foundation, Inc.
558a2b000SEvgeniy Ivanov * All rights reserved.
658a2b000SEvgeniy Ivanov *
758a2b000SEvgeniy Ivanov * This code is derived from software contributed to The NetBSD Foundation
858a2b000SEvgeniy Ivanov * by David Laight.
958a2b000SEvgeniy Ivanov *
1058a2b000SEvgeniy Ivanov * Redistribution and use in source and binary forms, with or without
1158a2b000SEvgeniy Ivanov * modification, are permitted provided that the following conditions
1258a2b000SEvgeniy Ivanov * are met:
1358a2b000SEvgeniy Ivanov * 1. Redistributions of source code must retain the above copyright
1458a2b000SEvgeniy Ivanov *    notice, this list of conditions and the following disclaimer.
1558a2b000SEvgeniy Ivanov * 2. Redistributions in binary form must reproduce the above copyright
1658a2b000SEvgeniy Ivanov *    notice, this list of conditions and the following disclaimer in the
1758a2b000SEvgeniy Ivanov *    documentation and/or other materials provided with the distribution.
1858a2b000SEvgeniy Ivanov *
1958a2b000SEvgeniy Ivanov * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2058a2b000SEvgeniy Ivanov * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2158a2b000SEvgeniy Ivanov * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2258a2b000SEvgeniy Ivanov * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2358a2b000SEvgeniy Ivanov * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2458a2b000SEvgeniy Ivanov * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2558a2b000SEvgeniy Ivanov * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2658a2b000SEvgeniy Ivanov * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2758a2b000SEvgeniy Ivanov * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2858a2b000SEvgeniy Ivanov * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2958a2b000SEvgeniy Ivanov * POSSIBILITY OF SUCH DAMAGE.
3058a2b000SEvgeniy Ivanov */
3158a2b000SEvgeniy Ivanov
3258a2b000SEvgeniy Ivanov/*
3358a2b000SEvgeniy Ivanov * Loosely based on code from stand/lib/libcrt/bootsect/start_bootsect.S
3458a2b000SEvgeniy Ivanov */
3558a2b000SEvgeniy Ivanov
3658a2b000SEvgeniy Ivanov#include <machine/asm.h>
37*0a6a1f1dSLionel Sambuc#include <x86/specialreg.h>
3858a2b000SEvgeniy Ivanov
3958a2b000SEvgeniy Ivanov	.text
4058a2b000SEvgeniy Ivanov	.align  16
4158a2b000SEvgeniy Ivanovgdt:
4258a2b000SEvgeniy Ivanov	.word	0, 0
4358a2b000SEvgeniy Ivanov	.byte	0, 0x00, 0x00, 0
4458a2b000SEvgeniy Ivanov
4558a2b000SEvgeniy Ivanov	/* kernel code segment */
4658a2b000SEvgeniy Ivanov	.globl flatcodeseg
4758a2b000SEvgeniy Ivanovflatcodeseg = . - gdt
4858a2b000SEvgeniy Ivanov	.word	0xffff, 0
4958a2b000SEvgeniy Ivanov	.byte	0, 0x9f, 0xcf, 0
5058a2b000SEvgeniy Ivanov
5158a2b000SEvgeniy Ivanov	/* kernel data segment */
5258a2b000SEvgeniy Ivanov	.globl flatdataseg
5358a2b000SEvgeniy Ivanovflatdataseg = . - gdt
5458a2b000SEvgeniy Ivanov	.word	0xffff, 0
5558a2b000SEvgeniy Ivanov	.byte	0, 0x93, 0xcf, 0
5658a2b000SEvgeniy Ivanov
5758a2b000SEvgeniy Ivanov	/* boot code segment, base will be patched */
5858a2b000SEvgeniy Ivanovbootcodeseg = . - gdt
5958a2b000SEvgeniy Ivanov	.word	0xffff, 0
6058a2b000SEvgeniy Ivanov	.byte	0, 0x9e, 0x4f, 0
6158a2b000SEvgeniy Ivanov
6258a2b000SEvgeniy Ivanov	/* boot data segment, base will be patched */
6358a2b000SEvgeniy Ivanovbootdataseg = . - gdt
6458a2b000SEvgeniy Ivanov	.word	0xffff, 0
6558a2b000SEvgeniy Ivanov	.byte	0, 0x92, 0xcf, 0
6658a2b000SEvgeniy Ivanov
6758a2b000SEvgeniy Ivanov	/* 16 bit real mode, base will be patched */
6858a2b000SEvgeniy Ivanovbootrealseg = . - gdt
6958a2b000SEvgeniy Ivanov	.word	0xffff, 0
7058a2b000SEvgeniy Ivanov	.byte	0, 0x9e, 0x00, 0
7158a2b000SEvgeniy Ivanov
7258a2b000SEvgeniy Ivanov	/* limits (etc) for data segment in real mode */
7358a2b000SEvgeniy Ivanovbootrealdata = . - gdt
7458a2b000SEvgeniy Ivanov	.word	0xffff, 0
7558a2b000SEvgeniy Ivanov	.byte	0, 0x92, 0x00, 0
7658a2b000SEvgeniy Ivanovgdtlen = . - gdt
7758a2b000SEvgeniy Ivanov
7858a2b000SEvgeniy Ivanov	.align	16
7958a2b000SEvgeniy Ivanovgdtarg:
8058a2b000SEvgeniy Ivanov	.word	gdtlen-1		/* limit */
8158a2b000SEvgeniy Ivanov	.long	0			/* physical addr, will be inserted */
8258a2b000SEvgeniy Ivanov
8358a2b000SEvgeniy Ivanovtoreal:	.word	xreal			/* off:seg address for indirect jump */
8458a2b000SEvgeniy Ivanovourseg:	.word	0			/* real mode code and data segment */
8558a2b000SEvgeniy Ivanov
8658a2b000SEvgeniy Ivanovstkseg:	.word	0			/* real mode stack segment */
8758a2b000SEvgeniy Ivanovstkdif:	.long	0			/* diff. between real and prot sp */
8858a2b000SEvgeniy Ivanov
8958a2b000SEvgeniy Ivanov	.global	gdt_fixup
9058a2b000SEvgeniy Ivanovgdt_fixup:
9158a2b000SEvgeniy Ivanov	.code16
9258a2b000SEvgeniy Ivanov	pushl	%eax
9358a2b000SEvgeniy Ivanov	pushl	%edx
9458a2b000SEvgeniy Ivanov
9558a2b000SEvgeniy Ivanov	xorl	%eax, %eax
9658a2b000SEvgeniy Ivanov	mov	%cs, %ax
9758a2b000SEvgeniy Ivanov	mov	%ax, ourseg
9858a2b000SEvgeniy Ivanov	/* sort out stuff for %ss != %ds */
9958a2b000SEvgeniy Ivanov	xorl	%edx, %edx
10058a2b000SEvgeniy Ivanov	movw	%ss, %dx
10158a2b000SEvgeniy Ivanov	movw	%dx, stkseg
10258a2b000SEvgeniy Ivanov	subl	%eax, %edx
10358a2b000SEvgeniy Ivanov	shll	$4, %edx
10458a2b000SEvgeniy Ivanov	movl	%edx, stkdif
10558a2b000SEvgeniy Ivanov
10658a2b000SEvgeniy Ivanov	/* fix up GDT entries for bootstrap */
10758a2b000SEvgeniy Ivanov	mov	%ax, %dx
10858a2b000SEvgeniy Ivanov	shll	$4, %eax
10958a2b000SEvgeniy Ivanov	shr	$12, %dx
11058a2b000SEvgeniy Ivanov
11158a2b000SEvgeniy Ivanov#define FIXUP(gdt_index) \
11258a2b000SEvgeniy Ivanov	movw	%ax, gdt+gdt_index+2; \
11358a2b000SEvgeniy Ivanov	movb	%dl, gdt+gdt_index+4
11458a2b000SEvgeniy Ivanov
11558a2b000SEvgeniy Ivanov	FIXUP(bootcodeseg)
11658a2b000SEvgeniy Ivanov	FIXUP(bootrealseg)
11758a2b000SEvgeniy Ivanov	FIXUP(bootdataseg)
11858a2b000SEvgeniy Ivanov
11958a2b000SEvgeniy Ivanov	/* fix up GDT pointer */
12058a2b000SEvgeniy Ivanov	addl	$gdt, %eax
12158a2b000SEvgeniy Ivanov	movl	%eax, gdtarg+2
12258a2b000SEvgeniy Ivanov
12358a2b000SEvgeniy Ivanov	popl	%edx
12458a2b000SEvgeniy Ivanov	popl	%eax
12558a2b000SEvgeniy Ivanov	ret
12658a2b000SEvgeniy Ivanov
12758a2b000SEvgeniy Ivanov/*
12858a2b000SEvgeniy Ivanov * real_to_prot()
12958a2b000SEvgeniy Ivanov *
13058a2b000SEvgeniy Ivanov * Switch CPU to 32bit protected mode to execute C.
13158a2b000SEvgeniy Ivanov *
13258a2b000SEvgeniy Ivanov * NB: Call with the 32bit calll instruction so that a 32 bit
13358a2b000SEvgeniy Ivanov *     return address is pushed.
13458a2b000SEvgeniy Ivanov *
13558a2b000SEvgeniy Ivanov * All registers are preserved, %ss:%esp will point to the same
13658a2b000SEvgeniy Ivanov * place as %ss:%sp did, although the actual value of %esp might
13758a2b000SEvgeniy Ivanov * be changed.
13858a2b000SEvgeniy Ivanov *
13958a2b000SEvgeniy Ivanov * Interrupts are disabled while we are in 32bit mode to save us
14058a2b000SEvgeniy Ivanov * having to setup a different IDT.  This code is only used during
14158a2b000SEvgeniy Ivanov * the boot process and it doesn't use any interrupts.
14258a2b000SEvgeniy Ivanov */
14358a2b000SEvgeniy IvanovENTRY(real_to_prot)
14458a2b000SEvgeniy Ivanov	.code16
14558a2b000SEvgeniy Ivanov	pushl	%eax
14658a2b000SEvgeniy Ivanov	cli
14758a2b000SEvgeniy Ivanov
14858a2b000SEvgeniy Ivanov	lgdt	%cs:gdtarg		/* Global descriptor table */
14958a2b000SEvgeniy Ivanov
15058a2b000SEvgeniy Ivanov	movl	%cr0, %eax
15158a2b000SEvgeniy Ivanov	or	$CR0_PE, %ax
15258a2b000SEvgeniy Ivanov	movl	%eax, %cr0 		/* Enter 'protected mode' */
15358a2b000SEvgeniy Ivanov
15458a2b000SEvgeniy Ivanov	ljmp	$bootcodeseg, $1f	/* Jump into a 32bit segment */
15558a2b000SEvgeniy Ivanov1:
15658a2b000SEvgeniy Ivanov
15758a2b000SEvgeniy Ivanov	.code32
15858a2b000SEvgeniy Ivanov	/*  Set all the segment registers to map the same area as the code */
15958a2b000SEvgeniy Ivanov	mov	$bootdataseg, %eax
16058a2b000SEvgeniy Ivanov	mov	%ax, %ds
16158a2b000SEvgeniy Ivanov	mov	%ax, %es
16258a2b000SEvgeniy Ivanov	mov	%ax, %ss
16358a2b000SEvgeniy Ivanov	addl	stkdif, %esp		/* Allow for real %ss != %ds */
16458a2b000SEvgeniy Ivanov
16558a2b000SEvgeniy Ivanov	popl	%eax
16658a2b000SEvgeniy Ivanov	ret
16758a2b000SEvgeniy Ivanov
16858a2b000SEvgeniy Ivanov/*
16958a2b000SEvgeniy Ivanov * prot_to_real()
17058a2b000SEvgeniy Ivanov *
17158a2b000SEvgeniy Ivanov * Switch CPU back to 16bit real mode in order to call system bios functions.
17258a2b000SEvgeniy Ivanov *
17358a2b000SEvgeniy Ivanov * All registers are preserved, except that %sp may be changed so that
17458a2b000SEvgeniy Ivanov * %ss:%sp points to the same memory.
17558a2b000SEvgeniy Ivanov * Note that %ebp is preserved and will not reference the correct part
17658a2b000SEvgeniy Ivanov * of the stack.
17758a2b000SEvgeniy Ivanov *
17858a2b000SEvgeniy Ivanov * Interrupts are enabled while in real mode.
17958a2b000SEvgeniy Ivanov *
18058a2b000SEvgeniy Ivanov * Based on the descripton in section 14.5 of the 80386 Programmer's
18158a2b000SEvgeniy Ivanov * reference book.
18258a2b000SEvgeniy Ivanov */
18358a2b000SEvgeniy Ivanov/*
18458a2b000SEvgeniy Ivanov * EPIA_HACK
18558a2b000SEvgeniy Ivanov *
18658a2b000SEvgeniy Ivanov * VIA C3 processors (Eden, Samuel 2) don't seem to correctly switch back to
18758a2b000SEvgeniy Ivanov * executing 16 bit code after the switch to real mode and subsequent jump.
18858a2b000SEvgeniy Ivanov *
18958a2b000SEvgeniy Ivanov * It is speculated that the CPU is prefetching and decoding branch
19058a2b000SEvgeniy Ivanov * targets and not invalidating this buffer on the long jump.
19158a2b000SEvgeniy Ivanov * Further investication indicates that the caching of return addresses
19258a2b000SEvgeniy Ivanov * is most likely the problem.
19358a2b000SEvgeniy Ivanov *
19458a2b000SEvgeniy Ivanov * Previous versions just used some extra call/ret and a few NOPs, these
19558a2b000SEvgeniy Ivanov * only helped a bit, but booting compressed kernels would still fail.
19658a2b000SEvgeniy Ivanov *
19758a2b000SEvgeniy Ivanov * Trashing the return address stack (by doing 'call' without matched 'ret')
19858a2b000SEvgeniy Ivanov * Seems to fix things completely. 1 iteration isn't enough, 16 is plenty.
19958a2b000SEvgeniy Ivanov */
20058a2b000SEvgeniy IvanovENTRY(prot_to_real)
20158a2b000SEvgeniy Ivanov	.code32
20258a2b000SEvgeniy Ivanov	pushl	%eax
20358a2b000SEvgeniy Ivanov#ifdef EPIA_HACK
20458a2b000SEvgeniy Ivanov	push	%ecx
20558a2b000SEvgeniy Ivanov	push	$0x10
20658a2b000SEvgeniy Ivanov	pop	%ecx
20758a2b000SEvgeniy Ivanov1:	call	trash_return_cache
20858a2b000SEvgeniy Ivanov	loop	1b
20958a2b000SEvgeniy Ivanov	pop	%ecx
21058a2b000SEvgeniy Ivanov#endif
21158a2b000SEvgeniy Ivanov
21258a2b000SEvgeniy Ivanov	/*
21358a2b000SEvgeniy Ivanov	 * Load the segment registers while still in protected mode.
21458a2b000SEvgeniy Ivanov	 * Otherwise the control bits don't get changed.
21558a2b000SEvgeniy Ivanov	 * The correct base addresses are loaded later.
21658a2b000SEvgeniy Ivanov	 */
21758a2b000SEvgeniy Ivanov	movw    $bootrealdata, %ax
21858a2b000SEvgeniy Ivanov	movw    %ax, %ds
21958a2b000SEvgeniy Ivanov	movw    %ax, %es
22058a2b000SEvgeniy Ivanov	movw    %ax, %ss
22158a2b000SEvgeniy Ivanov
22258a2b000SEvgeniy Ivanov	/*
22358a2b000SEvgeniy Ivanov	 * Load %cs with a segment that has the correct attributes for
22458a2b000SEvgeniy Ivanov	 * 16bit operation.
22558a2b000SEvgeniy Ivanov	 */
22658a2b000SEvgeniy Ivanov	ljmp	$bootrealseg, $1f
22758a2b000SEvgeniy Ivanov1:
22858a2b000SEvgeniy Ivanov
22958a2b000SEvgeniy Ivanov	.code16
23058a2b000SEvgeniy Ivanov	movl	%cr0, %eax
23158a2b000SEvgeniy Ivanov	and 	$~CR0_PE, %eax
23258a2b000SEvgeniy Ivanov	movl	%eax, %cr0		/* Disable potected mode */
23358a2b000SEvgeniy Ivanov
23458a2b000SEvgeniy Ivanov	/* Jump far indirect to load real mode %cs */
23558a2b000SEvgeniy Ivanov	ljmp	*%cs:toreal
23658a2b000SEvgeniy Ivanovxreal:
23758a2b000SEvgeniy Ivanov	/*
23858a2b000SEvgeniy Ivanov	 * CPU is now in real mode, load the other segment registers
23958a2b000SEvgeniy Ivanov	 * with their correct base addresses.
24058a2b000SEvgeniy Ivanov	 */
24158a2b000SEvgeniy Ivanov	mov	%cs, %ax
24258a2b000SEvgeniy Ivanov	mov	%ax, %ds
24358a2b000SEvgeniy Ivanov	mov	%ax, %es
24458a2b000SEvgeniy Ivanov	/*
24558a2b000SEvgeniy Ivanov	 * If stack was above 64k, 16bit %ss needs to be different from
24658a2b000SEvgeniy Ivanov	 * 32bit %ss (and the other segment registers).
24758a2b000SEvgeniy Ivanov	 */
24858a2b000SEvgeniy Ivanov	mov	stkseg, %ax
24958a2b000SEvgeniy Ivanov	mov	%ax, %ss
25058a2b000SEvgeniy Ivanov	subl	stkdif, %esp
25158a2b000SEvgeniy Ivanov
25258a2b000SEvgeniy Ivanov	/* Check we are returning to an address below 64k */
25358a2b000SEvgeniy Ivanov	push	%bp
25458a2b000SEvgeniy Ivanov	movw	%sp, %bp
25558a2b000SEvgeniy Ivanov	movw	2/*bp*/ + 4/*eax*/ + 2(%bp), %ax	/* high bits ret addr */
25658a2b000SEvgeniy Ivanov	test	%ax, %ax
25758a2b000SEvgeniy Ivanov	jne	1f
25858a2b000SEvgeniy Ivanov	pop	%bp
25958a2b000SEvgeniy Ivanov
26058a2b000SEvgeniy Ivanov	sti
26158a2b000SEvgeniy Ivanov	popl	%eax
26258a2b000SEvgeniy Ivanov	retl
26358a2b000SEvgeniy Ivanov
26458a2b000SEvgeniy Ivanov1:	movw	$3f, %si
26558a2b000SEvgeniy Ivanov	call	message
26658a2b000SEvgeniy Ivanov	movl	2/*bp*/ + 4/*eax*/(%bp), %eax		/*  return address */
26758a2b000SEvgeniy Ivanov	call	dump_eax
26858a2b000SEvgeniy Ivanov	int	$0x18
26958a2b000SEvgeniy Ivanov2:	sti
27058a2b000SEvgeniy Ivanov	hlt
27158a2b000SEvgeniy Ivanov	jmp	2b
27258a2b000SEvgeniy Ivanov3:	.asciz	"prot_to_real can't return to "
27358a2b000SEvgeniy Ivanov
27458a2b000SEvgeniy Ivanov	.global	dump_eax_buff
27558a2b000SEvgeniy Ivanovdump_eax_buff:
27658a2b000SEvgeniy Ivanov	. = . + 16
27758a2b000SEvgeniy Ivanov
27858a2b000SEvgeniy Ivanov#ifdef EPIA_HACK
27958a2b000SEvgeniy Ivanovtrash_return_cache:
28058a2b000SEvgeniy Ivanov	.code32
28158a2b000SEvgeniy Ivanov	pop	%eax
28258a2b000SEvgeniy Ivanov	jmp	*%eax
28358a2b000SEvgeniy Ivanov#endif
28458a2b000SEvgeniy Ivanov
28558a2b000SEvgeniy Ivanov/* vtophys(void *)
28658a2b000SEvgeniy Ivanov * convert boot time 'linear' address to a physical one
28758a2b000SEvgeniy Ivanov */
28858a2b000SEvgeniy Ivanov
28958a2b000SEvgeniy IvanovENTRY(vtophys)
29058a2b000SEvgeniy Ivanov	.code32
29158a2b000SEvgeniy Ivanov	xorl	%eax, %eax
29258a2b000SEvgeniy Ivanov	movw	ourseg, %ax
29358a2b000SEvgeniy Ivanov	shll	$4, %eax
29458a2b000SEvgeniy Ivanov	addl	4(%esp), %eax
29558a2b000SEvgeniy Ivanov	ret
296