1 1.13 andvar /* $NetBSD: realprot.S,v 1.13 2025/03/05 22:21:11 andvar 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.11 jakllsch #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.13 andvar * Based on the description 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.12 andvar * Further investigation 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.12 andvar movl %eax, %cr0 /* Disable protected 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