summaryrefslogtreecommitdiff
path: root/sys/arch/i386/netboot/start.s
blob: 0bc14e124d9bab65a6d25ad64db4e7042203706d (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
/*	$NetBSD: start.s,v 1.4 1994/10/27 04:21:25 cgd Exp $	*/

#include "asm.h"

/* At entry, the processor is in 16 bit real mode and the code is being
 * executed from an address it was not linked to. Code must be pic and
 * 32 bit sensitive until things are fixed up.
 */

	.word	0xaa55			/* bios extension signature */
	.byte	0			/* no. of 512B blocks */
	jmp	1f			/* enter from bios here */
	.byte	0			/* checksum */
ENTRY(start)
1:
	cli
	# save the bios return address in these registers until protected
	# mode %ds is set up
	#	mov	(%esp), %edx
	#	mov	2(%esp), %ebp
	pop	%dx			/* return offset */
	pop	%ebp			/* return segment */

	/*  save stack [, data] context in case we are under dos */
	mov	%esp, %ecx
	mov	%ss, %ax
	mov	%eax, %ebx

	/* set up a usable stack */
	.byte	0xb8			/* (mov $0xa000, %ax) */
	.word	0xa000
	mov	%ax, %ss
	xor	%esp, %esp

	push	%ebp			/* return segment */
	push	%dx			/* return offset */
	push	%ds

#if 0
	jmp	ret16
#endif


#ifdef CHECK_386
	# check if 386 or later
	# from Intel i486 programmer\'s reference manual, section 22.10
	# Care must be taken with the first few instructions to ensure
	# operations are compatible with 386 progenitors - no operand
	# or address size overrides, all operations must be 16 bit.
	# Real mode not well supported by gas so it looks a bit crufty

	# TBD - there is little stack space, although the routine below
	# uses only two bytes; might set up new stack first thing, then
	# check processor type - would mean carrying sp, ss in two gp
	# registers for a while. also make alternate provisions for saving
	# ds: below.

	pushf
	pop	%ebx
	.byte	0x81, 0xe3		/* (and 0x0fff, %ebx) */
	.word	0x0fff
	push	%ebx
	popf
	pushf
	pop	%eax
	.byte	0x25 			/* (and 0xf000, %eax) */
	.word	0xf000
	.byte	0x3d			/* (cmp 0xf000, %eax) */
	.word	0xf000
	jz	Lwrong_cpu		/* \'86 */
	.byte	0x81, 0xcb		/* (or 0xf000, %ebx) */
	.word	0xf000
	push	%ebx
	popf
	pushf
	pop	%eax
	.byte	0x25			/* (and 0xf000, %eax) */
	.word	0xf000
	jnz	Lcpu_ok			/* else is \'286 */

Lwrong_cpu:
	.byte	0xbb			/* (mov bad_cpu_msg, %ebx) */
	.word	bad_cpu_msg
	.byte	0xe8			/* (call xputs) */
	.word	xputs-.-2
	lret

xputc:	/* print byte in %al */
	data32
	pusha
	.byte	0xbb			/* (mov $0x1, %ebx) %bh=0, %bl=1 (blue) */
	.word	0x0001
	movb	$0xe, %ah
	# sti
	int	$0x10			/* display a byte */
	# cli
	data32
	popa
	ret

xputs:	/* print string pointed to by cs:bx */
	data32
	pusha
1:
	cs
	.byte	0x8a, 0x07		/* (mov (%ebx), %al) */
	cmpb	$0, %al
	jz	1f
	push	%ebx
	.byte	0xe8			/* (call xputc) */
	.word	xputc-.-2
	pop	%ebx
	inc	%ebx
	jmp	1b
1:
	data32
	popa
	ret

bad_cpu_msg:	.asciz	"netboot: cpu cannot execute '386 instructions, net boot not done.\n\r"

Lcpu_ok:
	# at this point it is known this can execute 386 instructions
	# so operand and address size prefixes are ok
#endif /* CHECK_386 */

	/* copy rom to link addr, prepare for relocation */
        xor     %esi, %esi		/* source */
        opsize
        mov     $0, %edi 		/* destination */
        opsize
        mov     $(RELOC)>>4, %eax
        mov     %ax, %es
        opsize
	mov     $(ROM_SIZE), %ecx		/* count */
        cs
        rep
        movsb

	addrsize
	cs
	lgdt	gdtarg-RELOC

	/* turn on protected mode */
	cli
	mov	%cr0, %eax
	opsize
	or	$CR0_PE, %eax
	mov	%eax, %cr0

	/* jump to relocation, flush prefetch queue, and reload %cs */
	opsize
	ljmp	$KERN_CODE_SEG, $1f
1:
	/* reload other segment registers */
	movl	$KERN_DATA_SEG, %eax
	movl	%ax, %ds
	movl	%ax, %es
	movl	%ax, %ss
	movl	$0xa0000, %esp
	call	_main
	call	_exit

_ExitToBios:
	.globl _ExitToBios
	/* set up a dummy stack frame for the second seg change. */
	mov	$(RELOC)>>4, %eax
	pushw	%ax			/* real cs */
	pushw	$2f			/* real pc */

	/* Change to use16 mode. */
	ljmp	$BOOT_16_SEG, $1f	/* jump to a 16 bit segment */
1:
	/* clear the PE bit of CR0 */
	mov	%cr0, %eax
	opsize
	and 	$0!CR0_PE, %eax
	mov	%eax, %cr0

	/* make intersegment jmp to flush the processor pipeline
	 * using the fake stack frame set up earlier
	 * and reload CS register
	 */
	lret
2:
	/* we are in real mode now
	 * set up the real mode segment registers : DS, SS, ES
	 */
	movw	%cs, %ax
	movw	%ax, %ds
	movw	%ax, %ss
	movw	%ax, %es

ret16:	/* temporary label - remove (TBD) */
	/* now in dumbed down mode, caveats */
	/* restore old context and return to whatever called us */
	pop	%ds
	pop	%dx
	pop	%ebp

	mov	%ebx, %eax
	mov	%ax, %ss
	mov	%ecx, %esp

	push	%ebp
	push	%dx	
	sti
	lret

#ifdef USE_BIOS
_real_to_prot:
	.global	_real_to_prot

	addrsize
	cs
	lgdt	gdtarg-RELOC
	cli
	mov	%cr0, %eax
	opsize
	or	$CR0_PE, %eax
	mov	%eax, %cr0

	/* jump to relocation, flush prefetch queue, and reload %cs */
	opsize
	ljmp	$KERN_CODE_SEG, $1f
1:
	movl	$KERN_DATA_SEG, %eax
	movl	%ax, %ds
	movl	%ax, %es
	movl	%ax, %ss

	ret
#endif

#ifdef USE_BIOS
_prot_to_real:
	.global	_prot_to_real

	/* set up a dummy stack frame for the second seg change. */
	movl 	$(RELOC), %eax
	sarl	$4, %eax
	pushw	%ax			/* real cs */
	pushw	$2f			/* real pc */

	/* Change to use16 mode. */
	ljmp	$BOOT_16_SEG, $1f	/* jump to a 16 bit segment */
1:
	/* clear the PE bit of CR0 */
	mov	%cr0, %eax
	opsize
	and 	$0!CR0_PE, %eax
	mov	%eax, %cr0

	/* make intersegment jmp to flush the processor pipeline
	 * using the fake stack frame set up earlier
	 * and reload CS register
	 */
	lret
2:
	/* we are in real mode now
	 * set up the real mode segment registers : DS, SS, ES
	 */
	movw	%cs, %ax
	movw	%ax, %ds
	movw	%ax, %ss
	movw	%ax, %es

	opsize
	ret
#endif

	.align	4
gdt:
	.word	0, 0
	.byte	0, 0x00, 0x00, 0

	/* code segment */
	.word	0xffff, 0
	.byte	0, 0x9f, 0xcf, 0

	/* data segment */
	.word	0xffff, 0
	.byte	0, 0x93, 0xcf, 0

	/* 16 bit real mode */
	.word	0xffff, 0
	.byte	0, 0x9e, 0x00, 0

	.align	4
gdtarg:
	.word	0x1f			/* limit */
	.long	gdt			/* addr */