disk.c revision 191829
1/*-
2 * Copyright (c) 2008 Semihalf, Rafal Jaworowski
3 * Copyright (c) 2009 Semihalf, Piotr Ziecik
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
29/*
30 * Block storage I/O routines for U-Boot
31 */
32
33#include <sys/cdefs.h>
34__FBSDID("$FreeBSD: head/sys/boot/uboot/lib/disk.c 191829 2009-05-05 16:29:08Z raj $");
35
36#include <sys/param.h>
37#include <sys/queue.h>
38#include <netinet/in.h>
39#include <machine/stdarg.h>
40#include <stand.h>
41#include <uuid.h>
42
43#define FSTYPENAMES
44#include <sys/disklabel.h>
45#include <sys/diskmbr.h>
46#include <sys/gpt.h>
47
48#include "api_public.h"
49#include "bootstrap.h"
50#include "glue.h"
51#include "libuboot.h"
52
53#define DEBUG
54#undef DEBUG
55
56#define stor_printf(fmt, args...) do {			\
57    printf("%s%d: ", dev->d_dev->dv_name, dev->d_unit);	\
58    printf(fmt, ##args);				\
59} while (0)
60
61#ifdef DEBUG
62#define debugf(fmt, args...) do { printf("%s(): ", __func__);	\
63    printf(fmt,##args); } while (0)
64#else
65#define debugf(fmt, args...)
66#endif
67
68struct gpt_part {
69	int		gp_index;
70	uuid_t		gp_type;
71	uint64_t	gp_start;
72	uint64_t	gp_end;
73};
74
75struct open_dev {
76	int		od_bsize;	/* block size */
77	int		od_bstart;	/* start block offset from beginning of disk */
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 uuid_t efi = GPT_ENT_TYPE_EFI;
94static uuid_t freebsd_boot = GPT_ENT_TYPE_FREEBSD_BOOT;
95static uuid_t freebsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS;
96static uuid_t freebsd_swap = GPT_ENT_TYPE_FREEBSD_SWAP;
97static uuid_t freebsd_zfs = GPT_ENT_TYPE_FREEBSD_ZFS;
98static uuid_t ms_basic_data = GPT_ENT_TYPE_MS_BASIC_DATA;
99
100static int stor_info[UB_MAX_DEV];
101static int stor_info_no = 0;
102static int stor_opendev(struct open_dev **, struct uboot_devdesc *);
103static int stor_closedev(struct uboot_devdesc *);
104static int stor_readdev(struct uboot_devdesc *, daddr_t, size_t, char *);
105static int stor_open_count = 0;
106
107/* devsw I/F */
108static int stor_init(void);
109static int stor_strategy(void *, int, daddr_t, size_t, char *, size_t *);
110static int stor_open(struct open_file *, ...);
111static int stor_close(struct open_file *);
112static void stor_print(int);
113
114struct devsw uboot_storage = {
115	"disk",
116	DEVT_DISK,
117	stor_init,
118	stor_strategy,
119	stor_open,
120	stor_close,
121	noioctl,
122	stor_print
123};
124
125static int
126stor_init(void)
127{
128	struct device_info *di;
129	int i, found = 0;
130
131	if (devs_no == 0) {
132		printf("No U-Boot devices! Really enumerated?\n");
133		return (-1);
134	}
135
136	for (i = 0; i < devs_no; i++) {
137		di = ub_dev_get(i);
138		if ((di != NULL) && (di->type & DEV_TYP_STOR)) {
139			if (stor_info_no >= UB_MAX_DEV) {
140				printf("Too many storage devices: %d\n",
141				    stor_info_no);
142				return (-1);
143			}
144			stor_info[stor_info_no++] = i;
145			found = 1;
146		}
147	}
148
149	if (!found) {
150		printf("No storage devices\n");
151		return (-1);
152	}
153
154	debugf("storage devices found: %d\n", stor_info_no);
155	return (0);
156}
157
158static int
159stor_strategy(void *devdata, int rw, daddr_t blk, size_t size, char *buf,
160    size_t *rsize)
161{
162	struct uboot_devdesc *dev = (struct uboot_devdesc *)devdata;
163	struct open_dev *od = (struct open_dev *)dev->d_disk.data;
164	int bcount, err;
165
166	debugf("od=%p, size=%d, bsize=%d\n", od, size, od->od_bsize);
167
168	if (rw != F_READ) {
169		stor_printf("write attempt, operation not supported!\n");
170		return (EROFS);
171	}
172
173	if (size % od->od_bsize) {
174		stor_printf("size=%d not multiple of device block size=%d\n",
175		    size, od->od_bsize);
176		return (EIO);
177	}
178	bcount = size / od->od_bsize;
179
180	if (rsize)
181		*rsize = 0;
182
183	err = stor_readdev(dev, blk + od->od_bstart, bcount, buf);
184	if (!err && rsize)
185		*rsize = size;
186
187	return (err);
188}
189
190static int
191stor_open(struct open_file *f, ...)
192{
193	va_list ap;
194	struct open_dev *od;
195	struct uboot_devdesc *dev;
196	int err;
197
198	va_start(ap, f);
199	dev = va_arg(ap, struct uboot_devdesc *);
200	va_end(ap);
201
202	if ((err = stor_opendev(&od, dev)) != 0)
203		return (err);
204
205	((struct uboot_devdesc *)(f->f_devdata))->d_disk.data = od;
206
207	return (0);
208}
209
210static int
211stor_close(struct open_file *f)
212{
213	struct uboot_devdesc *dev;
214
215	dev = (struct uboot_devdesc *)(f->f_devdata);
216
217	return (stor_closedev(dev));
218}
219
220static int
221stor_open_gpt(struct open_dev *od, struct uboot_devdesc *dev)
222{
223	char *buf;
224	struct dos_partition *dp;
225	struct gpt_hdr *hdr;
226	struct gpt_ent *ent;
227	daddr_t slba, lba, elba;
228	int eps, part, i;
229	int err = 0;
230
231	od->od_nparts = 0;
232	od->od_partitions = NULL;
233
234	/* Devices with block size smaller than 512 bytes cannot use GPT */
235	if (od->od_bsize < 512)
236		return (ENXIO);
237
238	/* Allocate 1 block */
239	buf = malloc(od->od_bsize);
240	if (!buf) {
241		stor_printf("could not allocate memory for GPT\n");
242		return (ENOMEM);
243	}
244
245	/* Read MBR */
246	err = stor_readdev(dev, 0, 1, buf);
247	if (err) {
248		stor_printf("GPT read error=%d\n", err);
249		err = EIO;
250		goto out;
251	}
252
253	/* Check the slice table magic. */
254	if (*((uint16_t *)(buf + DOSMAGICOFFSET)) != DOSMAGIC) {
255		err = ENXIO;
256		goto out;
257	}
258
259	/* Check GPT slice */
260	dp = (struct dos_partition *)(buf + DOSPARTOFF);
261	part = 0;
262
263	for (i = 0; i < NDOSPART; i++) {
264		if (dp[i].dp_typ == 0xee)
265			part += 1;
266		else if (dp[i].dp_typ != 0x00) {
267			err = EINVAL;
268			goto out;
269		}
270	}
271
272	if (part != 1) {
273		err = EINVAL;
274		goto out;
275	}
276
277	/* Read primary GPT header */
278	err = stor_readdev(dev, 1, 1, buf);
279	if (err) {
280		stor_printf("GPT read error=%d\n", err);
281		err = EIO;
282		goto out;
283	}
284
285	hdr = (struct gpt_hdr *)buf;
286
287	/* Check GPT header */
288	if (bcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0 ||
289	    hdr->hdr_lba_self != 1 || hdr->hdr_revision < 0x00010000 ||
290	    hdr->hdr_entsz < sizeof(*ent) ||
291	    od->od_bsize % hdr->hdr_entsz != 0) {
292		debugf("Invalid GPT header!\n");
293		err = EINVAL;
294		goto out;
295	}
296
297	/* Count number of valid partitions */
298	part = 0;
299	eps = od->od_bsize / hdr->hdr_entsz;
300	slba = hdr->hdr_lba_table;
301	elba = slba + hdr->hdr_entries / eps;
302
303	for (lba = slba; lba < elba; lba++) {
304		err = stor_readdev(dev, lba, 1, buf);
305		if (err) {
306			stor_printf("GPT read error=%d\n", err);
307			err = EIO;
308			goto out;
309		}
310
311		ent = (struct gpt_ent *)buf;
312
313		for (i = 0; i < eps; i++) {
314			if (uuid_is_nil(&ent[i].ent_type, NULL) ||
315			    ent[i].ent_lba_start == 0 ||
316			    ent[i].ent_lba_end < ent[i].ent_lba_start)
317				continue;
318
319			part += 1;
320		}
321	}
322
323	/* Save information about partitions */
324	if (part != 0) {
325		od->od_nparts = part;
326		od->od_partitions = malloc(part * sizeof(struct gpt_part));
327		if (!od->od_partitions) {
328			stor_printf("could not allocate memory for GPT\n");
329			err = ENOMEM;
330			goto out;
331		}
332
333		part = 0;
334		for (lba = slba; lba < elba; lba++) {
335			err = stor_readdev(dev, lba, 1, buf);
336			if (err) {
337				stor_printf("GPT read error=%d\n", err);
338				err = EIO;
339				goto out;
340			}
341
342			ent = (struct gpt_ent *)buf;
343
344			for (i = 0; i < eps; i++) {
345				if (uuid_is_nil(&ent[i].ent_type, NULL) ||
346				    ent[i].ent_lba_start == 0 ||
347				    ent[i].ent_lba_end < ent[i].ent_lba_start)
348					continue;
349
350				od->od_partitions[part].gp_index = (lba - slba)
351				    * eps + i + 1;
352				od->od_partitions[part].gp_type =
353				    ent[i].ent_type;
354				od->od_partitions[part].gp_start =
355				    ent[i].ent_lba_start;
356				od->od_partitions[part].gp_end =
357				    ent[i].ent_lba_end;
358				part += 1;
359			}
360		}
361	}
362
363	dev->d_disk.ptype = PTYPE_GPT;
364
365	for (i = 0; i < od->od_nparts; i++)
366		if (od->od_partitions[i].gp_index == dev->d_disk.pnum)
367			od->od_bstart = od->od_partitions[i].gp_start;
368
369out:
370	if (err && od->od_partitions)
371		free(od->od_partitions);
372
373	free(buf);
374	return (err);
375}
376
377static int
378stor_open_bsdlabel(struct open_dev *od, struct uboot_devdesc *dev)
379{
380	char *buf;
381	struct disklabel *dl;
382	int err = 0;
383
384	/* Allocate 1 block */
385	buf = malloc(od->od_bsize);
386	if (!buf) {
387		stor_printf("could not allocate memory for disklabel\n");
388		return (ENOMEM);
389	}
390
391	/* Read disklabel */
392	err = stor_readdev(dev, LABELSECTOR, 1, buf);
393	if (err) {
394		stor_printf("disklabel read error=%d\n", err);
395		err = ERDLAB;
396		goto out;
397	}
398	bcopy(buf + LABELOFFSET, &od->od_bsdlabel, sizeof(struct disklabel));
399	dl = &od->od_bsdlabel;
400
401	if (dl->d_magic != DISKMAGIC) {
402		stor_printf("no disklabel magic!\n");
403		err = EUNLAB;
404		goto out;
405	}
406
407	od->od_bstart = dl->d_partitions[dev->d_disk.pnum].p_offset;
408	dev->d_disk.ptype = PTYPE_BSDLABEL;
409
410	debugf("bstart=%d\n", od->od_bstart);
411
412out:
413	free(buf);
414	return (err);
415}
416
417static int
418stor_readdev(struct uboot_devdesc *dev, daddr_t blk, size_t size, char *buf)
419{
420	lbasize_t real_size;
421	int err, handle;
422
423	debugf("reading size=%d @ 0x%08x\n", size, (uint32_t)buf);
424
425	handle = stor_info[dev->d_unit];
426	err = ub_dev_read(handle, buf, size, blk, &real_size);
427	if (err != 0) {
428		stor_printf("read failed, error=%d\n", err);
429		return (EIO);
430	}
431
432	if (real_size != size) {
433		stor_printf("real size != size\n");
434		err = EIO;
435	}
436
437	return (err);
438}
439
440
441static int
442stor_opendev(struct open_dev **odp, struct uboot_devdesc *dev)
443{
444	struct device_info *di;
445	struct open_dev *od;
446	int err, h;
447
448	h = stor_info[dev->d_unit];
449
450	debugf("refcount=%d\n", stor_open_count);
451
452	/*
453	 * There can be recursive open calls from the infrastructure, but at
454	 * U-Boot level open the device only the first time.
455	 */
456	if (stor_open_count > 0)
457		stor_open_count++;
458	else if ((err = ub_dev_open(h)) != 0) {
459		stor_printf("device open failed with error=%d, handle=%d\n",
460		    err, h);
461		*odp = NULL;
462		return (ENXIO);
463	}
464
465	if ((di = ub_dev_get(h)) == NULL)
466		panic("could not retrieve U-Boot device_info, handle=%d", h);
467
468	if ((od = malloc(sizeof(struct open_dev))) == NULL) {
469		stor_printf("could not allocate memory for open_dev\n");
470		return (ENOMEM);
471	}
472	od->od_bsize = di->di_stor.block_size;
473	od->od_bstart = 0;
474
475	if ((err = stor_open_gpt(od, dev)) != 0)
476		err = stor_open_bsdlabel(od, dev);
477
478	if (err != 0)
479		free(od);
480	else {
481		stor_open_count = 1;
482		*odp = od;
483	}
484
485	return (err);
486}
487
488static int
489stor_closedev(struct uboot_devdesc *dev)
490{
491	struct open_dev *od;
492	int err, h;
493
494	od = (struct open_dev *)dev->d_disk.data;
495	if (dev->d_disk.ptype == PTYPE_GPT && od->od_nparts != 0)
496		free(od->od_partitions);
497
498	free(od);
499	dev->d_disk.data = NULL;
500
501	if (--stor_open_count == 0) {
502		h = stor_info[dev->d_unit];
503		if ((err = ub_dev_close(h)) != 0) {
504			stor_printf("device close failed with error=%d, "
505			    "handle=%d\n", err, h);
506			return (ENXIO);
507		}
508	}
509
510	return (0);
511}
512
513/* Given a size in 512 byte sectors, convert it to a human-readable number. */
514/* XXX stolen from sys/boot/i386/libi386/biosdisk.c, should really be shared */
515static char *
516display_size(uint64_t size)
517{
518	static char buf[80];
519	char unit;
520
521	size /= 2;
522	unit = 'K';
523	if (size >= 10485760000LL) {
524		size /= 1073741824;
525		unit = 'T';
526	} else if (size >= 10240000) {
527		size /= 1048576;
528		unit = 'G';
529	} else if (size >= 10000) {
530		size /= 1024;
531		unit = 'M';
532	}
533	sprintf(buf, "%.6ld%cB", (long)size, unit);
534	return (buf);
535}
536
537static void
538stor_print_bsdlabel(struct uboot_devdesc *dev, char *prefix, int verbose)
539{
540	char buf[512], line[80];
541	struct disklabel *dl;
542	uint32_t off, size;
543	int err, i, t;
544
545	/* Read disklabel */
546	err = stor_readdev(dev, LABELSECTOR, 1, buf);
547	if (err) {
548		sprintf(line, "%s%d: disklabel read error=%d\n",
549		    dev->d_dev->dv_name, dev->d_unit, err);
550		pager_output(line);
551		return;
552	}
553	dl = (struct disklabel *)buf;
554
555	if (dl->d_magic != DISKMAGIC) {
556		sprintf(line, "%s%d: no disklabel magic!\n",
557		    dev->d_dev->dv_name, dev->d_unit);
558		pager_output(line);
559		return;
560	}
561
562	/* Print partitions info */
563	for (i = 0; i < dl->d_npartitions; i++) {
564		if ((t = dl->d_partitions[i].p_fstype) < FSMAXTYPES) {
565
566			off = dl->d_partitions[i].p_offset;
567			size = dl->d_partitions[i].p_size;
568			if (fstypenames[t] == NULL || size == 0)
569				continue;
570
571			if ((('a' + i) == 'c') && (!verbose))
572				continue;
573
574			sprintf(line, "  %s%c: %s %s (%d - %d)\n", prefix,
575			    'a' + i, fstypenames[t], display_size(size),
576			    off, off + size);
577
578			pager_output(line);
579		}
580	}
581}
582
583static void
584stor_print_gpt(struct uboot_devdesc *dev, char *prefix, int verbose)
585{
586	struct open_dev *od = (struct open_dev *)dev->d_disk.data;
587	struct gpt_part *gp;
588	char line[80];
589	char *fs;
590	int i;
591
592	for (i = 0; i < od->od_nparts; i++) {
593		gp = &od->od_partitions[i];
594
595		if (uuid_equal(&gp->gp_type, &efi, NULL))
596			fs = "EFI";
597		else if (uuid_equal(&gp->gp_type, &ms_basic_data, NULL))
598			fs = "FAT/NTFS";
599		else if (uuid_equal(&gp->gp_type, &freebsd_boot, NULL))
600			fs = "FreeBSD Boot";
601		else if (uuid_equal(&gp->gp_type, &freebsd_ufs, NULL))
602			fs = "FreeBSD UFS";
603		else if (uuid_equal(&gp->gp_type, &freebsd_swap, NULL))
604			fs = "FreeBSD Swap";
605		else if (uuid_equal(&gp->gp_type, &freebsd_zfs, NULL))
606			fs = "FreeBSD ZFS";
607		else
608			fs = "unknown";
609
610		sprintf(line, "  %sp%u: %s %s (%lld - %lld)\n", prefix,
611		    gp->gp_index, fs,
612		    display_size(gp->gp_end + 1 - gp->gp_start), gp->gp_start,
613		    gp->gp_end);
614
615		pager_output(line);
616	}
617}
618
619static void
620stor_print_one(int i, struct device_info *di, int verbose)
621{
622	struct uboot_devdesc dev;
623	struct open_dev *od;
624	char line[80];
625
626	sprintf(line, "\tdisk%d (%s)\n", i, ub_stor_type(di->type));
627	pager_output(line);
628
629	dev.d_dev = &uboot_storage;
630	dev.d_unit = i;
631	dev.d_disk.pnum = -1;
632	dev.d_disk.data = NULL;
633
634	if (stor_opendev(&od, &dev) == 0) {
635		dev.d_disk.data = od;
636
637		if (dev.d_disk.ptype == PTYPE_GPT) {
638			sprintf(line, "\t\tdisk%d", i);
639			stor_print_gpt(&dev, line, verbose);
640		} else if (dev.d_disk.ptype == PTYPE_BSDLABEL) {
641			sprintf(line, "\t\tdisk%d", i);
642			stor_print_bsdlabel(&dev, line, verbose);
643		}
644
645		stor_closedev(&dev);
646	}
647}
648
649static void
650stor_print(int verbose)
651{
652	struct device_info *di;
653	int i;
654
655	for (i = 0; i < stor_info_no; i++) {
656		di = ub_dev_get(stor_info[i]);
657		if (di != NULL)
658			stor_print_one(i, di, verbose);
659	}
660}
661