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