]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c
Upgrade to OpenPAM Tabebuia.
[FreeBSD/FreeBSD.git] / sys / dev / bhnd / nvram / bhnd_nvram_data_bcmraw.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 #ifdef _KERNEL
34
35 #include <sys/param.h>
36 #include <sys/ctype.h>
37 #include <sys/malloc.h>
38 #include <sys/systm.h>
39
40 #else /* !_KERNEL */
41
42 #include <ctype.h>
43 #include <stdint.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47
48 #endif /* _KERNEL */
49
50 #include "bhnd_nvram_private.h"
51
52 #include "bhnd_nvram_datavar.h"
53 #include "bhnd_nvram_data_bcmvar.h"
54
55 /*
56  * Broadcom-RAW NVRAM data class.
57  * 
58  * The Broadcom NVRAM NUL-delimited ASCII format is used by most
59  * Broadcom SoCs.
60  * 
61  * The NVRAM data is encoded as a stream of of NUL-terminated 'key=value'
62  * strings; the end of the stream is denoted by a single extra NUL character.
63  */
64
65 struct bhnd_nvram_bcmraw;
66
67 /** BCM-RAW NVRAM data class instance */
68 struct bhnd_nvram_bcmraw {
69         struct bhnd_nvram_data           nv;    /**< common instance state */
70         char                            *data;  /**< backing buffer */
71         size_t                           size;  /**< buffer size */
72         size_t                           count; /**< variable count */
73 };
74
75 BHND_NVRAM_DATA_CLASS_DEFN(bcmraw, "Broadcom (RAW)",
76     BHND_NVRAM_DATA_CAP_DEVPATHS, sizeof(struct bhnd_nvram_bcmraw))
77
78 static int
79 bhnd_nvram_bcmraw_probe(struct bhnd_nvram_io *io)
80 {
81         char     envp[16];
82         size_t   envp_len;
83         size_t   io_size;
84         int      error;
85
86         io_size = bhnd_nvram_io_getsize(io);
87
88         /*
89          * Fetch initial bytes
90          */
91         envp_len = bhnd_nv_ummin(sizeof(envp), io_size);
92         if ((error = bhnd_nvram_io_read(io, 0x0, envp, envp_len)))
93                 return (error);
94
95         /* An empty BCM-RAW buffer should still contain a single terminating
96          * NUL */
97         if (envp_len == 0)
98                 return (ENXIO);
99
100         if (envp_len == 1) {
101                 if (envp[0] != '\0')
102                         return (ENXIO);
103
104                 return (BHND_NVRAM_DATA_PROBE_MAYBE);
105         }
106
107         /* Must contain only printable ASCII characters delimited
108          * by NUL record delimiters */
109         for (size_t i = 0; i < envp_len; i++) {
110                 char c = envp[i];
111
112                 /* If we hit a newline, this is probably BCM-TXT */
113                 if (c == '\n')
114                         return (ENXIO);
115
116                 if (c == '\0' && !bhnd_nv_isprint(c))
117                         continue;
118         }
119
120         /* A valid BCM-RAW buffer should contain a terminating NUL for
121          * the last record, followed by a final empty record terminated by
122          * NUL */
123         envp_len = 2;
124         if (io_size < envp_len)
125                 return (ENXIO);
126
127         if ((error = bhnd_nvram_io_read(io, io_size-envp_len, envp, envp_len)))
128                 return (error);
129
130         if (envp[0] != '\0' || envp[1] != '\0')
131                 return (ENXIO);
132
133         return (BHND_NVRAM_DATA_PROBE_MAYBE + 1);
134 }
135
136 static int
137 bhnd_nvram_bcmraw_getvar_direct(struct bhnd_nvram_io *io, const char *name,
138     void *buf, size_t *len, bhnd_nvram_type type)
139 {
140         return (bhnd_nvram_bcm_getvar_direct_common(io, name, buf, len, type,
141             false));
142 }
143
144 static int
145 bhnd_nvram_bcmraw_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props,
146     bhnd_nvram_plist *options, void *outp, size_t *olen)
147 {
148         bhnd_nvram_prop *prop;
149         size_t           limit, nbytes;
150         int              error;
151
152         /* Determine output byte limit */
153         if (outp != NULL)
154                 limit = *olen;
155         else
156                 limit = 0;
157
158         nbytes = 0;
159
160         /* Write all properties */
161         prop = NULL;
162         while ((prop = bhnd_nvram_plist_next(props, prop)) != NULL) {
163                 const char      *name;
164                 char            *p;
165                 size_t           prop_limit;
166                 size_t           name_len, value_len;
167
168                 if (outp == NULL || limit < nbytes) {
169                         p = NULL;
170                         prop_limit = 0;
171                 } else {
172                         p = ((char *)outp) + nbytes;
173                         prop_limit = limit - nbytes;
174                 }
175
176                 /* Fetch and write name + '=' to output */
177                 name = bhnd_nvram_prop_name(prop);
178                 name_len = strlen(name) + 1;
179
180                 if (prop_limit > name_len) {
181                         memcpy(p, name, name_len - 1);
182                         p[name_len - 1] = '=';
183
184                         prop_limit -= name_len;
185                         p += name_len;
186                 } else {
187                         prop_limit = 0;
188                         p = NULL;
189                 }
190
191                 /* Advance byte count */
192                 if (SIZE_MAX - nbytes < name_len)
193                         return (EFTYPE); /* would overflow size_t */
194
195                 nbytes += name_len;
196
197                 /* Attempt to write NUL-terminated value to output */
198                 value_len = prop_limit;
199                 error = bhnd_nvram_prop_encode(prop, p, &value_len,
200                     BHND_NVRAM_TYPE_STRING);
201
202                 /* If encoding failed for any reason other than ENOMEM (which
203                  * we'll detect and report after encoding all properties),
204                  * return immediately */
205                 if (error && error != ENOMEM) {
206                         BHND_NV_LOG("error serializing %s to required type "
207                             "%s: %d\n", name,
208                             bhnd_nvram_type_name(BHND_NVRAM_TYPE_STRING),
209                             error);
210                         return (error);
211                 }
212
213                 /* Advance byte count */
214                 if (SIZE_MAX - nbytes < value_len)
215                         return (EFTYPE); /* would overflow size_t */
216
217                 nbytes += value_len;
218         }
219
220         /* Write terminating '\0' */
221         if (limit > nbytes)
222                 *((char *)outp + nbytes) = '\0';
223
224         if (nbytes == SIZE_MAX)
225                 return (EFTYPE); /* would overflow size_t */
226         else
227                 nbytes++;
228
229         /* Provide required length */
230         *olen = nbytes;
231         if (limit < *olen) {
232                 if (outp == NULL)
233                         return (0);
234
235                 return (ENOMEM);
236         }
237
238         return (0);
239 }
240
241 /**
242  * Initialize @p bcm with the provided NVRAM data mapped by @p src.
243  * 
244  * @param bcm A newly allocated data instance.
245  */
246 static int
247 bhnd_nvram_bcmraw_init(struct bhnd_nvram_bcmraw *bcm, struct bhnd_nvram_io *src)
248 {
249         size_t   io_size;
250         size_t   capacity, offset;
251         int      error;
252
253         /* Fetch the input image size */
254         io_size = bhnd_nvram_io_getsize(src);
255
256         /* Allocate a buffer large enough to hold the NVRAM image, and
257          * an extra EOF-signaling NUL (on the chance it's missing from the
258          * source data) */
259         if (io_size == SIZE_MAX)
260                 return (ENOMEM);
261
262         capacity = io_size + 1 /* room for extra NUL */;
263         bcm->size = io_size;
264         if ((bcm->data = bhnd_nv_malloc(capacity)) == NULL)
265                 return (ENOMEM);
266
267         /* Copy in the NVRAM image */
268         if ((error = bhnd_nvram_io_read(src, 0x0, bcm->data, io_size)))
269                 return (error);
270
271         /* Process the buffer */
272         bcm->count = 0;
273         for (offset = 0; offset < bcm->size; offset++) {
274                 char            *envp;
275                 const char      *name, *value;
276                 size_t           envp_len;
277                 size_t           name_len, value_len;
278
279                 /* Parse the key=value string */
280                 envp = (char *) (bcm->data + offset);
281                 envp_len = strnlen(envp, bcm->size - offset);
282                 error = bhnd_nvram_parse_env(envp, envp_len, '=', &name,
283                                              &name_len, &value, &value_len);
284                 if (error) {
285                         BHND_NV_LOG("error parsing envp at offset %#zx: %d\n",
286                             offset, error);
287                         return (error);
288                 }
289
290                 /* Insert a '\0' character, replacing the '=' delimiter and
291                  * allowing us to vend references directly to the variable
292                  * name */
293                 *(envp + name_len) = '\0';
294
295                 /* Add to variable count */
296                 bcm->count++;
297
298                 /* Seek past the value's terminating '\0' */
299                 offset += envp_len;
300                 if (offset == io_size) {
301                         BHND_NV_LOG("missing terminating NUL at offset %#zx\n",
302                             offset);
303                         return (EINVAL);
304                 }
305
306                 /* If we hit EOF without finding a terminating NUL
307                  * byte, we need to append it */
308                 if (++offset == bcm->size) {
309                         BHND_NV_ASSERT(offset < capacity,
310                             ("appending past end of buffer"));
311                         bcm->size++;
312                         *(bcm->data + offset) = '\0';
313                 }
314
315                 /* Check for explicit EOF (encoded as a single empty NUL
316                  * terminated string) */
317                 if (*(bcm->data + offset) == '\0')
318                         break;
319         }
320
321         /* Reclaim any unused space in he backing buffer */
322         if (offset < bcm->size) {
323                 bcm->data = bhnd_nv_reallocf(bcm->data, bcm->size);
324                 if (bcm->data == NULL)
325                         return (ENOMEM);
326         }
327
328         return (0);
329 }
330
331 static int
332 bhnd_nvram_bcmraw_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io)
333 {
334         struct bhnd_nvram_bcmraw        *bcm;
335         int                              error;
336
337         bcm = (struct bhnd_nvram_bcmraw *)nv;
338
339         /* Parse the BCM input data and initialize our backing
340          * data representation */
341         if ((error = bhnd_nvram_bcmraw_init(bcm, io))) {
342                 bhnd_nvram_bcmraw_free(nv);
343                 return (error);
344         }
345
346         return (0);
347 }
348
349 static void
350 bhnd_nvram_bcmraw_free(struct bhnd_nvram_data *nv)
351 {
352         struct bhnd_nvram_bcmraw *bcm = (struct bhnd_nvram_bcmraw *)nv;
353
354         if (bcm->data != NULL)
355                 bhnd_nv_free(bcm->data);
356 }
357
358 static bhnd_nvram_plist *
359 bhnd_nvram_bcmraw_options(struct bhnd_nvram_data *nv)
360 {
361         return (NULL);
362 }
363
364 static size_t
365 bhnd_nvram_bcmraw_count(struct bhnd_nvram_data *nv)
366 {
367         struct bhnd_nvram_bcmraw *bcm = (struct bhnd_nvram_bcmraw *)nv;
368
369         return (bcm->count);
370 }
371
372 static uint32_t
373 bhnd_nvram_bcmraw_caps(struct bhnd_nvram_data *nv)
374 {
375         return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS);
376 }
377
378 static const char *
379 bhnd_nvram_bcmraw_next(struct bhnd_nvram_data *nv, void **cookiep)
380 {
381         struct bhnd_nvram_bcmraw        *bcm;
382         const char                      *envp;
383
384         bcm = (struct bhnd_nvram_bcmraw *)nv;
385
386         if (*cookiep == NULL) {
387                 /* Start at the first NVRAM data record */
388                 envp = bcm->data;
389         } else {
390                 /* Seek to next record */
391                 envp = *cookiep;
392                 envp += strlen(envp) + 1;       /* key + '\0' */
393                 envp += strlen(envp) + 1;       /* value + '\0' */
394         }
395
396         /* EOF? */
397         if (*envp == '\0')
398                 return (NULL);
399
400         *cookiep = (void *)(uintptr_t)envp;
401         return (envp);
402 }
403
404 static void *
405 bhnd_nvram_bcmraw_find(struct bhnd_nvram_data *nv, const char *name)
406 {
407         return (bhnd_nvram_data_generic_find(nv, name));
408 }
409
410 static int
411 bhnd_nvram_bcmraw_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1,
412     void *cookiep2)
413 {
414         if (cookiep1 < cookiep2)
415                 return (-1);
416
417         if (cookiep1 > cookiep2)
418                 return (1);
419
420         return (0);
421 }
422
423 static int
424 bhnd_nvram_bcmraw_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
425     size_t *len, bhnd_nvram_type type)
426 {
427         return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type));
428 }
429
430 static int
431 bhnd_nvram_bcmraw_copy_val(struct bhnd_nvram_data *nv, void *cookiep,
432     bhnd_nvram_val **value)
433 {
434         return (bhnd_nvram_data_generic_rp_copy_val(nv, cookiep, value));
435 }
436
437 static const void *
438 bhnd_nvram_bcmraw_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
439     size_t *len, bhnd_nvram_type *type)
440 {
441         const char *envp;
442
443         /* Cookie points to key\0value\0 -- get the value address */
444         envp = cookiep;
445         envp += strlen(envp) + 1;       /* key + '\0' */
446         *len = strlen(envp) + 1;        /* value + '\0' */
447         *type = BHND_NVRAM_TYPE_STRING;
448
449         return (envp);
450 }
451
452 static const char *
453 bhnd_nvram_bcmraw_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
454 {
455         /* Cookie points to key\0value\0 */
456         return (cookiep);
457 }
458
459 static int
460 bhnd_nvram_bcmraw_filter_setvar(struct bhnd_nvram_data *nv, const char *name,
461     bhnd_nvram_val *value, bhnd_nvram_val **result)
462 {
463         bhnd_nvram_val  *str;
464         int              error;
465
466         /* Name (trimmed of any path prefix) must be valid */
467         if (!bhnd_nvram_validate_name(bhnd_nvram_trim_path_name(name)))
468                 return (EINVAL);
469
470         /* Value must be bcm-formatted string */
471         error = bhnd_nvram_val_convert_new(&str, &bhnd_nvram_val_bcm_string_fmt,
472             value, BHND_NVRAM_VAL_DYNAMIC);
473         if (error)
474                 return (error);
475
476         /* Success. Transfer result ownership to the caller. */
477         *result = str;
478         return (0);
479 }
480
481 static int
482 bhnd_nvram_bcmraw_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name)
483 {
484         /* We permit deletion of any variable */
485         return (0);
486 }