1/* $OpenBSD: dkcsum.c,v 1.21 2014/09/14 14:17:23 jsg Exp $ */ 2 3/*- 4 * Copyright (c) 1997 Niklas Hallqvist. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27/* 28 * A checksumming pseudo device used to get unique labels of each disk 29 * that needs to be matched to BIOS disks. 30 */ 31 32#include <sys/param.h> 33#include <sys/buf.h> 34#include <sys/conf.h> 35#include <sys/device.h> 36#include <sys/disklabel.h> 37#include <sys/fcntl.h> 38#include <sys/reboot.h> 39#include <sys/stat.h> 40#include <sys/systm.h> 41 42#include <machine/biosvar.h> 43 44#include <lib/libz/zlib.h> 45 46dev_t dev_rawpart(struct device *); /* XXX */ 47 48extern u_int32_t bios_cksumlen; 49extern bios_diskinfo_t *bios_diskinfo; 50extern dev_t bootdev; 51 52void 53dkcsumattach(void) 54{ 55 struct device *dv; 56 struct buf *bp; 57 struct bdevsw *bdsw; 58 dev_t dev, pribootdev, altbootdev; 59 int error, picked; 60 u_int32_t csum; 61 bios_diskinfo_t *bdi, *hit; 62 63 /* do nothing if no diskinfo passed from /boot, or a bad length */ 64 if (bios_diskinfo == NULL || bios_cksumlen * DEV_BSIZE > MAXBSIZE) 65 return; 66 67 /* Do nothing if bootdev is a CD drive. */ 68 if (B_TYPE(bootdev) == 6) 69 return; 70 71#ifdef DEBUG 72 printf("dkcsum: bootdev=%#x\n", bootdev); 73 for (bdi = bios_diskinfo; bdi->bios_number != -1; bdi++) { 74 if (bdi->bios_number & 0x80) { 75 printf("dkcsum: BIOS drive %#x bsd_dev=%#x " 76 "checksum=%#x\n", bdi->bios_number, bdi->bsd_dev, 77 bdi->checksum); 78 } 79 } 80#endif 81 pribootdev = altbootdev = 0; 82 83 /* 84 * XXX What if DEV_BSIZE is changed to something else than the BIOS 85 * blocksize? Today, /boot doesn't cover that case so neither need 86 * I care here. 87 */ 88 bp = geteblk(bios_cksumlen * DEV_BSIZE); /* XXX error check? */ 89 90 TAILQ_FOREACH(dv, &alldevs, dv_list) { 91 if (dv->dv_class != DV_DISK) 92 continue; 93 bp->b_dev = dev = dev_rawpart(dv); 94 if (dev == NODEV) 95 continue; 96 bdsw = &bdevsw[major(dev)]; 97 98 /* 99 * This open operation guarantees a proper initialization 100 * of the device, for future strategy calls. 101 */ 102 error = (*bdsw->d_open)(dev, FREAD, S_IFCHR, curproc); 103 if (error) { 104 /* XXX What to do here? */ 105#ifdef DEBUG 106 printf("dkcsum: %s open failed (%d)\n", 107 dv->dv_xname, error); 108#endif 109 continue; 110 } 111 112 /* Read blocks to cksum. XXX maybe a d_read should be used. */ 113 bp->b_blkno = 0; 114 bp->b_bcount = bios_cksumlen * DEV_BSIZE; 115 bp->b_error = 0; /* B_ERROR and b_error may have stale data. */ 116 CLR(bp->b_flags, B_READ | B_WRITE | B_DONE | B_ERROR); 117 SET(bp->b_flags, B_BUSY | B_READ | B_RAW); 118 (*bdsw->d_strategy)(bp); 119 if ((error = biowait(bp))) { 120 /* XXX What to do here? */ 121#ifdef DEBUG 122 printf("dkcsum: %s read failed (%d)\n", 123 dv->dv_xname, error); 124#endif 125 error = (*bdsw->d_close)(dev, 0, S_IFCHR, curproc); 126#ifdef DEBUG 127 if (error) 128 printf("dkcsum: %s close failed (%d)\n", 129 dv->dv_xname, error); 130#endif 131 continue; 132 } 133 error = (*bdsw->d_close)(dev, FREAD, S_IFCHR, curproc); 134 if (error) { 135 /* XXX What to do here? */ 136#ifdef DEBUG 137 printf("dkcsum: %s closed failed (%d)\n", 138 dv->dv_xname, error); 139#endif 140 continue; 141 } 142 143 csum = adler32(0, bp->b_data, bios_cksumlen * DEV_BSIZE); 144#ifdef DEBUG 145 printf("dkcsum: %s checksum is %#x\n", dv->dv_xname, csum); 146#endif 147 148 /* Find the BIOS device */ 149 hit = 0; 150 for (bdi = bios_diskinfo; bdi->bios_number != -1; bdi++) { 151 /* Skip non-harddrives */ 152 if (!(bdi->bios_number & 0x80)) 153 continue; 154 if (bdi->checksum != csum) 155 continue; 156 picked = hit || (bdi->flags & BDI_PICKED); 157 if (!picked) 158 hit = bdi; 159#ifdef DEBUG 160 printf("dkcsum: %s matches BIOS drive %#x%s\n", 161 dv->dv_xname, bdi->bios_number, 162 (picked ? " IGNORED" : "")); 163#endif 164 } 165 166 /* 167 * If we have no hit, that's OK, we can see a lot more devices 168 * than the BIOS can, so this case is pretty normal. 169 */ 170 if (!hit) { 171#ifdef DEBUG 172 printf("dkcsum: %s has no matching BIOS drive\n", 173 dv->dv_xname); 174#endif 175 continue; 176 } 177 178 /* 179 * Fixup bootdev if units match. This means that all of 180 * hd*, sd*, wd*, will be interpreted the same. Not 100% 181 * backwards compatible, but sd* and wd* should be phased- 182 * out in the bootblocks. 183 */ 184 185 /* B_TYPE dependent hd unit counting bootblocks */ 186 if ((B_ADAPTOR(bootdev) == B_ADAPTOR(hit->bsd_dev)) && 187 (B_CONTROLLER(bootdev) == B_CONTROLLER(hit->bsd_dev)) && 188 (B_TYPE(bootdev) == B_TYPE(hit->bsd_dev)) && 189 (B_UNIT(bootdev) == B_UNIT(hit->bsd_dev))) { 190 int type, ctrl, adap, part, unit; 191 192 type = major(bp->b_dev); 193 adap = B_ADAPTOR(bootdev); 194 ctrl = B_CONTROLLER(bootdev); 195 unit = DISKUNIT(bp->b_dev); 196 part = B_PARTITION(bootdev); 197 198 pribootdev = MAKEBOOTDEV(type, ctrl, adap, unit, part); 199#ifdef DEBUG 200 printf("dkcsum: %s is primary boot disk\n", 201 dv->dv_xname); 202#endif 203 } 204 /* B_TYPE independent hd unit counting bootblocks */ 205 if (B_UNIT(bootdev) == (hit->bios_number & 0x7F)) { 206 int type, ctrl, adap, part, unit; 207 208 type = major(bp->b_dev); 209 adap = B_ADAPTOR(bootdev); 210 ctrl = B_CONTROLLER(bootdev); 211 unit = DISKUNIT(bp->b_dev); 212 part = B_PARTITION(bootdev); 213 214 altbootdev = MAKEBOOTDEV(type, ctrl, adap, unit, part); 215#ifdef DEBUG 216 printf("dkcsum: %s is alternate boot disk\n", 217 dv->dv_xname); 218#endif 219 } 220 221 /* This will overwrite /boot's guess, just so you remember */ 222 hit->bsd_dev = MAKEBOOTDEV(major(bp->b_dev), 0, 0, 223 DISKUNIT(bp->b_dev), RAW_PART); 224 hit->flags |= BDI_PICKED; 225 } 226 bootdev = pribootdev ? pribootdev : altbootdev ? altbootdev : bootdev; 227 228 bp->b_flags |= B_INVAL; 229 brelse(bp); 230} 231