1 /*- 2 * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org> 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 * without modification. 11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 12 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 13 * redistribution must be conditioned upon including a substantially 14 * similar Disclaimer requirement for further binary redistribution. 15 * 16 * NO WARRANTY 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 20 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 21 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 22 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 * THE POSSIBILITY OF SUCH DAMAGES. 28 */ 29 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 33 #ifdef _KERNEL 34 #include <sys/param.h> 35 #include <sys/malloc.h> 36 #include <sys/systm.h> 37 #else /* !_KERNEL */ 38 #include <errno.h> 39 #include <stdint.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #endif /* _KERNEL */ 43 44 #include "bhnd_nvram_private.h" 45 46 #include "bhnd_nvram_io.h" 47 #include "bhnd_nvram_iovar.h" 48 49 /** 50 * Buffer-backed NVRAM I/O context. 51 * 52 * iobuf instances are gauranteed to provide persistent references to its 53 * backing contigious buffer via bhnd_nvram_io_read_ptr() and 54 * bhnd_nvram_io_write_ptr(). 55 */ 56 struct bhnd_nvram_iobuf { 57 struct bhnd_nvram_io io; /**< common I/O instance state */ 58 void *buf; /**< backing buffer. if inline-allocated, will 59 be a reference to data[]. */ 60 size_t size; /**< size of @p buf */ 61 size_t capacity; /**< capacity of @p buf */ 62 uint8_t data[]; /**< inline buffer allocation */ 63 }; 64 65 BHND_NVRAM_IOPS_DEFN(iobuf) 66 67 /** 68 * Allocate and return a new I/O context with an uninitialized 69 * buffer of @p size and @p capacity. 70 * 71 * The caller is responsible for deallocating the returned I/O context via 72 * bhnd_nvram_io_free(). 73 * 74 * If @p capacity is less than @p size, a capacity of @p size will be used. 75 * 76 * @param size The initial size of the I/O context. 77 * @param capacity The total capacity of the I/O context buffer; 78 * the returned I/O context may be resized up to 79 * @p capacity via bhnd_nvram_io_setsize(). 80 * 81 * @retval bhnd_nvram_iobuf success. 82 * @retval NULL allocation failed. 83 * @retval NULL the requested @p capacity is less than 84 * @p size. 85 */ 86 struct bhnd_nvram_io * 87 bhnd_nvram_iobuf_empty(size_t size, size_t capacity) 88 { 89 struct bhnd_nvram_iobuf *iobuf; 90 size_t iosz; 91 bool inline_alloc; 92 93 /* Sanity check the capacity */ 94 if (size > capacity) 95 return (NULL); 96 97 /* Would sizeof(iobuf)+capacity overflow? */ 98 if (SIZE_MAX - sizeof(*iobuf) < capacity) { 99 inline_alloc = false; 100 iosz = sizeof(*iobuf); 101 } else { 102 inline_alloc = true; 103 iosz = sizeof(*iobuf) + capacity; 104 } 105 106 /* Allocate I/O context */ 107 iobuf = bhnd_nv_malloc(iosz); 108 if (iobuf == NULL) 109 return (NULL); 110 111 iobuf->io.iops = &bhnd_nvram_iobuf_ops; 112 iobuf->buf = NULL; 113 iobuf->size = size; 114 iobuf->capacity = capacity; 115 116 /* Either allocate our backing buffer, or initialize the 117 * backing buffer with a reference to our inline allocation. */ 118 if (inline_alloc) 119 iobuf->buf = &iobuf->data; 120 else 121 iobuf->buf = bhnd_nv_malloc(iobuf->capacity); 122 123 124 if (iobuf->buf == NULL) { 125 bhnd_nv_free(iobuf); 126 return (NULL); 127 } 128 129 return (&iobuf->io); 130 } 131 132 /** 133 * Allocate and return a new I/O context, copying @p size from @p buffer. 134 * 135 * The caller is responsible for deallocating the returned I/O context via 136 * bhnd_nvram_io_free(). 137 * 138 * @param buffer The buffer data be copied by the returned I/O context. 139 * @param size The size of @p buffer, in bytes. 140 * 141 * @retval bhnd_nvram_io success. 142 * @retval NULL allocation failed. 143 */ 144 struct bhnd_nvram_io * 145 bhnd_nvram_iobuf_new(const void *buffer, size_t size) 146 { 147 struct bhnd_nvram_io *io; 148 struct bhnd_nvram_iobuf *iobuf; 149 150 /* Allocate the iobuf */ 151 if ((io = bhnd_nvram_iobuf_empty(size, size)) == NULL) 152 return (NULL); 153 154 /* Copy the input to our new iobuf instance */ 155 iobuf = (struct bhnd_nvram_iobuf *)io; 156 memcpy(iobuf->buf, buffer, iobuf->size); 157 158 return (io); 159 } 160 161 /** 162 * Allocate and return a new I/O context providing an in-memory copy 163 * of the data mapped by @p src. 164 * 165 * The caller is responsible for deallocating the returned I/O context via 166 * bhnd_nvram_io_free(). 167 * 168 * @param src The I/O context to be copied. 169 * 170 * @retval bhnd_nvram_io success. 171 * @retval NULL allocation failed. 172 * @retval NULL copying @p src failed. 173 */ 174 struct bhnd_nvram_io * 175 bhnd_nvram_iobuf_copy(struct bhnd_nvram_io *src) 176 { 177 return (bhnd_nvram_iobuf_copy_range(src, 0x0, 178 bhnd_nvram_io_getsize(src))); 179 } 180 181 /** 182 * Allocate and return a new I/O context providing an in-memory copy 183 * of @p size bytes mapped at @p offset by @p src. 184 * 185 * The caller is responsible for deallocating the returned I/O context via 186 * bhnd_nvram_io_free(). 187 * 188 * @param src The I/O context to be copied. 189 * @param offset The offset of the bytes to be copied from @p src. 190 * @param size The number of bytes to copy at @p offset from @p src. 191 * 192 * @retval bhnd_nvram_io success. 193 * @retval NULL allocation failed. 194 * @retval NULL copying @p src failed. 195 */ 196 struct bhnd_nvram_io * 197 bhnd_nvram_iobuf_copy_range(struct bhnd_nvram_io *src, size_t offset, 198 size_t size) 199 { 200 struct bhnd_nvram_io *io; 201 struct bhnd_nvram_iobuf *iobuf; 202 int error; 203 204 /* Check if offset+size would overflow */ 205 if (SIZE_MAX - size < offset) 206 return (NULL); 207 208 /* Allocate the iobuf instance */ 209 if ((io = bhnd_nvram_iobuf_empty(size, size)) == NULL) 210 return (NULL); 211 212 /* Copy the input I/O context */ 213 iobuf = (struct bhnd_nvram_iobuf *)io; 214 if ((error = bhnd_nvram_io_read(src, offset, iobuf->buf, size))) { 215 bhnd_nvram_io_free(&iobuf->io); 216 return (NULL); 217 } 218 219 return (io); 220 } 221 222 223 static void 224 bhnd_nvram_iobuf_free(struct bhnd_nvram_io *io) 225 { 226 struct bhnd_nvram_iobuf *iobuf = (struct bhnd_nvram_iobuf *)io; 227 228 /* Free the backing buffer if it wasn't allocated inline */ 229 if (iobuf->buf != &iobuf->data) 230 bhnd_nv_free(iobuf->buf); 231 232 bhnd_nv_free(iobuf); 233 } 234 235 static size_t 236 bhnd_nvram_iobuf_getsize(struct bhnd_nvram_io *io) 237 { 238 struct bhnd_nvram_iobuf *iobuf = (struct bhnd_nvram_iobuf *)io; 239 return (iobuf->size); 240 } 241 242 static int 243 bhnd_nvram_iobuf_setsize(struct bhnd_nvram_io *io, size_t size) 244 { 245 struct bhnd_nvram_iobuf *iobuf = (struct bhnd_nvram_iobuf *)io; 246 247 /* Can't exceed the actual capacity */ 248 if (size > iobuf->capacity) 249 return (ENXIO); 250 251 iobuf->size = size; 252 return (0); 253 } 254 255 /* Common iobuf_(read|write)_ptr implementation */ 256 static int 257 bhnd_nvram_iobuf_ptr(struct bhnd_nvram_iobuf *iobuf, size_t offset, void **ptr, 258 size_t nbytes, size_t *navail) 259 { 260 size_t avail; 261 262 /* Verify offset+nbytes fall within the buffer range */ 263 if (offset > iobuf->size) 264 return (ENXIO); 265 266 avail = iobuf->size - offset; 267 if (avail < nbytes) 268 return (ENXIO); 269 270 /* Valid I/O range, provide a pointer to the buffer and the 271 * total count of available bytes */ 272 *ptr = ((uint8_t *)iobuf->buf) + offset; 273 if (navail != NULL) 274 *navail = avail; 275 276 return (0); 277 } 278 279 static int 280 bhnd_nvram_iobuf_read_ptr(struct bhnd_nvram_io *io, size_t offset, 281 const void **ptr, size_t nbytes, size_t *navail) 282 { 283 struct bhnd_nvram_iobuf *iobuf; 284 void *ioptr; 285 int error; 286 287 iobuf = (struct bhnd_nvram_iobuf *) io; 288 289 /* Return a pointer into our backing buffer */ 290 error = bhnd_nvram_iobuf_ptr(iobuf, offset, &ioptr, nbytes, navail); 291 if (error) 292 return (error); 293 294 *ptr = ioptr; 295 296 return (0); 297 } 298 299 static int 300 bhnd_nvram_iobuf_write_ptr(struct bhnd_nvram_io *io, size_t offset, 301 void **ptr, size_t nbytes, size_t *navail) 302 { 303 struct bhnd_nvram_iobuf *iobuf; 304 305 iobuf = (struct bhnd_nvram_iobuf *) io; 306 307 /* Return a pointer into our backing buffer */ 308 return (bhnd_nvram_iobuf_ptr(iobuf, offset, ptr, nbytes, navail)); 309 } 310 311 static int 312 bhnd_nvram_iobuf_read(struct bhnd_nvram_io *io, size_t offset, void *buffer, 313 size_t nbytes) 314 { 315 const void *ptr; 316 int error; 317 318 /* Try to fetch a direct pointer for at least nbytes */ 319 if ((error = bhnd_nvram_io_read_ptr(io, offset, &ptr, nbytes, NULL))) 320 return (error); 321 322 /* Copy out the requested data */ 323 memcpy(buffer, ptr, nbytes); 324 return (0); 325 } 326 327 static int 328 bhnd_nvram_iobuf_write(struct bhnd_nvram_io *io, size_t offset, 329 void *buffer, size_t nbytes) 330 { 331 void *ptr; 332 int error; 333 334 /* Try to fetch a direct pointer for at least nbytes */ 335 if ((error = bhnd_nvram_io_write_ptr(io, offset, &ptr, nbytes, NULL))) 336 return (error); 337 338 /* Copy in the provided data */ 339 memcpy(ptr, buffer, nbytes); 340 return (0); 341 } 342