1/* $NetBSD: pbr.S,v 1.20 2011/08/17 00:07:38 jakllsch Exp $ */ 2 3/*- 4 * Copyright (c) 2003,2004 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 */ 31 32/* 33 * i386 partition boot code 34 * 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. 41 * 42 * Makefile supplies: 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. 46 * 47 * Although this code is executing at 0x7c00, it is linked to address 0x1000. 48 * All data references MUST be fixed up using R(). 49 */ 50 51#include <machine/asm.h> 52#include <sys/bootblock.h> 53 54#define OURADDR 0x7c00 /* our address */ 55#define BOOTADDR PRIMARY_LOAD_ADDRESS 56 57#define R(a) (a - BOOTADDR + OURADDR) 58 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) 64 65#define TABENTRYSIZE (MBR_BS_PARTNAMESIZE + 1) 66#define NAMETABSIZE (4 * TABENTRYSIZE) 67 68#ifdef BOOT_FROM_FAT 69#define MBR_AFTERBPB 90 /* BPB size in FAT32 partition BR */ 70#else 71#define MBR_AFTERBPB 62 /* BPB size in floppy master BR */ 72#endif 73 74#ifdef TERSE_ERROR 75/* 76 * Error codes. Done this way to save space. 77 */ 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 */ 82 83#define set_err(err) movb $err, %al 84 85#else 86#define set_err(err) mov $R(err), %ax 87#endif 88 89/* 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. 95 * 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. 100 * 101 * Or, we may have been loaded by a GPT hybrid MBR, handoff state is 102 * specified in T13 EDD-4 annex A. 103 */ 104 105 .text 106 .code16 107ENTRY(start) 108 /* 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. 115 */ 116 jmp start0 117 nop 118 .ascii "NetBSD60" /* oemname (8 bytes) */ 119 120 . = start + MBR_BPB_OFFSET /* move to start of BPB */ 121 /* (ensures oemname doesn't overflow) */ 122 123 . = start + MBR_AFTERBPB /* skip BPB */ 124start0: 125 xor %cx, %cx /* don't trust values of ds, es or ss */ 126 mov %cx, %ss 127 mov %cx, %sp 128 mov %cx, %es 129#ifndef BOOT_FROM_FAT 130 cmpl $0x54504721, %eax /* did a GPT hybrid MBR start us? */ 131 je boot_gpt 132#endif 133 mov %cx, %ds 134 xor %ax, %ax 135 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 */ 139 pop %dx 140 141 /* Read from start of disk */ 142 incw %cx /* track zero sector 1 */ 143 movb %ch, %dh /* dh = head = 0 */ 144 call chs_read 145 146/* See if this is our code, if so we have already loaded the next stage */ 147 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. */ 151 je pbr_read_ok1 152 153/* Now scan the MBR partition table for a netbsd partition */ 154 155 xorl %ebx, %ebx /* for base extended ptn chain */ 156scan_ptn_tbl: 157 xorl %ecx, %ecx /* for next extended ptn */ 158 movw $BOOTADDR + MBR_PART_OFFSET, %di 1591: 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 */ 162#ifdef BOOT_FROM_FAT 163 cmpb $MBR_PTYPE_FAT12, %al 164 je 5f 165 cmpb $MBR_PTYPE_FAT16S, %al 166 je 5f 167 cmpb $MBR_PTYPE_FAT16B, %al 168 je 5f 169 cmpb $MBR_PTYPE_FAT32, %al 170 je 5f 171 cmpb $MBR_PTYPE_FAT32L, %al 172 je 5f 173 cmpb $MBR_PTYPE_FAT16L, %al 174 je 5f 175#elif BOOT_FROM_MINIXFS3 176 cmpb $MBR_PTYPE_MINIX_14B, %al 177 je 5f 178#else 179 cmpb $MBR_PTYPE_NETBSD, %al 180#endif 181 jne 10f 1825: testl %esi, %esi /* looking for a specific sector? */ 183 je boot 184 cmpl %ebp, %esi /* ptn we wanted? */ 185 je boot 186 /* check for extended partition */ 18710: cmpb $MBR_PTYPE_EXT, %al 188 je 15f 189 cmpb $MBR_PTYPE_EXT_LBA, %al 190 je 15f 191 cmpb $MBR_PTYPE_EXT_LNX, %al 192 jne 20f 19315: movl 8(%di), %ecx /* sector of next ext. ptn */ 19420: add $0x10, %di 195 cmp $BOOTADDR + MBR_MAGIC_OFFSET, %di 196 jne 1b 197 198 /* not in base partitions, check extended ones */ 199 jecxz no_netbsd_ptn 200 testl %ebx, %ebx 201 jne 30f 202 xchgl %ebx, %ecx /* save base of ext ptn chain */ 20330: addl %ebx, %ecx /* address this ptn */ 204 movl %ecx, lba_sector /* sector to read */ 205 call read_lba 206 jmp scan_ptn_tbl 207 208no_netbsd_ptn: 209 /* Specific sector not found: try again looking for first NetBSD ptn */ 210 testl %esi, %esi 211 set_err(ERR_PTN) 212 jz error 213 xorl %esi, %esi 214 movl %esi, lba_sector 215 jmp start 216 217/* 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...) 222 */ 223read_chs: 224 movb 1(%di), %dh /* head */ 225 movw 2(%di), %cx /* ch=cyl, cl=sect */ 226 call chs_read 227pbr_read_ok1: 228 jmp pbr_read_ok 229 230/* 231 * Active partition pointed to by di. 232 * 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. 238 * 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. 242 * 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. 246 */ 247boot: 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 */ 251 252/* get CHS values from BIOS */ 253 push %dx /* save drive number */ 254 movb $8, %ah 255 int $0x13 /* chs info */ 256 257/* 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). 264 */ 265 movw 2(%di), %ax /* cylinder + sector */ 266 push %ax /* save for sector */ 267 shr $6, %al 268 xchgb %al, %ah /* 10 bit cylinder number */ 269 shr $8, %dx /* last head */ 270 inc %dx /* number of heads */ 271 mul %dx 272 mov 1(%di), %dl /* head we want */ 273 add %dx, %ax 274 and $0x3f, %cx /* number of sectors */ 275 mul %cx 276 pop %dx /* recover sector we want */ 277 and $0x3f, %dx 278 add %dx, %ax 279 dec %ax 280 pop %dx /* recover drive nmber */ 281 282 cmp %bp, %ax 283 je read_chs 284 285check_lba: 286#ifdef NO_LBA_CHECK 287 jmp boot_lba 288#else 289/* 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. 293 * 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 298 */ 299 movw $0x55aa, %bx 300 movb $0x41, %ah 301 int $0x13 302 jc 1f /* no int13 extensions */ 303 cmpw $0xaa55, %bx 304 jnz 1f 305 testb $1, %cl 306 jnz boot_lba 3071: set_err(ERR_NO_LBA) 308#endif /* NO_LBA_CHECK */ 309 310/* 311 * Something went wrong, 312 * Output error code, 313 */ 314 315error: 316#ifdef TERSE_ERROR 317 movb %al, errcod 318 movw $errtxt, %si 319 call message 320#else 321 push %ax 322 movw $errtxt, %si 323 call message 324 pop %si 325 call message 326 movw $newline, %si 327 call message 328#endif 3291: sti 330 hlt 331 jmp 1b 332 333boot_lba: 334 call read_lba 335 336/* 337 * Check magic number for valid stage 2 bootcode 338 * then jump into it. 339 */ 340pbr_read_ok: 341 cmpl $X86_BOOT_MAGIC_1, bootxx_magic 342 set_err(ERR_NO_BOOTXX) 343 jnz error 344 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 */ 348 349/* Read disk using int13-extension parameter block */ 350read_lba: 351 pusha 352 movw $lba_info, %si /* ds:si is ctl block */ 353 movb $0x42, %ah 354do_read: 355 int $0x13 356 popa 357 358 set_err(ERR_READ) 359 jc error 360 ret 361 362/* Read using CHS */ 363 364chs_read: 365 movw $BOOTADDR, %bx /* es:bx is buffer */ 366 pusha 367 movw $0x200 + BOOTXX_SECTORS, %ax /* command 2, xx sectors */ 368 jmp do_read 369 370#ifndef BOOT_FROM_FAT 371boot_gpt: 372 movl (20+32+0)(%si), %ebp 373 movl (20+32+4)(%si), %edi 374 movw %cx, %ds 375 movl %ebp, lba_sector + 0 376 movl %edi, lba_sector + 4 377 movl %ebp, %esi 378 jmp boot_lba 379#endif 380 381_errtxt: .ascii "Error " /* runs into newline... */ 382_errcod: .byte 0 /* ... if errcod set */ 383_newline: 384 .asciz "\r\n" 385 386#ifndef TERSE_ERROR 387ERR_READ: .asciz "read" 388ERR_NO_BOOTXX: .asciz "no magic" 389ERR_PTN: .asciz "no slice" 390#ifndef NO_LBA_CHECK 391ERR_NO_LBA: .asciz "need LBA" 392#endif 393#endif 394 395/* 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. 399 */ 400 401#include <message.S> 402#if 0 403#include <dump_eax.S> 404#endif 405 406/* Control block for int-13 LBA read. */ 407_lba_info: 408 .word 0x10 /* control block length */ 409 .word BOOTXX_SECTORS /* sector count */ 410 .word BOOTADDR /* offset in segment */ 411 .word 0 /* segment */ 412_lba_sector: 413 .quad 0 /* sector # goes here... */ 414 415/* Drive Serial Number */ 416 . = _C_LABEL(start) + MBR_DSN_OFFSET 417 .long 0 418 419/* mbr_bootsel_magic (not used here) */ 420 . = _C_LABEL(start) + MBR_BS_MAGIC_OFFSET 421 .word 0 422 423/* 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 ... 427 */ 428 . = _C_LABEL(start) + MBR_PART_OFFSET 429_pbr_part0: 430 .byte 0, 0, 0, 0, 0, 0, 0, 0 431 .long 0, 0 432_pbr_part1: 433 .byte 0, 0, 0, 0, 0, 0, 0, 0 434 .long 0, 0 435_pbr_part2: 436 .byte 0, 0, 0, 0, 0, 0, 0, 0 437 .long 0, 0 438_pbr_part3: 439 .byte 0, 0, 0, 0, 0, 0, 0, 0 440 .long 0, 0 441 442/* 443 * The magic comes last 444 */ 445 . = _C_LABEL(start) + MBR_MAGIC_OFFSET 446pbr_magic: 447 .word MBR_MAGIC 448