g_part_vtoc8.c revision 265910
1/*-
2 * Copyright (c) 2008 Marcel Moolenaar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: stable/10/sys/geom/part/g_part_vtoc8.c 265910 2014-05-12 10:19:31Z ae $");
29
30#include <sys/param.h>
31#include <sys/bio.h>
32#include <sys/endian.h>
33#include <sys/kernel.h>
34#include <sys/kobj.h>
35#include <sys/limits.h>
36#include <sys/lock.h>
37#include <sys/malloc.h>
38#include <sys/mutex.h>
39#include <sys/queue.h>
40#include <sys/sbuf.h>
41#include <sys/systm.h>
42#include <sys/sysctl.h>
43#include <sys/vtoc.h>
44#include <geom/geom.h>
45#include <geom/geom_int.h>
46#include <geom/part/g_part.h>
47
48#include "g_part_if.h"
49
50FEATURE(geom_part_vtoc8, "GEOM partitioning class for SMI VTOC8 disk labels");
51
52struct g_part_vtoc8_table {
53	struct g_part_table	base;
54	struct vtoc8		vtoc;
55	uint32_t		secpercyl;
56};
57
58static int g_part_vtoc8_add(struct g_part_table *, struct g_part_entry *,
59    struct g_part_parms *);
60static int g_part_vtoc8_create(struct g_part_table *, struct g_part_parms *);
61static int g_part_vtoc8_destroy(struct g_part_table *, struct g_part_parms *);
62static void g_part_vtoc8_dumpconf(struct g_part_table *,
63    struct g_part_entry *, struct sbuf *, const char *);
64static int g_part_vtoc8_dumpto(struct g_part_table *, struct g_part_entry *);
65static int g_part_vtoc8_modify(struct g_part_table *, struct g_part_entry *,
66    struct g_part_parms *);
67static const char *g_part_vtoc8_name(struct g_part_table *,
68    struct g_part_entry *, char *, size_t);
69static int g_part_vtoc8_probe(struct g_part_table *, struct g_consumer *);
70static int g_part_vtoc8_read(struct g_part_table *, struct g_consumer *);
71static const char *g_part_vtoc8_type(struct g_part_table *,
72    struct g_part_entry *, char *, size_t);
73static int g_part_vtoc8_write(struct g_part_table *, struct g_consumer *);
74static int g_part_vtoc8_resize(struct g_part_table *, struct g_part_entry *,
75    struct g_part_parms *);
76
77static kobj_method_t g_part_vtoc8_methods[] = {
78	KOBJMETHOD(g_part_add,		g_part_vtoc8_add),
79	KOBJMETHOD(g_part_create,	g_part_vtoc8_create),
80	KOBJMETHOD(g_part_destroy,	g_part_vtoc8_destroy),
81	KOBJMETHOD(g_part_dumpconf,	g_part_vtoc8_dumpconf),
82	KOBJMETHOD(g_part_dumpto,	g_part_vtoc8_dumpto),
83	KOBJMETHOD(g_part_modify,	g_part_vtoc8_modify),
84	KOBJMETHOD(g_part_resize,	g_part_vtoc8_resize),
85	KOBJMETHOD(g_part_name,		g_part_vtoc8_name),
86	KOBJMETHOD(g_part_probe,	g_part_vtoc8_probe),
87	KOBJMETHOD(g_part_read,		g_part_vtoc8_read),
88	KOBJMETHOD(g_part_type,		g_part_vtoc8_type),
89	KOBJMETHOD(g_part_write,	g_part_vtoc8_write),
90	{ 0, 0 }
91};
92
93static struct g_part_scheme g_part_vtoc8_scheme = {
94	"VTOC8",
95	g_part_vtoc8_methods,
96	sizeof(struct g_part_vtoc8_table),
97	.gps_entrysz = sizeof(struct g_part_entry),
98	.gps_minent = VTOC8_NPARTS,
99	.gps_maxent = VTOC8_NPARTS,
100};
101G_PART_SCHEME_DECLARE(g_part_vtoc8);
102
103static int
104vtoc8_parse_type(const char *type, uint16_t *tag)
105{
106	const char *alias;
107	char *endp;
108	long lt;
109
110	if (type[0] == '!') {
111		lt = strtol(type + 1, &endp, 0);
112		if (type[1] == '\0' || *endp != '\0' || lt <= 0 ||
113		    lt >= 65536)
114			return (EINVAL);
115		*tag = (uint16_t)lt;
116		return (0);
117	}
118	alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_NANDFS);
119	if (!strcasecmp(type, alias)) {
120		*tag = VTOC_TAG_FREEBSD_NANDFS;
121		return (0);
122	}
123	alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_SWAP);
124	if (!strcasecmp(type, alias)) {
125		*tag = VTOC_TAG_FREEBSD_SWAP;
126		return (0);
127	}
128	alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_UFS);
129	if (!strcasecmp(type, alias)) {
130		*tag = VTOC_TAG_FREEBSD_UFS;
131		return (0);
132	}
133	alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_VINUM);
134	if (!strcasecmp(type, alias)) {
135		*tag = VTOC_TAG_FREEBSD_VINUM;
136		return (0);
137	}
138	alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_ZFS);
139	if (!strcasecmp(type, alias)) {
140		*tag = VTOC_TAG_FREEBSD_ZFS;
141		return (0);
142	}
143	return (EINVAL);
144}
145
146static int
147vtoc8_align(struct g_part_vtoc8_table *table, uint64_t *start, uint64_t *size)
148{
149
150	if (*size < table->secpercyl)
151		return (EINVAL);
152	if (start != NULL && (*start % table->secpercyl)) {
153		*size += (*start % table->secpercyl) - table->secpercyl;
154		*start -= (*start % table->secpercyl) - table->secpercyl;
155	}
156	if (*size % table->secpercyl)
157		*size -= (*size % table->secpercyl);
158	if (*size < table->secpercyl)
159		return (EINVAL);
160	return (0);
161}
162
163static int
164g_part_vtoc8_add(struct g_part_table *basetable, struct g_part_entry *entry,
165    struct g_part_parms *gpp)
166{
167	struct g_part_vtoc8_table *table;
168	int error, index;
169	uint64_t start, size;
170	uint16_t tag;
171
172	if (gpp->gpp_parms & G_PART_PARM_LABEL)
173		return (EINVAL);
174
175	error = vtoc8_parse_type(gpp->gpp_type, &tag);
176	if (error)
177		return (error);
178
179	table = (struct g_part_vtoc8_table *)basetable;
180	index = entry->gpe_index - 1;
181	start = gpp->gpp_start;
182	size = gpp->gpp_size;
183	if (vtoc8_align(table, &start, &size) != 0)
184		return (EINVAL);
185
186	KASSERT(entry->gpe_start <= start, (__func__));
187	KASSERT(entry->gpe_end >= start + size - 1, (__func__));
188	entry->gpe_start = start;
189	entry->gpe_end = start + size - 1;
190
191	be16enc(&table->vtoc.part[index].tag, tag);
192	be16enc(&table->vtoc.part[index].flag, 0);
193	be32enc(&table->vtoc.timestamp[index], 0);
194	be32enc(&table->vtoc.map[index].cyl, start / table->secpercyl);
195	be32enc(&table->vtoc.map[index].nblks, size);
196	return (0);
197}
198
199static int
200g_part_vtoc8_create(struct g_part_table *basetable, struct g_part_parms *gpp)
201{
202	struct g_provider *pp;
203	struct g_part_entry *entry;
204	struct g_part_vtoc8_table *table;
205	uint64_t msize;
206	uint32_t acyls, ncyls, pcyls;
207
208	pp = gpp->gpp_provider;
209
210	if (pp->sectorsize < sizeof(struct vtoc8))
211		return (ENOSPC);
212	if (pp->sectorsize > sizeof(struct vtoc8))
213		return (ENXIO);
214
215	table = (struct g_part_vtoc8_table *)basetable;
216
217	msize = MIN(pp->mediasize / pp->sectorsize, UINT32_MAX);
218	table->secpercyl = basetable->gpt_sectors * basetable->gpt_heads;
219	pcyls = msize / table->secpercyl;
220	acyls = 2;
221	ncyls = pcyls - acyls;
222	msize = ncyls * table->secpercyl;
223
224	sprintf(table->vtoc.ascii, "FreeBSD%lldM cyl %u alt %u hd %u sec %u",
225	    (long long)(msize / 2048), ncyls, acyls, basetable->gpt_heads,
226	    basetable->gpt_sectors);
227	be32enc(&table->vtoc.version, VTOC_VERSION);
228	be16enc(&table->vtoc.nparts, VTOC8_NPARTS);
229	be32enc(&table->vtoc.sanity, VTOC_SANITY);
230	be16enc(&table->vtoc.rpm, 3600);
231	be16enc(&table->vtoc.physcyls, pcyls);
232	be16enc(&table->vtoc.ncyls, ncyls);
233	be16enc(&table->vtoc.altcyls, acyls);
234	be16enc(&table->vtoc.nheads, basetable->gpt_heads);
235	be16enc(&table->vtoc.nsecs, basetable->gpt_sectors);
236	be16enc(&table->vtoc.magic, VTOC_MAGIC);
237
238	basetable->gpt_first = 0;
239	basetable->gpt_last = msize - 1;
240	basetable->gpt_isleaf = 1;
241
242	entry = g_part_new_entry(basetable, VTOC_RAW_PART + 1,
243	    basetable->gpt_first, basetable->gpt_last);
244	entry->gpe_internal = 1;
245	be16enc(&table->vtoc.part[VTOC_RAW_PART].tag, VTOC_TAG_BACKUP);
246	be32enc(&table->vtoc.map[VTOC_RAW_PART].nblks, msize);
247	return (0);
248}
249
250static int
251g_part_vtoc8_destroy(struct g_part_table *basetable, struct g_part_parms *gpp)
252{
253
254	/* Wipe the first sector to clear the partitioning. */
255	basetable->gpt_smhead |= 1;
256	return (0);
257}
258
259static void
260g_part_vtoc8_dumpconf(struct g_part_table *basetable,
261    struct g_part_entry *entry, struct sbuf *sb, const char *indent)
262{
263	struct g_part_vtoc8_table *table;
264
265	table = (struct g_part_vtoc8_table *)basetable;
266	if (indent == NULL) {
267		/* conftxt: libdisk compatibility */
268		sbuf_printf(sb, " xs SUN sc %u hd %u alt %u",
269		    be16dec(&table->vtoc.nsecs), be16dec(&table->vtoc.nheads),
270		    be16dec(&table->vtoc.altcyls));
271	} else if (entry != NULL) {
272		/* confxml: partition entry information */
273		sbuf_printf(sb, "%s<rawtype>%u</rawtype>\n", indent,
274		    be16dec(&table->vtoc.part[entry->gpe_index - 1].tag));
275	} else {
276		/* confxml: scheme information */
277	}
278}
279
280static int
281g_part_vtoc8_dumpto(struct g_part_table *basetable,
282    struct g_part_entry *entry)
283{
284	struct g_part_vtoc8_table *table;
285	uint16_t tag;
286
287	/*
288	 * Allow dumping to a swap partition or a partition that
289	 * has no type.
290	 */
291	table = (struct g_part_vtoc8_table *)basetable;
292	tag = be16dec(&table->vtoc.part[entry->gpe_index - 1].tag);
293	return ((tag == 0 || tag == VTOC_TAG_FREEBSD_SWAP ||
294	    tag == VTOC_TAG_SWAP) ? 1 : 0);
295}
296
297static int
298g_part_vtoc8_modify(struct g_part_table *basetable,
299    struct g_part_entry *entry, struct g_part_parms *gpp)
300{
301	struct g_part_vtoc8_table *table;
302	int error;
303	uint16_t tag;
304
305	if (gpp->gpp_parms & G_PART_PARM_LABEL)
306		return (EINVAL);
307
308	table = (struct g_part_vtoc8_table *)basetable;
309	if (gpp->gpp_parms & G_PART_PARM_TYPE) {
310		error = vtoc8_parse_type(gpp->gpp_type, &tag);
311		if (error)
312			return(error);
313
314		be16enc(&table->vtoc.part[entry->gpe_index - 1].tag, tag);
315	}
316	return (0);
317}
318
319static int
320g_part_vtoc8_resize(struct g_part_table *basetable,
321    struct g_part_entry *entry, struct g_part_parms *gpp)
322{
323	struct g_part_vtoc8_table *table;
324	struct g_provider *pp;
325	uint64_t size;
326
327	table = (struct g_part_vtoc8_table *)basetable;
328	size = gpp->gpp_size;
329	if (vtoc8_align(table, NULL, &size) != 0)
330		return (EINVAL);
331	/* XXX: prevent unexpected shrinking. */
332	pp = entry->gpe_pp;
333	if ((g_debugflags & 0x10) == 0 && size < gpp->gpp_size &&
334	    pp->mediasize / pp->sectorsize > size)
335		return (EBUSY);
336	entry->gpe_end = entry->gpe_start + size - 1;
337	be32enc(&table->vtoc.map[entry->gpe_index - 1].nblks, size);
338
339	return (0);
340}
341
342static const char *
343g_part_vtoc8_name(struct g_part_table *table, struct g_part_entry *baseentry,
344    char *buf, size_t bufsz)
345{
346
347	snprintf(buf, bufsz, "%c", 'a' + baseentry->gpe_index - 1);
348	return (buf);
349}
350
351static int
352g_part_vtoc8_probe(struct g_part_table *table, struct g_consumer *cp)
353{
354	struct g_provider *pp;
355	u_char *buf;
356	int error, ofs, res;
357	uint16_t cksum, magic;
358
359	pp = cp->provider;
360
361	/* Sanity-check the provider. */
362	if (pp->sectorsize != sizeof(struct vtoc8))
363		return (ENOSPC);
364
365	/* Check that there's a disklabel. */
366	buf = g_read_data(cp, 0, pp->sectorsize, &error);
367	if (buf == NULL)
368		return (error);
369
370	res = ENXIO;	/* Assume mismatch */
371
372	/* Check the magic */
373	magic = be16dec(buf + offsetof(struct vtoc8, magic));
374	if (magic != VTOC_MAGIC)
375		goto out;
376
377	/* Check the sum */
378	cksum = 0;
379	for (ofs = 0; ofs < sizeof(struct vtoc8); ofs += 2)
380		cksum ^= be16dec(buf + ofs);
381	if (cksum != 0)
382		goto out;
383
384	res = G_PART_PROBE_PRI_NORM;
385
386 out:
387	g_free(buf);
388	return (res);
389}
390
391static int
392g_part_vtoc8_read(struct g_part_table *basetable, struct g_consumer *cp)
393{
394	struct g_provider *pp;
395	struct g_part_vtoc8_table *table;
396	struct g_part_entry *entry;
397	u_char *buf;
398	off_t chs, msize;
399	uint64_t offset, size;
400	u_int cyls, heads, sectors;
401	int error, index, withtags;
402	uint16_t tag;
403
404	pp = cp->provider;
405	buf = g_read_data(cp, 0, pp->sectorsize, &error);
406	if (buf == NULL)
407		return (error);
408
409	table = (struct g_part_vtoc8_table *)basetable;
410	bcopy(buf, &table->vtoc, sizeof(table->vtoc));
411	g_free(buf);
412
413	msize = MIN(pp->mediasize / pp->sectorsize, UINT32_MAX);
414	sectors = be16dec(&table->vtoc.nsecs);
415	if (sectors < 1)
416		goto invalid_label;
417	if (sectors != basetable->gpt_sectors && !basetable->gpt_fixgeom) {
418		g_part_geometry_heads(msize, sectors, &chs, &heads);
419		if (chs != 0) {
420			basetable->gpt_sectors = sectors;
421			basetable->gpt_heads = heads;
422		}
423	}
424
425	heads = be16dec(&table->vtoc.nheads);
426	if (heads < 1)
427		goto invalid_label;
428	if (heads != basetable->gpt_heads && !basetable->gpt_fixgeom)
429		basetable->gpt_heads = heads;
430	/*
431	 * Except for ATA disks > 32GB, Solaris uses the native geometry
432	 * as reported by the target for the labels while da(4) typically
433	 * uses a synthetic one so we don't complain too loudly if these
434	 * geometries don't match.
435	 */
436	if (bootverbose && (sectors != basetable->gpt_sectors ||
437	    heads != basetable->gpt_heads))
438		printf("GEOM: %s: geometry does not match VTOC8 label "
439		    "(label: %uh,%us GEOM: %uh,%us).\n", pp->name, heads,
440		    sectors, basetable->gpt_heads, basetable->gpt_sectors);
441
442	table->secpercyl = heads * sectors;
443	cyls = be16dec(&table->vtoc.ncyls);
444	chs = cyls * table->secpercyl;
445	if (chs < 1 || chs > msize)
446		goto invalid_label;
447
448	basetable->gpt_first = 0;
449	basetable->gpt_last = chs - 1;
450	basetable->gpt_isleaf = 1;
451
452	withtags = (be32dec(&table->vtoc.sanity) == VTOC_SANITY) ? 1 : 0;
453	if (!withtags) {
454		printf("GEOM: %s: adding VTOC8 information.\n", pp->name);
455		be32enc(&table->vtoc.version, VTOC_VERSION);
456		bzero(&table->vtoc.volume, VTOC_VOLUME_LEN);
457		be16enc(&table->vtoc.nparts, VTOC8_NPARTS);
458		bzero(&table->vtoc.part, sizeof(table->vtoc.part));
459		be32enc(&table->vtoc.sanity, VTOC_SANITY);
460	}
461
462	basetable->gpt_entries = be16dec(&table->vtoc.nparts);
463	if (basetable->gpt_entries < g_part_vtoc8_scheme.gps_minent ||
464	    basetable->gpt_entries > g_part_vtoc8_scheme.gps_maxent)
465		goto invalid_label;
466
467	for (index = basetable->gpt_entries - 1; index >= 0; index--) {
468		offset = be32dec(&table->vtoc.map[index].cyl) *
469		    table->secpercyl;
470		size = be32dec(&table->vtoc.map[index].nblks);
471		if (size == 0)
472			continue;
473		if (withtags)
474			tag = be16dec(&table->vtoc.part[index].tag);
475		else
476			tag = (index == VTOC_RAW_PART)
477			    ? VTOC_TAG_BACKUP
478			    : VTOC_TAG_UNASSIGNED;
479
480		if (index == VTOC_RAW_PART && tag != VTOC_TAG_BACKUP)
481			continue;
482		if (index != VTOC_RAW_PART && tag == VTOC_TAG_BACKUP)
483			continue;
484		entry = g_part_new_entry(basetable, index + 1, offset,
485		    offset + size - 1);
486		if (tag == VTOC_TAG_BACKUP)
487			entry->gpe_internal = 1;
488
489		if (!withtags)
490			be16enc(&table->vtoc.part[index].tag, tag);
491	}
492
493	return (0);
494
495 invalid_label:
496	printf("GEOM: %s: invalid VTOC8 label.\n", pp->name);
497	return (EINVAL);
498}
499
500static const char *
501g_part_vtoc8_type(struct g_part_table *basetable, struct g_part_entry *entry,
502    char *buf, size_t bufsz)
503{
504	struct g_part_vtoc8_table *table;
505	uint16_t tag;
506
507	table = (struct g_part_vtoc8_table *)basetable;
508	tag = be16dec(&table->vtoc.part[entry->gpe_index - 1].tag);
509	if (tag == VTOC_TAG_FREEBSD_NANDFS)
510		return (g_part_alias_name(G_PART_ALIAS_FREEBSD_NANDFS));
511	if (tag == VTOC_TAG_FREEBSD_SWAP)
512		return (g_part_alias_name(G_PART_ALIAS_FREEBSD_SWAP));
513	if (tag == VTOC_TAG_FREEBSD_UFS)
514		return (g_part_alias_name(G_PART_ALIAS_FREEBSD_UFS));
515	if (tag == VTOC_TAG_FREEBSD_VINUM)
516		return (g_part_alias_name(G_PART_ALIAS_FREEBSD_VINUM));
517	if (tag == VTOC_TAG_FREEBSD_ZFS)
518		return (g_part_alias_name(G_PART_ALIAS_FREEBSD_ZFS));
519	snprintf(buf, bufsz, "!%d", tag);
520	return (buf);
521}
522
523static int
524g_part_vtoc8_write(struct g_part_table *basetable, struct g_consumer *cp)
525{
526	struct g_provider *pp;
527	struct g_part_entry *entry;
528	struct g_part_vtoc8_table *table;
529	uint16_t sum;
530	u_char *p;
531	int error, index, match, offset;
532
533	pp = cp->provider;
534	table = (struct g_part_vtoc8_table *)basetable;
535	entry = LIST_FIRST(&basetable->gpt_entry);
536	for (index = 0; index < basetable->gpt_entries; index++) {
537		match = (entry != NULL && index == entry->gpe_index - 1)
538		    ? 1 : 0;
539		if (match) {
540			if (entry->gpe_deleted) {
541				be16enc(&table->vtoc.part[index].tag, 0);
542				be16enc(&table->vtoc.part[index].flag, 0);
543				be32enc(&table->vtoc.map[index].cyl, 0);
544				be32enc(&table->vtoc.map[index].nblks, 0);
545			}
546			entry = LIST_NEXT(entry, gpe_entry);
547		}
548	}
549
550	/* Calculate checksum. */
551	sum = 0;
552	p = (void *)&table->vtoc;
553	for (offset = 0; offset < sizeof(table->vtoc) - 2; offset += 2)
554		sum ^= be16dec(p + offset);
555	be16enc(&table->vtoc.cksum, sum);
556
557	error = g_write_data(cp, 0, p, pp->sectorsize);
558	return (error);
559}
560