]> CyberLeo.Net >> Repos - FreeBSD/releng/10.1.git/blob - sys/dev/ofw/ofw_bus_subr.c
Fix OpenSSL SSLv2 ciphersuite downgrade vulnerability.
[FreeBSD/releng/10.1.git] / sys / dev / ofw / ofw_bus_subr.c
1 /*-
2  * Copyright (c) 2001 - 2003 by Thomas Moestl <tmm@FreeBSD.org>.
3  * Copyright (c) 2005 Marius Strobl <marius@FreeBSD.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions, and the following disclaimer,
11  *    without modification, immediately at the beginning of the file.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in
14  *    the documentation and/or other materials provided with the
15  *    distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
21  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #include "opt_platform.h"
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/bus.h>
37 #include <sys/errno.h>
38 #include <sys/libkern.h>
39
40 #include <dev/ofw/ofw_bus.h>
41 #include <dev/ofw/ofw_bus_subr.h>
42 #include <dev/ofw/openfirm.h>
43
44 #include "ofw_bus_if.h"
45
46 int
47 ofw_bus_gen_setup_devinfo(struct ofw_bus_devinfo *obd, phandle_t node)
48 {
49
50         if (obd == NULL)
51                 return (ENOMEM);
52         /* The 'name' property is considered mandatory. */
53         if ((OF_getprop_alloc(node, "name", 1, (void **)&obd->obd_name)) == -1)
54                 return (EINVAL);
55         OF_getprop_alloc(node, "compatible", 1, (void **)&obd->obd_compat);
56         OF_getprop_alloc(node, "device_type", 1, (void **)&obd->obd_type);
57         OF_getprop_alloc(node, "model", 1, (void **)&obd->obd_model);
58         OF_getprop_alloc(node, "status", 1, (void **)&obd->obd_status);
59         obd->obd_node = node;
60         return (0);
61 }
62
63 void
64 ofw_bus_gen_destroy_devinfo(struct ofw_bus_devinfo *obd)
65 {
66
67         if (obd == NULL)
68                 return;
69         if (obd->obd_compat != NULL)
70                 free(obd->obd_compat, M_OFWPROP);
71         if (obd->obd_model != NULL)
72                 free(obd->obd_model, M_OFWPROP);
73         if (obd->obd_name != NULL)
74                 free(obd->obd_name, M_OFWPROP);
75         if (obd->obd_type != NULL)
76                 free(obd->obd_type, M_OFWPROP);
77         if (obd->obd_status != NULL)
78                 free(obd->obd_status, M_OFWPROP);
79 }
80
81 int
82 ofw_bus_gen_child_pnpinfo_str(device_t cbdev, device_t child, char *buf,
83     size_t buflen)
84 {
85
86         if (ofw_bus_get_name(child) != NULL) {
87                 strlcat(buf, "name=", buflen);
88                 strlcat(buf, ofw_bus_get_name(child), buflen);
89         }
90
91         if (ofw_bus_get_compat(child) != NULL) {
92                 strlcat(buf, " compat=", buflen);
93                 strlcat(buf, ofw_bus_get_compat(child), buflen);
94         }
95         return (0);
96 };
97
98 const char *
99 ofw_bus_gen_get_compat(device_t bus, device_t dev)
100 {
101         const struct ofw_bus_devinfo *obd;
102
103         obd = OFW_BUS_GET_DEVINFO(bus, dev);
104         if (obd == NULL)
105                 return (NULL);
106         return (obd->obd_compat);
107 }
108
109 const char *
110 ofw_bus_gen_get_model(device_t bus, device_t dev)
111 {
112         const struct ofw_bus_devinfo *obd;
113
114         obd = OFW_BUS_GET_DEVINFO(bus, dev);
115         if (obd == NULL)
116                 return (NULL);
117         return (obd->obd_model);
118 }
119
120 const char *
121 ofw_bus_gen_get_name(device_t bus, device_t dev)
122 {
123         const struct ofw_bus_devinfo *obd;
124
125         obd = OFW_BUS_GET_DEVINFO(bus, dev);
126         if (obd == NULL)
127                 return (NULL);
128         return (obd->obd_name);
129 }
130
131 phandle_t
132 ofw_bus_gen_get_node(device_t bus, device_t dev)
133 {
134         const struct ofw_bus_devinfo *obd;
135
136         obd = OFW_BUS_GET_DEVINFO(bus, dev);
137         if (obd == NULL)
138                 return (0);
139         return (obd->obd_node);
140 }
141
142 const char *
143 ofw_bus_gen_get_type(device_t bus, device_t dev)
144 {
145         const struct ofw_bus_devinfo *obd;
146
147         obd = OFW_BUS_GET_DEVINFO(bus, dev);
148         if (obd == NULL)
149                 return (NULL);
150         return (obd->obd_type);
151 }
152
153 const char *
154 ofw_bus_get_status(device_t dev)
155 {
156         const struct ofw_bus_devinfo *obd;
157
158         obd = OFW_BUS_GET_DEVINFO(device_get_parent(dev), dev);
159         if (obd == NULL)
160                 return (NULL);
161
162         return (obd->obd_status);
163 }
164
165 int
166 ofw_bus_status_okay(device_t dev)
167 {
168         const char *status;
169
170         status = ofw_bus_get_status(dev);
171         if (status == NULL || strcmp(status, "okay") == 0)
172                 return (1);
173         
174         return (0);
175 }
176
177 int
178 ofw_bus_is_compatible(device_t dev, const char *onecompat)
179 {
180         phandle_t node;
181         const char *compat;
182         int len, onelen, l;
183
184         if ((compat = ofw_bus_get_compat(dev)) == NULL)
185                 return (0);
186
187         if ((node = ofw_bus_get_node(dev)) == -1)
188                 return (0);
189
190         /* Get total 'compatible' prop len */
191         if ((len = OF_getproplen(node, "compatible")) <= 0)
192                 return (0);
193
194         onelen = strlen(onecompat);
195
196         while (len > 0) {
197                 if (strlen(compat) == onelen &&
198                     strncasecmp(compat, onecompat, onelen) == 0)
199                         /* Found it. */
200                         return (1);
201
202                 /* Slide to the next sub-string. */
203                 l = strlen(compat) + 1;
204                 compat += l;
205                 len -= l;
206         }
207         return (0);
208 }
209
210 int
211 ofw_bus_is_compatible_strict(device_t dev, const char *compatible)
212 {
213         const char *compat;
214         size_t len;
215
216         if ((compat = ofw_bus_get_compat(dev)) == NULL)
217                 return (0);
218
219         len = strlen(compatible);
220         if (strlen(compat) == len &&
221             strncasecmp(compat, compatible, len) == 0)
222                 return (1);
223
224         return (0);
225 }
226
227 const struct ofw_compat_data *
228 ofw_bus_search_compatible(device_t dev, const struct ofw_compat_data *compat)
229 {
230
231         if (compat == NULL)
232                 return NULL;
233
234         for (; compat->ocd_str != NULL; ++compat) {
235                 if (ofw_bus_is_compatible(dev, compat->ocd_str))
236                         break;
237         }
238
239         return (compat);
240 }
241
242 int
243 ofw_bus_has_prop(device_t dev, const char *propname)
244 {
245         phandle_t node;
246
247         if ((node = ofw_bus_get_node(dev)) == -1)
248                 return (0);
249
250         return (OF_hasprop(node, propname));
251 }
252
253 void
254 ofw_bus_setup_iinfo(phandle_t node, struct ofw_bus_iinfo *ii, int intrsz)
255 {
256         pcell_t addrc;
257         int msksz;
258
259         if (OF_getencprop(node, "#address-cells", &addrc, sizeof(addrc)) == -1)
260                 addrc = 2;
261         ii->opi_addrc = addrc * sizeof(pcell_t);
262
263         ii->opi_imapsz = OF_getencprop_alloc(node, "interrupt-map", 1,
264             (void **)&ii->opi_imap);
265         if (ii->opi_imapsz > 0) {
266                 msksz = OF_getencprop_alloc(node, "interrupt-map-mask", 1,
267                     (void **)&ii->opi_imapmsk);
268                 /*
269                  * Failure to get the mask is ignored; a full mask is used
270                  * then.  We barf on bad mask sizes, however.
271                  */
272                 if (msksz != -1 && msksz != ii->opi_addrc + intrsz)
273                         panic("ofw_bus_setup_iinfo: bad interrupt-map-mask "
274                             "property!");
275         }
276 }
277
278 int
279 ofw_bus_lookup_imap(phandle_t node, struct ofw_bus_iinfo *ii, void *reg,
280     int regsz, void *pintr, int pintrsz, void *mintr, int mintrsz,
281     phandle_t *iparent)
282 {
283         uint8_t maskbuf[regsz + pintrsz];
284         int rv;
285
286         if (ii->opi_imapsz <= 0)
287                 return (0);
288         KASSERT(regsz >= ii->opi_addrc,
289             ("ofw_bus_lookup_imap: register size too small: %d < %d",
290                 regsz, ii->opi_addrc));
291         if (node != -1) {
292                 rv = OF_getencprop(node, "reg", reg, regsz);
293                 if (rv < regsz)
294                         panic("ofw_bus_lookup_imap: cannot get reg property");
295         }
296         return (ofw_bus_search_intrmap(pintr, pintrsz, reg, ii->opi_addrc,
297             ii->opi_imap, ii->opi_imapsz, ii->opi_imapmsk, maskbuf, mintr,
298             mintrsz, iparent));
299 }
300
301 /*
302  * Map an interrupt using the firmware reg, interrupt-map and
303  * interrupt-map-mask properties.
304  * The interrupt property to be mapped must be of size intrsz, and pointed to
305  * by intr.  The regs property of the node for which the mapping is done must
306  * be passed as regs. This property is an array of register specifications;
307  * the size of the address part of such a specification must be passed as
308  * physsz.  Only the first element of the property is used.
309  * imap and imapsz hold the interrupt mask and it's size.
310  * imapmsk is a pointer to the interrupt-map-mask property, which must have
311  * a size of physsz + intrsz; it may be NULL, in which case a full mask is
312  * assumed.
313  * maskbuf must point to a buffer of length physsz + intrsz.
314  * The interrupt is returned in result, which must point to a buffer of length
315  * rintrsz (which gives the expected size of the mapped interrupt).
316  * Returns number of cells in the interrupt if a mapping was found, 0 otherwise.
317  */
318 int
319 ofw_bus_search_intrmap(void *intr, int intrsz, void *regs, int physsz,
320     void *imap, int imapsz, void *imapmsk, void *maskbuf, void *result,
321     int rintrsz, phandle_t *iparent)
322 {
323         phandle_t parent;
324         uint8_t *ref = maskbuf;
325         uint8_t *uiintr = intr;
326         uint8_t *uiregs = regs;
327         uint8_t *uiimapmsk = imapmsk;
328         uint8_t *mptr;
329         pcell_t pintrsz;
330         int i, rsz, tsz;
331
332         rsz = -1;
333         if (imapmsk != NULL) {
334                 for (i = 0; i < physsz; i++)
335                         ref[i] = uiregs[i] & uiimapmsk[i];
336                 for (i = 0; i < intrsz; i++)
337                         ref[physsz + i] = uiintr[i] & uiimapmsk[physsz + i];
338         } else {
339                 bcopy(regs, ref, physsz);
340                 bcopy(intr, ref + physsz, intrsz);
341         }
342
343         mptr = imap;
344         i = imapsz;
345         while (i > 0) {
346                 bcopy(mptr + physsz + intrsz, &parent, sizeof(parent));
347                 if (OF_searchencprop(OF_xref_phandle(parent),
348                     "#interrupt-cells", &pintrsz, sizeof(pintrsz)) == -1)
349                         pintrsz = 1;    /* default */
350                 pintrsz *= sizeof(pcell_t);
351
352                 /* Compute the map stride size. */
353                 tsz = physsz + intrsz + sizeof(phandle_t) + pintrsz;
354                 KASSERT(i >= tsz, ("ofw_bus_search_intrmap: truncated map"));
355
356                 if (bcmp(ref, mptr, physsz + intrsz) == 0) {
357                         bcopy(mptr + physsz + intrsz + sizeof(parent),
358                             result, MIN(rintrsz, pintrsz));
359
360                         if (iparent != NULL)
361                                 *iparent = parent;
362                         return (pintrsz/sizeof(pcell_t));
363                 }
364                 mptr += tsz;
365                 i -= tsz;
366         }
367         return (0);
368 }
369