1 /*- 2 * Copyright (c) 2009 Marcel Moolenaar 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 <stand.h> 28 #include <sys/param.h> 29 #include <sys/endian.h> 30 #include <sys/queue.h> 31 #include <sys/stdarg.h> 32 33 #include "bootstrap.h" 34 35 #define MD_BLOCK_SIZE 512 36 #define MD_FLAG_MASK MD_FLAG_KERNEL 37 38 struct md_info { 39 STAILQ_ENTRY(md_info) md_link; 40 void *md_image; 41 size_t md_size; 42 unsigned int md_flags; 43 int md_unit; 44 }; 45 46 static STAILQ_HEAD(, md_info) md_list = STAILQ_HEAD_INITIALIZER(md_list); 47 static int md_next_unit; 48 49 static int md_init(void); 50 static int md_strategy(void *, int, daddr_t, size_t, char *, size_t *); 51 static int md_open(struct open_file *, ...); 52 static int md_close(struct open_file *); 53 static int md_print(int); 54 55 struct devsw md_dev = { 56 .dv_name = "md", 57 .dv_type = DEVT_DISK, 58 .dv_init = md_init, 59 .dv_strategy = md_strategy, 60 .dv_open = md_open, 61 .dv_close = md_close, 62 .dv_ioctl = noioctl, 63 .dv_print = md_print, 64 .dv_cleanup = nullsys, 65 }; 66 67 /* 68 * The kernel can accept a MD inline in the metadata, or out-of-line via 69 * hints, like when memory disks are passed to us in, for example, UEFI. 70 */ 71 void 72 md_export_to_kernel(uint64_t start, uint64_t len) 73 { 74 char key[32], value[32]; 75 static int unit = 0; /* Note: loader unit and kernel unit may differ */ 76 77 snprintf(key, sizeof(key), "hint.md.%d.physaddr", unit); 78 snprintf(value, sizeof(value), "0x%016jx", (uintmax_t)start); 79 setenv(key, value, 1); 80 snprintf(key, sizeof(key), "hint.md.%d.len", unit); 81 snprintf(value, sizeof(value), "%jd", (uintmax_t)len); 82 setenv(key, value, 1); 83 unit++; 84 } 85 86 static struct md_info * 87 md_get_info(int unit) 88 { 89 struct md_info *md; 90 91 STAILQ_FOREACH(md, &md_list, md_link) { 92 if (md->md_unit == unit) 93 return (md); 94 } 95 return (NULL); 96 } 97 98 int 99 md_register(void *image, size_t size, unsigned int flags) 100 { 101 struct md_info *md; 102 103 if (image == NULL || size == 0 || size % MD_BLOCK_SIZE != 0 || 104 (flags & ~MD_FLAG_MASK) != 0) { 105 errno = EINVAL; 106 return (-1); 107 } 108 md = malloc(sizeof(*md)); 109 if (md == NULL) { 110 errno = ENOMEM; 111 return (-1); 112 } 113 md->md_image = image; 114 md->md_size = size; 115 md->md_flags = flags; 116 md->md_unit = md_next_unit++; 117 STAILQ_INSERT_TAIL(&md_list, md, md_link); 118 119 if ((flags & MD_FLAG_KERNEL) != 0) 120 md_export_to_kernel((uintptr_t)image, size); 121 return (md->md_unit); 122 } 123 124 #ifdef MD_IMAGE_SIZE 125 126 #if (MD_IMAGE_SIZE == 0 || MD_IMAGE_SIZE % MD_BLOCK_SIZE) 127 #error Image size must be a multiple of 512. 128 #endif 129 130 /* 131 * Preloaded image gets put here. 132 * Applications that patch the object with the image can determine 133 * the size looking at the start and end markers (strings), 134 * so we want them contiguous. 135 */ 136 static struct { 137 u_char start[MD_IMAGE_SIZE]; 138 u_char end[128]; 139 } md_image = { 140 .start = "MFS Filesystem goes here", 141 .end = "MFS Filesystem had better STOP here", 142 }; 143 #endif 144 145 static int 146 md_init(void) 147 { 148 149 #ifdef MD_IMAGE_SIZE 150 if (md_register(md_image.start, MD_IMAGE_SIZE, 0) < 0) 151 return (errno); 152 #endif 153 return (0); 154 } 155 156 static int 157 md_strategy(void *devdata, int rw, daddr_t blk, size_t size, 158 char *buf, size_t *rsize) 159 { 160 struct devdesc *dev = (struct devdesc *)devdata; 161 struct md_info *md; 162 size_t ofs; 163 164 md = md_get_info(dev->d_unit); 165 if (md == NULL) 166 return (ENXIO); 167 168 if (blk < 0 || (uintmax_t)blk >= md->md_size / MD_BLOCK_SIZE) 169 return (EIO); 170 171 if (size % MD_BLOCK_SIZE) 172 return (EIO); 173 174 ofs = blk * MD_BLOCK_SIZE; 175 if (size > md->md_size - ofs) 176 size = md->md_size - ofs; 177 178 if (rsize != NULL) 179 *rsize = size; 180 181 switch (rw & F_MASK) { 182 case F_READ: 183 bcopy((char *)md->md_image + ofs, buf, size); 184 return (0); 185 case F_WRITE: 186 bcopy(buf, (char *)md->md_image + ofs, size); 187 return (0); 188 } 189 190 return (ENODEV); 191 } 192 193 static int 194 md_open(struct open_file *f, ...) 195 { 196 va_list ap; 197 struct devdesc *dev; 198 199 va_start(ap, f); 200 dev = va_arg(ap, struct devdesc *); 201 va_end(ap); 202 203 if (md_get_info(dev->d_unit) == NULL) 204 return (ENXIO); 205 206 return (0); 207 } 208 209 static int 210 md_close(struct open_file *f) 211 { 212 struct devdesc *dev; 213 214 dev = (struct devdesc *)(f->f_devdata); 215 return (md_get_info(dev->d_unit) == NULL ? ENXIO : 0); 216 } 217 218 static int 219 md_print(int verbose) 220 { 221 struct md_info *md; 222 int ret; 223 224 if (STAILQ_EMPTY(&md_list)) 225 return (0); 226 printf("%s devices:", md_dev.dv_name); 227 if ((ret = pager_output("\n")) != 0) 228 return (ret); 229 230 STAILQ_FOREACH(md, &md_list, md_link) { 231 printf(" %s%d: %ju X %u blocks", md_dev.dv_name, 232 md->md_unit, (uintmax_t)(md->md_size / MD_BLOCK_SIZE), 233 MD_BLOCK_SIZE); 234 if ((ret = pager_output("\n")) != 0) 235 return (ret); 236 } 237 return (0); 238 } 239