xref: /freebsd/stand/userboot/userboot/userboot_disk.c (revision 8a7df6c58d549bd2eaa5cad30b2ef93c3eca3c18)
1 /*-
2  * Copyright (c) 2011 Google, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 /*
28  * Userboot disk image handling.
29  */
30 
31 #include <sys/disk.h>
32 #include <stand.h>
33 #include <stdarg.h>
34 #include <bootstrap.h>
35 
36 #include "disk.h"
37 #include "libuserboot.h"
38 
39 struct userdisk_info {
40 	uint64_t	mediasize;
41 	uint16_t	sectorsize;
42 	int		ud_open;	/* reference counter */
43 	void		*ud_bcache;	/* buffer cache data */
44 };
45 
46 int userboot_disk_maxunit = 0;
47 
48 static int userdisk_maxunit = 0;
49 static struct userdisk_info	*ud_info;
50 
51 static int	userdisk_init(void);
52 static void	userdisk_cleanup(void);
53 static int	userdisk_strategy(void *devdata, int flag, daddr_t dblk,
54 		    size_t size, char *buf, size_t *rsize);
55 static int	userdisk_realstrategy(void *devdata, int flag, daddr_t dblk,
56 		    size_t size, char *buf, size_t *rsize);
57 static int	userdisk_open(struct open_file *f, ...);
58 static int	userdisk_close(struct open_file *f);
59 static int	userdisk_ioctl(struct open_file *f, u_long cmd, void *data);
60 static int	userdisk_print(int verbose);
61 
62 struct devsw userboot_disk = {
63 	.dv_name = "disk",
64 	.dv_type = DEVT_DISK,
65 	.dv_init = userdisk_init,
66 	.dv_strategy = userdisk_strategy,
67 	.dv_open = userdisk_open,
68 	.dv_close = userdisk_close,
69 	.dv_ioctl = userdisk_ioctl,
70 	.dv_print = userdisk_print,
71 	.dv_cleanup = userdisk_cleanup,
72 	.dv_fmtdev = disk_fmtdev,
73 	.dv_parsedev = disk_parsedev,
74 };
75 
76 /*
77  * Initialize userdisk_info structure for each disk.
78  */
79 static int
80 userdisk_init(void)
81 {
82 	off_t mediasize;
83 	u_int sectorsize;
84 	int i;
85 	int userdisk_usable = 0;
86 
87 	userdisk_maxunit = userboot_disk_maxunit;
88 	if (userdisk_maxunit > 0) {
89 		ud_info = calloc(userdisk_maxunit, sizeof(*ud_info));
90 		if (ud_info == NULL) {
91 			userdisk_maxunit = 0;
92 			userboot_disk_maxunit = 0;
93 			return (ENOMEM);
94 		}
95 		for (i = 0; i < userdisk_maxunit; i++) {
96 			if (CALLBACK(diskioctl, i, DIOCGSECTORSIZE,
97 			    &sectorsize) == 0 && CALLBACK(diskioctl, i,
98 			    DIOCGMEDIASIZE, &mediasize) == 0 && mediasize > 0) {
99 				ud_info[i].mediasize = mediasize;
100 				ud_info[i].sectorsize = sectorsize;
101 				userdisk_usable++;
102 			}
103 		}
104 	}
105 	bcache_add_dev(userdisk_usable);
106 
107 	return (0);
108 }
109 
110 static void
111 userdisk_cleanup(void)
112 {
113 
114 	if (userdisk_maxunit > 0)
115 		free(ud_info);
116 }
117 
118 int
119 userboot_disk_firstunit(void)
120 {
121 	int i;
122 
123 	for (i = 0; i < userdisk_maxunit; i++) {
124 		if (ud_info[i].mediasize != 0)
125 			return (i);
126 	}
127 	return (-1);
128 }
129 
130 /*
131  * Print information about disks
132  */
133 static int
134 userdisk_print(int verbose)
135 {
136 	struct disk_devdesc dev;
137 	char line[80];
138 	int i, ret = 0;
139 
140 	if (userboot_disk_firstunit() < 0)
141 		return (0);
142 
143 	printf("%s devices:", userboot_disk.dv_name);
144 	if ((ret = pager_output("\n")) != 0)
145 		return (ret);
146 
147 	for (i = 0; i < userdisk_maxunit; i++) {
148 		if (ud_info[i].mediasize == 0)
149 			continue;
150 		snprintf(line, sizeof(line),
151 		    "    disk%d:   Guest drive image\n", i);
152 		ret = pager_output(line);
153 		if (ret != 0)
154 			break;
155 		dev.dd.d_dev = &userboot_disk;
156 		dev.dd.d_unit = i;
157 		dev.d_slice = D_SLICENONE;
158 		dev.d_partition = D_PARTNONE;
159 		if (disk_open(&dev, ud_info[i].mediasize,
160 		    ud_info[i].sectorsize) == 0) {
161 			snprintf(line, sizeof(line), "    disk%d", i);
162 			ret = disk_print(&dev, line, verbose);
163 			disk_close(&dev);
164 			if (ret != 0)
165 				break;
166 		}
167 	}
168 	return (ret);
169 }
170 
171 /*
172  * Attempt to open the disk described by (dev) for use by (f).
173  */
174 static int
175 userdisk_open(struct open_file *f, ...)
176 {
177 	va_list			ap;
178 	struct disk_devdesc	*dev;
179 
180 	va_start(ap, f);
181 	dev = va_arg(ap, struct disk_devdesc *);
182 	va_end(ap);
183 
184 	if (dev->dd.d_unit < 0 || dev->dd.d_unit >= userdisk_maxunit ||
185 	    ud_info[dev->dd.d_unit].mediasize == 0)
186 		return (EIO);
187 	ud_info[dev->dd.d_unit].ud_open++;
188 	if (ud_info[dev->dd.d_unit].ud_bcache == NULL)
189 		ud_info[dev->dd.d_unit].ud_bcache = bcache_allocate();
190 	return (disk_open(dev, ud_info[dev->dd.d_unit].mediasize,
191 	    ud_info[dev->dd.d_unit].sectorsize));
192 }
193 
194 static int
195 userdisk_close(struct open_file *f)
196 {
197 	struct disk_devdesc *dev;
198 
199 	dev = (struct disk_devdesc *)f->f_devdata;
200 	ud_info[dev->dd.d_unit].ud_open--;
201 	if (ud_info[dev->dd.d_unit].ud_open == 0) {
202 		bcache_free(ud_info[dev->dd.d_unit].ud_bcache);
203 		ud_info[dev->dd.d_unit].ud_bcache = NULL;
204 	}
205 	return (disk_close(dev));
206 }
207 
208 static int
209 userdisk_strategy(void *devdata, int rw, daddr_t dblk, size_t size,
210     char *buf, size_t *rsize)
211 {
212 	struct bcache_devdata bcd;
213 	struct disk_devdesc *dev;
214 
215 	dev = (struct disk_devdesc *)devdata;
216 	bcd.dv_strategy = userdisk_realstrategy;
217 	bcd.dv_devdata = devdata;
218 	bcd.dv_cache = ud_info[dev->dd.d_unit].ud_bcache;
219 	return (bcache_strategy(&bcd, rw, dblk + dev->d_offset,
220 	    size, buf, rsize));
221 }
222 
223 static int
224 userdisk_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size,
225     char *buf, size_t *rsize)
226 {
227 	struct disk_devdesc *dev = devdata;
228 	uint64_t	off;
229 	size_t		resid;
230 	int		rc;
231 
232 	if (rsize)
233 		*rsize = 0;
234 	off = dblk * ud_info[dev->dd.d_unit].sectorsize;
235 	switch (rw & F_MASK) {
236 	case F_READ:
237 		rc = CALLBACK(diskread, dev->dd.d_unit, off, buf, size, &resid);
238 		break;
239 	case F_WRITE:
240 		rc = CALLBACK(diskwrite, dev->dd.d_unit, off, buf, size,
241 		    &resid);
242 		break;
243 	default:
244 		rc = EINVAL;
245 		break;
246 	}
247 	if (rc)
248 		return (rc);
249 	if (rsize)
250 		*rsize = size - resid;
251 	return (0);
252 }
253 
254 static int
255 userdisk_ioctl(struct open_file *f, u_long cmd, void *data)
256 {
257 	struct disk_devdesc *dev;
258 	int rc;
259 
260 	dev = (struct disk_devdesc *)f->f_devdata;
261 	rc = disk_ioctl(dev, cmd, data);
262 	if (rc != ENOTTY)
263 		return (rc);
264 
265 	return (CALLBACK(diskioctl, dev->dd.d_unit, cmd, data));
266 }
267