xref: /freebsd/sys/dev/bhnd/nvram/bhnd_nvram_iores.c (revision b077aed33b7b6aefca7b17ddb250cf521f938613)
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 }
302