root/sys/arch/next68k/next68k/disksubr.c
/*      $NetBSD: disksubr.c,v 1.33 2026/06/11 22:52:09 dbj Exp $        */

/*
 * Copyright (c) 1982, 1986, 1988, 1993
 *      The Regents of the University of California.  All rights reserved.
 * (c) UNIX System Laboratories, Inc.
 * All or some portions of this file are derived from material licensed
 * to the University of California by American Telephone and Telegraph
 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
 * the permission of UNIX System Laboratories, Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *      @(#)ufs_disksubr.c      8.5 (Berkeley) 1/21/94
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: disksubr.c,v 1.33 2026/06/11 22:52:09 dbj Exp $");

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#define FSTYPENAMES
#include <sys/disklabel.h>
#include <sys/syslog.h>

#include <sys/disk.h>

#include <ufs/ufs/dinode.h>             /* XXX for fs.h */
#include <ufs/ffs/fs.h>                 /* XXX for SBLOCKSIZE */

#define b_cylinder      b_resid

static uint16_t nextstep_checksum(unsigned char *, unsigned char *);
static const char *parse_nextstep_label(struct next68k_disklabel *,
    struct disklabel *, struct cpu_disklabel *);
static int build_nextstep_label(struct next68k_disklabel *, struct disklabel *);

static uint16_t
nextstep_checksum(unsigned char *buf, unsigned char *limit)
{
        int sum = 0;

        while (buf < limit) {
                sum += (buf[0] << 8) + buf[1];
                buf += 2;
        }
        sum += (sum >> 16);
        return sum & 0xffff;
}

static const char *
parse_nextstep_label(struct next68k_disklabel *ondisk, struct disklabel *lp,
    struct cpu_disklabel *osdep)
{
        int i, t, nbp;
        uint16_t *checksum;

        if (ondisk->cd_version == NEXT68K_LABEL_CD_V3) {
                checksum = &ondisk->NEXT68K_LABEL_cd_v3_checksum;
        } else {
                checksum = &ondisk->NEXT68K_LABEL_cd_checksum;
        }
        if (nextstep_checksum((uint8_t *)ondisk, (uint8_t *)checksum) !=
            *checksum) {
                return "disk label corrupted";
        }

        osdep->od_version = ondisk->cd_version;
        lp->d_magic = lp->d_magic2 = DISKMAGIC;
        lp->d_type = DKTYPE_SCSI;
        lp->d_subtype = 0;
        if (sizeof(lp->d_typename) > sizeof(ondisk->cd_name))
                lp->d_typename[sizeof (ondisk->cd_name)] = '\0';
        memcpy(lp->d_typename, ondisk->cd_name,
            uimin(sizeof (lp->d_typename), sizeof(ondisk->cd_name)));
        if (sizeof(lp->d_packname) > sizeof(ondisk->cd_label))
                lp->d_packname[sizeof (ondisk->cd_label)] = '\0';
        memcpy(lp->d_packname, ondisk->cd_label,
            uimin(sizeof(lp->d_packname), sizeof(ondisk->cd_label)));
        if (lp->d_secsize == 0)
                lp->d_secsize = ondisk->cd_secsize;
        KASSERT(ondisk->cd_secsize >= lp->d_secsize);
        lp->d_nsectors = ondisk->cd_nsectors;
        lp->d_ntracks = ondisk->cd_ntracks;
        lp->d_ncylinders = ondisk->cd_ncylinders;

        lp->d_rpm = ondisk->cd_rpm;
        lp->d_flags = ondisk->cd_flags;

        lp->d_bbsize = NEXT68K_LABEL_SIZE;
        lp->d_sbsize = SBLOCKSIZE;

        lp->d_npartitions = nbp = 0;
        for (i = 0; i < NEXT68K_LABEL_MAXPARTITIONS - 1; i++) {
                if (ondisk->cd_partitions[i].cp_size > 0) {
                        lp->d_partitions[nbp].p_size =
                            ondisk->cd_partitions[i].cp_size *
                            (ondisk->cd_secsize / lp->d_secsize);
                        lp->d_partitions[nbp].p_offset =
                            (ondisk->cd_front +
                             ondisk->cd_partitions[i].cp_offset) *
                             (ondisk->cd_secsize / lp->d_secsize);
                        lp->d_partitions[nbp].p_fsize =
                            ondisk->cd_partitions[i].cp_fsize;
#ifndef FSTYPENAMES
                        lp->d_partitions[nbp].p_fstype = FS_BSDFFS;
#else
                        for (t = 0; t < FSMAXTYPES; t++) {
                                if (strncmp(ondisk->cd_partitions[i].cp_type,
                                    fstypenames[t], NEXT68K_LABEL_MAXFSTLEN)
                                    == 0)
                                        break;
                        }
                        if (t == FSMAXTYPES)
                                t = FS_OTHER;
                        lp->d_partitions[nbp].p_fstype = t;
#endif
                        if (ondisk->cd_partitions[i].cp_fsize)
                                lp->d_partitions[nbp].p_frag =
                                    ondisk->cd_partitions[i].cp_bsize /
                                    ondisk->cd_partitions[i].cp_fsize;
                        else
                                lp->d_partitions[nbp].p_frag = 0;
                        lp->d_partitions[nbp].p_cpg =
                                ondisk->cd_partitions[i].cp_cpg;
                        lp->d_npartitions = nbp + 1;
                }
                nbp++;
                if (nbp == RAW_PART)
                        nbp++;
                if (nbp == MAXPARTITIONS)
                        break;
        }

        if (lp->d_npartitions <= RAW_PART)
                lp->d_npartitions = RAW_PART + 1;

        lp->d_checksum = 0;
        lp->d_checksum = dkcksum(lp);

        return NULL;
}

static int
build_nextstep_label(struct next68k_disklabel *ondisk, struct disklabel *lp)
{
        int i, t, nbp;
        int front_porch = NEXT68K_LABEL_DEFAULTFRONTPORCH;
        uint16_t *checksum;

        memset(ondisk, 0, sizeof(*ondisk));

        ondisk->cd_version = NEXT68K_LABEL_CD_V3;
        /* ondisk->cd_label_blkno = 0; */
        /* ondisk->cd_size = 0; */
        /* ondisk->cd_tag = 0; */
        strncpy(ondisk->cd_type, "fixed_rw_scsi", sizeof(ondisk->cd_type));
        ondisk->cd_secsize = lp->d_secsize;
        /* ondisk->cd_back = 0; */
        /* ondisk->cd_ngroups = 0; */
        /* ondisk->cd_ag_size = 0; */
        /* ondisk->cd_ag_alts = 0; */
        /* ondisk->cd_ag_off = 0; */
        /* ondisk->kernel */
        /* ondisk->hostname */
        /* ondisk->rootpartition */
        /* ondisk->rwpartition */
        KASSERT(ondisk->cd_secsize >= lp->d_secsize);

        if (memcmp(ondisk->cd_name, lp->d_typename,
            uimin(sizeof(lp->d_typename), sizeof(ondisk->cd_name))) &&
            sizeof (ondisk->cd_name) > sizeof (lp->d_typename))
                ondisk->cd_name[sizeof(lp->d_typename)] = '\0';
        memcpy(ondisk->cd_name, lp->d_typename,
            uimin(sizeof(lp->d_typename), sizeof(ondisk->cd_name)));
        if (memcmp (lp->d_packname, ondisk->cd_label,
            uimin(sizeof(lp->d_packname), sizeof(ondisk->cd_label))) &&
            sizeof(ondisk->cd_label) > sizeof(lp->d_packname))
                ondisk->cd_label[sizeof(lp->d_packname)] = '\0';
        memcpy(ondisk->cd_label, lp->d_packname,
            uimin(sizeof(lp->d_packname), sizeof(ondisk->cd_label)));

        ondisk->cd_nsectors = lp->d_nsectors;
        ondisk->cd_ntracks = lp->d_ntracks;
        ondisk->cd_ncylinders = lp->d_ncylinders;

        ondisk->cd_rpm = lp->d_rpm;
        ondisk->cd_flags = lp->d_flags;

        /*
         * figure out front porch
         * try to map partitions which were moved
         */
        for (nbp = 0; nbp < lp->d_npartitions; nbp++) {
                if (nbp != RAW_PART && lp->d_partitions[nbp].p_offset > 0 &&
                    lp->d_partitions[nbp].p_offset < front_porch)
                        front_porch = lp->d_partitions[nbp].p_offset;
                for (t = 0; t < NEXT68K_LABEL_MAXPARTITIONS; t++) {
                        if (t != (nbp > RAW_PART ? nbp - 1 : nbp) &&
                            (lp->d_partitions[nbp].p_size ==
                             ondisk->cd_partitions[t].cp_size *
                             (ondisk->cd_secsize / lp->d_secsize)) &&
                            (lp->d_partitions[nbp].p_offset ==
                             (ondisk->cd_front +
                              ondisk->cd_partitions[t].cp_offset) *
                             (ondisk->cd_secsize / lp->d_secsize)) &&
                            ((lp->d_partitions[nbp].p_fstype == FS_OTHER) ||
                             (!strncmp (ondisk->cd_partitions[t].cp_type,
                                 fstypenames[lp->d_partitions[nbp].p_fstype],
                                 NEXT68K_LABEL_MAXFSTLEN)))) {
                                struct next68k_partition tmp;
                                memcpy(&tmp, &ondisk->cd_partitions[t],
                                    sizeof(tmp));
                                memcpy(&ondisk->cd_partitions[t],
                                    &ondisk->cd_partitions[nbp > RAW_PART ?
                                    nbp - 1 : nbp],
                                    sizeof (tmp));
                                memcpy(&ondisk->cd_partitions[nbp > RAW_PART ?
                                    nbp - 1 : nbp],
                                    &tmp, sizeof (tmp));
                        }
                }
        }
        front_porch /= (ondisk->cd_secsize / lp->d_secsize);

        /*
         * update partitions
         */
        nbp = 0;
        for (i = 0; i < NEXT68K_LABEL_MAXPARTITIONS; i++) {
                struct next68k_partition *p = &ondisk->cd_partitions[i];
                if (nbp < lp->d_npartitions && lp->d_partitions[nbp].p_size) {
                        p->cp_size = lp->d_partitions[nbp].p_size /
                            (ondisk->cd_secsize / lp->d_secsize);
                        p->cp_offset = (lp->d_partitions[nbp].p_offset /
                            (ondisk->cd_secsize / lp->d_secsize)) -
                            front_porch;
                        p->cp_bsize = lp->d_partitions[nbp].p_frag
                            * lp->d_partitions[nbp].p_fsize;
                        p->cp_fsize = lp->d_partitions[nbp].p_fsize;
                        if (lp->d_partitions[nbp].p_fstype != FS_OTHER) {
                                memset(p->cp_type, 0, NEXT68K_LABEL_MAXFSTLEN);
                                strncpy(p->cp_type,
                                    fstypenames[lp->d_partitions[nbp].p_fstype],
                                    NEXT68K_LABEL_MAXFSTLEN);
                        }
                        if (p->cp_density < 0)
                                p->cp_density = 4096; /* set some default */
                        if (p->cp_minfree < 0)
                                p->cp_minfree = 5; /* set some default */
                        p->cp_cpg = lp->d_partitions[nbp].p_cpg;
                } else {
                        memset(p, 0, sizeof(*p));
                        p->cp_size = -1;
                        p->cp_offset = -1;
                        p->cp_bsize = -1;
                        p->cp_fsize = -1;
                        p->cp_density = -1;
                        p->cp_minfree = -1;
                }
                nbp++;
                if (nbp == RAW_PART)
                        nbp++;
        }

        ondisk->cd_front = front_porch;
        ondisk->cd_boot_blkno[0] = NEXT68K_LABEL_DEFAULTBOOT0_1 /
            (ondisk->cd_secsize / lp->d_secsize);
        ondisk->cd_boot_blkno[1] = NEXT68K_LABEL_DEFAULTBOOT0_2 /
            (ondisk->cd_secsize / lp->d_secsize);

        if (ondisk->cd_version == NEXT68K_LABEL_CD_V3) {
                checksum = &ondisk->NEXT68K_LABEL_cd_v3_checksum;
        } else {
                checksum = &ondisk->NEXT68K_LABEL_cd_checksum;
        }
        *checksum = nextstep_checksum((uint8_t *)ondisk, (uint8_t *)checksum);

        return 0;
}

/*
 * Attempt to read a disk label from a device using the indicated strategy
 * routine.  The label must be partly set up before this: secpercyl and
 * anything required in the strategy routine (e.g., sector size) must be
 * filled in before calling us.  Returns null on success and an error
 * string on failure.
 */
const char *
readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
    struct cpu_disklabel *osdep)
{
        struct buf *bp;
        struct disklabel *dlp;
        const char *msg = NULL;
        int i;

        /* minimal requirements for archetypal disk label */
        if (lp->d_secsize == 0)
                lp->d_secsize = DEV_BSIZE;
        if (lp->d_secperunit == 0)
                lp->d_secperunit = 0x1fffffff;
        lp->d_npartitions = RAW_PART + 1;
        for (i = 0; i < RAW_PART; i++) {
                lp->d_partitions[i].p_size = 0;
                lp->d_partitions[i].p_offset = 0;
        }
        if (lp->d_partitions[i].p_size == 0)
                lp->d_partitions[i].p_size = 0x1fffffff;
        lp->d_partitions[i].p_offset = 0;

        bp = geteblk(NEXT68K_LABEL_SIZE);
        bp->b_dev = dev;
        bp->b_blkno = NEXT68K_LABEL_SECTOR;
        bp->b_bcount = NEXT68K_LABEL_SIZE;
        bp->b_flags |= B_READ;
        bp->b_cylinder = NEXT68K_LABEL_SECTOR / lp->d_secpercyl;
        (*strat)(bp);

        if (osdep)
                osdep->od_version = 0;

        if (biowait(bp)) {
                brelse(bp, 0);
                return("I/O error");
        }
        dlp = (struct disklabel *)
            ((char *)bp->b_data + LABELSECTOR * lp->d_secsize + LABELOFFSET);
        if (dlp->d_magic == DISKMAGIC || dlp->d_magic2 == DISKMAGIC) {
                /* got a NetBSD disklabel */
                if (osdep)
                        osdep->od_version = DISKMAGIC;
                if (dlp->d_npartitions > MAXPARTITIONS ||
                           dkcksum(dlp) != 0)
                        msg = "disk label corrupted";
                else {
                        *lp = *dlp;
                        msg = NULL;
                }
                brelse(bp, 0);
                return msg;
        }
        if (IS_DISKLABEL((struct next68k_disklabel *)bp->b_data)) {
                /* got a NeXT disklabel */
                msg = parse_nextstep_label(
                    (struct next68k_disklabel *)bp->b_data, lp, osdep);
                brelse(bp, 0);
                return msg;
        }
        /*
         * no disklabel at the usual places. Try to locate a NetBSD disklabel
         * in the first sector. This ensure compatibility with others
         * big-endian systems, and with next68k when LABELSECTOR was 0.
         */
        msg = "no disk label";
        for (dlp = (struct disklabel *)bp->b_data;
             dlp <= (struct disklabel *)((char *)bp->b_data +
             DEV_BSIZE - sizeof(*dlp));
             dlp = (struct disklabel *)((char *)dlp + sizeof(long))) {
                if (dlp->d_magic == DISKMAGIC || dlp->d_magic2 == DISKMAGIC) {
                        if (dlp->d_npartitions > MAXPARTITIONS ||
                           dkcksum(dlp) != 0)
                                msg = "disk label corrupted";
                        else {
                                *lp = *dlp;
                                msg = NULL;
                                if (osdep)
                                        osdep->od_version = DISKMAGIC;
                                break;
                        }
                }
        }
        brelse(bp, 0);
        return msg;
}

/*
 * Write disk label back to device after modification.
 */
int
writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
    struct cpu_disklabel *osdep)
{
        struct buf *bp;
#if 0
        struct disklabel *dlp;
#endif
        int labelpart;
        int error = 0;

        labelpart = DISKPART(dev);
        if (lp->d_partitions[labelpart].p_offset != 0) {
                if (lp->d_partitions[0].p_offset != 0)
                        return EXDEV;                   /* not quite right */
                labelpart = 0;
        }
        /*
         * We always write a NeXT v3 disklabel, and a NetBSD disklabel in
         * the last sector of the NeXT label area.
         */

        bp = geteblk(NEXT68K_LABEL_SIZE);
        bp->b_dev = MAKEDISKDEV(major(dev), DISKUNIT(dev), labelpart);
        bp->b_blkno = NEXT68K_LABEL_SECTOR;
        bp->b_bcount = NEXT68K_LABEL_SIZE;
        bp->b_flags |= B_WRITE;
        bp->b_cylinder = NEXT68K_LABEL_SECTOR / lp->d_secpercyl;
        error =
            build_nextstep_label((struct next68k_disklabel *)bp->b_data, lp);
        if (error)
                goto done;
#if 0
        dlp = (struct disklabel *)
            ((char *)bp->b_data + LABELSECTOR * lp->d_secsize + LABELOFFSET);
        *dlp = *lp;
#endif
        (*strat)(bp);
        error = biowait(bp);
 done:
        brelse(bp, 0);
        return error;
}