summaryrefslogtreecommitdiff
path: root/sys/arch/i386/stand/lib/realprot.S
blob: 3900f4b9945ff6cb1997aba608fb3c3cddff45a1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
/*	$NetBSD: realprot.S,v 1.11 2013/12/24 19:00:56 jakllsch Exp $	*/

/*-
 * Copyright (c) 2003 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by David Laight.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Loosely based on code from stand/lib/libcrt/bootsect/start_bootsect.S
 */

#include <machine/asm.h>
#include <x86/specialreg.h>

	.text
	.align  16
gdt:
	.word	0, 0
	.byte	0, 0x00, 0x00, 0

	/* kernel code segment */
	.globl flatcodeseg
flatcodeseg = . - gdt
	.word	0xffff, 0
	.byte	0, 0x9f, 0xcf, 0

	/* kernel data segment */
	.globl flatdataseg
flatdataseg = . - gdt
	.word	0xffff, 0
	.byte	0, 0x93, 0xcf, 0

	/* boot code segment, base will be patched */
bootcodeseg = . - gdt
	.word	0xffff, 0
	.byte	0, 0x9e, 0x4f, 0

	/* boot data segment, base will be patched */
bootdataseg = . - gdt
	.word	0xffff, 0
	.byte	0, 0x92, 0xcf, 0

	/* 16 bit real mode, base will be patched */
bootrealseg = . - gdt
	.word	0xffff, 0
	.byte	0, 0x9e, 0x00, 0

	/* limits (etc) for data segment in real mode */
bootrealdata = . - gdt
	.word	0xffff, 0
	.byte	0, 0x92, 0x00, 0
gdtlen = . - gdt

	.align	16
gdtarg:
	.word	gdtlen-1		/* limit */
	.long	0			/* physical addr, will be inserted */

toreal:	.word	xreal			/* off:seg address for indirect jump */
ourseg:	.word	0			/* real mode code and data segment */

stkseg:	.word	0			/* real mode stack segment */
stkdif:	.long	0			/* diff. between real and prot sp */

	.global	gdt_fixup
gdt_fixup:
	.code16
	pushl	%eax
	pushl	%edx

	xorl	%eax, %eax
	mov	%cs, %ax
	mov	%ax, ourseg
	/* sort out stuff for %ss != %ds */
	xorl	%edx, %edx
	movw	%ss, %dx
	movw	%dx, stkseg
	subl	%eax, %edx
	shll	$4, %edx
	movl	%edx, stkdif

	/* fix up GDT entries for bootstrap */
	mov	%ax, %dx
	shll	$4, %eax
	shr	$12, %dx

#define FIXUP(gdt_index) \
	movw	%ax, gdt+gdt_index+2; \
	movb	%dl, gdt+gdt_index+4

	FIXUP(bootcodeseg)
	FIXUP(bootrealseg)
	FIXUP(bootdataseg)

	/* fix up GDT pointer */
	addl	$gdt, %eax
	movl	%eax, gdtarg+2

	popl	%edx
	popl	%eax
	ret

/*
 * real_to_prot()
 *
 * Switch CPU to 32bit protected mode to execute C.
 *
 * NB: Call with the 32bit calll instruction so that a 32 bit
 *     return address is pushed.
 *
 * All registers are preserved, %ss:%esp will point to the same
 * place as %ss:%sp did, although the actual value of %esp might
 * be changed.
 *
 * Interrupts are disabled while we are in 32bit mode to save us
 * having to setup a different IDT.  This code is only used during
 * the boot process and it doesn't use any interrupts.
 */
ENTRY(real_to_prot)
	.code16
	pushl	%eax
	cli

	lgdt	%cs:gdtarg		/* Global descriptor table */

	movl	%cr0, %eax
	or	$CR0_PE, %ax
	movl	%eax, %cr0 		/* Enter 'protected mode' */

	ljmp	$bootcodeseg, $1f	/* Jump into a 32bit segment */
1:

	.code32
	/*  Set all the segment registers to map the same area as the code */
	mov	$bootdataseg, %eax
	mov	%ax, %ds
	mov	%ax, %es
	mov	%ax, %ss
	addl	stkdif, %esp		/* Allow for real %ss != %ds */

	popl	%eax
	ret

/*
 * prot_to_real()
 *
 * Switch CPU back to 16bit real mode in order to call system bios functions.
 *
 * All registers are preserved, except that %sp may be changed so that
 * %ss:%sp points to the same memory.
 * Note that %ebp is preserved and will not reference the correct part
 * of the stack.
 *
 * Interrupts are enabled while in real mode.
 *
 * Based on the descripton in section 14.5 of the 80386 Programmer's
 * reference book.
 */
/*
 * EPIA_HACK
 *
 * VIA C3 processors (Eden, Samuel 2) don't seem to correctly switch back to
 * executing 16 bit code after the switch to real mode and subsequent jump.
 *
 * It is speculated that the CPU is prefetching and decoding branch
 * targets and not invalidating this buffer on the long jump.
 * Further investication indicates that the caching of return addresses
 * is most likely the problem.
 *
 * Previous versions just used some extra call/ret and a few NOPs, these
 * only helped a bit, but booting compressed kernels would still fail.
 *
 * Trashing the return address stack (by doing 'call' without matched 'ret')
 * Seems to fix things completely. 1 iteration isn't enough, 16 is plenty.
 */
ENTRY(prot_to_real)
	.code32
	pushl	%eax
#ifdef EPIA_HACK
	push	%ecx
	push	$0x10
	pop	%ecx
1:	call	trash_return_cache
	loop	1b
	pop	%ecx
#endif

	/*
	 * Load the segment registers while still in protected mode.
	 * Otherwise the control bits don't get changed.
	 * The correct base addresses are loaded later.
	 */
	movw    $bootrealdata, %ax
	movw    %ax, %ds
	movw    %ax, %es
	movw    %ax, %ss

	/*
	 * Load %cs with a segment that has the correct attributes for
	 * 16bit operation.
	 */
	ljmp	$bootrealseg, $1f
1:

	.code16
	movl	%cr0, %eax
	and 	$~CR0_PE, %eax
	movl	%eax, %cr0		/* Disable potected mode */

	/* Jump far indirect to load real mode %cs */
	ljmp	*%cs:toreal
xreal:
	/*
	 * CPU is now in real mode, load the other segment registers
	 * with their correct base addresses.
	 */
	mov	%cs, %ax
	mov	%ax, %ds
	mov	%ax, %es
	/*
	 * If stack was above 64k, 16bit %ss needs to be different from
	 * 32bit %ss (and the other segment registers).
	 */
	mov	stkseg, %ax
	mov	%ax, %ss
	subl	stkdif, %esp

	/* Check we are returning to an address below 64k */
	push	%bp
	movw	%sp, %bp
	movw	2/*bp*/ + 4/*eax*/ + 2(%bp), %ax	/* high bits ret addr */
	test	%ax, %ax
	jne	1f
	pop	%bp

	sti
	popl	%eax
	retl

1:	movw	$3f, %si
	call	message
	movl	2/*bp*/ + 4/*eax*/(%bp), %eax		/*  return address */
	call	dump_eax
	int	$0x18
2:	sti
	hlt
	jmp	2b
3:	.asciz	"prot_to_real can't return to "

	.global	dump_eax_buff
dump_eax_buff:
	. = . + 16

#ifdef EPIA_HACK
trash_return_cache:
	.code32
	pop	%eax
	jmp	*%eax
#endif

/* vtophys(void *)
 * convert boot time 'linear' address to a physical one
 */

ENTRY(vtophys)
	.code32
	xorl	%eax, %eax
	movw	ourseg, %ax
	shll	$4, %eax
	addl	4(%esp), %eax
	ret