aboutsummaryrefslogtreecommitdiff
path: root/sys/boot/i386/btx/btxldr/btxldr.S
blob: d4176eb9c3b9344716468dd3f1e0074ae2d32142 (plain) (blame)
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
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
/*
 * Copyright (c) 1998 Robert Nordier
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are freely
 * permitted provided that the above copyright notice and this
 * paragraph and the following disclaimer are duplicated in all
 * such forms.
 *
 * This software is provided "AS IS" and without any express or
 * implied warranties, including, without limitation, the implied
 * warranties of merchantability and fitness for a particular
 * purpose.
 *
 * $FreeBSD$
 */

#define	RBX_MUTE	0x10	/* -m */
#define	OPT_SET(opt)	(1 << (opt))

/*
 * Prototype BTX loader program, written in a couple of hours.  The
 * real thing should probably be more flexible, and in C.
 */

/*
 * Memory locations.
 */
		.set MEM_STUB,0x600		# Real mode stub
		.set MEM_ESP,0x1000		# New stack pointer
		.set MEM_TBL,0x5000		# BTX page tables
		.set MEM_ENTRY,0x9010		# BTX entry point
		.set MEM_DATA,start+0x1000	# Data segment
/*
 * Segment selectors.
 */
		.set SEL_SCODE,0x8		# 4GB code
		.set SEL_SDATA,0x10		# 4GB data
		.set SEL_RCODE,0x18		# 64K code
		.set SEL_RDATA,0x20		# 64K data
/*
 * Paging constants.
 */
		.set PAG_SIZ,0x1000		# Page size
		.set PAG_ENT,0x4		# Page entry size
/*
 * Screen constants.
 */
		.set SCR_MAT,0x7		# Mode/attribute
		.set SCR_COL,0x50		# Columns per row
		.set SCR_ROW,0x19		# Rows per screen
/*
 * BIOS Data Area locations.
 */
		.set BDA_MEM,0x413		# Free memory
		.set BDA_SCR,0x449		# Video mode
		.set BDA_POS,0x450		# Cursor position
/*
 * Required by aout gas inadequacy.
 */
		.set SIZ_STUB,0x1a		# Size of stub
/*
 * We expect to be loaded by boot2 at the origin defined in ./Makefile.
 */
		.globl start
/*
 * BTX program loader for ELF clients.
 */
start:		cld				# String ops inc
		testl $OPT_SET(RBX_MUTE), 4(%esp) # Check first argument
		setnz muted			#  for RBX_MUTE, set flag
		movl $m_logo,%esi		# Identify
		call putstr			#  ourselves
		movzwl BDA_MEM,%eax		# Get base memory
		shll $0xa,%eax			#  in bytes
		movl %eax,%ebp			# Base of user stack
#ifdef BTXLDR_VERBOSE
		movl $m_mem,%esi		# Display
		call hexout			#  amount of
		call putstr			#  base memory
#endif
		lgdt gdtdesc			# Load new GDT
/*
 * Relocate caller's arguments.
 */
#ifdef BTXLDR_VERBOSE
		movl $m_esp,%esi		# Display
		movl %esp,%eax			#  caller
		call hexout			#  stack
		call putstr			#  pointer
		movl $m_args,%esi		# Format string
		leal 0x4(%esp,1),%ebx		# First argument
		movl $0x6,%ecx			# Count
start.1:	movl (%ebx),%eax		# Get argument and
		addl $0x4,%ebx			#  bump pointer
		call hexout			# Display it
		loop start.1			# Till done
		call putstr			# End message
#endif
		movl $0x48,%ecx 		# Allocate space
		subl %ecx,%ebp			#  for bootinfo
		movl 0x18(%esp,1),%esi		# Source: bootinfo
		cmpl $0x0, %esi			# If the bootinfo pointer
		je start_null_bi		#  is null, don't copy it
		movl %ebp,%edi			# Destination
		rep				# Copy
		movsb				#  it
		movl %ebp,0x18(%esp,1)		# Update pointer
#ifdef BTXLDR_VERBOSE
		movl $m_rel_bi,%esi		# Display
		movl %ebp,%eax			#  bootinfo
		call hexout			#  relocation
		call putstr			#  message
#endif
start_null_bi:	movl $0x18,%ecx 		# Allocate space
		subl %ecx,%ebp			#  for arguments
		leal 0x4(%esp,1),%esi		# Source
		movl %ebp,%edi			# Destination
		rep				# Copy
		movsb				#  them
#ifdef BTXLDR_VERBOSE
		movl $m_rel_args,%esi		# Display
		movl %ebp,%eax			#  argument
		call hexout			#  relocation
		call putstr			#  message
#endif
/*
 * Set up BTX kernel.
 */
		movl $MEM_ESP,%esp		# Set up new stack
		movl $MEM_DATA,%ebx		# Data segment
		movl $m_vers,%esi		# Display BTX
		call putstr			#  version message
		movb 0x5(%ebx),%al		# Get major version
		addb $'0',%al			# Display
		call putchr			#  it
		movb $'.',%al			# And a
		call putchr			#  dot
		movb 0x6(%ebx),%al		# Get minor
		xorb %ah,%ah			#  version
		movb $0xa,%dl			# Divide
		divb %dl,%al			#  by 10
		addb $'0',%al			# Display
		call putchr			#  tens
		movb %ah,%al			# Get units
		addb $'0',%al			# Display
		call putchr			#  units
		call putstr			# End message
		movl %ebx,%esi			# BTX image
		movzwl 0x8(%ebx),%edi		# Compute
		orl $PAG_SIZ/PAG_ENT-1,%edi	#  the
		incl %edi			#  BTX
		shll $0x2,%edi			#  load
		addl $MEM_TBL,%edi		#  address
		pushl %edi			# Save load address
		movzwl 0xa(%ebx),%ecx		# Image size
#ifdef BTXLDR_VERBOSE
		pushl %ecx			# Save image size
#endif
		rep				# Relocate
		movsb				#  BTX
		movl %esi,%ebx			# Keep place
#ifdef BTXLDR_VERBOSE
		movl $m_rel_btx,%esi		# Restore
		popl %eax			#  parameters
		call hexout			#  and
#endif
		popl %ebp			#  display
#ifdef BTXLDR_VERBOSE
		movl %ebp,%eax			#  the
		call hexout			#  relocation
		call putstr			#  message
#endif
		addl $PAG_SIZ,%ebp		# Display
#ifdef BTXLDR_VERBOSE
		movl $m_base,%esi		#  the
		movl %ebp,%eax			#  user
		call hexout			#  base
		call putstr			#  address
#endif
/*
 * Set up ELF-format client program.
 */
		cmpl $0x464c457f,(%ebx) 	# ELF magic number?
		je start.3			# Yes
		movl $e_fmt,%esi		# Display error
		call putstr			#  message
start.2:	jmp start.2			# Hang
start.3:
#ifdef BTXLDR_VERBOSE
		movl $m_elf,%esi		# Display ELF
		call putstr			#  message
		movl $m_segs,%esi		# Format string
#endif
		movl $0x2,%edi			# Segment count
		movl 0x1c(%ebx),%edx		# Get e_phoff
		addl %ebx,%edx			# To pointer
		movzwl 0x2c(%ebx),%ecx		# Get e_phnum
start.4:	cmpl $0x1,(%edx)		# Is p_type PT_LOAD?
		jne start.6			# No
#ifdef BTXLDR_VERBOSE
		movl 0x4(%edx),%eax		# Display
		call hexout			#  p_offset
		movl 0x8(%edx),%eax		# Display
		call hexout			#  p_vaddr
		movl 0x10(%edx),%eax		# Display
		call hexout			#  p_filesz
		movl 0x14(%edx),%eax		# Display
		call hexout			#  p_memsz
		call putstr			# End message
#endif
		pushl %esi			# Save
		pushl %edi			#  working
		pushl %ecx			#  registers
		movl 0x4(%edx),%esi		# Get p_offset
		addl %ebx,%esi			#  as pointer
		movl 0x8(%edx),%edi		# Get p_vaddr
		addl %ebp,%edi			#  as pointer
		movl 0x10(%edx),%ecx		# Get p_filesz
		rep				# Set up
		movsb				#  segment
		movl 0x14(%edx),%ecx		# Any bytes
		subl 0x10(%edx),%ecx		#  to zero?
		jz start.5			# No
		xorb %al,%al			# Then
		rep				#  zero
		stosb				#  them
start.5:	popl %ecx			# Restore
		popl %edi			#  working
		popl %esi			#  registers
		decl %edi			# Segments to do
		je start.7			# If none
start.6:	addl $0x20,%edx 		# To next entry
		loop start.4			# Till done
start.7:
#ifdef BTXLDR_VERBOSE
		movl $m_done,%esi		# Display done
		call putstr			#  message
#endif
		movl $start.8,%esi		# Real mode stub
		movl $MEM_STUB,%edi		# Destination
		movl $start.9-start.8,%ecx	# Size
		rep				# Relocate
		movsb				#  it
		ljmp $SEL_RCODE,$MEM_STUB	# To 16-bit code
		.code16
start.8:	xorw %ax,%ax			# Data
		movb $SEL_RDATA,%al		#  selector
		movw %ax,%ss			# Reload SS
		movw %ax,%ds			# Reset
		movw %ax,%es			#  other
		movw %ax,%fs			#  segment
		movw %ax,%gs			#  limits
		movl %cr0,%eax			# Switch to
		decw %ax			#  real
		movl %eax,%cr0			#  mode
		ljmp $0,$MEM_ENTRY		# Jump to BTX entry point
start.9:
		.code32
/*
 * Output message [ESI] followed by EAX in hex.
 */
hexout: 	pushl %eax			# Save
		call putstr			# Display message
		popl %eax			# Restore
		pushl %esi			# Save
		pushl %edi			#  caller's
		movl $buf,%edi			# Buffer
		pushl %edi			# Save
		call hex32			# To hex
		xorb %al,%al			# Terminate
		stosb				#  string
		popl %esi			# Restore
hexout.1:	lodsb				# Get a char
		cmpb $'0',%al			# Leading zero?
		je hexout.1			# Yes
		testb %al,%al			# End of string?
		jne hexout.2			# No
		decl %esi			# Undo
hexout.2:	decl %esi			# Adjust for inc
		call putstr			# Display hex
		popl %edi			# Restore
		popl %esi			#  caller's
		ret				# To caller
/*
 * Output zero-terminated string [ESI] to the console.
 */
putstr.0:	call putchr			# Output char
putstr: 	lodsb				# Load char
		testb %al,%al			# End of string?
		jne putstr.0			# No
		ret				# To caller
/*
 * Output character AL to the console.
 */
putchr:		testb $1,muted			# Check muted
		jnz putchr.5			#  do a nop
		pusha				# Save
		xorl %ecx,%ecx			# Zero for loops
		movb $SCR_MAT,%ah		# Mode/attribute
		movl $BDA_POS,%ebx		# BDA pointer
		movw (%ebx),%dx 		# Cursor position
		movl $0xb8000,%edi		# Regen buffer (color)
		cmpb %ah,BDA_SCR-BDA_POS(%ebx)	# Mono mode?
		jne putchr.1			# No
		xorw %di,%di			# Regen buffer (mono)
putchr.1:	cmpb $0xa,%al			# New line?
		je putchr.2			# Yes
		xchgl %eax,%ecx 		# Save char
		movb $SCR_COL,%al		# Columns per row
		mulb %dh			#  * row position
		addb %dl,%al			#  + column
		adcb $0x0,%ah			#  position
		shll %eax			#  * 2
		xchgl %eax,%ecx 		# Swap char, offset
		movw %ax,(%edi,%ecx,1)		# Write attr:char
		incl %edx			# Bump cursor
		cmpb $SCR_COL,%dl		# Beyond row?
		jb putchr.3			# No
putchr.2:	xorb %dl,%dl			# Zero column
		incb %dh			# Bump row
putchr.3:	cmpb $SCR_ROW,%dh		# Beyond screen?
		jb putchr.4			# No
		leal 2*SCR_COL(%edi),%esi	# New top line
		movw $(SCR_ROW-1)*SCR_COL/2,%cx # Words to move
		rep				# Scroll
		movsl				#  screen
		movb $' ',%al			# Space
		movb $SCR_COL,%cl		# Columns to clear
		rep				# Clear
		stosw				#  line
		movb $SCR_ROW-1,%dh		# Bottom line
putchr.4:	movw %dx,(%ebx) 		# Update position
		popa				# Restore
putchr.5:	ret				# To caller
/*
 * Convert EAX, AX, or AL to hex, saving the result to [EDI].
 */
hex32:		pushl %eax			# Save
		shrl $0x10,%eax 		# Do upper
		call hex16			#  16
		popl %eax			# Restore
hex16:		call hex16.1			# Do upper 8
hex16.1:	xchgb %ah,%al			# Save/restore
hex8:		pushl %eax			# Save
		shrb $0x4,%al			# Do upper
		call hex8.1			#  4
		popl %eax			# Restore
hex8.1: 	andb $0xf,%al			# Get lower 4
		cmpb $0xa,%al			# Convert
		sbbb $0x69,%al			#  to hex
		das				#  digit
		orb $0x20,%al			# To lower case
		stosb				# Save char
		ret				# (Recursive)

		.data
		.p2align 4
/*
 * Global descriptor table.
 */
gdt:		.word 0x0,0x0,0x0,0x0		# Null entry
		.word 0xffff,0x0,0x9a00,0xcf	# SEL_SCODE
		.word 0xffff,0x0,0x9200,0xcf	# SEL_SDATA
		.word 0xffff,0x0,0x9a00,0x0	# SEL_RCODE
		.word 0xffff,0x0,0x9200,0x0	# SEL_RDATA
gdt.1:
gdtdesc:	.word gdt.1-gdt-1		# Limit
		.long gdt			# Base
/*
 * Messages.
 */
m_logo: 	.asciz " \nBTX loader 1.00  "
m_vers: 	.asciz "BTX version is \0\n"
e_fmt:		.asciz "Error: Client format not supported\n"
#ifdef BTXLDR_VERBOSE
m_mem:		.asciz "Starting in protected mode (base mem=\0)\n"
m_esp:		.asciz "Arguments passed (esp=\0):\n"
m_args: 	.asciz"<howto="
		.asciz" bootdev="
		.asciz" junk="
		.asciz" "
		.asciz" "
		.asciz" bootinfo=\0>\n"
m_rel_bi:	.asciz "Relocated bootinfo (size=48) to \0\n"
m_rel_args:	.asciz "Relocated arguments (size=18) to \0\n"
m_rel_btx:	.asciz "Relocated kernel (size=\0) to \0\n"
m_base: 	.asciz "Client base address is \0\n"
m_elf:		.asciz "Client format is ELF\n"
m_segs: 	.asciz "text segment: offset="
		.asciz " vaddr="
		.asciz " filesz="
		.asciz " memsz=\0\n"
		.asciz "data segment: offset="
		.asciz " vaddr="
		.asciz " filesz="
		.asciz " memsz=\0\n"
m_done: 	.asciz "Loading complete\n"
#endif

/*
 * Flags
 */
muted:		.byte 0x0

/*
 * Uninitialized data area.
 */
buf:						# Scratch buffer