]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/boot/i386/zfsboot/zfsldr.S
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / boot / i386 / zfsboot / zfsldr.S
1 /*
2  * Copyright (c) 1998 Robert Nordier
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are freely
6  * permitted provided that the above copyright notice and this
7  * paragraph and the following disclaimer are duplicated in all
8  * such forms.
9  *
10  * This software is provided "AS IS" and without any express or
11  * implied warranties, including, without limitation, the implied
12  * warranties of merchantability and fitness for a particular
13  * purpose.
14  *
15  * $FreeBSD$
16  */
17
18 /* Memory Locations */
19                 .set MEM_ARG,0x900              # Arguments
20                 .set MEM_ORG,0x7c00             # Origin
21                 .set MEM_BUF,0x8000             # Load area
22                 .set MEM_BTX,0x9000             # BTX start
23                 .set MEM_JMP,0x9010             # BTX entry point
24                 .set MEM_USR,0xa000             # Client start
25                 .set BDA_BOOT,0x472             # Boot howto flag
26         
27 /* Partition Constants */
28                 .set PRT_OFF,0x1be              # Partition offset
29                 .set PRT_NUM,0x4                # Partitions
30                 .set PRT_BSD,0xa5               # Partition type
31
32 /* Misc. Constants */
33                 .set SIZ_PAG,0x1000             # Page size
34                 .set SIZ_SEC,0x200              # Sector size
35
36                 .set NSECT,0x80
37                 .globl start
38                 .code16
39
40 /*
41  * Load the rest of zfsboot2 and BTX up, copy the parts to the right locations,
42  * and start it all up.
43  */
44
45 /*
46  * Setup the segment registers to flat addressing (segment 0) and setup the
47  * stack to end just below the start of our code.
48  */
49 start:          cld                             # String ops inc
50                 xor %cx,%cx                     # Zero
51                 mov %cx,%es                     # Address
52                 mov %cx,%ds                     #  data
53                 mov %cx,%ss                     # Set up
54                 mov $start,%sp                  #  stack
55 /*
56  * Load the MBR and look for the first FreeBSD slice.  We use the fake
57  * partition entry below that points to the MBR when we call read.
58  * The first pass looks for the first active FreeBSD slice.  The
59  * second pass looks for the first non-active FreeBSD slice if the
60  * first one fails.
61  */
62                 call check_edd                  # Make sure EDD works
63                 mov $part4,%si                  # Dummy partition
64                 xor %eax,%eax                   # Read MBR
65                 movl $MEM_BUF,%ebx              #  from first
66                 call read                       #  sector
67                 mov $0x1,%cx                    # Two passes
68 main.1:         mov $MEM_BUF+PRT_OFF,%si        # Partition table
69                 movb $0x1,%dh                   # Partition
70 main.2:         cmpb $PRT_BSD,0x4(%si)          # Our partition type?
71                 jne main.3                      # No
72                 jcxz main.5                     # If second pass
73                 testb $0x80,(%si)               # Active?
74                 jnz main.5                      # Yes
75 main.3:         add $0x10,%si                   # Next entry
76                 incb %dh                        # Partition
77                 cmpb $0x1+PRT_NUM,%dh           # In table?
78                 jb main.2                       # Yes
79                 dec %cx                         # Do two
80                 jcxz main.1                     #  passes
81 /*
82  * If we get here, we didn't find any FreeBSD slices at all, so print an
83  * error message and die.
84  */
85                 mov $msg_part,%si               # Message
86                 jmp error                       # Error
87
88 /*
89  * Ok, we have a slice and drive in %dx now, so use that to locate and
90  * load boot2.  %si references the start of the slice we are looking
91  * for, so go ahead and load up the 128 sectors starting at sector 1024
92  * (i.e. after the two vdev labels).  We don't have do anything fancy
93  * here to allow for an extra copy of boot1 and a partition table
94  * (compare to this section of the UFS bootstrap) so we just load it
95  * all at 0x9000. The first part of boot2 is BTX, which wants to run
96  * at 0x9000. The boot2.bin binary starts right after the end of BTX,
97  * so we have to figure out where the start of it is and then move the
98  * binary to 0xc000.  Normally, BTX clients start at MEM_USR, or 0xa000,
99  * but when we use btxld to create zfsboot2, we use an entry point of
100  * 0x2000.  That entry point is relative to MEM_USR; thus boot2.bin
101  * starts at 0xc000.
102  *
103  * The load area and the target area for the client overlap so we have
104  * to use a decrementing string move. We also play segment register
105  * games with the destination address for the move so that the client
106  * can be larger than 16k (which would overflow the zero segment since
107  * the client starts at 0xc000).
108  */
109 main.5:         mov %dx,MEM_ARG                 # Save args
110                 mov $NSECT,%cx                  # Sector count
111                 movl $1024,%eax                 # Offset to boot2
112                 mov $MEM_BTX,%ebx               # Destination buffer
113 main.6:         pushal                          # Save params
114                 call read                       # Read disk
115                 popal                           # Restore
116                 incl %eax                       # Advance to
117                 add $SIZ_SEC,%ebx               #  next sector
118                 loop main.6                     # If not last, read another
119                 mov MEM_BTX+0xa,%bx             # Get BTX length
120                 mov $NSECT*SIZ_SEC-1,%di        # Size of load area (less one)
121                 mov %di,%si                     # End of load area, 0x9000 rel
122                 sub %bx,%di                     # End of client, 0xc000 rel
123                 mov %di,%cx                     # Size of
124                 inc %cx                         #  client
125                 mov $(MEM_BTX)>>4,%dx           # Segment
126                 mov %dx,%ds                     #   addressing 0x9000
127                 mov $(MEM_USR+2*SIZ_PAG)>>4,%dx # Segment
128                 mov %dx,%es                     #   addressing 0xc000
129                 std                             # Move with decrement
130                 rep                             # Relocate
131                 movsb                           #  client
132                 cld                             # Back to increment
133                 xor %dx,%dx                     # Back
134                 mov %ds,%dx                     #  to zero
135                 mov %dx,%es                     #  segment
136
137 /*
138  * Enable A20 so we can access memory above 1 meg.
139  * Use the zero-valued %cx as a timeout for embedded hardware which do not
140  * have a keyboard controller.
141  */
142 seta20:         cli                             # Disable interrupts
143 seta20.1:       dec %cx                         # Timeout?
144                 jz seta20.3                     # Yes
145                 inb $0x64,%al                   # Get status
146                 testb $0x2,%al                  # Busy?
147                 jnz seta20.1                    # Yes
148                 movb $0xd1,%al                  # Command: Write
149                 outb %al,$0x64                  #  output port
150 seta20.2:       inb $0x64,%al                   # Get status
151                 testb $0x2,%al                  # Busy?
152                 jnz seta20.2                    # Yes
153                 movb $0xdf,%al                  # Enable
154                 outb %al,$0x60                  #  A20
155 seta20.3:       sti                             # Enable interrupts
156
157                 jmp start+MEM_JMP-MEM_ORG       # Start BTX
158
159
160 /*
161  * Read a sector from the disk.  Sets up an EDD packet on the stack
162  * and passes it to read.  We assume that the destination address is
163  * always segment-aligned.
164  *
165  * %eax         - int     - LBA to read in relative to partition start
166  * %ebx         - ptr     - destination address
167  * %dl          - byte    - drive to read from
168  * %si          - ptr     - MBR partition entry
169  */
170 read:           xor %ecx,%ecx                   # Get
171                 addl 0x8(%si),%eax              #  LBA
172                 adc $0,%ecx
173                 pushl %ecx                      # Starting absolute block
174                 pushl %eax                      #  block number
175                 shr $4,%ebx                     # Convert to segment
176                 push %bx                        # Address of
177                 push $0                         #  transfer buffer
178                 push $0x1                       # Read 1 sector
179                 push $0x10                      # Size of packet
180                 mov %sp,%si                     # Packet pointer
181                 mov $0x42,%ah                   # BIOS: Extended
182                 int $0x13                       #  read
183                 jc read.1                       # If error, fail
184                 lea 0x10(%si),%sp               # Clear stack
185                 ret                             # If success, return
186 read.1:         mov %ah,%al                     # Format
187                 mov $read_err,%di               #  error
188                 call hex8                       #  code
189                 mov $msg_read,%si               # Set the error message and
190                                                 #  fall through to the error
191                                                 #  routine
192 /*
193  * Print out the error message pointed to by %ds:(%si) followed
194  * by a prompt, wait for a keypress, and then reboot the machine.
195  */
196 error:          callw putstr                    # Display message
197                 mov $prompt,%si                 # Display
198                 callw putstr                    #  prompt
199                 xorb %ah,%ah                    # BIOS: Get
200                 int $0x16                       #  keypress
201                 movw $0x1234, BDA_BOOT          # Do a warm boot
202                 ljmp $0xffff,$0x0               # reboot the machine
203 /*
204  * Display a null-terminated string using the BIOS output.
205  */
206 putstr.0:       mov $0x7,%bx                    # Page:attribute
207                 movb $0xe,%ah                   # BIOS: Display
208                 int $0x10                       #  character
209 putstr:         lodsb                           # Get char
210                 testb %al,%al                   # End of string?
211                 jne putstr.0                    # No
212                 ret                             # To caller
213 /*
214  * Check to see if the disk supports EDD.  zfsboot requires EDD and does not
215  * support older C/H/S disk I/O.
216  */
217 check_edd:      cmpb $0x80,%dl                  # Hard drive?
218                 jb check_edd.1                  # No, fail to boot
219                 mov $0x55aa,%bx                 # Magic
220                 push %dx                        # Save
221                 movb $0x41,%ah                  # BIOS: Check
222                 int $0x13                       #  extensions present
223                 pop %dx                         # Restore
224                 jc check_edd.1                  # If error, fail
225                 cmp $0xaa55,%bx                 # Magic?
226                 jne check_edd.1                 # No, so fail
227                 testb $0x1,%cl                  # Packet interface?
228                 jz check_edd.1                  # No, so fail
229                 ret                             # EDD ok, keep booting
230 check_edd.1:    mov $msg_chs,%si                # Warn that CHS is
231                 jmp error                       #  unsupported and fail
232 /*
233  * AL to hex, saving the result to [EDI].
234  */
235 hex8:           push %ax                        # Save
236                 shrb $0x4,%al                   # Do upper
237                 call hex8.1                     #  4
238                 pop %ax                         # Restore
239 hex8.1:         andb $0xf,%al                   # Get lower 4
240                 cmpb $0xa,%al                   # Convert
241                 sbbb $0x69,%al                  #  to hex
242                 das                             #  digit
243                 orb $0x20,%al                   # To lower case
244                 stosb                           # Save char
245                 ret                             # (Recursive)
246
247 /* Messages */
248
249 msg_chs:        .asciz "CHS not supported"
250 msg_read:       .ascii "Read error: "
251 read_err:       .asciz "XX"
252 msg_part:       .asciz "Boot error"
253
254 prompt:         .asciz "\r\n"
255
256                 .org PRT_OFF,0x90
257
258 /* Partition table */
259
260                 .fill 0x30,0x1,0x0
261 part4:          .byte 0x80, 0x00, 0x01, 0x00
262                 .byte 0xa5, 0xfe, 0xff, 0xff
263                 .byte 0x00, 0x00, 0x00, 0x00
264                 .byte 0x50, 0xc3, 0x00, 0x00    # 50000 sectors long, bleh
265
266                 .word 0xaa55                    # Magic number