#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: disksubr.c,v 1.45 2022/05/24 06:28:00 andvar Exp $");
#ifndef DISKLABEL_NBDA
#define DISKLABEL_NBDA
#endif
#include "opt_compat_netbsd.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <ufs/ufs/dinode.h>
#include <ufs/ffs/fs.h>
#include <sys/disk.h>
#include <sys/disklabel.h>
#include <machine/ahdilabel.h>
#if BBSIZE < BBMINSIZE
#error BBSIZE smaller than BBMINSIZE
#endif
static void ck_label(struct disklabel *, struct cpu_disklabel *);
static int bsd_label(dev_t, void (*)(struct buf *),
struct disklabel *, u_int, u_int *);
static int ahdi_label(dev_t, void (*)(struct buf *),
struct disklabel *, struct cpu_disklabel *);
static void ahdi_to_bsd(struct disklabel *, struct ahdi_ptbl *);
static u_int ahdi_getparts(dev_t, void (*)(struct buf *), u_int,
u_int, u_int, struct ahdi_ptbl *);
const char *
readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
struct cpu_disklabel *clp)
{
int e;
if (clp != NULL)
memset(clp, 0, sizeof *clp);
else
printf("Warning: clp == NULL\n");
if (lp->d_secsize == 0)
lp->d_secsize = DEV_BSIZE;
if (lp->d_secperunit == 0)
lp->d_secperunit = 0x1fffffff;
if (lp->d_secpercyl == 0)
return "Zero secpercyl";
#if 0
memset(lp->d_partitions, 0, sizeof lp->d_partitions);
#endif
lp->d_partitions[RAW_PART].p_size = lp->d_secperunit;
lp->d_npartitions = RAW_PART + 1;
lp->d_bbsize = BBSIZE;
lp->d_sbsize = SBLOCKSIZE;
#ifdef DISKLABEL_NBDA
e = bsd_label(dev, strat, lp, 0, clp != NULL ? &clp->cd_label : NULL);
#endif
#if 0
if (e > 0)
e = foo_label(dev, strat, lp, ...);
#endif
#ifdef DISKLABEL_AHDI
if (e > 0 && (clp != NULL))
e = ahdi_label(dev, strat, lp, clp);
#endif
if (e < 0)
return "I/O error";
if (e > 0)
uprintf("Warning: unknown disklabel format"
"- assuming empty disk\n");
lp->d_magic = lp->d_magic2 = DISKMAGIC;
lp->d_checksum = 0;
lp->d_checksum = dkcksum(lp);
return NULL;
}
int
writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
struct cpu_disklabel *clp)
{
struct buf *bp;
u_int blk;
int rv;
blk = clp->cd_bblock;
if (blk == NO_BOOT_BLOCK)
return ENXIO;
bp = geteblk(BBMINSIZE);
bp->b_dev = MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART);
bp->b_flags |= B_READ;
bp->b_bcount = BBMINSIZE;
bp->b_blkno = blk;
bp->b_cylinder = blk / lp->d_secpercyl;
(*strat)(bp);
rv = biowait(bp);
if (rv == 0) {
struct bootblock *bb = (struct bootblock *)bp->b_data;
if (clp->cd_label != LABELOFFSET) {
clp->cd_label = LABELOFFSET;
memset(bb, 0, sizeof(*bb));
}
bb->bb_magic = (blk == 0) ? NBDAMAGIC : AHDIMAGIC;
BBSETLABEL(bb, lp);
bp->b_oflags &= ~(BO_DONE);
bp->b_flags &= ~(B_READ);
bp->b_flags |= B_WRITE;
bp->b_bcount = BBMINSIZE;
bp->b_blkno = blk;
bp->b_cylinder = blk / lp->d_secpercyl;
(*strat)(bp);
rv = biowait(bp);
}
brelse(bp, 0);
return rv;
}
static int
bsd_label(dev_t dev, void (*strat)(struct buf *), struct disklabel *label,
u_int blkno, u_int *offsetp)
{
struct buf *bp;
int rv;
bp = geteblk(BBMINSIZE);
bp->b_dev = MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART);
bp->b_flags |= B_READ;
bp->b_bcount = BBMINSIZE;
bp->b_blkno = blkno;
bp->b_cylinder = blkno / label->d_secpercyl;
(*strat)(bp);
rv = -1;
if (!biowait(bp)) {
struct bootblock *bb;
uint32_t *p, *end;
rv = 1;
bb = (struct bootblock *)bp->b_data;
end = (uint32_t *)((char *)&bb[1] - sizeof(struct disklabel));
for (p = (uint32_t *)bb; p < end; ++p) {
struct disklabel *dl = (struct disklabel *)&p[1];
if (((p[0] == NBDAMAGIC && blkno == 0) ||
(p[0] == AHDIMAGIC && blkno != 0)
#ifdef COMPAT_11
|| (char *)dl - (char *)bb == 7168
#endif
) &&
dl->d_npartitions <= MAXPARTITIONS &&
dl->d_magic2 == DISKMAGIC &&
dl->d_magic == DISKMAGIC &&
dkcksum(dl) == 0) {
if (offsetp != NULL)
*offsetp = (char *)dl - (char *)bb;
*label = *dl;
rv = 0;
break;
}
}
}
brelse(bp, 0);
return rv;
}
#ifdef DISKLABEL_AHDI
static void
ck_label(struct disklabel *dl, struct cpu_disklabel *cdl)
{
u_int *rp, i;
for (i = 0; i < dl->d_npartitions; ++i) {
struct partition *p = &dl->d_partitions[i];
if (i == RAW_PART || p->p_size == 0)
continue;
if ((p->p_offset >= cdl->cd_bslst &&
p->p_offset <= cdl->cd_bslend) ||
(cdl->cd_bslst >= p->p_offset &&
cdl->cd_bslst < p->p_offset + p->p_size)) {
uprintf("Warning: NetBSD partition %c includes"
" AHDI bad sector list\n", 'a'+i);
}
for (rp = &cdl->cd_roots[0]; *rp; ++rp) {
if (*rp >= p->p_offset &&
*rp < p->p_offset + p->p_size) {
uprintf("Warning: NetBSD partition %c"
" includes AHDI auxiliary root\n", 'a' + i);
}
}
}
}
int
ahdi_label(dev_t dev, void (*strat)(struct buf *), struct disklabel *dl,
struct cpu_disklabel *cdl)
{
struct ahdi_ptbl apt;
u_int i;
int j;
if (dl->d_secsize != AHDI_BSIZE)
return 1;
apt.at_cdl = cdl;
apt.at_nroots = apt.at_nparts = 0;
i = ahdi_getparts(dev, strat, dl->d_secpercyl,
AHDI_BBLOCK, AHDI_BBLOCK, &apt);
if (i) {
if (i < dl->d_secperunit)
return -1;
else
return 1;
}
if (apt.at_bslst == 0 || apt.at_bslend == 0) {
apt.at_bslst = apt.at_bslend = 0;
uprintf("Warning: Illegal 'bad sector list' format"
"- assuming non exists\n");
}
if (apt.at_hdsize == 0 || apt.at_nparts == 0)
return 1;
if (apt.at_nparts > AHDI_MAXPARTS)
return -1;
for (i = 0; i < apt.at_nparts; ++i) {
struct ahdi_part *p1 = &apt.at_parts[i];
for (j = 0; j < apt.at_nroots; ++j) {
u_int aux = apt.at_roots[j];
if (aux >= p1->ap_st && aux <= p1->ap_end)
return 1;
}
for (j = i + 1; j < apt.at_nparts; ++j) {
struct ahdi_part *p2 = &apt.at_parts[j];
if (p1->ap_st >= p2->ap_st && p1->ap_st <= p2->ap_end)
return 1;
if (p2->ap_st >= p1->ap_st && p2->ap_st <= p1->ap_end)
return 1;
}
if (p1->ap_st >= apt.at_bslst && p1->ap_st <= apt.at_bslend)
return 1;
if (apt.at_bslst >= p1->ap_st && apt.at_bslst <= p1->ap_end)
return 1;
}
apt.at_bblock = NO_BOOT_BLOCK;
for (i = 0; i < apt.at_nparts; ++i) {
struct ahdi_part *pd = &apt.at_parts[i];
u_int id = *((uint32_t *)&pd->ap_flg);
if (id == AHDI_PID_NBD || id == AHDI_PID_RAW) {
u_int blkno = pd->ap_st;
j = bsd_label(dev, strat, dl, blkno, &apt.at_label);
if (j < 0) {
return j;
}
if (j == 0) {
apt.at_bblock = blkno;
ck_label(dl, cdl);
return 0;
}
if (id == AHDI_PID_NBD &&
apt.at_bblock == NO_BOOT_BLOCK)
apt.at_bblock = blkno;
}
}
ahdi_to_bsd(dl, &apt);
return 0;
}
static void
ahdi_to_bsd(struct disklabel *dl, struct ahdi_ptbl *apt)
{
int i, have_root, user_part;
user_part = RAW_PART;
have_root = (apt->at_bblock != NO_BOOT_BLOCK);
for (i = 0; i < apt->at_nparts; ++i) {
struct ahdi_part *pd = &apt->at_parts[i];
int fst, pno = -1;
switch (*((uint32_t *)&pd->ap_flg)) {
case AHDI_PID_NBD:
if (pd->ap_st == apt->at_bblock)
pno = 0;
case AHDI_PID_NBR:
if (!have_root) {
have_root = 1;
pno = 0;
}
case AHDI_PID_NBU:
fst = FS_BSDFFS;
break;
case AHDI_PID_NBS:
case AHDI_PID_SWP:
if (dl->d_partitions[1].p_size == 0)
pno = 1;
fst = FS_SWAP;
break;
case AHDI_PID_BGM:
case AHDI_PID_GEM:
fst = FS_MSDOS;
break;
default:
fst = FS_OTHER;
break;
}
if (pno < 0) {
if ((pno = user_part + 1) >= MAXPARTITIONS)
continue;
user_part = pno;
}
dl->d_partitions[pno].p_size = pd->ap_end - pd->ap_st + 1;
dl->d_partitions[pno].p_offset = pd->ap_st;
dl->d_partitions[pno].p_fstype = fst;
}
dl->d_npartitions = user_part + 1;
}
static u_int
ahdi_getparts(dev_t dev, void (*strat)(struct buf *), u_int secpercyl,
u_int rsec, u_int esec, struct ahdi_ptbl *apt)
{
struct ahdi_part *part, *end;
struct ahdi_root *root;
struct buf *bp;
u_int rv;
bp = geteblk(AHDI_BSIZE);
bp->b_dev = MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART);
bp->b_flags |= B_READ;
bp->b_bcount = AHDI_BSIZE;
bp->b_blkno = rsec;
bp->b_cylinder = rsec / secpercyl;
(*strat)(bp);
if (biowait(bp)) {
rv = rsec + (rsec == 0);
goto done;
}
root = (struct ahdi_root *)bp->b_data;
if (rsec == AHDI_BBLOCK)
end = &root->ar_parts[AHDI_MAXRPD];
else
end = &root->ar_parts[AHDI_MAXARPD];
for (part = root->ar_parts; part < end; ++part) {
u_int id = *((uint32_t *)&part->ap_flg);
if (!(id & 0x01000000))
continue;
if ((id &= 0x00ffffff) == AHDI_PID_XGM) {
u_int offs = part->ap_st + esec;
if (apt->at_nroots < AHDI_MAXROOTS)
apt->at_roots[apt->at_nroots] = offs;
apt->at_nroots += 1;
rv = ahdi_getparts(dev, strat, secpercyl, offs,
(esec == AHDI_BBLOCK) ? offs : esec, apt);
if (rv != 0)
goto done;
continue;
}
else if (apt->at_nparts < AHDI_MAXPARTS) {
struct ahdi_part *p = &apt->at_parts[apt->at_nparts];
*((uint32_t *)&p->ap_flg) = id;
p->ap_st = part->ap_st + rsec;
p->ap_end = p->ap_st + part->ap_size - 1;
}
apt->at_nparts += 1;
}
apt->at_hdsize = root->ar_hdsize;
apt->at_bslst = root->ar_bslst;
apt->at_bslend = root->ar_bslst + root->ar_bslsize - 1;
rv = 0;
done:
brelse(bp, 0);
return rv;
}
#endif