/* $NetBSD: disksubr.c,v 1.32 2023/02/04 02:08:03 tsutsui 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 __KERNEL_RCSID(0, "$NetBSD: disksubr.c,v 1.32 2023/02/04 02:08:03 tsutsui Exp $"); #include #include #include #define FSTYPENAMES #include #include #include #include /* XXX for fs.h */ #include /* 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 archtypal 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; }