disk.c revision 185099
1176348Smarcel/*-
2185099Sraj * Copyright (c) 2008 Semihalf, Rafal Jaworowski
3176348Smarcel * All rights reserved.
4176348Smarcel *
5176348Smarcel * Redistribution and use in source and binary forms, with or without
6176348Smarcel * modification, are permitted provided that the following conditions
7176348Smarcel * are met:
8176348Smarcel * 1. Redistributions of source code must retain the above copyright
9176348Smarcel *    notice, this list of conditions and the following disclaimer.
10176348Smarcel * 2. Redistributions in binary form must reproduce the above copyright
11176348Smarcel *    notice, this list of conditions and the following disclaimer in the
12176348Smarcel *    documentation and/or other materials provided with the distribution.
13176348Smarcel *
14185099Sraj * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15185099Sraj * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16185099Sraj * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17185099Sraj * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18185099Sraj * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19185099Sraj * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20185099Sraj * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21185099Sraj * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22185099Sraj * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23185099Sraj * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24185099Sraj * SUCH DAMAGE.
25185099Sraj *
26176348Smarcel */
27176348Smarcel
28185099Sraj/*
29185099Sraj * Block storage I/O routines for U-Boot
30185099Sraj */
31185099Sraj
32176348Smarcel#include <sys/cdefs.h>
33176348Smarcel__FBSDID("$FreeBSD: head/sys/boot/uboot/lib/disk.c 185099 2008-11-19 17:34:28Z raj $");
34176348Smarcel
35176348Smarcel#include <sys/param.h>
36176348Smarcel#include <sys/queue.h>
37176348Smarcel#include <netinet/in.h>
38176348Smarcel#include <machine/stdarg.h>
39176348Smarcel#include <stand.h>
40185099Sraj#include <uuid.h>
41176348Smarcel
42185099Sraj#define FSTYPENAMES
43185099Sraj#include <sys/disklabel.h>
44185099Sraj
45185099Sraj#include "api_public.h"
46176348Smarcel#include "bootstrap.h"
47185099Sraj#include "glue.h"
48185099Sraj#include "libuboot.h"
49176348Smarcel
50185099Sraj#define DEBUG
51185099Sraj#undef DEBUG
52176348Smarcel
53185099Sraj#define stor_printf(fmt, args...) do {			\
54185099Sraj    printf("%s%d: ", dev->d_dev->dv_name, dev->d_unit);	\
55185099Sraj    printf(fmt, ##args);				\
56185099Sraj} while (0)
57185099Sraj
58185099Sraj#ifdef DEBUG
59185099Sraj#define debugf(fmt, args...) do { printf("%s(): ", __func__);	\
60185099Sraj    printf(fmt,##args); } while (0)
61185099Sraj#else
62185099Sraj#define debugf(fmt, args...)
63185099Sraj#endif
64185099Sraj
65185099Srajstruct gpt_part {
66185099Sraj	int		gp_index;
67185099Sraj	uuid_t		gp_type;
68185099Sraj	uint64_t	gp_start;
69185099Sraj	uint64_t	gp_end;
70176348Smarcel};
71176348Smarcel
72185099Srajstruct open_dev {
73185099Sraj	int		od_bsize;	/* block size */
74185099Sraj	int		od_bstart;	/* start block offset from beginning of disk */
75185099Sraj	int		od_type;
76185099Sraj#define OD_BSDLABEL	0x0001
77185099Sraj#define	OD_GPT		0x0002
78185099Sraj	union {
79185099Sraj		struct {
80185099Sraj			struct disklabel bsdlabel;
81185099Sraj		} _bsd;
82185099Sraj		struct {
83185099Sraj			struct gpt_part	*gpt_partitions;
84185099Sraj			int		gpt_nparts;
85185099Sraj		} _gpt;
86185099Sraj	} _data;
87176348Smarcel};
88176348Smarcel
89185099Sraj#define	od_bsdlabel	_data._bsd.bsdlabel
90185099Sraj#define	od_nparts	_data._gpt.gpt_nparts
91185099Sraj#define	od_partitions	_data._gpt.gpt_partitions
92176348Smarcel
93185099Srajstatic int stor_info[UB_MAX_DEV];
94185099Srajstatic int stor_info_no = 0;
95185099Srajstatic int stor_opendev(struct open_dev **, struct uboot_devdesc *);
96185099Srajstatic int stor_closedev(struct uboot_devdesc *);
97185099Srajstatic int stor_readdev(struct uboot_devdesc *, daddr_t, size_t, char *);
98185099Srajstatic int stor_open_count = 0;
99185099Sraj
100185099Sraj/* devsw I/F */
101185099Srajstatic int stor_init(void);
102185099Srajstatic int stor_strategy(void *, int, daddr_t, size_t, char *, size_t *);
103185099Srajstatic int stor_open(struct open_file *, ...);
104185099Srajstatic int stor_close(struct open_file *);
105185099Srajstatic void stor_print(int);
106185099Sraj
107185099Srajstruct devsw uboot_storage = {
108185099Sraj	"disk",
109185099Sraj	DEVT_DISK,
110185099Sraj	stor_init,
111185099Sraj	stor_strategy,
112185099Sraj	stor_open,
113185099Sraj	stor_close,
114185099Sraj	noioctl,
115185099Sraj	stor_print
116185099Sraj};
117185099Sraj
118176348Smarcelstatic int
119185099Srajstor_init(void)
120176348Smarcel{
121185099Sraj	struct device_info *di;
122185099Sraj	int i, found = 0;
123177152Sobrien
124185099Sraj	if (devs_no == 0) {
125185099Sraj		printf("No U-Boot devices! Really enumerated?\n");
126185099Sraj		return (-1);
127185099Sraj	}
128185099Sraj
129185099Sraj	for (i = 0; i < devs_no; i++) {
130185099Sraj		di = ub_dev_get(i);
131185099Sraj		if ((di != NULL) && (di->type & DEV_TYP_STOR)) {
132185099Sraj			if (stor_info_no >= UB_MAX_DEV) {
133185099Sraj				printf("Too many storage devices: %d\n",
134185099Sraj				    stor_info_no);
135185099Sraj				return (-1);
136185099Sraj			}
137185099Sraj			stor_info[stor_info_no++] = i;
138185099Sraj			found = 1;
139185099Sraj		}
140185099Sraj	}
141185099Sraj
142185099Sraj	if (!found) {
143185099Sraj		printf("No storage devices\n");
144185099Sraj		return (-1);
145185099Sraj	}
146185099Sraj
147185099Sraj	debugf("storage devices found: %d\n", stor_info_no);
148185099Sraj	return (0);
149176348Smarcel}
150176348Smarcel
151176348Smarcelstatic int
152185099Srajstor_strategy(void *devdata, int rw, daddr_t blk, size_t size, char *buf,
153176348Smarcel    size_t *rsize)
154176348Smarcel{
155185099Sraj	struct uboot_devdesc *dev = (struct uboot_devdesc *)devdata;
156185099Sraj	struct open_dev *od = (struct open_dev *)dev->d_disk.data;
157185099Sraj	int bcount, err;
158177152Sobrien
159185099Sraj	debugf("od=%p, size=%d, bsize=%d\n", od, size, od->od_bsize);
160185099Sraj
161185099Sraj	if (rw != F_READ) {
162185099Sraj		stor_printf("write attempt, operation not supported!\n");
163185099Sraj		return (EROFS);
164185099Sraj	}
165185099Sraj
166185099Sraj	if (size % od->od_bsize) {
167185099Sraj		stor_printf("size=%d not multiple of device block size=%d\n",
168185099Sraj		    size, od->od_bsize);
169185099Sraj		return (EIO);
170185099Sraj	}
171185099Sraj	bcount = size / od->od_bsize;
172185099Sraj
173185099Sraj	if (rsize)
174185099Sraj		*rsize = 0;
175185099Sraj
176185099Sraj	err = stor_readdev(dev, blk + od->od_bstart, bcount, buf);
177185099Sraj	if (!err && rsize)
178185099Sraj		*rsize = size;
179185099Sraj
180185099Sraj	return (err);
181176348Smarcel}
182176348Smarcel
183176348Smarcelstatic int
184185099Srajstor_open(struct open_file *f, ...)
185176348Smarcel{
186185099Sraj	va_list ap;
187185099Sraj	struct open_dev *od;
188185099Sraj	struct uboot_devdesc *dev;
189185099Sraj	int err;
190177152Sobrien
191185099Sraj	va_start(ap, f);
192185099Sraj	dev = va_arg(ap, struct uboot_devdesc *);
193185099Sraj	va_end(ap);
194185099Sraj
195185099Sraj	if ((err = stor_opendev(&od, dev)) != 0)
196185099Sraj		return (err);
197185099Sraj
198185099Sraj	((struct uboot_devdesc *)(f->f_devdata))->d_disk.data = od;
199185099Sraj
200185099Sraj	return (0);
201176348Smarcel}
202176348Smarcel
203176348Smarcelstatic int
204185099Srajstor_close(struct open_file *f)
205176348Smarcel{
206185099Sraj	struct uboot_devdesc *dev;
207177152Sobrien
208185099Sraj	dev = (struct uboot_devdesc *)(f->f_devdata);
209185099Sraj
210185099Sraj	return (stor_closedev(dev));
211176348Smarcel}
212176348Smarcel
213176348Smarcelstatic int
214185099Srajstor_open_gpt(struct open_dev *od, struct uboot_devdesc *dev)
215176348Smarcel{
216177152Sobrien
217185099Sraj	/* TODO */
218185099Sraj	return (ENXIO);
219176348Smarcel}
220176348Smarcel
221185099Srajstatic int
222185099Srajstor_open_bsdlabel(struct open_dev *od, struct uboot_devdesc *dev)
223185099Sraj{
224185099Sraj	char *buf;
225185099Sraj	struct disklabel *dl;
226185099Sraj	int err = 0;
227185099Sraj
228185099Sraj	/* Allocate 1 block */
229185099Sraj	buf = malloc(od->od_bsize);
230185099Sraj	if (!buf) {
231185099Sraj		stor_printf("could not allocate memory for disklabel\n");
232185099Sraj		return (ENOMEM);
233185099Sraj	}
234185099Sraj
235185099Sraj	/* Read disklabel */
236185099Sraj	err = stor_readdev(dev, LABELSECTOR, 1, buf);
237185099Sraj	if (err) {
238185099Sraj		stor_printf("disklabel read error=%d\n", err);
239185099Sraj		err = ERDLAB;
240185099Sraj		goto out;
241185099Sraj	}
242185099Sraj	bcopy(buf + LABELOFFSET, &od->od_bsdlabel, sizeof(struct disklabel));
243185099Sraj	dl = &od->od_bsdlabel;
244185099Sraj
245185099Sraj	if (dl->d_magic != DISKMAGIC) {
246185099Sraj		stor_printf("no disklabel magic!\n");
247185099Sraj		err = EUNLAB;
248185099Sraj		goto out;
249185099Sraj	}
250185099Sraj	od->od_type = OD_BSDLABEL;
251185099Sraj	od->od_bstart = dl->d_partitions[dev->d_disk.partition].p_offset;
252185099Sraj
253185099Sraj	debugf("bstart=%d\n", od->od_bstart);
254185099Sraj
255185099Srajout:
256185099Sraj	free(buf);
257185099Sraj	return (err);
258185099Sraj}
259185099Sraj
260185099Srajstatic int
261185099Srajstor_readdev(struct uboot_devdesc *dev, daddr_t blk, size_t size, char *buf)
262185099Sraj{
263185099Sraj	lbasize_t real_size;
264185099Sraj	int err, handle;
265185099Sraj
266185099Sraj	debugf("reading size=%d @ 0x%08x\n", size, (uint32_t)buf);
267185099Sraj
268185099Sraj	handle = stor_info[dev->d_unit];
269185099Sraj	err = ub_dev_read(handle, buf, size, blk, &real_size);
270185099Sraj	if (err != 0) {
271185099Sraj		stor_printf("read failed, error=%d\n", err);
272185099Sraj		return (EIO);
273185099Sraj	}
274185099Sraj
275185099Sraj	if (real_size != size) {
276185099Sraj		stor_printf("real size != size\n");
277185099Sraj		err = EIO;
278185099Sraj	}
279185099Sraj
280185099Sraj	return (err);
281185099Sraj}
282185099Sraj
283185099Sraj
284185099Srajstatic int
285185099Srajstor_opendev(struct open_dev **odp, struct uboot_devdesc *dev)
286185099Sraj{
287185099Sraj	struct device_info *di;
288185099Sraj	struct open_dev *od;
289185099Sraj	int err, h;
290185099Sraj
291185099Sraj	h = stor_info[dev->d_unit];
292185099Sraj
293185099Sraj	debugf("refcount=%d\n", stor_open_count);
294185099Sraj
295185099Sraj	/*
296185099Sraj	 * There can be recursive open calls from the infrastructure, but at
297185099Sraj	 * U-Boot level open the device only the first time.
298185099Sraj	 */
299185099Sraj	if (stor_open_count > 0)
300185099Sraj		stor_open_count++;
301185099Sraj	else if ((err = ub_dev_open(h)) != 0) {
302185099Sraj		stor_printf("device open failed with error=%d, handle=%d\n",
303185099Sraj		    err, h);
304185099Sraj		*odp = NULL;
305185099Sraj		return (ENXIO);
306185099Sraj	}
307185099Sraj
308185099Sraj	if ((di = ub_dev_get(h)) == NULL)
309185099Sraj		panic("could not retrieve U-Boot device_info, handle=%d", h);
310185099Sraj
311185099Sraj	if ((od = malloc(sizeof(struct open_dev))) == NULL) {
312185099Sraj		stor_printf("could not allocate memory for open_dev\n");
313185099Sraj		return (ENOMEM);
314185099Sraj	}
315185099Sraj	od->od_bsize = di->di_stor.block_size;
316185099Sraj	od->od_bstart = 0;
317185099Sraj	od->od_type = 0;
318185099Sraj
319185099Sraj	if ((err = stor_open_gpt(od, dev)) != 0)
320185099Sraj		err = stor_open_bsdlabel(od, dev);
321185099Sraj
322185099Sraj	if (err != 0)
323185099Sraj		free(od);
324185099Sraj	else {
325185099Sraj		stor_open_count = 1;
326185099Sraj		*odp = od;
327185099Sraj	}
328185099Sraj
329185099Sraj	return (err);
330185099Sraj}
331185099Sraj
332185099Srajstatic int
333185099Srajstor_closedev(struct uboot_devdesc *dev)
334185099Sraj{
335185099Sraj	int err, h;
336185099Sraj
337185099Sraj	free((struct open_dev *)dev->d_disk.data);
338185099Sraj	dev->d_disk.data = NULL;
339185099Sraj
340185099Sraj	if (--stor_open_count == 0) {
341185099Sraj		h = stor_info[dev->d_unit];
342185099Sraj		if ((err = ub_dev_close(h)) != 0) {
343185099Sraj			stor_printf("device close failed with error=%d, "
344185099Sraj			    "handle=%d\n", err, h);
345185099Sraj			return (ENXIO);
346185099Sraj		}
347185099Sraj	}
348185099Sraj
349185099Sraj	return (0);
350185099Sraj}
351185099Sraj
352185099Sraj/* Given a size in 512 byte sectors, convert it to a human-readable number. */
353185099Sraj/* XXX stolen from sys/boot/i386/libi386/biosdisk.c, should really be shared */
354185099Srajstatic char *
355185099Srajdisplay_size(uint64_t size)
356185099Sraj{
357185099Sraj	static char buf[80];
358185099Sraj	char unit;
359185099Sraj
360185099Sraj	size /= 2;
361185099Sraj	unit = 'K';
362185099Sraj	if (size >= 10485760000LL) {
363185099Sraj		size /= 1073741824;
364185099Sraj		unit = 'T';
365185099Sraj	} else if (size >= 10240000) {
366185099Sraj		size /= 1048576;
367185099Sraj		unit = 'G';
368185099Sraj	} else if (size >= 10000) {
369185099Sraj		size /= 1024;
370185099Sraj		unit = 'M';
371185099Sraj	}
372185099Sraj	sprintf(buf, "%.6ld%cB", (long)size, unit);
373185099Sraj	return (buf);
374185099Sraj}
375185099Sraj
376176348Smarcelstatic void
377185099Srajstor_print_bsdlabel(struct uboot_devdesc *dev, char *prefix, int verbose)
378176348Smarcel{
379185099Sraj	char buf[512], line[80];
380185099Sraj	struct disklabel *dl;
381185099Sraj	uint32_t off, size;
382185099Sraj	int err, i, t;
383176348Smarcel
384185099Sraj	/* Read disklabel */
385185099Sraj	err = stor_readdev(dev, LABELSECTOR, 1, buf);
386185099Sraj	if (err) {
387185099Sraj		sprintf(line, "%s%d: disklabel read error=%d\n",
388185099Sraj		    dev->d_dev->dv_name, dev->d_unit, err);
389185099Sraj		pager_output(line);
390185099Sraj		return;
391185099Sraj	}
392185099Sraj	dl = (struct disklabel *)buf;
393185099Sraj
394185099Sraj	if (dl->d_magic != DISKMAGIC) {
395185099Sraj		sprintf(line, "%s%d: no disklabel magic!\n",
396185099Sraj		    dev->d_dev->dv_name, dev->d_unit);
397185099Sraj		pager_output(line);
398185099Sraj		return;
399185099Sraj	}
400185099Sraj
401185099Sraj	/* Print partitions info */
402185099Sraj	for (i = 0; i < dl->d_npartitions; i++) {
403185099Sraj		if ((t = dl->d_partitions[i].p_fstype) < FSMAXTYPES) {
404185099Sraj
405185099Sraj			off = dl->d_partitions[i].p_offset;
406185099Sraj			size = dl->d_partitions[i].p_size;
407185099Sraj			if (fstypenames[t] == NULL || size == 0)
408185099Sraj				continue;
409185099Sraj
410185099Sraj			if ((('a' + i) == 'c') && (!verbose))
411185099Sraj				continue;
412185099Sraj
413185099Sraj			sprintf(line, "  %s%c: %s %s (%d - %d)\n", prefix,
414185099Sraj			    'a' + i, fstypenames[t], display_size(size),
415185099Sraj			    off, off + size);
416185099Sraj
417185099Sraj			pager_output(line);
418185099Sraj		}
419185099Sraj	}
420176348Smarcel}
421185099Sraj
422185099Srajstatic void
423185099Srajstor_print_one(int i, struct device_info *di, int verbose)
424185099Sraj{
425185099Sraj	struct uboot_devdesc dev;
426185099Sraj	struct open_dev *od;
427185099Sraj	char line[80];
428185099Sraj
429185099Sraj	sprintf(line, "\tdisk%d (%s)\n", i, ub_stor_type(di->type));
430185099Sraj	pager_output(line);
431185099Sraj
432185099Sraj	dev.d_dev = &uboot_storage;
433185099Sraj	dev.d_unit = i;
434185099Sraj	dev.d_disk.partition = -1;
435185099Sraj	dev.d_disk.data = NULL;
436185099Sraj
437185099Sraj	if (stor_opendev(&od, &dev) == 0) {
438185099Sraj		dev.d_disk.data = od;
439185099Sraj
440185099Sraj		if (od->od_type == OD_GPT) {
441185099Sraj			/* TODO */
442185099Sraj
443185099Sraj		} else if (od->od_type == OD_BSDLABEL) {
444185099Sraj			sprintf(line, "\t\tdisk%d", i);
445185099Sraj			stor_print_bsdlabel(&dev, line, verbose);
446185099Sraj		}
447185099Sraj
448185099Sraj		stor_closedev(&dev);
449185099Sraj	}
450185099Sraj}
451185099Sraj
452185099Srajstatic void
453185099Srajstor_print(int verbose)
454185099Sraj{
455185099Sraj	struct device_info *di;
456185099Sraj	int i;
457185099Sraj
458185099Sraj	for (i = 0; i < stor_info_no; i++) {
459185099Sraj		di = ub_dev_get(stor_info[i]);
460185099Sraj		if (di != NULL)
461185099Sraj			stor_print_one(i, di, verbose);
462185099Sraj	}
463185099Sraj}
464