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