1 /* $NetBSD: pbr.S,v 1.20 2011/08/17 00:07:38 jakllsch Exp $ */
4 * Copyright (c) 2003,2004 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
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.
33 * i386 partition boot code
35 * This code resides in sector zero of the netbsd partition, or sector
36 * zero of an unpartitioned disk (eg a floppy).
37 * Sector 1 is assumed to contain the netbsd disklabel.
38 * Sectors 2 until the end of the track contain the next phase of bootstrap.
39 * Which know how to read the interactive 'boot' program from filestore.
40 * The job of this code is to read in the phase 1 bootstrap.
43 * PRIMARY_LOAD_ADDRESS: Address we load code to (0x1000).
44 * BOOTXX_SECTORS: Number of sectors we load (15).
45 * X86_BOOT_MAGIC_1: A random magic number.
47 * Although this code is executing at 0x7c00, it is linked to address 0x1000.
48 * All data references MUST be fixed up using R().
51 #include <machine/asm.h>
52 #include <sys/bootblock.h>
54 #define OURADDR 0x7c00 /* our address */
55 #define BOOTADDR PRIMARY_LOAD_ADDRESS
57 #define R(a) (a - BOOTADDR + OURADDR)
59 #define lba_info R(_lba_info)
60 #define lba_sector R(_lba_sector)
61 #define errtxt R(_errtxt)
62 #define errcod R(_errcod)
63 #define newline R(_newline)
65 #define TABENTRYSIZE (MBR_BS_PARTNAMESIZE + 1)
66 #define NAMETABSIZE (4 * TABENTRYSIZE)
69 #define MBR_AFTERBPB 90 /* BPB size in FAT32 partition BR */
71 #define MBR_AFTERBPB 62 /* BPB size in floppy master BR */
76 * Error codes. Done this way to save space.
78 #define ERR_READ '2' /* Read error */
79 #define ERR_NO_BOOTXX 'B' /* No bootxx_xfs in 3rd sector */
80 #define ERR_PTN 'P' /* partition not defined */
81 #define ERR_NO_LBA 'L' /* sector above chs limit */
83 #define set_err(err) movb $err, %al
86 #define set_err(err) mov $R(err), %ax
90 * This code is loaded to addresss 0:7c00 by either the system BIOS
91 * (for a floppy) or the mbr boot code. Since the boot program will
92 * be loaded to address 1000:0, we don't need to relocate ourselves
93 * and can load the subsequent blocks (that load boot) to an address
94 * of our choosing. 0:1000 is a not unreasonable choice.
96 * On entry the BIOS drive number is in %dl and %esi may contain the
97 * sector we were loaded from (if we were loaded by NetBSD mbr code).
98 * In any case we have to re-read sector zero of the disk and hunt
99 * through the BIOS partition table for the NetBSD partition.
101 * Or, we may have been loaded by a GPT hybrid MBR, handoff state is
102 * specified in T13 EDD-4 annex A.
109 * The PC BIOS architecture defines a Boot Parameter Block (BPB) here.
110 * The actual format varies between different MS-DOS versions, but
111 * apparently some system BIOS insist on patching this area
112 * (especially on LS120 drives - which I thought had an MBR...).
113 * The initial jmp and nop are part of the standard and may be
114 * tested for by the system BIOS.
118 .ascii "NetBSD60" /* oemname (8 bytes) */
120 . = start + MBR_BPB_OFFSET /* move to start of BPB */
121 /* (ensures oemname doesn't overflow) */
123 . = start + MBR_AFTERBPB /* skip BPB */
125 xor %cx, %cx /* don't trust values of ds, es or ss */
129 #ifndef BOOT_FROM_FAT
130 cmpl $0x54504721, %eax /* did a GPT hybrid MBR start us? */
136 /* A 'reset disk system' request is traditional here... */
137 push %dx /* some BIOS zap %dl here :-( */
138 int $0x13 /* ah == 0 from code above */
141 /* Read from start of disk */
142 incw %cx /* track zero sector 1 */
143 movb %ch, %dh /* dh = head = 0 */
146 /* See if this is our code, if so we have already loaded the next stage */
148 xorl %ebp, %ebp /* pass sector 0 to next stage */
149 movl (%bx), %eax /* MBR code shouldn't even have ... */
150 cmpl R(start), %eax /* ... a jmp at the start. */
153 /* Now scan the MBR partition table for a netbsd partition */
155 xorl %ebx, %ebx /* for base extended ptn chain */
157 xorl %ecx, %ecx /* for next extended ptn */
158 movw $BOOTADDR + MBR_PART_OFFSET, %di
159 1: movb 4(%di), %al /* mbrp_type */
160 movl 8(%di), %ebp /* mbrp_start == LBA sector */
161 addl lba_sector, %ebp /* add base of extended partition */
163 cmpb $MBR_PTYPE_FAT12, %al
165 cmpb $MBR_PTYPE_FAT16S, %al
167 cmpb $MBR_PTYPE_FAT16B, %al
169 cmpb $MBR_PTYPE_FAT32, %al
171 cmpb $MBR_PTYPE_FAT32L, %al
173 cmpb $MBR_PTYPE_FAT16L, %al
175 #elif BOOT_FROM_MINIXFS3
176 cmpb $MBR_PTYPE_MINIX_14B, %al
179 cmpb $MBR_PTYPE_NETBSD, %al
182 5: testl %esi, %esi /* looking for a specific sector? */
184 cmpl %ebp, %esi /* ptn we wanted? */
186 /* check for extended partition */
187 10: cmpb $MBR_PTYPE_EXT, %al
189 cmpb $MBR_PTYPE_EXT_LBA, %al
191 cmpb $MBR_PTYPE_EXT_LNX, %al
193 15: movl 8(%di), %ecx /* sector of next ext. ptn */
195 cmp $BOOTADDR + MBR_MAGIC_OFFSET, %di
198 /* not in base partitions, check extended ones */
202 xchgl %ebx, %ecx /* save base of ext ptn chain */
203 30: addl %ebx, %ecx /* address this ptn */
204 movl %ecx, lba_sector /* sector to read */
209 /* Specific sector not found: try again looking for first NetBSD ptn */
214 movl %esi, lba_sector
218 * Sector below CHS limit
219 * Do a cylinder-head-sector read instead
220 * I believe the BIOS should do reads that cross track boundaries.
221 * (but the read should start at the beginning of a track...)
224 movb 1(%di), %dh /* head */
225 movw 2(%di), %cx /* ch=cyl, cl=sect */
231 * Active partition pointed to by di.
233 * We can either do a CHS (Cylinder Head Sector) or an LBA (Logical
234 * Block Address) read. Always doing the LBA one
235 * would be nice - unfortunately not all systems support it.
236 * Also some may contain a separate (eg SCSI) BIOS that doesn't
237 * support it even when the main BIOS does.
239 * The safest thing seems to be to find out whether the sector we
240 * want is inside the CHS sector count. If it is we use CHS, if
241 * outside we use LBA.
243 * Actually we check that the CHS values reference the LBA sector,
244 * if not we assume that the LBA sector is above the limit, or that
245 * the geometry used (by fdisk) isn't correct.
248 movl %ebp, lba_sector /* to control block */
249 testl %ebx, %ebx /* was it an extended ptn? */
250 jnz boot_lba /* yes - boot with LBA reads */
252 /* get CHS values from BIOS */
253 push %dx /* save drive number */
255 int $0x13 /* chs info */
258 * Validate geometry, if the CHS sector number doesn't match the LBA one
259 * we'll do an LBA read.
260 * calc: (cylinder * number_of_heads + head) * number_of_sectors + sector
261 * and compare against LBA sector number.
262 * Take a slight 'flier' and assume we can just check 16bits (very likely
263 * to be true because the number of sectors per track is 63).
265 movw 2(%di), %ax /* cylinder + sector */
266 push %ax /* save for sector */
268 xchgb %al, %ah /* 10 bit cylinder number */
269 shr $8, %dx /* last head */
270 inc %dx /* number of heads */
272 mov 1(%di), %dl /* head we want */
274 and $0x3f, %cx /* number of sectors */
276 pop %dx /* recover sector we want */
280 pop %dx /* recover drive nmber */
290 * Determine whether we have int13-extensions, by calling
291 * int 13, function 41. Check for the magic number returned,
292 * and the disk packet capability.
294 * This is actually relatively pointless:
295 * 1) we only use LBA reads if CHS ones would fail
296 * 2) the MBR code managed to read the same sectors
297 * 3) the BIOS will (ok should) reject the LBA read as a bad BIOS call
302 jc 1f /* no int13 extensions */
307 1: set_err(ERR_NO_LBA)
308 #endif /* NO_LBA_CHECK */
311 * Something went wrong,
337 * Check magic number for valid stage 2 bootcode
341 cmpl $X86_BOOT_MAGIC_1, bootxx_magic
342 set_err(ERR_NO_BOOTXX)
345 movl %ebp, %esi /* %esi ptn base, %dl disk id */
346 movl lba_sector + 4, %edi /* %edi ptn base high */
347 jmp $0, $bootxx /* our %cs may not be zero */
349 /* Read disk using int13-extension parameter block */
352 movw $lba_info, %si /* ds:si is ctl block */
365 movw $BOOTADDR, %bx /* es:bx is buffer */
367 movw $0x200 + BOOTXX_SECTORS, %ax /* command 2, xx sectors */
370 #ifndef BOOT_FROM_FAT
372 movl (20+32+0)(%si), %ebp
373 movl (20+32+4)(%si), %edi
375 movl %ebp, lba_sector + 0
376 movl %edi, lba_sector + 4
381 _errtxt: .ascii "Error " /* runs into newline... */
382 _errcod: .byte 0 /* ... if errcod set */
387 ERR_READ: .asciz "read"
388 ERR_NO_BOOTXX: .asciz "no magic"
389 ERR_PTN: .asciz "no slice"
391 ERR_NO_LBA: .asciz "need LBA"
396 * I hate #including source files, but pbr_magic below has to be at
397 * the correct absolute address.
398 * Clearly this could be done with a linker script.
403 #include <dump_eax.S>
406 /* Control block for int-13 LBA read. */
408 .word 0x10 /* control block length */
409 .word BOOTXX_SECTORS /* sector count */
410 .word BOOTADDR /* offset in segment */
411 .word 0 /* segment */
413 .quad 0 /* sector # goes here... */
415 /* Drive Serial Number */
416 . = _C_LABEL(start) + MBR_DSN_OFFSET
419 /* mbr_bootsel_magic (not used here) */
420 . = _C_LABEL(start) + MBR_BS_MAGIC_OFFSET
424 * Provide empty MBR partition table.
425 * If this is installed as an MBR, the user can use fdisk(8) to create
426 * the correct partition table ...
428 . = _C_LABEL(start) + MBR_PART_OFFSET
430 .byte 0, 0, 0, 0, 0, 0, 0, 0
433 .byte 0, 0, 0, 0, 0, 0, 0, 0
436 .byte 0, 0, 0, 0, 0, 0, 0, 0
439 .byte 0, 0, 0, 0, 0, 0, 0, 0
443 * The magic comes last
445 . = _C_LABEL(start) + MBR_MAGIC_OFFSET