disk.c revision 185099
1/*-
2 * Copyright (c) 2008 Semihalf, Rafal Jaworowski
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 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 */
27
28/*
29 * Block storage I/O routines for U-Boot
30 */
31
32#include <sys/cdefs.h>
33__FBSDID("$FreeBSD: head/sys/boot/uboot/lib/disk.c 185099 2008-11-19 17:34:28Z raj $");
34
35#include <sys/param.h>
36#include <sys/queue.h>
37#include <netinet/in.h>
38#include <machine/stdarg.h>
39#include <stand.h>
40#include <uuid.h>
41
42#define FSTYPENAMES
43#include <sys/disklabel.h>
44
45#include "api_public.h"
46#include "bootstrap.h"
47#include "glue.h"
48#include "libuboot.h"
49
50#define DEBUG
51#undef DEBUG
52
53#define stor_printf(fmt, args...) do {			\
54    printf("%s%d: ", dev->d_dev->dv_name, dev->d_unit);	\
55    printf(fmt, ##args);				\
56} while (0)
57
58#ifdef DEBUG
59#define debugf(fmt, args...) do { printf("%s(): ", __func__);	\
60    printf(fmt,##args); } while (0)
61#else
62#define debugf(fmt, args...)
63#endif
64
65struct gpt_part {
66	int		gp_index;
67	uuid_t		gp_type;
68	uint64_t	gp_start;
69	uint64_t	gp_end;
70};
71
72struct open_dev {
73	int		od_bsize;	/* block size */
74	int		od_bstart;	/* start block offset from beginning of disk */
75	int		od_type;
76#define OD_BSDLABEL	0x0001
77#define	OD_GPT		0x0002
78	union {
79		struct {
80			struct disklabel bsdlabel;
81		} _bsd;
82		struct {
83			struct gpt_part	*gpt_partitions;
84			int		gpt_nparts;
85		} _gpt;
86	} _data;
87};
88
89#define	od_bsdlabel	_data._bsd.bsdlabel
90#define	od_nparts	_data._gpt.gpt_nparts
91#define	od_partitions	_data._gpt.gpt_partitions
92
93static int stor_info[UB_MAX_DEV];
94static int stor_info_no = 0;
95static int stor_opendev(struct open_dev **, struct uboot_devdesc *);
96static int stor_closedev(struct uboot_devdesc *);
97static int stor_readdev(struct uboot_devdesc *, daddr_t, size_t, char *);
98static int stor_open_count = 0;
99
100/* devsw I/F */
101static int stor_init(void);
102static int stor_strategy(void *, int, daddr_t, size_t, char *, size_t *);
103static int stor_open(struct open_file *, ...);
104static int stor_close(struct open_file *);
105static void stor_print(int);
106
107struct devsw uboot_storage = {
108	"disk",
109	DEVT_DISK,
110	stor_init,
111	stor_strategy,
112	stor_open,
113	stor_close,
114	noioctl,
115	stor_print
116};
117
118static int
119stor_init(void)
120{
121	struct device_info *di;
122	int i, found = 0;
123
124	if (devs_no == 0) {
125		printf("No U-Boot devices! Really enumerated?\n");
126		return (-1);
127	}
128
129	for (i = 0; i < devs_no; i++) {
130		di = ub_dev_get(i);
131		if ((di != NULL) && (di->type & DEV_TYP_STOR)) {
132			if (stor_info_no >= UB_MAX_DEV) {
133				printf("Too many storage devices: %d\n",
134				    stor_info_no);
135				return (-1);
136			}
137			stor_info[stor_info_no++] = i;
138			found = 1;
139		}
140	}
141
142	if (!found) {
143		printf("No storage devices\n");
144		return (-1);
145	}
146
147	debugf("storage devices found: %d\n", stor_info_no);
148	return (0);
149}
150
151static int
152stor_strategy(void *devdata, int rw, daddr_t blk, size_t size, char *buf,
153    size_t *rsize)
154{
155	struct uboot_devdesc *dev = (struct uboot_devdesc *)devdata;
156	struct open_dev *od = (struct open_dev *)dev->d_disk.data;
157	int bcount, err;
158
159	debugf("od=%p, size=%d, bsize=%d\n", od, size, od->od_bsize);
160
161	if (rw != F_READ) {
162		stor_printf("write attempt, operation not supported!\n");
163		return (EROFS);
164	}
165
166	if (size % od->od_bsize) {
167		stor_printf("size=%d not multiple of device block size=%d\n",
168		    size, od->od_bsize);
169		return (EIO);
170	}
171	bcount = size / od->od_bsize;
172
173	if (rsize)
174		*rsize = 0;
175
176	err = stor_readdev(dev, blk + od->od_bstart, bcount, buf);
177	if (!err && rsize)
178		*rsize = size;
179
180	return (err);
181}
182
183static int
184stor_open(struct open_file *f, ...)
185{
186	va_list ap;
187	struct open_dev *od;
188	struct uboot_devdesc *dev;
189	int err;
190
191	va_start(ap, f);
192	dev = va_arg(ap, struct uboot_devdesc *);
193	va_end(ap);
194
195	if ((err = stor_opendev(&od, dev)) != 0)
196		return (err);
197
198	((struct uboot_devdesc *)(f->f_devdata))->d_disk.data = od;
199
200	return (0);
201}
202
203static int
204stor_close(struct open_file *f)
205{
206	struct uboot_devdesc *dev;
207
208	dev = (struct uboot_devdesc *)(f->f_devdata);
209
210	return (stor_closedev(dev));
211}
212
213static int
214stor_open_gpt(struct open_dev *od, struct uboot_devdesc *dev)
215{
216
217	/* TODO */
218	return (ENXIO);
219}
220
221static int
222stor_open_bsdlabel(struct open_dev *od, struct uboot_devdesc *dev)
223{
224	char *buf;
225	struct disklabel *dl;
226	int err = 0;
227
228	/* Allocate 1 block */
229	buf = malloc(od->od_bsize);
230	if (!buf) {
231		stor_printf("could not allocate memory for disklabel\n");
232		return (ENOMEM);
233	}
234
235	/* Read disklabel */
236	err = stor_readdev(dev, LABELSECTOR, 1, buf);
237	if (err) {
238		stor_printf("disklabel read error=%d\n", err);
239		err = ERDLAB;
240		goto out;
241	}
242	bcopy(buf + LABELOFFSET, &od->od_bsdlabel, sizeof(struct disklabel));
243	dl = &od->od_bsdlabel;
244
245	if (dl->d_magic != DISKMAGIC) {
246		stor_printf("no disklabel magic!\n");
247		err = EUNLAB;
248		goto out;
249	}
250	od->od_type = OD_BSDLABEL;
251	od->od_bstart = dl->d_partitions[dev->d_disk.partition].p_offset;
252
253	debugf("bstart=%d\n", od->od_bstart);
254
255out:
256	free(buf);
257	return (err);
258}
259
260static int
261stor_readdev(struct uboot_devdesc *dev, daddr_t blk, size_t size, char *buf)
262{
263	lbasize_t real_size;
264	int err, handle;
265
266	debugf("reading size=%d @ 0x%08x\n", size, (uint32_t)buf);
267
268	handle = stor_info[dev->d_unit];
269	err = ub_dev_read(handle, buf, size, blk, &real_size);
270	if (err != 0) {
271		stor_printf("read failed, error=%d\n", err);
272		return (EIO);
273	}
274
275	if (real_size != size) {
276		stor_printf("real size != size\n");
277		err = EIO;
278	}
279
280	return (err);
281}
282
283
284static int
285stor_opendev(struct open_dev **odp, struct uboot_devdesc *dev)
286{
287	struct device_info *di;
288	struct open_dev *od;
289	int err, h;
290
291	h = stor_info[dev->d_unit];
292
293	debugf("refcount=%d\n", stor_open_count);
294
295	/*
296	 * There can be recursive open calls from the infrastructure, but at
297	 * U-Boot level open the device only the first time.
298	 */
299	if (stor_open_count > 0)
300		stor_open_count++;
301	else if ((err = ub_dev_open(h)) != 0) {
302		stor_printf("device open failed with error=%d, handle=%d\n",
303		    err, h);
304		*odp = NULL;
305		return (ENXIO);
306	}
307
308	if ((di = ub_dev_get(h)) == NULL)
309		panic("could not retrieve U-Boot device_info, handle=%d", h);
310
311	if ((od = malloc(sizeof(struct open_dev))) == NULL) {
312		stor_printf("could not allocate memory for open_dev\n");
313		return (ENOMEM);
314	}
315	od->od_bsize = di->di_stor.block_size;
316	od->od_bstart = 0;
317	od->od_type = 0;
318
319	if ((err = stor_open_gpt(od, dev)) != 0)
320		err = stor_open_bsdlabel(od, dev);
321
322	if (err != 0)
323		free(od);
324	else {
325		stor_open_count = 1;
326		*odp = od;
327	}
328
329	return (err);
330}
331
332static int
333stor_closedev(struct uboot_devdesc *dev)
334{
335	int err, h;
336
337	free((struct open_dev *)dev->d_disk.data);
338	dev->d_disk.data = NULL;
339
340	if (--stor_open_count == 0) {
341		h = stor_info[dev->d_unit];
342		if ((err = ub_dev_close(h)) != 0) {
343			stor_printf("device close failed with error=%d, "
344			    "handle=%d\n", err, h);
345			return (ENXIO);
346		}
347	}
348
349	return (0);
350}
351
352/* Given a size in 512 byte sectors, convert it to a human-readable number. */
353/* XXX stolen from sys/boot/i386/libi386/biosdisk.c, should really be shared */
354static char *
355display_size(uint64_t size)
356{
357	static char buf[80];
358	char unit;
359
360	size /= 2;
361	unit = 'K';
362	if (size >= 10485760000LL) {
363		size /= 1073741824;
364		unit = 'T';
365	} else if (size >= 10240000) {
366		size /= 1048576;
367		unit = 'G';
368	} else if (size >= 10000) {
369		size /= 1024;
370		unit = 'M';
371	}
372	sprintf(buf, "%.6ld%cB", (long)size, unit);
373	return (buf);
374}
375
376static void
377stor_print_bsdlabel(struct uboot_devdesc *dev, char *prefix, int verbose)
378{
379	char buf[512], line[80];
380	struct disklabel *dl;
381	uint32_t off, size;
382	int err, i, t;
383
384	/* Read disklabel */
385	err = stor_readdev(dev, LABELSECTOR, 1, buf);
386	if (err) {
387		sprintf(line, "%s%d: disklabel read error=%d\n",
388		    dev->d_dev->dv_name, dev->d_unit, err);
389		pager_output(line);
390		return;
391	}
392	dl = (struct disklabel *)buf;
393
394	if (dl->d_magic != DISKMAGIC) {
395		sprintf(line, "%s%d: no disklabel magic!\n",
396		    dev->d_dev->dv_name, dev->d_unit);
397		pager_output(line);
398		return;
399	}
400
401	/* Print partitions info */
402	for (i = 0; i < dl->d_npartitions; i++) {
403		if ((t = dl->d_partitions[i].p_fstype) < FSMAXTYPES) {
404
405			off = dl->d_partitions[i].p_offset;
406			size = dl->d_partitions[i].p_size;
407			if (fstypenames[t] == NULL || size == 0)
408				continue;
409
410			if ((('a' + i) == 'c') && (!verbose))
411				continue;
412
413			sprintf(line, "  %s%c: %s %s (%d - %d)\n", prefix,
414			    'a' + i, fstypenames[t], display_size(size),
415			    off, off + size);
416
417			pager_output(line);
418		}
419	}
420}
421
422static void
423stor_print_one(int i, struct device_info *di, int verbose)
424{
425	struct uboot_devdesc dev;
426	struct open_dev *od;
427	char line[80];
428
429	sprintf(line, "\tdisk%d (%s)\n", i, ub_stor_type(di->type));
430	pager_output(line);
431
432	dev.d_dev = &uboot_storage;
433	dev.d_unit = i;
434	dev.d_disk.partition = -1;
435	dev.d_disk.data = NULL;
436
437	if (stor_opendev(&od, &dev) == 0) {
438		dev.d_disk.data = od;
439
440		if (od->od_type == OD_GPT) {
441			/* TODO */
442
443		} else if (od->od_type == OD_BSDLABEL) {
444			sprintf(line, "\t\tdisk%d", i);
445			stor_print_bsdlabel(&dev, line, verbose);
446		}
447
448		stor_closedev(&dev);
449	}
450}
451
452static void
453stor_print(int verbose)
454{
455	struct device_info *di;
456	int i;
457
458	for (i = 0; i < stor_info_no; i++) {
459		di = ub_dev_get(stor_info[i]);
460		if (di != NULL)
461			stor_print_one(i, di, verbose);
462	}
463}
464