#undef DKTYPENAMES
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/reboot.h>
#include <sys/disklabel.h>
#include <sys/hibernate.h>
#include <lib/libz/zlib.h>
#include <machine/biosvar.h>
#include <stand/boot/bootarg.h>
#include "disk.h"
#include "biosdev.h"
#include "libsa.h"
#ifdef SOFTRAID
#include "softraid_amd64.h"
#endif
#define MAX_CKSUMLEN MAXBSIZE / DEV_BSIZE
static int disksum(int);
int bootdev_has_hibernate(void);
struct disklist_lh disklist;
struct diskinfo *bootdev_dip;
extern int debug;
extern int bios_bootdev;
extern int bios_cddev;
static void
diskinfo_init(struct diskinfo *dip)
{
bzero(dip, sizeof(*dip));
dip->diskio = biosd_diskio;
dip->strategy = biosstrategy;
}
static void
floppyprobe(void)
{
struct diskinfo *dip;
int i;
for (i = 0; i < 4; i++) {
dip = alloc(sizeof(struct diskinfo));
diskinfo_init(dip);
if (bios_getdiskinfo(i, &dip->bios_info)) {
#ifdef BIOS_DEBUG
if (debug)
printf(" <!fd%u>", i);
#endif
free(dip, sizeof(*dip));
break;
}
printf(" fd%u", i);
dip->bios_info.bsd_dev = MAKEBOOTDEV(2, 0, 0, i, RAW_PART);
dip->bios_info.flags |= BDI_BADLABEL;
TAILQ_INSERT_TAIL(&disklist, dip, list);
}
}
static void
hardprobe(void)
{
struct diskinfo *dip;
int i;
u_int bsdunit, type;
u_int scsi = 0, ide = 0;
const char *dc = (const char *)((0x40 << 4) + 0x75);
for (i = 0x80; i < (0x80 + *dc); i++) {
dip = alloc(sizeof(struct diskinfo));
diskinfo_init(dip);
if (bios_getdiskinfo(i, &dip->bios_info)) {
#ifdef BIOS_DEBUG
if (debug)
printf(" <!hd%u>", i&0x7f);
#endif
free(dip, sizeof(*dip));
break;
}
printf(" hd%u%s", i&0x7f, (dip->bios_info.bios_edd > 0?"+":""));
if ((bios_getdisklabel(&dip->bios_info, &dip->disklabel)) ) {
printf("*");
bsdunit = ide++;
type = 0;
} else {
switch (dip->disklabel.d_type) {
case DTYPE_SCSI:
type = 4;
bsdunit = scsi++;
dip->bios_info.flags |= BDI_GOODLABEL;
break;
case DTYPE_ESDI:
case DTYPE_ST506:
type = 0;
bsdunit = ide++;
dip->bios_info.flags |= BDI_GOODLABEL;
break;
default:
dip->bios_info.flags |= BDI_BADLABEL;
type = 0;
bsdunit = ide++;
}
}
dip->bios_info.checksum = 0;
dip->bsddev = dip->bios_info.bsd_dev =
MAKEBOOTDEV(type, 0, 0, bsdunit, RAW_PART);
check_hibernate(dip);
TAILQ_INSERT_TAIL(&disklist, dip, list);
}
}
u_int32_t bios_cksumlen;
void
diskprobe(void)
{
struct diskinfo *dip;
int i;
bios_diskinfo_t *bios_diskinfo;
TAILQ_INIT(&disklist);
floppyprobe();
#ifdef BIOS_DEBUG
if (debug)
printf(";");
#endif
hardprobe();
#ifdef SOFTRAID
srprobe();
#endif
for (i = 0; disksum(i++) && i < MAX_CKSUMLEN; )
;
bios_cksumlen = i;
for (i = 0, dip = TAILQ_FIRST(&disklist); dip;
dip = TAILQ_NEXT(dip, list))
i++;
bios_diskinfo = alloc(++i * sizeof(bios_diskinfo_t));
for (i = 0, dip = TAILQ_FIRST(&disklist); dip;
dip = TAILQ_NEXT(dip, list))
bios_diskinfo[i++] = dip->bios_info;
bios_diskinfo[i++].bios_number = -1;
addbootarg(BOOTARG_CKSUMLEN, sizeof(u_int32_t), &bios_cksumlen);
addbootarg(BOOTARG_DISKINFO, i * sizeof(bios_diskinfo_t),
bios_diskinfo);
}
void
cdprobe(void)
{
struct diskinfo *dip;
int cddev = bios_cddev & 0xff;
if (bios_cddev == -1)
return;
dip = alloc(sizeof(struct diskinfo));
diskinfo_init(dip);
printf(" cd0");
dip->bios_info.bios_number = cddev;
dip->bios_info.bios_edd = 1;
dip->bios_info.flags |= BDI_GOODLABEL | BDI_EL_TORITO;
dip->bios_info.checksum = 0;
dip->bios_info.bsd_dev =
MAKEBOOTDEV(6, 0, 0, 0, RAW_PART);
dip->disklabel.d_secsize = 2048;
dip->disklabel.d_ntracks = 1;
dip->disklabel.d_nsectors = 100;
dip->disklabel.d_ncylinders = 1;
dip->disklabel.d_secpercyl = dip->disklabel.d_ntracks *
dip->disklabel.d_nsectors;
strncpy(dip->disklabel.d_typename, "ATAPI CD-ROM",
sizeof(dip->disklabel.d_typename));
dip->disklabel.d_type = DTYPE_ATAPI;
strncpy(dip->disklabel.d_packname, "fictitious",
sizeof(dip->disklabel.d_packname));
DL_SETDSIZE(&dip->disklabel, 100);
DL_SETPOFFSET(&dip->disklabel.d_partitions[0], 0);
DL_SETPSIZE(&dip->disklabel.d_partitions[0], 100);
dip->disklabel.d_partitions[0].p_fstype = FS_UNUSED;
DL_SETPOFFSET(&dip->disklabel.d_partitions[RAW_PART], 0);
DL_SETPSIZE(&dip->disklabel.d_partitions[RAW_PART], 100);
dip->disklabel.d_partitions[RAW_PART].p_fstype = FS_UNUSED;
dip->disklabel.d_npartitions = MAXPARTITIONS;
dip->disklabel.d_magic = DISKMAGIC;
dip->disklabel.d_magic2 = DISKMAGIC;
dip->disklabel.d_checksum = dkcksum(&dip->disklabel);
TAILQ_INSERT_TAIL(&disklist, dip, list);
}
struct diskinfo *
dklookup(int dev)
{
struct diskinfo *dip;
for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list))
if (dip->bios_info.bios_number == dev)
return dip;
return NULL;
}
void
dump_diskinfo(void)
{
struct diskinfo *dip;
printf("Disk\tBIOS#\tType\tCyls\tHeads\tSecs\tFlags\tChecksum\n");
for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) {
bios_diskinfo_t *bdi = &dip->bios_info;
int d = bdi->bios_number;
int u = d & 0x7f;
char c;
if (bdi->flags & BDI_EL_TORITO) {
c = 'c';
u = 0;
} else {
c = (d & 0x80) ? 'h' : 'f';
}
printf("%cd%d\t0x%x\t%s\t%d\t%d\t%d\t0x%x\t0x%x\n",
c, u, d,
(bdi->flags & BDI_BADLABEL)?"*none*":"label",
bdi->bios_cylinders, bdi->bios_heads, bdi->bios_sectors,
bdi->flags, bdi->checksum);
}
}
bios_diskinfo_t *
bios_dklookup(int dev)
{
struct diskinfo *dip;
dip = dklookup(dev);
if (dip)
return &dip->bios_info;
return NULL;
}
int
disksum(int blk)
{
struct diskinfo *dip, *dip2;
int st, reprobe = 0;
char buf[DEV_BSIZE];
for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) {
bios_diskinfo_t *bdi = &dip->bios_info;
if (!(bdi->bios_number & 0x80) || bdi->flags & BDI_INVALID)
continue;
st = dip->diskio(F_READ, dip, blk, 1, buf);
if (st) {
bdi->flags |= BDI_INVALID;
continue;
}
bdi->checksum = adler32(bdi->checksum, buf, DEV_BSIZE);
for (dip2 = TAILQ_FIRST(&disklist); dip2 != dip;
dip2 = TAILQ_NEXT(dip2, list)) {
bios_diskinfo_t *bd = &dip2->bios_info;
if ((bd->bios_number & 0x80) &&
!(bd->flags & BDI_INVALID) &&
bdi->checksum == bd->checksum)
reprobe = 1;
}
}
return reprobe;
}
int
bootdev_has_hibernate(void)
{
return ((bootdev_dip->bios_info.flags & BDI_HIBVALID)? 1 : 0);
}
void
check_hibernate(struct diskinfo *dip)
{
uint8_t buf[DEV_BSIZE];
daddr_t sec;
int error;
union hibernate_info *hib = (union hibernate_info *)&buf;
if (dip->disklabel.d_partitions[1].p_fstype != FS_SWAP ||
DL_GETPSIZE(&dip->disklabel.d_partitions[1]) == 0)
return;
sec = DL_GETPOFFSET(&dip->disklabel.d_partitions[1]) +
DL_GETPSIZE(&dip->disklabel.d_partitions[1]) - 1;
error = dip->strategy(dip, F_READ, DL_SECTOBLK(&dip->disklabel, sec),
sizeof buf, &buf, NULL);
if (error == 0 && hib->magic == HIBERNATE_MAGIC)
dip->bios_info.flags |= BDI_HIBVALID;
}