diff --git a/lib/geom/part/gpart.8 b/lib/geom/part/gpart.8 --- a/lib/geom/part/gpart.8 +++ b/lib/geom/part/gpart.8 @@ -22,7 +22,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd May 12, 2026 +.Dd June 5, 2026 .Dt GPART 8 .Os .Sh NAME @@ -611,6 +611,12 @@ option enables backward compatibility for partition names in the EBR scheme. It also prevents any type of actions on such partitions. +.It Cm SGI +SGI disklabel, used on Silicon Graphics workstations and servers running IRIX. +The label occupies the first sector of the disk. +Requires the +.Cd GEOM_PART_SGI +kernel option. .El .Pp See @@ -667,7 +673,9 @@ .Qq Li "!165" for MBR, .Qq Li "!FreeBSD" -for APM, and +for APM, +.Qq Li "!4" +for SGI, and .Qq Li "!516e7cb4-6ecf-11d6-8ff8-00022d09712b" for GPT. .It Cm freebsd-boot @@ -683,7 +691,9 @@ partition dedicated to swap space. The scheme-specific types are .Qq Li "!FreeBSD-swap" -for APM, and +for APM, +.Qq Li "!3" +for SGI, and .Qq Li "!516e7cb5-6ecf-11d6-8ff8-00022d09712b" for GPT. .It Cm freebsd-ufs @@ -973,6 +983,32 @@ for MBR and .Qq Li "!9e1a2d38-c612-4316-aa26-8b49521e5a8b" for GPT. +.It Cm sgi-bsd +A partition containing a BSD disklabel, for use on SGI systems. +The scheme-specific type is +.Qq Li "!4" . +.It Cm sgi-efs +An SGI Extent File System (EFS) partition. +The scheme-specific type is +.Qq Li "!7" . +.It Cm sgi-swap +An SGI swap partition. +The scheme-specific type is +.Qq Li "!3" . +.It Cm sgi-volhdr +An SGI volume header partition, typically used at the start of the disk +to hold standalone programs such as the system sash and fx utilities. +The scheme-specific type is +.Qq Li "!0" . +.It Cm sgi-volume +An SGI partition spanning the entire disk. +IRIX conventionally places this on partition 11. +The scheme-specific type is +.Qq Li "!6" . +.It Cm sgi-xfs +An SGI XFS filesystem partition. +The scheme-specific type is +.Qq Li "!a" . .It Cm solaris-boot A illumos/Solaris partition dedicated to boot loader. The scheme-specific type is diff --git a/share/man/man4/geom.4 b/share/man/man4/geom.4 --- a/share/man/man4/geom.4 +++ b/share/man/man4/geom.4 @@ -59,6 +59,7 @@ .Cd options GEOM_PART_GPT .Cd options GEOM_PART_LDM .Cd options GEOM_PART_MBR +.Cd options GEOM_PART_SGI .Cd options GEOM_RAID .Cd options GEOM_RAID3 .Cd options GEOM_SHSEC diff --git a/sys/conf/NOTES b/sys/conf/NOTES --- a/sys/conf/NOTES +++ b/sys/conf/NOTES @@ -172,6 +172,7 @@ options GEOM_PART_GPT # GPT partitioning options GEOM_PART_LDM # Logical Disk Manager options GEOM_PART_MBR # MBR partitioning +options GEOM_PART_SGI # SGI disklabel options GEOM_RAID # Soft RAID functionality. options GEOM_RAID3 # RAID3 functionality. options GEOM_SHSEC # Shared secret. diff --git a/sys/conf/files b/sys/conf/files --- a/sys/conf/files +++ b/sys/conf/files @@ -3768,6 +3768,7 @@ geom/part/g_part_gpt.c optional geom_part_gpt geom/part/g_part_ldm.c optional geom_part_ldm geom/part/g_part_mbr.c optional geom_part_mbr +geom/part/g_part_sgi.c optional geom_part_sgi geom/raid/g_raid.c optional geom_raid geom/raid/g_raid_ctl.c optional geom_raid geom/raid/g_raid_md_if.m optional geom_raid diff --git a/sys/conf/options b/sys/conf/options --- a/sys/conf/options +++ b/sys/conf/options @@ -130,6 +130,7 @@ GEOM_PART_GPT opt_geom.h GEOM_PART_LDM opt_geom.h GEOM_PART_MBR opt_geom.h +GEOM_PART_SGI opt_geom.h GEOM_RAID opt_geom.h GEOM_RAID3 opt_geom.h GEOM_SHSEC opt_geom.h diff --git a/sys/geom/part/g_part_sgi.c b/sys/geom/part/g_part_sgi.c new file mode 100644 --- /dev/null +++ b/sys/geom/part/g_part_sgi.c @@ -0,0 +1,448 @@ +/*- + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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%u\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); +} diff --git a/sys/modules/geom/geom_part/Makefile b/sys/modules/geom/geom_part/Makefile --- a/sys/modules/geom/geom_part/Makefile +++ b/sys/modules/geom/geom_part/Makefile @@ -4,6 +4,7 @@ geom_part_ebr \ geom_part_gpt \ geom_part_ldm \ - geom_part_mbr + geom_part_mbr \ + geom_part_sgi .include diff --git a/sys/modules/geom/geom_part/geom_part_sgi/Makefile b/sys/modules/geom/geom_part/geom_part_sgi/Makefile new file mode 100644 --- /dev/null +++ b/sys/modules/geom/geom_part/geom_part_sgi/Makefile @@ -0,0 +1,8 @@ +.PATH: ${SRCTOP}/sys/geom/part + +KMOD= geom_part_sgi +SRCS= g_part_sgi.c + +SRCS+= bus_if.h device_if.h g_part_if.h + +.include diff --git a/tests/sys/geom/class/part/Makefile b/tests/sys/geom/class/part/Makefile --- a/tests/sys/geom/class/part/Makefile +++ b/tests/sys/geom/class/part/Makefile @@ -4,5 +4,8 @@ # TODO: port the perl tests in tools/regression/geom_gpt ATF_TESTS_SH+= misc +ATF_TESTS_SH+= sgi + +${PACKAGE}FILES+= sgi-golden.hdr .include diff --git a/tests/sys/geom/class/part/sgi-golden.hdr b/tests/sys/geom/class/part/sgi-golden.hdr new file mode 100644 index 0000000000000000000000000000000000000000..79fb4aa04b6461b272491a3109093bdb5b52a43f GIT binary patch literal 512 zc%0*Yy3&z>fq_xKG%vG)0TnP#1`+}wRyhL`18%^*gMlG4B^AbHVqmplC{8TSfN~dr q_`?lwZ~nl*z#;%*axt)gC@`CudNv4Pw|57e$wrCko-F??Kr{eh)(!Ol literal 0 Hc$@