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 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 /* 31 * Userboot disk image handling. 32 */ 33 34 #include <sys/disk.h> 35 #include <stand.h> 36 #include <stdarg.h> 37 #include <bootstrap.h> 38 39 #include "disk.h" 40 #include "libuserboot.h" 41 42 struct userdisk_info { 43 uint64_t mediasize; 44 uint16_t sectorsize; 45 int ud_open; /* reference counter */ 46 void *ud_bcache; /* buffer cache data */ 47 }; 48 49 int userboot_disk_maxunit = 0; 50 51 static int userdisk_maxunit = 0; 52 static struct userdisk_info *ud_info; 53 54 static int userdisk_init(void); 55 static void userdisk_cleanup(void); 56 static int userdisk_strategy(void *devdata, int flag, daddr_t dblk, 57 size_t size, char *buf, size_t *rsize); 58 static int userdisk_realstrategy(void *devdata, int flag, daddr_t dblk, 59 size_t size, char *buf, size_t *rsize); 60 static int userdisk_open(struct open_file *f, ...); 61 static int userdisk_close(struct open_file *f); 62 static int userdisk_ioctl(struct open_file *f, u_long cmd, void *data); 63 static int userdisk_print(int verbose); 64 65 struct devsw userboot_disk = { 66 .dv_name = "disk", 67 .dv_type = DEVT_DISK, 68 .dv_init = userdisk_init, 69 .dv_strategy = userdisk_strategy, 70 .dv_open = userdisk_open, 71 .dv_close = userdisk_close, 72 .dv_ioctl = userdisk_ioctl, 73 .dv_print = userdisk_print, 74 .dv_cleanup = userdisk_cleanup, 75 .dv_fmtdev = disk_fmtdev, 76 .dv_parsedev = disk_parsedev, 77 }; 78 79 /* 80 * Initialize userdisk_info structure for each disk. 81 */ 82 static int 83 userdisk_init(void) 84 { 85 off_t mediasize; 86 u_int sectorsize; 87 int i; 88 89 userdisk_maxunit = userboot_disk_maxunit; 90 if (userdisk_maxunit > 0) { 91 ud_info = malloc(sizeof(*ud_info) * userdisk_maxunit); 92 if (ud_info == NULL) 93 return (ENOMEM); 94 for (i = 0; i < userdisk_maxunit; i++) { 95 if (CALLBACK(diskioctl, i, DIOCGSECTORSIZE, 96 §orsize) != 0 || CALLBACK(diskioctl, i, 97 DIOCGMEDIASIZE, &mediasize) != 0) 98 return (ENXIO); 99 ud_info[i].mediasize = mediasize; 100 ud_info[i].sectorsize = sectorsize; 101 ud_info[i].ud_open = 0; 102 ud_info[i].ud_bcache = NULL; 103 } 104 } 105 bcache_add_dev(userdisk_maxunit); 106 return(0); 107 } 108 109 static void 110 userdisk_cleanup(void) 111 { 112 113 if (userdisk_maxunit > 0) 114 free(ud_info); 115 } 116 117 /* 118 * Print information about disks 119 */ 120 static int 121 userdisk_print(int verbose) 122 { 123 struct disk_devdesc dev; 124 char line[80]; 125 int i, ret = 0; 126 127 if (userdisk_maxunit == 0) 128 return (0); 129 130 printf("%s devices:", userboot_disk.dv_name); 131 if ((ret = pager_output("\n")) != 0) 132 return (ret); 133 134 for (i = 0; i < userdisk_maxunit; i++) { 135 snprintf(line, sizeof(line), 136 " disk%d: Guest drive image\n", i); 137 ret = pager_output(line); 138 if (ret != 0) 139 break; 140 dev.dd.d_dev = &userboot_disk; 141 dev.dd.d_unit = i; 142 dev.d_slice = D_SLICENONE; 143 dev.d_partition = D_PARTNONE; 144 if (disk_open(&dev, ud_info[i].mediasize, 145 ud_info[i].sectorsize) == 0) { 146 snprintf(line, sizeof(line), " disk%d", i); 147 ret = disk_print(&dev, line, verbose); 148 disk_close(&dev); 149 if (ret != 0) 150 break; 151 } 152 } 153 return (ret); 154 } 155 156 /* 157 * Attempt to open the disk described by (dev) for use by (f). 158 */ 159 static int 160 userdisk_open(struct open_file *f, ...) 161 { 162 va_list ap; 163 struct disk_devdesc *dev; 164 165 va_start(ap, f); 166 dev = va_arg(ap, struct disk_devdesc *); 167 va_end(ap); 168 169 if (dev->dd.d_unit < 0 || dev->dd.d_unit >= userdisk_maxunit) 170 return (EIO); 171 ud_info[dev->dd.d_unit].ud_open++; 172 if (ud_info[dev->dd.d_unit].ud_bcache == NULL) 173 ud_info[dev->dd.d_unit].ud_bcache = bcache_allocate(); 174 return (disk_open(dev, ud_info[dev->dd.d_unit].mediasize, 175 ud_info[dev->dd.d_unit].sectorsize)); 176 } 177 178 static int 179 userdisk_close(struct open_file *f) 180 { 181 struct disk_devdesc *dev; 182 183 dev = (struct disk_devdesc *)f->f_devdata; 184 ud_info[dev->dd.d_unit].ud_open--; 185 if (ud_info[dev->dd.d_unit].ud_open == 0) { 186 bcache_free(ud_info[dev->dd.d_unit].ud_bcache); 187 ud_info[dev->dd.d_unit].ud_bcache = NULL; 188 } 189 return (disk_close(dev)); 190 } 191 192 static int 193 userdisk_strategy(void *devdata, int rw, daddr_t dblk, size_t size, 194 char *buf, size_t *rsize) 195 { 196 struct bcache_devdata bcd; 197 struct disk_devdesc *dev; 198 199 dev = (struct disk_devdesc *)devdata; 200 bcd.dv_strategy = userdisk_realstrategy; 201 bcd.dv_devdata = devdata; 202 bcd.dv_cache = ud_info[dev->dd.d_unit].ud_bcache; 203 return (bcache_strategy(&bcd, rw, dblk + dev->d_offset, 204 size, buf, rsize)); 205 } 206 207 static int 208 userdisk_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, 209 char *buf, size_t *rsize) 210 { 211 struct disk_devdesc *dev = devdata; 212 uint64_t off; 213 size_t resid; 214 int rc; 215 216 if (rsize) 217 *rsize = 0; 218 off = dblk * ud_info[dev->dd.d_unit].sectorsize; 219 switch (rw & F_MASK) { 220 case F_READ: 221 rc = CALLBACK(diskread, dev->dd.d_unit, off, buf, size, &resid); 222 break; 223 case F_WRITE: 224 rc = CALLBACK(diskwrite, dev->dd.d_unit, off, buf, size, 225 &resid); 226 break; 227 default: 228 rc = EINVAL; 229 break; 230 } 231 if (rc) 232 return (rc); 233 if (rsize) 234 *rsize = size - resid; 235 return (0); 236 } 237 238 static int 239 userdisk_ioctl(struct open_file *f, u_long cmd, void *data) 240 { 241 struct disk_devdesc *dev; 242 int rc; 243 244 dev = (struct disk_devdesc *)f->f_devdata; 245 rc = disk_ioctl(dev, cmd, data); 246 if (rc != ENOTTY) 247 return (rc); 248 249 return (CALLBACK(diskioctl, dev->dd.d_unit, cmd, data)); 250 } 251