File: bootsect.S

package info (click to toggle)
memtest86%2B 6.10-4
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 1,532 kB
  • sloc: ansic: 19,267; asm: 2,027; makefile: 367; sh: 134
file content (367 lines) | stat: -rw-r--r-- 7,616 bytes parent folder | download | duplicates (3)
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
// SPDX-License-Identifier: GPL-2.0
//
// bootsect.S supports booting directly from the BIOS or via an intermediate
// bootloader that supports the Linux boot protocol. If booted directly from
// the BIOS, it is loaded at address 0x7c00. It then loads setup.S immediately
// after itself (address 0x7e00) and the main program code at segment MAIN_SEG,
// using BIOS interrupts to read the data from disk. When using an intermediate
// bootloader, it provides the first few bytes of the Linux boot header (at the
// end of the boot sector), with the remainder of the header being provided by
// setup.S.
//
// Copyright (C) 2020 Martin Whitaker.
//
// Derived from memtest86+ bootsect.S:
//
//	 bootsect.s		Copyright (C) 1991, 1992 Linus Torvalds
//
// 1-Jan-96 Modified by Chris Brady for use as a boot loader for MemTest-86.

#define __ASSEMBLY__

#include "boot.h"

	.section ".bootsect", "ax", @progbits
	.code16

# The BIOS boot entry point. This will be located at 0x7c00.

	.globl	boot
boot:
	# Initialise the segment registers and the stack.

	ljmp	$BOOT_SEG, $init
init:
	movw	%cs, %ax
	movw	%ax, %ds
	movw	%ax, %es
	movw	%ax, %ss
	movw	$BOOT_STACK_TOP, %ax
	movw	%ax, %sp

	# Many BIOS's default disk parameter tables will not recognize
	# multi-sector reads beyond the maximum sector number specified
	# in the default diskette parameter tables - this may mean 7
	# sectors in some cases.
	#
	# Since single sector reads are slow and out of the question,
	# we must take care of this by creating new parameter tables
	# (for the first disk) in RAM. We will set the maximum sector
	# count to 18 - the most we will encounter on an HD 1.44.
	#
	# High doesn't hurt. Low does.
	#
	# Segments are as follows:
	#	ds=es=ss=cs = BOOT_SEG,
	#	fs = 0, gs = parameter table segment

	pushw	$0
	popw	%fs
	movw	$0x78, %bx		# fs:bx is parameter table address
	lgs	%fs:(%bx),%si		# gs:si is source

	movw	%dx, %di		# es:di is destination
	movw	$6, %cx 		# copy 12 bytes
	cld

	rep	movsw %gs:(%si), (%di)

	movw	%dx, %di
	movb	$18, 4(%di)		# patch sector count

	movw	%di, %fs:(%bx)
	movw	%es, %fs:2(%bx)

	movw	%cs, %ax
	movw	%ax, %fs
	movw	%ax, %gs

	xorb	%ah, %ah		# reset FDC
	xorb	%dl, %dl
	int	$0x13

	# Load the setup sectors directly after the boot block.
	# Note that 'es' is already set up.

load_setup:
	xorw	%dx, %dx			# drive 0, head 0
	movw	$0x0002, %cx			# sector 2, track 0
	movw	$0x0200, %bx			# address = 512, in BOOT_SEG
	movw	$(0x0200 + SETUP_SECS), %ax	# service 2, nr of sectors
						# (assume all on head 0, track 0)
	int	$0x13				# read it
	jnc	load_setup_done 		# ok - continue

	pushw	%ax			# dump error code
	call	print_nl
	movw	%sp, %bp
	call	print_hex
	popw	%ax

	xorb	%dl, %dl		# reset FDC
	xorb	%ah, %ah
	int	$0x13
	jmp	load_setup

load_setup_done:

	# Get disk drive parameters, specifically number of sectors/track.
	# It seems that there is no BIOS call to get the number of sectors.
	# Guess 18 sectors if sector 18 can be read, 15 if sector 15 can be
	# read. Otherwise guess 9.

	xorw	%dx, %dx		# drive 0, head 0
	movw	$0x0012, %cx		# sector 18, track 0
	movw	$BOOT_STACK, %bx	# use the bottom of the stack (es = cs)
	movw	$0x0201, %ax		# service 2, 1 sector
	int	$0x13
	jnc	got_sectors
	movb	$0x0f, %cl		# sector 15
	movw	$0x0201, %ax		# service 2, 1 sector
	int	$0x13
	jnc	got_sectors
	movb	$0x09, %cl

got_sectors:
	movw	%cx, %cs:sectors
	movw	$BOOT_SEG, %ax
	movw	%ax, %es

	# Print a message.

	movb	$0x03, %ah		# read cursor pos
	xorb	%bh, %bh
	int	$0x10

	leaw	boot_msg, %bp
	movw	$(boot_msg_end - boot_msg), %cx
	movw	$0x0007, %bx		# page 0, attribute 7 (normal)
	movw	$0x1301, %ax		# write string, move cursor
	int	$0x10

	# Load the main test program.

	movw	$MAIN_SEG, %ax
	movw	%ax, %es
	call	read_it
	call	kill_motor
	call	turn_off_cursor
	call	print_nl

	# Fix up the Linux boot header to indicate we've loaded into low memory.

	movl	$LOW_LOAD_ADDR, code32_start

	# After that (everything loaded), we jump to the setup code loaded
	# directly after the boot block.

	ljmp	$SETUP_SEG, $0

# This subroutine loads the system at address 0x10000, making sure no 64KB
# boundaries are crossed. We try to load it as fast as possible, loading
# whole tracks whenever we can.
#
# in:	es - starting address segment (normally 0x1000)
#
sread:	.word 1 + SETUP_SECS		# sectors read of current track
head:	.word 0 			# current head
track:	.word 0 			# current track

read_it:
	movw	%es, %ax
	testw	$0x0fff, %ax
die:
	jne	die			# es must be at 64kB boundary
	xorw	%bx,%bx 		# bx is starting address within segment
rp_read:
	movw	%es, %ax
	subw	$MAIN_SEG, %ax		# have we loaded all yet?
	cmpw	sys_size, %ax
	jbe	ok1_read
	ret
ok1_read:
	movw	%cs:sectors, %ax
	subw	sread, %ax
	movw	%ax, %cx
	shlw	$9, %cx
	addw	%bx, %cx
	jnc	ok2_read
	je	ok2_read
	xorw	%ax, %ax
	subw	%bx, %ax
	shrw	$9, %ax
ok2_read:
	call	read_track
	movw	%ax, %cx
	add	sread, %ax
	cmpw	%cs:sectors, %ax
	jne	ok3_read
	movw	$1, %ax
	subw	head, %ax
	jne	ok4_read
	incw	track
ok4_read:
	movw	%ax, head
	xorw	%ax, %ax
ok3_read:
	movw	%ax, sread
	shlw	$9, %cx
	addw	%cx, %bx
	jnc	rp_read
	movw	%es, %ax
	addb	$0x10, %ah
	movw	%ax, %es
	xorw	%bx, %bx
	jmp	rp_read

read_track:
	pusha
	pusha
	movw	$0xe2e, %ax		# loading... message 2e = .
	movw	$7, %bx
	int	$0x10
	popa

	movw	track, %dx
	movw	sread, %cx
	incw	%cx
	movb	%dl, %ch
	movw	head, %dx
	movb	%dl, %dh
	andw	$0x0100, %dx
	movb	$2, %ah

	pushw	%dx			# save for error dump
	pushw	%cx
	pushw	%bx
	pushw	%ax

	int	$0x13
	jc	bad_rt
	addw	$8, %sp
	popa
	ret

bad_rt:
	pushw	%ax			# save error code
	call	print_all		# ah = error, al = read

	xorb	%ah, %ah
	xorb	%dl, %dl
	int	$0x13

	addw	$10, %sp
	popa
	jmp read_track

# This subroutine is for debugging purposes. It will print out all of the
# registers.  The assumption is that this is called from a routine, with a
# stack frame like:
#	dx
#	cx
#	bx
#	ax
#	err
#	ret <- sp

print_all:
	movw	$5, %cx 		# error code + 4 registers
	movw	%sp, %bp

print_loop:
	pushw	%cx			# save count left
	call	print_nl		# nl for readability

	cmpb	5, %cl			# see if register name is needed
	jae	no_reg

	movw	$(0xe05 + 'A' - 1), %ax
	subb	%cl, %al
	int	$0x10
	movb	$'X', %al
	int	$0x10
	movb	$':', %al
	int	$0x10

no_reg:
	addw	$2, %bp 		# next register
	call	print_hex		# print it
	popw	%cx
	loop	print_loop
	ret

print_nl:
	movw	$0xe0d, %ax		# CR
	int	$0x10
	movb	$0x0a, %al		# LF
	int	$0x10
	ret

# This subroutine is for debugging purposes, and prints the word pointed to
# by ss:bp in hexadecimal.

print_hex:
	movw	$4, %cx 		# 4 hex digits
	movw	(%bp), %dx		# load word into dx

print_digit:
	rolw	$4, %dx 		# rotate so that lowest 4 bits are used
	movb	$0xe, %ah
	movb	%dl, %al		# mask off so we have only next nibble
	andb	$0xf, %al
	addb	$'0', %al		# convert to 0-based digit
	cmpb	$'9', %al		# check for overflow
	jbe	good_digit
	addb	$('A' - '0' - 10), %al

good_digit:
	int	$0x10
	loop	print_digit
	ret

# This subroutine turns off the floppy drive motor, so that we enter the
# kernel in a known state, and don't have to worry about it later.

kill_motor:
	pushw	%dx
	movw	$0x3f2, %dx
	xorb	%al, %al
	outb	%al, %dx
	popw	%dx
	ret

# This subroutine turns off the text display cursor.

turn_off_cursor:
	movb	$0x01, %ah
	movb	$0x00, %bh
	movw	$0x2000, %cx
	int	$0x10
	ret

# Local variables.

sectors:
	.word	0

boot_msg:
	.ascii	"Loading Memtest86+"
boot_msg_end:

# Emulate the Linux boot header, to allow loading by intermediate boot loaders.

	.org	497
setup_sects:
	.byte	SETUP_SECS
root_flags:
	.word	0
sys_size:
	.long	_sys_size
ram_size:
	.word	0
vid_mode:
	.word	0
root_dev:
	.word	0
boot_flag:
	.word	0xAA55