Linux Audio

Check our new training course

Embedded Linux Audio

Check our new training course
with Creative Commons CC-BY-SA
lecture materials

Bootlin logo

Elixir Cross Referencer

Loading...
  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
410
411
412
413
414
415
416
417
418
/*
 *	bootsect.S		Copyright (C) 1991, 1992 Linus Torvalds
 *
 *	modified by Drew Eckhardt
 *	modified by Bruce Evans (bde)
 *	modified by Chris Noe (May 1999) (as86 -> gas)
 *
 * 360k/720k disk support: Andrzej Krzysztofowicz <ankry@green.mif.pg.gda.pl>
 *
 * BIG FAT NOTE: We're in real mode using 64k segments.  Therefore segment
 * addresses must be multiplied by 16 to obtain their respective linear
 * addresses. To avoid confusion, linear addresses are written using leading
 * hex while segment addresses are written as segment:offset.
 *
 * bde - should not jump blindly, there may be systems with only 512K low
 * memory.  Use int 0x12 to get the top of memory, etc.
 *
 * It then loads 'setup' directly after itself (0x90200), and the system
 * at 0x10000, using BIOS interrupts. 
 *
 * NOTE! currently system is at most (8*65536-4096) bytes long. This should 
 * be no problem, even in the future. I want to keep it simple. This 508 kB
 * kernel size should be enough, especially as this doesn't contain the
 * buffer cache as in minix (and especially now that the kernel is 
 * compressed :-)
 *
 * The loader has been made as simple as possible, and continuous
 * read errors will result in a unbreakable loop. Reboot by hand. It
 * loads pretty fast by getting whole tracks at a time whenever possible.
 */

#include <asm/boot.h>

SETUPSECTS	= 4			/* default nr of setup-sectors */
BOOTSEG		= 0x07C0		/* original address of boot-sector */
INITSEG		= DEF_INITSEG		/* we move boot here - out of the way */
SETUPSEG	= DEF_SETUPSEG		/* setup starts here */
SYSSEG		= DEF_SYSSEG		/* system loaded at 0x10000 (65536) */
SYSSIZE		= DEF_SYSSIZE		/* system size: # of 16-byte clicks */
					/* to be loaded */
ROOT_DEV	= 0 			/* ROOT_DEV is now written by "build" */
SWAP_DEV	= 0			/* SWAP_DEV is now written by "build" */

#ifndef SVGA_MODE
#define SVGA_MODE ASK_VGA
#endif

#ifndef RAMDISK
#define RAMDISK 0
#endif

#ifndef ROOT_RDONLY
#define ROOT_RDONLY 1
#endif

.code16
.text

.global _start
_start:

# First things first. Move ourself from 0x7C00 -> 0x90000 and jump there.

	movw	$BOOTSEG, %ax
	movw	%ax, %ds		# %ds = BOOTSEG
	movw	$INITSEG, %ax
	movw	%ax, %es		# %ax = %es = INITSEG
	movw	$256, %cx
	subw	%si, %si
	subw	%di, %di
	cld
	rep
	movsw
	ljmp	$INITSEG, $go

# bde - changed 0xff00 to 0x4000 to use debugger at 0x6400 up (bde).  We
# wouldn't have to worry about this if we checked the top of memory.  Also
# my BIOS can be configured to put the wini drive tables in high memory
# instead of in the vector table.  The old stack might have clobbered the
# drive table.

go:	movw	$0x4000-12, %di		# 0x4000 is an arbitrary value >=
					# length of bootsect + length of
					# setup + room for stack;
					# 12 is disk parm size.
	movw	%ax, %ds		# %ax and %es already contain INITSEG
	movw	%ax, %ss
	movw	%di, %sp		# put stack at INITSEG:0x4000-12.

# 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 36 - the most we will encounter on an ED 2.88.  
#
# High doesn't hurt.  Low does.
#
# Segments are as follows: %cs = %ds = %es = %ss = INITSEG, %fs = 0,
# and %gs is unused.

	movw	%cx, %fs		# %fs = 0
	movw	$0x78, %bx		# %fs:%bx is parameter table address
	pushw	%ds
	ldsw	%fs:(%bx), %si		# %ds:%si is source
	movb	$6, %cl			# copy 12 bytes
	pushw	%di			# %di = 0x4000-12.
	rep				# don't worry about cld
	movsw				# already done above
	popw	%di
	popw	%ds
	movb	$36, 0x4(%di)		# patch sector count
	movw	%di, %fs:(%bx)
	movw	%es, %fs:2(%bx)

# Get disk drive parameters, specifically number of sectors/track.

# It seems that there is no BIOS call to get the number of sectors.
# Guess 36 sectors if sector 36 can be read, 18 sectors if sector 18
# can be read, 15 if sector 15 can be read.  Otherwise guess 9.
# Note that %cx = 0 from rep movsw above.

	movw	$disksizes, %si		# table of sizes to try
probe_loop:
	lodsb
	cbtw				# extend to word
	movw	%ax, sectors
	cmpw	$disksizes+4, %si
	jae	got_sectors		# If all else fails, try 9

	xchgw	%cx, %ax		# %cx = track and sector
	xorw	%dx, %dx		# drive 0, head 0
	movw	$0x0200, %bx		# address = 512, in INITSEG (%es = %cs)
	movw	$0x0201, %ax		# service 2, 1 sector
	int	$0x13
	jc	probe_loop		# try next value

got_sectors:
	movb	$0x03, %ah		# read cursor pos
	xorb	%bh, %bh
	int	$0x10
	movw	$9, %cx
	movb	$0x07, %bl		# page 0, attribute 7 (normal)
					# %bh is set above; int10 doesn't
					# modify it
	movw	$msg1, %bp
	movw	$0x1301, %ax		# write string, move cursor
	int	$0x10			# tell the user we're loading..

# Load the setup-sectors directly after the moved bootblock (at 0x90200).
# We should know the drive geometry to do it, as setup may exceed first
# cylinder (for 9-sector 360K and 720K floppies).

	movw	$0x0001, %ax		# set sread (sector-to-read) to 1 as
	movw	$sread, %si		# the boot sector has already been read
	movw	%ax, (%si)

	xorw	%ax, %ax		# reset FDC
	xorb	%dl, %dl
	int	$0x13
	movw	$0x0200, %bx		# address = 512, in INITSEG
next_step:
	movb	setup_sects, %al
	movw	sectors, %cx
	subw	(%si), %cx		# (%si) = sread
	cmpb	%cl, %al
	jbe	no_cyl_crossing
	movw	sectors, %ax
	subw	(%si), %ax		# (%si) = sread
no_cyl_crossing:
	call	read_track
	pushw	%ax			# save it
	call	set_next		# set %bx properly; it uses %ax,%cx,%dx
	popw	%ax			# restore
	subb	%al, setup_sects	# rest - for next step
	jnz	next_step

	pushw	$SYSSEG
	popw	%es			# %es = SYSSEG
	call	read_it
	call	kill_motor
	call	print_nl

# After that we check which root-device to use. If the device is
# defined (!= 0), nothing is done and the given device is used.
# Otherwise, one of /dev/fd0H2880 (2,32) or /dev/PS0 (2,28) or /dev/at0 (2,8)
# depending on the number of sectors we pretend to know we have.

# Segments are as follows: %cs = %ds = %ss = INITSEG,
#	%es = SYSSEG, %fs = 0, %gs is unused.

	movw	root_dev, %ax
	orw	%ax, %ax
	jne	root_defined

	movw	sectors, %bx
	movw	$0x0208, %ax		# /dev/ps0 - 1.2Mb
	cmpw	$15, %bx
	je	root_defined

	movb	$0x1c, %al		# /dev/PS0 - 1.44Mb
	cmpw	$18, %bx
	je	root_defined

	movb	$0x20, %al		# /dev/fd0H2880 - 2.88Mb
	cmpw	$36, %bx
	je	root_defined

	movb	$0, %al			# /dev/fd0 - autodetect
root_defined:
	movw	%ax, root_dev

# After that (everything loaded), we jump to the setup-routine
# loaded directly after the bootblock:

	ljmp	$SETUPSEG, $0

# These variables are addressed via %si register as it gives shorter code.

sread:	.word 0				# sectors read of current track
head:	.word 0				# current head
track:	.word 0				# current track

# This routine loads the system at address SYSSEG, making sure
# no 64kB boundaries are crossed. We try to load it as fast as
# possible, loading whole tracks whenever we can.

read_it:
	movw	%es, %ax		# %es = SYSSEG when called
	testw	$0x0fff, %ax
die:	jne	die			# %es must be at 64kB boundary
	xorw	%bx, %bx		# %bx is starting address within segment
rp_read:
#ifdef __BIG_KERNEL__			# look in setup.S for bootsect_kludge
	bootsect_kludge = 0x220		# 0x200 + 0x20 which is the size of the
	lcall	bootsect_kludge		# bootsector + bootsect_kludge offset
#else
	movw	%es, %ax
	subw	$SYSSEG, %ax
	movw	%bx, %cx
	shr	$4, %cx
	add	%cx, %ax		# check offset
#endif
	cmpw	syssize, %ax		# have we loaded everything yet?
	jbe	ok1_read

	ret

ok1_read:
	movw	sectors, %ax
	subw	(%si), %ax		# (%si) = sread
	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
	call	set_next
	jmp	rp_read

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

# Accessing head, track, sread via %si gives shorter code.

	movw	4(%si), %dx		# 4(%si) = track
	movw	(%si), %cx		# (%si)  = sread
	incw	%cx
	movb	%dl, %ch
	movw	2(%si), %dx		# 2(%si) = head
	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

set_next:
	movw	%ax, %cx
	addw	(%si), %ax		# (%si) = sread
	cmp	sectors, %ax
	jne	ok3_set
	movw	$0x0001, %ax
	xorw	%ax, 2(%si)		# change head
	jne	ok4_set
	incw	4(%si)			# next track
ok4_set:
	xorw	%ax, %ax
ok3_set:
	movw	%ax, (%si)		# set sread
	shlw	$9, %cx
	addw	%cx, %bx
	jnc	set_next_fin
	movw	%es, %ax
	addb	$0x10, %ah
	movw	%ax, %es
	xorw	%bx, %bx
set_next_fin:
	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

# print_all 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
#	(error)
#	ret <- %sp
 
print_all:
	movw	$5, %cx			# error code + 4 registers
	movw	%sp, %bp
print_loop:
	pushw	%cx			# save count remaining
	call	print_nl		# <-- for readability
	cmpb	$5, %cl
	jae	no_reg			# see if register name is needed
	
	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	$0xa, %al		# LF
	int 	$0x10
	ret

# print_hex 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 to use low 4 bits
	movw	$0xe0f, %ax		# %ah = request
	andb	%dl, %al		# %al = mask for nybble
	addb	$0x90, %al		# convert %al to ascii hex
	daa				# in only four instructions!
	adc	$0x40, %al
	daa
	int	$0x10
	loop	print_digit
	ret

# This procedure 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.
# NOTE: Doesn't save %ax or %dx; do it yourself if you need to.

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

sectors:	.word 0
disksizes:	.byte 36, 18, 15, 9
msg1:		.byte 13, 10
		.ascii "Loading"

# XXX: This is a fairly snug fit.

.org 497
setup_sects:	.byte SETUPSECTS
root_flags:	.word ROOT_RDONLY
syssize:	.word SYSSIZE
swap_dev:	.word SWAP_DEV
ram_size:	.word RAMDISK
vid_mode:	.word SVGA_MODE
root_dev:	.word ROOT_DEV
boot_flag:	.word 0xAA55