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 #include <sys/param.h> 32 #include <sys/bus.h> 33 #include <sys/malloc.h> 34 #include <sys/rman.h> 35 36 #include <machine/bus.h> 37 38 #include <dev/bhnd/bhnd.h> 39 40 #include "bhnd_nvram_private.h" 41 42 #include "bhnd_nvram_io.h" 43 #include "bhnd_nvram_iovar.h" 44 45 /** 46 * BHND resource-backed NVRAM I/O context. 47 */ 48 struct bhnd_nvram_iores { 49 struct bhnd_nvram_io io; /**< common I/O instance state */ 50 struct bhnd_resource *res; /**< backing resource (borrowed ref) */ 51 size_t offset; /**< offset within res */ 52 size_t size; /**< size relative to the base offset */ 53 u_int bus_width; /**< data type byte width to be used 54 when performing bus operations 55 on res. (1, 2, or 4 bytes) */ 56 }; 57 58 BHND_NVRAM_IOPS_DEFN(iores); 59 60 /** 61 * Allocate and return a new I/O context backed by a borrowed reference to @p r. 62 * 63 * The caller is responsible for deallocating the returned I/O context via 64 * bhnd_nvram_io_free(). 65 * 66 * @param r The resource to be mapped by the returned I/O 67 * context. 68 * @param offset Offset 69 * @param bus_width The required I/O width (1, 2, or 4 bytes) to be 70 * used when reading from @p r. 71 * 72 * @retval bhnd_nvram_io success. 73 * @retval NULL if allocation fails, or an invalid argument 74 * is supplied. 75 */ 76 struct bhnd_nvram_io * 77 bhnd_nvram_iores_new(struct bhnd_resource *r, bus_size_t offset, 78 bus_size_t size, u_int bus_width) 79 { 80 struct bhnd_nvram_iores *iores; 81 rman_res_t r_start, r_size; 82 83 /* Verify the bus width */ 84 switch (bus_width) { 85 case 1: 86 case 2: 87 case 4: 88 /* valid */ 89 break; 90 default: 91 BHND_NV_LOG("invalid bus width %u\n", bus_width); 92 return (NULL); 93 } 94 95 /* offset/size must not exceed our internal size_t representation, 96 * or our bus_size_t usage (note that BUS_SPACE_MAXSIZE may be less 97 * than 2^(sizeof(bus_size_t) * 32). */ 98 if (size > SIZE_MAX || offset > SIZE_MAX) { 99 BHND_NV_LOG("offset %#jx+%#jx exceeds SIZE_MAX\n", 100 (uintmax_t)offset, (uintmax_t)offset); 101 return (NULL); 102 } 103 104 if (size > BUS_SPACE_MAXSIZE || offset > BUS_SPACE_MAXSIZE) 105 { 106 BHND_NV_LOG("offset %#jx+%#jx exceeds BUS_SPACE_MAXSIZE\n", 107 (uintmax_t)offset, (uintmax_t)offset); 108 return (NULL); 109 } 110 111 /* offset/size fall within the resource's mapped range */ 112 r_size = rman_get_size(r->res); 113 r_start = rman_get_start(r->res); 114 if (r_size < offset || r_size < size || r_size - size < offset) 115 return (NULL); 116 117 /* offset/size must be bus_width aligned */ 118 if ((r_start + offset) % bus_width != 0) { 119 BHND_NV_LOG("base address %#jx+%#jx not aligned to bus width " 120 "%u\n", (uintmax_t)r_start, (uintmax_t)offset, bus_width); 121 return (NULL); 122 } 123 124 if (size % bus_width != 0) { 125 BHND_NV_LOG("size %#jx not aligned to bus width %u\n", 126 (uintmax_t)size, bus_width); 127 return (NULL); 128 } 129 130 /* Allocate and return the I/O context */ 131 iores = malloc(sizeof(*iores), M_BHND_NVRAM, M_WAITOK); 132 iores->io.iops = &bhnd_nvram_iores_ops; 133 iores->res = r; 134 iores->offset = offset; 135 iores->size = size; 136 iores->bus_width = bus_width; 137 138 return (&iores->io); 139 } 140 141 static void 142 bhnd_nvram_iores_free(struct bhnd_nvram_io *io) 143 { 144 free(io, M_BHND_NVRAM); 145 } 146 147 static size_t 148 bhnd_nvram_iores_getsize(struct bhnd_nvram_io *io) 149 { 150 struct bhnd_nvram_iores *iores = (struct bhnd_nvram_iores *)io; 151 return (iores->size); 152 } 153 154 static int 155 bhnd_nvram_iores_setsize(struct bhnd_nvram_io *io, size_t size) 156 { 157 /* unsupported */ 158 return (ENODEV); 159 } 160 161 static int 162 bhnd_nvram_iores_read_ptr(struct bhnd_nvram_io *io, size_t offset, 163 const void **ptr, size_t nbytes, size_t *navail) 164 { 165 /* unsupported */ 166 return (ENODEV); 167 } 168 169 static int 170 bhnd_nvram_iores_write_ptr(struct bhnd_nvram_io *io, size_t offset, 171 void **ptr, size_t nbytes, size_t *navail) 172 { 173 /* unsupported */ 174 return (ENODEV); 175 } 176 177 /** 178 * Validate @p offset and @p nbytes: 179 * 180 * - Verify that @p offset is mapped by the backing resource. 181 * - If less than @p nbytes are available at @p offset, write the actual number 182 * of bytes available to @p nbytes. 183 * - Verify that @p offset + @p nbytes are correctly aligned. 184 */ 185 static int 186 bhnd_nvram_iores_validate_req(struct bhnd_nvram_iores *iores, size_t offset, 187 size_t *nbytes) 188 { 189 /* Verify offset falls within the resource range */ 190 if (offset > iores->size) 191 return (ENXIO); 192 193 /* Check for eof */ 194 if (offset == iores->size) { 195 *nbytes = 0; 196 return (0); 197 } 198 199 /* Verify offset alignment */ 200 if (offset % iores->bus_width != 0) 201 return (EFAULT); 202 203 /* Limit nbytes to available range and verify size alignment */ 204 *nbytes = ummin(*nbytes, iores->size - offset); 205 if (*nbytes < iores->bus_width && *nbytes % iores->bus_width != 0) 206 return (EFAULT); 207 208 return (0); 209 } 210 211 static int 212 bhnd_nvram_iores_read(struct bhnd_nvram_io *io, size_t offset, void *buffer, 213 size_t nbytes) 214 { 215 struct bhnd_nvram_iores *iores; 216 bus_size_t r_offset; 217 size_t navail; 218 int error; 219 220 iores = (struct bhnd_nvram_iores *)io; 221 222 /* Validate the request and determine the actual number of readable 223 * bytes */ 224 navail = nbytes; 225 if ((error = bhnd_nvram_iores_validate_req(iores, offset, &navail))) 226 return (error); 227 228 /* At least nbytes must be readable */ 229 if (navail < nbytes) 230 return (ENXIO); 231 232 /* Handle zero length read */ 233 if (nbytes == 0) 234 return (0); 235 236 /* Determine actual resource offset and perform the read */ 237 r_offset = iores->offset + offset; 238 switch (iores->bus_width) { 239 case 1: 240 bhnd_bus_read_region_stream_1(iores->res, r_offset, buffer, 241 nbytes); 242 break; 243 case 2: 244 bhnd_bus_read_region_stream_2(iores->res, r_offset, buffer, 245 nbytes / 2); 246 break; 247 case 4: 248 bhnd_bus_read_region_stream_4(iores->res, r_offset, buffer, 249 nbytes / 4); 250 break; 251 default: 252 panic("unreachable!"); 253 } 254 255 return (0); 256 } 257 258 static int 259 bhnd_nvram_iores_write(struct bhnd_nvram_io *io, size_t offset, 260 void *buffer, size_t nbytes) 261 { 262 struct bhnd_nvram_iores *iores; 263 size_t navail; 264 bus_size_t r_offset; 265 int error; 266 267 iores = (struct bhnd_nvram_iores *)io; 268 269 /* Validate the request and determine the actual number of writable 270 * bytes */ 271 navail = nbytes; 272 if ((error = bhnd_nvram_iores_validate_req(iores, offset, &navail))) 273 return (error); 274 275 /* At least nbytes must be writable */ 276 if (navail < nbytes) 277 return (ENXIO); 278 279 /* Determine actual resource offset and perform the write */ 280 r_offset = iores->offset + offset; 281 switch (iores->bus_width) { 282 case 1: 283 bhnd_bus_write_region_stream_1(iores->res, r_offset, buffer, 284 nbytes); 285 break; 286 case 2: 287 bhnd_bus_write_region_stream_2(iores->res, r_offset, buffer, 288 nbytes / 2); 289 break; 290 case 4: 291 bhnd_bus_write_region_stream_4(iores->res, r_offset, buffer, 292 nbytes / 4); 293 break; 294 default: 295 panic("unreachable!"); 296 } 297 298 return (0); 299 } 300