fatboot.S revision 1.2 1 /* $NetBSD: fatboot.S,v 1.2 2007/01/06 20:47:15 dsl Exp $ */
2
3 /*-
4 * Copyright (c) 2007 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by David Laight.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of The NetBSD Foundation nor the names of its
19 * contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
26 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 */
34
35 /*
36 * i386 partition boot code
37 * This version reads boot directly from a FAT16 filesystem on LBA
38 * Addressable media - eg USB media.
39 *
40 * The code is read to address 0:7c00 by the mbr code (in sector zero).
41 *
42 * We assume that the partition contains a 'boot parameter block' that
43 * correctly identifies the filesystem.
44 *
45 * On entry and exit the BIOS drive number is in %dl.
46 */
47
48 #include <machine/asm.h>
49 #include <sys/bootblock.h>
50
51 #ifndef FAT_ENTRY_SIZE
52 #error FAT_ENTRY_SIZE not defined
53 #endif
54
55 /* Support for FAT32 could be added - but hasn't been yet. */
56 #if FAT_ENTRY_SIZE != 16
57 #error Unsupported FAT_ENTRY_SIZE value
58 #endif
59
60 #define PBR_AFTERBPB 62 /* BPB size in floppy master BR */
61
62 #ifdef TERSE_ERROR
63 /*
64 * Error codes. Done this way to save space.
65 */
66 #define ERR_READ 'R' /* Read error */
67 #define ERR_NO_BOOT 'B' /* No /boot */
68 #define ERR_NOT_FAT16 'F' /* Not a FAT16 filesystem */
69 #define ERR_NO_BOOT_MAGIC_2 'M' /* No magic in loaded /boot */
70
71 #define set_err(err) movb $err, %al
72
73 #else
74 #define set_err(err) mov $err, %ax
75 #endif
76
77 .text
78 .code16
79 ENTRY(start)
80 jmp start0
81 nop
82 oem_name: .ascii "NetBSD40" /* 8 bytes */
83 /* FAT16 BIOS/BOOT Parameter Block - see struct mbr_bpbFAT16 in bootblock.h */
84 #define bpb_bytes_per_sec /* .word 0 */ 0x0b /* bytes per sector */
85 #define bpb_sec_per_clust /* .byte 0 */ 0x0d /* sectors per cluster */
86 #define bpb_res_sectors /* .word 0 */ 0x0e /* number of reserved sectors */
87 #define bpb_FATs /* .byte 0 */ 0x10 /* number of FATs */
88 #define bpb_root_dir_ents /* .word 0 */ 0x11 /* number of root dir entries */
89 #define bpb_sectors /* .word 0 */ 0x13 /* total number of sectors */
90 #define bpb_media /* .byte 0 */ 0x15 /* media descriptor */
91 #define bpb_FAT_secs /* .word 0 */ 0x16 /* number of sectors per FAT */
92 #define bpb_sec_per_track /* .word 0 */ 0x18 /* sectors per track */
93 #define bpb_heads /* .word 0 */ 0x1a /* number of heads */
94 #define bpb_hidden_secs /* .long 0 */ 0x1c /* # of hidden sectors */
95 #define bpb_huge_sectors /* .long 0 */ 0x20 /* # of sectors if !bpbSectors*/
96 /* Extended boot area */
97 #define bs_drive_number /* .byte 0 */ 0x24 /* but we believe the BIOS ! */
98 #define bs_reserved_1 /* .byte 0 */ 0x25 /* */
99 #define bs_boot_sig /* .byte 0 */ 0x26 /* */
100 #define bs_volume_id /* .long 0 */ 0x27 /* Volume ID number */
101 #define bs_volume_label /* .space 11*/ 0x2b /* Volume label */
102 #define bs_file_sys_type /* .space 8 */ 0x36 /* "FAT16 " */
103
104 /* Some locals overlaying the end of the above */
105 #define sec_p_cl_w 0x2c /* 16bit bpb_sec_per_clust */
106 #define fat_sector 0x30 /* start of FAT in sectors */
107
108 . = start + PBR_AFTERBPB /* skip BPB */
109 start0:
110 xor %eax, %eax /* don't trust values of ds, es or ss */
111 mov %ax, %ds
112 mov %ax, %es
113 mov %ax, %ss
114 mov $start, %sp
115 mov %sp, %bp /* to access the pbp */
116 push %dx /* save drive at -2(%bp) */
117
118 set_err(ERR_NOT_FAT16)
119 cmpl $'A'|'T'<<8|'1'<<16|'6'<<24, bs_file_sys_type+1(%bp)
120 jne error
121
122 /* Add 'reserved' (inside ptn) to 'hidden' (ptn offset) */
123 mov bpb_res_sectors(%bp), %ax
124 addl bpb_hidden_secs(%bp), %eax
125 mov %eax, fat_sector(%bp) /* To get first sector of FAT */
126
127 /* Determine base of root directory */
128 movzbw bpb_FATs(%bp), %ax /* Count of FATs */
129 mulw bpb_FAT_secs(%bp) /* FAT size in %dx:%ax */
130 shl $16,%edx
131 xchg %ax,%dx /* FAT size now in %edx */
132 addl fat_sector(%bp), %edx /* Directory is after FATs */
133 push %cs /* %cs is zero */
134 push %cs /* 64-bit for LBA read */
135 pushl %edx /* Sector number of root dir */
136
137 push $0x1000 /* Read to 0x10000:0 */
138 pop %es /* Which we need in %es later */
139 push %es
140 push %cs /* Offset zero */
141
142 /* Convert the root directory size to sectors */
143 push %dx
144 mov bpb_root_dir_ents(%bp), %ax
145 mov $0x20, %dx
146 mul %dx
147 divw bpb_bytes_per_sec(%bp)
148 add $0xffff, %dx /* Set carry if remainder non-zero */
149 adc $0, %ax /* and round up the division */
150 pop %dx
151
152 /* Read in the entire root directory */
153 push %ax /* Sectors in root directory */
154 cwtl
155 addl %eax, %edx /* %edx now sector of first file */
156 call read_lba /* Read entire directory */
157
158 /* Scan directory for our file */
159 xor %di, %di
160 scan_dir:
161 mov $boot_filename, %si
162 mov $11, %cx
163 repz cmpsb
164 je found_boot
165 or $31,%di
166 inc %di
167 cmp %ch, %es:(%di) /* %ch is zero - test end of dir */
168 jz 1f
169 decw bpb_root_dir_ents(%bp)
170 jnz scan_dir
171 1: set_err(ERR_NO_BOOT)
172
173 error:
174 #ifdef TERSE_ERROR
175 movb %al, errcod
176 movw $errtxt, %si
177 call message
178 #else
179 push %ax
180 movw $errtxt, %si
181 call message
182 pop %si
183 call message
184 movw $newline, %si
185 call message
186 #endif
187 1: sti
188 hlt
189 jmp 1b
190
191 found_boot:
192 movzbl bpb_sec_per_clust(%bp), %eax
193 movw %ax, sec_p_cl_w(%bp)
194 add %ax, %ax
195 subl %eax, %edx /* 1st file sector is cluster 2 */
196 1: inc %cl /* Convert power of 2 ... */
197 shr $1, %ax /* ... to shift */
198 jnz 1b
199 dec %cx
200 dec %cx
201 movw %es:15(%di), %ax /* Cluster number for file start */
202 push %es /* We increment the 'segment' ... */
203 pop %di /* ... after each read, offset is 0 */
204
205 read_data_block:
206 mov %ax, %bx /* Save cluster number */
207 shl %cl, %eax /* Convert to sector number */
208 add %eax, %edx
209 pushl %edx /* Sector to read */
210 sub %eax, %edx /* Recover base segment */
211 push %di /* Target address segment! */
212 push $0
213 push sec_p_cl_w(%bp)
214 call read_lba /* Read a cluster */
215
216 /* Update read ptr for next cluster */
217 mov bpb_bytes_per_sec(%bp), %ax
218 shr $4, %ax /* x86 segment count */
219 shl %cl, %ax /* for a cluster */
220 add %ax, %di
221
222 /* Lookup FAT slot number in FAT table */
223 mov %bx, %ax /* Recover cluster number */
224 push %dx
225 xor %dx, %dx
226 divw bpb_bytes_per_sec(%bp)
227 mov %dx, %bx /* Entry in FAT block */
228 pop %dx
229 cmp %ax, fat_cache
230 je lookup_fat
231
232 /* We must read a different chuck of the FAT */
233 mov %ax, fat_cache
234 cwtl
235 shl $1, %ax
236 addl fat_sector(%bp), %eax
237 push %eax
238 push %ds
239 push $fat_buffer
240 push $2 /* Always read 2 sectors of FAT */
241 call read_lba
242
243 /* Now use low part of cluster number to index FAT sector */
244 lookup_fat:
245 add %bx, %bx /* 2 bytes per entry... */
246 movzwl fat_buffer(%bx), %eax /* Next FAT slot */
247 cmp $0xfff0, %ax
248 jb read_data_block
249
250 /* Found end of FAT chain - must be EOF - leap into loaded code */
251 mov $0x1000, %ax
252 mov %ax, %es
253 cmpl $X86_BOOT_MAGIC_2, %es:4
254 je magic_ok
255 set_err(ERR_NO_BOOT_MAGIC_2)
256 err1: jmp error
257
258 /* Set parameters expected by /boot */
259 magic_ok:
260 mov bpb_hidden_secs(%bp), %ebx /* ptn base sector */
261 movb -2(%bp), %dl /* disk number (could pop %dx) */
262 mov $boot_params + 4, %si
263 push %es
264 push $0
265 lret
266
267 /* Read disk using on-stack int13-extension parameter block */
268 read_lba:
269 pop %ax /* Save rtn addr */
270 pushw $16 /* Stack ctl block length */
271 mov %sp, %si /* Address ctl block */
272 push %ax /* restack rtn addr */
273 pushal /* Save everything except %si and %ax*/
274 mov -2(%bp), %dl /* Disk # saved on entry */
275 movb $0x42, %ah
276 int $0x13
277 popal
278
279 set_err(ERR_READ)
280 jc err1
281 ret $12 /* Discard all except high LBA zeros */
282
283 /*
284 * I hate #including source files, but pbr_magic below has to be at
285 * the correct absolute address.
286 * Clearly this could be done with a linker script.
287 */
288
289 #include <message.S>
290 #if 0
291 #include <dump_eax.S>
292 #endif
293
294 errtxt: .ascii "Error " /* runs into newline... */
295 errcod: .byte 0 /* ... if errcod set */
296 newline:
297 .asciz "\r\n"
298
299 #ifndef TERSE_ERROR
300 ERR_READ: .asciz "Disk read"
301 ERR_NO_BOOT: .asciz "No /boot"
302 ERR_NOT_FAT16: .asciz "Not FAT16 ptn"
303 ERR_NO_BOOT_MAGIC_2: .asciz "No magic in /boot"
304 #endif
305
306 boot_filename: .ascii "BOOT "
307
308 space:
309 pbr_space = boot_params - .
310
311 /*
312 * Add magic number, with a zero sized patchable area - just in case something
313 * finds it and tries to update the area.
314 * Boot options can be set using 'installboot -e boot' so we don't need to
315 * use any of our valuable bytes.
316 */
317
318 . = _C_LABEL(start) + 0x1fe - 2 - 4 - 4
319 boot_params:
320 .long X86_BOOT_MAGIC_FAT
321 .long 1f - .
322 1:
323 fat_cache: .word 0xffff /* Sector number in buffer */
324 . = _C_LABEL(start) + 0x1fe
325 .word 0xaa55
326 fat_buffer: /* 2 sectors worth of FAT table */
327