#include <sys/param.h>
#include <sys/buf.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/disklabel.h>
#include <sys/fcntl.h>
#include <sys/reboot.h>
#include <sys/stat.h>
#include <sys/systm.h>
#include <machine/biosvar.h>
#include <lib/libz/zlib.h>
dev_t dev_rawpart(struct device *);
extern u_int32_t bios_cksumlen;
extern bios_diskinfo_t *bios_diskinfo;
extern dev_t bootdev;
void
dkcsumattach(void)
{
struct device *dv;
struct buf *bp;
struct bdevsw *bdsw;
dev_t dev, pribootdev, altbootdev;
int error, picked;
u_int32_t csum;
bios_diskinfo_t *bdi, *hit;
if (bios_diskinfo == NULL || bios_cksumlen * DEV_BSIZE > MAXBSIZE)
return;
if (B_TYPE(bootdev) == 6)
return;
#ifdef DEBUG
printf("dkcsum: bootdev=%#x\n", bootdev);
for (bdi = bios_diskinfo; bdi->bios_number != -1; bdi++) {
if (bdi->bios_number & 0x80) {
printf("dkcsum: BIOS drive %#x bsd_dev=%#x "
"checksum=%#x\n", bdi->bios_number, bdi->bsd_dev,
bdi->checksum);
}
}
#endif
pribootdev = altbootdev = 0;
bp = geteblk(bios_cksumlen * DEV_BSIZE);
TAILQ_FOREACH(dv, &alldevs, dv_list) {
if (dv->dv_class != DV_DISK)
continue;
bp->b_dev = dev = dev_rawpart(dv);
if (dev == NODEV)
continue;
bdsw = &bdevsw[major(dev)];
error = (*bdsw->d_open)(dev, FREAD, S_IFCHR, curproc);
if (error) {
#ifdef DEBUG
printf("dkcsum: %s open failed (%d)\n",
dv->dv_xname, error);
#endif
continue;
}
bp->b_blkno = 0;
bp->b_bcount = bios_cksumlen * DEV_BSIZE;
bp->b_error = 0;
CLR(bp->b_flags, B_READ | B_WRITE | B_DONE | B_ERROR);
SET(bp->b_flags, B_BUSY | B_READ | B_RAW);
(*bdsw->d_strategy)(bp);
if ((error = biowait(bp))) {
#ifdef DEBUG
printf("dkcsum: %s read failed (%d)\n",
dv->dv_xname, error);
#endif
error = (*bdsw->d_close)(dev, 0, S_IFCHR, curproc);
#ifdef DEBUG
if (error)
printf("dkcsum: %s close failed (%d)\n",
dv->dv_xname, error);
#endif
continue;
}
error = (*bdsw->d_close)(dev, FREAD, S_IFCHR, curproc);
if (error) {
#ifdef DEBUG
printf("dkcsum: %s closed failed (%d)\n",
dv->dv_xname, error);
#endif
continue;
}
csum = adler32(0, bp->b_data, bios_cksumlen * DEV_BSIZE);
#ifdef DEBUG
printf("dkcsum: %s checksum is %#x\n", dv->dv_xname, csum);
#endif
hit = 0;
for (bdi = bios_diskinfo; bdi->bios_number != -1; bdi++) {
if (!(bdi->bios_number & 0x80))
continue;
if (bdi->checksum != csum)
continue;
picked = hit || (bdi->flags & BDI_PICKED);
if (!picked)
hit = bdi;
#ifdef DEBUG
printf("dkcsum: %s matches BIOS drive %#x%s\n",
dv->dv_xname, bdi->bios_number,
(picked ? " IGNORED" : ""));
#endif
}
if (!hit) {
#ifdef DEBUG
printf("dkcsum: %s has no matching BIOS drive\n",
dv->dv_xname);
#endif
continue;
}
if ((B_ADAPTOR(bootdev) == B_ADAPTOR(hit->bsd_dev)) &&
(B_CONTROLLER(bootdev) == B_CONTROLLER(hit->bsd_dev)) &&
(B_TYPE(bootdev) == B_TYPE(hit->bsd_dev)) &&
(B_UNIT(bootdev) == B_UNIT(hit->bsd_dev))) {
int type, ctrl, adap, part, unit;
type = major(bp->b_dev);
adap = B_ADAPTOR(bootdev);
ctrl = B_CONTROLLER(bootdev);
unit = DISKUNIT(bp->b_dev);
part = B_PARTITION(bootdev);
pribootdev = MAKEBOOTDEV(type, ctrl, adap, unit, part);
#ifdef DEBUG
printf("dkcsum: %s is primary boot disk\n",
dv->dv_xname);
#endif
}
if (B_UNIT(bootdev) == (hit->bios_number & 0x7F)) {
int type, ctrl, adap, part, unit;
type = major(bp->b_dev);
adap = B_ADAPTOR(bootdev);
ctrl = B_CONTROLLER(bootdev);
unit = DISKUNIT(bp->b_dev);
part = B_PARTITION(bootdev);
altbootdev = MAKEBOOTDEV(type, ctrl, adap, unit, part);
#ifdef DEBUG
printf("dkcsum: %s is alternate boot disk\n",
dv->dv_xname);
#endif
}
hit->bsd_dev = MAKEBOOTDEV(major(bp->b_dev), 0, 0,
DISKUNIT(bp->b_dev), RAW_PART);
hit->flags |= BDI_PICKED;
}
bootdev = pribootdev ? pribootdev : altbootdev ? altbootdev : bootdev;
bp->b_flags |= B_INVAL;
brelse(bp);
}