xref: /freebsd-src/stand/uboot/uboot_disk.c (revision 7c43148a974877188a930e4078a164f83da8e652)
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