177cb4d3eSLandon J. Fuller /*-
277cb4d3eSLandon J. Fuller * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org>
377cb4d3eSLandon J. Fuller * All rights reserved.
477cb4d3eSLandon J. Fuller *
577cb4d3eSLandon J. Fuller * Redistribution and use in source and binary forms, with or without
677cb4d3eSLandon J. Fuller * modification, are permitted provided that the following conditions
777cb4d3eSLandon J. Fuller * are met:
877cb4d3eSLandon J. Fuller * 1. Redistributions of source code must retain the above copyright
977cb4d3eSLandon J. Fuller * notice, this list of conditions and the following disclaimer,
1077cb4d3eSLandon J. Fuller * without modification.
1177cb4d3eSLandon J. Fuller * 2. Redistributions in binary form must reproduce at minimum a disclaimer
1277cb4d3eSLandon J. Fuller * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
1377cb4d3eSLandon J. Fuller * redistribution must be conditioned upon including a substantially
1477cb4d3eSLandon J. Fuller * similar Disclaimer requirement for further binary redistribution.
1577cb4d3eSLandon J. Fuller *
1677cb4d3eSLandon J. Fuller * NO WARRANTY
1777cb4d3eSLandon J. Fuller * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1877cb4d3eSLandon J. Fuller * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1977cb4d3eSLandon J. Fuller * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
2077cb4d3eSLandon J. Fuller * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
2177cb4d3eSLandon J. Fuller * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
2277cb4d3eSLandon J. Fuller * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2377cb4d3eSLandon J. Fuller * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2477cb4d3eSLandon J. Fuller * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
2577cb4d3eSLandon J. Fuller * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2677cb4d3eSLandon J. Fuller * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
2777cb4d3eSLandon J. Fuller * THE POSSIBILITY OF SUCH DAMAGES.
2877cb4d3eSLandon J. Fuller */
2977cb4d3eSLandon J. Fuller
3077cb4d3eSLandon J. Fuller #include <sys/param.h>
3177cb4d3eSLandon J. Fuller #include <sys/endian.h>
3277cb4d3eSLandon J. Fuller
3377cb4d3eSLandon J. Fuller #ifdef _KERNEL
3477cb4d3eSLandon J. Fuller
3577cb4d3eSLandon J. Fuller #include <sys/bus.h>
3677cb4d3eSLandon J. Fuller #include <sys/ctype.h>
3777cb4d3eSLandon J. Fuller #include <sys/malloc.h>
3877cb4d3eSLandon J. Fuller #include <sys/systm.h>
3977cb4d3eSLandon J. Fuller
4077cb4d3eSLandon J. Fuller #else /* !_KERNEL */
4177cb4d3eSLandon J. Fuller
4277cb4d3eSLandon J. Fuller #include <ctype.h>
4377cb4d3eSLandon J. Fuller #include <stdint.h>
4477cb4d3eSLandon J. Fuller #include <stdio.h>
4577cb4d3eSLandon J. Fuller #include <stdlib.h>
4677cb4d3eSLandon J. Fuller #include <string.h>
4777cb4d3eSLandon J. Fuller
4877cb4d3eSLandon J. Fuller #endif /* _KERNEL */
4977cb4d3eSLandon J. Fuller
5077cb4d3eSLandon J. Fuller #include "bhnd_nvram_private.h"
5177cb4d3eSLandon J. Fuller
5277cb4d3eSLandon J. Fuller #include "bhnd_nvram_datavar.h"
5377cb4d3eSLandon J. Fuller
5477cb4d3eSLandon J. Fuller #include "bhnd_nvram_data_bcmreg.h"
5577cb4d3eSLandon J. Fuller #include "bhnd_nvram_data_bcmvar.h"
5677cb4d3eSLandon J. Fuller
5777cb4d3eSLandon J. Fuller /*
5877cb4d3eSLandon J. Fuller * Broadcom NVRAM data class.
5977cb4d3eSLandon J. Fuller *
6077cb4d3eSLandon J. Fuller * The Broadcom NVRAM NUL-delimited ASCII format is used by most
6177cb4d3eSLandon J. Fuller * Broadcom SoCs.
6277cb4d3eSLandon J. Fuller *
6377cb4d3eSLandon J. Fuller * The NVRAM data is encoded as a standard header, followed by series of
6477cb4d3eSLandon J. Fuller * NUL-terminated 'key=value' strings; the end of the stream is denoted
6577cb4d3eSLandon J. Fuller * by a single extra NUL character.
6677cb4d3eSLandon J. Fuller */
6777cb4d3eSLandon J. Fuller
6877cb4d3eSLandon J. Fuller struct bhnd_nvram_bcm;
6977cb4d3eSLandon J. Fuller
7077cb4d3eSLandon J. Fuller static struct bhnd_nvram_bcm_hvar *bhnd_nvram_bcm_gethdrvar(
7177cb4d3eSLandon J. Fuller struct bhnd_nvram_bcm *bcm,
7277cb4d3eSLandon J. Fuller const char *name);
7377cb4d3eSLandon J. Fuller static struct bhnd_nvram_bcm_hvar *bhnd_nvram_bcm_to_hdrvar(
7477cb4d3eSLandon J. Fuller struct bhnd_nvram_bcm *bcm,
7577cb4d3eSLandon J. Fuller void *cookiep);
7677cb4d3eSLandon J. Fuller static size_t bhnd_nvram_bcm_hdrvar_index(
7777cb4d3eSLandon J. Fuller struct bhnd_nvram_bcm *bcm,
7877cb4d3eSLandon J. Fuller struct bhnd_nvram_bcm_hvar *hvar);
7977cb4d3eSLandon J. Fuller /*
8077cb4d3eSLandon J. Fuller * Set of BCM NVRAM header values that are required to be mirrored in the
8177cb4d3eSLandon J. Fuller * NVRAM data itself.
8277cb4d3eSLandon J. Fuller *
8377cb4d3eSLandon J. Fuller * If they're not included in the parsed NVRAM data, we need to vend the
8477cb4d3eSLandon J. Fuller * header-parsed values with their appropriate keys, and add them in any
8577cb4d3eSLandon J. Fuller * updates to the NVRAM data.
8677cb4d3eSLandon J. Fuller *
8777cb4d3eSLandon J. Fuller * If they're modified in NVRAM, we need to sync the changes with the
8877cb4d3eSLandon J. Fuller * the NVRAM header values.
8977cb4d3eSLandon J. Fuller */
9077cb4d3eSLandon J. Fuller static const struct bhnd_nvram_bcm_hvar bhnd_nvram_bcm_hvars[] = {
9177cb4d3eSLandon J. Fuller {
9277cb4d3eSLandon J. Fuller .name = BCM_NVRAM_CFG0_SDRAM_INIT_VAR,
9377cb4d3eSLandon J. Fuller .type = BHND_NVRAM_TYPE_UINT16,
9477cb4d3eSLandon J. Fuller .len = sizeof(uint16_t),
9577cb4d3eSLandon J. Fuller .nelem = 1,
9677cb4d3eSLandon J. Fuller },
9777cb4d3eSLandon J. Fuller {
9877cb4d3eSLandon J. Fuller .name = BCM_NVRAM_CFG1_SDRAM_CFG_VAR,
9977cb4d3eSLandon J. Fuller .type = BHND_NVRAM_TYPE_UINT16,
10077cb4d3eSLandon J. Fuller .len = sizeof(uint16_t),
10177cb4d3eSLandon J. Fuller .nelem = 1,
10277cb4d3eSLandon J. Fuller },
10377cb4d3eSLandon J. Fuller {
10477cb4d3eSLandon J. Fuller .name = BCM_NVRAM_CFG1_SDRAM_REFRESH_VAR,
10577cb4d3eSLandon J. Fuller .type = BHND_NVRAM_TYPE_UINT16,
10677cb4d3eSLandon J. Fuller .len = sizeof(uint16_t),
10777cb4d3eSLandon J. Fuller .nelem = 1,
10877cb4d3eSLandon J. Fuller },
10977cb4d3eSLandon J. Fuller {
11077cb4d3eSLandon J. Fuller .name = BCM_NVRAM_SDRAM_NCDL_VAR,
11177cb4d3eSLandon J. Fuller .type = BHND_NVRAM_TYPE_UINT32,
11277cb4d3eSLandon J. Fuller .len = sizeof(uint32_t),
11377cb4d3eSLandon J. Fuller .nelem = 1,
11477cb4d3eSLandon J. Fuller },
11577cb4d3eSLandon J. Fuller };
11677cb4d3eSLandon J. Fuller
11777cb4d3eSLandon J. Fuller /** BCM NVRAM data class instance */
11877cb4d3eSLandon J. Fuller struct bhnd_nvram_bcm {
11977cb4d3eSLandon J. Fuller struct bhnd_nvram_data nv; /**< common instance state */
12077cb4d3eSLandon J. Fuller struct bhnd_nvram_io *data; /**< backing buffer */
121a7c43ebdSLandon J. Fuller bhnd_nvram_plist *opts; /**< serialization options */
12277cb4d3eSLandon J. Fuller
12377cb4d3eSLandon J. Fuller /** BCM header values */
12477cb4d3eSLandon J. Fuller struct bhnd_nvram_bcm_hvar hvars[nitems(bhnd_nvram_bcm_hvars)];
12577cb4d3eSLandon J. Fuller
12677cb4d3eSLandon J. Fuller size_t count; /**< total variable count */
12777cb4d3eSLandon J. Fuller };
12877cb4d3eSLandon J. Fuller
129c283839dSLandon J. Fuller BHND_NVRAM_DATA_CLASS_DEFN(bcm, "Broadcom", BHND_NVRAM_DATA_CAP_DEVPATHS,
130c283839dSLandon J. Fuller sizeof(struct bhnd_nvram_bcm))
13177cb4d3eSLandon J. Fuller
13277cb4d3eSLandon J. Fuller static int
bhnd_nvram_bcm_probe(struct bhnd_nvram_io * io)13377cb4d3eSLandon J. Fuller bhnd_nvram_bcm_probe(struct bhnd_nvram_io *io)
13477cb4d3eSLandon J. Fuller {
13577cb4d3eSLandon J. Fuller struct bhnd_nvram_bcmhdr hdr;
13677cb4d3eSLandon J. Fuller int error;
13777cb4d3eSLandon J. Fuller
13877cb4d3eSLandon J. Fuller if ((error = bhnd_nvram_io_read(io, 0x0, &hdr, sizeof(hdr))))
13977cb4d3eSLandon J. Fuller return (error);
14077cb4d3eSLandon J. Fuller
14177cb4d3eSLandon J. Fuller if (le32toh(hdr.magic) != BCM_NVRAM_MAGIC)
14277cb4d3eSLandon J. Fuller return (ENXIO);
14377cb4d3eSLandon J. Fuller
144*591e79bcSLandon J. Fuller if (le32toh(hdr.size) > bhnd_nvram_io_getsize(io))
145*591e79bcSLandon J. Fuller return (ENXIO);
146*591e79bcSLandon J. Fuller
14777cb4d3eSLandon J. Fuller return (BHND_NVRAM_DATA_PROBE_DEFAULT);
14877cb4d3eSLandon J. Fuller }
14977cb4d3eSLandon J. Fuller
150*591e79bcSLandon J. Fuller /**
151*591e79bcSLandon J. Fuller * Parser states for bhnd_nvram_bcm_getvar_direct_common().
152*591e79bcSLandon J. Fuller */
153*591e79bcSLandon J. Fuller typedef enum {
154*591e79bcSLandon J. Fuller BCM_PARSE_KEY_START,
155*591e79bcSLandon J. Fuller BCM_PARSE_KEY_CONT,
156*591e79bcSLandon J. Fuller BCM_PARSE_KEY,
157*591e79bcSLandon J. Fuller BCM_PARSE_NEXT_KEY,
158*591e79bcSLandon J. Fuller BCM_PARSE_VALUE_START,
159*591e79bcSLandon J. Fuller BCM_PARSE_VALUE
160*591e79bcSLandon J. Fuller } bcm_parse_state;
161*591e79bcSLandon J. Fuller
162*591e79bcSLandon J. Fuller static int
bhnd_nvram_bcm_getvar_direct(struct bhnd_nvram_io * io,const char * name,void * outp,size_t * olen,bhnd_nvram_type otype)163*591e79bcSLandon J. Fuller bhnd_nvram_bcm_getvar_direct(struct bhnd_nvram_io *io, const char *name,
164*591e79bcSLandon J. Fuller void *outp, size_t *olen, bhnd_nvram_type otype)
165*591e79bcSLandon J. Fuller {
166*591e79bcSLandon J. Fuller return (bhnd_nvram_bcm_getvar_direct_common(io, name, outp, olen, otype,
167*591e79bcSLandon J. Fuller true));
168*591e79bcSLandon J. Fuller }
169*591e79bcSLandon J. Fuller
170*591e79bcSLandon J. Fuller /**
171*591e79bcSLandon J. Fuller * Common BCM/BCMRAW implementation of bhnd_nvram_getvar_direct().
172*591e79bcSLandon J. Fuller */
173*591e79bcSLandon J. Fuller int
bhnd_nvram_bcm_getvar_direct_common(struct bhnd_nvram_io * io,const char * name,void * outp,size_t * olen,bhnd_nvram_type otype,bool have_header)174*591e79bcSLandon J. Fuller bhnd_nvram_bcm_getvar_direct_common(struct bhnd_nvram_io *io, const char *name,
175*591e79bcSLandon J. Fuller void *outp, size_t *olen, bhnd_nvram_type otype, bool have_header)
176*591e79bcSLandon J. Fuller {
177*591e79bcSLandon J. Fuller struct bhnd_nvram_bcmhdr hdr;
178*591e79bcSLandon J. Fuller char buf[512];
179*591e79bcSLandon J. Fuller bcm_parse_state pstate;
180*591e79bcSLandon J. Fuller size_t limit, offset;
181*591e79bcSLandon J. Fuller size_t buflen, bufpos;
182*591e79bcSLandon J. Fuller size_t namelen, namepos;
183*591e79bcSLandon J. Fuller size_t vlen;
184*591e79bcSLandon J. Fuller int error;
185*591e79bcSLandon J. Fuller
186*591e79bcSLandon J. Fuller limit = bhnd_nvram_io_getsize(io);
187*591e79bcSLandon J. Fuller offset = 0;
188*591e79bcSLandon J. Fuller
189*591e79bcSLandon J. Fuller /* Fetch and validate the header */
190*591e79bcSLandon J. Fuller if (have_header) {
191*591e79bcSLandon J. Fuller if ((error = bhnd_nvram_io_read(io, offset, &hdr, sizeof(hdr))))
192*591e79bcSLandon J. Fuller return (error);
193*591e79bcSLandon J. Fuller
194*591e79bcSLandon J. Fuller if (le32toh(hdr.magic) != BCM_NVRAM_MAGIC)
195*591e79bcSLandon J. Fuller return (ENXIO);
196*591e79bcSLandon J. Fuller
197*591e79bcSLandon J. Fuller offset += sizeof(hdr);
198*591e79bcSLandon J. Fuller limit = bhnd_nv_ummin(le32toh(hdr.size), limit);
199*591e79bcSLandon J. Fuller }
200*591e79bcSLandon J. Fuller
201*591e79bcSLandon J. Fuller /* Loop our parser until we find the requested variable, or hit EOF */
202*591e79bcSLandon J. Fuller pstate = BCM_PARSE_KEY_START;
203*591e79bcSLandon J. Fuller buflen = 0;
204*591e79bcSLandon J. Fuller bufpos = 0;
205*591e79bcSLandon J. Fuller namelen = strlen(name);
206*591e79bcSLandon J. Fuller namepos = 0;
207*591e79bcSLandon J. Fuller vlen = 0;
208*591e79bcSLandon J. Fuller
209*591e79bcSLandon J. Fuller while ((offset - bufpos) < limit) {
210*591e79bcSLandon J. Fuller BHND_NV_ASSERT(bufpos <= buflen,
211*591e79bcSLandon J. Fuller ("buf position invalid (%zu > %zu)", bufpos, buflen));
212*591e79bcSLandon J. Fuller BHND_NV_ASSERT(buflen <= sizeof(buf),
213*591e79bcSLandon J. Fuller ("buf length invalid (%zu > %zu", buflen, sizeof(buf)));
214*591e79bcSLandon J. Fuller
215*591e79bcSLandon J. Fuller /* Repopulate our parse buffer? */
216*591e79bcSLandon J. Fuller if (buflen - bufpos == 0) {
217*591e79bcSLandon J. Fuller BHND_NV_ASSERT(offset < limit, ("offset overrun"));
218*591e79bcSLandon J. Fuller
219*591e79bcSLandon J. Fuller buflen = bhnd_nv_ummin(sizeof(buf), limit - offset);
220*591e79bcSLandon J. Fuller bufpos = 0;
221*591e79bcSLandon J. Fuller
222*591e79bcSLandon J. Fuller error = bhnd_nvram_io_read(io, offset, buf, buflen);
223*591e79bcSLandon J. Fuller if (error)
224*591e79bcSLandon J. Fuller return (error);
225*591e79bcSLandon J. Fuller
226*591e79bcSLandon J. Fuller offset += buflen;
227*591e79bcSLandon J. Fuller }
228*591e79bcSLandon J. Fuller
229*591e79bcSLandon J. Fuller switch (pstate) {
230*591e79bcSLandon J. Fuller case BCM_PARSE_KEY_START:
231*591e79bcSLandon J. Fuller BHND_NV_ASSERT(buflen - bufpos > 0, ("empty buffer!"));
232*591e79bcSLandon J. Fuller
233*591e79bcSLandon J. Fuller /* An extra '\0' denotes NVRAM EOF */
234*591e79bcSLandon J. Fuller if (buf[bufpos] == '\0')
235*591e79bcSLandon J. Fuller return (ENOENT);
236*591e79bcSLandon J. Fuller
237*591e79bcSLandon J. Fuller /* Reset name matching position */
238*591e79bcSLandon J. Fuller namepos = 0;
239*591e79bcSLandon J. Fuller
240*591e79bcSLandon J. Fuller /* Start name matching */
241*591e79bcSLandon J. Fuller pstate = BCM_PARSE_KEY_CONT;
242*591e79bcSLandon J. Fuller break;
243*591e79bcSLandon J. Fuller
244*591e79bcSLandon J. Fuller case BCM_PARSE_KEY_CONT: {
245*591e79bcSLandon J. Fuller size_t navail, nleft;
246*591e79bcSLandon J. Fuller
247*591e79bcSLandon J. Fuller nleft = namelen - namepos;
248*591e79bcSLandon J. Fuller navail = bhnd_nv_ummin(buflen - bufpos, nleft);
249*591e79bcSLandon J. Fuller
250*591e79bcSLandon J. Fuller if (strncmp(name+namepos, buf+bufpos, navail) == 0) {
251*591e79bcSLandon J. Fuller /* Matched */
252*591e79bcSLandon J. Fuller namepos += navail;
253*591e79bcSLandon J. Fuller bufpos += navail;
254*591e79bcSLandon J. Fuller
255*591e79bcSLandon J. Fuller /* If we've matched the full variable name,
256*591e79bcSLandon J. Fuller * look for its trailing delimiter */
257*591e79bcSLandon J. Fuller if (namepos == namelen)
258*591e79bcSLandon J. Fuller pstate = BCM_PARSE_KEY;
259*591e79bcSLandon J. Fuller } else {
260*591e79bcSLandon J. Fuller /* No match; advance to next entry and restart
261*591e79bcSLandon J. Fuller * name matching */
262*591e79bcSLandon J. Fuller pstate = BCM_PARSE_NEXT_KEY;
263*591e79bcSLandon J. Fuller }
264*591e79bcSLandon J. Fuller
265*591e79bcSLandon J. Fuller break;
266*591e79bcSLandon J. Fuller }
267*591e79bcSLandon J. Fuller
268*591e79bcSLandon J. Fuller case BCM_PARSE_KEY:
269*591e79bcSLandon J. Fuller BHND_NV_ASSERT(buflen - bufpos > 0, ("empty buffer!"));
270*591e79bcSLandon J. Fuller
271*591e79bcSLandon J. Fuller if (buf[bufpos] == '=') {
272*591e79bcSLandon J. Fuller /* Key fully matched; advance past '=' and
273*591e79bcSLandon J. Fuller * parse the value */
274*591e79bcSLandon J. Fuller bufpos++;
275*591e79bcSLandon J. Fuller pstate = BCM_PARSE_VALUE_START;
276*591e79bcSLandon J. Fuller } else {
277*591e79bcSLandon J. Fuller /* No match; advance to next entry and restart
278*591e79bcSLandon J. Fuller * name matching */
279*591e79bcSLandon J. Fuller pstate = BCM_PARSE_NEXT_KEY;
280*591e79bcSLandon J. Fuller }
281*591e79bcSLandon J. Fuller
282*591e79bcSLandon J. Fuller break;
283*591e79bcSLandon J. Fuller
284*591e79bcSLandon J. Fuller case BCM_PARSE_NEXT_KEY: {
285*591e79bcSLandon J. Fuller const char *p;
286*591e79bcSLandon J. Fuller
287*591e79bcSLandon J. Fuller /* Scan for a '\0' terminator */
288*591e79bcSLandon J. Fuller p = memchr(buf+bufpos, '\0', buflen - bufpos);
289*591e79bcSLandon J. Fuller
290*591e79bcSLandon J. Fuller if (p != NULL) {
291*591e79bcSLandon J. Fuller /* Found entry terminator; restart name
292*591e79bcSLandon J. Fuller * matching at next entry */
293*591e79bcSLandon J. Fuller pstate = BCM_PARSE_KEY_START;
294*591e79bcSLandon J. Fuller bufpos = (p - buf) + 1 /* skip '\0' */;
295*591e79bcSLandon J. Fuller } else {
296*591e79bcSLandon J. Fuller /* Consumed full buffer looking for '\0';
297*591e79bcSLandon J. Fuller * force repopulation of the buffer and
298*591e79bcSLandon J. Fuller * retry */
299*591e79bcSLandon J. Fuller bufpos = buflen;
300*591e79bcSLandon J. Fuller }
301*591e79bcSLandon J. Fuller
302*591e79bcSLandon J. Fuller break;
303*591e79bcSLandon J. Fuller }
304*591e79bcSLandon J. Fuller
305*591e79bcSLandon J. Fuller case BCM_PARSE_VALUE_START: {
306*591e79bcSLandon J. Fuller const char *p;
307*591e79bcSLandon J. Fuller
308*591e79bcSLandon J. Fuller /* Scan for a '\0' terminator */
309*591e79bcSLandon J. Fuller p = memchr(buf+bufpos, '\0', buflen - bufpos);
310*591e79bcSLandon J. Fuller
311*591e79bcSLandon J. Fuller if (p != NULL) {
312*591e79bcSLandon J. Fuller /* Found entry terminator; parse the value */
313*591e79bcSLandon J. Fuller vlen = p - &buf[bufpos];
314*591e79bcSLandon J. Fuller pstate = BCM_PARSE_VALUE;
315*591e79bcSLandon J. Fuller
316*591e79bcSLandon J. Fuller } else if (p == NULL && offset == limit) {
317*591e79bcSLandon J. Fuller /* Hit EOF without a terminating '\0';
318*591e79bcSLandon J. Fuller * treat the entry as implicitly terminated */
319*591e79bcSLandon J. Fuller vlen = buflen - bufpos;
320*591e79bcSLandon J. Fuller pstate = BCM_PARSE_VALUE;
321*591e79bcSLandon J. Fuller
322*591e79bcSLandon J. Fuller } else if (p == NULL && bufpos > 0) {
323*591e79bcSLandon J. Fuller size_t nread;
324*591e79bcSLandon J. Fuller
325*591e79bcSLandon J. Fuller /* Move existing value data to start of
326*591e79bcSLandon J. Fuller * buffer */
327*591e79bcSLandon J. Fuller memmove(buf, buf+bufpos, buflen - bufpos);
328*591e79bcSLandon J. Fuller buflen = bufpos;
329*591e79bcSLandon J. Fuller bufpos = 0;
330*591e79bcSLandon J. Fuller
331*591e79bcSLandon J. Fuller /* Populate full buffer to allow retry of
332*591e79bcSLandon J. Fuller * value parsing */
333*591e79bcSLandon J. Fuller nread = bhnd_nv_ummin(sizeof(buf) - buflen,
334*591e79bcSLandon J. Fuller limit - offset);
335*591e79bcSLandon J. Fuller
336*591e79bcSLandon J. Fuller error = bhnd_nvram_io_read(io, offset,
337*591e79bcSLandon J. Fuller buf+buflen, nread);
338*591e79bcSLandon J. Fuller if (error)
339*591e79bcSLandon J. Fuller return (error);
340*591e79bcSLandon J. Fuller
341*591e79bcSLandon J. Fuller offset += nread;
342*591e79bcSLandon J. Fuller buflen += nread;
343*591e79bcSLandon J. Fuller } else {
344*591e79bcSLandon J. Fuller /* Value exceeds our buffer capacity */
345*591e79bcSLandon J. Fuller BHND_NV_LOG("cannot parse value for '%s' "
346*591e79bcSLandon J. Fuller "(exceeds %zu byte limit)\n", name,
347*591e79bcSLandon J. Fuller sizeof(buf));
348*591e79bcSLandon J. Fuller
349*591e79bcSLandon J. Fuller return (ENXIO);
350*591e79bcSLandon J. Fuller }
351*591e79bcSLandon J. Fuller
352*591e79bcSLandon J. Fuller break;
353*591e79bcSLandon J. Fuller }
354*591e79bcSLandon J. Fuller
355*591e79bcSLandon J. Fuller case BCM_PARSE_VALUE:
356*591e79bcSLandon J. Fuller BHND_NV_ASSERT(vlen <= buflen, ("value buf overrun"));
357*591e79bcSLandon J. Fuller
358*591e79bcSLandon J. Fuller return (bhnd_nvram_value_coerce(buf+bufpos, vlen,
359*591e79bcSLandon J. Fuller BHND_NVRAM_TYPE_STRING, outp, olen, otype));
360*591e79bcSLandon J. Fuller }
361*591e79bcSLandon J. Fuller }
362*591e79bcSLandon J. Fuller
363*591e79bcSLandon J. Fuller /* Variable not found */
364*591e79bcSLandon J. Fuller return (ENOENT);
365*591e79bcSLandon J. Fuller }
366*591e79bcSLandon J. Fuller
367c283839dSLandon J. Fuller static int
bhnd_nvram_bcm_serialize(bhnd_nvram_data_class * cls,bhnd_nvram_plist * props,bhnd_nvram_plist * options,void * outp,size_t * olen)368c283839dSLandon J. Fuller bhnd_nvram_bcm_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props,
369c283839dSLandon J. Fuller bhnd_nvram_plist *options, void *outp, size_t *olen)
370c283839dSLandon J. Fuller {
371c283839dSLandon J. Fuller struct bhnd_nvram_bcmhdr hdr;
372c283839dSLandon J. Fuller bhnd_nvram_prop *prop;
373c283839dSLandon J. Fuller size_t limit, nbytes;
374c283839dSLandon J. Fuller uint32_t sdram_ncdl;
375c283839dSLandon J. Fuller uint16_t sdram_init, sdram_cfg, sdram_refresh;
376c283839dSLandon J. Fuller uint8_t bcm_ver, crc8;
377c283839dSLandon J. Fuller int error;
378c283839dSLandon J. Fuller
379c283839dSLandon J. Fuller /* Determine output byte limit */
380c283839dSLandon J. Fuller if (outp != NULL)
381c283839dSLandon J. Fuller limit = *olen;
382c283839dSLandon J. Fuller else
383c283839dSLandon J. Fuller limit = 0;
384c283839dSLandon J. Fuller
385c283839dSLandon J. Fuller /* Fetch required header variables */
386c283839dSLandon J. Fuller #define PROPS_GET_HDRVAR(_name, _dest, _type) do { \
387c283839dSLandon J. Fuller const char *name = BCM_NVRAM_ ## _name ## _VAR; \
388c283839dSLandon J. Fuller if (!bhnd_nvram_plist_contains(props, name)) { \
389c283839dSLandon J. Fuller BHND_NV_LOG("missing required property: %s\n", \
390c283839dSLandon J. Fuller name); \
391c283839dSLandon J. Fuller return (EFTYPE); \
392c283839dSLandon J. Fuller } \
393c283839dSLandon J. Fuller \
394c283839dSLandon J. Fuller error = bhnd_nvram_plist_get_encoded(props, name, \
395c283839dSLandon J. Fuller (_dest), sizeof(*(_dest)), \
396c283839dSLandon J. Fuller BHND_NVRAM_TYPE_ ##_type); \
397c283839dSLandon J. Fuller if (error) { \
398c283839dSLandon J. Fuller BHND_NV_LOG("error reading required header " \
399c283839dSLandon J. Fuller "%s property: %d\n", name, error); \
400c283839dSLandon J. Fuller return (EFTYPE); \
401c283839dSLandon J. Fuller } \
402c283839dSLandon J. Fuller } while (0)
403c283839dSLandon J. Fuller
404c283839dSLandon J. Fuller PROPS_GET_HDRVAR(SDRAM_NCDL, &sdram_ncdl, UINT32);
405c283839dSLandon J. Fuller PROPS_GET_HDRVAR(CFG0_SDRAM_INIT, &sdram_init, UINT16);
406c283839dSLandon J. Fuller PROPS_GET_HDRVAR(CFG1_SDRAM_CFG, &sdram_cfg, UINT16);
407c283839dSLandon J. Fuller PROPS_GET_HDRVAR(CFG1_SDRAM_REFRESH, &sdram_refresh, UINT16);
408c283839dSLandon J. Fuller
409c283839dSLandon J. Fuller #undef PROPS_GET_HDRVAR
410c283839dSLandon J. Fuller
411c283839dSLandon J. Fuller /* Fetch BCM nvram version from options */
412c283839dSLandon J. Fuller if (options != NULL &&
413c283839dSLandon J. Fuller bhnd_nvram_plist_contains(options, BCM_NVRAM_ENCODE_OPT_VERSION))
414c283839dSLandon J. Fuller {
415c283839dSLandon J. Fuller error = bhnd_nvram_plist_get_uint8(options,
416c283839dSLandon J. Fuller BCM_NVRAM_ENCODE_OPT_VERSION, &bcm_ver);
417c283839dSLandon J. Fuller if (error) {
418c283839dSLandon J. Fuller BHND_NV_LOG("error reading %s uint8 option value: %d\n",
419c283839dSLandon J. Fuller BCM_NVRAM_ENCODE_OPT_VERSION, error);
420c283839dSLandon J. Fuller return (EINVAL);
421c283839dSLandon J. Fuller }
422c283839dSLandon J. Fuller } else {
423c283839dSLandon J. Fuller bcm_ver = BCM_NVRAM_CFG0_VER_DEFAULT;
424c283839dSLandon J. Fuller }
425c283839dSLandon J. Fuller
426c283839dSLandon J. Fuller /* Construct our header */
427c283839dSLandon J. Fuller hdr = (struct bhnd_nvram_bcmhdr) {
428c283839dSLandon J. Fuller .magic = htole32(BCM_NVRAM_MAGIC),
429c283839dSLandon J. Fuller .size = 0,
430c283839dSLandon J. Fuller .cfg0 = 0,
431c283839dSLandon J. Fuller .cfg1 = 0,
432c283839dSLandon J. Fuller .sdram_ncdl = htole32(sdram_ncdl)
433c283839dSLandon J. Fuller };
434c283839dSLandon J. Fuller
435c283839dSLandon J. Fuller hdr.cfg0 = BCM_NVRAM_SET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_CRC, 0x0);
436c283839dSLandon J. Fuller hdr.cfg0 = BCM_NVRAM_SET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_VER, bcm_ver);
437c283839dSLandon J. Fuller hdr.cfg0 = BCM_NVRAM_SET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_SDRAM_INIT,
438c283839dSLandon J. Fuller htole16(sdram_init));
439c283839dSLandon J. Fuller
440c283839dSLandon J. Fuller hdr.cfg1 = BCM_NVRAM_SET_BITS(hdr.cfg1, BCM_NVRAM_CFG1_SDRAM_CFG,
441c283839dSLandon J. Fuller htole16(sdram_cfg));
442c283839dSLandon J. Fuller hdr.cfg1 = BCM_NVRAM_SET_BITS(hdr.cfg1, BCM_NVRAM_CFG1_SDRAM_REFRESH,
443c283839dSLandon J. Fuller htole16(sdram_refresh));
444c283839dSLandon J. Fuller
445c283839dSLandon J. Fuller /* Write the header */
446c283839dSLandon J. Fuller nbytes = sizeof(hdr);
447c283839dSLandon J. Fuller if (limit >= nbytes)
448c283839dSLandon J. Fuller memcpy(outp, &hdr, sizeof(hdr));
449c283839dSLandon J. Fuller
450c283839dSLandon J. Fuller /* Write all properties */
451c283839dSLandon J. Fuller prop = NULL;
452c283839dSLandon J. Fuller while ((prop = bhnd_nvram_plist_next(props, prop)) != NULL) {
453c283839dSLandon J. Fuller const char *name;
454c283839dSLandon J. Fuller char *p;
455c283839dSLandon J. Fuller size_t prop_limit;
456c283839dSLandon J. Fuller size_t name_len, value_len;
457c283839dSLandon J. Fuller
458c283839dSLandon J. Fuller if (outp == NULL || limit < nbytes) {
459c283839dSLandon J. Fuller p = NULL;
460c283839dSLandon J. Fuller prop_limit = 0;
461c283839dSLandon J. Fuller } else {
462c283839dSLandon J. Fuller p = ((char *)outp) + nbytes;
463c283839dSLandon J. Fuller prop_limit = limit - nbytes;
464c283839dSLandon J. Fuller }
465c283839dSLandon J. Fuller
466c283839dSLandon J. Fuller /* Fetch and write name + '=' to output */
467c283839dSLandon J. Fuller name = bhnd_nvram_prop_name(prop);
468c283839dSLandon J. Fuller name_len = strlen(name) + 1;
469c283839dSLandon J. Fuller
470c283839dSLandon J. Fuller if (prop_limit > name_len) {
471c283839dSLandon J. Fuller memcpy(p, name, name_len - 1);
472c283839dSLandon J. Fuller p[name_len - 1] = '=';
473c283839dSLandon J. Fuller
474c283839dSLandon J. Fuller prop_limit -= name_len;
475c283839dSLandon J. Fuller p += name_len;
476c283839dSLandon J. Fuller } else {
477c283839dSLandon J. Fuller prop_limit = 0;
478c283839dSLandon J. Fuller p = NULL;
479c283839dSLandon J. Fuller }
480c283839dSLandon J. Fuller
481c283839dSLandon J. Fuller /* Advance byte count */
482c283839dSLandon J. Fuller if (SIZE_MAX - nbytes < name_len)
483c283839dSLandon J. Fuller return (EFTYPE); /* would overflow size_t */
484c283839dSLandon J. Fuller
485c283839dSLandon J. Fuller nbytes += name_len;
486c283839dSLandon J. Fuller
487c283839dSLandon J. Fuller /* Attempt to write NUL-terminated value to output */
488c283839dSLandon J. Fuller value_len = prop_limit;
489c283839dSLandon J. Fuller error = bhnd_nvram_prop_encode(prop, p, &value_len,
490c283839dSLandon J. Fuller BHND_NVRAM_TYPE_STRING);
491c283839dSLandon J. Fuller
492c283839dSLandon J. Fuller /* If encoding failed for any reason other than ENOMEM (which
493c283839dSLandon J. Fuller * we'll detect and report after encoding all properties),
494c283839dSLandon J. Fuller * return immediately */
495c283839dSLandon J. Fuller if (error && error != ENOMEM) {
496c283839dSLandon J. Fuller BHND_NV_LOG("error serializing %s to required type "
497c283839dSLandon J. Fuller "%s: %d\n", name,
498c283839dSLandon J. Fuller bhnd_nvram_type_name(BHND_NVRAM_TYPE_STRING),
499c283839dSLandon J. Fuller error);
500c283839dSLandon J. Fuller return (error);
501c283839dSLandon J. Fuller }
502c283839dSLandon J. Fuller
503c283839dSLandon J. Fuller /* Advance byte count */
504c283839dSLandon J. Fuller if (SIZE_MAX - nbytes < value_len)
505c283839dSLandon J. Fuller return (EFTYPE); /* would overflow size_t */
506c283839dSLandon J. Fuller
507c283839dSLandon J. Fuller nbytes += value_len;
508c283839dSLandon J. Fuller }
509c283839dSLandon J. Fuller
510c283839dSLandon J. Fuller /* Write terminating '\0' */
511c283839dSLandon J. Fuller if (limit > nbytes)
512c283839dSLandon J. Fuller *((char *)outp + nbytes) = '\0';
513c283839dSLandon J. Fuller
514c283839dSLandon J. Fuller if (nbytes == SIZE_MAX)
515c283839dSLandon J. Fuller return (EFTYPE); /* would overflow size_t */
516c283839dSLandon J. Fuller else
517c283839dSLandon J. Fuller nbytes++;
518c283839dSLandon J. Fuller
519c283839dSLandon J. Fuller /* Update header length; this must fit within the header's 32-bit size
520c283839dSLandon J. Fuller * field */
521c283839dSLandon J. Fuller if (nbytes <= UINT32_MAX) {
522c283839dSLandon J. Fuller hdr.size = (uint32_t)nbytes;
523c283839dSLandon J. Fuller } else {
524c283839dSLandon J. Fuller BHND_NV_LOG("size %zu exceeds maximum supported size of %u "
525c283839dSLandon J. Fuller "bytes\n", nbytes, UINT32_MAX);
526c283839dSLandon J. Fuller return (EFTYPE);
527c283839dSLandon J. Fuller }
528c283839dSLandon J. Fuller
529c283839dSLandon J. Fuller /* Provide required length */
530c283839dSLandon J. Fuller *olen = nbytes;
531c283839dSLandon J. Fuller if (limit < *olen) {
532c283839dSLandon J. Fuller if (outp == NULL)
533c283839dSLandon J. Fuller return (0);
534c283839dSLandon J. Fuller
535c283839dSLandon J. Fuller return (ENOMEM);
536c283839dSLandon J. Fuller }
537c283839dSLandon J. Fuller
538c283839dSLandon J. Fuller /* Calculate the CRC value */
539c283839dSLandon J. Fuller BHND_NV_ASSERT(nbytes >= BCM_NVRAM_CRC_SKIP, ("invalid output size"));
540c283839dSLandon J. Fuller crc8 = bhnd_nvram_crc8((uint8_t *)outp + BCM_NVRAM_CRC_SKIP,
541c283839dSLandon J. Fuller nbytes - BCM_NVRAM_CRC_SKIP, BHND_NVRAM_CRC8_INITIAL);
542c283839dSLandon J. Fuller
543c283839dSLandon J. Fuller /* Update CRC and write the finalized header */
544c283839dSLandon J. Fuller BHND_NV_ASSERT(nbytes >= sizeof(hdr), ("invalid output size"));
545c283839dSLandon J. Fuller hdr.cfg0 = BCM_NVRAM_SET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_CRC, crc8);
546c283839dSLandon J. Fuller memcpy(outp, &hdr, sizeof(hdr));
547c283839dSLandon J. Fuller
548c283839dSLandon J. Fuller return (0);
549c283839dSLandon J. Fuller }
550c283839dSLandon J. Fuller
55177cb4d3eSLandon J. Fuller /**
55277cb4d3eSLandon J. Fuller * Initialize @p bcm with the provided NVRAM data mapped by @p src.
55377cb4d3eSLandon J. Fuller *
55477cb4d3eSLandon J. Fuller * @param bcm A newly allocated data instance.
55577cb4d3eSLandon J. Fuller */
55677cb4d3eSLandon J. Fuller static int
bhnd_nvram_bcm_init(struct bhnd_nvram_bcm * bcm,struct bhnd_nvram_io * src)55777cb4d3eSLandon J. Fuller bhnd_nvram_bcm_init(struct bhnd_nvram_bcm *bcm, struct bhnd_nvram_io *src)
55877cb4d3eSLandon J. Fuller {
55977cb4d3eSLandon J. Fuller struct bhnd_nvram_bcmhdr hdr;
56077cb4d3eSLandon J. Fuller uint8_t *p;
56177cb4d3eSLandon J. Fuller void *ptr;
56277cb4d3eSLandon J. Fuller size_t io_offset, io_size;
563a7c43ebdSLandon J. Fuller uint8_t crc, valid, bcm_ver;
56477cb4d3eSLandon J. Fuller int error;
56577cb4d3eSLandon J. Fuller
56677cb4d3eSLandon J. Fuller if ((error = bhnd_nvram_io_read(src, 0x0, &hdr, sizeof(hdr))))
56777cb4d3eSLandon J. Fuller return (error);
56877cb4d3eSLandon J. Fuller
56977cb4d3eSLandon J. Fuller if (le32toh(hdr.magic) != BCM_NVRAM_MAGIC)
57077cb4d3eSLandon J. Fuller return (ENXIO);
57177cb4d3eSLandon J. Fuller
57277cb4d3eSLandon J. Fuller /* Fetch the actual NVRAM image size */
57377cb4d3eSLandon J. Fuller io_size = le32toh(hdr.size);
57477cb4d3eSLandon J. Fuller if (io_size < sizeof(hdr)) {
57577cb4d3eSLandon J. Fuller /* The header size must include the header itself */
57677cb4d3eSLandon J. Fuller BHND_NV_LOG("corrupt header size: %zu\n", io_size);
57777cb4d3eSLandon J. Fuller return (EINVAL);
57877cb4d3eSLandon J. Fuller }
57977cb4d3eSLandon J. Fuller
58077cb4d3eSLandon J. Fuller if (io_size > bhnd_nvram_io_getsize(src)) {
58177cb4d3eSLandon J. Fuller BHND_NV_LOG("header size %zu exceeds input size %zu\n",
58277cb4d3eSLandon J. Fuller io_size, bhnd_nvram_io_getsize(src));
58377cb4d3eSLandon J. Fuller return (EINVAL);
58477cb4d3eSLandon J. Fuller }
58577cb4d3eSLandon J. Fuller
58677cb4d3eSLandon J. Fuller /* Allocate a buffer large enough to hold the NVRAM image, and
58777cb4d3eSLandon J. Fuller * an extra EOF-signaling NUL (on the chance it's missing from the
58877cb4d3eSLandon J. Fuller * source data) */
58977cb4d3eSLandon J. Fuller if (io_size == SIZE_MAX)
59077cb4d3eSLandon J. Fuller return (ENOMEM);
59177cb4d3eSLandon J. Fuller
59277cb4d3eSLandon J. Fuller bcm->data = bhnd_nvram_iobuf_empty(io_size, io_size + 1);
59377cb4d3eSLandon J. Fuller if (bcm->data == NULL)
59477cb4d3eSLandon J. Fuller return (ENOMEM);
59577cb4d3eSLandon J. Fuller
59677cb4d3eSLandon J. Fuller /* Fetch a pointer into our backing buffer and copy in the
59777cb4d3eSLandon J. Fuller * NVRAM image. */
59877cb4d3eSLandon J. Fuller error = bhnd_nvram_io_write_ptr(bcm->data, 0x0, &ptr, io_size, NULL);
59977cb4d3eSLandon J. Fuller if (error)
60077cb4d3eSLandon J. Fuller return (error);
60177cb4d3eSLandon J. Fuller
60277cb4d3eSLandon J. Fuller p = ptr;
60377cb4d3eSLandon J. Fuller if ((error = bhnd_nvram_io_read(src, 0x0, p, io_size)))
60477cb4d3eSLandon J. Fuller return (error);
60577cb4d3eSLandon J. Fuller
60677cb4d3eSLandon J. Fuller /* Verify the CRC */
60777cb4d3eSLandon J. Fuller valid = BCM_NVRAM_GET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_CRC);
60877cb4d3eSLandon J. Fuller crc = bhnd_nvram_crc8(p + BCM_NVRAM_CRC_SKIP,
60977cb4d3eSLandon J. Fuller io_size - BCM_NVRAM_CRC_SKIP, BHND_NVRAM_CRC8_INITIAL);
61077cb4d3eSLandon J. Fuller
61177cb4d3eSLandon J. Fuller if (crc != valid) {
61277cb4d3eSLandon J. Fuller BHND_NV_LOG("warning: NVRAM CRC error (crc=%#hhx, "
61377cb4d3eSLandon J. Fuller "expected=%hhx)\n", crc, valid);
61477cb4d3eSLandon J. Fuller }
61577cb4d3eSLandon J. Fuller
61677cb4d3eSLandon J. Fuller /* Populate header variable definitions */
61777cb4d3eSLandon J. Fuller #define BCM_READ_HDR_VAR(_name, _dest, _swap) do { \
61877cb4d3eSLandon J. Fuller struct bhnd_nvram_bcm_hvar *data; \
61977cb4d3eSLandon J. Fuller data = bhnd_nvram_bcm_gethdrvar(bcm, _name ##_VAR); \
62077cb4d3eSLandon J. Fuller BHND_NV_ASSERT(data != NULL, \
62177cb4d3eSLandon J. Fuller ("no such header variable: " __STRING(_name))); \
62277cb4d3eSLandon J. Fuller \
62377cb4d3eSLandon J. Fuller \
62477cb4d3eSLandon J. Fuller data->value. _dest = _swap(BCM_NVRAM_GET_BITS( \
62577cb4d3eSLandon J. Fuller hdr. _name ## _FIELD, _name)); \
62677cb4d3eSLandon J. Fuller } while(0)
62777cb4d3eSLandon J. Fuller
62877cb4d3eSLandon J. Fuller BCM_READ_HDR_VAR(BCM_NVRAM_CFG0_SDRAM_INIT, u16, le16toh);
62977cb4d3eSLandon J. Fuller BCM_READ_HDR_VAR(BCM_NVRAM_CFG1_SDRAM_CFG, u16, le16toh);
63077cb4d3eSLandon J. Fuller BCM_READ_HDR_VAR(BCM_NVRAM_CFG1_SDRAM_REFRESH, u16, le16toh);
63177cb4d3eSLandon J. Fuller BCM_READ_HDR_VAR(BCM_NVRAM_SDRAM_NCDL, u32, le32toh);
63277cb4d3eSLandon J. Fuller
63377cb4d3eSLandon J. Fuller _Static_assert(nitems(bcm->hvars) == 4, "missing initialization for"
63477cb4d3eSLandon J. Fuller "NVRAM header variable(s)");
63577cb4d3eSLandon J. Fuller
63677cb4d3eSLandon J. Fuller #undef BCM_READ_HDR_VAR
63777cb4d3eSLandon J. Fuller
63877cb4d3eSLandon J. Fuller /* Process the buffer */
63977cb4d3eSLandon J. Fuller bcm->count = 0;
64077cb4d3eSLandon J. Fuller io_offset = sizeof(hdr);
64177cb4d3eSLandon J. Fuller while (io_offset < io_size) {
64277cb4d3eSLandon J. Fuller char *envp;
64377cb4d3eSLandon J. Fuller const char *name, *value;
64477cb4d3eSLandon J. Fuller size_t envp_len;
64577cb4d3eSLandon J. Fuller size_t name_len, value_len;
64677cb4d3eSLandon J. Fuller
64777cb4d3eSLandon J. Fuller /* Parse the key=value string */
64877cb4d3eSLandon J. Fuller envp = (char *) (p + io_offset);
64977cb4d3eSLandon J. Fuller envp_len = strnlen(envp, io_size - io_offset);
65077cb4d3eSLandon J. Fuller error = bhnd_nvram_parse_env(envp, envp_len, '=', &name,
65177cb4d3eSLandon J. Fuller &name_len, &value, &value_len);
65277cb4d3eSLandon J. Fuller if (error) {
65377cb4d3eSLandon J. Fuller BHND_NV_LOG("error parsing envp at offset %#zx: %d\n",
65477cb4d3eSLandon J. Fuller io_offset, error);
65577cb4d3eSLandon J. Fuller return (error);
65677cb4d3eSLandon J. Fuller }
65777cb4d3eSLandon J. Fuller
65877cb4d3eSLandon J. Fuller /* Insert a '\0' character, replacing the '=' delimiter and
65977cb4d3eSLandon J. Fuller * allowing us to vend references directly to the variable
66077cb4d3eSLandon J. Fuller * name */
66177cb4d3eSLandon J. Fuller *(envp + name_len) = '\0';
66277cb4d3eSLandon J. Fuller
66377cb4d3eSLandon J. Fuller /* Record any NVRAM variables that mirror our header variables.
66477cb4d3eSLandon J. Fuller * This is a brute-force search -- for the amount of data we're
66577cb4d3eSLandon J. Fuller * operating on, it shouldn't be an issue. */
66677cb4d3eSLandon J. Fuller for (size_t i = 0; i < nitems(bcm->hvars); i++) {
66777cb4d3eSLandon J. Fuller struct bhnd_nvram_bcm_hvar *hvar;
66877cb4d3eSLandon J. Fuller union bhnd_nvram_bcm_hvar_value hval;
66977cb4d3eSLandon J. Fuller size_t hval_len;
67077cb4d3eSLandon J. Fuller
67177cb4d3eSLandon J. Fuller hvar = &bcm->hvars[i];
67277cb4d3eSLandon J. Fuller
67377cb4d3eSLandon J. Fuller /* Already matched? */
67477cb4d3eSLandon J. Fuller if (hvar->envp != NULL)
67577cb4d3eSLandon J. Fuller continue;
67677cb4d3eSLandon J. Fuller
67777cb4d3eSLandon J. Fuller /* Name matches? */
67877cb4d3eSLandon J. Fuller if ((strcmp(name, hvar->name)) != 0)
67977cb4d3eSLandon J. Fuller continue;
68077cb4d3eSLandon J. Fuller
68177cb4d3eSLandon J. Fuller /* Save pointer to mirrored envp */
68277cb4d3eSLandon J. Fuller hvar->envp = envp;
68377cb4d3eSLandon J. Fuller
68477cb4d3eSLandon J. Fuller /* Check for stale value */
68577cb4d3eSLandon J. Fuller hval_len = sizeof(hval);
68677cb4d3eSLandon J. Fuller error = bhnd_nvram_value_coerce(value, value_len,
68777cb4d3eSLandon J. Fuller BHND_NVRAM_TYPE_STRING, &hval, &hval_len,
68877cb4d3eSLandon J. Fuller hvar->type);
68977cb4d3eSLandon J. Fuller if (error) {
69077cb4d3eSLandon J. Fuller /* If parsing fails, we can likely only make
69177cb4d3eSLandon J. Fuller * things worse by trying to synchronize the
69277cb4d3eSLandon J. Fuller * variables */
69377cb4d3eSLandon J. Fuller BHND_NV_LOG("error parsing header variable "
69477cb4d3eSLandon J. Fuller "'%s=%s': %d\n", name, value, error);
69577cb4d3eSLandon J. Fuller } else if (hval_len != hvar->len) {
69677cb4d3eSLandon J. Fuller hvar->stale = true;
69777cb4d3eSLandon J. Fuller } else if (memcmp(&hval, &hvar->value, hval_len) != 0) {
69877cb4d3eSLandon J. Fuller hvar->stale = true;
69977cb4d3eSLandon J. Fuller }
70077cb4d3eSLandon J. Fuller }
70177cb4d3eSLandon J. Fuller
70277cb4d3eSLandon J. Fuller /* Seek past the value's terminating '\0' */
70377cb4d3eSLandon J. Fuller io_offset += envp_len;
70477cb4d3eSLandon J. Fuller if (io_offset == io_size) {
70577cb4d3eSLandon J. Fuller BHND_NV_LOG("missing terminating NUL at offset %#zx\n",
70677cb4d3eSLandon J. Fuller io_offset);
70777cb4d3eSLandon J. Fuller return (EINVAL);
70877cb4d3eSLandon J. Fuller }
70977cb4d3eSLandon J. Fuller
71077cb4d3eSLandon J. Fuller if (*(p + io_offset) != '\0') {
71177cb4d3eSLandon J. Fuller BHND_NV_LOG("invalid terminator '%#hhx' at offset "
71277cb4d3eSLandon J. Fuller "%#zx\n", *(p + io_offset), io_offset);
71377cb4d3eSLandon J. Fuller return (EINVAL);
71477cb4d3eSLandon J. Fuller }
71577cb4d3eSLandon J. Fuller
71677cb4d3eSLandon J. Fuller /* Update variable count */
71777cb4d3eSLandon J. Fuller bcm->count++;
71877cb4d3eSLandon J. Fuller
71977cb4d3eSLandon J. Fuller /* Seek to the next record */
72077cb4d3eSLandon J. Fuller if (++io_offset == io_size) {
72177cb4d3eSLandon J. Fuller char ch;
72277cb4d3eSLandon J. Fuller
72377cb4d3eSLandon J. Fuller /* Hit EOF without finding a terminating NUL
72477cb4d3eSLandon J. Fuller * byte; we need to grow our buffer and append
72577cb4d3eSLandon J. Fuller * it */
72677cb4d3eSLandon J. Fuller io_size++;
72777cb4d3eSLandon J. Fuller if ((error = bhnd_nvram_io_setsize(bcm->data, io_size)))
72877cb4d3eSLandon J. Fuller return (error);
72977cb4d3eSLandon J. Fuller
73077cb4d3eSLandon J. Fuller /* Write NUL byte */
73177cb4d3eSLandon J. Fuller ch = '\0';
73277cb4d3eSLandon J. Fuller error = bhnd_nvram_io_write(bcm->data, io_size-1, &ch,
73377cb4d3eSLandon J. Fuller sizeof(ch));
73477cb4d3eSLandon J. Fuller if (error)
73577cb4d3eSLandon J. Fuller return (error);
73677cb4d3eSLandon J. Fuller }
73777cb4d3eSLandon J. Fuller
73877cb4d3eSLandon J. Fuller /* Check for explicit EOF (encoded as a single empty NUL
73977cb4d3eSLandon J. Fuller * terminated string) */
74077cb4d3eSLandon J. Fuller if (*(p + io_offset) == '\0')
74177cb4d3eSLandon J. Fuller break;
74277cb4d3eSLandon J. Fuller }
74377cb4d3eSLandon J. Fuller
74477cb4d3eSLandon J. Fuller /* Add non-mirrored header variables to total count variable */
74577cb4d3eSLandon J. Fuller for (size_t i = 0; i < nitems(bcm->hvars); i++) {
74677cb4d3eSLandon J. Fuller if (bcm->hvars[i].envp == NULL)
74777cb4d3eSLandon J. Fuller bcm->count++;
74877cb4d3eSLandon J. Fuller }
74977cb4d3eSLandon J. Fuller
750a7c43ebdSLandon J. Fuller /* Populate serialization options from our header */
751a7c43ebdSLandon J. Fuller bcm_ver = BCM_NVRAM_GET_BITS(hdr.cfg0, BCM_NVRAM_CFG0_VER);
752a7c43ebdSLandon J. Fuller error = bhnd_nvram_plist_append_bytes(bcm->opts,
753a7c43ebdSLandon J. Fuller BCM_NVRAM_ENCODE_OPT_VERSION, &bcm_ver, sizeof(bcm_ver),
754a7c43ebdSLandon J. Fuller BHND_NVRAM_TYPE_UINT8);
755a7c43ebdSLandon J. Fuller if (error)
756a7c43ebdSLandon J. Fuller return (error);
757a7c43ebdSLandon J. Fuller
75877cb4d3eSLandon J. Fuller return (0);
75977cb4d3eSLandon J. Fuller }
76077cb4d3eSLandon J. Fuller
76177cb4d3eSLandon J. Fuller static int
bhnd_nvram_bcm_new(struct bhnd_nvram_data * nv,struct bhnd_nvram_io * io)76277cb4d3eSLandon J. Fuller bhnd_nvram_bcm_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io)
76377cb4d3eSLandon J. Fuller {
76477cb4d3eSLandon J. Fuller struct bhnd_nvram_bcm *bcm;
76577cb4d3eSLandon J. Fuller int error;
76677cb4d3eSLandon J. Fuller
76777cb4d3eSLandon J. Fuller bcm = (struct bhnd_nvram_bcm *)nv;
76877cb4d3eSLandon J. Fuller
76977cb4d3eSLandon J. Fuller /* Populate default BCM mirrored header variable set */
77077cb4d3eSLandon J. Fuller _Static_assert(sizeof(bcm->hvars) == sizeof(bhnd_nvram_bcm_hvars),
77177cb4d3eSLandon J. Fuller "hvar declarations must match bhnd_nvram_bcm_hvars template");
77277cb4d3eSLandon J. Fuller memcpy(bcm->hvars, bhnd_nvram_bcm_hvars, sizeof(bcm->hvars));
77377cb4d3eSLandon J. Fuller
774a7c43ebdSLandon J. Fuller /* Allocate (empty) option list, to be populated by
775a7c43ebdSLandon J. Fuller * bhnd_nvram_bcm_init() */
776a7c43ebdSLandon J. Fuller bcm->opts = bhnd_nvram_plist_new();
777a7c43ebdSLandon J. Fuller if (bcm->opts == NULL)
778a7c43ebdSLandon J. Fuller return (ENOMEM);
779a7c43ebdSLandon J. Fuller
78077cb4d3eSLandon J. Fuller /* Parse the BCM input data and initialize our backing
78177cb4d3eSLandon J. Fuller * data representation */
78277cb4d3eSLandon J. Fuller if ((error = bhnd_nvram_bcm_init(bcm, io))) {
78377cb4d3eSLandon J. Fuller bhnd_nvram_bcm_free(nv);
78477cb4d3eSLandon J. Fuller return (error);
78577cb4d3eSLandon J. Fuller }
78677cb4d3eSLandon J. Fuller
78777cb4d3eSLandon J. Fuller return (0);
78877cb4d3eSLandon J. Fuller }
78977cb4d3eSLandon J. Fuller
79077cb4d3eSLandon J. Fuller static void
bhnd_nvram_bcm_free(struct bhnd_nvram_data * nv)79177cb4d3eSLandon J. Fuller bhnd_nvram_bcm_free(struct bhnd_nvram_data *nv)
79277cb4d3eSLandon J. Fuller {
79377cb4d3eSLandon J. Fuller struct bhnd_nvram_bcm *bcm = (struct bhnd_nvram_bcm *)nv;
79477cb4d3eSLandon J. Fuller
79577cb4d3eSLandon J. Fuller if (bcm->data != NULL)
79677cb4d3eSLandon J. Fuller bhnd_nvram_io_free(bcm->data);
797a7c43ebdSLandon J. Fuller
798a7c43ebdSLandon J. Fuller if (bcm->opts != NULL)
799a7c43ebdSLandon J. Fuller bhnd_nvram_plist_release(bcm->opts);
80077cb4d3eSLandon J. Fuller }
80177cb4d3eSLandon J. Fuller
80277cb4d3eSLandon J. Fuller size_t
bhnd_nvram_bcm_count(struct bhnd_nvram_data * nv)80377cb4d3eSLandon J. Fuller bhnd_nvram_bcm_count(struct bhnd_nvram_data *nv)
80477cb4d3eSLandon J. Fuller {
80577cb4d3eSLandon J. Fuller struct bhnd_nvram_bcm *bcm = (struct bhnd_nvram_bcm *)nv;
80677cb4d3eSLandon J. Fuller return (bcm->count);
80777cb4d3eSLandon J. Fuller }
80877cb4d3eSLandon J. Fuller
809a7c43ebdSLandon J. Fuller static bhnd_nvram_plist *
bhnd_nvram_bcm_options(struct bhnd_nvram_data * nv)810a7c43ebdSLandon J. Fuller bhnd_nvram_bcm_options(struct bhnd_nvram_data *nv)
811a7c43ebdSLandon J. Fuller {
812a7c43ebdSLandon J. Fuller struct bhnd_nvram_bcm *bcm = (struct bhnd_nvram_bcm *)nv;
813a7c43ebdSLandon J. Fuller return (bcm->opts);
814a7c43ebdSLandon J. Fuller }
815a7c43ebdSLandon J. Fuller
81677cb4d3eSLandon J. Fuller static uint32_t
bhnd_nvram_bcm_caps(struct bhnd_nvram_data * nv)81777cb4d3eSLandon J. Fuller bhnd_nvram_bcm_caps(struct bhnd_nvram_data *nv)
81877cb4d3eSLandon J. Fuller {
81977cb4d3eSLandon J. Fuller return (BHND_NVRAM_DATA_CAP_READ_PTR|BHND_NVRAM_DATA_CAP_DEVPATHS);
82077cb4d3eSLandon J. Fuller }
82177cb4d3eSLandon J. Fuller
82277cb4d3eSLandon J. Fuller static const char *
bhnd_nvram_bcm_next(struct bhnd_nvram_data * nv,void ** cookiep)82377cb4d3eSLandon J. Fuller bhnd_nvram_bcm_next(struct bhnd_nvram_data *nv, void **cookiep)
82477cb4d3eSLandon J. Fuller {
82577cb4d3eSLandon J. Fuller struct bhnd_nvram_bcm *bcm;
82677cb4d3eSLandon J. Fuller struct bhnd_nvram_bcm_hvar *hvar, *hvar_next;
82777cb4d3eSLandon J. Fuller const void *ptr;
82877cb4d3eSLandon J. Fuller const char *envp, *basep;
82977cb4d3eSLandon J. Fuller size_t io_size, io_offset;
83077cb4d3eSLandon J. Fuller int error;
83177cb4d3eSLandon J. Fuller
83277cb4d3eSLandon J. Fuller bcm = (struct bhnd_nvram_bcm *)nv;
83377cb4d3eSLandon J. Fuller
83477cb4d3eSLandon J. Fuller io_offset = sizeof(struct bhnd_nvram_bcmhdr);
83577cb4d3eSLandon J. Fuller io_size = bhnd_nvram_io_getsize(bcm->data) - io_offset;
83677cb4d3eSLandon J. Fuller
83777cb4d3eSLandon J. Fuller /* Map backing buffer */
83877cb4d3eSLandon J. Fuller error = bhnd_nvram_io_read_ptr(bcm->data, io_offset, &ptr, io_size,
83977cb4d3eSLandon J. Fuller NULL);
84077cb4d3eSLandon J. Fuller if (error) {
84177cb4d3eSLandon J. Fuller BHND_NV_LOG("error mapping backing buffer: %d\n", error);
84277cb4d3eSLandon J. Fuller return (NULL);
84377cb4d3eSLandon J. Fuller }
84477cb4d3eSLandon J. Fuller
84577cb4d3eSLandon J. Fuller basep = ptr;
84677cb4d3eSLandon J. Fuller
84777cb4d3eSLandon J. Fuller /* If cookiep pointers into our header variable array, handle as header
84877cb4d3eSLandon J. Fuller * variable iteration. */
84977cb4d3eSLandon J. Fuller hvar = bhnd_nvram_bcm_to_hdrvar(bcm, *cookiep);
85077cb4d3eSLandon J. Fuller if (hvar != NULL) {
85177cb4d3eSLandon J. Fuller size_t idx;
85277cb4d3eSLandon J. Fuller
85377cb4d3eSLandon J. Fuller /* Advance to next entry, if any */
85477cb4d3eSLandon J. Fuller idx = bhnd_nvram_bcm_hdrvar_index(bcm, hvar) + 1;
85577cb4d3eSLandon J. Fuller
85677cb4d3eSLandon J. Fuller /* Find the next header-defined variable that isn't defined in
85777cb4d3eSLandon J. Fuller * the NVRAM data, start iteration there */
85877cb4d3eSLandon J. Fuller for (size_t i = idx; i < nitems(bcm->hvars); i++) {
85977cb4d3eSLandon J. Fuller hvar_next = &bcm->hvars[i];
86077cb4d3eSLandon J. Fuller if (hvar_next->envp != NULL && !hvar_next->stale)
86177cb4d3eSLandon J. Fuller continue;
86277cb4d3eSLandon J. Fuller
86377cb4d3eSLandon J. Fuller *cookiep = hvar_next;
86477cb4d3eSLandon J. Fuller return (hvar_next->name);
86577cb4d3eSLandon J. Fuller }
86677cb4d3eSLandon J. Fuller
86777cb4d3eSLandon J. Fuller /* No further header-defined variables; iteration
86877cb4d3eSLandon J. Fuller * complete */
86977cb4d3eSLandon J. Fuller return (NULL);
87077cb4d3eSLandon J. Fuller }
87177cb4d3eSLandon J. Fuller
87277cb4d3eSLandon J. Fuller /* Handle standard NVRAM data iteration */
87377cb4d3eSLandon J. Fuller if (*cookiep == NULL) {
87477cb4d3eSLandon J. Fuller /* Start at the first NVRAM data record */
87577cb4d3eSLandon J. Fuller envp = basep;
87677cb4d3eSLandon J. Fuller } else {
87777cb4d3eSLandon J. Fuller /* Seek to next record */
87877cb4d3eSLandon J. Fuller envp = *cookiep;
87977cb4d3eSLandon J. Fuller envp += strlen(envp) + 1; /* key + '\0' */
88077cb4d3eSLandon J. Fuller envp += strlen(envp) + 1; /* value + '\0' */
88177cb4d3eSLandon J. Fuller }
88277cb4d3eSLandon J. Fuller
88377cb4d3eSLandon J. Fuller /*
88477cb4d3eSLandon J. Fuller * Skip entries that have an existing header variable entry that takes
88577cb4d3eSLandon J. Fuller * precedence over the NVRAM data value.
88677cb4d3eSLandon J. Fuller *
88777cb4d3eSLandon J. Fuller * The header's value will be provided when performing header variable
88877cb4d3eSLandon J. Fuller * iteration
88977cb4d3eSLandon J. Fuller */
89077cb4d3eSLandon J. Fuller while ((size_t)(envp - basep) < io_size && *envp != '\0') {
89177cb4d3eSLandon J. Fuller /* Locate corresponding header variable */
89277cb4d3eSLandon J. Fuller hvar = NULL;
89377cb4d3eSLandon J. Fuller for (size_t i = 0; i < nitems(bcm->hvars); i++) {
89477cb4d3eSLandon J. Fuller if (bcm->hvars[i].envp != envp)
89577cb4d3eSLandon J. Fuller continue;
89677cb4d3eSLandon J. Fuller
89777cb4d3eSLandon J. Fuller hvar = &bcm->hvars[i];
89877cb4d3eSLandon J. Fuller break;
89977cb4d3eSLandon J. Fuller }
90077cb4d3eSLandon J. Fuller
90177cb4d3eSLandon J. Fuller /* If no corresponding hvar entry, or the entry does not take
90277cb4d3eSLandon J. Fuller * precedence over this NVRAM value, we can safely return this
90377cb4d3eSLandon J. Fuller * value as-is. */
90477cb4d3eSLandon J. Fuller if (hvar == NULL || !hvar->stale)
90577cb4d3eSLandon J. Fuller break;
90677cb4d3eSLandon J. Fuller
90777cb4d3eSLandon J. Fuller /* Seek to next record */
90877cb4d3eSLandon J. Fuller envp += strlen(envp) + 1; /* key + '\0' */
90977cb4d3eSLandon J. Fuller envp += strlen(envp) + 1; /* value + '\0' */
91077cb4d3eSLandon J. Fuller }
91177cb4d3eSLandon J. Fuller
91277cb4d3eSLandon J. Fuller /* On NVRAM data EOF, try switching to header variables */
91377cb4d3eSLandon J. Fuller if ((size_t)(envp - basep) == io_size || *envp == '\0') {
91477cb4d3eSLandon J. Fuller /* Find first valid header variable */
91577cb4d3eSLandon J. Fuller for (size_t i = 0; i < nitems(bcm->hvars); i++) {
91677cb4d3eSLandon J. Fuller if (bcm->hvars[i].envp != NULL)
91777cb4d3eSLandon J. Fuller continue;
91877cb4d3eSLandon J. Fuller
91977cb4d3eSLandon J. Fuller *cookiep = &bcm->hvars[i];
92077cb4d3eSLandon J. Fuller return (bcm->hvars[i].name);
92177cb4d3eSLandon J. Fuller }
92277cb4d3eSLandon J. Fuller
92377cb4d3eSLandon J. Fuller /* No header variables */
92477cb4d3eSLandon J. Fuller return (NULL);
92577cb4d3eSLandon J. Fuller }
92677cb4d3eSLandon J. Fuller
92719be09f3SLandon J. Fuller *cookiep = __DECONST(void *, envp);
92877cb4d3eSLandon J. Fuller return (envp);
92977cb4d3eSLandon J. Fuller }
93077cb4d3eSLandon J. Fuller
93177cb4d3eSLandon J. Fuller static void *
bhnd_nvram_bcm_find(struct bhnd_nvram_data * nv,const char * name)93277cb4d3eSLandon J. Fuller bhnd_nvram_bcm_find(struct bhnd_nvram_data *nv, const char *name)
93377cb4d3eSLandon J. Fuller {
93477cb4d3eSLandon J. Fuller return (bhnd_nvram_data_generic_find(nv, name));
93577cb4d3eSLandon J. Fuller }
93677cb4d3eSLandon J. Fuller
93777cb4d3eSLandon J. Fuller static int
bhnd_nvram_bcm_getvar_order(struct bhnd_nvram_data * nv,void * cookiep1,void * cookiep2)93819be09f3SLandon J. Fuller bhnd_nvram_bcm_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1,
93919be09f3SLandon J. Fuller void *cookiep2)
94019be09f3SLandon J. Fuller {
94119be09f3SLandon J. Fuller struct bhnd_nvram_bcm *bcm;
94219be09f3SLandon J. Fuller struct bhnd_nvram_bcm_hvar *hvar1, *hvar2;
94319be09f3SLandon J. Fuller
94419be09f3SLandon J. Fuller bcm = (struct bhnd_nvram_bcm *)nv;
94519be09f3SLandon J. Fuller
94619be09f3SLandon J. Fuller hvar1 = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep1);
94719be09f3SLandon J. Fuller hvar2 = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep2);
94819be09f3SLandon J. Fuller
94919be09f3SLandon J. Fuller /* Header variables are always ordered below any variables defined
95019be09f3SLandon J. Fuller * in the BCM data */
95119be09f3SLandon J. Fuller if (hvar1 != NULL && hvar2 == NULL) {
95219be09f3SLandon J. Fuller return (1); /* hvar follows non-hvar */
95319be09f3SLandon J. Fuller } else if (hvar1 == NULL && hvar2 != NULL) {
95419be09f3SLandon J. Fuller return (-1); /* non-hvar precedes hvar */
95519be09f3SLandon J. Fuller }
95619be09f3SLandon J. Fuller
95719be09f3SLandon J. Fuller /* Otherwise, both cookies are either hvars or non-hvars. We can
95819be09f3SLandon J. Fuller * safely fall back on pointer order, which will provide a correct
95919be09f3SLandon J. Fuller * ordering matching the behavior of bhnd_nvram_data_next() for
96019be09f3SLandon J. Fuller * both cases */
96119be09f3SLandon J. Fuller if (cookiep1 < cookiep2)
96219be09f3SLandon J. Fuller return (-1);
96319be09f3SLandon J. Fuller
96419be09f3SLandon J. Fuller if (cookiep1 > cookiep2)
96519be09f3SLandon J. Fuller return (1);
96619be09f3SLandon J. Fuller
96719be09f3SLandon J. Fuller return (0);
96819be09f3SLandon J. Fuller }
96919be09f3SLandon J. Fuller
97019be09f3SLandon J. Fuller static int
bhnd_nvram_bcm_getvar(struct bhnd_nvram_data * nv,void * cookiep,void * buf,size_t * len,bhnd_nvram_type type)97177cb4d3eSLandon J. Fuller bhnd_nvram_bcm_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
97277cb4d3eSLandon J. Fuller size_t *len, bhnd_nvram_type type)
97377cb4d3eSLandon J. Fuller {
97477cb4d3eSLandon J. Fuller return (bhnd_nvram_data_generic_rp_getvar(nv, cookiep, buf, len, type));
97577cb4d3eSLandon J. Fuller }
97677cb4d3eSLandon J. Fuller
97719be09f3SLandon J. Fuller static int
bhnd_nvram_bcm_copy_val(struct bhnd_nvram_data * nv,void * cookiep,bhnd_nvram_val ** value)97819be09f3SLandon J. Fuller bhnd_nvram_bcm_copy_val(struct bhnd_nvram_data *nv, void *cookiep,
97919be09f3SLandon J. Fuller bhnd_nvram_val **value)
98019be09f3SLandon J. Fuller {
98119be09f3SLandon J. Fuller return (bhnd_nvram_data_generic_rp_copy_val(nv, cookiep, value));
98219be09f3SLandon J. Fuller }
98319be09f3SLandon J. Fuller
98477cb4d3eSLandon J. Fuller static const void *
bhnd_nvram_bcm_getvar_ptr(struct bhnd_nvram_data * nv,void * cookiep,size_t * len,bhnd_nvram_type * type)98577cb4d3eSLandon J. Fuller bhnd_nvram_bcm_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
98677cb4d3eSLandon J. Fuller size_t *len, bhnd_nvram_type *type)
98777cb4d3eSLandon J. Fuller {
98877cb4d3eSLandon J. Fuller struct bhnd_nvram_bcm *bcm;
98977cb4d3eSLandon J. Fuller struct bhnd_nvram_bcm_hvar *hvar;
99077cb4d3eSLandon J. Fuller const char *envp;
99177cb4d3eSLandon J. Fuller
99277cb4d3eSLandon J. Fuller bcm = (struct bhnd_nvram_bcm *)nv;
99377cb4d3eSLandon J. Fuller
99477cb4d3eSLandon J. Fuller /* Handle header variables */
99577cb4d3eSLandon J. Fuller if ((hvar = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep)) != NULL) {
9969be0790dSLandon J. Fuller BHND_NV_ASSERT(bhnd_nvram_value_check_aligned(&hvar->value,
9979be0790dSLandon J. Fuller hvar->len, hvar->type) == 0, ("value misaligned"));
99877cb4d3eSLandon J. Fuller
99977cb4d3eSLandon J. Fuller *type = hvar->type;
100077cb4d3eSLandon J. Fuller *len = hvar->len;
100177cb4d3eSLandon J. Fuller return (&hvar->value);
100277cb4d3eSLandon J. Fuller }
100377cb4d3eSLandon J. Fuller
100477cb4d3eSLandon J. Fuller /* Cookie points to key\0value\0 -- get the value address */
100577cb4d3eSLandon J. Fuller BHND_NV_ASSERT(cookiep != NULL, ("NULL cookiep"));
100677cb4d3eSLandon J. Fuller
100777cb4d3eSLandon J. Fuller envp = cookiep;
100877cb4d3eSLandon J. Fuller envp += strlen(envp) + 1; /* key + '\0' */
100977cb4d3eSLandon J. Fuller *len = strlen(envp) + 1; /* value + '\0' */
101077cb4d3eSLandon J. Fuller *type = BHND_NVRAM_TYPE_STRING;
101177cb4d3eSLandon J. Fuller
101277cb4d3eSLandon J. Fuller return (envp);
101377cb4d3eSLandon J. Fuller }
101477cb4d3eSLandon J. Fuller
101577cb4d3eSLandon J. Fuller static const char *
bhnd_nvram_bcm_getvar_name(struct bhnd_nvram_data * nv,void * cookiep)101677cb4d3eSLandon J. Fuller bhnd_nvram_bcm_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
101777cb4d3eSLandon J. Fuller {
101877cb4d3eSLandon J. Fuller struct bhnd_nvram_bcm *bcm;
101977cb4d3eSLandon J. Fuller struct bhnd_nvram_bcm_hvar *hvar;
102077cb4d3eSLandon J. Fuller
102177cb4d3eSLandon J. Fuller bcm = (struct bhnd_nvram_bcm *)nv;
102277cb4d3eSLandon J. Fuller
102377cb4d3eSLandon J. Fuller /* Handle header variables */
102477cb4d3eSLandon J. Fuller if ((hvar = bhnd_nvram_bcm_to_hdrvar(bcm, cookiep)) != NULL) {
102577cb4d3eSLandon J. Fuller return (hvar->name);
102677cb4d3eSLandon J. Fuller }
102777cb4d3eSLandon J. Fuller
102877cb4d3eSLandon J. Fuller /* Cookie points to key\0value\0 */
102977cb4d3eSLandon J. Fuller return (cookiep);
103077cb4d3eSLandon J. Fuller }
103177cb4d3eSLandon J. Fuller
103219be09f3SLandon J. Fuller static int
bhnd_nvram_bcm_filter_setvar(struct bhnd_nvram_data * nv,const char * name,bhnd_nvram_val * value,bhnd_nvram_val ** result)103319be09f3SLandon J. Fuller bhnd_nvram_bcm_filter_setvar(struct bhnd_nvram_data *nv, const char *name,
103419be09f3SLandon J. Fuller bhnd_nvram_val *value, bhnd_nvram_val **result)
103519be09f3SLandon J. Fuller {
103619be09f3SLandon J. Fuller bhnd_nvram_val *str;
103719be09f3SLandon J. Fuller int error;
103819be09f3SLandon J. Fuller
103919be09f3SLandon J. Fuller /* Name (trimmed of any path prefix) must be valid */
104019be09f3SLandon J. Fuller if (!bhnd_nvram_validate_name(bhnd_nvram_trim_path_name(name)))
104119be09f3SLandon J. Fuller return (EINVAL);
104219be09f3SLandon J. Fuller
104319be09f3SLandon J. Fuller /* Value must be bcm-formatted string */
104419be09f3SLandon J. Fuller error = bhnd_nvram_val_convert_new(&str, &bhnd_nvram_val_bcm_string_fmt,
104519be09f3SLandon J. Fuller value, BHND_NVRAM_VAL_DYNAMIC);
104619be09f3SLandon J. Fuller if (error)
104719be09f3SLandon J. Fuller return (error);
104819be09f3SLandon J. Fuller
104919be09f3SLandon J. Fuller /* Success. Transfer result ownership to the caller. */
105019be09f3SLandon J. Fuller *result = str;
105119be09f3SLandon J. Fuller return (0);
105219be09f3SLandon J. Fuller }
105319be09f3SLandon J. Fuller
105419be09f3SLandon J. Fuller static int
bhnd_nvram_bcm_filter_unsetvar(struct bhnd_nvram_data * nv,const char * name)105519be09f3SLandon J. Fuller bhnd_nvram_bcm_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name)
105619be09f3SLandon J. Fuller {
105719be09f3SLandon J. Fuller /* We permit deletion of any variable */
105819be09f3SLandon J. Fuller return (0);
105919be09f3SLandon J. Fuller }
106019be09f3SLandon J. Fuller
106177cb4d3eSLandon J. Fuller /**
106277cb4d3eSLandon J. Fuller * Return the internal BCM data reference for a header-defined variable
106377cb4d3eSLandon J. Fuller * with @p name, or NULL if none exists.
106477cb4d3eSLandon J. Fuller */
106577cb4d3eSLandon J. Fuller static struct bhnd_nvram_bcm_hvar *
bhnd_nvram_bcm_gethdrvar(struct bhnd_nvram_bcm * bcm,const char * name)106677cb4d3eSLandon J. Fuller bhnd_nvram_bcm_gethdrvar(struct bhnd_nvram_bcm *bcm, const char *name)
106777cb4d3eSLandon J. Fuller {
106877cb4d3eSLandon J. Fuller for (size_t i = 0; i < nitems(bcm->hvars); i++) {
106977cb4d3eSLandon J. Fuller if (strcmp(bcm->hvars[i].name, name) == 0)
107077cb4d3eSLandon J. Fuller return (&bcm->hvars[i]);
107177cb4d3eSLandon J. Fuller }
107277cb4d3eSLandon J. Fuller
107377cb4d3eSLandon J. Fuller /* Not found */
107477cb4d3eSLandon J. Fuller return (NULL);
107577cb4d3eSLandon J. Fuller }
107677cb4d3eSLandon J. Fuller
107777cb4d3eSLandon J. Fuller /**
107877cb4d3eSLandon J. Fuller * If @p cookiep references a header-defined variable, return the
107977cb4d3eSLandon J. Fuller * internal BCM data reference. Otherwise, returns NULL.
108077cb4d3eSLandon J. Fuller */
108177cb4d3eSLandon J. Fuller static struct bhnd_nvram_bcm_hvar *
bhnd_nvram_bcm_to_hdrvar(struct bhnd_nvram_bcm * bcm,void * cookiep)108277cb4d3eSLandon J. Fuller bhnd_nvram_bcm_to_hdrvar(struct bhnd_nvram_bcm *bcm, void *cookiep)
108377cb4d3eSLandon J. Fuller {
108477cb4d3eSLandon J. Fuller #ifdef BHND_NVRAM_INVARIANTS
108577cb4d3eSLandon J. Fuller uintptr_t base, ptr;
108677cb4d3eSLandon J. Fuller #endif
108777cb4d3eSLandon J. Fuller
108877cb4d3eSLandon J. Fuller /* If the cookie falls within the hvar array, it's a
108977cb4d3eSLandon J. Fuller * header variable cookie */
109077cb4d3eSLandon J. Fuller if (nitems(bcm->hvars) == 0)
109177cb4d3eSLandon J. Fuller return (NULL);
109277cb4d3eSLandon J. Fuller
109377cb4d3eSLandon J. Fuller if (cookiep < (void *)&bcm->hvars[0])
109477cb4d3eSLandon J. Fuller return (NULL);
109577cb4d3eSLandon J. Fuller
109677cb4d3eSLandon J. Fuller if (cookiep > (void *)&bcm->hvars[nitems(bcm->hvars)-1])
109777cb4d3eSLandon J. Fuller return (NULL);
109877cb4d3eSLandon J. Fuller
109977cb4d3eSLandon J. Fuller #ifdef BHND_NVRAM_INVARIANTS
110077cb4d3eSLandon J. Fuller base = (uintptr_t)bcm->hvars;
110177cb4d3eSLandon J. Fuller ptr = (uintptr_t)cookiep;
110277cb4d3eSLandon J. Fuller
110377cb4d3eSLandon J. Fuller BHND_NV_ASSERT((ptr - base) % sizeof(bcm->hvars[0]) == 0,
110477cb4d3eSLandon J. Fuller ("misaligned hvar pointer %p/%p", cookiep, bcm->hvars));
110577cb4d3eSLandon J. Fuller #endif /* INVARIANTS */
110677cb4d3eSLandon J. Fuller
110777cb4d3eSLandon J. Fuller return ((struct bhnd_nvram_bcm_hvar *)cookiep);
110877cb4d3eSLandon J. Fuller }
110977cb4d3eSLandon J. Fuller
111077cb4d3eSLandon J. Fuller /**
111177cb4d3eSLandon J. Fuller * Return the index of @p hdrvar within @p bcm's backing hvars array.
111277cb4d3eSLandon J. Fuller */
111377cb4d3eSLandon J. Fuller static size_t
bhnd_nvram_bcm_hdrvar_index(struct bhnd_nvram_bcm * bcm,struct bhnd_nvram_bcm_hvar * hdrvar)111477cb4d3eSLandon J. Fuller bhnd_nvram_bcm_hdrvar_index(struct bhnd_nvram_bcm *bcm,
111577cb4d3eSLandon J. Fuller struct bhnd_nvram_bcm_hvar *hdrvar)
111677cb4d3eSLandon J. Fuller {
111777cb4d3eSLandon J. Fuller BHND_NV_ASSERT(bhnd_nvram_bcm_to_hdrvar(bcm, (void *)hdrvar) != NULL,
111877cb4d3eSLandon J. Fuller ("%p is not a valid hdrvar reference", hdrvar));
111977cb4d3eSLandon J. Fuller
112077cb4d3eSLandon J. Fuller return (hdrvar - &bcm->hvars[0]);
112177cb4d3eSLandon J. Fuller }
1122