1dd3a790marcel/*-
2a82e3a8pfg * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3a82e3a8pfg *
4dd3a790marcel * Copyright (c) 2008 Marcel Moolenaar
5dd3a790marcel * All rights reserved.
6dd3a790marcel *
7dd3a790marcel * Redistribution and use in source and binary forms, with or without
8dd3a790marcel * modification, are permitted provided that the following conditions
9dd3a790marcel * are met:
10dd3a790marcel *
11dd3a790marcel * 1. Redistributions of source code must retain the above copyright
12dd3a790marcel *    notice, this list of conditions and the following disclaimer.
13dd3a790marcel * 2. Redistributions in binary form must reproduce the above copyright
14dd3a790marcel *    notice, this list of conditions and the following disclaimer in the
15dd3a790marcel *    documentation and/or other materials provided with the distribution.
16dd3a790marcel *
17dd3a790marcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18dd3a790marcel * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19dd3a790marcel * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20dd3a790marcel * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21dd3a790marcel * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22dd3a790marcel * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23dd3a790marcel * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24dd3a790marcel * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25dd3a790marcel * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26dd3a790marcel * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27dd3a790marcel */
28dd3a790marcel
29dd3a790marcel#include <sys/cdefs.h>
30dd3a790marcel__FBSDID("$FreeBSD$");
31dd3a790marcel
32dd3a790marcel#include <sys/param.h>
33dd3a790marcel#include <sys/bio.h>
34dd3a790marcel#include <sys/endian.h>
35dd3a790marcel#include <sys/kernel.h>
36dd3a790marcel#include <sys/kobj.h>
37dd3a790marcel#include <sys/limits.h>
38dd3a790marcel#include <sys/lock.h>
39dd3a790marcel#include <sys/malloc.h>
40dd3a790marcel#include <sys/mutex.h>
41dd3a790marcel#include <sys/queue.h>
42dd3a790marcel#include <sys/sbuf.h>
43dd3a790marcel#include <sys/systm.h>
446bf702anetchild#include <sys/sysctl.h>
45dd3a790marcel#include <sys/vtoc.h>
46dd3a790marcel#include <geom/geom.h>
478488e49ae#include <geom/geom_int.h>
48dd3a790marcel#include <geom/part/g_part.h>
49dd3a790marcel
50dd3a790marcel#include "g_part_if.h"
51dd3a790marcel
526bf702anetchildFEATURE(geom_part_vtoc8, "GEOM partitioning class for SMI VTOC8 disk labels");
536bf702anetchild
54dd3a790marcelstruct g_part_vtoc8_table {
55dd3a790marcel	struct g_part_table	base;
56dd3a790marcel	struct vtoc8		vtoc;
57dd3a790marcel	uint32_t		secpercyl;
58dd3a790marcel};
59dd3a790marcel
60dd3a790marcelstatic int g_part_vtoc8_add(struct g_part_table *, struct g_part_entry *,
61dd3a790marcel    struct g_part_parms *);
62dd3a790marcelstatic int g_part_vtoc8_create(struct g_part_table *, struct g_part_parms *);
63dd3a790marcelstatic int g_part_vtoc8_destroy(struct g_part_table *, struct g_part_parms *);
64e326b6bmariusstatic void g_part_vtoc8_dumpconf(struct g_part_table *,
65e326b6bmarius    struct g_part_entry *, struct sbuf *, const char *);
66dd3a790marcelstatic int g_part_vtoc8_dumpto(struct g_part_table *, struct g_part_entry *);
67e326b6bmariusstatic int g_part_vtoc8_modify(struct g_part_table *, struct g_part_entry *,
68dd3a790marcel    struct g_part_parms *);
69e326b6bmariusstatic const char *g_part_vtoc8_name(struct g_part_table *,
70e326b6bmarius    struct g_part_entry *, char *, size_t);
71dd3a790marcelstatic int g_part_vtoc8_probe(struct g_part_table *, struct g_consumer *);
72dd3a790marcelstatic int g_part_vtoc8_read(struct g_part_table *, struct g_consumer *);
73e326b6bmariusstatic const char *g_part_vtoc8_type(struct g_part_table *,
74e326b6bmarius    struct g_part_entry *, char *, size_t);
75dd3a790marcelstatic int g_part_vtoc8_write(struct g_part_table *, struct g_consumer *);
76e326b6bmariusstatic int g_part_vtoc8_resize(struct g_part_table *, struct g_part_entry *,
77be854afmarcel    struct g_part_parms *);
78dd3a790marcel
79dd3a790marcelstatic kobj_method_t g_part_vtoc8_methods[] = {
80dd3a790marcel	KOBJMETHOD(g_part_add,		g_part_vtoc8_add),
81dd3a790marcel	KOBJMETHOD(g_part_create,	g_part_vtoc8_create),
82dd3a790marcel	KOBJMETHOD(g_part_destroy,	g_part_vtoc8_destroy),
8338e8adfmarcel	KOBJMETHOD(g_part_dumpconf,	g_part_vtoc8_dumpconf),
84dd3a790marcel	KOBJMETHOD(g_part_dumpto,	g_part_vtoc8_dumpto),
85dd3a790marcel	KOBJMETHOD(g_part_modify,	g_part_vtoc8_modify),
86be854afmarcel	KOBJMETHOD(g_part_resize,	g_part_vtoc8_resize),
87dd3a790marcel	KOBJMETHOD(g_part_name,		g_part_vtoc8_name),
88dd3a790marcel	KOBJMETHOD(g_part_probe,	g_part_vtoc8_probe),
89dd3a790marcel	KOBJMETHOD(g_part_read,		g_part_vtoc8_read),
90dd3a790marcel	KOBJMETHOD(g_part_type,		g_part_vtoc8_type),
91dd3a790marcel	KOBJMETHOD(g_part_write,	g_part_vtoc8_write),
92dd3a790marcel	{ 0, 0 }
93dd3a790marcel};
94dd3a790marcel
95dd3a790marcelstatic struct g_part_scheme g_part_vtoc8_scheme = {
96dd3a790marcel	"VTOC8",
97dd3a790marcel	g_part_vtoc8_methods,
98dd3a790marcel	sizeof(struct g_part_vtoc8_table),
99dd3a790marcel	.gps_entrysz = sizeof(struct g_part_entry),
100dd3a790marcel	.gps_minent = VTOC8_NPARTS,
101dd3a790marcel	.gps_maxent = VTOC8_NPARTS,
102dd3a790marcel};
103c184f6cmarcelG_PART_SCHEME_DECLARE(g_part_vtoc8);
104dd6f2f2kevansMODULE_VERSION(geom_part_vtoc8, 0);
105dd3a790marcel
106dd3a790marcelstatic int
107dd3a790marcelvtoc8_parse_type(const char *type, uint16_t *tag)
108dd3a790marcel{
109dd3a790marcel	const char *alias;
110dd3a790marcel	char *endp;
111dd3a790marcel	long lt;
112dd3a790marcel
113dd3a790marcel	if (type[0] == '!') {
114dd3a790marcel		lt = strtol(type + 1, &endp, 0);
115dd3a790marcel		if (type[1] == '\0' || *endp != '\0' || lt <= 0 ||
116dd3a790marcel		    lt >= 65536)
117dd3a790marcel			return (EINVAL);
118dd3a790marcel		*tag = (uint16_t)lt;
119dd3a790marcel		return (0);
120dd3a790marcel	}
1215306b1emarcel	alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_NANDFS);
1225306b1emarcel	if (!strcasecmp(type, alias)) {
1235306b1emarcel		*tag = VTOC_TAG_FREEBSD_NANDFS;
1245306b1emarcel		return (0);
1255306b1emarcel	}
126dd3a790marcel	alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_SWAP);
127dd3a790marcel	if (!strcasecmp(type, alias)) {
128dd3a790marcel		*tag = VTOC_TAG_FREEBSD_SWAP;
129dd3a790marcel		return (0);
130dd3a790marcel	}
131dd3a790marcel	alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_UFS);
132dd3a790marcel	if (!strcasecmp(type, alias)) {
133dd3a790marcel		*tag = VTOC_TAG_FREEBSD_UFS;
134dd3a790marcel		return (0);
135dd3a790marcel	}
136dd3a790marcel	alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_VINUM);
137dd3a790marcel	if (!strcasecmp(type, alias)) {
138dd3a790marcel		*tag = VTOC_TAG_FREEBSD_VINUM;
139dd3a790marcel		return (0);
140dd3a790marcel	}
141dd3a790marcel	alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_ZFS);
142dd3a790marcel	if (!strcasecmp(type, alias)) {
143dd3a790marcel		*tag = VTOC_TAG_FREEBSD_ZFS;
144dd3a790marcel		return (0);
145dd3a790marcel	}
146dd3a790marcel	return (EINVAL);
147dd3a790marcel}
148dd3a790marcel
149dd3a790marcelstatic int
150000f6e7aevtoc8_align(struct g_part_vtoc8_table *table, uint64_t *start, uint64_t *size)
151000f6e7ae{
152000f6e7ae
153000f6e7ae	if (*size < table->secpercyl)
154000f6e7ae		return (EINVAL);
155000f6e7ae	if (start != NULL && (*start % table->secpercyl)) {
156000f6e7ae		*size += (*start % table->secpercyl) - table->secpercyl;
157000f6e7ae		*start -= (*start % table->secpercyl) - table->secpercyl;
158000f6e7ae	}
159000f6e7ae	if (*size % table->secpercyl)
160000f6e7ae		*size -= (*size % table->secpercyl);
161000f6e7ae	if (*size < table->secpercyl)
162000f6e7ae		return (EINVAL);
163000f6e7ae	return (0);
164000f6e7ae}
165000f6e7ae
166000f6e7aestatic int
167dd3a790marcelg_part_vtoc8_add(struct g_part_table *basetable, struct g_part_entry *entry,
168dd3a790marcel    struct g_part_parms *gpp)
169dd3a790marcel{
170dd3a790marcel	struct g_part_vtoc8_table *table;
171dd3a790marcel	int error, index;
172dd3a790marcel	uint64_t start, size;
173dd3a790marcel	uint16_t tag;
174dd3a790marcel
175dd3a790marcel	if (gpp->gpp_parms & G_PART_PARM_LABEL)
176dd3a790marcel		return (EINVAL);
177dd3a790marcel
178dd3a790marcel	error = vtoc8_parse_type(gpp->gpp_type, &tag);
179dd3a790marcel	if (error)
180dd3a790marcel		return (error);
181dd3a790marcel
182dd3a790marcel	table = (struct g_part_vtoc8_table *)basetable;
183dd3a790marcel	index = entry->gpe_index - 1;
184dd3a790marcel	start = gpp->gpp_start;
185dd3a790marcel	size = gpp->gpp_size;
186000f6e7ae	if (vtoc8_align(table, &start, &size) != 0)
187dd3a790marcel		return (EINVAL);
188dd3a790marcel
189dd3a790marcel	KASSERT(entry->gpe_start <= start, (__func__));
190dd3a790marcel	KASSERT(entry->gpe_end >= start + size - 1, (__func__));
191dd3a790marcel	entry->gpe_start = start;
192dd3a790marcel	entry->gpe_end = start + size - 1;
193dd3a790marcel
194dd3a790marcel	be16enc(&table->vtoc.part[index].tag, tag);
195dd3a790marcel	be16enc(&table->vtoc.part[index].flag, 0);
196dd3a790marcel	be32enc(&table->vtoc.timestamp[index], 0);
197dd3a790marcel	be32enc(&table->vtoc.map[index].cyl, start / table->secpercyl);
198dd3a790marcel	be32enc(&table->vtoc.map[index].nblks, size);
199dd3a790marcel	return (0);
200dd3a790marcel}
201dd3a790marcel
202dd3a790marcelstatic int
203dd3a790marcelg_part_vtoc8_create(struct g_part_table *basetable, struct g_part_parms *gpp)
204dd3a790marcel{
205dd3a790marcel	struct g_provider *pp;
206dd3a790marcel	struct g_part_entry *entry;
207dd3a790marcel	struct g_part_vtoc8_table *table;
208dd3a790marcel	uint64_t msize;
209dd3a790marcel	uint32_t acyls, ncyls, pcyls;
210dd3a790marcel
211dd3a790marcel	pp = gpp->gpp_provider;
212dd3a790marcel
213dd3a790marcel	if (pp->sectorsize < sizeof(struct vtoc8))
214dd3a790marcel		return (ENOSPC);
215dd3a790marcel	if (pp->sectorsize > sizeof(struct vtoc8))
216dd3a790marcel		return (ENXIO);
217dd3a790marcel
218dd3a790marcel	table = (struct g_part_vtoc8_table *)basetable;
219dd3a790marcel
220c286c25ae	msize = MIN(pp->mediasize / pp->sectorsize, UINT32_MAX);
221dd3a790marcel	table->secpercyl = basetable->gpt_sectors * basetable->gpt_heads;
222dd3a790marcel	pcyls = msize / table->secpercyl;
223dd3a790marcel	acyls = 2;
224dd3a790marcel	ncyls = pcyls - acyls;
225dd3a790marcel	msize = ncyls * table->secpercyl;
226dd3a790marcel
2278d66dafdelphij	snprintf(table->vtoc.ascii, sizeof(table->vtoc.ascii),
2288d66dafdelphij	    "FreeBSD%lldM cyl %u alt %u hd %u sec %u",
229dd3a790marcel	    (long long)(msize / 2048), ncyls, acyls, basetable->gpt_heads,
230dd3a790marcel	    basetable->gpt_sectors);
231dd3a790marcel	be32enc(&table->vtoc.version, VTOC_VERSION);
232dd3a790marcel	be16enc(&table->vtoc.nparts, VTOC8_NPARTS);
233dd3a790marcel	be32enc(&table->vtoc.sanity, VTOC_SANITY);
234dd3a790marcel	be16enc(&table->vtoc.rpm, 3600);
235dd3a790marcel	be16enc(&table->vtoc.physcyls, pcyls);
236dd3a790marcel	be16enc(&table->vtoc.ncyls, ncyls);
237dd3a790marcel	be16enc(&table->vtoc.altcyls, acyls);
238dd3a790marcel	be16enc(&table->vtoc.nheads, basetable->gpt_heads);
239dd3a790marcel	be16enc(&table->vtoc.nsecs, basetable->gpt_sectors);
240dd3a790marcel	be16enc(&table->vtoc.magic, VTOC_MAGIC);
241dd3a790marcel
242dd3a790marcel	basetable->gpt_first = 0;
243dd3a790marcel	basetable->gpt_last = msize - 1;
244dd3a790marcel	basetable->gpt_isleaf = 1;
245dd3a790marcel
246dd3a790marcel	entry = g_part_new_entry(basetable, VTOC_RAW_PART + 1,
247dd3a790marcel	    basetable->gpt_first, basetable->gpt_last);
248dd3a790marcel	entry->gpe_internal = 1;
249dd3a790marcel	be16enc(&table->vtoc.part[VTOC_RAW_PART].tag, VTOC_TAG_BACKUP);
250dd3a790marcel	be32enc(&table->vtoc.map[VTOC_RAW_PART].nblks, msize);
251dd3a790marcel	return (0);
252dd3a790marcel}
253dd3a790marcel
254dd3a790marcelstatic int
255dd3a790marcelg_part_vtoc8_destroy(struct g_part_table *basetable, struct g_part_parms *gpp)
256dd3a790marcel{
257dd3a790marcel
258dd3a790marcel	/* Wipe the first sector to clear the partitioning. */
259dd3a790marcel	basetable->gpt_smhead |= 1;
260dd3a790marcel	return (0);
261dd3a790marcel}
262dd3a790marcel
263fb9da0fimpstatic void
26438e8adfmarcelg_part_vtoc8_dumpconf(struct g_part_table *basetable,
26538e8adfmarcel    struct g_part_entry *entry, struct sbuf *sb, const char *indent)
26638e8adfmarcel{
26738e8adfmarcel	struct g_part_vtoc8_table *table;
26838e8adfmarcel
26938e8adfmarcel	table = (struct g_part_vtoc8_table *)basetable;
2701053568marcel	if (indent == NULL) {
2711053568marcel		/* conftxt: libdisk compatibility */
2721053568marcel		sbuf_printf(sb, " xs SUN sc %u hd %u alt %u",
2731053568marcel		    be16dec(&table->vtoc.nsecs), be16dec(&table->vtoc.nheads),
2741053568marcel		    be16dec(&table->vtoc.altcyls));
2751053568marcel	} else if (entry != NULL) {
2761053568marcel		/* confxml: partition entry information */
2771053568marcel		sbuf_printf(sb, "%s<rawtype>%u</rawtype>\n", indent,
2781053568marcel		    be16dec(&table->vtoc.part[entry->gpe_index - 1].tag));
2791053568marcel	} else {
2801053568marcel		/* confxml: scheme information */
2811053568marcel	}
28238e8adfmarcel}
28338e8adfmarcel
28438e8adfmarcelstatic int
285e326b6bmariusg_part_vtoc8_dumpto(struct g_part_table *basetable,
286e326b6bmarius    struct g_part_entry *entry)
287dd3a790marcel{
288dd3a790marcel	struct g_part_vtoc8_table *table;
289dd3a790marcel	uint16_t tag;
290dd3a790marcel
291c02233bmarcel	/*
292c02233bmarcel	 * Allow dumping to a swap partition or a partition that
293c02233bmarcel	 * has no type.
294c02233bmarcel	 */
295dd3a790marcel	table = (struct g_part_vtoc8_table *)basetable;
296dd3a790marcel	tag = be16dec(&table->vtoc.part[entry->gpe_index - 1].tag);
297081cdfemarius	return ((tag == 0 || tag == VTOC_TAG_FREEBSD_SWAP ||
298081cdfemarius	    tag == VTOC_TAG_SWAP) ? 1 : 0);
299dd3a790marcel}
300dd3a790marcel
301dd3a790marcelstatic int
302dd3a790marcelg_part_vtoc8_modify(struct g_part_table *basetable,
303dd3a790marcel    struct g_part_entry *entry, struct g_part_parms *gpp)
304dd3a790marcel{
305dd3a790marcel	struct g_part_vtoc8_table *table;
306dd3a790marcel	int error;
307dd3a790marcel	uint16_t tag;
308dd3a790marcel
309dd3a790marcel	if (gpp->gpp_parms & G_PART_PARM_LABEL)
310dd3a790marcel		return (EINVAL);
311dd3a790marcel
312dd3a790marcel	table = (struct g_part_vtoc8_table *)basetable;
313dd3a790marcel	if (gpp->gpp_parms & G_PART_PARM_TYPE) {
314dd3a790marcel		error = vtoc8_parse_type(gpp->gpp_type, &tag);
315dd3a790marcel		if (error)
316dd3a790marcel			return(error);
317dd3a790marcel
318dd3a790marcel		be16enc(&table->vtoc.part[entry->gpe_index - 1].tag, tag);
319dd3a790marcel	}
320dd3a790marcel	return (0);
321dd3a790marcel}
322dd3a790marcel
323be854afmarcelstatic int
32405ca533aevtoc8_set_rawsize(struct g_part_table *basetable, struct g_provider *pp)
32505ca533ae{
32605ca533ae	struct g_part_vtoc8_table *table;
32705ca533ae	struct g_part_entry *baseentry;
32805ca533ae	off_t msize;
32905ca533ae	uint32_t acyls, ncyls, pcyls;
33005ca533ae
33105ca533ae	table = (struct g_part_vtoc8_table *)basetable;
33205ca533ae	msize = MIN(pp->mediasize / pp->sectorsize, UINT32_MAX);
33305ca533ae	pcyls = msize / table->secpercyl;
33405ca533ae	if (pcyls > UINT16_MAX)
33505ca533ae		return (ERANGE);
33605ca533ae	acyls = be16dec(&table->vtoc.altcyls);
33705ca533ae	ncyls = pcyls - acyls;
33805ca533ae	msize = ncyls * table->secpercyl;
33905ca533ae	basetable->gpt_last = msize - 1;
34005ca533ae
34105ca533ae	bzero(table->vtoc.ascii, sizeof(table->vtoc.ascii));
3428d66dafdelphij	snprintf(table->vtoc.ascii, sizeof(table->vtoc.ascii),
3438d66dafdelphij	    "FreeBSD%lldM cyl %u alt %u hd %u sec %u",
34405ca533ae	    (long long)(msize / 2048), ncyls, acyls, basetable->gpt_heads,
34505ca533ae	    basetable->gpt_sectors);
34605ca533ae	be16enc(&table->vtoc.physcyls, pcyls);
34705ca533ae	be16enc(&table->vtoc.ncyls, ncyls);
34805ca533ae	be32enc(&table->vtoc.map[VTOC_RAW_PART].nblks, msize);
34905ca533ae	if (be32dec(&table->vtoc.sanity) == VTOC_SANITY)
35005ca533ae		be16enc(&table->vtoc.part[VTOC_RAW_PART].tag, VTOC_TAG_BACKUP);
35105ca533ae	LIST_FOREACH(baseentry, &basetable->gpt_entry, gpe_entry) {
35205ca533ae		if (baseentry->gpe_index == VTOC_RAW_PART + 1) {
35305ca533ae			baseentry->gpe_end = basetable->gpt_last;
35405ca533ae			return (0);
35505ca533ae		}
35605ca533ae	}
35705ca533ae	return (ENXIO);
35805ca533ae}
35905ca533ae
36005ca533aestatic int
361be854afmarcelg_part_vtoc8_resize(struct g_part_table *basetable,
362be854afmarcel    struct g_part_entry *entry, struct g_part_parms *gpp)
363be854afmarcel{
364be854afmarcel	struct g_part_vtoc8_table *table;
36505ca533ae	struct g_provider *pp;
366be854afmarcel	uint64_t size;
367be854afmarcel
36805ca533ae	if (entry == NULL) {
36905ca533ae		pp = LIST_FIRST(&basetable->gpt_gp->consumer)->provider;
37005ca533ae		return (vtoc8_set_rawsize(basetable, pp));
37105ca533ae	}
372be854afmarcel	table = (struct g_part_vtoc8_table *)basetable;
373be854afmarcel	size = gpp->gpp_size;
374000f6e7ae	if (vtoc8_align(table, NULL, &size) != 0)
375be854afmarcel		return (EINVAL);
3768488e49ae	/* XXX: prevent unexpected shrinking. */
3778488e49ae	pp = entry->gpe_pp;
37806fbdebmav	if ((g_debugflags & G_F_FOOTSHOOTING) == 0 && size < gpp->gpp_size &&
3799a65da5ae	    pp->mediasize / pp->sectorsize > size)
3808488e49ae		return (EBUSY);
381be854afmarcel	entry->gpe_end = entry->gpe_start + size - 1;
382be854afmarcel	be32enc(&table->vtoc.map[entry->gpe_index - 1].nblks, size);
383be854afmarcel
384be854afmarcel	return (0);
385be854afmarcel}
386be854afmarcel
387fb9da0fimpstatic const char *
388dd3a790marcelg_part_vtoc8_name(struct g_part_table *table, struct g_part_entry *baseentry,
389dd3a790marcel    char *buf, size_t bufsz)
390dd3a790marcel{
391dd3a790marcel
392dd3a790marcel	snprintf(buf, bufsz, "%c", 'a' + baseentry->gpe_index - 1);
393dd3a790marcel	return (buf);
394dd3a790marcel}
395dd3a790marcel
396dd3a790marcelstatic int
397dd3a790marcelg_part_vtoc8_probe(struct g_part_table *table, struct g_consumer *cp)
398dd3a790marcel{
399dd3a790marcel	struct g_provider *pp;
400dd3a790marcel	u_char *buf;
401dd3a790marcel	int error, ofs, res;
402dd3a790marcel	uint16_t cksum, magic;
403dd3a790marcel
404dd3a790marcel	pp = cp->provider;
405dd3a790marcel
406dd3a790marcel	/* Sanity-check the provider. */
407dd3a790marcel	if (pp->sectorsize != sizeof(struct vtoc8))
408dd3a790marcel		return (ENOSPC);
409dd3a790marcel
410dd3a790marcel	/* Check that there's a disklabel. */
411dd3a790marcel	buf = g_read_data(cp, 0, pp->sectorsize, &error);
412dd3a790marcel	if (buf == NULL)
413dd3a790marcel		return (error);
414dd3a790marcel
415dd3a790marcel	res = ENXIO;	/* Assume mismatch */
416dd3a790marcel
417dd3a790marcel	/* Check the magic */
418dd3a790marcel	magic = be16dec(buf + offsetof(struct vtoc8, magic));
419dd3a790marcel	if (magic != VTOC_MAGIC)
420dd3a790marcel		goto out;
421dd3a790marcel
422dd3a790marcel	/* Check the sum */
423dd3a790marcel	cksum = 0;
424dd3a790marcel	for (ofs = 0; ofs < sizeof(struct vtoc8); ofs += 2)
425dd3a790marcel		cksum ^= be16dec(buf + ofs);
426dd3a790marcel	if (cksum != 0)
427dd3a790marcel		goto out;
428dd3a790marcel
429dd3a790marcel	res = G_PART_PROBE_PRI_NORM;
430dd3a790marcel
431dd3a790marcel out:
432dd3a790marcel	g_free(buf);
433dd3a790marcel	return (res);
434dd3a790marcel}
435dd3a790marcel
436dd3a790marcelstatic int
437dd3a790marcelg_part_vtoc8_read(struct g_part_table *basetable, struct g_consumer *cp)
438dd3a790marcel{
439dd3a790marcel	struct g_provider *pp;
440dd3a790marcel	struct g_part_vtoc8_table *table;
441dd3a790marcel	struct g_part_entry *entry;
442dd3a790marcel	u_char *buf;
443dd3a790marcel	off_t chs, msize;
444dd3a790marcel	uint64_t offset, size;
445dd3a790marcel	u_int cyls, heads, sectors;
446dd3a790marcel	int error, index, withtags;
447dd3a790marcel	uint16_t tag;
448dd3a790marcel
449dd3a790marcel	pp = cp->provider;
450dd3a790marcel	buf = g_read_data(cp, 0, pp->sectorsize, &error);
451dd3a790marcel	if (buf == NULL)
452dd3a790marcel		return (error);
453dd3a790marcel
454dd3a790marcel	table = (struct g_part_vtoc8_table *)basetable;
455dd3a790marcel	bcopy(buf, &table->vtoc, sizeof(table->vtoc));
456dd3a790marcel	g_free(buf);
457dd3a790marcel
458c286c25ae	msize = MIN(pp->mediasize / pp->sectorsize, UINT32_MAX);
459dd3a790marcel	sectors = be16dec(&table->vtoc.nsecs);
46087687bfmarius	if (sectors < 1)
461dd3a790marcel		goto invalid_label;
462dd3a790marcel	if (sectors != basetable->gpt_sectors && !basetable->gpt_fixgeom) {
463dd3a790marcel		g_part_geometry_heads(msize, sectors, &chs, &heads);
464dd3a790marcel		if (chs != 0) {
465dd3a790marcel			basetable->gpt_sectors = sectors;
466dd3a790marcel			basetable->gpt_heads = heads;
467dd3a790marcel		}
468dd3a790marcel	}
469dd3a790marcel
470dd3a790marcel	heads = be16dec(&table->vtoc.nheads);
47187687bfmarius	if (heads < 1)
472dd3a790marcel		goto invalid_label;
473dd3a790marcel	if (heads != basetable->gpt_heads && !basetable->gpt_fixgeom)
474dd3a790marcel		basetable->gpt_heads = heads;
47587687bfmarius	/*
47687687bfmarius	 * Except for ATA disks > 32GB, Solaris uses the native geometry
47787687bfmarius	 * as reported by the target for the labels while da(4) typically
47887687bfmarius	 * uses a synthetic one so we don't complain too loudly if these
47987687bfmarius	 * geometries don't match.
48087687bfmarius	 */
48187687bfmarius	if (bootverbose && (sectors != basetable->gpt_sectors ||
48287687bfmarius	    heads != basetable->gpt_heads))
48387687bfmarius		printf("GEOM: %s: geometry does not match VTOC8 label "
48487687bfmarius		    "(label: %uh,%us GEOM: %uh,%us).\n", pp->name, heads,
48587687bfmarius		    sectors, basetable->gpt_heads, basetable->gpt_sectors);
486dd3a790marcel
487dd3a790marcel	table->secpercyl = heads * sectors;
488dd3a790marcel	cyls = be16dec(&table->vtoc.ncyls);
489dd3a790marcel	chs = cyls * table->secpercyl;
490dd3a790marcel	if (chs < 1 || chs > msize)
491dd3a790marcel		goto invalid_label;
492dd3a790marcel
493dd3a790marcel	basetable->gpt_first = 0;
494dd3a790marcel	basetable->gpt_last = chs - 1;
495dd3a790marcel	basetable->gpt_isleaf = 1;
496dd3a790marcel
497dd3a790marcel	withtags = (be32dec(&table->vtoc.sanity) == VTOC_SANITY) ? 1 : 0;
498dd3a790marcel	if (!withtags) {
49987687bfmarius		printf("GEOM: %s: adding VTOC8 information.\n", pp->name);
500dd3a790marcel		be32enc(&table->vtoc.version, VTOC_VERSION);
501dd3a790marcel		bzero(&table->vtoc.volume, VTOC_VOLUME_LEN);
502dd3a790marcel		be16enc(&table->vtoc.nparts, VTOC8_NPARTS);
503dd3a790marcel		bzero(&table->vtoc.part, sizeof(table->vtoc.part));
504dd3a790marcel		be32enc(&table->vtoc.sanity, VTOC_SANITY);
505dd3a790marcel	}
506dd3a790marcel
507dd3a790marcel	basetable->gpt_entries = be16dec(&table->vtoc.nparts);
508dd3a790marcel	if (basetable->gpt_entries < g_part_vtoc8_scheme.gps_minent ||
509dd3a790marcel	    basetable->gpt_entries > g_part_vtoc8_scheme.gps_maxent)
510dd3a790marcel		goto invalid_label;
511dd3a790marcel
512dd3a790marcel	for (index = basetable->gpt_entries - 1; index >= 0; index--) {
513dd3a790marcel		offset = be32dec(&table->vtoc.map[index].cyl) *
514dd3a790marcel		    table->secpercyl;
515dd3a790marcel		size = be32dec(&table->vtoc.map[index].nblks);
516dd3a790marcel		if (size == 0)
517dd3a790marcel			continue;
518dd3a790marcel		if (withtags)
519dd3a790marcel			tag = be16dec(&table->vtoc.part[index].tag);
520dd3a790marcel		else
521dd3a790marcel			tag = (index == VTOC_RAW_PART)
522dd3a790marcel			    ? VTOC_TAG_BACKUP
523dd3a790marcel			    : VTOC_TAG_UNASSIGNED;
524dd3a790marcel
525dd3a790marcel		if (index == VTOC_RAW_PART && tag != VTOC_TAG_BACKUP)
526dd3a790marcel			continue;
527dd3a790marcel		if (index != VTOC_RAW_PART && tag == VTOC_TAG_BACKUP)
528dd3a790marcel			continue;
529dd3a790marcel		entry = g_part_new_entry(basetable, index + 1, offset,
530dd3a790marcel		    offset + size - 1);
531dd3a790marcel		if (tag == VTOC_TAG_BACKUP)
532dd3a790marcel			entry->gpe_internal = 1;
533dd3a790marcel
534dd3a790marcel		if (!withtags)
535dd3a790marcel			be16enc(&table->vtoc.part[index].tag, tag);
536dd3a790marcel	}
537dd3a790marcel
538dd3a790marcel	return (0);
539dd3a790marcel
540dd3a790marcel invalid_label:
54187687bfmarius	printf("GEOM: %s: invalid VTOC8 label.\n", pp->name);
542dd3a790marcel	return (EINVAL);
543dd3a790marcel}
544dd3a790marcel
545dd3a790marcelstatic const char *
546e326b6bmariusg_part_vtoc8_type(struct g_part_table *basetable, struct g_part_entry *entry,
547dd3a790marcel    char *buf, size_t bufsz)
548dd3a790marcel{
549dd3a790marcel	struct g_part_vtoc8_table *table;
550dd3a790marcel	uint16_t tag;
551dd3a790marcel
552dd3a790marcel	table = (struct g_part_vtoc8_table *)basetable;
553dd3a790marcel	tag = be16dec(&table->vtoc.part[entry->gpe_index - 1].tag);
5545306b1emarcel	if (tag == VTOC_TAG_FREEBSD_NANDFS)
5555306b1emarcel		return (g_part_alias_name(G_PART_ALIAS_FREEBSD_NANDFS));
556dd3a790marcel	if (tag == VTOC_TAG_FREEBSD_SWAP)
557dd3a790marcel		return (g_part_alias_name(G_PART_ALIAS_FREEBSD_SWAP));
558dd3a790marcel	if (tag == VTOC_TAG_FREEBSD_UFS)
559dd3a790marcel		return (g_part_alias_name(G_PART_ALIAS_FREEBSD_UFS));
560dd3a790marcel	if (tag == VTOC_TAG_FREEBSD_VINUM)
561dd3a790marcel		return (g_part_alias_name(G_PART_ALIAS_FREEBSD_VINUM));
562dd3a790marcel	if (tag == VTOC_TAG_FREEBSD_ZFS)
563dd3a790marcel		return (g_part_alias_name(G_PART_ALIAS_FREEBSD_ZFS));
564dd3a790marcel	snprintf(buf, bufsz, "!%d", tag);
565dd3a790marcel	return (buf);
566dd3a790marcel}
567dd3a790marcel
568dd3a790marcelstatic int
569dd3a790marcelg_part_vtoc8_write(struct g_part_table *basetable, struct g_consumer *cp)
570dd3a790marcel{
571dd3a790marcel	struct g_provider *pp;
572dd3a790marcel	struct g_part_entry *entry;
573dd3a790marcel	struct g_part_vtoc8_table *table;
574dd3a790marcel	uint16_t sum;
575dd3a790marcel	u_char *p;
576dd3a790marcel	int error, index, match, offset;
577dd3a790marcel
578dd3a790marcel	pp = cp->provider;
579dd3a790marcel	table = (struct g_part_vtoc8_table *)basetable;
580dd3a790marcel	entry = LIST_FIRST(&basetable->gpt_entry);
581dd3a790marcel	for (index = 0; index < basetable->gpt_entries; index++) {
582dd3a790marcel		match = (entry != NULL && index == entry->gpe_index - 1)
583dd3a790marcel		    ? 1 : 0;
584dd3a790marcel		if (match) {
585dd3a790marcel			if (entry->gpe_deleted) {
586dd3a790marcel				be16enc(&table->vtoc.part[index].tag, 0);
587dd3a790marcel				be16enc(&table->vtoc.part[index].flag, 0);
588dd3a790marcel				be32enc(&table->vtoc.map[index].cyl, 0);
589dd3a790marcel				be32enc(&table->vtoc.map[index].nblks, 0);
590dd3a790marcel			}
591dd3a790marcel			entry = LIST_NEXT(entry, gpe_entry);
592dd3a790marcel		}
593dd3a790marcel	}
594dd3a790marcel
595dd3a790marcel	/* Calculate checksum. */
596dd3a790marcel	sum = 0;
597dd3a790marcel	p = (void *)&table->vtoc;
598dd3a790marcel	for (offset = 0; offset < sizeof(table->vtoc) - 2; offset += 2)
599dd3a790marcel		sum ^= be16dec(p + offset);
600dd3a790marcel	be16enc(&table->vtoc.cksum, sum);
601dd3a790marcel
602dd3a790marcel	error = g_write_data(cp, 0, p, pp->sectorsize);
603dd3a790marcel	return (error);
604dd3a790marcel}
605