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