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