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 */
|