]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sys/boot/uboot/lib/disk.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / sys / boot / uboot / lib / disk.c
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$");
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
69 struct gpt_part {
70         int             gp_index;
71         uuid_t          gp_type;
72         uint64_t        gp_start;
73         uint64_t        gp_end;
74 };
75
76 struct 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
94 static uuid_t efi = GPT_ENT_TYPE_EFI;
95 static uuid_t freebsd_boot = GPT_ENT_TYPE_FREEBSD_BOOT;
96 static uuid_t freebsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS;
97 static uuid_t freebsd_swap = GPT_ENT_TYPE_FREEBSD_SWAP;
98 static uuid_t freebsd_zfs = GPT_ENT_TYPE_FREEBSD_ZFS;
99 static uuid_t ms_basic_data = GPT_ENT_TYPE_MS_BASIC_DATA;
100
101 static int stor_info[UB_MAX_DEV];
102 static int stor_info_no = 0;
103 static int stor_opendev(struct open_dev **, struct uboot_devdesc *);
104 static int stor_closedev(struct uboot_devdesc *);
105 static int stor_readdev(struct uboot_devdesc *, daddr_t, size_t, char *);
106 static int stor_open_count = 0;
107
108 /* devsw I/F */
109 static int stor_init(void);
110 static int stor_strategy(void *, int, daddr_t, size_t, char *, size_t *);
111 static int stor_open(struct open_file *, ...);
112 static int stor_close(struct open_file *);
113 static void stor_print(int);
114
115 struct 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
126 static void
127 uuid_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
135 static int
136 stor_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                 printf("No storage devices\n");
161                 return (-1);
162         }
163
164         debugf("storage devices found: %d\n", stor_info_no);
165         return (0);
166 }
167
168 static int
169 stor_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
200 static int
201 stor_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
220 static int
221 stor_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
230 static int
231 stor_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
392 out:
393         if (err && od->od_partitions)
394                 free(od->od_partitions);
395
396         free(buf);
397         return (err);
398 }
399
400 static int
401 stor_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
435 out:
436         free(buf);
437         return (err);
438 }
439
440 static int
441 stor_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
464 static int
465 stor_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
511 static int
512 stor_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 */
538 static char *
539 display_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
560 static void
561 stor_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
606 static void
607 stor_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
642 static void
643 stor_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
672 static void
673 stor_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 }