19dc70af8SWarner Losh /*-
29dc70af8SWarner Losh * Copyright (c) 2008 Semihalf, Rafal Jaworowski
39dc70af8SWarner Losh * Copyright (c) 2009 Semihalf, Piotr Ziecik
49dc70af8SWarner Losh * Copyright (c) 2012 Andrey V. Elsukov <ae@FreeBSD.org>
59dc70af8SWarner Losh * All rights reserved.
69dc70af8SWarner Losh *
79dc70af8SWarner Losh * Redistribution and use in source and binary forms, with or without
89dc70af8SWarner Losh * modification, are permitted provided that the following conditions
99dc70af8SWarner Losh * are met:
109dc70af8SWarner Losh * 1. Redistributions of source code must retain the above copyright
119dc70af8SWarner Losh * notice, this list of conditions and the following disclaimer.
129dc70af8SWarner Losh * 2. Redistributions in binary form must reproduce the above copyright
139dc70af8SWarner Losh * notice, this list of conditions and the following disclaimer in the
149dc70af8SWarner Losh * documentation and/or other materials provided with the distribution.
159dc70af8SWarner Losh *
169dc70af8SWarner Losh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
179dc70af8SWarner Losh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
189dc70af8SWarner Losh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
199dc70af8SWarner Losh * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
209dc70af8SWarner Losh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
219dc70af8SWarner Losh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
229dc70af8SWarner Losh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
239dc70af8SWarner Losh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
249dc70af8SWarner Losh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
259dc70af8SWarner Losh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
269dc70af8SWarner Losh * SUCH DAMAGE.
279dc70af8SWarner Losh *
289dc70af8SWarner Losh */
299dc70af8SWarner Losh
309dc70af8SWarner Losh /*
319dc70af8SWarner Losh * Block storage I/O routines for U-Boot
329dc70af8SWarner Losh */
339dc70af8SWarner Losh
349dc70af8SWarner Losh #include <sys/param.h>
359dc70af8SWarner Losh #include <sys/disk.h>
369dc70af8SWarner Losh #include <machine/stdarg.h>
379dc70af8SWarner Losh #include <stand.h>
389dc70af8SWarner Losh
399dc70af8SWarner Losh #include "api_public.h"
409dc70af8SWarner Losh #include "bootstrap.h"
419dc70af8SWarner Losh #include "disk.h"
429dc70af8SWarner Losh #include "glue.h"
439dc70af8SWarner Losh #include "libuboot.h"
449dc70af8SWarner Losh
459dc70af8SWarner Losh #define stor_printf(fmt, args...) do { \
469dc70af8SWarner Losh printf("%s%d: ", dev->dd.d_dev->dv_name, dev->dd.d_unit); \
479dc70af8SWarner Losh printf(fmt, ##args); \
489dc70af8SWarner Losh } while (0)
499dc70af8SWarner Losh
509dc70af8SWarner Losh #ifdef DEBUG
519dc70af8SWarner Losh #define debugf(fmt, args...) do { printf("%s(): ", __func__); \
529dc70af8SWarner Losh printf(fmt,##args); } while (0)
539dc70af8SWarner Losh #else
549dc70af8SWarner Losh #define debugf(fmt, args...)
559dc70af8SWarner Losh #endif
569dc70af8SWarner Losh
579dc70af8SWarner Losh static struct {
589dc70af8SWarner Losh int opened; /* device is opened */
599dc70af8SWarner Losh int handle; /* storage device handle */
609dc70af8SWarner Losh int type; /* storage type */
619dc70af8SWarner Losh off_t blocks; /* block count */
629dc70af8SWarner Losh u_int bsize; /* block size */
639dc70af8SWarner Losh } stor_info[UB_MAX_DEV];
649dc70af8SWarner Losh
659dc70af8SWarner Losh #define SI(dev) (stor_info[(dev)->dd.d_unit])
669dc70af8SWarner Losh
679dc70af8SWarner Losh static int stor_info_no = 0;
689dc70af8SWarner Losh static int stor_opendev(struct disk_devdesc *);
699dc70af8SWarner Losh static int stor_readdev(struct disk_devdesc *, daddr_t, size_t, char *);
709dc70af8SWarner Losh
719dc70af8SWarner Losh /* devsw I/F */
729dc70af8SWarner Losh static int stor_init(void);
739dc70af8SWarner Losh static int stor_strategy(void *, int, daddr_t, size_t, char *, size_t *);
749dc70af8SWarner Losh static int stor_open(struct open_file *, ...);
759dc70af8SWarner Losh static int stor_close(struct open_file *);
769dc70af8SWarner Losh static int stor_ioctl(struct open_file *f, u_long cmd, void *data);
779dc70af8SWarner Losh static int stor_print(int);
789dc70af8SWarner Losh static void stor_cleanup(void);
799dc70af8SWarner Losh
809dc70af8SWarner Losh struct devsw uboot_storage = {
811e3d1c86SWarner Losh .dv_name = "disk",
821e3d1c86SWarner Losh .dv_type = DEVT_DISK,
831e3d1c86SWarner Losh .dv_init = stor_init,
841e3d1c86SWarner Losh .dv_strategy = stor_strategy,
851e3d1c86SWarner Losh .dv_open = stor_open,
861e3d1c86SWarner Losh .dv_close = stor_close,
871e3d1c86SWarner Losh .dv_ioctl = stor_ioctl,
881e3d1c86SWarner Losh .dv_print = stor_print,
891e3d1c86SWarner Losh .dv_cleanup = stor_cleanup,
90ad759c73SWarner Losh .dv_fmtdev = disk_fmtdev,
91*8337ab69SWarner Losh .dv_parsedev = disk_parsedev,
929dc70af8SWarner Losh };
939dc70af8SWarner Losh
949dc70af8SWarner Losh static int
stor_init(void)959dc70af8SWarner Losh stor_init(void)
969dc70af8SWarner Losh {
979dc70af8SWarner Losh struct device_info *di;
989dc70af8SWarner Losh int i;
999dc70af8SWarner Losh
1009dc70af8SWarner Losh if (devs_no == 0) {
1019dc70af8SWarner Losh printf("No U-Boot devices! Really enumerated?\n");
1029dc70af8SWarner Losh return (-1);
1039dc70af8SWarner Losh }
1049dc70af8SWarner Losh
1059dc70af8SWarner Losh for (i = 0; i < devs_no; i++) {
1069dc70af8SWarner Losh di = ub_dev_get(i);
1079dc70af8SWarner Losh if ((di != NULL) && (di->type & DEV_TYP_STOR)) {
1089dc70af8SWarner Losh if (stor_info_no >= UB_MAX_DEV) {
1099dc70af8SWarner Losh printf("Too many storage devices: %d\n",
1109dc70af8SWarner Losh stor_info_no);
1119dc70af8SWarner Losh return (-1);
1129dc70af8SWarner Losh }
1139dc70af8SWarner Losh stor_info[stor_info_no].handle = i;
1149dc70af8SWarner Losh stor_info[stor_info_no].opened = 0;
1159dc70af8SWarner Losh stor_info[stor_info_no].type = di->type;
1169dc70af8SWarner Losh stor_info[stor_info_no].blocks =
1179dc70af8SWarner Losh di->di_stor.block_count;
1189dc70af8SWarner Losh stor_info[stor_info_no].bsize =
1199dc70af8SWarner Losh di->di_stor.block_size;
1209dc70af8SWarner Losh stor_info_no++;
1219dc70af8SWarner Losh }
1229dc70af8SWarner Losh }
1239dc70af8SWarner Losh
1249dc70af8SWarner Losh if (!stor_info_no) {
1259dc70af8SWarner Losh debugf("No storage devices\n");
1269dc70af8SWarner Losh return (-1);
1279dc70af8SWarner Losh }
1289dc70af8SWarner Losh
1299dc70af8SWarner Losh debugf("storage devices found: %d\n", stor_info_no);
1309dc70af8SWarner Losh return (0);
1319dc70af8SWarner Losh }
1329dc70af8SWarner Losh
1339dc70af8SWarner Losh static void
stor_cleanup(void)1349dc70af8SWarner Losh stor_cleanup(void)
1359dc70af8SWarner Losh {
1369dc70af8SWarner Losh int i;
1379dc70af8SWarner Losh
1389dc70af8SWarner Losh for (i = 0; i < stor_info_no; i++)
1399dc70af8SWarner Losh if (stor_info[i].opened > 0)
1409dc70af8SWarner Losh ub_dev_close(stor_info[i].handle);
1419dc70af8SWarner Losh }
1429dc70af8SWarner Losh
1439dc70af8SWarner Losh static int
stor_strategy(void * devdata,int rw,daddr_t blk,size_t size,char * buf,size_t * rsize)1449dc70af8SWarner Losh stor_strategy(void *devdata, int rw, daddr_t blk, size_t size,
1459dc70af8SWarner Losh char *buf, size_t *rsize)
1469dc70af8SWarner Losh {
1479dc70af8SWarner Losh struct disk_devdesc *dev = (struct disk_devdesc *)devdata;
1489dc70af8SWarner Losh daddr_t bcount;
1499dc70af8SWarner Losh int err;
1509dc70af8SWarner Losh
1519dc70af8SWarner Losh rw &= F_MASK;
1529dc70af8SWarner Losh if (rw != F_READ) {
1539dc70af8SWarner Losh stor_printf("write attempt, operation not supported!\n");
1549dc70af8SWarner Losh return (EROFS);
1559dc70af8SWarner Losh }
1569dc70af8SWarner Losh
1579dc70af8SWarner Losh if (size % SI(dev).bsize) {
1589dc70af8SWarner Losh stor_printf("size=%zu not multiple of device "
1599dc70af8SWarner Losh "block size=%d\n",
1609dc70af8SWarner Losh size, SI(dev).bsize);
1619dc70af8SWarner Losh return (EIO);
1629dc70af8SWarner Losh }
1639dc70af8SWarner Losh bcount = size / SI(dev).bsize;
1649dc70af8SWarner Losh if (rsize)
1659dc70af8SWarner Losh *rsize = 0;
1669dc70af8SWarner Losh
1679dc70af8SWarner Losh err = stor_readdev(dev, blk + dev->d_offset, bcount, buf);
1689dc70af8SWarner Losh if (!err && rsize)
1699dc70af8SWarner Losh *rsize = size;
1709dc70af8SWarner Losh
1719dc70af8SWarner Losh return (err);
1729dc70af8SWarner Losh }
1739dc70af8SWarner Losh
1749dc70af8SWarner Losh static int
stor_open(struct open_file * f,...)1759dc70af8SWarner Losh stor_open(struct open_file *f, ...)
1769dc70af8SWarner Losh {
1779dc70af8SWarner Losh va_list ap;
1789dc70af8SWarner Losh struct disk_devdesc *dev;
1799dc70af8SWarner Losh
1809dc70af8SWarner Losh va_start(ap, f);
1819dc70af8SWarner Losh dev = va_arg(ap, struct disk_devdesc *);
1829dc70af8SWarner Losh va_end(ap);
1839dc70af8SWarner Losh
1849dc70af8SWarner Losh return (stor_opendev(dev));
1859dc70af8SWarner Losh }
1869dc70af8SWarner Losh
1879dc70af8SWarner Losh static int
stor_opendev(struct disk_devdesc * dev)1889dc70af8SWarner Losh stor_opendev(struct disk_devdesc *dev)
1899dc70af8SWarner Losh {
1909dc70af8SWarner Losh int err;
1919dc70af8SWarner Losh
1929dc70af8SWarner Losh if (dev->dd.d_unit < 0 || dev->dd.d_unit >= stor_info_no)
1939dc70af8SWarner Losh return (EIO);
1949dc70af8SWarner Losh
1959dc70af8SWarner Losh if (SI(dev).opened == 0) {
1969dc70af8SWarner Losh err = ub_dev_open(SI(dev).handle);
1979dc70af8SWarner Losh if (err != 0) {
1989dc70af8SWarner Losh stor_printf("device open failed with error=%d, "
1999dc70af8SWarner Losh "handle=%d\n", err, SI(dev).handle);
2009dc70af8SWarner Losh return (ENXIO);
2019dc70af8SWarner Losh }
2029dc70af8SWarner Losh SI(dev).opened++;
2039dc70af8SWarner Losh }
2049dc70af8SWarner Losh return (disk_open(dev, SI(dev).blocks * SI(dev).bsize,
2059dc70af8SWarner Losh SI(dev).bsize));
2069dc70af8SWarner Losh }
2079dc70af8SWarner Losh
2089dc70af8SWarner Losh static int
stor_close(struct open_file * f)2099dc70af8SWarner Losh stor_close(struct open_file *f)
2109dc70af8SWarner Losh {
2119dc70af8SWarner Losh struct disk_devdesc *dev;
2129dc70af8SWarner Losh
2139dc70af8SWarner Losh dev = (struct disk_devdesc *)(f->f_devdata);
2149dc70af8SWarner Losh return (disk_close(dev));
2159dc70af8SWarner Losh }
2169dc70af8SWarner Losh
2179dc70af8SWarner Losh static int
stor_readdev(struct disk_devdesc * dev,daddr_t blk,size_t size,char * buf)2189dc70af8SWarner Losh stor_readdev(struct disk_devdesc *dev, daddr_t blk, size_t size, char *buf)
2199dc70af8SWarner Losh {
2209dc70af8SWarner Losh lbasize_t real_size;
2219dc70af8SWarner Losh int err;
2229dc70af8SWarner Losh
2239dc70af8SWarner Losh debugf("reading blk=%d size=%d @ 0x%08x\n", (int)blk, size, (uint32_t)buf);
2249dc70af8SWarner Losh
2259dc70af8SWarner Losh err = ub_dev_read(SI(dev).handle, buf, size, blk, &real_size);
2269dc70af8SWarner Losh if (err != 0) {
2279dc70af8SWarner Losh stor_printf("read failed, error=%d\n", err);
2289dc70af8SWarner Losh return (EIO);
2299dc70af8SWarner Losh }
2309dc70af8SWarner Losh
2319dc70af8SWarner Losh if (real_size != size) {
2329dc70af8SWarner Losh stor_printf("real size != size\n");
2339dc70af8SWarner Losh err = EIO;
2349dc70af8SWarner Losh }
2359dc70af8SWarner Losh
2369dc70af8SWarner Losh return (err);
2379dc70af8SWarner Losh }
2389dc70af8SWarner Losh
2399dc70af8SWarner Losh static int
stor_print(int verbose)2409dc70af8SWarner Losh stor_print(int verbose)
2419dc70af8SWarner Losh {
2429dc70af8SWarner Losh struct disk_devdesc dev;
2439dc70af8SWarner Losh static char line[80];
2449dc70af8SWarner Losh int i, ret = 0;
2459dc70af8SWarner Losh
2469dc70af8SWarner Losh if (stor_info_no == 0)
2479dc70af8SWarner Losh return (ret);
2489dc70af8SWarner Losh
2499dc70af8SWarner Losh printf("%s devices:", uboot_storage.dv_name);
2509dc70af8SWarner Losh if ((ret = pager_output("\n")) != 0)
2519dc70af8SWarner Losh return (ret);
2529dc70af8SWarner Losh
2539dc70af8SWarner Losh for (i = 0; i < stor_info_no; i++) {
2549dc70af8SWarner Losh dev.dd.d_dev = &uboot_storage;
2559dc70af8SWarner Losh dev.dd.d_unit = i;
2569dc70af8SWarner Losh dev.d_slice = D_SLICENONE;
2579dc70af8SWarner Losh dev.d_partition = D_PARTNONE;
2589dc70af8SWarner Losh snprintf(line, sizeof(line), "\tdisk%d (%s)\n", i,
2599dc70af8SWarner Losh ub_stor_type(SI(&dev).type));
2609dc70af8SWarner Losh if ((ret = pager_output(line)) != 0)
2619dc70af8SWarner Losh break;
2629dc70af8SWarner Losh if (stor_opendev(&dev) == 0) {
2639dc70af8SWarner Losh sprintf(line, "\tdisk%d", i);
2649dc70af8SWarner Losh ret = disk_print(&dev, line, verbose);
2659dc70af8SWarner Losh disk_close(&dev);
2669dc70af8SWarner Losh if (ret != 0)
2679dc70af8SWarner Losh break;
2689dc70af8SWarner Losh }
2699dc70af8SWarner Losh }
2709dc70af8SWarner Losh return (ret);
2719dc70af8SWarner Losh }
2729dc70af8SWarner Losh
2739dc70af8SWarner Losh static int
stor_ioctl(struct open_file * f,u_long cmd,void * data)2749dc70af8SWarner Losh stor_ioctl(struct open_file *f, u_long cmd, void *data)
2759dc70af8SWarner Losh {
2769dc70af8SWarner Losh struct disk_devdesc *dev;
2779dc70af8SWarner Losh int rc;
2789dc70af8SWarner Losh
2799dc70af8SWarner Losh dev = (struct disk_devdesc *)f->f_devdata;
2809dc70af8SWarner Losh rc = disk_ioctl(dev, cmd, data);
2819dc70af8SWarner Losh if (rc != ENOTTY)
2829dc70af8SWarner Losh return (rc);
2839dc70af8SWarner Losh
2849dc70af8SWarner Losh switch (cmd) {
2859dc70af8SWarner Losh case DIOCGSECTORSIZE:
2869dc70af8SWarner Losh *(u_int *)data = SI(dev).bsize;
2879dc70af8SWarner Losh break;
2889dc70af8SWarner Losh case DIOCGMEDIASIZE:
2899dc70af8SWarner Losh *(uint64_t *)data = SI(dev).bsize * SI(dev).blocks;
2909dc70af8SWarner Losh break;
2919dc70af8SWarner Losh default:
2929dc70af8SWarner Losh return (ENOTTY);
2939dc70af8SWarner Losh }
2949dc70af8SWarner Losh return (0);
2959dc70af8SWarner Losh }
2969dc70af8SWarner Losh
2979dc70af8SWarner Losh
2989dc70af8SWarner Losh /*
2999dc70af8SWarner Losh * Return the device unit number for the given type and type-relative unit
3009dc70af8SWarner Losh * number.
3019dc70af8SWarner Losh */
3029dc70af8SWarner Losh int
uboot_diskgetunit(int type,int type_unit)3039dc70af8SWarner Losh uboot_diskgetunit(int type, int type_unit)
3049dc70af8SWarner Losh {
3059dc70af8SWarner Losh int local_type_unit;
3069dc70af8SWarner Losh int i;
3079dc70af8SWarner Losh
3089dc70af8SWarner Losh local_type_unit = 0;
3099dc70af8SWarner Losh for (i = 0; i < stor_info_no; i++) {
3109dc70af8SWarner Losh if ((stor_info[i].type & type) == type) {
3119dc70af8SWarner Losh if (local_type_unit == type_unit) {
3129dc70af8SWarner Losh return (i);
3139dc70af8SWarner Losh }
3149dc70af8SWarner Losh local_type_unit++;
3159dc70af8SWarner Losh }
3169dc70af8SWarner Losh }
3179dc70af8SWarner Losh
3189dc70af8SWarner Losh return (-1);
3199dc70af8SWarner Losh }
320