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 if (iobuf->buf == NULL) { 124 bhnd_nv_free(iobuf); 125 return (NULL); 126 } 127 128 return (&iobuf->io); 129 } 130 131 /** 132 * Allocate and return a new I/O context, copying @p size from @p buffer. 133 * 134 * The caller is responsible for deallocating the returned I/O context via 135 * bhnd_nvram_io_free(). 136 * 137 * @param buffer The buffer data be copied by the returned I/O context. 138 * @param size The size of @p buffer, in bytes. 139 * 140 * @retval bhnd_nvram_io success. 141 * @retval NULL allocation failed. 142 */ 143 struct bhnd_nvram_io * 144 bhnd_nvram_iobuf_new(const void *buffer, size_t size) 145 { 146 struct bhnd_nvram_io *io; 147 struct bhnd_nvram_iobuf *iobuf; 148 149 /* Allocate the iobuf */ 150 if ((io = bhnd_nvram_iobuf_empty(size, size)) == NULL) 151 return (NULL); 152 153 /* Copy the input to our new iobuf instance */ 154 iobuf = (struct bhnd_nvram_iobuf *)io; 155 memcpy(iobuf->buf, buffer, iobuf->size); 156 157 return (io); 158 } 159 160 /** 161 * Allocate and return a new I/O context providing an in-memory copy 162 * of the data mapped by @p src. 163 * 164 * The caller is responsible for deallocating the returned I/O context via 165 * bhnd_nvram_io_free(). 166 * 167 * @param src The I/O context to be copied. 168 * 169 * @retval bhnd_nvram_io success. 170 * @retval NULL allocation failed. 171 * @retval NULL copying @p src failed. 172 */ 173 struct bhnd_nvram_io * 174 bhnd_nvram_iobuf_copy(struct bhnd_nvram_io *src) 175 { 176 return (bhnd_nvram_iobuf_copy_range(src, 0x0, 177 bhnd_nvram_io_getsize(src))); 178 } 179 180 /** 181 * Allocate and return a new I/O context providing an in-memory copy 182 * of @p size bytes mapped at @p offset by @p src. 183 * 184 * The caller is responsible for deallocating the returned I/O context via 185 * bhnd_nvram_io_free(). 186 * 187 * @param src The I/O context to be copied. 188 * @param offset The offset of the bytes to be copied from @p src. 189 * @param size The number of bytes to copy at @p offset from @p src. 190 * 191 * @retval bhnd_nvram_io success. 192 * @retval NULL allocation failed. 193 * @retval NULL copying @p src failed. 194 */ 195 struct bhnd_nvram_io * 196 bhnd_nvram_iobuf_copy_range(struct bhnd_nvram_io *src, size_t offset, 197 size_t size) 198 { 199 struct bhnd_nvram_io *io; 200 struct bhnd_nvram_iobuf *iobuf; 201 int error; 202 203 /* Check if offset+size would overflow */ 204 if (SIZE_MAX - size < offset) 205 return (NULL); 206 207 /* Allocate the iobuf instance */ 208 if ((io = bhnd_nvram_iobuf_empty(size, size)) == NULL) 209 return (NULL); 210 211 /* Copy the input I/O context */ 212 iobuf = (struct bhnd_nvram_iobuf *)io; 213 if ((error = bhnd_nvram_io_read(src, offset, iobuf->buf, size))) { 214 bhnd_nvram_io_free(&iobuf->io); 215 return (NULL); 216 } 217 218 return (io); 219 } 220 221 static void 222 bhnd_nvram_iobuf_free(struct bhnd_nvram_io *io) 223 { 224 struct bhnd_nvram_iobuf *iobuf = (struct bhnd_nvram_iobuf *)io; 225 226 /* Free the backing buffer if it wasn't allocated inline */ 227 if (iobuf->buf != &iobuf->data) 228 bhnd_nv_free(iobuf->buf); 229 230 bhnd_nv_free(iobuf); 231 } 232 233 static size_t 234 bhnd_nvram_iobuf_getsize(struct bhnd_nvram_io *io) 235 { 236 struct bhnd_nvram_iobuf *iobuf = (struct bhnd_nvram_iobuf *)io; 237 return (iobuf->size); 238 } 239 240 static int 241 bhnd_nvram_iobuf_setsize(struct bhnd_nvram_io *io, size_t size) 242 { 243 struct bhnd_nvram_iobuf *iobuf = (struct bhnd_nvram_iobuf *)io; 244 245 /* Can't exceed the actual capacity */ 246 if (size > iobuf->capacity) 247 return (ENXIO); 248 249 iobuf->size = size; 250 return (0); 251 } 252 253 /* Common iobuf_(read|write)_ptr implementation */ 254 static int 255 bhnd_nvram_iobuf_ptr(struct bhnd_nvram_iobuf *iobuf, size_t offset, void **ptr, 256 size_t nbytes, size_t *navail) 257 { 258 size_t avail; 259 260 /* Verify offset+nbytes fall within the buffer range */ 261 if (offset > iobuf->size) 262 return (ENXIO); 263 264 avail = iobuf->size - offset; 265 if (avail < nbytes) 266 return (ENXIO); 267 268 /* Valid I/O range, provide a pointer to the buffer and the 269 * total count of available bytes */ 270 *ptr = ((uint8_t *)iobuf->buf) + offset; 271 if (navail != NULL) 272 *navail = avail; 273 274 return (0); 275 } 276 277 static int 278 bhnd_nvram_iobuf_read_ptr(struct bhnd_nvram_io *io, size_t offset, 279 const void **ptr, size_t nbytes, size_t *navail) 280 { 281 struct bhnd_nvram_iobuf *iobuf; 282 void *ioptr; 283 int error; 284 285 iobuf = (struct bhnd_nvram_iobuf *) io; 286 287 /* Return a pointer into our backing buffer */ 288 error = bhnd_nvram_iobuf_ptr(iobuf, offset, &ioptr, nbytes, navail); 289 if (error) 290 return (error); 291 292 *ptr = ioptr; 293 294 return (0); 295 } 296 297 static int 298 bhnd_nvram_iobuf_write_ptr(struct bhnd_nvram_io *io, size_t offset, 299 void **ptr, size_t nbytes, size_t *navail) 300 { 301 struct bhnd_nvram_iobuf *iobuf; 302 303 iobuf = (struct bhnd_nvram_iobuf *) io; 304 305 /* Return a pointer into our backing buffer */ 306 return (bhnd_nvram_iobuf_ptr(iobuf, offset, ptr, nbytes, navail)); 307 } 308 309 static int 310 bhnd_nvram_iobuf_read(struct bhnd_nvram_io *io, size_t offset, void *buffer, 311 size_t nbytes) 312 { 313 const void *ptr; 314 int error; 315 316 /* Try to fetch a direct pointer for at least nbytes */ 317 if ((error = bhnd_nvram_io_read_ptr(io, offset, &ptr, nbytes, NULL))) 318 return (error); 319 320 /* Copy out the requested data */ 321 memcpy(buffer, ptr, nbytes); 322 return (0); 323 } 324 325 static int 326 bhnd_nvram_iobuf_write(struct bhnd_nvram_io *io, size_t offset, 327 void *buffer, size_t nbytes) 328 { 329 void *ptr; 330 int error; 331 332 /* Try to fetch a direct pointer for at least nbytes */ 333 if ((error = bhnd_nvram_io_write_ptr(io, offset, &ptr, nbytes, NULL))) 334 return (error); 335 336 /* Copy in the provided data */ 337 memcpy(ptr, buffer, nbytes); 338 return (0); 339 } 340