Home | History | Annotate | Line # | Download | only in lib
realprot.S revision 1.10.22.1
      1  1.10.22.1     rmind /*	$NetBSD: realprot.S,v 1.10.22.1 2014/05/18 17:45:13 rmind 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.10.22.1     rmind #include <x86/specialreg.h>
     38        1.1       dsl 
     39        1.1       dsl 	.text
     40        1.1       dsl 	.align  16
     41        1.1       dsl gdt:
     42        1.1       dsl 	.word	0, 0
     43        1.1       dsl 	.byte	0, 0x00, 0x00, 0
     44        1.1       dsl 
     45        1.1       dsl 	/* kernel code segment */
     46        1.1       dsl 	.globl flatcodeseg
     47        1.1       dsl flatcodeseg = . - gdt
     48        1.1       dsl 	.word	0xffff, 0
     49        1.1       dsl 	.byte	0, 0x9f, 0xcf, 0
     50        1.1       dsl 
     51        1.1       dsl 	/* kernel data segment */
     52        1.1       dsl 	.globl flatdataseg
     53        1.1       dsl flatdataseg = . - gdt
     54        1.1       dsl 	.word	0xffff, 0
     55        1.1       dsl 	.byte	0, 0x93, 0xcf, 0
     56        1.1       dsl 
     57        1.1       dsl 	/* boot code segment, base will be patched */
     58        1.1       dsl bootcodeseg = . - gdt
     59        1.1       dsl 	.word	0xffff, 0
     60        1.1       dsl 	.byte	0, 0x9e, 0x4f, 0
     61        1.1       dsl 
     62        1.1       dsl 	/* boot data segment, base will be patched */
     63        1.1       dsl bootdataseg = . - gdt
     64        1.1       dsl 	.word	0xffff, 0
     65        1.8  jmcneill 	.byte	0, 0x92, 0xcf, 0
     66        1.1       dsl 
     67        1.1       dsl 	/* 16 bit real mode, base will be patched */
     68        1.1       dsl bootrealseg = . - gdt
     69        1.1       dsl 	.word	0xffff, 0
     70        1.1       dsl 	.byte	0, 0x9e, 0x00, 0
     71        1.1       dsl 
     72        1.1       dsl 	/* limits (etc) for data segment in real mode */
     73        1.1       dsl bootrealdata = . - gdt
     74        1.1       dsl 	.word	0xffff, 0
     75        1.1       dsl 	.byte	0, 0x92, 0x00, 0
     76        1.1       dsl gdtlen = . - gdt
     77        1.1       dsl 
     78        1.1       dsl 	.align	16
     79        1.1       dsl gdtarg:
     80        1.1       dsl 	.word	gdtlen-1		/* limit */
     81        1.1       dsl 	.long	0			/* physical addr, will be inserted */
     82        1.1       dsl 
     83        1.1       dsl toreal:	.word	xreal			/* off:seg address for indirect jump */
     84        1.1       dsl ourseg:	.word	0			/* real mode code and data segment */
     85        1.1       dsl 
     86        1.1       dsl stkseg:	.word	0			/* real mode stack segment */
     87        1.1       dsl stkdif:	.long	0			/* diff. between real and prot sp */
     88        1.1       dsl 
     89        1.1       dsl 	.global	gdt_fixup
     90        1.1       dsl gdt_fixup:
     91        1.1       dsl 	.code16
     92       1.10  jakllsch 	pushl	%eax
     93       1.10  jakllsch 	pushl	%edx
     94        1.1       dsl 
     95        1.1       dsl 	xorl	%eax, %eax
     96        1.1       dsl 	mov	%cs, %ax
     97        1.1       dsl 	mov	%ax, ourseg
     98        1.1       dsl 	/* sort out stuff for %ss != %ds */
     99       1.10  jakllsch 	xorl	%edx, %edx
    100        1.1       dsl 	movw	%ss, %dx
    101        1.1       dsl 	movw	%dx, stkseg
    102       1.10  jakllsch 	subl	%eax, %edx
    103       1.10  jakllsch 	shll	$4, %edx
    104        1.1       dsl 	movl	%edx, stkdif
    105        1.1       dsl 
    106        1.1       dsl 	/* fix up GDT entries for bootstrap */
    107        1.1       dsl 	mov	%ax, %dx
    108        1.1       dsl 	shll	$4, %eax
    109        1.1       dsl 	shr	$12, %dx
    110        1.1       dsl 
    111        1.1       dsl #define FIXUP(gdt_index) \
    112        1.1       dsl 	movw	%ax, gdt+gdt_index+2; \
    113        1.1       dsl 	movb	%dl, gdt+gdt_index+4
    114        1.1       dsl 
    115        1.1       dsl 	FIXUP(bootcodeseg)
    116        1.1       dsl 	FIXUP(bootrealseg)
    117        1.1       dsl 	FIXUP(bootdataseg)
    118        1.1       dsl 
    119        1.1       dsl 	/* fix up GDT pointer */
    120        1.1       dsl 	addl	$gdt, %eax
    121        1.1       dsl 	movl	%eax, gdtarg+2
    122        1.1       dsl 
    123       1.10  jakllsch 	popl	%edx
    124       1.10  jakllsch 	popl	%eax
    125        1.1       dsl 	ret
    126        1.1       dsl 
    127        1.1       dsl /*
    128        1.1       dsl  * real_to_prot()
    129        1.1       dsl  *
    130        1.3       wiz  * Switch CPU to 32bit protected mode to execute C.
    131        1.1       dsl  *
    132        1.1       dsl  * NB: Call with the 32bit calll instruction so that a 32 bit
    133        1.1       dsl  *     return address is pushed.
    134        1.1       dsl  *
    135        1.1       dsl  * All registers are preserved, %ss:%esp will point to the same
    136        1.1       dsl  * place as %ss:%sp did, although the actual value of %esp might
    137        1.1       dsl  * be changed.
    138        1.1       dsl  *
    139        1.1       dsl  * Interrupts are disabled while we are in 32bit mode to save us
    140        1.1       dsl  * having to setup a different IDT.  This code is only used during
    141        1.1       dsl  * the boot process and it doesn't use any interrupts.
    142        1.1       dsl  */
    143        1.1       dsl ENTRY(real_to_prot)
    144        1.1       dsl 	.code16
    145        1.1       dsl 	pushl	%eax
    146        1.1       dsl 	cli
    147        1.1       dsl 
    148        1.1       dsl 	lgdt	%cs:gdtarg		/* Global descriptor table */
    149        1.1       dsl 
    150        1.1       dsl 	movl	%cr0, %eax
    151        1.1       dsl 	or	$CR0_PE, %ax
    152        1.2       dsl 	movl	%eax, %cr0 		/* Enter 'protected mode' */
    153        1.1       dsl 
    154        1.1       dsl 	ljmp	$bootcodeseg, $1f	/* Jump into a 32bit segment */
    155        1.1       dsl 1:
    156        1.1       dsl 
    157        1.1       dsl 	.code32
    158        1.1       dsl 	/*  Set all the segment registers to map the same area as the code */
    159        1.1       dsl 	mov	$bootdataseg, %eax
    160        1.1       dsl 	mov	%ax, %ds
    161        1.1       dsl 	mov	%ax, %es
    162        1.1       dsl 	mov	%ax, %ss
    163        1.1       dsl 	addl	stkdif, %esp		/* Allow for real %ss != %ds */
    164        1.1       dsl 
    165        1.1       dsl 	popl	%eax
    166        1.1       dsl 	ret
    167        1.1       dsl 
    168        1.1       dsl /*
    169        1.1       dsl  * prot_to_real()
    170        1.1       dsl  *
    171        1.3       wiz  * Switch CPU back to 16bit real mode in order to call system bios functions.
    172        1.1       dsl  *
    173        1.1       dsl  * All registers are preserved, except that %sp may be changed so that
    174        1.1       dsl  * %ss:%sp points to the same memory.
    175        1.1       dsl  * Note that %ebp is preserved and will not reference the correct part
    176        1.1       dsl  * of the stack.
    177        1.1       dsl  *
    178        1.1       dsl  * Interrupts are enabled while in real mode.
    179        1.1       dsl  *
    180        1.1       dsl  * Based on the descripton in section 14.5 of the 80386 Programmer's
    181        1.1       dsl  * reference book.
    182        1.1       dsl  */
    183        1.4     gavan /*
    184        1.4     gavan  * EPIA_HACK
    185        1.4     gavan  *
    186        1.9       dsl  * VIA C3 processors (Eden, Samuel 2) don't seem to correctly switch back to
    187        1.9       dsl  * executing 16 bit code after the switch to real mode and subsequent jump.
    188        1.4     gavan  *
    189        1.4     gavan  * It is speculated that the CPU is prefetching and decoding branch
    190        1.4     gavan  * targets and not invalidating this buffer on the long jump.
    191        1.9       dsl  * Further investication indicates that the caching of return addresses
    192        1.9       dsl  * is most likely the problem.
    193        1.4     gavan  *
    194        1.9       dsl  * Previous versions just used some extra call/ret and a few NOPs, these
    195        1.9       dsl  * only helped a bit, but booting compressed kernels would still fail.
    196        1.4     gavan  *
    197        1.9       dsl  * Trashing the return address stack (by doing 'call' without matched 'ret')
    198        1.9       dsl  * Seems to fix things completely. 1 iteration isn't enough, 16 is plenty.
    199        1.4     gavan  */
    200        1.1       dsl ENTRY(prot_to_real)
    201        1.9       dsl 	.code32
    202        1.9       dsl 	pushl	%eax
    203        1.4     gavan #ifdef EPIA_HACK
    204        1.9       dsl 	push	%ecx
    205        1.9       dsl 	push	$0x10
    206        1.9       dsl 	pop	%ecx
    207        1.9       dsl 1:	call	trash_return_cache
    208        1.9       dsl 	loop	1b
    209        1.9       dsl 	pop	%ecx
    210        1.4     gavan #endif
    211        1.1       dsl 
    212        1.1       dsl 	/*
    213        1.1       dsl 	 * Load the segment registers while still in protected mode.
    214        1.1       dsl 	 * Otherwise the control bits don't get changed.
    215        1.1       dsl 	 * The correct base addresses are loaded later.
    216        1.1       dsl 	 */
    217        1.5  junyoung 	movw    $bootrealdata, %ax
    218        1.1       dsl 	movw    %ax, %ds
    219        1.1       dsl 	movw    %ax, %es
    220        1.1       dsl 	movw    %ax, %ss
    221        1.1       dsl 
    222        1.1       dsl 	/*
    223        1.1       dsl 	 * Load %cs with a segment that has the correct attributes for
    224        1.1       dsl 	 * 16bit operation.
    225        1.1       dsl 	 */
    226        1.1       dsl 	ljmp	$bootrealseg, $1f
    227        1.1       dsl 1:
    228        1.1       dsl 
    229        1.1       dsl 	.code16
    230        1.1       dsl 	movl	%cr0, %eax
    231        1.1       dsl 	and 	$~CR0_PE, %eax
    232        1.1       dsl 	movl	%eax, %cr0		/* Disable potected mode */
    233        1.1       dsl 
    234        1.1       dsl 	/* Jump far indirect to load real mode %cs */
    235        1.1       dsl 	ljmp	*%cs:toreal
    236        1.1       dsl xreal:
    237        1.1       dsl 	/*
    238        1.3       wiz 	 * CPU is now in real mode, load the other segment registers
    239        1.1       dsl 	 * with their correct base addresses.
    240        1.1       dsl 	 */
    241        1.1       dsl 	mov	%cs, %ax
    242        1.1       dsl 	mov	%ax, %ds
    243        1.1       dsl 	mov	%ax, %es
    244        1.1       dsl 	/*
    245        1.1       dsl 	 * If stack was above 64k, 16bit %ss needs to be different from
    246        1.1       dsl 	 * 32bit %ss (and the other segment registers).
    247        1.1       dsl 	 */
    248        1.1       dsl 	mov	stkseg, %ax
    249        1.1       dsl 	mov	%ax, %ss
    250        1.1       dsl 	subl	stkdif, %esp
    251        1.1       dsl 
    252        1.1       dsl 	/* Check we are returning to an address below 64k */
    253        1.1       dsl 	push	%bp
    254        1.1       dsl 	movw	%sp, %bp
    255        1.1       dsl 	movw	2/*bp*/ + 4/*eax*/ + 2(%bp), %ax	/* high bits ret addr */
    256        1.1       dsl 	test	%ax, %ax
    257        1.1       dsl 	jne	1f
    258        1.1       dsl 	pop	%bp
    259        1.1       dsl 
    260        1.1       dsl 	sti
    261        1.1       dsl 	popl	%eax
    262        1.1       dsl 	retl
    263        1.1       dsl 
    264        1.1       dsl 1:	movw	$3f, %si
    265        1.1       dsl 	call	message
    266        1.1       dsl 	movl	2/*bp*/ + 4/*eax*/(%bp), %eax		/*  return address */
    267        1.1       dsl 	call	dump_eax
    268        1.1       dsl 	int	$0x18
    269        1.1       dsl 2:	sti
    270        1.1       dsl 	hlt
    271        1.1       dsl 	jmp	2b
    272        1.1       dsl 3:	.asciz	"prot_to_real can't return to "
    273        1.1       dsl 
    274        1.1       dsl 	.global	dump_eax_buff
    275        1.1       dsl dump_eax_buff:
    276        1.1       dsl 	. = . + 16
    277        1.1       dsl 
    278        1.4     gavan #ifdef EPIA_HACK
    279        1.9       dsl trash_return_cache:
    280        1.9       dsl 	.code32
    281        1.9       dsl 	pop	%eax
    282        1.9       dsl 	jmp	*%eax
    283        1.4     gavan #endif
    284        1.4     gavan 
    285        1.1       dsl /* vtophys(void *)
    286        1.1       dsl  * convert boot time 'linear' address to a physical one
    287        1.1       dsl  */
    288        1.1       dsl 
    289        1.1       dsl ENTRY(vtophys)
    290        1.1       dsl 	.code32
    291        1.1       dsl 	xorl	%eax, %eax
    292        1.1       dsl 	movw	ourseg, %ax
    293        1.1       dsl 	shll	$4, %eax
    294        1.1       dsl 	addl	4(%esp), %eax
    295        1.1       dsl 	ret
    296