]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/bhnd/nvram/bhnd_nvram_iores.c
MFV r353619: 9691 fat zap should prefetch when iterating
[FreeBSD/FreeBSD.git] / sys / dev / bhnd / nvram / bhnd_nvram_iores.c
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
214 static int
215 bhnd_nvram_iores_read(struct bhnd_nvram_io *io, size_t offset, void *buffer,
216     size_t nbytes)
217 {
218         struct bhnd_nvram_iores *iores;
219         bus_size_t               r_offset;
220         size_t                   navail;
221         int                      error;
222
223         iores = (struct bhnd_nvram_iores *)io;
224
225         /* Validate the request and determine the actual number of readable
226          * bytes */
227         navail = nbytes;
228         if ((error = bhnd_nvram_iores_validate_req(iores, offset, &navail)))
229                 return (error);
230
231         /* At least nbytes must be readable */
232         if (navail < nbytes)
233                 return (ENXIO);
234
235         /* Handle zero length read */
236         if (nbytes == 0)
237                 return (0);
238
239         /* Determine actual resource offset and perform the read */
240         r_offset = iores->offset + offset;
241         switch (iores->bus_width) {
242         case 1:
243                 bhnd_bus_read_region_stream_1(iores->res, r_offset, buffer,
244                     nbytes);
245                 break;
246         case 2:
247                 bhnd_bus_read_region_stream_2(iores->res, r_offset, buffer,
248                     nbytes / 2);
249                 break;
250         case 4:
251                 bhnd_bus_read_region_stream_4(iores->res, r_offset, buffer,
252                     nbytes / 4);
253                 break;
254         default:
255                 panic("unreachable!");
256         }
257
258         return (0);
259 }
260
261 static int
262 bhnd_nvram_iores_write(struct bhnd_nvram_io *io, size_t offset,
263     void *buffer, size_t nbytes)
264 {
265         struct bhnd_nvram_iores *iores;
266         size_t                   navail;
267         bus_size_t               r_offset;
268         int                      error;
269
270         iores = (struct bhnd_nvram_iores *)io;
271
272         /* Validate the request and determine the actual number of writable
273          * bytes */
274         navail = nbytes;
275         if ((error = bhnd_nvram_iores_validate_req(iores, offset, &navail)))
276                 return (error);
277
278         /* At least nbytes must be writable */
279         if (navail < nbytes)
280                 return (ENXIO);
281
282         /* Determine actual resource offset and perform the write */
283         r_offset = iores->offset + offset;
284         switch (iores->bus_width) {
285         case 1:
286                 bhnd_bus_write_region_stream_1(iores->res, r_offset, buffer,
287                     nbytes);
288                 break;
289         case 2:
290                 bhnd_bus_write_region_stream_2(iores->res, r_offset, buffer,
291                     nbytes / 2);
292                 break;
293         case 4:
294                 bhnd_bus_write_region_stream_4(iores->res, r_offset, buffer,
295                     nbytes / 4);
296                 break;
297         default:
298                 panic("unreachable!");
299         }
300
301         return (0);
302 }