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