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