g_part_mbr.c revision 176672
191094Sdes/*-
2115619Sdes * Copyright (c) 2007 Marcel Moolenaar
3174832Sdes * All rights reserved.
491094Sdes *
591094Sdes * Redistribution and use in source and binary forms, with or without
691094Sdes * modification, are permitted provided that the following conditions
799158Sdes * are met:
899158Sdes *
999158Sdes * 1. Redistributions of source code must retain the above copyright
1091094Sdes *    notice, this list of conditions and the following disclaimer.
1191094Sdes * 2. Redistributions in binary form must reproduce the above copyright
1291094Sdes *    notice, this list of conditions and the following disclaimer in the
1391094Sdes *    documentation and/or other materials provided with the distribution.
1491094Sdes *
1591094Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1691094Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1791094Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1891094Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1991094Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2091094Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2191094Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2291094Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2391094Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2491094Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2591094Sdes */
2691094Sdes
2791094Sdes#include <sys/cdefs.h>
2891094Sdes__FBSDID("$FreeBSD: head/sys/geom/part/g_part_mbr.c 176672 2008-02-29 22:41:36Z marcel $");
2991094Sdes
3091094Sdes#include <sys/param.h>
3191094Sdes#include <sys/bio.h>
3291094Sdes#include <sys/diskmbr.h>
3391094Sdes#include <sys/endian.h>
3491094Sdes#include <sys/kernel.h>
35174832Sdes#include <sys/kobj.h>
3691094Sdes#include <sys/limits.h>
3791094Sdes#include <sys/lock.h>
38117610Sdes#include <sys/malloc.h>
39117610Sdes#include <sys/mutex.h>
40117610Sdes#include <sys/queue.h>
41117610Sdes#include <sys/sbuf.h>
4291094Sdes#include <sys/systm.h>
4391094Sdes#include <geom/geom.h>
4491094Sdes#include <geom/part/g_part.h>
4591094Sdes
4691094Sdes#include "g_part_if.h"
4791094Sdes
4891094Sdes#define	MBRSIZE		512
4991094Sdes
5091094Sdesstruct g_part_mbr_table {
51174832Sdes	struct g_part_table	base;
52174832Sdes	u_char		mbr[MBRSIZE];
53174832Sdes};
54174832Sdes
5591094Sdesstruct g_part_mbr_entry {
5691094Sdes	struct g_part_entry	base;
5791094Sdes	struct dos_partition ent;
5891094Sdes};
5991094Sdes
6091094Sdesstatic int g_part_mbr_add(struct g_part_table *, struct g_part_entry *,
6191094Sdes    struct g_part_parms *);
6291094Sdesstatic int g_part_mbr_create(struct g_part_table *, struct g_part_parms *);
63174832Sdesstatic int g_part_mbr_destroy(struct g_part_table *, struct g_part_parms *);
64174832Sdesstatic int g_part_mbr_dumpto(struct g_part_table *, struct g_part_entry *);
65174832Sdesstatic int g_part_mbr_modify(struct g_part_table *, struct g_part_entry *,
66174832Sdes    struct g_part_parms *);
6791094Sdesstatic char *g_part_mbr_name(struct g_part_table *, struct g_part_entry *,
6891094Sdes    char *, size_t);
6991094Sdesstatic int g_part_mbr_probe(struct g_part_table *, struct g_consumer *);
7091094Sdesstatic int g_part_mbr_read(struct g_part_table *, struct g_consumer *);
7191094Sdesstatic const char *g_part_mbr_type(struct g_part_table *, struct g_part_entry *,
7291094Sdes    char *, size_t);
7391094Sdesstatic int g_part_mbr_write(struct g_part_table *, struct g_consumer *);
7491094Sdes
75174832Sdesstatic kobj_method_t g_part_mbr_methods[] = {
76174832Sdes	KOBJMETHOD(g_part_add,		g_part_mbr_add),
77174832Sdes	KOBJMETHOD(g_part_create,	g_part_mbr_create),
78174832Sdes	KOBJMETHOD(g_part_destroy,	g_part_mbr_destroy),
7991094Sdes	KOBJMETHOD(g_part_dumpto,	g_part_mbr_dumpto),
8091094Sdes	KOBJMETHOD(g_part_modify,	g_part_mbr_modify),
8191094Sdes	KOBJMETHOD(g_part_name,		g_part_mbr_name),
8291094Sdes	KOBJMETHOD(g_part_probe,	g_part_mbr_probe),
8391094Sdes	KOBJMETHOD(g_part_read,		g_part_mbr_read),
8491094Sdes	KOBJMETHOD(g_part_type,		g_part_mbr_type),
8591094Sdes	KOBJMETHOD(g_part_write,	g_part_mbr_write),
8691094Sdes	{ 0, 0 }
87174832Sdes};
88174832Sdes
89174832Sdesstatic struct g_part_scheme g_part_mbr_scheme = {
90174832Sdes	"MBR",
9191094Sdes	g_part_mbr_methods,
9291094Sdes	sizeof(struct g_part_mbr_table),
9391094Sdes	.gps_entrysz = sizeof(struct g_part_mbr_entry),
9491094Sdes	.gps_minent = NDOSPART,
9591094Sdes	.gps_maxent = NDOSPART,
9691094Sdes};
9791094SdesG_PART_SCHEME_DECLARE(g_part_mbr_scheme);
9891094Sdes
99174832Sdesstatic int
100174832Sdesmbr_parse_type(const char *type, u_char *dp_typ)
101174832Sdes{
102174832Sdes	const char *alias;
10391094Sdes	char *endp;
10491094Sdes	long lt;
10591094Sdes
10691094Sdes	if (type[0] == '!') {
10791094Sdes		lt = strtol(type + 1, &endp, 0);
10891094Sdes		if (type[1] == '\0' || *endp != '\0' || lt <= 0 || lt >= 256)
10991094Sdes			return (EINVAL);
11091094Sdes		*dp_typ = (u_char)lt;
111174832Sdes		return (0);
112174832Sdes	}
113174832Sdes	alias = g_part_alias_name(G_PART_ALIAS_FREEBSD);
114174832Sdes	if (!strcasecmp(type, alias)) {
11591094Sdes		*dp_typ = DOSPTYP_386BSD;
11691094Sdes		return (0);
11791094Sdes	}
11891094Sdes	return (EINVAL);
119}
120
121static int
122mbr_probe_bpb(u_char *bpb)
123{
124	uint16_t secsz;
125	uint8_t clstsz;
126
127#define PO2(x)	((x & (x - 1)) == 0)
128	secsz = le16dec(bpb);
129	if (secsz < 512 || secsz > 4096 || !PO2(secsz))
130		return (0);
131	clstsz = bpb[2];
132	if (clstsz < 1 || clstsz > 128 || !PO2(clstsz))
133		return (0);
134#undef PO2
135
136	return (1);
137}
138
139static void
140mbr_set_chs(struct g_part_table *table, uint32_t lba, u_char *cylp, u_char *hdp,
141    u_char *secp)
142{
143	uint32_t cyl, hd, sec;
144
145	sec = lba % table->gpt_sectors + 1;
146	lba /= table->gpt_sectors;
147	hd = lba % table->gpt_heads;
148	lba /= table->gpt_heads;
149	cyl = lba;
150	if (cyl > 1023)
151		sec = hd = cyl = ~0;
152
153	*cylp = cyl & 0xff;
154	*hdp = hd & 0xff;
155	*secp = (sec & 0x3f) | ((cyl >> 2) & 0xc0);
156}
157
158static int
159g_part_mbr_add(struct g_part_table *basetable, struct g_part_entry *baseentry,
160    struct g_part_parms *gpp)
161{
162	struct g_part_mbr_entry *entry;
163	struct g_part_mbr_table *table;
164	uint32_t start, size, sectors;
165
166	if (gpp->gpp_parms & G_PART_PARM_LABEL)
167		return (EINVAL);
168
169	sectors = basetable->gpt_sectors;
170
171	entry = (struct g_part_mbr_entry *)baseentry;
172	table = (struct g_part_mbr_table *)basetable;
173
174	start = gpp->gpp_start;
175	size = gpp->gpp_size;
176	if (size < sectors)
177		return (EINVAL);
178	if (start % sectors) {
179		size = size - sectors + (start % sectors);
180		start = start - (start % sectors) + sectors;
181	}
182	if (size % sectors)
183		size = size - (size % sectors);
184	if (size < sectors)
185		return (EINVAL);
186
187	if (baseentry->gpe_deleted)
188		bzero(&entry->ent, sizeof(entry->ent));
189
190	KASSERT(baseentry->gpe_start <= start, (__func__));
191	KASSERT(baseentry->gpe_end >= start + size - 1, (__func__));
192	baseentry->gpe_start = start;
193	baseentry->gpe_end = start + size - 1;
194	entry->ent.dp_start = start;
195	entry->ent.dp_size = size;
196	mbr_set_chs(basetable, baseentry->gpe_start, &entry->ent.dp_scyl,
197	    &entry->ent.dp_shd, &entry->ent.dp_ssect);
198	mbr_set_chs(basetable, baseentry->gpe_end, &entry->ent.dp_ecyl,
199	    &entry->ent.dp_ehd, &entry->ent.dp_esect);
200	return (mbr_parse_type(gpp->gpp_type, &entry->ent.dp_typ));
201}
202
203static int
204g_part_mbr_create(struct g_part_table *basetable, struct g_part_parms *gpp)
205{
206	struct g_consumer *cp;
207	struct g_provider *pp;
208	struct g_part_mbr_table *table;
209	uint64_t msize;
210
211	pp = gpp->gpp_provider;
212	cp = LIST_FIRST(&pp->consumers);
213
214	if (pp->sectorsize < MBRSIZE)
215		return (ENOSPC);
216
217	msize = pp->mediasize / pp->sectorsize;
218	basetable->gpt_first = basetable->gpt_sectors;
219	basetable->gpt_last = msize - (msize % basetable->gpt_sectors) - 1;
220
221	table = (struct g_part_mbr_table *)basetable;
222	le16enc(table->mbr + DOSMAGICOFFSET, DOSMAGIC);
223	return (0);
224}
225
226static int
227g_part_mbr_destroy(struct g_part_table *basetable, struct g_part_parms *gpp)
228{
229
230	/* Wipe the first sector to clear the partitioning. */
231	basetable->gpt_smhead |= 1;
232	return (0);
233}
234
235static int
236g_part_mbr_dumpto(struct g_part_table *table, struct g_part_entry *baseentry)
237{
238	struct g_part_mbr_entry *entry;
239
240	/* Allow dumping to a FreeBSD partition only. */
241	entry = (struct g_part_mbr_entry *)baseentry;
242	return ((entry->ent.dp_typ == DOSPTYP_386BSD) ? 1 : 0);
243}
244
245static int
246g_part_mbr_modify(struct g_part_table *basetable,
247    struct g_part_entry *baseentry, struct g_part_parms *gpp)
248{
249	struct g_part_mbr_entry *entry;
250
251	if (gpp->gpp_parms & G_PART_PARM_LABEL)
252		return (EINVAL);
253
254	entry = (struct g_part_mbr_entry *)baseentry;
255	if (gpp->gpp_parms & G_PART_PARM_TYPE)
256		return (mbr_parse_type(gpp->gpp_type, &entry->ent.dp_typ));
257	return (0);
258}
259
260static char *
261g_part_mbr_name(struct g_part_table *table, struct g_part_entry *baseentry,
262    char *buf, size_t bufsz)
263{
264
265	snprintf(buf, bufsz, "s%d", baseentry->gpe_index);
266	return (buf);
267}
268
269static int
270g_part_mbr_probe(struct g_part_table *table, struct g_consumer *cp)
271{
272	struct g_provider *pp;
273	u_char *buf, *p;
274	int error, index, res, sum;
275	uint16_t magic;
276
277	pp = cp->provider;
278
279	/* Sanity-check the provider. */
280	if (pp->sectorsize < MBRSIZE || pp->mediasize < pp->sectorsize)
281		return (ENOSPC);
282	if (pp->sectorsize > 4096)
283		return (ENXIO);
284
285	/* Check that there's a MBR. */
286	buf = g_read_data(cp, 0L, pp->sectorsize, &error);
287	if (buf == NULL)
288		return (error);
289
290	/* We goto out on mismatch. */
291	res = ENXIO;
292
293	magic = le16dec(buf + DOSMAGICOFFSET);
294	if (magic != DOSMAGIC)
295		goto out;
296
297	for (index = 0; index < NDOSPART; index++) {
298		p = buf + DOSPARTOFF + index * DOSPARTSIZE;
299		if (p[0] != 0 && p[0] != 0x80)
300			goto out;
301	}
302
303	/*
304	 * If the partition table does not consist of all zeroes,
305	 * assume we have a MBR. If it's all zeroes, we could have
306	 * a boot sector. For example, a boot sector that doesn't
307	 * have boot code -- common on non-i386 hardware. In that
308	 * case we check if we have a possible BPB. If so, then we
309	 * assume we have a boot sector instead.
310	 */
311	sum = 0;
312	for (index = 0; index < NDOSPART * DOSPARTSIZE; index++)
313		sum += buf[DOSPARTOFF + index];
314	if (sum != 0 || !mbr_probe_bpb(buf + 0x0b))
315		res = G_PART_PROBE_PRI_NORM;
316
317 out:
318	g_free(buf);
319	return (res);
320}
321
322static int
323g_part_mbr_read(struct g_part_table *basetable, struct g_consumer *cp)
324{
325	struct dos_partition ent;
326	struct g_provider *pp;
327	struct g_part_mbr_table *table;
328	struct g_part_mbr_entry *entry;
329	u_char *buf, *p;
330	off_t chs, msize;
331	u_int sectors, heads;
332	int error, index;
333
334	pp = cp->provider;
335	table = (struct g_part_mbr_table *)basetable;
336	msize = pp->mediasize / pp->sectorsize;
337
338	buf = g_read_data(cp, 0L, pp->sectorsize, &error);
339	if (buf == NULL)
340		return (error);
341
342	bcopy(buf, table->mbr, sizeof(table->mbr));
343	for (index = NDOSPART - 1; index >= 0; index--) {
344		p = buf + DOSPARTOFF + index * DOSPARTSIZE;
345		ent.dp_flag = p[0];
346		ent.dp_shd = p[1];
347		ent.dp_ssect = p[2];
348		ent.dp_scyl = p[3];
349		ent.dp_typ = p[4];
350		ent.dp_ehd = p[5];
351		ent.dp_esect = p[6];
352		ent.dp_ecyl = p[7];
353		ent.dp_start = le32dec(p + 8);
354		ent.dp_size = le32dec(p + 12);
355		if (ent.dp_typ == 0 || ent.dp_typ == DOSPTYP_PMBR)
356			continue;
357		if (ent.dp_start == 0 || ent.dp_size == 0)
358			continue;
359		sectors = ent.dp_esect & 0x3f;
360		if (sectors > basetable->gpt_sectors &&
361		    !basetable->gpt_fixgeom) {
362			g_part_geometry_heads(msize, sectors, &chs, &heads);
363			if (chs != 0) {
364				basetable->gpt_sectors = sectors;
365				basetable->gpt_heads = heads;
366			}
367		}
368		if ((ent.dp_start % basetable->gpt_sectors) != 0)
369			printf("GEOM: %s: partition %d does not start on a "
370			    "track boundary.\n", pp->name, index + 1);
371		if ((ent.dp_size % basetable->gpt_sectors) != 0)
372			printf("GEOM: %s: partition %d does not end on a "
373			    "track boundary.\n", pp->name, index + 1);
374
375		entry = (struct g_part_mbr_entry *)g_part_new_entry(basetable,
376		    index + 1, ent.dp_start, ent.dp_start + ent.dp_size - 1);
377		entry->ent = ent;
378	}
379
380	basetable->gpt_entries = NDOSPART;
381	basetable->gpt_first = basetable->gpt_sectors;
382	basetable->gpt_last = msize - (msize % basetable->gpt_sectors) - 1;
383
384	return (0);
385}
386
387static const char *
388g_part_mbr_type(struct g_part_table *basetable, struct g_part_entry *baseentry,
389    char *buf, size_t bufsz)
390{
391	struct g_part_mbr_entry *entry;
392	int type;
393
394	entry = (struct g_part_mbr_entry *)baseentry;
395	type = entry->ent.dp_typ;
396	if (type == DOSPTYP_386BSD)
397		return (g_part_alias_name(G_PART_ALIAS_FREEBSD));
398	snprintf(buf, bufsz, "!%d", type);
399	return (buf);
400}
401
402static int
403g_part_mbr_write(struct g_part_table *basetable, struct g_consumer *cp)
404{
405	struct g_part_entry *baseentry;
406	struct g_part_mbr_entry *entry;
407	struct g_part_mbr_table *table;
408	u_char *p;
409	int error, index;
410
411	table = (struct g_part_mbr_table *)basetable;
412	baseentry = LIST_FIRST(&basetable->gpt_entry);
413	for (index = 1; index <= basetable->gpt_entries; index++) {
414		p = table->mbr + DOSPARTOFF + (index - 1) * DOSPARTSIZE;
415		entry = (baseentry != NULL && index == baseentry->gpe_index)
416		    ? (struct g_part_mbr_entry *)baseentry : NULL;
417		if (entry != NULL && !baseentry->gpe_deleted) {
418			p[0] = entry->ent.dp_flag;
419			p[1] = entry->ent.dp_shd;
420			p[2] = entry->ent.dp_ssect;
421			p[3] = entry->ent.dp_scyl;
422			p[4] = entry->ent.dp_typ;
423			p[5] = entry->ent.dp_ehd;
424			p[6] = entry->ent.dp_esect;
425			p[7] = entry->ent.dp_ecyl;
426			le32enc(p + 8, entry->ent.dp_start);
427			le32enc(p + 12, entry->ent.dp_size);
428		} else
429			bzero(p, DOSPARTSIZE);
430
431		if (entry != NULL)
432			baseentry = LIST_NEXT(baseentry, gpe_entry);
433	}
434
435	error = g_write_data(cp, 0, table->mbr, cp->provider->sectorsize);
436	return (error);
437}
438