Home | History | Annotate | Line # | Download | only in lib
realprot.S revision 1.8
      1  1.8  jmcneill /*	$NetBSD: realprot.S,v 1.8 2009/02/16 22:39:30 jmcneill Exp $	*/
      2  1.1       dsl 
      3  1.1       dsl /*-
      4  1.1       dsl  * Copyright (c) 2003 The NetBSD Foundation, Inc.
      5  1.1       dsl  * All rights reserved.
      6  1.1       dsl  *
      7  1.1       dsl  * This code is derived from software contributed to The NetBSD Foundation
      8  1.1       dsl  * by David Laight.
      9  1.1       dsl  *
     10  1.1       dsl  * Redistribution and use in source and binary forms, with or without
     11  1.1       dsl  * modification, are permitted provided that the following conditions
     12  1.1       dsl  * are met:
     13  1.1       dsl  * 1. Redistributions of source code must retain the above copyright
     14  1.1       dsl  *    notice, this list of conditions and the following disclaimer.
     15  1.1       dsl  * 2. Redistributions in binary form must reproduce the above copyright
     16  1.1       dsl  *    notice, this list of conditions and the following disclaimer in the
     17  1.1       dsl  *    documentation and/or other materials provided with the distribution.
     18  1.1       dsl  *
     19  1.1       dsl  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  1.1       dsl  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  1.1       dsl  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  1.1       dsl  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  1.1       dsl  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  1.1       dsl  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  1.1       dsl  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  1.1       dsl  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  1.1       dsl  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  1.1       dsl  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  1.1       dsl  * POSSIBILITY OF SUCH DAMAGE.
     30  1.1       dsl  */
     31  1.1       dsl 
     32  1.1       dsl /*
     33  1.1       dsl  * Loosely based on code from stand/lib/libcrt/bootsect/start_bootsect.S
     34  1.1       dsl  */
     35  1.1       dsl 
     36  1.1       dsl #include <machine/asm.h>
     37  1.1       dsl 
     38  1.1       dsl #define	CR0_PE		1
     39  1.1       dsl 
     40  1.1       dsl 	.text
     41  1.1       dsl 	.align  16
     42  1.1       dsl gdt:
     43  1.1       dsl 	.word	0, 0
     44  1.1       dsl 	.byte	0, 0x00, 0x00, 0
     45  1.1       dsl 
     46  1.1       dsl 	/* kernel code segment */
     47  1.1       dsl 	.globl flatcodeseg
     48  1.1       dsl flatcodeseg = . - gdt
     49  1.1       dsl 	.word	0xffff, 0
     50  1.1       dsl 	.byte	0, 0x9f, 0xcf, 0
     51  1.1       dsl 
     52  1.1       dsl 	/* kernel data segment */
     53  1.1       dsl 	.globl flatdataseg
     54  1.1       dsl flatdataseg = . - gdt
     55  1.1       dsl 	.word	0xffff, 0
     56  1.1       dsl 	.byte	0, 0x93, 0xcf, 0
     57  1.1       dsl 
     58  1.1       dsl 	/* boot code segment, base will be patched */
     59  1.1       dsl bootcodeseg = . - gdt
     60  1.1       dsl 	.word	0xffff, 0
     61  1.1       dsl 	.byte	0, 0x9e, 0x4f, 0
     62  1.1       dsl 
     63  1.1       dsl 	/* boot data segment, base will be patched */
     64  1.1       dsl bootdataseg = . - gdt
     65  1.1       dsl 	.word	0xffff, 0
     66  1.8  jmcneill 	.byte	0, 0x92, 0xcf, 0
     67  1.1       dsl 
     68  1.1       dsl 	/* 16 bit real mode, base will be patched */
     69  1.1       dsl bootrealseg = . - gdt
     70  1.1       dsl 	.word	0xffff, 0
     71  1.1       dsl 	.byte	0, 0x9e, 0x00, 0
     72  1.1       dsl 
     73  1.1       dsl 	/* limits (etc) for data segment in real mode */
     74  1.1       dsl bootrealdata = . - gdt
     75  1.1       dsl 	.word	0xffff, 0
     76  1.1       dsl 	.byte	0, 0x92, 0x00, 0
     77  1.1       dsl gdtlen = . - gdt
     78  1.1       dsl 
     79  1.1       dsl 	.align	16
     80  1.1       dsl gdtarg:
     81  1.1       dsl 	.word	gdtlen-1		/* limit */
     82  1.1       dsl 	.long	0			/* physical addr, will be inserted */
     83  1.1       dsl 
     84  1.1       dsl toreal:	.word	xreal			/* off:seg address for indirect jump */
     85  1.1       dsl ourseg:	.word	0			/* real mode code and data segment */
     86  1.1       dsl 
     87  1.1       dsl stkseg:	.word	0			/* real mode stack segment */
     88  1.1       dsl stkdif:	.long	0			/* diff. between real and prot sp */
     89  1.1       dsl 
     90  1.1       dsl 	.global	gdt_fixup
     91  1.1       dsl gdt_fixup:
     92  1.1       dsl 	.code16
     93  1.1       dsl 	push	%ax
     94  1.1       dsl 	push	%dx
     95  1.1       dsl 
     96  1.1       dsl 	xorl	%eax, %eax
     97  1.1       dsl 	mov	%cs, %ax
     98  1.1       dsl 	mov	%ax, ourseg
     99  1.1       dsl 	/* sort out stuff for %ss != %ds */
    100  1.1       dsl 	movw	%ss, %dx
    101  1.1       dsl 	movw	%dx, stkseg
    102  1.1       dsl 	subw	%ax, %dx
    103  1.1       dsl 	shll	$16, %edx
    104  1.1       dsl 	shrl	$12, %edx
    105  1.1       dsl 	movl	%edx, stkdif
    106  1.1       dsl 
    107  1.1       dsl 	/* fix up GDT entries for bootstrap */
    108  1.1       dsl 	mov	%ax, %dx
    109  1.1       dsl 	shll	$4, %eax
    110  1.1       dsl 	shr	$12, %dx
    111  1.1       dsl 
    112  1.1       dsl #define FIXUP(gdt_index) \
    113  1.1       dsl 	movw	%ax, gdt+gdt_index+2; \
    114  1.1       dsl 	movb	%dl, gdt+gdt_index+4
    115  1.1       dsl 
    116  1.1       dsl 	FIXUP(bootcodeseg)
    117  1.1       dsl 	FIXUP(bootrealseg)
    118  1.1       dsl 	FIXUP(bootdataseg)
    119  1.1       dsl 
    120  1.1       dsl 	/* fix up GDT pointer */
    121  1.1       dsl 	addl	$gdt, %eax
    122  1.1       dsl 	movl	%eax, gdtarg+2
    123  1.1       dsl 
    124  1.1       dsl 	pop	%dx
    125  1.1       dsl 	pop	%ax
    126  1.1       dsl 	ret
    127  1.1       dsl 
    128  1.1       dsl /*
    129  1.1       dsl  * real_to_prot()
    130  1.1       dsl  *
    131  1.3       wiz  * Switch CPU to 32bit protected mode to execute C.
    132  1.1       dsl  *
    133  1.1       dsl  * NB: Call with the 32bit calll instruction so that a 32 bit
    134  1.1       dsl  *     return address is pushed.
    135  1.1       dsl  *
    136  1.1       dsl  * All registers are preserved, %ss:%esp will point to the same
    137  1.1       dsl  * place as %ss:%sp did, although the actual value of %esp might
    138  1.1       dsl  * be changed.
    139  1.1       dsl  *
    140  1.1       dsl  * Interrupts are disabled while we are in 32bit mode to save us
    141  1.1       dsl  * having to setup a different IDT.  This code is only used during
    142  1.1       dsl  * the boot process and it doesn't use any interrupts.
    143  1.1       dsl  */
    144  1.1       dsl ENTRY(real_to_prot)
    145  1.1       dsl 	.code16
    146  1.1       dsl 	pushl	%eax
    147  1.1       dsl 	cli
    148  1.1       dsl 
    149  1.1       dsl 	lgdt	%cs:gdtarg		/* Global descriptor table */
    150  1.1       dsl 
    151  1.1       dsl 	movl	%cr0, %eax
    152  1.1       dsl 	or	$CR0_PE, %ax
    153  1.2       dsl 	movl	%eax, %cr0 		/* Enter 'protected mode' */
    154  1.1       dsl 
    155  1.1       dsl 	ljmp	$bootcodeseg, $1f	/* Jump into a 32bit segment */
    156  1.1       dsl 1:
    157  1.1       dsl 
    158  1.1       dsl 	.code32
    159  1.1       dsl 	/*  Set all the segment registers to map the same area as the code */
    160  1.1       dsl 	mov	$bootdataseg, %eax
    161  1.1       dsl 	mov	%ax, %ds
    162  1.1       dsl 	mov	%ax, %es
    163  1.1       dsl 	mov	%ax, %ss
    164  1.1       dsl 	addl	stkdif, %esp		/* Allow for real %ss != %ds */
    165  1.1       dsl 
    166  1.1       dsl 	popl	%eax
    167  1.1       dsl 	ret
    168  1.1       dsl 
    169  1.1       dsl /*
    170  1.1       dsl  * prot_to_real()
    171  1.1       dsl  *
    172  1.3       wiz  * Switch CPU back to 16bit real mode in order to call system bios functions.
    173  1.1       dsl  *
    174  1.1       dsl  * All registers are preserved, except that %sp may be changed so that
    175  1.1       dsl  * %ss:%sp points to the same memory.
    176  1.1       dsl  * Note that %ebp is preserved and will not reference the correct part
    177  1.1       dsl  * of the stack.
    178  1.1       dsl  *
    179  1.1       dsl  * Interrupts are enabled while in real mode.
    180  1.1       dsl  *
    181  1.1       dsl  * Based on the descripton in section 14.5 of the 80386 Programmer's
    182  1.1       dsl  * reference book.
    183  1.1       dsl  */
    184  1.4     gavan /*
    185  1.4     gavan  * EPIA_HACK
    186  1.4     gavan  *
    187  1.4     gavan  * VIA C3 processors don't seem to correctly switch back to executing
    188  1.4     gavan  * 16 bit code after the switch to real mode and subsequent jump.
    189  1.4     gavan  *
    190  1.4     gavan  * It is speculated that the CPU is prefetching and decoding branch
    191  1.4     gavan  * targets and not invalidating this buffer on the long jump.
    192  1.4     gavan  *
    193  1.4     gavan  * The precise reason for this hack working is still unknown, but
    194  1.4     gavan  * it was determined experimentally on two theories:
    195  1.4     gavan  * 1) Flush the pipeline with NOPs
    196  1.4     gavan  * 2) call/ret after the return from prot_to_real seems to improve matters,
    197  1.4     gavan  *    perhaps by making more work for the branch prediction/prefetch logic.
    198  1.4     gavan  *
    199  1.4     gavan  * Neither of these individually are effective, but this combination is
    200  1.4     gavan  * determined experimentally to be sufficient.
    201  1.4     gavan  */
    202  1.1       dsl ENTRY(prot_to_real)
    203  1.4     gavan #ifdef EPIA_HACK
    204  1.4     gavan 	.code32
    205  1.4     gavan 	call prot_to_real_main
    206  1.4     gavan 	.code16
    207  1.4     gavan 	call epia_nops
    208  1.4     gavan 	retl
    209  1.4     gavan 
    210  1.4     gavan prot_to_real_main:
    211  1.4     gavan #endif
    212  1.1       dsl 	.code32
    213  1.1       dsl 	pushl	%eax
    214  1.1       dsl 
    215  1.1       dsl 	/*
    216  1.1       dsl 	 * Load the segment registers while still in protected mode.
    217  1.1       dsl 	 * Otherwise the control bits don't get changed.
    218  1.1       dsl 	 * The correct base addresses are loaded later.
    219  1.1       dsl 	 */
    220  1.5  junyoung 	movw    $bootrealdata, %ax
    221  1.1       dsl 	movw    %ax, %ds
    222  1.1       dsl 	movw    %ax, %es
    223  1.1       dsl 	movw    %ax, %ss
    224  1.1       dsl 
    225  1.1       dsl 	/*
    226  1.1       dsl 	 * Load %cs with a segment that has the correct attributes for
    227  1.1       dsl 	 * 16bit operation.
    228  1.1       dsl 	 */
    229  1.1       dsl 	ljmp	$bootrealseg, $1f
    230  1.1       dsl 1:
    231  1.1       dsl 
    232  1.1       dsl 	.code16
    233  1.1       dsl 	movl	%cr0, %eax
    234  1.1       dsl 	and 	$~CR0_PE, %eax
    235  1.1       dsl 	movl	%eax, %cr0		/* Disable potected mode */
    236  1.1       dsl 
    237  1.1       dsl 	/* Jump far indirect to load real mode %cs */
    238  1.1       dsl 	ljmp	*%cs:toreal
    239  1.1       dsl xreal:
    240  1.1       dsl 	/*
    241  1.3       wiz 	 * CPU is now in real mode, load the other segment registers
    242  1.1       dsl 	 * with their correct base addresses.
    243  1.1       dsl 	 */
    244  1.1       dsl 	mov	%cs, %ax
    245  1.1       dsl 	mov	%ax, %ds
    246  1.1       dsl 	mov	%ax, %es
    247  1.1       dsl 	/*
    248  1.1       dsl 	 * If stack was above 64k, 16bit %ss needs to be different from
    249  1.1       dsl 	 * 32bit %ss (and the other segment registers).
    250  1.1       dsl 	 */
    251  1.1       dsl 	mov	stkseg, %ax
    252  1.1       dsl 	mov	%ax, %ss
    253  1.1       dsl 	subl	stkdif, %esp
    254  1.1       dsl 
    255  1.1       dsl 	/* Check we are returning to an address below 64k */
    256  1.1       dsl 	push	%bp
    257  1.1       dsl 	movw	%sp, %bp
    258  1.1       dsl 	movw	2/*bp*/ + 4/*eax*/ + 2(%bp), %ax	/* high bits ret addr */
    259  1.1       dsl 	test	%ax, %ax
    260  1.1       dsl 	jne	1f
    261  1.1       dsl 	pop	%bp
    262  1.1       dsl 
    263  1.4     gavan #ifdef EPIA_HACK
    264  1.5  junyoung 	call epia_nops
    265  1.4     gavan #endif
    266  1.4     gavan 
    267  1.1       dsl 	sti
    268  1.1       dsl 	popl	%eax
    269  1.1       dsl 	retl
    270  1.1       dsl 
    271  1.1       dsl 1:	movw	$3f, %si
    272  1.1       dsl 	call	message
    273  1.1       dsl 	movl	2/*bp*/ + 4/*eax*/(%bp), %eax		/*  return address */
    274  1.1       dsl 	call	dump_eax
    275  1.1       dsl 	int	$0x18
    276  1.1       dsl 2:	sti
    277  1.1       dsl 	hlt
    278  1.1       dsl 	jmp	2b
    279  1.1       dsl 3:	.asciz	"prot_to_real can't return to "
    280  1.1       dsl 
    281  1.1       dsl 	.global	dump_eax_buff
    282  1.1       dsl dump_eax_buff:
    283  1.1       dsl 	. = . + 16
    284  1.1       dsl 
    285  1.4     gavan #ifdef EPIA_HACK
    286  1.4     gavan epia_nops:
    287  1.4     gavan 	.code16
    288  1.4     gavan 	nop
    289  1.4     gavan 	nop
    290  1.4     gavan 	nop
    291  1.4     gavan 	nop
    292  1.4     gavan 	nop
    293  1.4     gavan 	nop
    294  1.4     gavan 	ret
    295  1.4     gavan #endif
    296  1.4     gavan 
    297  1.1       dsl /* vtophys(void *)
    298  1.1       dsl  * convert boot time 'linear' address to a physical one
    299  1.1       dsl  */
    300  1.1       dsl 
    301  1.1       dsl ENTRY(vtophys)
    302  1.1       dsl 	.code32
    303  1.1       dsl 	xorl	%eax, %eax
    304  1.1       dsl 	movw	ourseg, %ax
    305  1.1       dsl 	shll	$4, %eax
    306  1.1       dsl 	addl	4(%esp), %eax
    307  1.1       dsl 	ret
    308