xref: /freebsd/sys/dev/bhnd/nvram/bhnd_nvram_data_bcmraw.c (revision 7f9dff23d3092aa33ad45b2b63e52469b3c13a6e)
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 
54 /*
55  * Broadcom-RAW NVRAM data class.
56  *
57  * The Broadcom NVRAM NUL-delimited ASCII format is used by most
58  * Broadcom SoCs.
59  *
60  * The NVRAM data is encoded as a stream of of NUL-terminated 'key=value'
61  * strings; the end of the stream is denoted by a single extra NUL character.
62  */
63 
64 struct bhnd_nvram_bcmraw;
65 
66 /** BCM-RAW NVRAM data class instance */
67 struct bhnd_nvram_bcmraw {
68 	struct bhnd_nvram_data		 nv;	/**< common instance state */
69 	char				*data;	/**< backing buffer */
70 	size_t				 size;	/**< buffer size */
71 	size_t				 count;	/**< variable count */
72 };
73 
74 BHND_NVRAM_DATA_CLASS_DEFN(bcmraw, "Broadcom (RAW)",
75    sizeof(struct bhnd_nvram_bcmraw))
76 
77 static int
78 bhnd_nvram_bcmraw_probe(struct bhnd_nvram_io *io)
79 {
80 	char	 envp[16];
81 	size_t	 envp_len;
82 	int	 error;
83 
84 	/*
85 	 * Fetch the initial bytes to try to identify BCM data.
86 	 *
87 	 * We always assert a low probe priority, as we only scan the initial
88 	 * bytes of the file.
89 	 */
90 	envp_len = bhnd_nv_ummin(sizeof(envp), bhnd_nvram_io_getsize(io));
91 	if ((error = bhnd_nvram_io_read(io, 0x0, envp, envp_len)))
92 		return (error);
93 
94 	/* A zero-length BCM-RAW buffer should contain a single terminating
95 	 * NUL */
96 	if (envp_len == 0)
97 		return (ENXIO);
98 
99 	if (envp_len == 1) {
100 		if (envp[0] != '\0')
101 			return (ENXIO);
102 
103 		return (BHND_NVRAM_DATA_PROBE_MAYBE);
104 	}
105 
106 	/* Don't match on non-ASCII, non-printable data */
107 	for (size_t i = 0; i < envp_len; i++) {
108 		char c = envp[i];
109 		if (envp[i] == '\0')
110 			break;
111 
112 		if (!bhnd_nv_isprint(c))
113 			return (ENXIO);
114 	}
115 
116 	/* The first character should be a valid key char */
117 	if (!bhnd_nv_isalpha(envp[0]))
118 		return (ENXIO);
119 
120 	return (BHND_NVRAM_DATA_PROBE_MAYBE);
121 }
122 
123 /**
124  * Initialize @p bcm with the provided NVRAM data mapped by @p src.
125  *
126  * @param bcm A newly allocated data instance.
127  */
128 static int
129 bhnd_nvram_bcmraw_init(struct bhnd_nvram_bcmraw *bcm, struct bhnd_nvram_io *src)
130 {
131 	size_t	 io_size;
132 	size_t	 capacity, offset;
133 	int	 error;
134 
135 	/* Fetch the input image size */
136 	io_size = bhnd_nvram_io_getsize(src);
137 
138 	/* Allocate a buffer large enough to hold the NVRAM image, and
139 	 * an extra EOF-signaling NUL (on the chance it's missing from the
140 	 * source data) */
141 	if (io_size == SIZE_MAX)
142 		return (ENOMEM);
143 
144 	capacity = io_size + 1 /* room for extra NUL */;
145 	bcm->size = io_size;
146 	if ((bcm->data = bhnd_nv_malloc(capacity)) == NULL)
147 		return (ENOMEM);
148 
149 	/* Copy in the NVRAM image */
150 	if ((error = bhnd_nvram_io_read(src, 0x0, bcm->data, io_size)))
151 		return (error);
152 
153 	/* Process the buffer */
154 	bcm->count = 0;
155 	for (offset = 0; offset < bcm->size; offset++) {
156 		char		*envp;
157 		const char	*name, *value;
158 		size_t		 envp_len;
159 		size_t		 name_len, value_len;
160 
161 		/* Parse the key=value string */
162 		envp = (char *) (bcm->data + offset);
163 		envp_len = strnlen(envp, bcm->size - offset);
164 		error = bhnd_nvram_parse_env(envp, envp_len, '=', &name,
165 					     &name_len, &value, &value_len);
166 		if (error) {
167 			BHND_NV_LOG("error parsing envp at offset %#zx: %d\n",
168 			    offset, error);
169 			return (error);
170 		}
171 
172 		/* Insert a '\0' character, replacing the '=' delimiter and
173 		 * allowing us to vend references directly to the variable
174 		 * name */
175 		*(envp + name_len) = '\0';
176 
177 		/* Add to variable count */
178 		bcm->count++;
179 
180 		/* Seek past the value's terminating '\0' */
181 		offset += envp_len;
182 		if (offset == io_size) {
183 			BHND_NV_LOG("missing terminating NUL at offset %#zx\n",
184 			    offset);
185 			return (EINVAL);
186 		}
187 
188 		/* If we hit EOF without finding a terminating NUL
189 		 * byte, we need to append it */
190 		if (++offset == bcm->size) {
191 			BHND_NV_ASSERT(offset < capacity,
192 			    ("appending past end of buffer"));
193 			bcm->size++;
194 			*(bcm->data + offset) = '\0';
195 		}
196 
197 		/* Check for explicit EOF (encoded as a single empty NUL
198 		 * terminated string) */
199 		if (*(bcm->data + offset) == '\0')
200 			break;
201 	}
202 
203 	/* Reclaim any unused space in he backing buffer */
204 	if (offset < bcm->size) {
205 		bcm->data = bhnd_nv_reallocf(bcm->data, bcm->size);
206 		if (bcm->data == NULL)
207 			return (ENOMEM);
208 	}
209 
210 	return (0);
211 }
212 
213 static int
214 bhnd_nvram_bcmraw_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io)
215 {
216 	struct bhnd_nvram_bcmraw	*bcm;
217 	int				 error;
218 
219 	bcm = (struct bhnd_nvram_bcmraw *)nv;
220 
221 	/* Parse the BCM input data and initialize our backing
222 	 * data representation */
223 	if ((error = bhnd_nvram_bcmraw_init(bcm, io))) {
224 		bhnd_nvram_bcmraw_free(nv);
225 		return (error);
226 	}
227 
228 	return (0);
229 }
230 
231 static void
232 bhnd_nvram_bcmraw_free(struct bhnd_nvram_data *nv)
233 {
234 	struct bhnd_nvram_bcmraw *bcm = (struct bhnd_nvram_bcmraw *)nv;
235 
236 	if (bcm->data != NULL)
237 		bhnd_nv_free(bcm->data);
238 }
239 
240 static size_t
241 bhnd_nvram_bcmraw_count(struct bhnd_nvram_data *nv)
242 {
243 	struct bhnd_nvram_bcmraw *bcm = (struct bhnd_nvram_bcmraw *)nv;
244 
245 	return (bcm->count);
246 }
247 
248 static int
249 bhnd_nvram_bcmraw_size(struct bhnd_nvram_data *nv, size_t *size)
250 {
251 	return (bhnd_nvram_bcmraw_serialize(nv, NULL, size));
252 }
253 
254 static int
255 bhnd_nvram_bcmraw_serialize(struct bhnd_nvram_data *nv, void *buf, size_t *len)
256 {
257 	struct bhnd_nvram_bcmraw	*bcm;
258 	char * const			 p = (char *)buf;
259 	size_t				 limit;
260 	size_t				 offset;
261 
262 	bcm = (struct bhnd_nvram_bcmraw *)nv;
263 
264 	/* Save the output buffer limit */
265 	if (buf == NULL)
266 		limit = 0;
267 	else
268 		limit = *len;
269 
270 	/* The serialized form will be exactly the length
271 	 * of our backing buffer representation */
272 	*len = bcm->size;
273 
274 	/* Skip serialization if not requested, or report ENOMEM if
275 	 * buffer is too small */
276 	if (buf == NULL) {
277 		return (0);
278 	} else if (*len > limit) {
279 		return (ENOMEM);
280 	}
281 
282 	/* Write all variables to the output buffer */
283 	memcpy(buf, bcm->data, *len);
284 
285 	/* Rewrite all '\0' delimiters back to '=' */
286 	offset = 0;
287 	while (offset < bcm->size) {
288 		size_t name_len, value_len;
289 
290 		name_len = strlen(p + offset);
291 
292 		/* EOF? */
293 		if (name_len == 0) {
294 			BHND_NV_ASSERT(*(p + offset) == '\0',
295 			    ("no NUL terminator"));
296 
297 			offset++;
298 			break;
299 		}
300 
301 		/* Rewrite 'name\0' to 'name=' */
302 		offset += name_len;
303 		BHND_NV_ASSERT(*(p + offset) == '\0', ("incorrect offset"));
304 
305 		*(p + offset) = '=';
306 		offset++;
307 
308 		value_len = strlen(p + offset);
309 		offset += value_len + 1;
310 	}
311 
312 	return (0);
313 }
314 
315 static uint32_t
316 bhnd_nvram_bcmraw_caps(struct bhnd_nvram_data *nv)
317 {
318 	return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS);
319 }
320 
321 static const char *
322 bhnd_nvram_bcmraw_next(struct bhnd_nvram_data *nv, void **cookiep)
323 {
324 	struct bhnd_nvram_bcmraw	*bcm;
325 	const char			*envp;
326 
327 	bcm = (struct bhnd_nvram_bcmraw *)nv;
328 
329 	if (*cookiep == NULL) {
330 		/* Start at the first NVRAM data record */
331 		envp = bcm->data;
332 	} else {
333 		/* Seek to next record */
334 		envp = *cookiep;
335 		envp += strlen(envp) + 1;	/* key + '\0' */
336 		envp += strlen(envp) + 1;	/* value + '\0' */
337 	}
338 
339 	/* EOF? */
340 	if (*envp == '\0')
341 		return (NULL);
342 
343 	*cookiep = (void *)(uintptr_t)envp;
344 	return (envp);
345 }
346 
347 static void *
348 bhnd_nvram_bcmraw_find(struct bhnd_nvram_data *nv, const char *name)
349 {
350 	return (bhnd_nvram_data_generic_find(nv, name));
351 }
352 
353 static int
354 bhnd_nvram_bcmraw_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
355     size_t *len, bhnd_nvram_type type)
356 {
357 	return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type));
358 }
359 
360 static const void *
361 bhnd_nvram_bcmraw_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
362     size_t *len, bhnd_nvram_type *type)
363 {
364 	const char *envp;
365 
366 	/* Cookie points to key\0value\0 -- get the value address */
367 	envp = cookiep;
368 	envp += strlen(envp) + 1;	/* key + '\0' */
369 	*len = strlen(envp) + 1;	/* value + '\0' */
370 	*type = BHND_NVRAM_TYPE_STRING;
371 
372 	return (envp);
373 }
374 
375 static const char *
376 bhnd_nvram_bcmraw_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
377 {
378 	/* Cookie points to key\0value\0 */
379 	return (cookiep);
380 }
381