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