Path: blob/main/sys/dev/bhnd/nvram/bhnd_nvram_iobuf.c
39536 views
/*-1* Copyright (c) 2016 Landon Fuller <[email protected]>2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer,9* without modification.10* 2. Redistributions in binary form must reproduce at minimum a disclaimer11* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any12* redistribution must be conditioned upon including a substantially13* similar Disclaimer requirement for further binary redistribution.14*15* NO WARRANTY16* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS17* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT18* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY19* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL20* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,21* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF22* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS23* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER24* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)25* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF26* THE POSSIBILITY OF SUCH DAMAGES.27*/2829#include <sys/cdefs.h>30#ifdef _KERNEL31#include <sys/param.h>32#include <sys/malloc.h>33#include <sys/systm.h>34#else /* !_KERNEL */35#include <errno.h>36#include <stdint.h>37#include <stdlib.h>38#include <string.h>39#endif /* _KERNEL */4041#include "bhnd_nvram_private.h"4243#include "bhnd_nvram_io.h"44#include "bhnd_nvram_iovar.h"4546/**47* Buffer-backed NVRAM I/O context.48*49* iobuf instances are gauranteed to provide persistent references to its50* backing contigious buffer via bhnd_nvram_io_read_ptr() and51* bhnd_nvram_io_write_ptr().52*/53struct bhnd_nvram_iobuf {54struct bhnd_nvram_io io; /**< common I/O instance state */55void *buf; /**< backing buffer. if inline-allocated, will56be a reference to data[]. */57size_t size; /**< size of @p buf */58size_t capacity; /**< capacity of @p buf */59uint8_t data[]; /**< inline buffer allocation */60};6162BHND_NVRAM_IOPS_DEFN(iobuf)6364/**65* Allocate and return a new I/O context with an uninitialized66* buffer of @p size and @p capacity.67*68* The caller is responsible for deallocating the returned I/O context via69* bhnd_nvram_io_free().70*71* If @p capacity is less than @p size, a capacity of @p size will be used.72*73* @param size The initial size of the I/O context.74* @param capacity The total capacity of the I/O context buffer;75* the returned I/O context may be resized up to76* @p capacity via bhnd_nvram_io_setsize().77*78* @retval bhnd_nvram_iobuf success.79* @retval NULL allocation failed.80* @retval NULL the requested @p capacity is less than81* @p size.82*/83struct bhnd_nvram_io *84bhnd_nvram_iobuf_empty(size_t size, size_t capacity)85{86struct bhnd_nvram_iobuf *iobuf;87size_t iosz;88bool inline_alloc;8990/* Sanity check the capacity */91if (size > capacity)92return (NULL);9394/* Would sizeof(iobuf)+capacity overflow? */95if (SIZE_MAX - sizeof(*iobuf) < capacity) {96inline_alloc = false;97iosz = sizeof(*iobuf);98} else {99inline_alloc = true;100iosz = sizeof(*iobuf) + capacity;101}102103/* Allocate I/O context */104iobuf = bhnd_nv_malloc(iosz);105if (iobuf == NULL)106return (NULL);107108iobuf->io.iops = &bhnd_nvram_iobuf_ops;109iobuf->buf = NULL;110iobuf->size = size;111iobuf->capacity = capacity;112113/* Either allocate our backing buffer, or initialize the114* backing buffer with a reference to our inline allocation. */115if (inline_alloc)116iobuf->buf = &iobuf->data;117else118iobuf->buf = bhnd_nv_malloc(iobuf->capacity);119120if (iobuf->buf == NULL) {121bhnd_nv_free(iobuf);122return (NULL);123}124125return (&iobuf->io);126}127128/**129* Allocate and return a new I/O context, copying @p size from @p buffer.130*131* The caller is responsible for deallocating the returned I/O context via132* bhnd_nvram_io_free().133*134* @param buffer The buffer data be copied by the returned I/O context.135* @param size The size of @p buffer, in bytes.136*137* @retval bhnd_nvram_io success.138* @retval NULL allocation failed.139*/140struct bhnd_nvram_io *141bhnd_nvram_iobuf_new(const void *buffer, size_t size)142{143struct bhnd_nvram_io *io;144struct bhnd_nvram_iobuf *iobuf;145146/* Allocate the iobuf */147if ((io = bhnd_nvram_iobuf_empty(size, size)) == NULL)148return (NULL);149150/* Copy the input to our new iobuf instance */151iobuf = (struct bhnd_nvram_iobuf *)io;152memcpy(iobuf->buf, buffer, iobuf->size);153154return (io);155}156157/**158* Allocate and return a new I/O context providing an in-memory copy159* of the data mapped by @p src.160*161* The caller is responsible for deallocating the returned I/O context via162* bhnd_nvram_io_free().163*164* @param src The I/O context to be copied.165*166* @retval bhnd_nvram_io success.167* @retval NULL allocation failed.168* @retval NULL copying @p src failed.169*/170struct bhnd_nvram_io *171bhnd_nvram_iobuf_copy(struct bhnd_nvram_io *src)172{173return (bhnd_nvram_iobuf_copy_range(src, 0x0,174bhnd_nvram_io_getsize(src)));175}176177/**178* Allocate and return a new I/O context providing an in-memory copy179* of @p size bytes mapped at @p offset by @p src.180*181* The caller is responsible for deallocating the returned I/O context via182* bhnd_nvram_io_free().183*184* @param src The I/O context to be copied.185* @param offset The offset of the bytes to be copied from @p src.186* @param size The number of bytes to copy at @p offset from @p src.187*188* @retval bhnd_nvram_io success.189* @retval NULL allocation failed.190* @retval NULL copying @p src failed.191*/192struct bhnd_nvram_io *193bhnd_nvram_iobuf_copy_range(struct bhnd_nvram_io *src, size_t offset,194size_t size)195{196struct bhnd_nvram_io *io;197struct bhnd_nvram_iobuf *iobuf;198int error;199200/* Check if offset+size would overflow */201if (SIZE_MAX - size < offset)202return (NULL);203204/* Allocate the iobuf instance */205if ((io = bhnd_nvram_iobuf_empty(size, size)) == NULL)206return (NULL);207208/* Copy the input I/O context */209iobuf = (struct bhnd_nvram_iobuf *)io;210if ((error = bhnd_nvram_io_read(src, offset, iobuf->buf, size))) {211bhnd_nvram_io_free(&iobuf->io);212return (NULL);213}214215return (io);216}217218static void219bhnd_nvram_iobuf_free(struct bhnd_nvram_io *io)220{221struct bhnd_nvram_iobuf *iobuf = (struct bhnd_nvram_iobuf *)io;222223/* Free the backing buffer if it wasn't allocated inline */224if (iobuf->buf != &iobuf->data)225bhnd_nv_free(iobuf->buf);226227bhnd_nv_free(iobuf);228}229230static size_t231bhnd_nvram_iobuf_getsize(struct bhnd_nvram_io *io)232{233struct bhnd_nvram_iobuf *iobuf = (struct bhnd_nvram_iobuf *)io;234return (iobuf->size);235}236237static int238bhnd_nvram_iobuf_setsize(struct bhnd_nvram_io *io, size_t size)239{240struct bhnd_nvram_iobuf *iobuf = (struct bhnd_nvram_iobuf *)io;241242/* Can't exceed the actual capacity */243if (size > iobuf->capacity)244return (ENXIO);245246iobuf->size = size;247return (0);248}249250/* Common iobuf_(read|write)_ptr implementation */251static int252bhnd_nvram_iobuf_ptr(struct bhnd_nvram_iobuf *iobuf, size_t offset, void **ptr,253size_t nbytes, size_t *navail)254{255size_t avail;256257/* Verify offset+nbytes fall within the buffer range */258if (offset > iobuf->size)259return (ENXIO);260261avail = iobuf->size - offset;262if (avail < nbytes)263return (ENXIO);264265/* Valid I/O range, provide a pointer to the buffer and the266* total count of available bytes */267*ptr = ((uint8_t *)iobuf->buf) + offset;268if (navail != NULL)269*navail = avail;270271return (0);272}273274static int275bhnd_nvram_iobuf_read_ptr(struct bhnd_nvram_io *io, size_t offset,276const void **ptr, size_t nbytes, size_t *navail)277{278struct bhnd_nvram_iobuf *iobuf;279void *ioptr;280int error;281282iobuf = (struct bhnd_nvram_iobuf *) io;283284/* Return a pointer into our backing buffer */285error = bhnd_nvram_iobuf_ptr(iobuf, offset, &ioptr, nbytes, navail);286if (error)287return (error);288289*ptr = ioptr;290291return (0);292}293294static int295bhnd_nvram_iobuf_write_ptr(struct bhnd_nvram_io *io, size_t offset,296void **ptr, size_t nbytes, size_t *navail)297{298struct bhnd_nvram_iobuf *iobuf;299300iobuf = (struct bhnd_nvram_iobuf *) io;301302/* Return a pointer into our backing buffer */303return (bhnd_nvram_iobuf_ptr(iobuf, offset, ptr, nbytes, navail));304}305306static int307bhnd_nvram_iobuf_read(struct bhnd_nvram_io *io, size_t offset, void *buffer,308size_t nbytes)309{310const void *ptr;311int error;312313/* Try to fetch a direct pointer for at least nbytes */314if ((error = bhnd_nvram_io_read_ptr(io, offset, &ptr, nbytes, NULL)))315return (error);316317/* Copy out the requested data */318memcpy(buffer, ptr, nbytes);319return (0);320}321322static int323bhnd_nvram_iobuf_write(struct bhnd_nvram_io *io, size_t offset,324void *buffer, size_t nbytes)325{326void *ptr;327int error;328329/* Try to fetch a direct pointer for at least nbytes */330if ((error = bhnd_nvram_io_write_ptr(io, offset, &ptr, nbytes, NULL)))331return (error);332333/* Copy in the provided data */334memcpy(ptr, buffer, nbytes);335return (0);336}337338339