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
|
#
# 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$
# Memory Locations
.set MEM_REL,0x700 # Relocation address
.set MEM_ARG,0x900 # Arguments
.set MEM_ORG,0x7c00 # Origin
.set MEM_BUF,0x8c00 # Load area
.set MEM_BTX,0x9000 # BTX start
.set MEM_JMP,0x9010 # BTX entry point
.set MEM_USR,0xa000 # Client start
# Partition Constants
.set PRT_OFF,0x1be # Partition offset
.set PRT_NUM,0x4 # Partitions
.set PRT_BSD,0xa5 # Partition type
# Flag Bits
.set FL_PACKET,0x80 # Packet mode
# Misc. Constants
.set SIZ_PAG,0x1000 # Page size
.set SIZ_SEC,0x200 # Sector size
.globl start
.globl xread
.code16
start: jmp main # Start recognizably
.org 0x4,0x90
#
# Trampoline used by boot2 to call read to read data from the disk via
# the BIOS. Call with:
#
# %cx:%ax - long - LBA to read in
# %es:(%bx) - caddr_t - buffer to read data into
# %dl - byte - drive to read from
# %dh - byte - num sectors to read
#
xread: push %ss # Address
pop %ds # data
#
# Setup an EDD disk packet and pass it to read
#
xread.1: # Starting
pushl $0x0 # absolute
push %cx # block
push %ax # number
push %es # Address of
push %bx # transfer buffer
xor %ax,%ax # Number of
movb %dh,%al # blocks to
push %ax # transfer
push $0x10 # Size of packet
mov %sp,%bp # Packet pointer
callw read # Read from disk
lea 0x10(%bp),%sp # Clear stack
lret # To far caller
#
# Load the rest of boot2 and BTX up, copy the parts to the right locations,
# and start it all up.
#
#
# Setup the segment registers to flat addressing (segment 0) and setup the
# stack to end just below the start of our code.
#
main: cld # String ops inc
xor %cx,%cx # Zero
mov %cx,%es # Address
mov %cx,%ds # data
mov %cx,%ss # Set up
mov $start,%sp # stack
#
# Relocate ourself to MEM_REL. Since %cx == 0, the inc %ch sets
# %cx == 0x100.
#
mov %sp,%si # Source
mov $MEM_REL,%di # Destination
incb %ch # Word count
rep # Copy
movsw # code
#
# If we are on a hard drive, then load the MBR and look for the first
# FreeBSD slice. We use the fake partition entry below that points to
# the MBR when we call nread. The first pass looks for the first active
# FreeBSD slice. The second pass looks for the first non-active FreeBSD
# slice if the first one fails.
#
mov $part4,%si # Partition
cmpb $0x80,%dl # Hard drive?
jb main.4 # No
movb $0x1,%dh # Block count
callw nread # Read MBR
mov $0x1,%cx # Two passes
main.1: mov $MEM_BUF+PRT_OFF,%si # Partition table
movb $0x1,%dh # Partition
main.2: cmpb $PRT_BSD,0x4(%si) # Our partition type?
jne main.3 # No
jcxz main.5 # If second pass
testb $0x80,(%si) # Active?
jnz main.5 # Yes
main.3: add $0x10,%si # Next entry
incb %dh # Partition
cmpb $0x1+PRT_NUM,%dh # In table?
jb main.2 # Yes
dec %cx # Do two
jcxz main.1 # passes
#
# If we get here, we didn't find any FreeBSD slices at all, so print an
# error message and die.
#
mov $msg_part,%si # Message
jmp error # Error
#
# Floppies use partition 0 of drive 0.
#
main.4: xor %dx,%dx # Partition:drive
#
# Ok, we have a slice and drive in %dx now, so use that to locate and load
# boot2. %si references the start of the slice we are looking for, so go
# ahead and load up the first 16 sectors (boot1 + boot2) from that. When
# we read it in, we conveniently use 0x8c00 as our transfer buffer. Thus,
# boot1 ends up at 0x8c00, and boot2 starts at 0x8c00 + 0x200 = 0x8e00.
# The first part of boot2 is boot2.ldr, which is 0x200 bytes of zeros.
# The second part is BTX, which is thus loaded into 0x9000, which is where
# it also runs from. The boot2.bin binary starts right after the end of
# BTX, so we have to figure out where the start of it is and then move the
# binary to 0xb000. Normally, BTX clients start at MEM_USR, or 0xa000, but
# when we use btxld create boot2, we use an entry point of 0x1000. That
# entry point is relative, to MEM_USR, thus boot2.bin starts at 0xb000.
#
main.5: mov %dx,MEM_ARG # Save args
movb $0x10,%dh # Sector count
callw nread # Read disk
mov $MEM_BTX,%bx # BTX
mov 0xa(%bx),%si # Get BTX length and set
add %bx,%si # %si to start of boot2.bin
mov $MEM_USR+SIZ_PAG,%di # Client page 1
mov $MEM_BTX+0xe*SIZ_SEC,%cx # Byte
sub %si,%cx # count
rep # Relocate
movsb # client
sub %di,%cx # Byte count
xorb %al,%al # Zero assumed bss from
rep # the end of boot2.bin
stosb # up to 0x10000
callw seta20 # Enable A20
jmp start+MEM_JMP-MEM_ORG # Start BTX
#
# Enable A20 so we can access memory above 1 meg.
#
seta20: cli # Disable interrupts
seta20.1: inb $0x64,%al # Get status
testb $0x2,%al # Busy?
jnz seta20.1 # Yes
movb $0xd1,%al # Command: Write
outb %al,$0x64 # output port
seta20.2: inb $0x64,%al # Get status
testb $0x2,%al # Busy?
jnz seta20.2 # Yes
movb $0xdf,%al # Enable
outb %al,$0x60 # A20
sti # Enable interrupts
retw # To caller
#
# Trampoline used to call read from within boot1.
#
nread: mov $MEM_BUF,%bx # Transfer buffer
mov 0x8(%si),%ax # Get
mov 0xa(%si),%cx # LBA
push %cs # Read from
callw xread.1 # disk
jnc return # If success, return
mov $msg_read,%si # Otherwise, set the error
# message and fall through to
# the error routine
#
# Print out the error message pointed to by %ds:(%si) followed
# by a prompt, wait for a keypress, and then reboot the machine.
#
error: callw putstr # Display message
mov $prompt,%si # Display
callw putstr # prompt
xorb %ah,%ah # BIOS: Get
int $0x16 # keypress
int $0x19 # BIOS: Reboot
#
# Display a null-terminated string using the BIOS output.
#
putstr.0: mov $0x7,%bx # Page:attribute
movb $0xe,%ah # BIOS: Display
int $0x10 # character
putstr: lodsb # Get char
testb %al,%al # End of string?
jne putstr.0 # No
#
# Overused return code. ereturn is used to return an error from the
# read function. Since we assume putstr succeeds, we (ab)use the
# same code when we return from putstr.
#
ereturn: movb $0x1,%ah # Invalid
stc # argument
return: retw # To caller
#
# Reads sectors from the disk. If EDD is enabled, then check if it is
# installed and use it if it is. If it is not installed or not enabled, then
# fall back to using CHS. Since we use a LBA, if we are using CHS, we have to
# fetch the drive parameters from the BIOS and divide it out ourselves.
# Call with:
#
# %dl - byte - drive number
# stack - 10 bytes - EDD Packet
#
read: testb $FL_PACKET,%cs:MEM_REL+flags-start # LBA support enabled?
jz read.1 # No
mov $0x55aa,%bx # Magic
push %dx # Save
movb $0x41,%ah # BIOS: Check
int $0x13 # extensions present
pop %dx # Restore
jc read.1 # If error
cmp $0xaa55,%bx # Magic?
jne read.1 # No
testb $0x1,%cl # Packet interface?
jz read.1 # No
mov %bp,%si # Disk packet
movb $0x42,%ah # BIOS: Extended
int $0x13 # read
retw # To caller
read.1: push %dx # Save
movb $0x8,%ah # BIOS: Get drive
int $0x13 # parameters
movb %dh,%ch # Max head number
pop %dx # Restore
jc return # If error
andb $0x3f,%cl # Sectors per track
jz ereturn # If zero
cli # Disable interrupts
mov 0x8(%bp),%eax # Get LBA
push %dx # Save
movzbl %cl,%ebx # Divide by
xor %edx,%edx # sectors
div %ebx # per track
movb %ch,%bl # Max head number
movb %dl,%ch # Sector number
inc %bx # Divide by
xorb %dl,%dl # number
div %ebx # of heads
movb %dl,%bh # Head number
pop %dx # Restore
cmpl $0x3ff,%eax # Cylinder number supportable?
sti # Enable interrupts
ja ereturn # No
xchgb %al,%ah # Set up cylinder
rorb $0x2,%al # number
orb %ch,%al # Merge
inc %ax # sector
xchg %ax,%cx # number
movb %bh,%dh # Head number
subb %ah,%al # Sectors this track
mov 0x2(%bp),%ah # Blocks to read
cmpb %ah,%al # To read
jb read.2 # this
movb %ah,%al # track
read.2: mov $0x5,%di # Try count
read.3: les 0x4(%bp),%bx # Transfer buffer
push %ax # Save
movb $0x2,%ah # BIOS: Read
int $0x13 # from disk
pop %bx # Restore
jnc read.4 # If success
dec %di # Retry?
jz read.6 # No
xorb %ah,%ah # BIOS: Reset
int $0x13 # disk system
xchg %bx,%ax # Block count
jmp read.3 # Continue
read.4: movzbw %bl,%ax # Sectors read
add %ax,0x8(%bp) # Adjust
jnc read.5 # LBA,
incw 0xa(%bp) # transfer
read.5: shlb %bl # buffer
add %bl,0x5(%bp) # pointer,
sub %al,0x2(%bp) # block count
ja read.1 # If not done
read.6: retw # To caller
# Messages
msg_read: .asciz "Read"
msg_part: .asciz "Boot"
prompt: .asciz " error\r\n"
flags: .byte FLAGS # Flags
.org PRT_OFF,0x90
# Partition table
.fill 0x30,0x1,0x0
part4: .byte 0x80, 0x00, 0x01, 0x00
.byte 0xa5, 0xff, 0xff, 0xff
.byte 0x00, 0x00, 0x00, 0x00
.byte 0x50, 0xc3, 0x00, 0x00 # 50000 sectors long, bleh
.word 0xaa55 # Magic number
|