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 86 userdisk_maxunit = userboot_disk_maxunit; 87 if (userdisk_maxunit > 0) { 88 ud_info = malloc(sizeof(*ud_info) * userdisk_maxunit); 89 if (ud_info == NULL) 90 return (ENOMEM); 91 for (i = 0; i < userdisk_maxunit; i++) { 92 if (CALLBACK(diskioctl, i, DIOCGSECTORSIZE, 93 §orsize) != 0 || CALLBACK(diskioctl, i, 94 DIOCGMEDIASIZE, &mediasize) != 0) 95 return (ENXIO); 96 ud_info[i].mediasize = mediasize; 97 ud_info[i].sectorsize = sectorsize; 98 ud_info[i].ud_open = 0; 99 ud_info[i].ud_bcache = NULL; 100 } 101 } 102 bcache_add_dev(userdisk_maxunit); 103 return(0); 104 } 105 106 static void 107 userdisk_cleanup(void) 108 { 109 110 if (userdisk_maxunit > 0) 111 free(ud_info); 112 } 113 114 /* 115 * Print information about disks 116 */ 117 static int 118 userdisk_print(int verbose) 119 { 120 struct disk_devdesc dev; 121 char line[80]; 122 int i, ret = 0; 123 124 if (userdisk_maxunit == 0) 125 return (0); 126 127 printf("%s devices:", userboot_disk.dv_name); 128 if ((ret = pager_output("\n")) != 0) 129 return (ret); 130 131 for (i = 0; i < userdisk_maxunit; i++) { 132 snprintf(line, sizeof(line), 133 " disk%d: Guest drive image\n", i); 134 ret = pager_output(line); 135 if (ret != 0) 136 break; 137 dev.dd.d_dev = &userboot_disk; 138 dev.dd.d_unit = i; 139 dev.d_slice = D_SLICENONE; 140 dev.d_partition = D_PARTNONE; 141 if (disk_open(&dev, ud_info[i].mediasize, 142 ud_info[i].sectorsize) == 0) { 143 snprintf(line, sizeof(line), " disk%d", i); 144 ret = disk_print(&dev, line, verbose); 145 disk_close(&dev); 146 if (ret != 0) 147 break; 148 } 149 } 150 return (ret); 151 } 152 153 /* 154 * Attempt to open the disk described by (dev) for use by (f). 155 */ 156 static int 157 userdisk_open(struct open_file *f, ...) 158 { 159 va_list ap; 160 struct disk_devdesc *dev; 161 162 va_start(ap, f); 163 dev = va_arg(ap, struct disk_devdesc *); 164 va_end(ap); 165 166 if (dev->dd.d_unit < 0 || dev->dd.d_unit >= userdisk_maxunit) 167 return (EIO); 168 ud_info[dev->dd.d_unit].ud_open++; 169 if (ud_info[dev->dd.d_unit].ud_bcache == NULL) 170 ud_info[dev->dd.d_unit].ud_bcache = bcache_allocate(); 171 return (disk_open(dev, ud_info[dev->dd.d_unit].mediasize, 172 ud_info[dev->dd.d_unit].sectorsize)); 173 } 174 175 static int 176 userdisk_close(struct open_file *f) 177 { 178 struct disk_devdesc *dev; 179 180 dev = (struct disk_devdesc *)f->f_devdata; 181 ud_info[dev->dd.d_unit].ud_open--; 182 if (ud_info[dev->dd.d_unit].ud_open == 0) { 183 bcache_free(ud_info[dev->dd.d_unit].ud_bcache); 184 ud_info[dev->dd.d_unit].ud_bcache = NULL; 185 } 186 return (disk_close(dev)); 187 } 188 189 static int 190 userdisk_strategy(void *devdata, int rw, daddr_t dblk, size_t size, 191 char *buf, size_t *rsize) 192 { 193 struct bcache_devdata bcd; 194 struct disk_devdesc *dev; 195 196 dev = (struct disk_devdesc *)devdata; 197 bcd.dv_strategy = userdisk_realstrategy; 198 bcd.dv_devdata = devdata; 199 bcd.dv_cache = ud_info[dev->dd.d_unit].ud_bcache; 200 return (bcache_strategy(&bcd, rw, dblk + dev->d_offset, 201 size, buf, rsize)); 202 } 203 204 static int 205 userdisk_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, 206 char *buf, size_t *rsize) 207 { 208 struct disk_devdesc *dev = devdata; 209 uint64_t off; 210 size_t resid; 211 int rc; 212 213 if (rsize) 214 *rsize = 0; 215 off = dblk * ud_info[dev->dd.d_unit].sectorsize; 216 switch (rw & F_MASK) { 217 case F_READ: 218 rc = CALLBACK(diskread, dev->dd.d_unit, off, buf, size, &resid); 219 break; 220 case F_WRITE: 221 rc = CALLBACK(diskwrite, dev->dd.d_unit, off, buf, size, 222 &resid); 223 break; 224 default: 225 rc = EINVAL; 226 break; 227 } 228 if (rc) 229 return (rc); 230 if (rsize) 231 *rsize = size - resid; 232 return (0); 233 } 234 235 static int 236 userdisk_ioctl(struct open_file *f, u_long cmd, void *data) 237 { 238 struct disk_devdesc *dev; 239 int rc; 240 241 dev = (struct disk_devdesc *)f->f_devdata; 242 rc = disk_ioctl(dev, cmd, data); 243 if (rc != ENOTTY) 244 return (rc); 245 246 return (CALLBACK(diskioctl, dev->dd.d_unit, cmd, data)); 247 } 248