Changeset View
Changeset View
Standalone View
Standalone View
sys/geom/part/g_part_sgi.c
- This file was added.
| /*- | |||||
| * SPDX-License-Identifier: BSD-2-Clause | |||||
| * | |||||
| * Copyright (c) 2026 ConnectWise | |||||
| * All rights reserved. | |||||
| * | |||||
| * 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. | |||||
| * | |||||
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. | |||||
| */ | |||||
| /* | |||||
| * Read-only SGI disk label support | |||||
| * | |||||
| * Read-only just because I don't have the time to test read-write support | |||||
| */ | |||||
| #include <sys/param.h> | |||||
| #include <sys/endian.h> | |||||
| #include <sys/kernel.h> | |||||
| #include <sys/kobj.h> | |||||
| #include <sys/malloc.h> | |||||
| #include <sys/sbuf.h> | |||||
| #include <sys/systm.h> | |||||
| #include <sys/sysctl.h> | |||||
| #include <geom/geom.h> | |||||
| #include <geom/part/g_part.h> | |||||
| #include "g_part_if.h" | |||||
| FEATURE(geom_part_sgi, "GEOM partitioning class for SGI disk labels"); | |||||
| SDT_PROVIDER_DECLARE(geom); | |||||
| #define SGI_LABEL_MAGIC 0x0be5a941 | |||||
| #define SGI_MAXPARTITIONS 16 | |||||
| #define SGI_MAXVOLUMES 15 | |||||
| #define SGI_TYPE_VOLHDR 0x00 | |||||
| #define SGI_TYPE_TRKREPL 0x01 | |||||
| #define SGI_TYPE_SECREPL 0x02 | |||||
| #define SGI_TYPE_SWAP 0x03 | |||||
| #define SGI_TYPE_BSD 0x04 | |||||
| #define SGI_TYPE_SYSV 0x05 | |||||
| #define SGI_TYPE_ENTIRE_DISK 0x06 | |||||
| #define SGI_TYPE_EFS 0x07 | |||||
| #define SGI_TYPE_LVOL 0x08 | |||||
| #define SGI_TYPE_RLVOL 0x09 | |||||
| #define SGI_TYPE_XFS 0x0a | |||||
| #define SGI_TYPE_XFSLOG 0x0b | |||||
| #define SGI_TYPE_XLV 0x0c | |||||
| #define SGI_TYPE_XVM 0x0d | |||||
| #define SGI_DEVPARAM_TRACK_FWD 0x04 | |||||
| #define SGI_DEVPARAM_IGNORE_ERRORS 0x10 | |||||
| #define SGI_DEVPARAM_RESEEK 0x20 | |||||
| struct sgi_device_parameter { | |||||
| uint8_t skew; | |||||
| uint8_t gap1; | |||||
| uint8_t gap2; | |||||
| uint8_t sparecyl; | |||||
| uint16_t pcylcount; | |||||
| uint16_t head_vol0; | |||||
| uint16_t ntrks; | |||||
| uint8_t cmd_tag_queue_depth; | |||||
| uint8_t unused0; | |||||
| uint16_t unused1; | |||||
| uint16_t nsect; | |||||
| uint16_t bytes; | |||||
| uint16_t ilfact; | |||||
| uint32_t flags; | |||||
| uint32_t datarate; | |||||
| uint32_t retries_on_error; | |||||
| uint32_t ms_per_word; | |||||
| uint16_t xylogics_gap1; | |||||
| uint16_t xylogics_syncdelay; | |||||
| uint16_t xylogics_readdelay; | |||||
| uint16_t xylogics_gap2; | |||||
| uint16_t xylogics_readgate; | |||||
| uint16_t xylogics_writecont; | |||||
| } __packed; | |||||
| struct sgi_volume { | |||||
| uint8_t name[8]; | |||||
| uint32_t block_num; | |||||
| uint32_t num_bytes; | |||||
| } __packed; | |||||
| struct sgi_partition { | |||||
| uint32_t num_blocks; | |||||
| uint32_t first_block; | |||||
| uint32_t type; | |||||
| } __packed; | |||||
| struct sgi_disklabel { | |||||
| uint32_t magic; | |||||
| uint16_t root_part_num; | |||||
| uint16_t swap_part_num; | |||||
| uint8_t boot_file[16]; | |||||
| struct sgi_device_parameter devparam; | |||||
| struct sgi_volume volume[SGI_MAXVOLUMES]; | |||||
| struct sgi_partition partitions[SGI_MAXPARTITIONS]; | |||||
| uint32_t csum; | |||||
| uint32_t padding; | |||||
| } __packed; | |||||
| CTASSERT(sizeof(struct sgi_disklabel) == 512); | |||||
| struct g_part_sgi_table { | |||||
| struct g_part_table base; | |||||
| struct sgi_disklabel label; | |||||
| }; | |||||
| struct g_part_sgi_entry { | |||||
| struct g_part_entry base; | |||||
| struct sgi_partition part; | |||||
| }; | |||||
| static int g_part_sgi_add(struct g_part_table *, struct g_part_entry *, | |||||
| struct g_part_parms *); | |||||
| static int g_part_sgi_create(struct g_part_table *, struct g_part_parms *); | |||||
| static int g_part_sgi_destroy(struct g_part_table *, struct g_part_parms *); | |||||
| static void g_part_sgi_dumpconf(struct g_part_table *, struct g_part_entry *, | |||||
| struct sbuf *, const char *); | |||||
| static int g_part_sgi_dumpto(struct g_part_table *, struct g_part_entry *); | |||||
| static int g_part_sgi_modify(struct g_part_table *, struct g_part_entry *, | |||||
| struct g_part_parms *); | |||||
| static const char *g_part_sgi_name(struct g_part_table *, struct g_part_entry *, | |||||
| char *, size_t); | |||||
| static int g_part_sgi_probe(struct g_part_table *, struct g_consumer *); | |||||
| static int g_part_sgi_read(struct g_part_table *, struct g_consumer *); | |||||
| static int g_part_sgi_setunset(struct g_part_table *, struct g_part_entry *, | |||||
| const char *, unsigned int); | |||||
| static const char *g_part_sgi_type(struct g_part_table *, struct g_part_entry *, | |||||
| char *, size_t); | |||||
| static int g_part_sgi_write(struct g_part_table *, struct g_consumer *); | |||||
| static int g_part_sgi_resize(struct g_part_table *, struct g_part_entry *, | |||||
| struct g_part_parms *); | |||||
| static kobj_method_t g_part_sgi_methods[] = { | |||||
| KOBJMETHOD(g_part_add, g_part_sgi_add), | |||||
| KOBJMETHOD(g_part_create, g_part_sgi_create), | |||||
| KOBJMETHOD(g_part_destroy, g_part_sgi_destroy), | |||||
| KOBJMETHOD(g_part_dumpconf, g_part_sgi_dumpconf), | |||||
| KOBJMETHOD(g_part_dumpto, g_part_sgi_dumpto), | |||||
| KOBJMETHOD(g_part_modify, g_part_sgi_modify), | |||||
| KOBJMETHOD(g_part_resize, g_part_sgi_resize), | |||||
| KOBJMETHOD(g_part_name, g_part_sgi_name), | |||||
| KOBJMETHOD(g_part_probe, g_part_sgi_probe), | |||||
| KOBJMETHOD(g_part_read, g_part_sgi_read), | |||||
| KOBJMETHOD(g_part_setunset, g_part_sgi_setunset), | |||||
| KOBJMETHOD(g_part_type, g_part_sgi_type), | |||||
| KOBJMETHOD(g_part_write, g_part_sgi_write), | |||||
| { 0, 0 } | |||||
| }; | |||||
| static struct g_part_scheme g_part_sgi_scheme = { | |||||
| "SGI", | |||||
| g_part_sgi_methods, | |||||
| sizeof(struct g_part_sgi_table), | |||||
| .gps_entrysz = sizeof(struct g_part_sgi_entry), | |||||
| .gps_minent = SGI_MAXPARTITIONS, | |||||
| .gps_defent = SGI_MAXPARTITIONS, | |||||
| .gps_maxent = SGI_MAXPARTITIONS, | |||||
| }; | |||||
| G_PART_SCHEME_DECLARE(g_part_sgi); | |||||
| MODULE_VERSION(geom_part_sgi, 0); | |||||
| static struct g_part_sgi_alias { | |||||
| uint32_t type; | |||||
| const char *name; | |||||
| int alias; | |||||
| } sgi_alias_match[] = { | |||||
| { SGI_TYPE_SWAP, "sgi-swap", G_PART_ALIAS_FREEBSD_SWAP }, | |||||
| { SGI_TYPE_BSD, "sgi-bsd", G_PART_ALIAS_FREEBSD }, | |||||
| { SGI_TYPE_BSD, "freebsd", G_PART_ALIAS_FREEBSD }, | |||||
| { SGI_TYPE_SWAP, "freebsd-swap", G_PART_ALIAS_FREEBSD_SWAP }, | |||||
| { SGI_TYPE_EFS, "sgi-efs", -1 }, | |||||
| { SGI_TYPE_XFS, "sgi-xfs", -1 }, | |||||
| { SGI_TYPE_VOLHDR, "sgi-volhdr", -1 }, | |||||
| { SGI_TYPE_ENTIRE_DISK, "sgi-volume", -1 }, | |||||
| }; | |||||
| static uint32_t | |||||
| sgi_checksum(struct sgi_disklabel *label) | |||||
| { | |||||
| uint32_t sum, val; | |||||
| u_char *ptr; | |||||
| int count; | |||||
| sum = 0; | |||||
| count = sizeof(*label) / sizeof(uint32_t); | |||||
| ptr = (u_char *)label + sizeof(uint32_t) * (count - 1); | |||||
| while (count-- > 0) { | |||||
| memcpy(&val, ptr, sizeof(val)); | |||||
| sum -= be32toh(val); | |||||
| ptr -= sizeof(uint32_t); | |||||
| } | |||||
| return (sum); | |||||
| } | |||||
| static uint32_t | |||||
| sgi_lastblock(struct g_part_table *table, struct sgi_disklabel *label) | |||||
| { | |||||
| uint32_t cyls, heads, sect; | |||||
| cyls = be16toh(label->devparam.pcylcount); | |||||
| heads = be16toh(label->devparam.ntrks); | |||||
| sect = be16toh(label->devparam.nsect); | |||||
| if (cyls != 0 && heads != 0 && sect != 0) | |||||
| return (cyls * heads * sect); | |||||
| return (table->gpt_last + 1); | |||||
| } | |||||
| static int | |||||
| g_part_sgi_add(struct g_part_table *basetable, struct g_part_entry *baseentry, | |||||
| struct g_part_parms *gpp) | |||||
| { | |||||
| return (EOPNOTSUPP); | |||||
| } | |||||
| static int | |||||
| g_part_sgi_create(struct g_part_table *basetable, struct g_part_parms *gpp) | |||||
| { | |||||
| return (EOPNOTSUPP); | |||||
| } | |||||
| static int | |||||
| g_part_sgi_destroy(struct g_part_table *basetable, struct g_part_parms *gpp) | |||||
| { | |||||
| return (EOPNOTSUPP); | |||||
| } | |||||
| static void | |||||
| g_part_sgi_dumpconf(struct g_part_table *table, struct g_part_entry *baseentry, | |||||
| struct sbuf *sb, const char *indent) | |||||
| { | |||||
| struct g_part_sgi_entry *entry; | |||||
| entry = (struct g_part_sgi_entry *)baseentry; | |||||
| if (indent == NULL) { | |||||
| sbuf_printf(sb, " xs SGI xt %u", | |||||
| be32toh(entry->part.type)); | |||||
| } else if (entry != NULL) { | |||||
| sbuf_printf(sb, "%s<rawtype>%u</rawtype>\n", indent, | |||||
| be32toh(entry->part.type)); | |||||
| } | |||||
| } | |||||
| static int | |||||
| g_part_sgi_dumpto(struct g_part_table *table, struct g_part_entry *baseentry) | |||||
| { | |||||
| struct g_part_sgi_entry *entry; | |||||
| entry = (struct g_part_sgi_entry *)baseentry; | |||||
| return (be32toh(entry->part.type) == SGI_TYPE_SWAP ? 1 : 0); | |||||
| } | |||||
| static int | |||||
| g_part_sgi_modify(struct g_part_table *basetable, | |||||
| struct g_part_entry *baseentry, struct g_part_parms *gpp) | |||||
| { | |||||
| return (EOPNOTSUPP); | |||||
| } | |||||
| static int | |||||
| g_part_sgi_resize(struct g_part_table *basetable, | |||||
| struct g_part_entry *baseentry, struct g_part_parms *gpp) | |||||
| { | |||||
| return (EOPNOTSUPP); | |||||
| } | |||||
| static const char * | |||||
| g_part_sgi_name(struct g_part_table *table, struct g_part_entry *baseentry, | |||||
| char *buf, size_t bufsz) | |||||
| { | |||||
| snprintf(buf, bufsz, "s%d", baseentry->gpe_index); | |||||
| return (buf); | |||||
| } | |||||
| static int | |||||
| g_part_sgi_probe(struct g_part_table *table, struct g_consumer *cp) | |||||
| { | |||||
| struct g_provider *pp; | |||||
| struct sgi_disklabel *label; | |||||
| u_char *buf; | |||||
| int error; | |||||
| pp = cp->provider; | |||||
| if (pp->sectorsize < sizeof(struct sgi_disklabel) || | |||||
| pp->mediasize < pp->sectorsize) | |||||
| return (ENOSPC); | |||||
| if (sizeof(struct sgi_disklabel) % pp->sectorsize != 0) | |||||
| return (ENOTBLK); | |||||
| buf = g_read_data(cp, 0, sizeof(struct sgi_disklabel), &error); | |||||
| if (buf == NULL) | |||||
| return (error); | |||||
| label = (struct sgi_disklabel *)buf; | |||||
| if (be32toh(label->magic) != SGI_LABEL_MAGIC) { | |||||
| g_free(buf); | |||||
| return (ENXIO); | |||||
| } | |||||
| g_free(buf); | |||||
| return (G_PART_PROBE_PRI_NORM); | |||||
| } | |||||
| SDT_PROBE_DEFINE1(geom, part, sgi, read_label, "struct sgi_disklabel*"); | |||||
| SDT_PROBE_DEFINE2(geom, part, sgi, read_part, "int", "struct sgi_partition*"); | |||||
| static int | |||||
| g_part_sgi_read(struct g_part_table *basetable, struct g_consumer *cp) | |||||
| { | |||||
| struct g_provider *pp; | |||||
| struct g_part_sgi_entry *entry; | |||||
| struct g_part_sgi_table *table; | |||||
| struct sgi_disklabel *label; | |||||
| struct sgi_partition *part; | |||||
| u_char *buf; | |||||
| uint32_t blocksize, msize, nblocks, start; | |||||
| u_int heads, sectors; | |||||
| int error, index; | |||||
| pp = cp->provider; | |||||
| table = (struct g_part_sgi_table *)basetable; | |||||
| msize = MIN(pp->mediasize / pp->sectorsize, UINT32_MAX); | |||||
| buf = g_read_data(cp, 0, sizeof(struct sgi_disklabel), &error); | |||||
| if (buf == NULL) | |||||
| return (error); | |||||
| memcpy(&table->label, buf, sizeof(table->label)); | |||||
| g_free(buf); | |||||
| label = &table->label; | |||||
| SDT_PROBE1(geom, part, sgi, read_label, label); | |||||
| if (be32toh(label->magic) != SGI_LABEL_MAGIC) | |||||
| goto invalid_label; | |||||
| if (sgi_checksum(label) != 0) | |||||
| basetable->gpt_corrupt = 1; | |||||
| blocksize = be16toh(label->devparam.bytes); | |||||
| if (blocksize == 0) | |||||
| blocksize = 512; | |||||
| if (blocksize != pp->sectorsize) | |||||
| goto invalid_label; | |||||
| sectors = be16toh(label->devparam.nsect); | |||||
| if (sectors != basetable->gpt_sectors && !basetable->gpt_fixgeom) { | |||||
| off_t chs; | |||||
| g_part_geometry_heads(msize, sectors, &chs, &heads); | |||||
| if (chs != 0) { | |||||
| basetable->gpt_sectors = sectors; | |||||
| basetable->gpt_heads = heads; | |||||
| } | |||||
| } | |||||
| heads = be16toh(label->devparam.ntrks); | |||||
| if (heads != basetable->gpt_heads && !basetable->gpt_fixgeom) | |||||
| basetable->gpt_heads = heads; | |||||
| basetable->gpt_first = 0; | |||||
| basetable->gpt_last = msize - 1; | |||||
| basetable->gpt_isleaf = 1; | |||||
| basetable->gpt_entries = SGI_MAXPARTITIONS; | |||||
| for (index = 0; index < SGI_MAXPARTITIONS; index++) { | |||||
| part = &label->partitions[index]; | |||||
| nblocks = be32toh(part->num_blocks); | |||||
| SDT_PROBE2(geom, part, sgi, read_part, index, part); | |||||
| if (nblocks == 0) | |||||
| continue; | |||||
| start = be32toh(part->first_block); | |||||
| if (start > basetable->gpt_last || | |||||
| nblocks > basetable->gpt_last + 1 - start) { | |||||
| goto invalid_label; | |||||
| } | |||||
| entry = (struct g_part_sgi_entry *)g_part_new_entry(basetable, | |||||
| index, start, start + nblocks - 1); | |||||
| entry->part = *part; | |||||
| if (be32toh(part->type) == SGI_TYPE_ENTIRE_DISK) | |||||
| entry->base.gpe_internal = 1; | |||||
| } | |||||
| return (0); | |||||
| invalid_label: | |||||
| printf("GEOM: %s: invalid SGI disklabel.\n", pp->name); | |||||
| bzero(&table->label, sizeof(table->label)); | |||||
| return (EINVAL); | |||||
| } | |||||
| static int | |||||
| g_part_sgi_setunset(struct g_part_table *basetable, | |||||
| struct g_part_entry *baseentry, const char *attrib, unsigned int set) | |||||
| { | |||||
| return (EOPNOTSUPP); | |||||
| } | |||||
| static const char * | |||||
| g_part_sgi_type(struct g_part_table *basetable, struct g_part_entry *baseentry, | |||||
| char *buf, size_t bufsz) | |||||
| { | |||||
| struct g_part_sgi_entry *entry; | |||||
| uint32_t type; | |||||
| int i; | |||||
| entry = (struct g_part_sgi_entry *)baseentry; | |||||
| type = be32toh(entry->part.type); | |||||
| if (type == SGI_TYPE_SWAP) | |||||
| return (g_part_alias_name(G_PART_ALIAS_FREEBSD_SWAP)); | |||||
| if (type == SGI_TYPE_BSD) | |||||
| return (g_part_alias_name(G_PART_ALIAS_FREEBSD)); | |||||
| for (i = 0; i < nitems(sgi_alias_match); i++) { | |||||
| if (sgi_alias_match[i].alias >= 0) | |||||
| continue; | |||||
| if (type == sgi_alias_match[i].type) | |||||
| return (sgi_alias_match[i].name); | |||||
| } | |||||
| snprintf(buf, bufsz, "!%u", type); | |||||
| return (buf); | |||||
| } | |||||
| static int | |||||
| g_part_sgi_write(struct g_part_table *basetable, struct g_consumer *cp) | |||||
| { | |||||
| return (EOPNOTSUPP); | |||||
| } | |||||