freebsd-dev/sys/dev/bhnd/nvram/bhnd_nvram_private.h
Landon J. Fuller 77cb4d3e50 bhnd(4): Unify NVRAM/SPROM parsing, implement compact SPROM layout encoding.
- Defined an abstract NVRAM I/O API (bhnd_nvram_io), decoupling NVRAM/SPROM
  parsing from the actual underlying NVRAM data provider (e.g. CFE firmware
  devices).
- Defined an abstract NVRAM data API (bhnd_nvram_data), decoupling
  higher-level NVRAM operations (indexed lookup, data conversion, etc) from
  the underlying NVRAM file format parsing/serialization.
- Implemented a new high-level bhnd_nvram_store API, providing indexed
  variable lookup, pending write tracking, etc on top of an arbitrary
  bhnd_nvram_data instance.
- Migrated all bhnd(4) NVRAM device drivers to the common bhnd_nvram_store
  API.
- Implemented a common bhnd_nvram_val API for parsing/encoding NVRAM
  variable values, including applying format-specific behavior when
  converting to/from the NVRAM string representations.
- Dropped the now unnecessary bhnd_nvram driver, and moved the
  broadcom/mips-specific CFE NVRAM driver out into sys/mips/broadcom.
- Implemented a new nvram_map file format:
        - Variable definitions are now defined separately from the SPROM
          layout. This will also allow us to define CIS tuple NVRAM
          mappings referencing the common NVRAM variable definitions.
        - Variables can now be defined within arbitrary named groups.
        - Textual descriptions and help information can be defined inline
          for both variables and variable groups.
        - Implemented a new, compact encoding of SPROM image layout
          offsets.
- Source-level (but not build system) support for building the NVRAM file
  format APIs (bhnd_nvram_io, bhnd_nvram_data, bhnd_nvram_store) as a
  userspace library.

The new compact SPROM image layout encoding is loosely modeled on Apple
dyld compressed LINKEDIT symbol binding opcodes; it provides a compact
state-machine encoding of the mapping between NVRAM variables and the SPROM
image offset, mask, and shift instructions necessary to decode or encode
the SPROM variable data.

The compact encoding reduces the size of the generated SPROM layout data
from roughly 60KB to 3KB. The sequential nature SPROM layout opcode tables
also simplify iteration of the SPROM variables, as it's no longer
neccessary to iterate the full NVRAM variable definition table, but
instead simply scan the SPROM revision's layout opcode table.

Approved by:    adrian (mentor)
Differential Revision:  https://reviews.freebsd.org/D8645
2016-11-26 23:22:32 +00:00

403 lines
14 KiB
C

/*-
* Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
* redistribution must be conditioned upon including a substantially
* similar Disclaimer requirement for further binary redistribution.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGES.
*
* $FreeBSD$
*/
#ifndef _BHND_NVRAM_BHND_NVRAM_PRIVATE_H_
#define _BHND_NVRAM_BHND_NVRAM_PRIVATE_H_
/*
* Private BHND NVRAM definitions.
*/
#include <sys/param.h>
#ifdef _KERNEL
#include <sys/malloc.h>
#include <machine/stdarg.h>
#else
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#endif
#include "bhnd_nvram.h"
#include "bhnd_nvram_value.h"
/*
* bhnd_nvram_crc8() lookup table.
*/
extern const uint8_t bhnd_nvram_crc8_tab[];
/* Forward declarations */
struct bhnd_nvram_vardefn;
#ifdef _KERNEL
MALLOC_DECLARE(M_BHND_NVRAM);
#define bhnd_nv_isupper(c) isupper(c)
#define bhnd_nv_islower(c) islower(c)
#define bhnd_nv_isalpha(c) isalpha(c)
#define bhnd_nv_isprint(c) isprint(c)
#define bhnd_nv_isspace(c) isspace(c)
#define bhnd_nv_isdigit(c) isdigit(c)
#define bhnd_nv_isxdigit(c) isxdigit(c)
#define bhnd_nv_toupper(c) toupper(c)
#define bhnd_nv_malloc(size) malloc((size), M_BHND_NVRAM, M_WAITOK)
#define bhnd_nv_calloc(n, size) malloc((n) * (size), M_BHND_NVRAM, \
M_WAITOK | M_ZERO)
#define bhnd_nv_reallocf(buf, size) reallocf((buf), (size), M_BHND_NVRAM, \
M_WAITOK)
#define bhnd_nv_free(buf) free((buf), M_BHND_NVRAM)
#define bhnd_nv_strndup(str, len) strndup(str, len, M_BHND_NVRAM)
#ifdef INVARIANTS
#define BHND_NV_INVARIANTS
#endif
#define BHND_NV_ASSERT(expr, ...) KASSERT(expr, __VA_ARGS__)
#define BHND_NV_VERBOSE (bootverbose)
#define BHND_NV_PANIC(...) panic(__VA_ARGS__)
#define BHND_NV_LOG(fmt, ...) \
printf("%s: " fmt, __FUNCTION__, ##__VA_ARGS__)
#define bhnd_nv_ummax(a, b) ummax((a), (b))
#define bhnd_nv_ummin(a, b) ummin((a), (b))
#else /* !_KERNEL */
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
/* ASCII-specific ctype variants that work consistently regardless
* of current locale */
#define bhnd_nv_isupper(c) ((c) >= 'A' && (c) <= 'Z')
#define bhnd_nv_islower(c) ((c) >= 'a' && (c) <= 'z')
#define bhnd_nv_isalpha(c) (bhnd_nv_isupper(c) || bhnd_nv_islower(c))
#define bhnd_nv_isprint(c) ((c) >= ' ' && (c) <= '~')
#define bhnd_nv_isspace(c) ((c) == ' ' || ((c) >= '\t' && (c) <= '\r'))
#define bhnd_nv_isdigit(c) isdigit(c)
#define bhnd_nv_isxdigit(c) isxdigit(c)
#define bhnd_nv_toupper(c) ((c) - \
(('a' - 'A') * ((c) >= 'a' && (c) <= 'z')))
#define bhnd_nv_malloc(size) malloc((size))
#define bhnd_nv_calloc(n, size) calloc((n), (size))
#define bhnd_nv_reallocf(buf, size) reallocf((buf), (size))
#define bhnd_nv_free(buf) free((buf))
#define bhnd_nv_strndup(str, len) strndup(str, len)
#ifndef NDEBUG
#define BHND_NV_INVARIANTS
#endif
#define BHND_NV_ASSERT(expr, ...) assert(expr)
#define BHND_NV_VERBOSE (0)
#define BHND_NV_PANIC(fmt, ...) do { \
fprintf(stderr, "panic: " fmt "\n", ##__VA_ARGS__); \
abort(); \
} while(0)
#define BHND_NV_LOG(fmt, ...) \
fprintf(stderr, "%s: " fmt, __FUNCTION__, ##__VA_ARGS__)
static inline uintmax_t
bhnd_nv_ummax(uintmax_t a, uintmax_t b)
{
return (a > b ? a : b);
}
static inline uintmax_t
bhnd_nv_ummin(uintmax_t a, uintmax_t b)
{
return (a < b ? a : b);
}
#endif /* _KERNEL */
#ifdef BHND_NV_VERBOSE
#define BHND_NV_DEBUG(...) BHND_NV_LOG(__VA_ARGS__)
#else /* !BHND_NV_VERBOSE */
#define BHND_NV_DEBUG(...)
#endif /* BHND_NV_VERBOSE */
/* Limit a size_t value to a suitable range for use as a printf string field
* width */
#define BHND_NV_PRINT_WIDTH(_len) \
((_len) > (INT_MAX) ? (INT_MAX) : (int)(_len))
int bhnd_nvram_value_coerce(const void *inp,
size_t ilen, bhnd_nvram_type itype,
void *outp, size_t *olen,
bhnd_nvram_type otype);
int bhnd_nvram_value_nelem(bhnd_nvram_type type,
const void *data, size_t len,
size_t *nelem);
size_t bhnd_nvram_value_size(bhnd_nvram_type type,
const void *data, size_t nbytes,
size_t nelem);
int bhnd_nvram_value_printf(const char *fmt,
const void *inp, size_t ilen,
bhnd_nvram_type itype, char *outp,
size_t *olen, ...);
int bhnd_nvram_value_vprintf(const char *fmt,
const void *inp, size_t ilen,
bhnd_nvram_type itype, char *outp,
size_t *olen, va_list ap);
const struct bhnd_nvram_vardefn *bhnd_nvram_find_vardefn(const char *varname);
const struct bhnd_nvram_vardefn *bhnd_nvram_get_vardefn(size_t id);
size_t bhnd_nvram_get_vardefn_id(
const struct bhnd_nvram_vardefn *defn);
int bhnd_nvram_parse_int(const char *s,
size_t maxlen, u_int base, size_t *nbytes,
void *outp, size_t *olen,
bhnd_nvram_type otype);
int bhnd_nvram_parse_env(const char *env,
size_t env_len, char delim,
const char **name, size_t *name_len,
const char **value, size_t *value_len);
size_t bhnd_nvram_parse_field(const char **inp,
size_t ilen, char delim);
size_t bhnd_nvram_trim_field(const char **inp,
size_t ilen, char delim);
bool bhnd_nvram_validate_name(const char *name,
size_t name_len);
/**
* Calculate CRC-8 over @p buf using the Broadcom SPROM/NVRAM CRC-8
* polynomial.
*
* @param buf input buffer
* @param size buffer size
* @param crc last computed crc, or BHND_NVRAM_CRC8_INITIAL
*/
static inline uint8_t
bhnd_nvram_crc8(const void *buf, size_t size, uint8_t crc)
{
const uint8_t *p = (const uint8_t *)buf;
while (size--)
crc = bhnd_nvram_crc8_tab[(crc ^ *p++)];
return (crc);
}
#define BHND_NVRAM_CRC8_INITIAL 0xFF /**< Initial bhnd_nvram_crc8 value */
#define BHND_NVRAM_CRC8_VALID 0x9F /**< Valid CRC-8 checksum */
/** NVRAM variable flags */
enum {
BHND_NVRAM_VF_MFGINT = 1<<0, /**< mfg-internal variable; should not
be externally visible */
BHND_NVRAM_VF_IGNALL1 = 1<<1 /**< hide variable if its value has all
bits set. */
};
/**
* SPROM layout flags
*/
enum {
/**
* SPROM layout does not have magic identification value.
*
* This applies to SPROM revisions 1-3, where the actual
* layout must be determined by looking for a matching sromrev
* at the expected offset, and then verifying the CRC to ensure
* that the match was not a false positive.
*/
SPROM_LAYOUT_MAGIC_NONE = (1<<0),
};
/** NVRAM variable definition */
struct bhnd_nvram_vardefn {
const char *name; /**< variable name */
const char *desc; /**< human readable description,
or NULL */
const char *help; /**< human readable help text,
or NULL */
bhnd_nvram_type type; /**< variable type */
uint8_t nelem; /**< element count, or 1 if not
an array-typed variable */
const bhnd_nvram_val_fmt_t *fmt; /**< value format, or NULL */
uint32_t flags; /**< flags (BHND_NVRAM_VF_*) */
};
/*
* NVRAM variable definitions generated from nvram_map.
*/
extern const struct bhnd_nvram_vardefn bhnd_nvram_vardefns[];
extern const size_t bhnd_nvram_num_vardefns;
/**
* SPROM layout descriptor.
*/
struct bhnd_sprom_layout {
size_t size; /**< SPROM image size, in bytes */
uint8_t rev; /**< SPROM revision */
uint8_t flags; /**< layout flags (SPROM_LAYOUT_*) */
size_t srev_offset; /**< offset to SROM revision */
size_t magic_offset; /**< offset to magic value */
uint16_t magic_value; /**< expected magic value */
const uint8_t *bindings; /**< SPROM binding opcode table */
size_t bindings_size; /**< SPROM binding opcode table size */
uint16_t num_vars; /**< total number of variables defined
for this layout by the binding
table */
};
/*
* SPROM layout descriptions generated from nvram_map.
*/
extern const struct bhnd_sprom_layout bhnd_sprom_layouts[];
extern const size_t bhnd_sprom_num_layouts;
/*
* SPROM binding opcodes.
*
* Most opcodes are provided with two variants:
*
* - Standard: The opcode's data directly follows the opcode. The data type
* (SPROM_OPCODE_DATA_*) is encoded in the opcode immediate (IMM).
* - Immediate: The opcode's data is encoded directly in the opcode immediate
* (IMM).
*/
#define SPROM_OPC_MASK 0xF0 /**< operation mask */
#define SPROM_IMM_MASK 0x0F /**< immediate value mask */
#define SPROM_IMM_MAX SPROM_IMM_MASK
#define SPROM_OP_DATA_U8 0x00 /**< data is u8 */
#define SPROM_OP_DATA_U8_SCALED 0x01 /**< data is u8; multiply by
type width */
#define SPROM_OP_DATA_U16 0x02 /**< data is u16-le */
#define SPROM_OP_DATA_U32 0x03 /**< data is u32-le */
#define SPROM_OP_DATA_I8 0x04 /**< data is i8 */
#define SPROM_OPCODE_EXT 0x00 /**< extended opcodes defined
in IMM */
#define SPROM_OPCODE_EOF 0x00 /**< marks end of opcode
stream */
#define SPROM_OPCODE_NELEM 0x01 /**< variable array element
count follows as U8 */
#define SPROM_OPCODE_VAR_END 0x02 /**< marks end of variable
definition */
#define SPROM_OPCODE_TYPE 0x03 /**< input type follows as U8
(see BHND_NVRAM_TYPE_*) */
#define SPROM_OPCODE_VAR_IMM 0x10 /**< variable ID (imm) */
#define SPROM_OPCODE_VAR_REL_IMM 0x20 /**< relative variable ID
(last ID + imm) */
#define SPROM_OPCODE_VAR 0x30 /**< variable ID */
#define SPROM_OPCODE_REV_IMM 0x40 /**< revision range (imm) */
#define SPROM_OPCODE_REV_RANGE 0x50 /**< revision range (8-bit range)*/
#define SPROM_OP_REV_RANGE_MAX 0x0F /**< maximum representable SROM
revision */
#define SPROM_OP_REV_START_MASK 0xF0
#define SPROM_OP_REV_START_SHIFT 4
#define SPROM_OP_REV_END_MASK 0x0F
#define SPROM_OP_REV_END_SHIFT 0
#define SPROM_OPCODE_MASK_IMM 0x60 /**< value mask (imm) */
#define SPROM_OPCODE_MASK 0x70 /**< value mask */
#define SPROM_OPCODE_SHIFT_IMM 0x80 /**< value shift (unsigned
imm, multipled by 2) */
#define SPROM_OPCODE_SHIFT 0x90 /**< value shift */
#define SPROM_OPCODE_OFFSET_REL_IMM 0xA0 /**< relative input offset
(last offset +
(imm * type width)) */
#define SPROM_OPCODE_OFFSET 0xB0 /**< input offset */
#define SPROM_OPCODE_TYPE_IMM 0xC0 /**< input type (imm,
see BHND_NVRAM_TYPE_*) */
#define SPROM_OPCODE_DO_BIND 0xD0 /**< bind current value,
advance input/output
offsets as per IMM */
#define SPROM_OP_BIND_SKIP_IN_MASK 0x03 /**< the number of input
elements to advance after
the bind */
#define SPROM_OP_BIND_SKIP_IN_SHIFT 0
#define SPROM_OP_BIND_SKIP_IN_SIGN (1<<2) /**< SKIP_IN sign bit */
#define SPROM_OP_BIND_SKIP_OUT_MASK 0x08 /**< the number of output
elements to advance after
the bind */
#define SPROM_OP_BIND_SKIP_OUT_SHIFT 3
#define SPROM_OPCODE_DO_BINDN_IMM 0xE0 /**< bind IMM times, advancing
input/output offsets by one
element each time */
#define SPROM_OPCODE_DO_BINDN 0xF0 /**< bind N times, advancing
input/output offsets as per
SPROM_OP_BIND_SKIP_IN/SPROM_OP_BIND_SKIP_OUT
IMM values. The U8 element
count follows. */
/** Evaluates to true if opcode is an extended opcode */
#define SPROM_OPCODE_IS_EXT(_opcode) \
(((_opcode) & SPROM_OPC_MASK) == SPROM_OPCODE_EXT)
/** Return the opcode constant for a simple or extended opcode */
#define SPROM_OPCODE_OP(_opcode) \
(SPROM_OPCODE_IS_EXT(_opcode) ? (_opcode) : ((_opcode) & SPROM_OPC_MASK))
/** Return the opcode immediate for a simple opcode, or zero if this is
* an extended opcode */
#define SPROM_OPCODE_IMM(_opcode) \
(SPROM_OPCODE_IS_EXT(_opcode) ? 0 : ((_opcode) & SPROM_IMM_MASK))
/** Evaluates to true if the given opcode produces an implicit
* SPROM_OPCODE_VAR_END instruction for any open variable */
#define SPROM_OP_IS_IMPLICIT_VAR_END(_opcode) \
(((_opcode) == SPROM_OPCODE_VAR_IMM) || \
((_opcode) == SPROM_OPCODE_VAR_REL_IMM) || \
((_opcode) == SPROM_OPCODE_VAR) || \
((_opcode) == SPROM_OPCODE_REV_IMM) || \
((_opcode) == SPROM_OPCODE_REV_RANGE))
/** Evaluates to true if the given opcode is either an explicit
* SPROM_OPCODE_VAR_END instruction, or is an opcode that produces an
* implicit terminatation of any open variable */
#define SPROM_OP_IS_VAR_END(_opcode) \
(((_opcode) == SPROM_OPCODE_VAR_END) || \
SPROM_OP_IS_IMPLICIT_VAR_END(_opcode))
/** maximum representable immediate value */
#define SPROM_OP_IMM_MAX SPROM_IMM_MASK
/** maximum representable SROM revision */
#define SPROM_OP_REV_MAX MAX(SPROM_OP_REV_RANGE_MAX, SPROM_IMM_MAX)
#endif /* _BHND_NVRAM_BHND_NVRAM_PRIVATE_H_ */