177cb4d3eSLandon J. Fuller /*-
277cb4d3eSLandon J. Fuller * Copyright (c) 2015-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/cdefs.h>
3177cb4d3eSLandon J. Fuller #include <sys/endian.h>
3277cb4d3eSLandon J. Fuller
3377cb4d3eSLandon J. Fuller #ifdef _KERNEL
3477cb4d3eSLandon J. Fuller #include <sys/param.h>
3577cb4d3eSLandon J. Fuller #include <sys/ctype.h>
3677cb4d3eSLandon J. Fuller #include <sys/malloc.h>
3777cb4d3eSLandon J. Fuller #include <sys/systm.h>
3877cb4d3eSLandon J. Fuller
3977cb4d3eSLandon J. Fuller #include <machine/_inttypes.h>
4077cb4d3eSLandon J. Fuller #else /* !_KERNEL */
4177cb4d3eSLandon J. Fuller #include <ctype.h>
4277cb4d3eSLandon J. Fuller #include <errno.h>
4377cb4d3eSLandon J. Fuller #include <inttypes.h>
4477cb4d3eSLandon J. Fuller #include <stdint.h>
4577cb4d3eSLandon J. Fuller #include <stdio.h>
4677cb4d3eSLandon J. Fuller #include <stdlib.h>
4777cb4d3eSLandon J. Fuller #include <string.h>
4877cb4d3eSLandon J. Fuller #endif /* _KERNEL */
4977cb4d3eSLandon J. Fuller
50c283839dSLandon J. Fuller #include "bhnd_nvram_map.h"
5177cb4d3eSLandon J. Fuller
52c283839dSLandon J. Fuller #include "bhnd_nvram_private.h"
5377cb4d3eSLandon J. Fuller #include "bhnd_nvram_datavar.h"
5477cb4d3eSLandon J. Fuller
5577cb4d3eSLandon J. Fuller #include "bhnd_nvram_data_spromvar.h"
5677cb4d3eSLandon J. Fuller
5777cb4d3eSLandon J. Fuller /*
5877cb4d3eSLandon J. Fuller * BHND SPROM NVRAM data class
5977cb4d3eSLandon J. Fuller *
6077cb4d3eSLandon J. Fuller * The SPROM data format is a fixed-layout, non-self-descriptive binary format,
6177cb4d3eSLandon J. Fuller * used on Broadcom wireless and wired adapters, that provides a subset of the
6277cb4d3eSLandon J. Fuller * variables defined by Broadcom SoC NVRAM formats.
6377cb4d3eSLandon J. Fuller */
6477cb4d3eSLandon J. Fuller
65c283839dSLandon J. Fuller static const bhnd_sprom_layout *bhnd_nvram_sprom_get_layout(uint8_t sromrev);
6677cb4d3eSLandon J. Fuller
67c283839dSLandon J. Fuller static int bhnd_nvram_sprom_ident(
68c283839dSLandon J. Fuller struct bhnd_nvram_io *io,
69591e79bcSLandon J. Fuller const bhnd_sprom_layout **ident);
7077cb4d3eSLandon J. Fuller
71c283839dSLandon J. Fuller static int bhnd_nvram_sprom_write_var(
72c283839dSLandon J. Fuller bhnd_sprom_opcode_state *state,
73c283839dSLandon J. Fuller bhnd_sprom_opcode_idx_entry *entry,
74c283839dSLandon J. Fuller bhnd_nvram_val *value,
75c283839dSLandon J. Fuller struct bhnd_nvram_io *io);
7677cb4d3eSLandon J. Fuller
77591e79bcSLandon J. Fuller static int bhnd_nvram_sprom_read_var(
78591e79bcSLandon J. Fuller struct bhnd_sprom_opcode_state *state,
79591e79bcSLandon J. Fuller struct bhnd_sprom_opcode_idx_entry *entry,
80591e79bcSLandon J. Fuller struct bhnd_nvram_io *io,
81591e79bcSLandon J. Fuller union bhnd_nvram_sprom_storage *storage,
82591e79bcSLandon J. Fuller bhnd_nvram_val *val);
83591e79bcSLandon J. Fuller
84c283839dSLandon J. Fuller static int bhnd_nvram_sprom_write_offset(
85c283839dSLandon J. Fuller const struct bhnd_nvram_vardefn *var,
86c283839dSLandon J. Fuller struct bhnd_nvram_io *data,
87c283839dSLandon J. Fuller bhnd_nvram_type type, size_t offset,
88c283839dSLandon J. Fuller uint32_t mask, int8_t shift,
89c283839dSLandon J. Fuller uint32_t value);
9077cb4d3eSLandon J. Fuller
91c283839dSLandon J. Fuller static int bhnd_nvram_sprom_read_offset(
92c283839dSLandon J. Fuller const struct bhnd_nvram_vardefn *var,
93c283839dSLandon J. Fuller struct bhnd_nvram_io *data,
94c283839dSLandon J. Fuller bhnd_nvram_type type, size_t offset,
95c283839dSLandon J. Fuller uint32_t mask, int8_t shift,
9677cb4d3eSLandon J. Fuller uint32_t *value);
9777cb4d3eSLandon J. Fuller
98c283839dSLandon J. Fuller static bool bhnd_sprom_is_external_immutable(
99c283839dSLandon J. Fuller const char *name);
10077cb4d3eSLandon J. Fuller
101c283839dSLandon J. Fuller BHND_NVRAM_DATA_CLASS_DEFN(sprom, "Broadcom SPROM",
102c283839dSLandon J. Fuller BHND_NVRAM_DATA_CAP_DEVPATHS, sizeof(struct bhnd_nvram_sprom))
10377cb4d3eSLandon J. Fuller
104c283839dSLandon J. Fuller #define SPROM_COOKIE_TO_VID(_cookie) \
105c283839dSLandon J. Fuller (((struct bhnd_sprom_opcode_idx_entry *)(_cookie))->vid)
106c283839dSLandon J. Fuller
107c283839dSLandon J. Fuller #define SPROM_COOKIE_TO_NVRAM_VAR(_cookie) \
108c283839dSLandon J. Fuller bhnd_nvram_get_vardefn(SPROM_COOKIE_TO_VID(_cookie))
10977cb4d3eSLandon J. Fuller
11077cb4d3eSLandon J. Fuller /**
11177cb4d3eSLandon J. Fuller * Read the magic value from @p io, and verify that it matches
11277cb4d3eSLandon J. Fuller * the @p layout's expected magic value.
11377cb4d3eSLandon J. Fuller *
11477cb4d3eSLandon J. Fuller * If @p layout does not defined a magic value, @p magic is set to 0x0
11577cb4d3eSLandon J. Fuller * and success is returned.
11677cb4d3eSLandon J. Fuller *
11777cb4d3eSLandon J. Fuller * @param io An I/O context mapping the SPROM data to be identified.
11877cb4d3eSLandon J. Fuller * @param layout The SPROM layout against which @p io should be verified.
11977cb4d3eSLandon J. Fuller * @param[out] magic On success, the SPROM magic value.
12077cb4d3eSLandon J. Fuller *
12177cb4d3eSLandon J. Fuller * @retval 0 success
12277cb4d3eSLandon J. Fuller * @retval non-zero If checking @p io otherwise fails, a regular unix
12377cb4d3eSLandon J. Fuller * error code will be returned.
12477cb4d3eSLandon J. Fuller */
12577cb4d3eSLandon J. Fuller static int
bhnd_nvram_sprom_check_magic(struct bhnd_nvram_io * io,const bhnd_sprom_layout * layout,uint16_t * magic)12677cb4d3eSLandon J. Fuller bhnd_nvram_sprom_check_magic(struct bhnd_nvram_io *io,
127c283839dSLandon J. Fuller const bhnd_sprom_layout *layout, uint16_t *magic)
12877cb4d3eSLandon J. Fuller {
12977cb4d3eSLandon J. Fuller int error;
13077cb4d3eSLandon J. Fuller
13177cb4d3eSLandon J. Fuller /* Skip if layout does not define a magic value */
13277cb4d3eSLandon J. Fuller if (layout->flags & SPROM_LAYOUT_MAGIC_NONE)
13377cb4d3eSLandon J. Fuller return (0);
13477cb4d3eSLandon J. Fuller
13577cb4d3eSLandon J. Fuller /* Read the magic value */
13677cb4d3eSLandon J. Fuller error = bhnd_nvram_io_read(io, layout->magic_offset, magic,
13777cb4d3eSLandon J. Fuller sizeof(*magic));
13877cb4d3eSLandon J. Fuller if (error)
13977cb4d3eSLandon J. Fuller return (error);
14077cb4d3eSLandon J. Fuller
14177cb4d3eSLandon J. Fuller *magic = le16toh(*magic);
14277cb4d3eSLandon J. Fuller
14377cb4d3eSLandon J. Fuller /* If the signature does not match, skip to next layout */
14477cb4d3eSLandon J. Fuller if (*magic != layout->magic_value)
14577cb4d3eSLandon J. Fuller return (ENXIO);
14677cb4d3eSLandon J. Fuller
14777cb4d3eSLandon J. Fuller return (0);
14877cb4d3eSLandon J. Fuller }
14977cb4d3eSLandon J. Fuller
15077cb4d3eSLandon J. Fuller /**
15177cb4d3eSLandon J. Fuller * Attempt to identify the format of the SPROM data mapped by @p io.
15277cb4d3eSLandon J. Fuller *
15377cb4d3eSLandon J. Fuller * The SPROM data format does not provide any identifying information at a
15477cb4d3eSLandon J. Fuller * known offset, instead requiring that we iterate over the known SPROM image
15577cb4d3eSLandon J. Fuller * sizes until we are able to compute a valid checksum (and, for later
15677cb4d3eSLandon J. Fuller * revisions, validate a signature at a revision-specific offset).
15777cb4d3eSLandon J. Fuller *
15877cb4d3eSLandon J. Fuller * @param io An I/O context mapping the SPROM data to be identified.
15977cb4d3eSLandon J. Fuller * @param[out] ident On success, the identified SPROM layout.
16077cb4d3eSLandon J. Fuller *
16177cb4d3eSLandon J. Fuller * @retval 0 success
16277cb4d3eSLandon J. Fuller * @retval non-zero If identifying @p io otherwise fails, a regular unix
16377cb4d3eSLandon J. Fuller * error code will be returned.
16477cb4d3eSLandon J. Fuller */
16577cb4d3eSLandon J. Fuller static int
bhnd_nvram_sprom_ident(struct bhnd_nvram_io * io,const bhnd_sprom_layout ** ident)16677cb4d3eSLandon J. Fuller bhnd_nvram_sprom_ident(struct bhnd_nvram_io *io,
167591e79bcSLandon J. Fuller const bhnd_sprom_layout **ident)
16877cb4d3eSLandon J. Fuller {
16977cb4d3eSLandon J. Fuller uint8_t crc;
17077cb4d3eSLandon J. Fuller size_t crc_errors;
171591e79bcSLandon J. Fuller size_t nbytes;
17277cb4d3eSLandon J. Fuller int error;
17377cb4d3eSLandon J. Fuller
17477cb4d3eSLandon J. Fuller crc = BHND_NVRAM_CRC8_INITIAL;
17577cb4d3eSLandon J. Fuller crc_errors = 0;
176591e79bcSLandon J. Fuller nbytes = 0;
17777cb4d3eSLandon J. Fuller
17877cb4d3eSLandon J. Fuller /* We iterate the SPROM layouts smallest to largest, allowing us to
17977cb4d3eSLandon J. Fuller * perform incremental checksum calculation */
18077cb4d3eSLandon J. Fuller for (size_t i = 0; i < bhnd_sprom_num_layouts; i++) {
181c283839dSLandon J. Fuller const bhnd_sprom_layout *layout;
182591e79bcSLandon J. Fuller u_char buf[512];
183591e79bcSLandon J. Fuller size_t nread;
18477cb4d3eSLandon J. Fuller uint16_t magic;
18526e4f220SLandon J. Fuller uint8_t srevcrc[2];
18677cb4d3eSLandon J. Fuller uint8_t srev;
18777cb4d3eSLandon J. Fuller bool crc_valid;
18877cb4d3eSLandon J. Fuller bool have_magic;
18977cb4d3eSLandon J. Fuller
19077cb4d3eSLandon J. Fuller layout = &bhnd_sprom_layouts[i];
1916467a17bSLandon J. Fuller crc_valid = true;
19277cb4d3eSLandon J. Fuller
19377cb4d3eSLandon J. Fuller have_magic = true;
194591e79bcSLandon J. Fuller if ((layout->flags & SPROM_LAYOUT_MAGIC_NONE))
195591e79bcSLandon J. Fuller have_magic = false;
19677cb4d3eSLandon J. Fuller
197591e79bcSLandon J. Fuller /*
198591e79bcSLandon J. Fuller * Read image data and update CRC (errors are reported
199591e79bcSLandon J. Fuller * after the signature check)
200591e79bcSLandon J. Fuller *
201591e79bcSLandon J. Fuller * Layout instances must be ordered from smallest to largest by
202591e79bcSLandon J. Fuller * the nvram_map compiler, allowing us to incrementally update
203591e79bcSLandon J. Fuller * our CRC.
204591e79bcSLandon J. Fuller */
20577cb4d3eSLandon J. Fuller if (nbytes > layout->size)
206591e79bcSLandon J. Fuller BHND_NV_PANIC("SPROM layout defined out-of-order");
20777cb4d3eSLandon J. Fuller
208591e79bcSLandon J. Fuller nread = layout->size - nbytes;
20977cb4d3eSLandon J. Fuller
210591e79bcSLandon J. Fuller while (nread > 0) {
211591e79bcSLandon J. Fuller size_t nr;
21277cb4d3eSLandon J. Fuller
213591e79bcSLandon J. Fuller nr = bhnd_nv_ummin(nread, sizeof(buf));
21477cb4d3eSLandon J. Fuller
215591e79bcSLandon J. Fuller if ((error = bhnd_nvram_io_read(io, nbytes, buf, nr)))
216591e79bcSLandon J. Fuller return (error);
21777cb4d3eSLandon J. Fuller
218591e79bcSLandon J. Fuller crc = bhnd_nvram_crc8(buf, nr, crc);
21977cb4d3eSLandon J. Fuller crc_valid = (crc == BHND_NVRAM_CRC8_VALID);
22077cb4d3eSLandon J. Fuller if (!crc_valid)
22177cb4d3eSLandon J. Fuller crc_errors++;
22277cb4d3eSLandon J. Fuller
223591e79bcSLandon J. Fuller nread -= nr;
224591e79bcSLandon J. Fuller nbytes += nr;
225591e79bcSLandon J. Fuller }
226591e79bcSLandon J. Fuller
22726e4f220SLandon J. Fuller /* Read 8-bit SPROM revision, maintaining 16-bit size alignment
22826e4f220SLandon J. Fuller * required by some OTP/SPROM chipsets. */
22926e4f220SLandon J. Fuller error = bhnd_nvram_io_read(io, layout->srev_offset, &srevcrc,
23026e4f220SLandon J. Fuller sizeof(srevcrc));
23177cb4d3eSLandon J. Fuller if (error)
232591e79bcSLandon J. Fuller return (error);
23377cb4d3eSLandon J. Fuller
23426e4f220SLandon J. Fuller srev = srevcrc[0];
23526e4f220SLandon J. Fuller
23677cb4d3eSLandon J. Fuller /* Early sromrev 1 devices (specifically some BCM440x enet
23777cb4d3eSLandon J. Fuller * cards) are reported to have been incorrectly programmed
23877cb4d3eSLandon J. Fuller * with a revision of 0x10. */
23977cb4d3eSLandon J. Fuller if (layout->rev == 1 && srev == 0x10)
24077cb4d3eSLandon J. Fuller srev = 0x1;
24177cb4d3eSLandon J. Fuller
24277cb4d3eSLandon J. Fuller /* Check revision against the layout definition */
24377cb4d3eSLandon J. Fuller if (srev != layout->rev)
24477cb4d3eSLandon J. Fuller continue;
24577cb4d3eSLandon J. Fuller
24677cb4d3eSLandon J. Fuller /* Check the magic value, skipping to the next layout on
24777cb4d3eSLandon J. Fuller * failure. */
248591e79bcSLandon J. Fuller error = bhnd_nvram_sprom_check_magic(io, layout, &magic);
24977cb4d3eSLandon J. Fuller if (error) {
25077cb4d3eSLandon J. Fuller /* If the CRC is was valid, log the mismatch */
25177cb4d3eSLandon J. Fuller if (crc_valid || BHND_NV_VERBOSE) {
25277cb4d3eSLandon J. Fuller BHND_NV_LOG("invalid sprom %hhu signature: "
25377cb4d3eSLandon J. Fuller "0x%hx (expected 0x%hx)\n", srev,
25477cb4d3eSLandon J. Fuller magic, layout->magic_value);
25577cb4d3eSLandon J. Fuller
256591e79bcSLandon J. Fuller return (ENXIO);
25777cb4d3eSLandon J. Fuller }
25877cb4d3eSLandon J. Fuller
25977cb4d3eSLandon J. Fuller continue;
26077cb4d3eSLandon J. Fuller }
26177cb4d3eSLandon J. Fuller
26277cb4d3eSLandon J. Fuller /* Check for an earlier CRC error */
26377cb4d3eSLandon J. Fuller if (!crc_valid) {
26477cb4d3eSLandon J. Fuller /* If the magic check succeeded, then we may just have
26577cb4d3eSLandon J. Fuller * data corruption -- log the CRC error */
26677cb4d3eSLandon J. Fuller if (have_magic || BHND_NV_VERBOSE) {
26777cb4d3eSLandon J. Fuller BHND_NV_LOG("sprom %hhu CRC error (crc=%#hhx, "
26877cb4d3eSLandon J. Fuller "expected=%#x)\n", srev, crc,
26977cb4d3eSLandon J. Fuller BHND_NVRAM_CRC8_VALID);
27077cb4d3eSLandon J. Fuller }
27177cb4d3eSLandon J. Fuller
27277cb4d3eSLandon J. Fuller continue;
27377cb4d3eSLandon J. Fuller }
27477cb4d3eSLandon J. Fuller
27577cb4d3eSLandon J. Fuller /* Identified */
27677cb4d3eSLandon J. Fuller *ident = layout;
27777cb4d3eSLandon J. Fuller return (0);
27877cb4d3eSLandon J. Fuller }
27977cb4d3eSLandon J. Fuller
280591e79bcSLandon J. Fuller /* No match */
28177cb4d3eSLandon J. Fuller if (crc_errors > 0 && BHND_NV_VERBOSE) {
28277cb4d3eSLandon J. Fuller BHND_NV_LOG("sprom parsing failed with %zu CRC errors\n",
28377cb4d3eSLandon J. Fuller crc_errors);
28477cb4d3eSLandon J. Fuller }
28577cb4d3eSLandon J. Fuller
286591e79bcSLandon J. Fuller return (ENXIO);
28777cb4d3eSLandon J. Fuller }
28877cb4d3eSLandon J. Fuller
28977cb4d3eSLandon J. Fuller static int
bhnd_nvram_sprom_probe(struct bhnd_nvram_io * io)29077cb4d3eSLandon J. Fuller bhnd_nvram_sprom_probe(struct bhnd_nvram_io *io)
29177cb4d3eSLandon J. Fuller {
292c283839dSLandon J. Fuller const bhnd_sprom_layout *layout;
29377cb4d3eSLandon J. Fuller int error;
29477cb4d3eSLandon J. Fuller
29577cb4d3eSLandon J. Fuller /* Try to parse the input */
296591e79bcSLandon J. Fuller if ((error = bhnd_nvram_sprom_ident(io, &layout)))
29777cb4d3eSLandon J. Fuller return (error);
29877cb4d3eSLandon J. Fuller
29977cb4d3eSLandon J. Fuller return (BHND_NVRAM_DATA_PROBE_DEFAULT);
30077cb4d3eSLandon J. Fuller }
30177cb4d3eSLandon J. Fuller
302591e79bcSLandon J. Fuller static int
bhnd_nvram_sprom_getvar_direct(struct bhnd_nvram_io * io,const char * name,void * buf,size_t * len,bhnd_nvram_type type)303591e79bcSLandon J. Fuller bhnd_nvram_sprom_getvar_direct(struct bhnd_nvram_io *io, const char *name,
304591e79bcSLandon J. Fuller void *buf, size_t *len, bhnd_nvram_type type)
305591e79bcSLandon J. Fuller {
306591e79bcSLandon J. Fuller const bhnd_sprom_layout *layout;
307591e79bcSLandon J. Fuller bhnd_sprom_opcode_state state;
308591e79bcSLandon J. Fuller const struct bhnd_nvram_vardefn *var;
309591e79bcSLandon J. Fuller size_t vid;
310591e79bcSLandon J. Fuller int error;
311591e79bcSLandon J. Fuller
312591e79bcSLandon J. Fuller /* Look up the variable definition and ID */
313591e79bcSLandon J. Fuller if ((var = bhnd_nvram_find_vardefn(name)) == NULL)
314591e79bcSLandon J. Fuller return (ENOENT);
315591e79bcSLandon J. Fuller
316591e79bcSLandon J. Fuller vid = bhnd_nvram_get_vardefn_id(var);
317591e79bcSLandon J. Fuller
318591e79bcSLandon J. Fuller /* Identify the SPROM image layout */
319591e79bcSLandon J. Fuller if ((error = bhnd_nvram_sprom_ident(io, &layout)))
320591e79bcSLandon J. Fuller return (error);
321591e79bcSLandon J. Fuller
322591e79bcSLandon J. Fuller /* Initialize SPROM layout interpreter */
323591e79bcSLandon J. Fuller if ((error = bhnd_sprom_opcode_init(&state, layout))) {
324591e79bcSLandon J. Fuller BHND_NV_LOG("error initializing opcode state: %d\n", error);
325591e79bcSLandon J. Fuller return (ENXIO);
326591e79bcSLandon J. Fuller }
327591e79bcSLandon J. Fuller
328591e79bcSLandon J. Fuller /* Find SPROM layout entry for the requested variable */
329591e79bcSLandon J. Fuller while ((error = bhnd_sprom_opcode_next_var(&state)) == 0) {
330591e79bcSLandon J. Fuller bhnd_sprom_opcode_idx_entry entry;
331591e79bcSLandon J. Fuller union bhnd_nvram_sprom_storage storage;
332591e79bcSLandon J. Fuller bhnd_nvram_val val;
333591e79bcSLandon J. Fuller
334591e79bcSLandon J. Fuller /* Fetch the variable's entry state */
335591e79bcSLandon J. Fuller if ((error = bhnd_sprom_opcode_init_entry(&state, &entry)))
336591e79bcSLandon J. Fuller return (error);
337591e79bcSLandon J. Fuller
338591e79bcSLandon J. Fuller /* Match against expected VID */
339591e79bcSLandon J. Fuller if (entry.vid != vid)
340591e79bcSLandon J. Fuller continue;
341591e79bcSLandon J. Fuller
342591e79bcSLandon J. Fuller /* Decode variable to a new value instance */
343591e79bcSLandon J. Fuller error = bhnd_nvram_sprom_read_var(&state, &entry, io, &storage,
344591e79bcSLandon J. Fuller &val);
345591e79bcSLandon J. Fuller if (error)
346591e79bcSLandon J. Fuller return (error);
347591e79bcSLandon J. Fuller
348591e79bcSLandon J. Fuller /* Perform value coercion */
349591e79bcSLandon J. Fuller error = bhnd_nvram_val_encode(&val, buf, len, type);
350591e79bcSLandon J. Fuller
351591e79bcSLandon J. Fuller /* Clean up */
352591e79bcSLandon J. Fuller bhnd_nvram_val_release(&val);
353591e79bcSLandon J. Fuller return (error);
354591e79bcSLandon J. Fuller }
355591e79bcSLandon J. Fuller
356591e79bcSLandon J. Fuller /* Hit EOF without matching the requested variable? */
357591e79bcSLandon J. Fuller if (error == ENOENT)
358591e79bcSLandon J. Fuller return (ENOENT);
359591e79bcSLandon J. Fuller
360*88cdf609SGordon Bergling /* Some other parse error occurred */
361591e79bcSLandon J. Fuller return (error);
362591e79bcSLandon J. Fuller }
363c283839dSLandon J. Fuller
364c283839dSLandon J. Fuller /**
365c283839dSLandon J. Fuller * Return the SPROM layout definition for the given @p sromrev, or NULL if
366c283839dSLandon J. Fuller * not found.
367c283839dSLandon J. Fuller */
368c283839dSLandon J. Fuller static const bhnd_sprom_layout *
bhnd_nvram_sprom_get_layout(uint8_t sromrev)369c283839dSLandon J. Fuller bhnd_nvram_sprom_get_layout(uint8_t sromrev)
370c283839dSLandon J. Fuller {
371c283839dSLandon J. Fuller /* Find matching SPROM layout definition */
372c283839dSLandon J. Fuller for (size_t i = 0; i < bhnd_sprom_num_layouts; i++) {
373c283839dSLandon J. Fuller if (bhnd_sprom_layouts[i].rev == sromrev)
374c283839dSLandon J. Fuller return (&bhnd_sprom_layouts[i]);
375c283839dSLandon J. Fuller }
376c283839dSLandon J. Fuller
377c283839dSLandon J. Fuller /* Not found */
378c283839dSLandon J. Fuller return (NULL);
379c283839dSLandon J. Fuller }
380c283839dSLandon J. Fuller
381c283839dSLandon J. Fuller /**
382c283839dSLandon J. Fuller * Serialize a SPROM variable.
383c283839dSLandon J. Fuller *
384c283839dSLandon J. Fuller * @param state The SPROM opcode state describing the layout of @p io.
385c283839dSLandon J. Fuller * @param entry The variable's SPROM opcode index entry.
386c283839dSLandon J. Fuller * @param value The value to encode to @p io as per @p entry.
387c283839dSLandon J. Fuller * @param io I/O context to which @p value should be written, or NULL
388c283839dSLandon J. Fuller * if no output should be produced. This may be used to validate
389c283839dSLandon J. Fuller * values prior to write.
390c283839dSLandon J. Fuller *
391c283839dSLandon J. Fuller * @retval 0 success
392c283839dSLandon J. Fuller * @retval EFTYPE If value coercion from @p value to the type required by
393c283839dSLandon J. Fuller * @p entry is unsupported.
394c283839dSLandon J. Fuller * @retval ERANGE If value coercion from @p value would overflow
395c283839dSLandon J. Fuller * (or underflow) the type required by @p entry.
396c283839dSLandon J. Fuller * @retval non-zero If serialization otherwise fails, a regular unix error
397c283839dSLandon J. Fuller * code will be returned.
398c283839dSLandon J. Fuller */
399c283839dSLandon J. Fuller static int
bhnd_nvram_sprom_write_var(bhnd_sprom_opcode_state * state,bhnd_sprom_opcode_idx_entry * entry,bhnd_nvram_val * value,struct bhnd_nvram_io * io)400c283839dSLandon J. Fuller bhnd_nvram_sprom_write_var(bhnd_sprom_opcode_state *state,
401c283839dSLandon J. Fuller bhnd_sprom_opcode_idx_entry *entry, bhnd_nvram_val *value,
402c283839dSLandon J. Fuller struct bhnd_nvram_io *io)
403c283839dSLandon J. Fuller {
404c283839dSLandon J. Fuller const struct bhnd_nvram_vardefn *var;
405c283839dSLandon J. Fuller uint32_t u32[BHND_SPROM_ARRAY_MAXLEN];
406c283839dSLandon J. Fuller bhnd_nvram_type itype, var_base_type;
407c283839dSLandon J. Fuller size_t ipos, ilen, nelem;
408c283839dSLandon J. Fuller int error;
409c283839dSLandon J. Fuller
410c283839dSLandon J. Fuller /* Fetch variable definition and the native element type */
411c283839dSLandon J. Fuller var = bhnd_nvram_get_vardefn(entry->vid);
412c283839dSLandon J. Fuller BHND_NV_ASSERT(var != NULL, ("missing variable definition"));
413c283839dSLandon J. Fuller
414c283839dSLandon J. Fuller var_base_type = bhnd_nvram_base_type(var->type);
415c283839dSLandon J. Fuller
416c283839dSLandon J. Fuller /* Fetch the element count from the SPROM variable layout definition */
417591e79bcSLandon J. Fuller if ((error = bhnd_sprom_opcode_eval_var(state, entry)))
418c283839dSLandon J. Fuller return (error);
419c283839dSLandon J. Fuller
420c283839dSLandon J. Fuller nelem = state->var.nelem;
421c283839dSLandon J. Fuller BHND_NV_ASSERT(nelem <= var->nelem, ("SPROM nelem=%zu exceeds maximum "
422c283839dSLandon J. Fuller "NVRAM nelem=%hhu", nelem, var->nelem));
423c283839dSLandon J. Fuller
424c283839dSLandon J. Fuller /* Promote the data to a common 32-bit representation */
425c283839dSLandon J. Fuller if (bhnd_nvram_is_signed_type(var_base_type))
426c283839dSLandon J. Fuller itype = BHND_NVRAM_TYPE_INT32_ARRAY;
427c283839dSLandon J. Fuller else
428c283839dSLandon J. Fuller itype = BHND_NVRAM_TYPE_UINT32_ARRAY;
429c283839dSLandon J. Fuller
430c283839dSLandon J. Fuller /* Calculate total size of the 32-bit promoted representation */
431c283839dSLandon J. Fuller if ((ilen = bhnd_nvram_value_size(NULL, 0, itype, nelem)) == 0) {
432c283839dSLandon J. Fuller /* Variable-width types are unsupported */
433c283839dSLandon J. Fuller BHND_NV_LOG("invalid %s SPROM variable type %d\n",
434c283839dSLandon J. Fuller var->name, var->type);
435c283839dSLandon J. Fuller return (EFTYPE);
436c283839dSLandon J. Fuller }
437c283839dSLandon J. Fuller
438c283839dSLandon J. Fuller /* The native representation must fit within our scratch array */
439c283839dSLandon J. Fuller if (ilen > sizeof(u32)) {
440c283839dSLandon J. Fuller BHND_NV_LOG("error encoding '%s', SPROM_ARRAY_MAXLEN "
441c283839dSLandon J. Fuller "incorrect\n", var->name);
442c283839dSLandon J. Fuller return (EFTYPE);
443c283839dSLandon J. Fuller }
444c283839dSLandon J. Fuller
445c283839dSLandon J. Fuller /* Initialize our common 32-bit value representation */
446c283839dSLandon J. Fuller if (bhnd_nvram_val_type(value) == BHND_NVRAM_TYPE_NULL) {
447c283839dSLandon J. Fuller /* No value provided; can this variable be encoded as missing
448c283839dSLandon J. Fuller * by setting all bits to one? */
449c283839dSLandon J. Fuller if (!(var->flags & BHND_NVRAM_VF_IGNALL1)) {
450c283839dSLandon J. Fuller BHND_NV_LOG("missing required property: %s\n",
451c283839dSLandon J. Fuller var->name);
452c283839dSLandon J. Fuller return (EINVAL);
453c283839dSLandon J. Fuller }
454c283839dSLandon J. Fuller
455c283839dSLandon J. Fuller /* Set all bits */
456c283839dSLandon J. Fuller memset(u32, 0xFF, ilen);
457c283839dSLandon J. Fuller } else {
458c283839dSLandon J. Fuller bhnd_nvram_val bcm_val;
459c283839dSLandon J. Fuller const void *var_ptr;
460c283839dSLandon J. Fuller bhnd_nvram_type var_type, raw_type;
461c283839dSLandon J. Fuller size_t var_len, enc_nelem;
462c283839dSLandon J. Fuller
463c283839dSLandon J. Fuller /* Try to coerce the value to the native variable format. */
464c283839dSLandon J. Fuller error = bhnd_nvram_val_convert_init(&bcm_val, var->fmt, value,
465c283839dSLandon J. Fuller BHND_NVRAM_VAL_DYNAMIC|BHND_NVRAM_VAL_BORROW_DATA);
466c283839dSLandon J. Fuller if (error) {
467c283839dSLandon J. Fuller BHND_NV_LOG("error converting input type %s to %s "
468c283839dSLandon J. Fuller "format\n",
469c283839dSLandon J. Fuller bhnd_nvram_type_name(bhnd_nvram_val_type(value)),
470c283839dSLandon J. Fuller bhnd_nvram_val_fmt_name(var->fmt));
471c283839dSLandon J. Fuller return (error);
472c283839dSLandon J. Fuller }
473c283839dSLandon J. Fuller
474c283839dSLandon J. Fuller var_ptr = bhnd_nvram_val_bytes(&bcm_val, &var_len, &var_type);
475c283839dSLandon J. Fuller
476c283839dSLandon J. Fuller /*
477c283839dSLandon J. Fuller * Promote to a common 32-bit representation.
478c283839dSLandon J. Fuller *
479c283839dSLandon J. Fuller * We must use the raw type to interpret the input data as its
480c283839dSLandon J. Fuller * underlying integer representation -- otherwise, coercion
481c283839dSLandon J. Fuller * would attempt to parse the input as its complex
482c283839dSLandon J. Fuller * representation.
483c283839dSLandon J. Fuller *
484c283839dSLandon J. Fuller * For example, direct CHAR -> UINT32 coercion would attempt to
485c283839dSLandon J. Fuller * parse the character as a decimal integer, rather than
486c283839dSLandon J. Fuller * promoting the raw UTF8 byte value to a 32-bit value.
487c283839dSLandon J. Fuller */
488c283839dSLandon J. Fuller raw_type = bhnd_nvram_raw_type(var_type);
489c283839dSLandon J. Fuller error = bhnd_nvram_value_coerce(var_ptr, var_len, raw_type,
490c283839dSLandon J. Fuller u32, &ilen, itype);
491c283839dSLandon J. Fuller
492c283839dSLandon J. Fuller /* Clean up temporary value representation */
493c283839dSLandon J. Fuller bhnd_nvram_val_release(&bcm_val);
494c283839dSLandon J. Fuller
495c283839dSLandon J. Fuller /* Report coercion failure */
496c283839dSLandon J. Fuller if (error) {
497c283839dSLandon J. Fuller BHND_NV_LOG("error promoting %s to %s: %d\n",
498c283839dSLandon J. Fuller bhnd_nvram_type_name(var_type),
499c283839dSLandon J. Fuller bhnd_nvram_type_name(itype), error);
500c283839dSLandon J. Fuller return (error);
501c283839dSLandon J. Fuller }
502c283839dSLandon J. Fuller
503c283839dSLandon J. Fuller /* Encoded element count must match SPROM's definition */
504c283839dSLandon J. Fuller error = bhnd_nvram_value_nelem(u32, ilen, itype, &enc_nelem);
505c283839dSLandon J. Fuller if (error)
506c283839dSLandon J. Fuller return (error);
507c283839dSLandon J. Fuller
508c283839dSLandon J. Fuller if (enc_nelem != nelem) {
509c283839dSLandon J. Fuller const char *type_name;
510c283839dSLandon J. Fuller
511c283839dSLandon J. Fuller type_name = bhnd_nvram_type_name(var_base_type);
512c283839dSLandon J. Fuller BHND_NV_LOG("invalid %s property value '%s[%zu]': "
513c283839dSLandon J. Fuller "required %s[%zu]", var->name, type_name,
514c283839dSLandon J. Fuller enc_nelem, type_name, nelem);
515c283839dSLandon J. Fuller return (EFTYPE);
516c283839dSLandon J. Fuller }
517c283839dSLandon J. Fuller }
518c283839dSLandon J. Fuller
519c283839dSLandon J. Fuller /*
520c283839dSLandon J. Fuller * Seek to the start of the variable's SPROM layout definition and
521c283839dSLandon J. Fuller * iterate over all bindings.
522c283839dSLandon J. Fuller */
523c283839dSLandon J. Fuller if ((error = bhnd_sprom_opcode_seek(state, entry))) {
524c283839dSLandon J. Fuller BHND_NV_LOG("variable seek failed: %d\n", error);
525c283839dSLandon J. Fuller return (error);
526c283839dSLandon J. Fuller }
527c283839dSLandon J. Fuller
528c283839dSLandon J. Fuller ipos = 0;
529c283839dSLandon J. Fuller while ((error = bhnd_sprom_opcode_next_binding(state)) == 0) {
530c283839dSLandon J. Fuller bhnd_sprom_opcode_bind *binding;
531c283839dSLandon J. Fuller bhnd_sprom_opcode_var *binding_var;
532c283839dSLandon J. Fuller size_t offset;
533c283839dSLandon J. Fuller uint32_t skip_out_bytes;
534c283839dSLandon J. Fuller
535c283839dSLandon J. Fuller BHND_NV_ASSERT(
536c283839dSLandon J. Fuller state->var_state >= SPROM_OPCODE_VAR_STATE_OPEN,
537c283839dSLandon J. Fuller ("invalid var state"));
538c283839dSLandon J. Fuller BHND_NV_ASSERT(state->var.have_bind, ("invalid bind state"));
539c283839dSLandon J. Fuller
540c283839dSLandon J. Fuller binding_var = &state->var;
541c283839dSLandon J. Fuller binding = &state->var.bind;
542c283839dSLandon J. Fuller
543c283839dSLandon J. Fuller /* Calculate output skip bytes for this binding.
544c283839dSLandon J. Fuller *
545c283839dSLandon J. Fuller * Skip directions are defined in terms of decoding, and
546c283839dSLandon J. Fuller * reversed when encoding. */
547c283839dSLandon J. Fuller skip_out_bytes = binding->skip_in;
548c283839dSLandon J. Fuller error = bhnd_sprom_opcode_apply_scale(state, &skip_out_bytes);
549c283839dSLandon J. Fuller if (error)
550c283839dSLandon J. Fuller return (error);
551c283839dSLandon J. Fuller
552c283839dSLandon J. Fuller /* Bind */
553c283839dSLandon J. Fuller offset = state->offset;
554c283839dSLandon J. Fuller for (size_t i = 0; i < binding->count; i++) {
555c283839dSLandon J. Fuller if (ipos >= nelem) {
556c283839dSLandon J. Fuller BHND_NV_LOG("input skip %u positioned %zu "
557c283839dSLandon J. Fuller "beyond nelem %zu\n", binding->skip_out,
558c283839dSLandon J. Fuller ipos, nelem);
559c283839dSLandon J. Fuller return (EINVAL);
560c283839dSLandon J. Fuller }
561c283839dSLandon J. Fuller
562c283839dSLandon J. Fuller /* Write next offset */
563c283839dSLandon J. Fuller if (io != NULL) {
564c283839dSLandon J. Fuller error = bhnd_nvram_sprom_write_offset(var, io,
565c283839dSLandon J. Fuller binding_var->base_type,
566c283839dSLandon J. Fuller offset,
567c283839dSLandon J. Fuller binding_var->mask,
568c283839dSLandon J. Fuller binding_var->shift,
569c283839dSLandon J. Fuller u32[ipos]);
570c283839dSLandon J. Fuller if (error)
571c283839dSLandon J. Fuller return (error);
572c283839dSLandon J. Fuller }
573c283839dSLandon J. Fuller
574c283839dSLandon J. Fuller /* Adjust output position; this was already verified to
575c283839dSLandon J. Fuller * not overflow/underflow during SPROM opcode
576c283839dSLandon J. Fuller * evaluation */
577c283839dSLandon J. Fuller if (binding->skip_in_negative) {
578c283839dSLandon J. Fuller offset -= skip_out_bytes;
579c283839dSLandon J. Fuller } else {
580c283839dSLandon J. Fuller offset += skip_out_bytes;
581c283839dSLandon J. Fuller }
582c283839dSLandon J. Fuller
583c283839dSLandon J. Fuller /* Skip advancing input if additional bindings are
584c283839dSLandon J. Fuller * required to fully encode intv */
585c283839dSLandon J. Fuller if (binding->skip_out == 0)
586c283839dSLandon J. Fuller continue;
587c283839dSLandon J. Fuller
588c283839dSLandon J. Fuller /* Advance input position */
589c283839dSLandon J. Fuller if (SIZE_MAX - binding->skip_out < ipos) {
590c283839dSLandon J. Fuller BHND_NV_LOG("output skip %u would overflow "
591c283839dSLandon J. Fuller "%zu\n", binding->skip_out, ipos);
592c283839dSLandon J. Fuller return (EINVAL);
593c283839dSLandon J. Fuller }
594c283839dSLandon J. Fuller
595c283839dSLandon J. Fuller ipos += binding->skip_out;
596c283839dSLandon J. Fuller }
597c283839dSLandon J. Fuller }
598c283839dSLandon J. Fuller
599c283839dSLandon J. Fuller /* Did we iterate all bindings until hitting end of the variable
600c283839dSLandon J. Fuller * definition? */
601c283839dSLandon J. Fuller BHND_NV_ASSERT(error != 0, ("loop terminated early"));
602c283839dSLandon J. Fuller if (error != ENOENT)
603c283839dSLandon J. Fuller return (error);
604c283839dSLandon J. Fuller
605c283839dSLandon J. Fuller return (0);
606c283839dSLandon J. Fuller }
607c283839dSLandon J. Fuller
608c283839dSLandon J. Fuller static int
bhnd_nvram_sprom_serialize(bhnd_nvram_data_class * cls,bhnd_nvram_plist * props,bhnd_nvram_plist * options,void * outp,size_t * olen)609c283839dSLandon J. Fuller bhnd_nvram_sprom_serialize(bhnd_nvram_data_class *cls, bhnd_nvram_plist *props,
610c283839dSLandon J. Fuller bhnd_nvram_plist *options, void *outp, size_t *olen)
611c283839dSLandon J. Fuller {
612c283839dSLandon J. Fuller bhnd_sprom_opcode_state state;
613c283839dSLandon J. Fuller struct bhnd_nvram_io *io;
614c283839dSLandon J. Fuller bhnd_nvram_prop *prop;
615c283839dSLandon J. Fuller bhnd_sprom_opcode_idx_entry *entry;
616c283839dSLandon J. Fuller const bhnd_sprom_layout *layout;
617c283839dSLandon J. Fuller size_t limit;
618c283839dSLandon J. Fuller uint8_t crc;
619c283839dSLandon J. Fuller uint8_t sromrev;
620c283839dSLandon J. Fuller int error;
621c283839dSLandon J. Fuller
622c283839dSLandon J. Fuller limit = *olen;
623c283839dSLandon J. Fuller layout = NULL;
624c283839dSLandon J. Fuller io = NULL;
625c283839dSLandon J. Fuller
626c283839dSLandon J. Fuller /* Fetch sromrev property */
627c283839dSLandon J. Fuller if (!bhnd_nvram_plist_contains(props, BHND_NVAR_SROMREV)) {
628c283839dSLandon J. Fuller BHND_NV_LOG("missing required property: %s\n",
629c283839dSLandon J. Fuller BHND_NVAR_SROMREV);
630c283839dSLandon J. Fuller return (EINVAL);
631c283839dSLandon J. Fuller }
632c283839dSLandon J. Fuller
633c283839dSLandon J. Fuller error = bhnd_nvram_plist_get_uint8(props, BHND_NVAR_SROMREV, &sromrev);
634c283839dSLandon J. Fuller if (error) {
635c283839dSLandon J. Fuller BHND_NV_LOG("error reading sromrev property: %d\n", error);
636c283839dSLandon J. Fuller return (EFTYPE);
637c283839dSLandon J. Fuller }
638c283839dSLandon J. Fuller
639c283839dSLandon J. Fuller /* Find SPROM layout definition */
640c283839dSLandon J. Fuller if ((layout = bhnd_nvram_sprom_get_layout(sromrev)) == NULL) {
641c283839dSLandon J. Fuller BHND_NV_LOG("unsupported sromrev: %hhu\n", sromrev);
642c283839dSLandon J. Fuller return (EFTYPE);
643c283839dSLandon J. Fuller }
644c283839dSLandon J. Fuller
645c283839dSLandon J. Fuller /* Provide required size to caller */
646c283839dSLandon J. Fuller *olen = layout->size;
647c283839dSLandon J. Fuller if (outp == NULL)
648c283839dSLandon J. Fuller return (0);
649c283839dSLandon J. Fuller else if (limit < *olen)
650c283839dSLandon J. Fuller return (ENOMEM);
651c283839dSLandon J. Fuller
652c283839dSLandon J. Fuller /* Initialize SPROM layout interpreter */
653c283839dSLandon J. Fuller if ((error = bhnd_sprom_opcode_init(&state, layout))) {
654c283839dSLandon J. Fuller BHND_NV_LOG("error initializing opcode state: %d\n", error);
655c283839dSLandon J. Fuller return (ENXIO);
656c283839dSLandon J. Fuller }
657c283839dSLandon J. Fuller
658c283839dSLandon J. Fuller /* Check for unsupported properties */
659c283839dSLandon J. Fuller prop = NULL;
660c283839dSLandon J. Fuller while ((prop = bhnd_nvram_plist_next(props, prop)) != NULL) {
661c283839dSLandon J. Fuller const char *name;
662c283839dSLandon J. Fuller
663c283839dSLandon J. Fuller /* Fetch the corresponding SPROM layout index entry */
664c283839dSLandon J. Fuller name = bhnd_nvram_prop_name(prop);
665c283839dSLandon J. Fuller entry = bhnd_sprom_opcode_index_find(&state, name);
666c283839dSLandon J. Fuller if (entry == NULL) {
667c283839dSLandon J. Fuller BHND_NV_LOG("property '%s' unsupported by sromrev "
668c283839dSLandon J. Fuller "%hhu\n", name, layout->rev);
669c283839dSLandon J. Fuller error = EINVAL;
670c283839dSLandon J. Fuller goto finished;
671c283839dSLandon J. Fuller }
672c283839dSLandon J. Fuller }
673c283839dSLandon J. Fuller
674c283839dSLandon J. Fuller /* Zero-initialize output */
675c283839dSLandon J. Fuller memset(outp, 0, *olen);
676c283839dSLandon J. Fuller
677c283839dSLandon J. Fuller /* Allocate wrapping I/O context for output buffer */
678c283839dSLandon J. Fuller io = bhnd_nvram_ioptr_new(outp, *olen, *olen, BHND_NVRAM_IOPTR_RDWR);
679c283839dSLandon J. Fuller if (io == NULL) {
680c283839dSLandon J. Fuller error = ENOMEM;
681c283839dSLandon J. Fuller goto finished;
682c283839dSLandon J. Fuller }
683c283839dSLandon J. Fuller
684c283839dSLandon J. Fuller /*
685c283839dSLandon J. Fuller * Serialize all SPROM variable data.
686c283839dSLandon J. Fuller */
687c283839dSLandon J. Fuller entry = NULL;
688c283839dSLandon J. Fuller while ((entry = bhnd_sprom_opcode_index_next(&state, entry)) != NULL) {
689c283839dSLandon J. Fuller const struct bhnd_nvram_vardefn *var;
690c283839dSLandon J. Fuller bhnd_nvram_val *val;
691c283839dSLandon J. Fuller
692c283839dSLandon J. Fuller var = bhnd_nvram_get_vardefn(entry->vid);
693c283839dSLandon J. Fuller BHND_NV_ASSERT(var != NULL, ("missing variable definition"));
694c283839dSLandon J. Fuller
695c283839dSLandon J. Fuller /* Fetch prop; will be NULL if unavailable */
696c283839dSLandon J. Fuller prop = bhnd_nvram_plist_get_prop(props, var->name);
697c283839dSLandon J. Fuller if (prop != NULL) {
698c283839dSLandon J. Fuller val = bhnd_nvram_prop_val(prop);
699c283839dSLandon J. Fuller } else {
700c283839dSLandon J. Fuller val = BHND_NVRAM_VAL_NULL;
701c283839dSLandon J. Fuller }
702c283839dSLandon J. Fuller
703c283839dSLandon J. Fuller /* Attempt to serialize the property value to the appropriate
704c283839dSLandon J. Fuller * offset within the output buffer */
705c283839dSLandon J. Fuller error = bhnd_nvram_sprom_write_var(&state, entry, val, io);
706c283839dSLandon J. Fuller if (error) {
707c283839dSLandon J. Fuller BHND_NV_LOG("error serializing %s to required type "
708c283839dSLandon J. Fuller "%s: %d\n", var->name,
709c283839dSLandon J. Fuller bhnd_nvram_type_name(var->type), error);
710c283839dSLandon J. Fuller
711c283839dSLandon J. Fuller /* ENOMEM is reserved for signaling that the output
712c283839dSLandon J. Fuller * buffer capacity is insufficient */
713c283839dSLandon J. Fuller if (error == ENOMEM)
714c283839dSLandon J. Fuller error = EINVAL;
715c283839dSLandon J. Fuller
716c283839dSLandon J. Fuller goto finished;
717c283839dSLandon J. Fuller }
718c283839dSLandon J. Fuller }
719c283839dSLandon J. Fuller
720c283839dSLandon J. Fuller /*
721c283839dSLandon J. Fuller * Write magic value, if any.
722c283839dSLandon J. Fuller */
723c283839dSLandon J. Fuller if (!(layout->flags & SPROM_LAYOUT_MAGIC_NONE)) {
724c283839dSLandon J. Fuller uint16_t magic;
725c283839dSLandon J. Fuller
726c283839dSLandon J. Fuller magic = htole16(layout->magic_value);
727c283839dSLandon J. Fuller error = bhnd_nvram_io_write(io, layout->magic_offset, &magic,
728c283839dSLandon J. Fuller sizeof(magic));
729c283839dSLandon J. Fuller if (error) {
730c283839dSLandon J. Fuller BHND_NV_LOG("error writing magic value: %d\n", error);
731c283839dSLandon J. Fuller goto finished;
732c283839dSLandon J. Fuller }
733c283839dSLandon J. Fuller }
734c283839dSLandon J. Fuller
735c283839dSLandon J. Fuller /* Calculate the CRC over all SPROM data, not including the CRC byte. */
736c283839dSLandon J. Fuller crc = ~bhnd_nvram_crc8(outp, layout->crc_offset,
737c283839dSLandon J. Fuller BHND_NVRAM_CRC8_INITIAL);
738c283839dSLandon J. Fuller
739c283839dSLandon J. Fuller /* Write the checksum. */
740c283839dSLandon J. Fuller error = bhnd_nvram_io_write(io, layout->crc_offset, &crc, sizeof(crc));
741c283839dSLandon J. Fuller if (error) {
742c283839dSLandon J. Fuller BHND_NV_LOG("error writing CRC value: %d\n", error);
743c283839dSLandon J. Fuller goto finished;
744c283839dSLandon J. Fuller }
745c283839dSLandon J. Fuller
746c283839dSLandon J. Fuller /*
747c283839dSLandon J. Fuller * Success!
748c283839dSLandon J. Fuller */
749c283839dSLandon J. Fuller error = 0;
750c283839dSLandon J. Fuller
751c283839dSLandon J. Fuller finished:
752c283839dSLandon J. Fuller bhnd_sprom_opcode_fini(&state);
753c283839dSLandon J. Fuller
754c283839dSLandon J. Fuller if (io != NULL)
755c283839dSLandon J. Fuller bhnd_nvram_io_free(io);
756c283839dSLandon J. Fuller
757c283839dSLandon J. Fuller return (error);
758c283839dSLandon J. Fuller }
759c283839dSLandon J. Fuller
76077cb4d3eSLandon J. Fuller static int
bhnd_nvram_sprom_new(struct bhnd_nvram_data * nv,struct bhnd_nvram_io * io)76177cb4d3eSLandon J. Fuller bhnd_nvram_sprom_new(struct bhnd_nvram_data *nv, struct bhnd_nvram_io *io)
76277cb4d3eSLandon J. Fuller {
76377cb4d3eSLandon J. Fuller struct bhnd_nvram_sprom *sp;
76477cb4d3eSLandon J. Fuller int error;
76577cb4d3eSLandon J. Fuller
76677cb4d3eSLandon J. Fuller sp = (struct bhnd_nvram_sprom *)nv;
76777cb4d3eSLandon J. Fuller
76877cb4d3eSLandon J. Fuller /* Identify the SPROM input data */
769591e79bcSLandon J. Fuller if ((error = bhnd_nvram_sprom_ident(io, &sp->layout)))
770591e79bcSLandon J. Fuller return (error);
771591e79bcSLandon J. Fuller
772591e79bcSLandon J. Fuller /* Copy SPROM image to our shadow buffer */
773591e79bcSLandon J. Fuller sp->data = bhnd_nvram_iobuf_copy_range(io, 0, sp->layout->size);
774591e79bcSLandon J. Fuller if (sp->data == NULL)
77577cb4d3eSLandon J. Fuller goto failed;
77677cb4d3eSLandon J. Fuller
77777cb4d3eSLandon J. Fuller /* Initialize SPROM binding eval state */
778c283839dSLandon J. Fuller if ((error = bhnd_sprom_opcode_init(&sp->state, sp->layout)))
77977cb4d3eSLandon J. Fuller goto failed;
78077cb4d3eSLandon J. Fuller
78177cb4d3eSLandon J. Fuller return (0);
78277cb4d3eSLandon J. Fuller
78377cb4d3eSLandon J. Fuller failed:
78477cb4d3eSLandon J. Fuller if (sp->data != NULL)
78577cb4d3eSLandon J. Fuller bhnd_nvram_io_free(sp->data);
78677cb4d3eSLandon J. Fuller
78777cb4d3eSLandon J. Fuller return (error);
78877cb4d3eSLandon J. Fuller }
78977cb4d3eSLandon J. Fuller
79077cb4d3eSLandon J. Fuller static void
bhnd_nvram_sprom_free(struct bhnd_nvram_data * nv)79177cb4d3eSLandon J. Fuller bhnd_nvram_sprom_free(struct bhnd_nvram_data *nv)
79277cb4d3eSLandon J. Fuller {
79377cb4d3eSLandon J. Fuller struct bhnd_nvram_sprom *sp = (struct bhnd_nvram_sprom *)nv;
79477cb4d3eSLandon J. Fuller
795c283839dSLandon J. Fuller bhnd_sprom_opcode_fini(&sp->state);
79677cb4d3eSLandon J. Fuller bhnd_nvram_io_free(sp->data);
797a7c43ebdSLandon J. Fuller }
798a7c43ebdSLandon J. Fuller
79977cb4d3eSLandon J. Fuller size_t
bhnd_nvram_sprom_count(struct bhnd_nvram_data * nv)80077cb4d3eSLandon J. Fuller bhnd_nvram_sprom_count(struct bhnd_nvram_data *nv)
80177cb4d3eSLandon J. Fuller {
80277cb4d3eSLandon J. Fuller struct bhnd_nvram_sprom *sprom = (struct bhnd_nvram_sprom *)nv;
80377cb4d3eSLandon J. Fuller return (sprom->layout->num_vars);
80477cb4d3eSLandon J. Fuller }
80577cb4d3eSLandon J. Fuller
806c283839dSLandon J. Fuller static bhnd_nvram_plist *
bhnd_nvram_sprom_options(struct bhnd_nvram_data * nv)807c283839dSLandon J. Fuller bhnd_nvram_sprom_options(struct bhnd_nvram_data *nv)
80877cb4d3eSLandon J. Fuller {
809c283839dSLandon J. Fuller return (NULL);
81077cb4d3eSLandon J. Fuller }
81177cb4d3eSLandon J. Fuller
81277cb4d3eSLandon J. Fuller static uint32_t
bhnd_nvram_sprom_caps(struct bhnd_nvram_data * nv)81377cb4d3eSLandon J. Fuller bhnd_nvram_sprom_caps(struct bhnd_nvram_data *nv)
81477cb4d3eSLandon J. Fuller {
81577cb4d3eSLandon J. Fuller return (BHND_NVRAM_DATA_CAP_INDEXED);
81677cb4d3eSLandon J. Fuller }
81777cb4d3eSLandon J. Fuller
81877cb4d3eSLandon J. Fuller static const char *
bhnd_nvram_sprom_next(struct bhnd_nvram_data * nv,void ** cookiep)81977cb4d3eSLandon J. Fuller bhnd_nvram_sprom_next(struct bhnd_nvram_data *nv, void **cookiep)
82077cb4d3eSLandon J. Fuller {
82177cb4d3eSLandon J. Fuller struct bhnd_nvram_sprom *sp;
822c283839dSLandon J. Fuller bhnd_sprom_opcode_idx_entry *entry;
82377cb4d3eSLandon J. Fuller const struct bhnd_nvram_vardefn *var;
82477cb4d3eSLandon J. Fuller
82577cb4d3eSLandon J. Fuller sp = (struct bhnd_nvram_sprom *)nv;
82677cb4d3eSLandon J. Fuller
827c283839dSLandon J. Fuller /* Find next index entry that is not disabled by virtue of IGNALL1 */
828c283839dSLandon J. Fuller entry = *cookiep;
829c283839dSLandon J. Fuller while ((entry = bhnd_sprom_opcode_index_next(&sp->state, entry))) {
830c283839dSLandon J. Fuller /* Update cookiep and fetch variable definition */
831c283839dSLandon J. Fuller *cookiep = entry;
832c283839dSLandon J. Fuller var = SPROM_COOKIE_TO_NVRAM_VAR(*cookiep);
8336467a17bSLandon J. Fuller BHND_NV_ASSERT(var != NULL, ("invalid cookiep %p", cookiep));
83477cb4d3eSLandon J. Fuller
83577cb4d3eSLandon J. Fuller /* We might need to parse the variable's value to determine
83677cb4d3eSLandon J. Fuller * whether it should be treated as unset */
83777cb4d3eSLandon J. Fuller if (var->flags & BHND_NVRAM_VF_IGNALL1) {
83877cb4d3eSLandon J. Fuller int error;
83977cb4d3eSLandon J. Fuller size_t len;
84077cb4d3eSLandon J. Fuller
84177cb4d3eSLandon J. Fuller error = bhnd_nvram_sprom_getvar(nv, *cookiep, NULL,
84277cb4d3eSLandon J. Fuller &len, var->type);
84377cb4d3eSLandon J. Fuller if (error) {
84477cb4d3eSLandon J. Fuller BHND_NV_ASSERT(error == ENOENT, ("unexpected "
84577cb4d3eSLandon J. Fuller "error parsing variable: %d", error));
84677cb4d3eSLandon J. Fuller continue;
84777cb4d3eSLandon J. Fuller }
84877cb4d3eSLandon J. Fuller }
84977cb4d3eSLandon J. Fuller
85077cb4d3eSLandon J. Fuller /* Found! */
85177cb4d3eSLandon J. Fuller return (var->name);
85277cb4d3eSLandon J. Fuller }
85377cb4d3eSLandon J. Fuller
85477cb4d3eSLandon J. Fuller /* Reached end of index entries */
85577cb4d3eSLandon J. Fuller return (NULL);
85677cb4d3eSLandon J. Fuller }
85777cb4d3eSLandon J. Fuller
85877cb4d3eSLandon J. Fuller static void *
bhnd_nvram_sprom_find(struct bhnd_nvram_data * nv,const char * name)85977cb4d3eSLandon J. Fuller bhnd_nvram_sprom_find(struct bhnd_nvram_data *nv, const char *name)
86077cb4d3eSLandon J. Fuller {
86177cb4d3eSLandon J. Fuller struct bhnd_nvram_sprom *sp;
862c283839dSLandon J. Fuller bhnd_sprom_opcode_idx_entry *entry;
86377cb4d3eSLandon J. Fuller
86477cb4d3eSLandon J. Fuller sp = (struct bhnd_nvram_sprom *)nv;
86577cb4d3eSLandon J. Fuller
866c283839dSLandon J. Fuller entry = bhnd_sprom_opcode_index_find(&sp->state, name);
867c283839dSLandon J. Fuller return (entry);
86877cb4d3eSLandon J. Fuller }
86977cb4d3eSLandon J. Fuller
87077cb4d3eSLandon J. Fuller /**
871c283839dSLandon J. Fuller * Write @p value of @p type to the SPROM @p data at @p offset, applying
872c283839dSLandon J. Fuller * @p mask and @p shift, and OR with the existing data.
873c283839dSLandon J. Fuller *
874c283839dSLandon J. Fuller * @param var The NVRAM variable definition.
875c283839dSLandon J. Fuller * @param data The SPROM data to be modified.
876c283839dSLandon J. Fuller * @param type The type to write at @p offset.
877c283839dSLandon J. Fuller * @param offset The data offset to be written.
878c283839dSLandon J. Fuller * @param mask The mask to be applied to @p value after shifting.
879c283839dSLandon J. Fuller * @param shift The shift to be applied to @p value; if positive, a left
880c283839dSLandon J. Fuller * shift will be applied, if negative, a right shift (this is the reverse of the
881c283839dSLandon J. Fuller * decoding behavior)
882c283839dSLandon J. Fuller * @param value The value to be written. The parsed value will be OR'd with the
883c283839dSLandon J. Fuller * current contents of @p data at @p offset.
884c283839dSLandon J. Fuller */
885c283839dSLandon J. Fuller static int
bhnd_nvram_sprom_write_offset(const struct bhnd_nvram_vardefn * var,struct bhnd_nvram_io * data,bhnd_nvram_type type,size_t offset,uint32_t mask,int8_t shift,uint32_t value)886c283839dSLandon J. Fuller bhnd_nvram_sprom_write_offset(const struct bhnd_nvram_vardefn *var,
887c283839dSLandon J. Fuller struct bhnd_nvram_io *data, bhnd_nvram_type type, size_t offset,
888c283839dSLandon J. Fuller uint32_t mask, int8_t shift, uint32_t value)
889c283839dSLandon J. Fuller {
890c283839dSLandon J. Fuller union bhnd_nvram_sprom_storage scratch;
891c283839dSLandon J. Fuller int error;
892c283839dSLandon J. Fuller
893c283839dSLandon J. Fuller #define NV_WRITE_INT(_widen, _repr, _swap) do { \
894c283839dSLandon J. Fuller /* Narrow the 32-bit representation */ \
895c283839dSLandon J. Fuller scratch._repr[1] = (_widen)value; \
896c283839dSLandon J. Fuller \
897c283839dSLandon J. Fuller /* Shift and mask the new value */ \
898c283839dSLandon J. Fuller if (shift > 0) \
899c283839dSLandon J. Fuller scratch._repr[1] <<= shift; \
900c283839dSLandon J. Fuller else if (shift < 0) \
901c283839dSLandon J. Fuller scratch._repr[1] >>= -shift; \
902c283839dSLandon J. Fuller scratch._repr[1] &= mask; \
903c283839dSLandon J. Fuller \
904c283839dSLandon J. Fuller /* Swap to output byte order */ \
905c283839dSLandon J. Fuller scratch._repr[1] = _swap(scratch._repr[1]); \
906c283839dSLandon J. Fuller \
907c283839dSLandon J. Fuller /* Fetch the current value */ \
908c283839dSLandon J. Fuller error = bhnd_nvram_io_read(data, offset, \
909c283839dSLandon J. Fuller &scratch._repr[0], sizeof(scratch._repr[0])); \
910c283839dSLandon J. Fuller if (error) { \
911c283839dSLandon J. Fuller BHND_NV_LOG("error reading %s SPROM offset " \
912c283839dSLandon J. Fuller "%#zx: %d\n", var->name, offset, error); \
913c283839dSLandon J. Fuller return (EFTYPE); \
914c283839dSLandon J. Fuller } \
915c283839dSLandon J. Fuller \
916c283839dSLandon J. Fuller /* Mask and set our new value's bits in the current \
917c283839dSLandon J. Fuller * value */ \
918c283839dSLandon J. Fuller if (shift >= 0) \
919c283839dSLandon J. Fuller scratch._repr[0] &= ~_swap(mask << shift); \
920c283839dSLandon J. Fuller else if (shift < 0) \
921c283839dSLandon J. Fuller scratch._repr[0] &= ~_swap(mask >> (-shift)); \
922c283839dSLandon J. Fuller scratch._repr[0] |= scratch._repr[1]; \
923c283839dSLandon J. Fuller \
924c283839dSLandon J. Fuller /* Perform write */ \
925c283839dSLandon J. Fuller error = bhnd_nvram_io_write(data, offset, \
926c283839dSLandon J. Fuller &scratch._repr[0], sizeof(scratch._repr[0])); \
927c283839dSLandon J. Fuller if (error) { \
928c283839dSLandon J. Fuller BHND_NV_LOG("error writing %s SPROM offset " \
929c283839dSLandon J. Fuller "%#zx: %d\n", var->name, offset, error); \
930c283839dSLandon J. Fuller return (EFTYPE); \
931c283839dSLandon J. Fuller } \
932c283839dSLandon J. Fuller } while(0)
933c283839dSLandon J. Fuller
934c283839dSLandon J. Fuller /* Apply mask/shift and widen to a common 32bit representation */
935c283839dSLandon J. Fuller switch (type) {
936c283839dSLandon J. Fuller case BHND_NVRAM_TYPE_UINT8:
937c283839dSLandon J. Fuller NV_WRITE_INT(uint32_t, u8, );
938c283839dSLandon J. Fuller break;
939c283839dSLandon J. Fuller case BHND_NVRAM_TYPE_UINT16:
940c283839dSLandon J. Fuller NV_WRITE_INT(uint32_t, u16, htole16);
941c283839dSLandon J. Fuller break;
942c283839dSLandon J. Fuller case BHND_NVRAM_TYPE_UINT32:
943c283839dSLandon J. Fuller NV_WRITE_INT(uint32_t, u32, htole32);
944c283839dSLandon J. Fuller break;
945c283839dSLandon J. Fuller case BHND_NVRAM_TYPE_INT8:
946c283839dSLandon J. Fuller NV_WRITE_INT(int32_t, i8, );
947c283839dSLandon J. Fuller break;
948c283839dSLandon J. Fuller case BHND_NVRAM_TYPE_INT16:
949c283839dSLandon J. Fuller NV_WRITE_INT(int32_t, i16, htole16);
950c283839dSLandon J. Fuller break;
951c283839dSLandon J. Fuller case BHND_NVRAM_TYPE_INT32:
952c283839dSLandon J. Fuller NV_WRITE_INT(int32_t, i32, htole32);
953c283839dSLandon J. Fuller break;
954c283839dSLandon J. Fuller case BHND_NVRAM_TYPE_CHAR:
955c283839dSLandon J. Fuller NV_WRITE_INT(uint32_t, u8, );
956c283839dSLandon J. Fuller break;
957c283839dSLandon J. Fuller default:
958c283839dSLandon J. Fuller BHND_NV_LOG("unhandled %s offset type: %d\n", var->name, type);
959c283839dSLandon J. Fuller return (EFTYPE);
960c283839dSLandon J. Fuller }
961c283839dSLandon J. Fuller #undef NV_WRITE_INT
962c283839dSLandon J. Fuller
963c283839dSLandon J. Fuller return (0);
964c283839dSLandon J. Fuller }
965c283839dSLandon J. Fuller
966c283839dSLandon J. Fuller /**
967c283839dSLandon J. Fuller * Read the value of @p type from the SPROM @p data at @p offset, apply @p mask
96877cb4d3eSLandon J. Fuller * and @p shift, and OR with the existing @p value.
96977cb4d3eSLandon J. Fuller *
970c283839dSLandon J. Fuller * @param var The NVRAM variable definition.
971c283839dSLandon J. Fuller * @param data The SPROM data to be decoded.
97277cb4d3eSLandon J. Fuller * @param type The type to read at @p offset
97377cb4d3eSLandon J. Fuller * @param offset The data offset to be read.
97477cb4d3eSLandon J. Fuller * @param mask The mask to be applied to the value read at @p offset.
97577cb4d3eSLandon J. Fuller * @param shift The shift to be applied after masking; if positive, a right
97677cb4d3eSLandon J. Fuller * shift will be applied, if negative, a left shift.
97777cb4d3eSLandon J. Fuller * @param value The read destination; the parsed value will be OR'd with the
97877cb4d3eSLandon J. Fuller * current contents of @p value.
97977cb4d3eSLandon J. Fuller */
98077cb4d3eSLandon J. Fuller static int
bhnd_nvram_sprom_read_offset(const struct bhnd_nvram_vardefn * var,struct bhnd_nvram_io * data,bhnd_nvram_type type,size_t offset,uint32_t mask,int8_t shift,uint32_t * value)981c283839dSLandon J. Fuller bhnd_nvram_sprom_read_offset(const struct bhnd_nvram_vardefn *var,
982c283839dSLandon J. Fuller struct bhnd_nvram_io *data, bhnd_nvram_type type, size_t offset,
983c283839dSLandon J. Fuller uint32_t mask, int8_t shift, uint32_t *value)
98477cb4d3eSLandon J. Fuller {
985c283839dSLandon J. Fuller union bhnd_nvram_sprom_storage scratch;
98677cb4d3eSLandon J. Fuller int error;
98777cb4d3eSLandon J. Fuller
988c283839dSLandon J. Fuller #define NV_PARSE_INT(_widen, _repr, _swap) do { \
989c283839dSLandon J. Fuller /* Perform read */ \
990c283839dSLandon J. Fuller error = bhnd_nvram_io_read(data, offset, \
991c283839dSLandon J. Fuller &scratch._repr[0], sizeof(scratch._repr[0])); \
992c283839dSLandon J. Fuller if (error) { \
993c283839dSLandon J. Fuller BHND_NV_LOG("error reading %s SPROM offset " \
994c283839dSLandon J. Fuller "%#zx: %d\n", var->name, offset, error); \
995c283839dSLandon J. Fuller return (EFTYPE); \
99677cb4d3eSLandon J. Fuller } \
99777cb4d3eSLandon J. Fuller \
998c283839dSLandon J. Fuller /* Swap to host byte order */ \
999c283839dSLandon J. Fuller scratch._repr[0] = _swap(scratch._repr[0]); \
1000c283839dSLandon J. Fuller \
1001c283839dSLandon J. Fuller /* Mask and shift the value */ \
1002c283839dSLandon J. Fuller scratch._repr[0] &= mask; \
1003c283839dSLandon J. Fuller if (shift > 0) { \
1004c283839dSLandon J. Fuller scratch. _repr[0] >>= shift; \
1005c283839dSLandon J. Fuller } else if (shift < 0) { \
1006c283839dSLandon J. Fuller scratch. _repr[0] <<= -shift; \
1007c283839dSLandon J. Fuller } \
1008c283839dSLandon J. Fuller \
1009c283839dSLandon J. Fuller /* Widen to 32-bit representation and OR with current \
1010c283839dSLandon J. Fuller * value */ \
1011c283839dSLandon J. Fuller (*value) |= (_widen)scratch._repr[0]; \
101277cb4d3eSLandon J. Fuller } while(0)
101377cb4d3eSLandon J. Fuller
101477cb4d3eSLandon J. Fuller /* Apply mask/shift and widen to a common 32bit representation */
101577cb4d3eSLandon J. Fuller switch (type) {
101677cb4d3eSLandon J. Fuller case BHND_NVRAM_TYPE_UINT8:
1017c283839dSLandon J. Fuller NV_PARSE_INT(uint32_t, u8, );
101877cb4d3eSLandon J. Fuller break;
101977cb4d3eSLandon J. Fuller case BHND_NVRAM_TYPE_UINT16:
1020c283839dSLandon J. Fuller NV_PARSE_INT(uint32_t, u16, le16toh);
102177cb4d3eSLandon J. Fuller break;
102277cb4d3eSLandon J. Fuller case BHND_NVRAM_TYPE_UINT32:
1023c283839dSLandon J. Fuller NV_PARSE_INT(uint32_t, u32, le32toh);
102477cb4d3eSLandon J. Fuller break;
102577cb4d3eSLandon J. Fuller case BHND_NVRAM_TYPE_INT8:
1026c283839dSLandon J. Fuller NV_PARSE_INT(int32_t, i8, );
102777cb4d3eSLandon J. Fuller break;
102877cb4d3eSLandon J. Fuller case BHND_NVRAM_TYPE_INT16:
1029c283839dSLandon J. Fuller NV_PARSE_INT(int32_t, i16, le16toh);
103077cb4d3eSLandon J. Fuller break;
103177cb4d3eSLandon J. Fuller case BHND_NVRAM_TYPE_INT32:
1032c283839dSLandon J. Fuller NV_PARSE_INT(int32_t, i32, le32toh);
103377cb4d3eSLandon J. Fuller break;
103477cb4d3eSLandon J. Fuller case BHND_NVRAM_TYPE_CHAR:
1035c283839dSLandon J. Fuller NV_PARSE_INT(uint32_t, u8, );
103677cb4d3eSLandon J. Fuller break;
103777cb4d3eSLandon J. Fuller default:
103877cb4d3eSLandon J. Fuller BHND_NV_LOG("unhandled %s offset type: %d\n", var->name, type);
103977cb4d3eSLandon J. Fuller return (EFTYPE);
104077cb4d3eSLandon J. Fuller }
1041c283839dSLandon J. Fuller #undef NV_PARSE_INT
104277cb4d3eSLandon J. Fuller
104377cb4d3eSLandon J. Fuller return (0);
104477cb4d3eSLandon J. Fuller }
104577cb4d3eSLandon J. Fuller
104619be09f3SLandon J. Fuller /**
1047591e79bcSLandon J. Fuller * Read a SPROM variable value from @p io.
1048591e79bcSLandon J. Fuller *
1049591e79bcSLandon J. Fuller * @param state The SPROM opcode state describing the layout of @p io.
1050591e79bcSLandon J. Fuller * @param entry The variable's SPROM opcode index entry.
1051591e79bcSLandon J. Fuller * @param io The input I/O context.
1052591e79bcSLandon J. Fuller * @param storage Storage to be used with @p val.
1053591e79bcSLandon J. Fuller * @param[out] val Value instance to be initialized with the
1054591e79bcSLandon J. Fuller * parsed variable data.
105519be09f3SLandon J. Fuller *
105619be09f3SLandon J. Fuller * The returned @p val instance will hold a borrowed reference to @p storage,
105719be09f3SLandon J. Fuller * and must be copied via bhnd_nvram_val_copy() if it will be referenced beyond
105819be09f3SLandon J. Fuller * the lifetime of @p storage.
105919be09f3SLandon J. Fuller *
106019be09f3SLandon J. Fuller * The caller is responsible for releasing any allocated value state
106119be09f3SLandon J. Fuller * via bhnd_nvram_val_release().
106219be09f3SLandon J. Fuller */
106377cb4d3eSLandon J. Fuller static int
bhnd_nvram_sprom_read_var(struct bhnd_sprom_opcode_state * state,struct bhnd_sprom_opcode_idx_entry * entry,struct bhnd_nvram_io * io,union bhnd_nvram_sprom_storage * storage,bhnd_nvram_val * val)1064591e79bcSLandon J. Fuller bhnd_nvram_sprom_read_var(struct bhnd_sprom_opcode_state *state,
1065591e79bcSLandon J. Fuller struct bhnd_sprom_opcode_idx_entry *entry, struct bhnd_nvram_io *io,
106619be09f3SLandon J. Fuller union bhnd_nvram_sprom_storage *storage, bhnd_nvram_val *val)
106777cb4d3eSLandon J. Fuller {
106877cb4d3eSLandon J. Fuller union bhnd_nvram_sprom_storage *inp;
1069591e79bcSLandon J. Fuller const struct bhnd_nvram_vardefn *var;
107077cb4d3eSLandon J. Fuller bhnd_nvram_type var_btype;
1071c283839dSLandon J. Fuller uint32_t intv;
107277cb4d3eSLandon J. Fuller size_t ilen, ipos, iwidth;
107377cb4d3eSLandon J. Fuller size_t nelem;
107477cb4d3eSLandon J. Fuller bool all_bits_set;
107577cb4d3eSLandon J. Fuller int error;
107677cb4d3eSLandon J. Fuller
107777cb4d3eSLandon J. Fuller /* Fetch canonical variable definition */
1078591e79bcSLandon J. Fuller var = bhnd_nvram_get_vardefn(entry->vid);
1079591e79bcSLandon J. Fuller BHND_NV_ASSERT(var != NULL, ("invalid entry"));
108077cb4d3eSLandon J. Fuller
108177cb4d3eSLandon J. Fuller /*
108277cb4d3eSLandon J. Fuller * Fetch the array length from the SPROM variable definition.
108377cb4d3eSLandon J. Fuller *
108477cb4d3eSLandon J. Fuller * This generally be identical to the array length provided by the
108577cb4d3eSLandon J. Fuller * canonical NVRAM variable definition, but some SPROM layouts may
108677cb4d3eSLandon J. Fuller * define a smaller element count.
108777cb4d3eSLandon J. Fuller */
1088591e79bcSLandon J. Fuller if ((error = bhnd_sprom_opcode_eval_var(state, entry))) {
108977cb4d3eSLandon J. Fuller BHND_NV_LOG("variable evaluation failed: %d\n", error);
109077cb4d3eSLandon J. Fuller return (error);
109177cb4d3eSLandon J. Fuller }
109277cb4d3eSLandon J. Fuller
1093591e79bcSLandon J. Fuller nelem = state->var.nelem;
109477cb4d3eSLandon J. Fuller if (nelem > var->nelem) {
109577cb4d3eSLandon J. Fuller BHND_NV_LOG("SPROM array element count %zu cannot be "
109677cb4d3eSLandon J. Fuller "represented by '%s' element count of %hhu\n", nelem,
109777cb4d3eSLandon J. Fuller var->name, var->nelem);
109877cb4d3eSLandon J. Fuller return (EFTYPE);
109977cb4d3eSLandon J. Fuller }
110077cb4d3eSLandon J. Fuller
110177cb4d3eSLandon J. Fuller /* Fetch the var's base element type */
110277cb4d3eSLandon J. Fuller var_btype = bhnd_nvram_base_type(var->type);
110377cb4d3eSLandon J. Fuller
110477cb4d3eSLandon J. Fuller /* Calculate total byte length of the native encoding */
11059be0790dSLandon J. Fuller if ((iwidth = bhnd_nvram_value_size(NULL, 0, var_btype, 1)) == 0) {
110677cb4d3eSLandon J. Fuller /* SPROM does not use (and we do not support) decoding of
110777cb4d3eSLandon J. Fuller * variable-width data types */
110877cb4d3eSLandon J. Fuller BHND_NV_LOG("invalid SPROM data type: %d", var->type);
110977cb4d3eSLandon J. Fuller return (EFTYPE);
111077cb4d3eSLandon J. Fuller }
111177cb4d3eSLandon J. Fuller ilen = nelem * iwidth;
111277cb4d3eSLandon J. Fuller
111319be09f3SLandon J. Fuller /* Decode into our caller's local storage */
111419be09f3SLandon J. Fuller inp = storage;
111519be09f3SLandon J. Fuller if (ilen > sizeof(*storage)) {
111677cb4d3eSLandon J. Fuller BHND_NV_LOG("error decoding '%s', SPROM_ARRAY_MAXLEN "
111777cb4d3eSLandon J. Fuller "incorrect\n", var->name);
111877cb4d3eSLandon J. Fuller return (EFTYPE);
111977cb4d3eSLandon J. Fuller }
112077cb4d3eSLandon J. Fuller
112177cb4d3eSLandon J. Fuller /* Zero-initialize our decode buffer; any output elements skipped
112277cb4d3eSLandon J. Fuller * during decode should default to zero. */
112377cb4d3eSLandon J. Fuller memset(inp, 0, ilen);
112477cb4d3eSLandon J. Fuller
112577cb4d3eSLandon J. Fuller /*
112677cb4d3eSLandon J. Fuller * Decode the SPROM data, iteratively decoding up to nelem values.
112777cb4d3eSLandon J. Fuller */
1128591e79bcSLandon J. Fuller if ((error = bhnd_sprom_opcode_seek(state, entry))) {
112977cb4d3eSLandon J. Fuller BHND_NV_LOG("variable seek failed: %d\n", error);
113077cb4d3eSLandon J. Fuller return (error);
113177cb4d3eSLandon J. Fuller }
113277cb4d3eSLandon J. Fuller
113377cb4d3eSLandon J. Fuller ipos = 0;
1134c283839dSLandon J. Fuller intv = 0x0;
113577cb4d3eSLandon J. Fuller if (var->flags & BHND_NVRAM_VF_IGNALL1)
113677cb4d3eSLandon J. Fuller all_bits_set = true;
113777cb4d3eSLandon J. Fuller else
113877cb4d3eSLandon J. Fuller all_bits_set = false;
1139591e79bcSLandon J. Fuller while ((error = bhnd_sprom_opcode_next_binding(state)) == 0) {
1140c283839dSLandon J. Fuller bhnd_sprom_opcode_bind *binding;
1141c283839dSLandon J. Fuller bhnd_sprom_opcode_var *binding_var;
114277cb4d3eSLandon J. Fuller bhnd_nvram_type intv_type;
114377cb4d3eSLandon J. Fuller size_t offset;
114477cb4d3eSLandon J. Fuller size_t nbyte;
114577cb4d3eSLandon J. Fuller uint32_t skip_in_bytes;
114677cb4d3eSLandon J. Fuller void *ptr;
114777cb4d3eSLandon J. Fuller
114877cb4d3eSLandon J. Fuller BHND_NV_ASSERT(
1149591e79bcSLandon J. Fuller state->var_state >= SPROM_OPCODE_VAR_STATE_OPEN,
115077cb4d3eSLandon J. Fuller ("invalid var state"));
1151591e79bcSLandon J. Fuller BHND_NV_ASSERT(state->var.have_bind, ("invalid bind state"));
115277cb4d3eSLandon J. Fuller
1153591e79bcSLandon J. Fuller binding_var = &state->var;
1154591e79bcSLandon J. Fuller binding = &state->var.bind;
115577cb4d3eSLandon J. Fuller
115677cb4d3eSLandon J. Fuller if (ipos >= nelem) {
115777cb4d3eSLandon J. Fuller BHND_NV_LOG("output skip %u positioned "
115877cb4d3eSLandon J. Fuller "%zu beyond nelem %zu\n",
115977cb4d3eSLandon J. Fuller binding->skip_out, ipos, nelem);
116077cb4d3eSLandon J. Fuller return (EINVAL);
116177cb4d3eSLandon J. Fuller }
116277cb4d3eSLandon J. Fuller
116377cb4d3eSLandon J. Fuller /* Calculate input skip bytes for this binding */
116477cb4d3eSLandon J. Fuller skip_in_bytes = binding->skip_in;
1165591e79bcSLandon J. Fuller error = bhnd_sprom_opcode_apply_scale(state, &skip_in_bytes);
116677cb4d3eSLandon J. Fuller if (error)
116777cb4d3eSLandon J. Fuller return (error);
116877cb4d3eSLandon J. Fuller
116977cb4d3eSLandon J. Fuller /* Bind */
1170591e79bcSLandon J. Fuller offset = state->offset;
117177cb4d3eSLandon J. Fuller for (size_t i = 0; i < binding->count; i++) {
117277cb4d3eSLandon J. Fuller /* Read the offset value, OR'ing with the current
117377cb4d3eSLandon J. Fuller * value of intv */
1174591e79bcSLandon J. Fuller error = bhnd_nvram_sprom_read_offset(var, io,
117577cb4d3eSLandon J. Fuller binding_var->base_type,
117677cb4d3eSLandon J. Fuller offset,
117777cb4d3eSLandon J. Fuller binding_var->mask,
117877cb4d3eSLandon J. Fuller binding_var->shift,
117977cb4d3eSLandon J. Fuller &intv);
118077cb4d3eSLandon J. Fuller if (error)
118177cb4d3eSLandon J. Fuller return (error);
118277cb4d3eSLandon J. Fuller
118377cb4d3eSLandon J. Fuller /* If IGNALL1, record whether value does not have
118477cb4d3eSLandon J. Fuller * all bits set. */
118577cb4d3eSLandon J. Fuller if (var->flags & BHND_NVRAM_VF_IGNALL1 &&
118677cb4d3eSLandon J. Fuller all_bits_set)
118777cb4d3eSLandon J. Fuller {
118877cb4d3eSLandon J. Fuller uint32_t all1;
118977cb4d3eSLandon J. Fuller
119077cb4d3eSLandon J. Fuller all1 = binding_var->mask;
119177cb4d3eSLandon J. Fuller if (binding_var->shift > 0)
119277cb4d3eSLandon J. Fuller all1 >>= binding_var->shift;
119377cb4d3eSLandon J. Fuller else if (binding_var->shift < 0)
119477cb4d3eSLandon J. Fuller all1 <<= -binding_var->shift;
119577cb4d3eSLandon J. Fuller
1196c283839dSLandon J. Fuller if ((intv & all1) != all1)
119777cb4d3eSLandon J. Fuller all_bits_set = false;
119877cb4d3eSLandon J. Fuller }
119977cb4d3eSLandon J. Fuller
120077cb4d3eSLandon J. Fuller /* Adjust input position; this was already verified to
120177cb4d3eSLandon J. Fuller * not overflow/underflow during SPROM opcode
120277cb4d3eSLandon J. Fuller * evaluation */
120377cb4d3eSLandon J. Fuller if (binding->skip_in_negative) {
120477cb4d3eSLandon J. Fuller offset -= skip_in_bytes;
120577cb4d3eSLandon J. Fuller } else {
120677cb4d3eSLandon J. Fuller offset += skip_in_bytes;
120777cb4d3eSLandon J. Fuller }
120877cb4d3eSLandon J. Fuller
120977cb4d3eSLandon J. Fuller /* Skip writing to inp if additional bindings are
121077cb4d3eSLandon J. Fuller * required to fully populate intv */
121177cb4d3eSLandon J. Fuller if (binding->skip_out == 0)
121277cb4d3eSLandon J. Fuller continue;
121377cb4d3eSLandon J. Fuller
121477cb4d3eSLandon J. Fuller /* We use bhnd_nvram_value_coerce() to perform
121577cb4d3eSLandon J. Fuller * overflow-checked coercion from the widened
121677cb4d3eSLandon J. Fuller * uint32/int32 intv value to the requested output
121777cb4d3eSLandon J. Fuller * type */
121877cb4d3eSLandon J. Fuller if (bhnd_nvram_is_signed_type(var_btype))
121977cb4d3eSLandon J. Fuller intv_type = BHND_NVRAM_TYPE_INT32;
122077cb4d3eSLandon J. Fuller else
122177cb4d3eSLandon J. Fuller intv_type = BHND_NVRAM_TYPE_UINT32;
122277cb4d3eSLandon J. Fuller
122377cb4d3eSLandon J. Fuller /* Calculate address of the current element output
122477cb4d3eSLandon J. Fuller * position */
122577cb4d3eSLandon J. Fuller ptr = (uint8_t *)inp + (iwidth * ipos);
122677cb4d3eSLandon J. Fuller
122777cb4d3eSLandon J. Fuller /* Perform coercion of the array element */
122877cb4d3eSLandon J. Fuller nbyte = iwidth;
1229c283839dSLandon J. Fuller error = bhnd_nvram_value_coerce(&intv, sizeof(intv),
1230c283839dSLandon J. Fuller intv_type, ptr, &nbyte, var_btype);
123177cb4d3eSLandon J. Fuller if (error)
123277cb4d3eSLandon J. Fuller return (error);
123377cb4d3eSLandon J. Fuller
123477cb4d3eSLandon J. Fuller /* Clear temporary state */
1235c283839dSLandon J. Fuller intv = 0x0;
123677cb4d3eSLandon J. Fuller
123777cb4d3eSLandon J. Fuller /* Advance output position */
123877cb4d3eSLandon J. Fuller if (SIZE_MAX - binding->skip_out < ipos) {
123977cb4d3eSLandon J. Fuller BHND_NV_LOG("output skip %u would overflow "
124077cb4d3eSLandon J. Fuller "%zu\n", binding->skip_out, ipos);
124177cb4d3eSLandon J. Fuller return (EINVAL);
124277cb4d3eSLandon J. Fuller }
124377cb4d3eSLandon J. Fuller
124477cb4d3eSLandon J. Fuller ipos += binding->skip_out;
124577cb4d3eSLandon J. Fuller }
124677cb4d3eSLandon J. Fuller }
124777cb4d3eSLandon J. Fuller
124877cb4d3eSLandon J. Fuller /* Did we iterate all bindings until hitting end of the variable
124977cb4d3eSLandon J. Fuller * definition? */
125077cb4d3eSLandon J. Fuller BHND_NV_ASSERT(error != 0, ("loop terminated early"));
125177cb4d3eSLandon J. Fuller if (error != ENOENT) {
125277cb4d3eSLandon J. Fuller return (error);
125377cb4d3eSLandon J. Fuller }
125477cb4d3eSLandon J. Fuller
125577cb4d3eSLandon J. Fuller /* If marked IGNALL1 and all bits are set, treat variable as
125677cb4d3eSLandon J. Fuller * unavailable */
125777cb4d3eSLandon J. Fuller if ((var->flags & BHND_NVRAM_VF_IGNALL1) && all_bits_set)
125877cb4d3eSLandon J. Fuller return (ENOENT);
125977cb4d3eSLandon J. Fuller
126019be09f3SLandon J. Fuller /* Provide value wrapper */
126119be09f3SLandon J. Fuller return (bhnd_nvram_val_init(val, var->fmt, inp, ilen, var->type,
126219be09f3SLandon J. Fuller BHND_NVRAM_VAL_BORROW_DATA));
126319be09f3SLandon J. Fuller }
1264591e79bcSLandon J. Fuller
1265591e79bcSLandon J. Fuller /**
1266591e79bcSLandon J. Fuller * Common variable decoding; fetches and decodes variable to @p val,
1267591e79bcSLandon J. Fuller * using @p storage for actual data storage.
1268591e79bcSLandon J. Fuller *
1269591e79bcSLandon J. Fuller * The returned @p val instance will hold a borrowed reference to @p storage,
1270591e79bcSLandon J. Fuller * and must be copied via bhnd_nvram_val_copy() if it will be referenced beyond
1271591e79bcSLandon J. Fuller * the lifetime of @p storage.
1272591e79bcSLandon J. Fuller *
1273591e79bcSLandon J. Fuller * The caller is responsible for releasing any allocated value state
1274591e79bcSLandon J. Fuller * via bhnd_nvram_val_release().
1275591e79bcSLandon J. Fuller */
1276591e79bcSLandon J. Fuller static int
bhnd_nvram_sprom_getvar_common(struct bhnd_nvram_data * nv,void * cookiep,union bhnd_nvram_sprom_storage * storage,bhnd_nvram_val * val)1277591e79bcSLandon J. Fuller bhnd_nvram_sprom_getvar_common(struct bhnd_nvram_data *nv, void *cookiep,
1278591e79bcSLandon J. Fuller union bhnd_nvram_sprom_storage *storage, bhnd_nvram_val *val)
1279591e79bcSLandon J. Fuller {
1280591e79bcSLandon J. Fuller struct bhnd_nvram_sprom *sp;
1281591e79bcSLandon J. Fuller bhnd_sprom_opcode_idx_entry *entry;
1282acb7423dSJohn Baldwin const struct bhnd_nvram_vardefn *var __diagused;
1283591e79bcSLandon J. Fuller
1284591e79bcSLandon J. Fuller BHND_NV_ASSERT(cookiep != NULL, ("NULL variable cookiep"));
1285591e79bcSLandon J. Fuller
1286591e79bcSLandon J. Fuller sp = (struct bhnd_nvram_sprom *)nv;
1287591e79bcSLandon J. Fuller entry = cookiep;
1288591e79bcSLandon J. Fuller
1289591e79bcSLandon J. Fuller /* Fetch canonical variable definition */
1290591e79bcSLandon J. Fuller var = SPROM_COOKIE_TO_NVRAM_VAR(cookiep);
1291591e79bcSLandon J. Fuller BHND_NV_ASSERT(var != NULL, ("invalid cookiep %p", cookiep));
1292591e79bcSLandon J. Fuller
1293591e79bcSLandon J. Fuller return (bhnd_nvram_sprom_read_var(&sp->state, entry, sp->data, storage,
1294591e79bcSLandon J. Fuller val));
1295591e79bcSLandon J. Fuller }
1296591e79bcSLandon J. Fuller
129719be09f3SLandon J. Fuller static int
bhnd_nvram_sprom_getvar_order(struct bhnd_nvram_data * nv,void * cookiep1,void * cookiep2)129819be09f3SLandon J. Fuller bhnd_nvram_sprom_getvar_order(struct bhnd_nvram_data *nv, void *cookiep1,
129919be09f3SLandon J. Fuller void *cookiep2)
130019be09f3SLandon J. Fuller {
1301c283839dSLandon J. Fuller struct bhnd_sprom_opcode_idx_entry *e1, *e2;
130219be09f3SLandon J. Fuller
130319be09f3SLandon J. Fuller e1 = cookiep1;
130419be09f3SLandon J. Fuller e2 = cookiep2;
130519be09f3SLandon J. Fuller
130619be09f3SLandon J. Fuller /* Use the index entry order; this matches the order of variables
130719be09f3SLandon J. Fuller * returned via bhnd_nvram_sprom_next() */
130819be09f3SLandon J. Fuller if (e1 < e2)
130919be09f3SLandon J. Fuller return (-1);
131019be09f3SLandon J. Fuller else if (e1 > e2)
131119be09f3SLandon J. Fuller return (1);
131219be09f3SLandon J. Fuller
131319be09f3SLandon J. Fuller return (0);
131419be09f3SLandon J. Fuller }
131519be09f3SLandon J. Fuller
131619be09f3SLandon J. Fuller static int
bhnd_nvram_sprom_getvar(struct bhnd_nvram_data * nv,void * cookiep,void * buf,size_t * len,bhnd_nvram_type otype)131719be09f3SLandon J. Fuller bhnd_nvram_sprom_getvar(struct bhnd_nvram_data *nv, void *cookiep, void *buf,
131819be09f3SLandon J. Fuller size_t *len, bhnd_nvram_type otype)
131919be09f3SLandon J. Fuller {
132019be09f3SLandon J. Fuller bhnd_nvram_val val;
132119be09f3SLandon J. Fuller union bhnd_nvram_sprom_storage storage;
132219be09f3SLandon J. Fuller int error;
132319be09f3SLandon J. Fuller
132419be09f3SLandon J. Fuller /* Decode variable to a new value instance */
132519be09f3SLandon J. Fuller error = bhnd_nvram_sprom_getvar_common(nv, cookiep, &storage, &val);
132677cb4d3eSLandon J. Fuller if (error)
132777cb4d3eSLandon J. Fuller return (error);
132877cb4d3eSLandon J. Fuller
132919be09f3SLandon J. Fuller /* Perform value coercion */
133077cb4d3eSLandon J. Fuller error = bhnd_nvram_val_encode(&val, buf, len, otype);
133177cb4d3eSLandon J. Fuller
133277cb4d3eSLandon J. Fuller /* Clean up */
133377cb4d3eSLandon J. Fuller bhnd_nvram_val_release(&val);
133477cb4d3eSLandon J. Fuller return (error);
133577cb4d3eSLandon J. Fuller }
133677cb4d3eSLandon J. Fuller
133719be09f3SLandon J. Fuller static int
bhnd_nvram_sprom_copy_val(struct bhnd_nvram_data * nv,void * cookiep,bhnd_nvram_val ** value)133819be09f3SLandon J. Fuller bhnd_nvram_sprom_copy_val(struct bhnd_nvram_data *nv, void *cookiep,
133919be09f3SLandon J. Fuller bhnd_nvram_val **value)
134019be09f3SLandon J. Fuller {
134119be09f3SLandon J. Fuller bhnd_nvram_val val;
134219be09f3SLandon J. Fuller union bhnd_nvram_sprom_storage storage;
134319be09f3SLandon J. Fuller int error;
134419be09f3SLandon J. Fuller
134519be09f3SLandon J. Fuller /* Decode variable to a new value instance */
134619be09f3SLandon J. Fuller error = bhnd_nvram_sprom_getvar_common(nv, cookiep, &storage, &val);
134719be09f3SLandon J. Fuller if (error)
134819be09f3SLandon J. Fuller return (error);
134919be09f3SLandon J. Fuller
135019be09f3SLandon J. Fuller /* Attempt to copy to heap */
135119be09f3SLandon J. Fuller *value = bhnd_nvram_val_copy(&val);
135219be09f3SLandon J. Fuller bhnd_nvram_val_release(&val);
135319be09f3SLandon J. Fuller
135419be09f3SLandon J. Fuller if (*value == NULL)
135519be09f3SLandon J. Fuller return (ENOMEM);
135619be09f3SLandon J. Fuller
135719be09f3SLandon J. Fuller return (0);
135819be09f3SLandon J. Fuller }
135919be09f3SLandon J. Fuller
136077cb4d3eSLandon J. Fuller static const void *
bhnd_nvram_sprom_getvar_ptr(struct bhnd_nvram_data * nv,void * cookiep,size_t * len,bhnd_nvram_type * type)136177cb4d3eSLandon J. Fuller bhnd_nvram_sprom_getvar_ptr(struct bhnd_nvram_data *nv, void *cookiep,
136277cb4d3eSLandon J. Fuller size_t *len, bhnd_nvram_type *type)
136377cb4d3eSLandon J. Fuller {
136477cb4d3eSLandon J. Fuller /* Unsupported */
136577cb4d3eSLandon J. Fuller return (NULL);
136677cb4d3eSLandon J. Fuller }
136777cb4d3eSLandon J. Fuller
136877cb4d3eSLandon J. Fuller static const char *
bhnd_nvram_sprom_getvar_name(struct bhnd_nvram_data * nv,void * cookiep)136977cb4d3eSLandon J. Fuller bhnd_nvram_sprom_getvar_name(struct bhnd_nvram_data *nv, void *cookiep)
137077cb4d3eSLandon J. Fuller {
137177cb4d3eSLandon J. Fuller const struct bhnd_nvram_vardefn *var;
137277cb4d3eSLandon J. Fuller
137377cb4d3eSLandon J. Fuller BHND_NV_ASSERT(cookiep != NULL, ("NULL variable cookiep"));
137477cb4d3eSLandon J. Fuller
1375c283839dSLandon J. Fuller var = SPROM_COOKIE_TO_NVRAM_VAR(cookiep);
137677cb4d3eSLandon J. Fuller BHND_NV_ASSERT(var != NULL, ("invalid cookiep %p", cookiep));
137777cb4d3eSLandon J. Fuller
137877cb4d3eSLandon J. Fuller return (var->name);
137977cb4d3eSLandon J. Fuller }
138077cb4d3eSLandon J. Fuller
138119be09f3SLandon J. Fuller static int
bhnd_nvram_sprom_filter_setvar(struct bhnd_nvram_data * nv,const char * name,bhnd_nvram_val * value,bhnd_nvram_val ** result)138219be09f3SLandon J. Fuller bhnd_nvram_sprom_filter_setvar(struct bhnd_nvram_data *nv, const char *name,
138319be09f3SLandon J. Fuller bhnd_nvram_val *value, bhnd_nvram_val **result)
138419be09f3SLandon J. Fuller {
1385c283839dSLandon J. Fuller struct bhnd_nvram_sprom *sp;
1386c283839dSLandon J. Fuller const struct bhnd_nvram_vardefn *var;
1387c283839dSLandon J. Fuller bhnd_sprom_opcode_idx_entry *entry;
1388c283839dSLandon J. Fuller bhnd_nvram_val *spval;
1389c283839dSLandon J. Fuller int error;
1390c283839dSLandon J. Fuller
1391c283839dSLandon J. Fuller sp = (struct bhnd_nvram_sprom *)nv;
1392c283839dSLandon J. Fuller
1393c283839dSLandon J. Fuller /* Is this an externally immutable variable name? */
1394c283839dSLandon J. Fuller if (bhnd_sprom_is_external_immutable(name))
1395c283839dSLandon J. Fuller return (EINVAL);
1396c283839dSLandon J. Fuller
1397c283839dSLandon J. Fuller /* Variable must be defined in our SPROM layout */
1398c283839dSLandon J. Fuller if ((entry = bhnd_sprom_opcode_index_find(&sp->state, name)) == NULL)
1399c283839dSLandon J. Fuller return (ENOENT);
1400c283839dSLandon J. Fuller
1401c283839dSLandon J. Fuller var = bhnd_nvram_get_vardefn(entry->vid);
1402c283839dSLandon J. Fuller BHND_NV_ASSERT(var != NULL, ("missing variable definition"));
1403c283839dSLandon J. Fuller
1404c283839dSLandon J. Fuller /* Value must be convertible to the native variable type */
1405c283839dSLandon J. Fuller error = bhnd_nvram_val_convert_new(&spval, var->fmt, value,
1406c283839dSLandon J. Fuller BHND_NVRAM_VAL_DYNAMIC);
1407c283839dSLandon J. Fuller if (error)
1408c283839dSLandon J. Fuller return (error);
1409c283839dSLandon J. Fuller
1410c283839dSLandon J. Fuller /* Value must be encodeable by our SPROM layout */
1411c283839dSLandon J. Fuller error = bhnd_nvram_sprom_write_var(&sp->state, entry, spval, NULL);
1412c283839dSLandon J. Fuller if (error) {
1413c283839dSLandon J. Fuller bhnd_nvram_val_release(spval);
1414c283839dSLandon J. Fuller return (error);
1415c283839dSLandon J. Fuller }
1416c283839dSLandon J. Fuller
1417c283839dSLandon J. Fuller /* Success. Transfer our ownership of the converted value to the
1418c283839dSLandon J. Fuller * caller */
1419c283839dSLandon J. Fuller *result = spval;
1420c283839dSLandon J. Fuller return (0);
142119be09f3SLandon J. Fuller }
142219be09f3SLandon J. Fuller
142319be09f3SLandon J. Fuller static int
bhnd_nvram_sprom_filter_unsetvar(struct bhnd_nvram_data * nv,const char * name)142419be09f3SLandon J. Fuller bhnd_nvram_sprom_filter_unsetvar(struct bhnd_nvram_data *nv, const char *name)
142519be09f3SLandon J. Fuller {
1426c283839dSLandon J. Fuller struct bhnd_nvram_sprom *sp;
142777cb4d3eSLandon J. Fuller const struct bhnd_nvram_vardefn *var;
1428c283839dSLandon J. Fuller bhnd_sprom_opcode_idx_entry *entry;
142977cb4d3eSLandon J. Fuller
1430c283839dSLandon J. Fuller sp = (struct bhnd_nvram_sprom *)nv;
1431c283839dSLandon J. Fuller
1432c283839dSLandon J. Fuller /* Is this an externally immutable variable name? */
1433c283839dSLandon J. Fuller if (bhnd_sprom_is_external_immutable(name))
143477cb4d3eSLandon J. Fuller return (EINVAL);
143577cb4d3eSLandon J. Fuller
1436c283839dSLandon J. Fuller /* Variable must be defined in our SPROM layout */
1437c283839dSLandon J. Fuller if ((entry = bhnd_sprom_opcode_index_find(&sp->state, name)) == NULL)
143877cb4d3eSLandon J. Fuller return (ENOENT);
143977cb4d3eSLandon J. Fuller
1440c283839dSLandon J. Fuller var = bhnd_nvram_get_vardefn(entry->vid);
14416467a17bSLandon J. Fuller BHND_NV_ASSERT(var != NULL, ("missing variable definition"));
1442c283839dSLandon J. Fuller
1443c283839dSLandon J. Fuller /* Variable must be capable of representing a NULL/deleted value.
144477cb4d3eSLandon J. Fuller *
1445c283839dSLandon J. Fuller * Since SPROM's layout is fixed, this requires IGNALL -- if
1446c283839dSLandon J. Fuller * all bits are set, an IGNALL variable is treated as unset. */
1447c283839dSLandon J. Fuller if (!(var->flags & BHND_NVRAM_VF_IGNALL1))
144877cb4d3eSLandon J. Fuller return (EINVAL);
144977cb4d3eSLandon J. Fuller
145077cb4d3eSLandon J. Fuller return (0);
145177cb4d3eSLandon J. Fuller }
145277cb4d3eSLandon J. Fuller
1453c283839dSLandon J. Fuller /**
1454c283839dSLandon J. Fuller * Return true if @p name represents a special immutable variable name
1455c283839dSLandon J. Fuller * (e.g. sromrev) that cannot be updated in an SPROM existing image.
1456c283839dSLandon J. Fuller *
1457c283839dSLandon J. Fuller * @param name The name to check.
1458c283839dSLandon J. Fuller */
1459c283839dSLandon J. Fuller static bool
bhnd_sprom_is_external_immutable(const char * name)1460c283839dSLandon J. Fuller bhnd_sprom_is_external_immutable(const char *name)
1461c283839dSLandon J. Fuller {
1462c283839dSLandon J. Fuller /* The layout revision is immutable and cannot be changed */
1463c283839dSLandon J. Fuller if (strcmp(name, BHND_NVAR_SROMREV) == 0)
1464c283839dSLandon J. Fuller return (true);
1465c283839dSLandon J. Fuller
1466c283839dSLandon J. Fuller return (false);
146777cb4d3eSLandon J. Fuller }
1468