disk.c revision 239210
1/*-
2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3 * Copyright (c) 2012 Andrey V. Elsukov <ae@FreeBSD.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
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 AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD: head/sys/boot/common/disk.c 239210 2012-08-12 14:16:21Z ae $");
30
31#include <sys/disk.h>
32#include <stand.h>
33#include <stdarg.h>
34#include <bootstrap.h>
35#include <part.h>
36
37#include "disk.h"
38
39#ifdef DISK_DEBUG
40# define DEBUG(fmt, args...)	printf("%s: " fmt "\n" , __func__ , ## args)
41#else
42# define DEBUG(fmt, args...)
43#endif
44
45struct open_disk {
46	struct ptable		*table;
47	off_t			mediasize;
48	u_int			sectorsize;
49};
50
51struct print_args {
52	struct disk_devdesc	*dev;
53	const char		*prefix;
54	int			verbose;
55};
56
57/* Convert size to a human-readable number. */
58static char *
59display_size(uint64_t size, u_int sectorsize)
60{
61	static char buf[80];
62	char unit;
63
64	size = size * sectorsize / 1024;
65	unit = 'K';
66	if (size >= 10485760000LL) {
67		size /= 1073741824;
68		unit = 'T';
69	} else if (size >= 10240000) {
70		size /= 1048576;
71		unit = 'G';
72	} else if (size >= 10000) {
73		size /= 1024;
74		unit = 'M';
75	}
76	sprintf(buf, "%ld%cB", (long)size, unit);
77	return (buf);
78}
79
80static int
81ptblread(void *d, void *buf, size_t blocks, off_t offset)
82{
83	struct disk_devdesc *dev;
84	struct open_disk *od;
85
86	dev = (struct disk_devdesc *)d;
87	od = (struct open_disk *)dev->d_opendata;
88	return (dev->d_dev->dv_strategy(dev, F_READ, offset,
89	    blocks * od->sectorsize, (char *)buf, NULL));
90}
91
92#define	PWIDTH	35
93static void
94ptable_print(void *arg, const char *pname, const struct ptable_entry *part)
95{
96	struct print_args *pa, bsd;
97	struct open_disk *od;
98	struct ptable *table;
99	char line[80];
100
101	pa = (struct print_args *)arg;
102	od = (struct open_disk *)pa->dev->d_opendata;
103	sprintf(line, "  %s%s: %s", pa->prefix, pname,
104	    parttype2str(part->type));
105	if (pa->verbose)
106		sprintf(line, "%-*s%s", PWIDTH, line,
107		    display_size(part->end - part->start + 1,
108		    od->sectorsize));
109	strcat(line, "\n");
110	pager_output(line);
111	if (part->type == PART_FREEBSD) {
112		/* Open slice with BSD label */
113		pa->dev->d_offset = part->start;
114		table = ptable_open(pa->dev, part->end - part->start + 1,
115		    od->sectorsize, ptblread);
116		if (table == NULL)
117			return;
118		sprintf(line, "  %s%s", pa->prefix, pname);
119		bsd.dev = pa->dev;
120		bsd.prefix = line;
121		bsd.verbose = pa->verbose;
122		ptable_iterate(table, &bsd, ptable_print);
123		ptable_close(table);
124	}
125}
126#undef PWIDTH
127
128void
129disk_print(struct disk_devdesc *dev, char *prefix, int verbose)
130{
131	struct open_disk *od;
132	struct print_args pa;
133
134	/* Disk should be opened */
135	od = (struct open_disk *)dev->d_opendata;
136	pa.dev = dev;
137	pa.prefix = prefix;
138	pa.verbose = verbose;
139	ptable_iterate(od->table, &pa, ptable_print);
140}
141
142int
143disk_open(struct disk_devdesc *dev, off_t mediasize, u_int sectorsize)
144{
145	struct open_disk *od;
146	struct ptable *table;
147	struct ptable_entry part;
148	int rc;
149
150	od = (struct open_disk *)malloc(sizeof(struct open_disk));
151	if (od == NULL) {
152		DEBUG("no memory");
153		return (ENOMEM);
154	}
155	/*
156	 * While we are reading disk metadata, make sure we do it relative
157	 * to the start of the disk
158	 */
159	rc = 0;
160	table = NULL;
161	dev->d_offset = 0;
162	dev->d_opendata = od;
163	od->mediasize = mediasize;
164	od->sectorsize = sectorsize;
165	DEBUG("%s: unit %d, slice %d, partition %d",
166	    disk_fmtdev(dev), dev->d_unit, dev->d_slice, dev->d_partition);
167
168	/* Determine disk layout. */
169	od->table = ptable_open(dev, mediasize / sectorsize, sectorsize,
170	    ptblread);
171	if (od->table == NULL) {
172		DEBUG("Can't read partition table");
173		rc = ENXIO;
174		goto out;
175	}
176	if (dev->d_slice > 0) {
177		/* Try to get information about partition */
178		rc = ptable_getpart(od->table, &part, dev->d_slice);
179		if (rc != 0) /* Partition doesn't exist */
180			goto out;
181		dev->d_offset = part.start;
182		if (dev->d_partition == -1 ||
183		    dev->d_partition == 255)
184			goto out; /* Nothing more to do */
185
186		/* Try to read BSD label */
187		table = ptable_open(dev, part.end - part.start + 1,
188		    od->sectorsize, ptblread);
189		if (table == NULL) {
190			DEBUG("Can't read BSD label");
191			rc = ENXIO;
192			goto out;
193		}
194		rc = ptable_getpart(table, &part, dev->d_partition);
195		if (rc != 0)
196			goto out;
197		dev->d_offset += part.start;
198	} else if (dev->d_slice == 0) {
199		rc = ptable_getbestpart(od->table, &part);
200		if (rc != 0)
201			goto out;
202		/* Save the slice number of best partition to dev */
203		dev->d_slice = part.index;
204		dev->d_offset = part.start;
205	}
206out:
207	if (table != NULL)
208		ptable_close(table);
209
210	if (rc != 0) {
211		if (od->table != NULL)
212			ptable_close(od->table);
213		free(od);
214		DEBUG("%s: could not open", disk_fmtdev(dev));
215	} else {
216		DEBUG("%s: offset %lld", disk_fmtdev(dev), dev->d_offset);
217	}
218	return (rc);
219}
220
221int
222disk_close(struct disk_devdesc *dev)
223{
224	struct open_disk *od;
225
226	od = (struct open_disk *)dev->d_opendata;
227	DEBUG("%s: closed", disk_fmtdev(dev));
228	ptable_close(od->table);
229	free(od);
230	return (0);
231}
232
233char*
234disk_fmtdev(struct disk_devdesc *dev)
235{
236	static char buf[128];
237	char *cp;
238
239	cp = buf + sprintf(buf, "%s%d", dev->d_dev->dv_name, dev->d_unit);
240	if (dev->d_slice > 0) {
241#ifdef LOADER_GPT_SUPPORT
242		if (dev->d_partition == 255) {
243			sprintf(cp, "p%d:", dev->d_slice);
244			return (buf);
245		} else
246#endif
247#ifdef LOADER_MBR_SUPPORT
248			cp += sprintf(cp, "s%d", dev->d_slice);
249#endif
250		if (dev->d_partition >= 0)
251			cp += sprintf(cp, "%c", dev->d_partition + 'a');
252	}
253	strcat(cp, ":");
254	return (buf);
255}
256
257int
258disk_parsedev(struct disk_devdesc *dev, const char *devspec, const char **path)
259{
260	int unit, slice, partition;
261	const char *np;
262	char *cp;
263
264	np = devspec;
265	unit = slice = partition = -1;
266	if (*np != '\0' && *np != ':') {
267		unit = strtol(np, &cp, 10);
268		if (cp == np)
269			return (EUNIT);
270#ifdef LOADER_GPT_SUPPORT
271		if (*cp == 'p') {
272			np = cp + 1;
273			slice = strtol(np, &cp, 10);
274			if (np == cp)
275				return (ESLICE);
276			/* we don't support nested partitions on GPT */
277			if (*cp != '\0' && *cp != ':')
278				return (EINVAL);
279			partition = 255;
280		} else
281#endif
282#ifdef LOADER_MBR_SUPPORT
283		if (*cp == 's') {
284			np = cp + 1;
285			slice = strtol(np, &cp, 10);
286			if (np == cp)
287				return (ESLICE);
288		}
289#endif
290		if (*cp != '\0' && *cp != ':') {
291			partition = *cp - 'a';
292			if (partition < 0)
293				return (EPART);
294			cp++;
295		}
296	} else
297		return (EINVAL);
298
299	if (*cp != '\0' && *cp != ':')
300		return (EINVAL);
301	dev->d_unit = unit;
302	dev->d_slice = slice;
303	dev->d_partition = partition;
304	if (path != NULL)
305		*path = (*cp == '\0') ? cp: cp + 1;
306	return (0);
307}
308