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